From ea61c7a5dbf35586e74f6fd1ccb20dffe6e7c8f4 Mon Sep 17 00:00:00 2001 From: fragosti Date: Thu, 29 Nov 2018 11:23:16 -0800 Subject: feat: have basic code syntax highlighting working --- packages/website/package.json | 2 + packages/website/public/index.html | 217 ++++++++++++--------- .../ts/pages/documentation/developers_page.tsx | 4 + packages/website/ts/pages/instant/code_demo.tsx | 45 +++++ packages/website/ts/pages/instant/configurator.tsx | 7 +- yarn.lock | 128 +++++++++++- 6 files changed, 308 insertions(+), 95 deletions(-) create mode 100644 packages/website/ts/pages/instant/code_demo.tsx diff --git a/packages/website/package.json b/packages/website/package.json index dc10c7b1c..7d5c81858 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -54,6 +54,7 @@ "react-popper": "^1.0.0-beta.6", "react-redux": "^5.0.3", "react-scroll": "0xproject/react-scroll#pr-330-and-replace-state", + "react-syntax-highlighter": "^10.1.1", "react-tooltip": "^3.2.7", "react-typist": "^2.0.4", "redux": "^3.6.0", @@ -83,6 +84,7 @@ "@types/react-helmet": "^5.0.6", "@types/react-redux": "^4.4.37", "@types/react-scroll": "1.5.3", + "@types/react-syntax-highlighter": "^0.0.8", "@types/react-tap-event-plugin": "0.0.30", "@types/redux": "^3.6.0", "@types/web3-provider-engine": "^14.0.0", diff --git a/packages/website/public/index.html b/packages/website/public/index.html index a8a61f8ad..538eca6d9 100644 --- a/packages/website/public/index.html +++ b/packages/website/public/index.html @@ -1,95 +1,132 @@ + + + + + + + + + 0x: The Protocol for Trading Tokens + + + + + + + + + + - - - - - - - - - 0x: The Protocol for Trading Tokens - - - - - - - - - - - + + + + + + + - - - - + + +
+ +
+ + + - - -
- -
- - - - - - - - - - - - \ No newline at end of file + t._e = []; + t.ready = function(f) { + t._e.push(f); + }; + return t; + })(document, 'script', 'twitter-wjs'); + + + + + + + + + diff --git a/packages/website/ts/pages/documentation/developers_page.tsx b/packages/website/ts/pages/documentation/developers_page.tsx index a84be7bfe..fcca2b6ad 100644 --- a/packages/website/ts/pages/documentation/developers_page.tsx +++ b/packages/website/ts/pages/documentation/developers_page.tsx @@ -2,6 +2,7 @@ import { colors, constants as sharedConstants, utils as sharedUtils } from '@0x/ import * as _ from 'lodash'; import * as React from 'react'; import DocumentTitle from 'react-document-title'; +import { Helmet } from 'react-helmet'; import { DocsLogo } from 'ts/components/documentation/docs_logo'; import { DocsTopBar } from 'ts/components/documentation/docs_top_bar'; import { Container } from 'ts/components/ui/container'; @@ -146,6 +147,9 @@ export class DevelopersPage extends React.Component + + + = props => { + const codeString = ` + + + + +`; + return ( + + {codeString} + + ); +}; diff --git a/packages/website/ts/pages/instant/configurator.tsx b/packages/website/ts/pages/instant/configurator.tsx index c836739bb..2314f968f 100644 --- a/packages/website/ts/pages/instant/configurator.tsx +++ b/packages/website/ts/pages/instant/configurator.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { Container } from 'ts/components/ui/container'; +import { CodeDemo } from 'ts/pages/instant/code_demo'; import { colors } from 'ts/style/colors'; export interface ConfiguratorProps { @@ -8,5 +9,9 @@ export interface ConfiguratorProps { } export const Configurator = (props: ConfiguratorProps) => ( - + + + + + ); diff --git a/yarn.lock b/yarn.lock index a7a55a7b3..e07aa724d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1531,6 +1531,12 @@ dependencies: "@types/react" "*" +"@types/react-syntax-highlighter@^0.0.8": + version "0.0.8" + resolved "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-0.0.8.tgz#ed44e2ead992c513327bcf2ede5eda7daa981421" + dependencies: + "@types/react" "*" + "@types/react-tap-event-plugin@0.0.30": version "0.0.30" resolved "https://registry.yarnpkg.com/@types/react-tap-event-plugin/-/react-tap-event-plugin-0.0.30.tgz#123f35080412f489b6770c5a65c159ff96654cb5" @@ -1906,6 +1912,10 @@ aes-js@^0.2.3: version "0.2.4" resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-0.2.4.tgz#94b881ab717286d015fa219e08fb66709dda5a3d" +aes-js@^3.1.1: + version "3.1.2" + resolved "https://registry.npmjs.org/aes-js/-/aes-js-3.1.2.tgz#db9aabde85d5caabbfc0d4f2a4446960f627146a" + agent-base@4, agent-base@^4.1.0, agent-base@~4.2.0: version "4.2.1" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" @@ -3351,7 +3361,7 @@ bs-logger@0.x: dependencies: fast-json-stable-stringify "^2.0.0" -bs58@=4.0.1: +bs58@=4.0.1, bs58@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" dependencies: @@ -3374,6 +3384,14 @@ bs58check@^1.0.8: bs58 "^3.1.0" create-hash "^1.1.0" +bs58check@^2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" + dependencies: + bs58 "^4.0.0" + create-hash "^1.1.0" + safe-buffer "^5.1.2" + bser@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719" @@ -4079,6 +4097,12 @@ combined-stream@1.0.6, combined-stream@^1.0.5, combined-stream@~1.0.5, combined- dependencies: delayed-stream "~1.0.0" +comma-separated-tokens@^1.0.0: + version "1.0.5" + resolved "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.5.tgz#b13793131d9ea2d2431cf5b507ddec258f0ce0db" + dependencies: + trim "0.0.1" + commander@2.11.0: version "2.11.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" @@ -5970,6 +5994,19 @@ ethereumjs-wallet@0.6.0: utf8 "^2.1.1" uuid "^2.0.1" +ethereumjs-wallet@~0.6.0: + version "0.6.2" + resolved "https://registry.npmjs.org/ethereumjs-wallet/-/ethereumjs-wallet-0.6.2.tgz#67244b6af3e8113b53d709124b25477b64aeccda" + dependencies: + aes-js "^3.1.1" + bs58check "^2.1.2" + ethereumjs-util "^5.2.0" + hdkey "^1.0.0" + safe-buffer "^5.1.2" + scrypt.js "^0.2.0" + utf8 "^3.0.0" + uuid "^3.3.2" + ethers@~4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.4.tgz#d3f85e8b27f4b59537e06526439b0fb15b44dc65" @@ -6318,6 +6355,12 @@ fastparse@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8" +fault@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/fault/-/fault-1.0.2.tgz#c3d0fec202f172a3a4d414042ad2bb5e2a3ffbaa" + dependencies: + format "^0.2.2" + faye-websocket@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" @@ -6616,6 +6659,10 @@ format-util@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/format-util/-/format-util-1.0.3.tgz#032dca4a116262a12c43f4c3ec8566416c5b2d95" +format@^0.2.2: + version "0.2.2" + resolved "https://registry.npmjs.org/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b" + forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" @@ -6778,7 +6825,7 @@ ganache-core@0xProject/ganache-core#monorepo-dep: ethereumjs-tx "0xProject/ethereumjs-tx#fake-tx-include-signature-by-default" ethereumjs-util "^5.2.0" ethereumjs-vm "2.3.5" - ethereumjs-wallet "0.6.0" + ethereumjs-wallet "~0.6.0" fake-merkle-patricia-tree "~1.0.1" heap "~0.2.6" js-scrypt "^0.2.0" @@ -7478,6 +7525,19 @@ hash.js@1.1.3, hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.0" +hast-util-parse-selector@^2.2.0: + version "2.2.1" + resolved "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.1.tgz#4ddbae1ae12c124e3eb91b581d2556441766f0ab" + +hastscript@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/hastscript/-/hastscript-5.0.0.tgz#fee10382c1bc4ba3f1be311521d368c047d2c43a" + dependencies: + comma-separated-tokens "^1.0.0" + hast-util-parse-selector "^2.2.0" + property-information "^5.0.1" + space-separated-tokens "^1.0.0" + hawk@3.1.3, hawk@~3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" @@ -7503,6 +7563,14 @@ hdkey@^0.7.0, hdkey@^0.7.1: coinstring "^2.0.0" secp256k1 "^3.0.1" +hdkey@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/hdkey/-/hdkey-1.1.0.tgz#e74e7b01d2c47f797fa65d1d839adb7a44639f29" + dependencies: + coinstring "^2.0.0" + safe-buffer "^5.1.1" + secp256k1 "^3.0.1" + he@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" @@ -7518,7 +7586,7 @@ heap@~0.2.6: version "0.2.6" resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.6.tgz#087e1f10b046932fc8594dd9e6d378afc9d1e5ac" -highlight.js@^9.0.0, highlight.js@^9.11.0: +highlight.js@^9.0.0, highlight.js@^9.11.0, highlight.js@~9.12.0: version "9.12.0" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.12.0.tgz#e6d9dbe57cbefe60751f02af336195870c90c01e" @@ -9875,6 +9943,13 @@ lowercase-keys@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" +lowlight@~1.9.1: + version "1.9.2" + resolved "https://registry.npmjs.org/lowlight/-/lowlight-1.9.2.tgz#0b9127e3cec2c3021b7795dd81005c709a42fdd1" + dependencies: + fault "^1.0.2" + highlight.js "~9.12.0" + lru-cache@2: version "2.7.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" @@ -11443,6 +11518,17 @@ parse-entities@^1.1.0: is-decimal "^1.0.0" is-hexadecimal "^1.0.0" +parse-entities@^1.1.2: + version "1.2.0" + resolved "https://registry.npmjs.org/parse-entities/-/parse-entities-1.2.0.tgz#9deac087661b2e36814153cb78d7e54a4c5fd6f4" + dependencies: + character-entities "^1.0.0" + character-entities-legacy "^1.0.0" + character-reference-invalid "^1.0.0" + is-alphanumerical "^1.0.0" + is-decimal "^1.0.0" + is-hexadecimal "^1.0.0" + parse-filepath@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" @@ -12021,7 +12107,7 @@ pretty-hrtime@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" -prismjs@^1.15.0: +prismjs@^1.15.0, prismjs@^1.8.4, prismjs@~1.15.0: version "1.15.0" resolved "https://registry.npmjs.org/prismjs/-/prismjs-1.15.0.tgz#8801d332e472091ba8def94976c8877ad60398d9" optionalDependencies: @@ -12121,6 +12207,12 @@ prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.5.8, loose-envify "^1.3.1" object-assign "^4.1.1" +property-information@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/property-information/-/property-information-5.0.1.tgz#c3b09f4f5750b1634c0b24205adbf78f18bdf94f" + dependencies: + xtend "^4.0.1" + proto-list@~1.2.1: version "1.2.4" resolved "http://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" @@ -12584,6 +12676,16 @@ react-side-effect@^1.0.2, react-side-effect@^1.1.0: exenv "^1.2.1" shallowequal "^1.0.1" +react-syntax-highlighter@^10.1.1: + version "10.1.1" + resolved "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-10.1.1.tgz#1bf7ad4f2f16d2978b04594407b670671b4d3316" + dependencies: + babel-runtime "^6.18.0" + highlight.js "~9.12.0" + lowlight "~1.9.1" + prismjs "^1.8.4" + refractor "^2.4.1" + react-tabs@^2.0.0: version "2.2.2" resolved "https://registry.npmjs.org/react-tabs/-/react-tabs-2.2.2.tgz#2f2935da379889484751d1df47c1b639e5ee835d" @@ -12909,6 +13011,14 @@ redux@^3.6.0: loose-envify "^1.1.0" symbol-observable "^1.0.3" +refractor@^2.4.1: + version "2.6.2" + resolved "https://registry.npmjs.org/refractor/-/refractor-2.6.2.tgz#8e0877ab8864165275aafeea5d9c8eebe871552f" + dependencies: + hastscript "^5.0.0" + parse-entities "^1.1.2" + prismjs "~1.15.0" + regenerate@^1.2.1: version "1.3.3" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.3.tgz#0c336d3980553d755c39b586ae3b20aa49c82b7f" @@ -14093,6 +14203,12 @@ source-map@~0.2.0: dependencies: amdefine ">=0.0.4" +space-separated-tokens@^1.0.0: + version "1.1.2" + resolved "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.2.tgz#e95ab9d19ae841e200808cd96bc7bd0adbbb3412" + dependencies: + trim "0.0.1" + sparkles@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.0.tgz#1acbbfb592436d10bbe8f785b7cc6f82815012c3" @@ -15622,6 +15738,10 @@ utf8@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/utf8/-/utf8-2.1.2.tgz#1fa0d9270e9be850d9b05027f63519bf46457d96" +utf8@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" + util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" -- cgit v1.2.3 From 6f394128d46420b336fb50a07f638b12bbb55eec Mon Sep 17 00:00:00 2001 From: fragosti Date: Thu, 29 Nov 2018 13:01:41 -0800 Subject: feat: improve styling of code demo component --- packages/website/ts/pages/instant/code_demo.tsx | 131 +++++++++++++++++++-- packages/website/ts/pages/instant/configurator.tsx | 7 +- 2 files changed, 126 insertions(+), 12 deletions(-) diff --git a/packages/website/ts/pages/instant/code_demo.tsx b/packages/website/ts/pages/instant/code_demo.tsx index 9764227a1..b029cc368 100644 --- a/packages/website/ts/pages/instant/code_demo.tsx +++ b/packages/website/ts/pages/instant/code_demo.tsx @@ -1,24 +1,145 @@ import * as React from 'react'; import SyntaxHighlighter from 'react-syntax-highlighter'; import { atelierCaveDark } from 'react-syntax-highlighter/styles/hljs'; +import { colors } from 'ts/style/colors'; import { styled } from 'ts/style/theme'; import { Container } from 'ts/components/ui/container'; const CustomPre = styled.pre` + margin: 0px; + line-height: 24px; code { background-color: inherit !important; border-radius: 0px; font-family: 'Roboto Mono', sans-serif; border: none; } + .lsl { + background-color: #2a2a2a !important; + color: #999; + height: 100%; + text-align: center; + padding-right: 5px !important; + padding-left: 5px; + margin-right: 5px; + line-height: 25px; + padding-top: 10px; + } + .xml { + position: relative; + top: 10px; + } `; +const customStyle = { + 'hljs-comment': { + color: '#7e7887', + }, + 'hljs-quote': { + color: '#7e7887', + }, + 'hljs-variable': { + color: '#be4678', + }, + 'hljs-template-variable': { + color: '#be4678', + }, + 'hljs-attribute': { + color: '#be4678', + }, + 'hljs-regexp': { + color: '#be4678', + }, + 'hljs-link': { + color: '#be4678', + }, + 'hljs-tag': { + color: '#61f5ff', + }, + 'hljs-name': { + color: '#61f5ff', + }, + 'hljs-selector-id': { + color: '#be4678', + }, + 'hljs-selector-class': { + color: '#be4678', + }, + 'hljs-number': { + color: '#c994ff', + }, + 'hljs-meta': { + color: '#aa573c', + }, + 'hljs-built_in': { + color: '#aa573c', + }, + 'hljs-builtin-name': { + color: '#aa573c', + }, + 'hljs-literal': { + color: '#aa573c', + }, + 'hljs-type': { + color: '#aa573c', + }, + 'hljs-params': { + color: '#aa573c', + }, + 'hljs-string': { + color: '#bcff88', + }, + 'hljs-symbol': { + color: '#2a9292', + }, + 'hljs-bullet': { + color: '#2a9292', + }, + 'hljs-title': { + color: '#576ddb', + }, + 'hljs-section': { + color: '#576ddb', + }, + 'hljs-keyword': { + color: '#955ae7', + }, + 'hljs-selector-tag': { + color: '#955ae7', + }, + 'hljs-deletion': { + color: '#19171c', + display: 'inline-block', + width: '100%', + backgroundColor: '#be4678', + }, + 'hljs-addition': { + color: '#19171c', + display: 'inline-block', + width: '100%', + backgroundColor: '#2a9292', + }, + hljs: { + display: 'block', + overflowX: 'auto', + background: colors.instantSecondaryBackground, + color: 'white', + fontSize: '12px', + }, + 'hljs-emphasis': { + fontStyle: 'italic', + }, + 'hljs-strong': { + fontWeight: 'bold', + }, +}; + export interface CodeDemoProps {} export const CodeDemo: React.StatelessComponent = props => { const codeString = ` - + `; return ( - + {codeString} ); diff --git a/packages/website/ts/pages/instant/configurator.tsx b/packages/website/ts/pages/instant/configurator.tsx index 2314f968f..09a51d0bb 100644 --- a/packages/website/ts/pages/instant/configurator.tsx +++ b/packages/website/ts/pages/instant/configurator.tsx @@ -9,9 +9,8 @@ export interface ConfiguratorProps { } export const Configurator = (props: ConfiguratorProps) => ( - - - - + + Forms + ); -- cgit v1.2.3 From 09e2157639ad2d986b80f5480a1e9188a56ff3f9 Mon Sep 17 00:00:00 2001 From: fragosti Date: Thu, 29 Nov 2018 13:45:29 -0800 Subject: feat: factor out ActionLink component from features --- packages/website/ts/pages/instant/action_link.tsx | 51 +++++++++++++++++++++++ packages/website/ts/pages/instant/features.tsx | 40 ++---------------- 2 files changed, 54 insertions(+), 37 deletions(-) create mode 100644 packages/website/ts/pages/instant/action_link.tsx diff --git a/packages/website/ts/pages/instant/action_link.tsx b/packages/website/ts/pages/instant/action_link.tsx new file mode 100644 index 000000000..8a0bf24ff --- /dev/null +++ b/packages/website/ts/pages/instant/action_link.tsx @@ -0,0 +1,51 @@ +import * as _ from 'lodash'; +import * as React from 'react'; + +import { Container } from 'ts/components/ui/container'; +import { Text } from 'ts/components/ui/text'; +import { colors } from 'ts/style/colors'; +import { utils } from 'ts/utils/utils'; + +export interface ActionLinkProps { + displayText: string; + linkSrc?: string; + onClick?: () => void; + fontSize?: number; + color?: string; + className?: string; +} + +export class ActionLink extends React.Component { + public static defaultProps = { + fontSize: 16, + color: colors.white, + }; + public render(): React.ReactNode { + const { displayText, fontSize, color, className } = this.props; + return ( + + + + {displayText} + + + + + + + ); + } + + private _handleClick = (event: React.MouseEvent) => { + if (!_.isUndefined(this.props.onClick)) { + this.props.onClick(); + } else if (!_.isUndefined(this.props.linkSrc)) { + utils.openUrl(this.props.linkSrc); + } + }; +} diff --git a/packages/website/ts/pages/instant/features.tsx b/packages/website/ts/pages/instant/features.tsx index 9c1668c1c..6c6835311 100644 --- a/packages/website/ts/pages/instant/features.tsx +++ b/packages/website/ts/pages/instant/features.tsx @@ -2,6 +2,7 @@ import * as _ from 'lodash'; import * as React from 'react'; import { Container } from 'ts/components/ui/container'; +import { ActionLink, ActionLinkProps } from 'ts/pages/instant/action_link'; import { Image } from 'ts/components/ui/image'; import { Text } from 'ts/components/ui/text'; import { colors } from 'ts/style/colors'; @@ -61,17 +62,11 @@ export const Features = (props: FeatureProps) => { ); }; -interface LinkInfo { - displayText: string; - linkSrc?: string; - onClick?: () => void; -} - interface FeatureItemProps { imgSrc: string; title: string; description: string; - linkInfos: LinkInfo[]; + linkInfos: ActionLinkProps[]; screenWidth: ScreenWidths; } @@ -95,36 +90,7 @@ const FeatureItem = (props: FeatureItemProps) => { - {_.map(linkInfos, linkInfo => { - const onClick = (event: React.MouseEvent) => { - if (!_.isUndefined(linkInfo.onClick)) { - linkInfo.onClick(); - } else if (!_.isUndefined(linkInfo.linkSrc)) { - utils.openUrl(linkInfo.linkSrc); - } - }; - return ( - - - - {linkInfo.displayText} - - - - - - - ); - })} + {_.map(linkInfos, linkInfo => )} ); -- cgit v1.2.3 From a41dfa9ae0924a342324e49d9a3eba22fb255667 Mon Sep 17 00:00:00 2001 From: fragosti Date: Thu, 29 Nov 2018 13:45:40 -0800 Subject: feat: implement configurator layout --- packages/website/ts/pages/instant/code_demo.tsx | 6 ++++- packages/website/ts/pages/instant/configurator.tsx | 27 +++++++++++++++++++--- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/packages/website/ts/pages/instant/code_demo.tsx b/packages/website/ts/pages/instant/code_demo.tsx index b029cc368..4f44a6160 100644 --- a/packages/website/ts/pages/instant/code_demo.tsx +++ b/packages/website/ts/pages/instant/code_demo.tsx @@ -9,6 +9,10 @@ import { Container } from 'ts/components/ui/container'; const CustomPre = styled.pre` margin: 0px; line-height: 24px; + overflow: hidden; + width: 600px; + height: 500px; + border-radius: 4px; code { background-color: inherit !important; border-radius: 0px; @@ -22,7 +26,7 @@ const CustomPre = styled.pre` text-align: center; padding-right: 5px !important; padding-left: 5px; - margin-right: 5px; + margin-right: 15px; line-height: 25px; padding-top: 10px; } diff --git a/packages/website/ts/pages/instant/configurator.tsx b/packages/website/ts/pages/instant/configurator.tsx index 09a51d0bb..29a3e2b1e 100644 --- a/packages/website/ts/pages/instant/configurator.tsx +++ b/packages/website/ts/pages/instant/configurator.tsx @@ -1,6 +1,8 @@ import * as React from 'react'; import { Container } from 'ts/components/ui/container'; +import { Text } from 'ts/components/ui/text'; +import { ActionLink } from 'ts/pages/instant/action_link'; import { CodeDemo } from 'ts/pages/instant/code_demo'; import { colors } from 'ts/style/colors'; @@ -9,8 +11,27 @@ export interface ConfiguratorProps { } export const Configurator = (props: ConfiguratorProps) => ( - - Forms - + + + + + 0x Instant Configurator + + + + + + + + Code Snippet + + + + + ); -- cgit v1.2.3 From 0af07bcf49f62aeeed4aa5ae7bf0e4d5835836e9 Mon Sep 17 00:00:00 2001 From: fragosti Date: Thu, 29 Nov 2018 14:10:12 -0800 Subject: feat: move instant base config to types file --- .../src/components/zero_ex_instant_provider.tsx | 20 ++------------------ packages/instant/src/types.ts | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx index e006a5a5f..a57dd7993 100644 --- a/packages/instant/src/components/zero_ex_instant_provider.tsx +++ b/packages/instant/src/components/zero_ex_instant_provider.tsx @@ -11,7 +11,7 @@ import { asyncData } from '../redux/async_data'; import { DEFAULT_STATE, DefaultState, State } from '../redux/reducer'; import { store, Store } from '../redux/store'; import { fonts } from '../style/fonts'; -import { AccountState, AffiliateInfo, AssetMetaData, Network, OrderSource } from '../types'; +import { AccountState, AffiliateInfo, AssetMetaData, Network, ZeroExInstantBaseConfig } from '../types'; import { analytics, disableAnalytics } from '../util/analytics'; import { assetUtils } from '../util/asset'; import { errorFlasher } from '../util/error_flasher'; @@ -20,23 +20,7 @@ import { Heartbeater } from '../util/heartbeater'; import { generateAccountHeartbeater, generateBuyQuoteHeartbeater } from '../util/heartbeater_factory'; import { providerStateFactory } from '../util/provider_state_factory'; -export type ZeroExInstantProviderProps = ZeroExInstantProviderRequiredProps & - Partial; - -export interface ZeroExInstantProviderRequiredProps { - orderSource: OrderSource; -} - -export interface ZeroExInstantProviderOptionalProps { - provider: Provider; - availableAssetDatas: string[]; - defaultAssetBuyAmount: number; - defaultSelectedAssetData: string; - additionalAssetMetaDataMap: ObjectMap; - networkId: Network; - affiliateInfo: AffiliateInfo; - shouldDisableAnalyticsTracking: boolean; -} +export type ZeroExInstantProviderProps = ZeroExInstantBaseConfig; export class ZeroExInstantProvider extends React.Component { private readonly _store: Store; diff --git a/packages/instant/src/types.ts b/packages/instant/src/types.ts index 999d50fed..233390bf5 100644 --- a/packages/instant/src/types.ts +++ b/packages/instant/src/types.ts @@ -167,3 +167,20 @@ export enum ProviderType { Cipher = 'CIPHER', Fallback = 'FALLBACK', } + +export interface ZeroExInstantRequiredBaseConfig { + orderSource: OrderSource; +} + +export interface ZeroExInstantOptionalBaseConfig { + provider: Provider; + availableAssetDatas: string[]; + defaultAssetBuyAmount: number; + defaultSelectedAssetData: string; + additionalAssetMetaDataMap: ObjectMap; + networkId: Network; + affiliateInfo: AffiliateInfo; + shouldDisableAnalyticsTracking: boolean; +} + +export type ZeroExInstantBaseConfig = ZeroExInstantRequiredBaseConfig & Partial; -- cgit v1.2.3 From f80768cae0c2fdb71237bbdddecc67aec1c1f67f Mon Sep 17 00:00:00 2001 From: fragosti Date: Thu, 29 Nov 2018 15:57:35 -0800 Subject: feat: add Select component and use for configurator --- packages/website/ts/components/ui/container.tsx | 33 +++- packages/website/ts/components/ui/select.tsx | 168 +++++++++++++++++++++ packages/website/ts/pages/instant/action_link.tsx | 2 +- packages/website/ts/pages/instant/code_demo.tsx | 3 - .../website/ts/pages/instant/config_generator.tsx | 38 +++++ packages/website/ts/pages/instant/configurator.tsx | 70 ++++++--- packages/website/ts/pages/instant/features.tsx | 2 +- 7 files changed, 285 insertions(+), 31 deletions(-) create mode 100644 packages/website/ts/components/ui/select.tsx create mode 100644 packages/website/ts/pages/instant/config_generator.tsx diff --git a/packages/website/ts/components/ui/container.tsx b/packages/website/ts/components/ui/container.tsx index 7eab2a50f..cd0ed9986 100644 --- a/packages/website/ts/components/ui/container.tsx +++ b/packages/website/ts/components/ui/container.tsx @@ -1,6 +1,9 @@ import { TextAlignProperty } from 'csstype'; +import { darken } from 'polished'; import * as React from 'react'; +import { styled } from 'ts/style/theme'; + type StringOrNum = string | number; export type ContainerTag = 'div' | 'span'; @@ -17,10 +20,13 @@ export interface ContainerProps { paddingLeft?: StringOrNum; backgroundColor?: string; background?: string; + border?: string; + borderTop?: string; borderRadius?: StringOrNum; borderBottomLeftRadius?: StringOrNum; borderBottomRightRadius?: StringOrNum; borderBottom?: StringOrNum; + borderColor?: string; maxWidth?: StringOrNum; maxHeight?: StringOrNum; width?: StringOrNum; @@ -42,10 +48,23 @@ export interface ContainerProps { id?: string; onClick?: (event: React.MouseEvent) => void; overflowX?: 'scroll' | 'hidden' | 'auto' | 'visible'; + shouldDarkenOnHover?: boolean; + shouldAddBoxShadowOnHover?: boolean; } -export const Container: React.StatelessComponent = props => { - const { children, className, Tag, isHidden, id, onClick, ...style } = props; +export const PlainContainer: React.StatelessComponent = props => { + const { + children, + className, + Tag, + isHidden, + id, + onClick, + shouldDarkenOnHover, + shouldAddBoxShadowOnHover, + // tslint:disable-next-line:trailing-comma + ...style + } = props; const visibility = isHidden ? 'hidden' : undefined; return ( @@ -54,6 +73,16 @@ export const Container: React.StatelessComponent = props => { ); }; +export const Container = styled(PlainContainer)` + &:hover { + ${props => + props.shouldDarkenOnHover + ? `background-color: ${props.backgroundColor ? darken(0.05, props.backgroundColor) : 'none'} !important` + : ''}; + ${props => (props.shouldAddBoxShadowOnHover ? 'box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1)' : '')}; + } +`; + Container.defaultProps = { Tag: 'div', }; diff --git a/packages/website/ts/components/ui/select.tsx b/packages/website/ts/components/ui/select.tsx new file mode 100644 index 000000000..abf202c67 --- /dev/null +++ b/packages/website/ts/components/ui/select.tsx @@ -0,0 +1,168 @@ +import { colors } from '@0x/react-shared'; +import * as _ from 'lodash'; +import * as React from 'react'; + +import { zIndex } from 'ts/style/z_index'; + +import { Container } from './container'; +import { Overlay } from './overlay'; +import { Text } from './text'; + +export interface SelectItemConfig { + text: string; + onClick?: () => void; +} + +export interface SelectProps { + value: string; + label?: string; + items: SelectItemConfig[]; + onOpen?: () => void; + border?: string; + fontSize?: string; + iconSize?: number; + textColor?: string; + labelColor?: string; + backgroundColor?: string; +} + +export interface SelectState { + isOpen: boolean; +} + +export class Select extends React.Component { + public static defaultProps = { + items: [] as SelectItemConfig[], + textColor: colors.black, + backgroundColor: colors.white, + fontSize: '16px', + iconSize: 25, + }; + public state: SelectState = { + isOpen: false, + }; + public render(): React.ReactNode { + const { value, label, items, border, textColor, labelColor, backgroundColor, fontSize, iconSize } = this.props; + const { isOpen } = this.state; + const hasItems = !_.isEmpty(items); + const borderRadius = isOpen ? '4px 4px 0px 0px' : '4px'; + return ( + + {isOpen && ( + + )} + + + + + {value} + + + {label && ( + + {label} + + )} + {hasItems && ( + + + + )} + + + + {isOpen && ( + + {_.map(items, (item, index) => ( + + ))} + + )} + + + ); + } + private readonly _handleDropdownClick = (): void => { + if (_.isEmpty(this.props.items)) { + return; + } + const isOpen = !this.state.isOpen; + this.setState({ + isOpen, + }); + + if (isOpen && this.props.onOpen) { + this.props.onOpen(); + } + }; + private readonly _closeDropdown = (): void => { + this.setState({ + isOpen: false, + }); + }; +} + +export interface SelectItemProps extends SelectItemConfig { + text: string; + onClick?: () => void; + isLast: boolean; + backgroundColor?: string; + border?: string; + textColor?: string; + fontSize?: string; +} + +export const SelectItem: React.StatelessComponent = ({ + text, + onClick, + isLast, + border, + backgroundColor, + textColor, + fontSize, +}) => ( + + + {text} + + +); diff --git a/packages/website/ts/pages/instant/action_link.tsx b/packages/website/ts/pages/instant/action_link.tsx index 8a0bf24ff..5612dc25b 100644 --- a/packages/website/ts/pages/instant/action_link.tsx +++ b/packages/website/ts/pages/instant/action_link.tsx @@ -41,7 +41,7 @@ export class ActionLink extends React.Component { ); } - private _handleClick = (event: React.MouseEvent) => { + private readonly _handleClick = (event: React.MouseEvent) => { if (!_.isUndefined(this.props.onClick)) { this.props.onClick(); } else if (!_.isUndefined(this.props.linkSrc)) { diff --git a/packages/website/ts/pages/instant/code_demo.tsx b/packages/website/ts/pages/instant/code_demo.tsx index 4f44a6160..e9ecad40d 100644 --- a/packages/website/ts/pages/instant/code_demo.tsx +++ b/packages/website/ts/pages/instant/code_demo.tsx @@ -1,11 +1,8 @@ import * as React from 'react'; import SyntaxHighlighter from 'react-syntax-highlighter'; -import { atelierCaveDark } from 'react-syntax-highlighter/styles/hljs'; import { colors } from 'ts/style/colors'; import { styled } from 'ts/style/theme'; -import { Container } from 'ts/components/ui/container'; - const CustomPre = styled.pre` margin: 0px; line-height: 24px; diff --git a/packages/website/ts/pages/instant/config_generator.tsx b/packages/website/ts/pages/instant/config_generator.tsx new file mode 100644 index 000000000..d63975e31 --- /dev/null +++ b/packages/website/ts/pages/instant/config_generator.tsx @@ -0,0 +1,38 @@ +import * as _ from 'lodash'; +import * as React from 'react'; + +import { Container } from 'ts/components/ui/container'; +import { Select, SelectItemConfig } from 'ts/components/ui/select'; + +import { ZeroExInstantBaseConfig } from '../../../../instant/src/types'; + +export interface ConfigGeneratorProps { + value: ZeroExInstantBaseConfig; + onConfigChange: (config: ZeroExInstantBaseConfig) => void; +} + +const SRA_ENDPOINTS = ['https://api.radarrelay.com/0x/v2/', 'https://api.openrelay.xyz/v2/']; + +export class ConfigGenerator extends React.Component { + public render(): React.ReactNode { + const { value } = this.props; + return ( + + + + + - + {this._renderTokenMultiSelectOrSpinner()} ); } + private readonly _getTokenSelectorProps = (): ConfigGeneratorSectionProps => { + if (_.isUndefined(this.props.value.availableAssetDatas)) { + return { + title: 'What tokens can users buy?', + actionText: 'Unselect All', + onActionTextClick: this._handleUnselectAllClick, + }; + } + return { + title: 'What tokens can users buy?', + actionText: 'Select All', + onActionTextClick: this._handleSelectAllClick, + }; + }; private readonly _generateItems = (): SelectItemConfig[] => { return _.map(SRA_ENDPOINTS, endpoint => ({ text: endpoint, onClick: this._handleSRASelection.bind(this, endpoint), })); }; + private readonly _getAllKnownAssetDatas = (): string[] => { + return _.map(this.state.allKnownTokens, token => assetDataUtils.encodeERC20AssetData(token.address)); + }; private readonly _handleSRASelection = (sraEndpoint: string) => { - const newConfig = { + const newConfig: ZeroExInstantBaseConfig = { ...this.props.value, orderSource: sraEndpoint, }; this.props.onConfigChange(newConfig); }; + private readonly _handleSelectAllClick = () => { + const newConfig: ZeroExInstantBaseConfig = { + ...this.props.value, + availableAssetDatas: undefined, + }; + this.props.onConfigChange(newConfig); + }; + private readonly _handleUnselectAllClick = () => { + const newConfig: ZeroExInstantBaseConfig = { + ...this.props.value, + availableAssetDatas: [], + }; + this.props.onConfigChange(newConfig); + }; private readonly _handleTokenClick = (assetData: string) => { const { value } = this.props; - let newAvailableAssetDatas = []; - if (_.includes(value.availableAssetDatas, assetData)) { + const { allKnownTokens } = this.state; + let newAvailableAssetDatas: string[] = []; + const availableAssetDatas = value.availableAssetDatas; + if (_.isUndefined(availableAssetDatas)) { + // It being undefined means it's all tokens. + const allKnownAssetDatas = this._getAllKnownAssetDatas(); + newAvailableAssetDatas = _.pull(allKnownAssetDatas, assetData); + } else if (!_.includes(availableAssetDatas, assetData)) { // Add it - newAvailableAssetDatas = [...value.availableAssetDatas, assetData]; + newAvailableAssetDatas = [...availableAssetDatas, assetData]; } else { // Remove it - newAvailableAssetDatas = _.remove(value.availableAssetDatas, assetData); + newAvailableAssetDatas = _.pull(availableAssetDatas, assetData); } - const newConfig = { + const newConfig: ZeroExInstantBaseConfig = { ...this.props.value, availableAssetDatas: newAvailableAssetDatas, }; this.props.onConfigChange(newConfig); }; - private _setAllKnownTokens = async (callback: () => void): Promise => { + private readonly _setAllKnownTokens = async (callback: () => void): Promise => { const tokenInfos = await backendClient.getTokenInfosAsync(); const allKnownTokens = _.reduce( tokenInfos, @@ -101,7 +139,7 @@ export class ConfigGenerator extends React.Component { ); this.setState({ allKnownTokens }, callback); }; - private _setAvailableAssetsFromOrderProvider = async (): Promise => { + private readonly _setAvailableAssetsFromOrderProvider = async (): Promise => { const { value } = this.props; if (!_.isUndefined(value.orderSource) && _.isString(value.orderSource)) { this.setState({ isLoadingAvailableTokens: true }); @@ -119,13 +157,23 @@ export class ConfigGenerator extends React.Component { this.setState({ availableTokens, isLoadingAvailableTokens: false }); } }; - private _renderTokenMultiSelectOrSpinner = (): React.ReactNode => { + private readonly _renderTokenMultiSelectOrSpinner = (): React.ReactNode => { const { value } = this.props; const { availableTokens, isLoadingAvailableTokens } = this.state; + const multiSelectHeight = '200px'; if (isLoadingAvailableTokens) { return ( - - + + + + + Loading... ); } @@ -133,15 +181,24 @@ export class ConfigGenerator extends React.Component { const assetData = assetDataUtils.encodeERC20AssetData(token.address); return { value: assetDataUtils.encodeERC20AssetData(token.address), - displayText: ( - - {token.symbol} - {token.name} - + renderItemContent: (isSelected: boolean) => ( + + + + + + {token.symbol} — {token.name} + + ), onClick: this._handleTokenClick.bind(this, assetData), }; }); - return ; + return ; }; } @@ -151,13 +208,23 @@ export interface ConfigGeneratorSectionProps { onActionTextClick?: () => void; } -export const ConfigGeneratorSection: React.StatelessComponent = props => ( +export const ConfigGeneratorSection: React.StatelessComponent = ({ + title, + actionText, + onActionTextClick, + children, +}) => ( - + - {props.title} + {title} + {actionText && ( + + {actionText} + + )} - {props.children} + {children} ); diff --git a/packages/website/ts/pages/instant/configurator.tsx b/packages/website/ts/pages/instant/configurator.tsx index cf9985675..f4987c0de 100644 --- a/packages/website/ts/pages/instant/configurator.tsx +++ b/packages/website/ts/pages/instant/configurator.tsx @@ -18,9 +18,10 @@ export interface ConfiguratorState { } export class Configurator extends React.Component { - public state = { + public state: ConfiguratorState = { instantConfig: { orderSource: 'https://api.radarrelay.com/0x/v2/', + availableAssetDatas: [], }, }; public render(): React.ReactNode { diff --git a/packages/website/ts/pages/instant/features.tsx b/packages/website/ts/pages/instant/features.tsx index 1e187c9da..c88958bbf 100644 --- a/packages/website/ts/pages/instant/features.tsx +++ b/packages/website/ts/pages/instant/features.tsx @@ -90,7 +90,11 @@ const FeatureItem = (props: FeatureItemProps) => { - {_.map(linkInfos, linkInfo => )} + {_.map(linkInfos, linkInfo => ( + + + + ))} ); -- cgit v1.2.3 From 9e69257b0db8ce959c0209d4389f9e3a753839bd Mon Sep 17 00:00:00 2001 From: fragosti Date: Fri, 30 Nov 2018 11:08:49 -0800 Subject: feat: implement code generation --- packages/website/ts/pages/instant/code_demo.tsx | 34 ++++++------------- packages/website/ts/pages/instant/configurator.tsx | 38 +++++++++++++++++++++- packages/website/ts/pages/instant/features.tsx | 4 +-- 3 files changed, 49 insertions(+), 27 deletions(-) diff --git a/packages/website/ts/pages/instant/code_demo.tsx b/packages/website/ts/pages/instant/code_demo.tsx index e9ecad40d..fa0cebc62 100644 --- a/packages/website/ts/pages/instant/code_demo.tsx +++ b/packages/website/ts/pages/instant/code_demo.tsx @@ -6,7 +6,7 @@ import { styled } from 'ts/style/theme'; const CustomPre = styled.pre` margin: 0px; line-height: 24px; - overflow: hidden; + overflow: scroll; width: 600px; height: 500px; border-radius: 4px; @@ -123,7 +123,7 @@ const customStyle = { }, hljs: { display: 'block', - overflowX: 'auto', + overflowX: 'hidden', background: colors.instantSecondaryBackground, color: 'white', fontSize: '12px', @@ -136,26 +136,12 @@ const customStyle = { }, }; -export interface CodeDemoProps {} +export interface CodeDemoProps { + children: string; +} -export const CodeDemo: React.StatelessComponent = props => { - const codeString = ` - - - - -`; - return ( - - {codeString} - - ); -}; +export const CodeDemo: React.StatelessComponent = props => ( + + {props.children} + +); diff --git a/packages/website/ts/pages/instant/configurator.tsx b/packages/website/ts/pages/instant/configurator.tsx index f4987c0de..0fb6d7ef8 100644 --- a/packages/website/ts/pages/instant/configurator.tsx +++ b/packages/website/ts/pages/instant/configurator.tsx @@ -1,3 +1,4 @@ +import * as _ from 'lodash'; import * as React from 'react'; import { Container } from 'ts/components/ui/container'; @@ -26,6 +27,7 @@ export class Configurator extends React.Component { }; public render(): React.ReactNode { const { hash } = this.props; + const codeToDisplay = this._generateCodeDemoCode(); return ( { - + {codeToDisplay} ); @@ -57,4 +59,38 @@ export class Configurator extends React.Component { instantConfig: config, }); }; + private readonly _generateCodeDemoCode = (): string => { + const { instantConfig } = this.state; + console.log(instantConfig.availableAssetDatas); + return ` + + + + + `; + }; + private readonly _renderAvailableAssetDatasString = (availableAssetDatas: string[]): string => { + const stringAvailableAssetDatas = availableAssetDatas.map(assetData => `'${assetData}'`); + if (availableAssetDatas.length < 2) { + return `[${stringAvailableAssetDatas.join(', ')}]`; + } + return `[\n\t\t\t${stringAvailableAssetDatas.join(', \n\t\t\t')}\n\t\t]`; + }; } diff --git a/packages/website/ts/pages/instant/features.tsx b/packages/website/ts/pages/instant/features.tsx index c88958bbf..230a8496b 100644 --- a/packages/website/ts/pages/instant/features.tsx +++ b/packages/website/ts/pages/instant/features.tsx @@ -91,8 +91,8 @@ const FeatureItem = (props: FeatureItemProps) => { {_.map(linkInfos, linkInfo => ( - - + + ))} -- cgit v1.2.3 From 4b6ac96a8db908b02a352357592dfef986e23fdc Mon Sep 17 00:00:00 2001 From: fragosti Date: Fri, 30 Nov 2018 16:01:16 -0800 Subject: fix: use correct styles even when syntax highlighter component removes classnames on second render --- packages/website/ts/pages/instant/code_demo.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/website/ts/pages/instant/code_demo.tsx b/packages/website/ts/pages/instant/code_demo.tsx index fa0cebc62..20eb6b8d1 100644 --- a/packages/website/ts/pages/instant/code_demo.tsx +++ b/packages/website/ts/pages/instant/code_demo.tsx @@ -8,7 +8,8 @@ const CustomPre = styled.pre` line-height: 24px; overflow: scroll; width: 600px; - height: 500px; + height: 100%; + max-height: 800px; border-radius: 4px; code { background-color: inherit !important; @@ -16,7 +17,7 @@ const CustomPre = styled.pre` font-family: 'Roboto Mono', sans-serif; border: none; } - .lsl { + code:first-of-type { background-color: #2a2a2a !important; color: #999; height: 100%; @@ -27,7 +28,7 @@ const CustomPre = styled.pre` line-height: 25px; padding-top: 10px; } - .xml { + code:last-of-type { position: relative; top: 10px; } -- cgit v1.2.3 From a192b309f339f451d647f4b9f71eac2d6684f738 Mon Sep 17 00:00:00 2001 From: fragosti Date: Mon, 3 Dec 2018 12:24:37 -0800 Subject: feat: implement basic feeRecipient address in config generator --- packages/website/ts/components/ui/input.tsx | 6 ++- .../website/ts/pages/instant/config_generator.tsx | 22 +++++++- .../instant/config_generator_address_input.tsx | 60 ++++++++++++++++++++++ packages/website/ts/pages/instant/configurator.tsx | 19 ++++--- 4 files changed, 96 insertions(+), 11 deletions(-) create mode 100644 packages/website/ts/pages/instant/config_generator_address_input.tsx diff --git a/packages/website/ts/components/ui/input.tsx b/packages/website/ts/components/ui/input.tsx index e5f4f6c70..1f56c814f 100644 --- a/packages/website/ts/components/ui/input.tsx +++ b/packages/website/ts/components/ui/input.tsx @@ -8,6 +8,7 @@ export interface InputProps { width?: string; fontSize?: string; fontColor?: string; + border?: string; placeholderColor?: string; placeholder?: string; backgroundColor?: string; @@ -23,9 +24,11 @@ export const Input = styled(PlainInput)` width: ${props => props.width}; padding: 0.8em 1.2em; border-radius: 3px; + box-sizing: border-box; font-family: 'Roboto Mono'; color: ${props => props.fontColor}; - border: none; + border: ${props => props.border}; + outline: none; background-color: ${props => props.backgroundColor}; &::placeholder { color: ${props => props.placeholderColor}; @@ -38,6 +41,7 @@ Input.defaultProps = { fontColor: colors.darkestGrey, placeholderColor: colors.darkGrey, fontSize: '12px', + border: 'none', }; Input.displayName = 'Input'; diff --git a/packages/website/ts/pages/instant/config_generator.tsx b/packages/website/ts/pages/instant/config_generator.tsx index d1ea66557..9c3161dcc 100644 --- a/packages/website/ts/pages/instant/config_generator.tsx +++ b/packages/website/ts/pages/instant/config_generator.tsx @@ -11,6 +11,7 @@ import { MultiSelect } from 'ts/components/ui/multi_select'; import { Select, SelectItemConfig } from 'ts/components/ui/select'; import { Spinner } from 'ts/components/ui/spinner'; import { Text } from 'ts/components/ui/text'; +import { ConfigGeneratorAddressInput } from 'ts/pages/instant/config_generator_address_input'; import { colors } from 'ts/style/colors'; import { WebsiteBackendTokenInfo } from 'ts/types'; import { backendClient } from 'ts/utils/backend_client'; @@ -32,7 +33,7 @@ export interface ConfigGeneratorState { const SRA_ENDPOINTS = ['https://api.radarrelay.com/0x/v2/', 'https://api.openrelay.xyz/v2/']; -export class ConfigGenerator extends React.Component { +export class ConfigGenerator extends React.Component { public state: ConfigGeneratorState = { isLoadingAvailableTokens: true, allKnownTokens: {}, @@ -52,12 +53,18 @@ export class ConfigGenerator extends React.Component { } return ( - + + {hasError && ( + + + {errMsg} + + + )} + + ); + } + + private _handleChange = (event: React.ChangeEvent): void => { + const address = event.target.value; + const isValidAddress = addressUtils.isAddress(address.toLowerCase()) || address === ''; + const errMsg = isValidAddress ? '' : 'Please enter a valid Ethereum address'; + this.setState({ + errMsg, + }); + this.props.onChange(address); + }; +} diff --git a/packages/website/ts/pages/instant/configurator.tsx b/packages/website/ts/pages/instant/configurator.tsx index 0fb6d7ef8..e72f07a70 100644 --- a/packages/website/ts/pages/instant/configurator.tsx +++ b/packages/website/ts/pages/instant/configurator.tsx @@ -23,6 +23,10 @@ export class Configurator extends React.Component { instantConfig: { orderSource: 'https://api.radarrelay.com/0x/v2/', availableAssetDatas: [], + affiliateInfo: { + feeRecipient: '', + feePercentage: 0.1, + }, }, }; public render(): React.ReactNode { @@ -61,7 +65,6 @@ export class Configurator extends React.Component { }; private readonly _generateCodeDemoCode = (): string => { const { instantConfig } = this.state; - console.log(instantConfig.availableAssetDatas); return ` @@ -69,18 +72,18 @@ export class Configurator extends React.Component { -- cgit v1.2.3 From de1c296d280266d71aeebcfc7050de639536513c Mon Sep 17 00:00:00 2001 From: fragosti Date: Mon, 3 Dec 2018 12:54:29 -0800 Subject: feat: add fee percentage slier --- .../website/ts/pages/instant/config_generator.tsx | 28 ++++++++++++++++++++++ .../instant/config_generator_address_input.tsx | 2 +- packages/website/ts/pages/instant/configurator.tsx | 2 +- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/packages/website/ts/pages/instant/config_generator.tsx b/packages/website/ts/pages/instant/config_generator.tsx index 9c3161dcc..8b56834ea 100644 --- a/packages/website/ts/pages/instant/config_generator.tsx +++ b/packages/website/ts/pages/instant/config_generator.tsx @@ -3,6 +3,7 @@ import { getContractAddressesForNetworkOrThrow } from '@0x/contract-addresses'; import { assetDataUtils } from '@0x/order-utils'; import { ObjectMap } from '@0x/types'; import * as _ from 'lodash'; +import Slider from 'material-ui/Slider'; import * as React from 'react'; import { CheckMark } from 'ts/components/ui/check_mark'; @@ -65,6 +66,19 @@ export class ConfigGenerator extends React.Component + + + ); } @@ -91,6 +105,9 @@ export class ConfigGenerator extends React.Component { return _.map(this.state.allKnownTokens, token => assetDataUtils.encodeERC20AssetData(token.address)); }; + private readonly _handleAffiliatePercentageLearnMoreClick = (): void => { + window.open('/wiki#Learn-About-Affiliate-Fees', '_blank'); + }; private readonly _handleSRASelection = (sraEndpoint: string) => { const newConfig: ZeroExInstantBaseConfig = { ...this.props.value, @@ -109,6 +126,17 @@ export class ConfigGenerator extends React.Component { + const oldConfig: ZeroExInstantBaseConfig = this.props.value; + const newConfig: ZeroExInstantBaseConfig = { + ...oldConfig, + affiliateInfo: { + feeRecipient: oldConfig.affiliateInfo.feeRecipient, + feePercentage: value, + }, + }; + this.props.onConfigChange(newConfig); + }; private readonly _handleSelectAllClick = () => { const newConfig: ZeroExInstantBaseConfig = { ...this.props.value, diff --git a/packages/website/ts/pages/instant/config_generator_address_input.tsx b/packages/website/ts/pages/instant/config_generator_address_input.tsx index dbda547cd..de2640f48 100644 --- a/packages/website/ts/pages/instant/config_generator_address_input.tsx +++ b/packages/website/ts/pages/instant/config_generator_address_input.tsx @@ -48,7 +48,7 @@ export class ConfigGeneratorAddressInput extends React.Component< ); } - private _handleChange = (event: React.ChangeEvent): void => { + private readonly _handleChange = (event: React.ChangeEvent): void => { const address = event.target.value; const isValidAddress = addressUtils.isAddress(address.toLowerCase()) || address === ''; const errMsg = isValidAddress ? '' : 'Please enter a valid Ethereum address'; diff --git a/packages/website/ts/pages/instant/configurator.tsx b/packages/website/ts/pages/instant/configurator.tsx index e72f07a70..0ce1d9810 100644 --- a/packages/website/ts/pages/instant/configurator.tsx +++ b/packages/website/ts/pages/instant/configurator.tsx @@ -25,7 +25,7 @@ export class Configurator extends React.Component { availableAssetDatas: [], affiliateInfo: { feeRecipient: '', - feePercentage: 0.1, + feePercentage: 0.01, }, }, }; -- cgit v1.2.3 From 50bfbda79a312651581f03614c1b4f4cbbe49cf1 Mon Sep 17 00:00:00 2001 From: fragosti Date: Mon, 3 Dec 2018 14:30:38 -0800 Subject: feat: add fee percentage slider --- packages/website/ts/pages/instant/code_demo.tsx | 2 +- .../website/ts/pages/instant/config_generator.tsx | 27 ++++++++++++++---- .../instant/config_generator_address_input.tsx | 14 ++++------ packages/website/ts/pages/instant/configurator.tsx | 32 +++++++++++----------- 4 files changed, 44 insertions(+), 31 deletions(-) diff --git a/packages/website/ts/pages/instant/code_demo.tsx b/packages/website/ts/pages/instant/code_demo.tsx index 20eb6b8d1..e57e39dff 100644 --- a/packages/website/ts/pages/instant/code_demo.tsx +++ b/packages/website/ts/pages/instant/code_demo.tsx @@ -20,7 +20,7 @@ const CustomPre = styled.pre` code:first-of-type { background-color: #2a2a2a !important; color: #999; - height: 100%; + min-height: 100%; text-align: center; padding-right: 5px !important; padding-left: 5px; diff --git a/packages/website/ts/pages/instant/config_generator.tsx b/packages/website/ts/pages/instant/config_generator.tsx index 8b56834ea..efd1be096 100644 --- a/packages/website/ts/pages/instant/config_generator.tsx +++ b/packages/website/ts/pages/instant/config_generator.tsx @@ -53,14 +53,14 @@ export class ConfigGenerator extends React.Component + - {hasError && ( - - - {errMsg} - - - )} + + + {errMsg} + + ); } diff --git a/packages/website/ts/pages/instant/configurator.tsx b/packages/website/ts/pages/instant/configurator.tsx index 0ce1d9810..5700bdc1d 100644 --- a/packages/website/ts/pages/instant/configurator.tsx +++ b/packages/website/ts/pages/instant/configurator.tsx @@ -46,7 +46,7 @@ export class Configurator extends React.Component { - + Code Snippet @@ -66,34 +66,34 @@ export class Configurator extends React.Component { private readonly _generateCodeDemoCode = (): string => { const { instantConfig } = this.state; return ` - - - - + + + - `; + }, 'body'); + +`; }; private readonly _renderAvailableAssetDatasString = (availableAssetDatas: string[]): string => { const stringAvailableAssetDatas = availableAssetDatas.map(assetData => `'${assetData}'`); if (availableAssetDatas.length < 2) { return `[${stringAvailableAssetDatas.join(', ')}]`; } - return `[\n\t\t\t${stringAvailableAssetDatas.join(', \n\t\t\t')}\n\t\t]`; + return `[\n\t\t${stringAvailableAssetDatas.join(', \n\t\t')}\n ]`; }; } -- cgit v1.2.3 From d8a3fc0be012ff88a851d2f388662b4aaaec5199 Mon Sep 17 00:00:00 2001 From: fragosti Date: Mon, 3 Dec 2018 15:07:24 -0800 Subject: feat: make (optional) label inline --- packages/website/ts/pages/instant/config_generator.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/website/ts/pages/instant/config_generator.tsx b/packages/website/ts/pages/instant/config_generator.tsx index efd1be096..08aaa1d81 100644 --- a/packages/website/ts/pages/instant/config_generator.tsx +++ b/packages/website/ts/pages/instant/config_generator.tsx @@ -267,11 +267,12 @@ export const ConfigGeneratorSection: React.StatelessComponent - + {title} {isOptional && ( - + + {' '} (optional) )} -- cgit v1.2.3 From 48bfe9274079be308df754f19dcd0030f014b93c Mon Sep 17 00:00:00 2001 From: fragosti Date: Mon, 3 Dec 2018 16:47:52 -0800 Subject: feat: create more dank slider --- packages/website/package.json | 2 + .../website/ts/pages/instant/config_generator.tsx | 9 +- .../ts/pages/instant/fee_percentage_slider.tsx | 81 ++++++++++++++ yarn.lock | 120 +++++++++++++++++++-- 4 files changed, 199 insertions(+), 13 deletions(-) create mode 100644 packages/website/ts/pages/instant/fee_percentage_slider.tsx diff --git a/packages/website/package.json b/packages/website/package.json index c02e1eee2..5d2e563e9 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -48,6 +48,7 @@ "numeral": "^2.0.6", "polished": "^1.9.2", "query-string": "^6.0.0", + "rc-slider": "^8.6.3", "react": "^16.4.2", "react-copy-to-clipboard": "^5.0.0", "react-document-title": "^2.0.3", @@ -80,6 +81,7 @@ "@types/node": "*", "@types/numeral": "^0.0.22", "@types/query-string": "^5.1.0", + "@types/rc-slider": "^8.6.0", "@types/react": "^16.4.2", "@types/react-copy-to-clipboard": "^4.2.0", "@types/react-dom": "^16.0.7", diff --git a/packages/website/ts/pages/instant/config_generator.tsx b/packages/website/ts/pages/instant/config_generator.tsx index 08aaa1d81..facda850e 100644 --- a/packages/website/ts/pages/instant/config_generator.tsx +++ b/packages/website/ts/pages/instant/config_generator.tsx @@ -3,7 +3,6 @@ import { getContractAddressesForNetworkOrThrow } from '@0x/contract-addresses'; import { assetDataUtils } from '@0x/order-utils'; import { ObjectMap } from '@0x/types'; import * as _ from 'lodash'; -import Slider from 'material-ui/Slider'; import * as React from 'react'; import { CheckMark } from 'ts/components/ui/check_mark'; @@ -13,6 +12,7 @@ import { Select, SelectItemConfig } from 'ts/components/ui/select'; import { Spinner } from 'ts/components/ui/spinner'; import { Text } from 'ts/components/ui/text'; import { ConfigGeneratorAddressInput } from 'ts/pages/instant/config_generator_address_input'; +import { FeePercentageSlider } from 'ts/pages/instant/fee_percentage_slider'; import { colors } from 'ts/style/colors'; import { WebsiteBackendTokenInfo } from 'ts/types'; import { backendClient } from 'ts/utils/backend_client'; @@ -71,10 +71,7 @@ export class ConfigGenerator extends React.Component - @@ -126,7 +123,7 @@ export class ConfigGenerator extends React.Component { + private readonly _handleAffiliatePercentageChange = (value: number) => { const oldConfig: ZeroExInstantBaseConfig = this.props.value; const newConfig: ZeroExInstantBaseConfig = { ...oldConfig, diff --git a/packages/website/ts/pages/instant/fee_percentage_slider.tsx b/packages/website/ts/pages/instant/fee_percentage_slider.tsx new file mode 100644 index 000000000..e57319ed5 --- /dev/null +++ b/packages/website/ts/pages/instant/fee_percentage_slider.tsx @@ -0,0 +1,81 @@ +import Slider from 'rc-slider'; +import 'rc-slider/assets/index.css'; +import * as React from 'react'; + +import { Text } from 'ts/components/ui/text'; +import { colors } from 'ts/style/colors'; +import { injectGlobal, styled } from 'ts/style/theme'; + +const SliderWithTooltip = (Slider as any).createSliderWithTooltip(Slider); + +injectGlobal` + .rc-slider-tooltip-inner { + box-shadow: none !important; + background-color: ${colors.white} !important; + border-radius: 4px !important; + padding: 3px 12px !important; + height: auto !important; + position: relative; + top: 7px; + &: after { + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; + border-color: rgba(136, 183, 213, 0); + border-width: 6px; + bottom: 100%; + left: 100%; + border-bottom-color: ${colors.white}; + margin-left: -60%; + } + } +`; + +const StyledSlider = styled(SliderWithTooltip)` + .rc-slider-tooltip-inner { + background-color: ${colors.white}; + box-shadow: none; + padding: 3px; + } +`; + +export interface FeePercentageSliderProps { + value: number; + onChange: (value: number) => void; +} + +export class FeePercentageSlider extends React.Component { + public render(): React.ReactNode { + return ( + + ); + } + private readonly _feePercentageSliderFormatter = (value: number): React.ReactNode => { + return {`${(value * 100).toFixed(2)}%`}; + }; +} diff --git a/yarn.lock b/yarn.lock index ed01009aa..c64f666e0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1457,6 +1457,19 @@ version "5.1.0" resolved "https://registry.yarnpkg.com/@types/query-string/-/query-string-5.1.0.tgz#7f40cdea49ddafa0ea4f3db35fb6c24d3bfd4dcc" +"@types/rc-slider@^8.6.0": + version "8.6.0" + resolved "https://registry.npmjs.org/@types/rc-slider/-/rc-slider-8.6.0.tgz#7f061a920b067825c8455cf481c57b0927889c72" + dependencies: + "@types/rc-tooltip" "*" + "@types/react" "*" + +"@types/rc-tooltip@*": + version "3.7.0" + resolved "https://registry.npmjs.org/@types/rc-tooltip/-/rc-tooltip-3.7.0.tgz#6dc9898dc426495baea1b786e5dbde8980bf9737" + dependencies: + "@types/react" "*" + "@types/react-addons-linked-state-mixin@*": version "0.14.19" resolved "https://registry.yarnpkg.com/@types/react-addons-linked-state-mixin/-/react-addons-linked-state-mixin-0.14.19.tgz#7ef00a5618a089da4a99e1f58c9aa4c1781d46d5" @@ -1898,6 +1911,12 @@ acorn@^6.0.2: version "6.0.4" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.0.4.tgz#77377e7353b72ec5104550aa2d2097a2fd40b754" +add-dom-event-listener@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/add-dom-event-listener/-/add-dom-event-listener-1.1.0.tgz#6a92db3a0dd0abc254e095c0f1dc14acbbaae310" + dependencies: + object-assign "4.x" + aes-js@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" @@ -2856,7 +2875,7 @@ babel-register@^6.26.0: mkdirp "^0.5.1" source-map-support "^0.4.15" -babel-runtime@6.x.x, babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.23.0, babel-runtime@^6.26.0: +babel-runtime@6.x, babel-runtime@6.x.x, babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.23.0, babel-runtime@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" dependencies: @@ -4147,10 +4166,20 @@ compare-versions@^3.0.1: version "3.1.0" resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.1.0.tgz#43310256a5c555aaed4193c04d8f154cf9c6efd5" +component-classes@^1.2.5: + version "1.2.6" + resolved "https://registry.npmjs.org/component-classes/-/component-classes-1.2.6.tgz#c642394c3618a4d8b0b8919efccbbd930e5cd691" + dependencies: + component-indexof "0.0.3" + component-emitter@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" +component-indexof@0.0.3: + version "0.0.3" + resolved "https://registry.npmjs.org/component-indexof/-/component-indexof-0.0.3.tgz#11d091312239eb8f32c8f25ae9cb002ffe8d3c24" + compressible@~2.0.13: version "2.0.13" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.13.tgz#0d1020ab924b2fdb4d6279875c7d6daba6baa7a9" @@ -4597,6 +4626,13 @@ crypto-random-string@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" +css-animation@^1.3.2: + version "1.5.0" + resolved "https://registry.npmjs.org/css-animation/-/css-animation-1.5.0.tgz#c96b9097a5ef74a7be8480b45cc44e4ec6ca2bf5" + dependencies: + babel-runtime "6.x" + component-classes "^1.2.5" + css-color-keywords@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05" @@ -5216,6 +5252,10 @@ doctrine@^2.1.0: dependencies: esutils "^2.0.2" +dom-align@^1.7.0: + version "1.8.0" + resolved "https://registry.npmjs.org/dom-align/-/dom-align-1.8.0.tgz#c0e89b5b674c6e836cd248c52c2992135f093654" + dom-helpers@^3.2.0, dom-helpers@^3.2.1, dom-helpers@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.3.1.tgz#fc1a4e15ffdf60ddde03a480a9c0fece821dd4a6" @@ -9767,7 +9807,7 @@ lodash.isequal@^4.0.0, lodash.isequal@^4.5.0: version "4.5.0" resolved "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" -lodash.keys@^3.0.0: +lodash.keys@^3.0.0, lodash.keys@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" dependencies: @@ -11091,14 +11131,14 @@ oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" +object-assign@4.x, object-assign@^4, object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + object-assign@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" -object-assign@^4, object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - object-copy@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" @@ -12231,7 +12271,7 @@ promzard@^0.3.0: dependencies: read "1" -prop-types@^15.5.0, prop-types@^15.5.10, prop-types@^15.6.2: +prop-types@15.x, prop-types@^15.5.0, prop-types@^15.5.10, prop-types@^15.6.2: version "15.6.2" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102" dependencies: @@ -12531,6 +12571,66 @@ raw-loader@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa" +rc-align@^2.4.0: + version "2.4.3" + resolved "https://registry.npmjs.org/rc-align/-/rc-align-2.4.3.tgz#b9b3c2a6d68adae71a8e1d041cd5e3b2a655f99a" + dependencies: + babel-runtime "^6.26.0" + dom-align "^1.7.0" + prop-types "^15.5.8" + rc-util "^4.0.4" + +rc-animate@2.x: + version "2.6.0" + resolved "https://registry.npmjs.org/rc-animate/-/rc-animate-2.6.0.tgz#ca8440d042781af7a1329d84f97ea94794c5ec15" + dependencies: + babel-runtime "6.x" + classnames "^2.2.6" + css-animation "^1.3.2" + prop-types "15.x" + raf "^3.4.0" + react-lifecycles-compat "^3.0.4" + +rc-slider@^8.6.3: + version "8.6.3" + resolved "https://registry.npmjs.org/rc-slider/-/rc-slider-8.6.3.tgz#1ca0e0bd2863252741de75e7bf8c9f2cfcffccb7" + dependencies: + babel-runtime "6.x" + classnames "^2.2.5" + prop-types "^15.5.4" + rc-tooltip "^3.7.0" + rc-util "^4.0.4" + shallowequal "^1.0.1" + warning "^3.0.0" + +rc-tooltip@^3.7.0: + version "3.7.3" + resolved "https://registry.npmjs.org/rc-tooltip/-/rc-tooltip-3.7.3.tgz#280aec6afcaa44e8dff0480fbaff9e87fc00aecc" + dependencies: + babel-runtime "6.x" + prop-types "^15.5.8" + rc-trigger "^2.2.2" + +rc-trigger@^2.2.2: + version "2.6.2" + resolved "https://registry.npmjs.org/rc-trigger/-/rc-trigger-2.6.2.tgz#a9c09ba5fad63af3b2ec46349c7db6cb46657001" + dependencies: + babel-runtime "6.x" + classnames "^2.2.6" + prop-types "15.x" + rc-align "^2.4.0" + rc-animate "2.x" + rc-util "^4.4.0" + +rc-util@^4.0.4, rc-util@^4.4.0: + version "4.6.0" + resolved "https://registry.npmjs.org/rc-util/-/rc-util-4.6.0.tgz#ba33721783192ec4f3afb259e182b04e55deb7f6" + dependencies: + add-dom-event-listener "^1.1.0" + babel-runtime "6.x" + prop-types "^15.5.10" + shallowequal "^0.2.2" + rc@^1.0.1, rc@^1.1.6, rc@^1.1.7: version "1.2.6" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.6.tgz#eb18989c6d4f4f162c399f79ddd29f3835568092" @@ -13907,6 +14007,12 @@ sha3@^1.1.0: dependencies: nan "2.10.0" +shallowequal@^0.2.2: + version "0.2.2" + resolved "https://registry.npmjs.org/shallowequal/-/shallowequal-0.2.2.tgz#1e32fd5bcab6ad688a4812cb0cc04efc75c7014e" + dependencies: + lodash.keys "^3.1.2" + shallowequal@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.0.2.tgz#1561dbdefb8c01408100319085764da3fcf83f8f" -- cgit v1.2.3 From 1ce84f9bb2b537b5cf5161fa5c5fe930d3981cd6 Mon Sep 17 00:00:00 2001 From: fragosti Date: Mon, 3 Dec 2018 16:51:55 -0800 Subject: fix: address to lowercase and explore the docs leads to correct wiki --- packages/website/ts/pages/instant/configurator.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/website/ts/pages/instant/configurator.tsx b/packages/website/ts/pages/instant/configurator.tsx index 5700bdc1d..725b02fae 100644 --- a/packages/website/ts/pages/instant/configurator.tsx +++ b/packages/website/ts/pages/instant/configurator.tsx @@ -51,7 +51,7 @@ export class Configurator extends React.Component { Code Snippet - + {codeToDisplay} @@ -74,7 +74,7 @@ export class Configurator extends React.Component { liquiditySource: '${instantConfig.orderSource}',${ !_.isUndefined(instantConfig.affiliateInfo) && instantConfig.affiliateInfo.feeRecipient ? `\n affiliateInfo: { - feeRecipient: '${instantConfig.affiliateInfo.feeRecipient}', + feeRecipient: '${instantConfig.affiliateInfo.feeRecipient.toLowerCase()}', feePercentage: ${instantConfig.affiliateInfo.feePercentage} }` : '' -- cgit v1.2.3 From db21ae3af1513f696b5c5ec0f4a1a3434cb4546d Mon Sep 17 00:00:00 2001 From: fragosti Date: Mon, 3 Dec 2018 17:29:31 -0800 Subject: feat: fetch asset meta data from the asset meta data map --- .../website/ts/pages/instant/config_generator.tsx | 54 +++++++++------------- 1 file changed, 23 insertions(+), 31 deletions(-) diff --git a/packages/website/ts/pages/instant/config_generator.tsx b/packages/website/ts/pages/instant/config_generator.tsx index facda850e..671b1f6df 100644 --- a/packages/website/ts/pages/instant/config_generator.tsx +++ b/packages/website/ts/pages/instant/config_generator.tsx @@ -18,7 +18,8 @@ import { WebsiteBackendTokenInfo } from 'ts/types'; import { backendClient } from 'ts/utils/backend_client'; import { constants } from 'ts/utils/constants'; -import { ZeroExInstantBaseConfig } from '../../../../instant/src/types'; +import { assetMetaDataMap } from '../../../../instant/src/data/asset_meta_data_map'; +import { ERC20AssetMetaData, ZeroExInstantBaseConfig } from '../../../../instant/src/types'; export interface ConfigGeneratorProps { value: ZeroExInstantBaseConfig; @@ -28,8 +29,7 @@ export interface ConfigGeneratorProps { export interface ConfigGeneratorState { isLoadingAvailableTokens: boolean; // Address to token info - allKnownTokens: ObjectMap; - availableTokens?: WebsiteBackendTokenInfo[]; + availableTokens?: ObjectMap; } const SRA_ENDPOINTS = ['https://api.radarrelay.com/0x/v2/', 'https://api.openrelay.xyz/v2/']; @@ -37,10 +37,9 @@ const SRA_ENDPOINTS = ['https://api.radarrelay.com/0x/v2/', 'https://api.openrel export class ConfigGenerator extends React.Component { public state: ConfigGeneratorState = { isLoadingAvailableTokens: true, - allKnownTokens: {}, }; public componentDidMount(): void { - this._setAllKnownTokens(this._setAvailableAssetsFromOrderProvider); + this._setAvailableAssetsFromOrderProvider(); } public componentDidUpdate(prevProps: ConfigGeneratorProps): void { if (prevProps.value.orderSource !== this.props.value.orderSource) { @@ -99,9 +98,6 @@ export class ConfigGenerator extends React.Component { - return _.map(this.state.allKnownTokens, token => assetDataUtils.encodeERC20AssetData(token.address)); - }; private readonly _handleAffiliatePercentageLearnMoreClick = (): void => { window.open('/wiki#Learn-About-Affiliate-Fees', '_blank'); }; @@ -150,16 +146,19 @@ export class ConfigGenerator extends React.Component { const { value } = this.props; - const { allKnownTokens } = this.state; let newAvailableAssetDatas: string[] = []; + const allKnownAssetDatas = _.keys(this.state.availableTokens); const availableAssetDatas = value.availableAssetDatas; if (_.isUndefined(availableAssetDatas)) { // It being undefined means it's all tokens. - const allKnownAssetDatas = this._getAllKnownAssetDatas(); newAvailableAssetDatas = _.pull(allKnownAssetDatas, assetData); } else if (!_.includes(availableAssetDatas, assetData)) { // Add it newAvailableAssetDatas = [...availableAssetDatas, assetData]; + if (newAvailableAssetDatas.length === allKnownAssetDatas.length) { + // If all tokens are manually selected, just show none. + newAvailableAssetDatas = undefined; + } } else { // Remove it newAvailableAssetDatas = _.pull(availableAssetDatas, assetData); @@ -170,18 +169,6 @@ export class ConfigGenerator extends React.Component void): Promise => { - const tokenInfos = await backendClient.getTokenInfosAsync(); - const allKnownTokens = _.reduce( - tokenInfos, - (acc, tokenInfo) => { - acc[tokenInfo.address] = tokenInfo; - return acc; - }, - {} as ObjectMap, - ); - this.setState({ allKnownTokens }, callback); - }; private readonly _setAvailableAssetsFromOrderProvider = async (): Promise => { const { value } = this.props; if (!_.isUndefined(value.orderSource) && _.isString(value.orderSource)) { @@ -191,11 +178,16 @@ export class ConfigGenerator extends React.Component { - const address = assetDataUtils.decodeAssetDataOrThrow(assetData).tokenAddress; - return this.state.allKnownTokens[address]; - }), + const availableTokens = _.reduce( + assetDatas, + (acc, assetData) => { + const metaDataIfExists = assetMetaDataMap[assetData] as ERC20AssetMetaData; + if (metaDataIfExists) { + acc[assetData] = metaDataIfExists; + } + return acc; + }, + {} as ObjectMap, ); this.setState({ availableTokens, isLoadingAvailableTokens: false }); } @@ -220,10 +212,10 @@ export class ConfigGenerator extends React.Component ); } - const items = _.map(availableTokens, token => { - const assetData = assetDataUtils.encodeERC20AssetData(token.address); + const items = _.map(_.keys(availableTokens), assetData => { + const metaData = availableTokens[assetData]; return { - value: assetDataUtils.encodeERC20AssetData(token.address), + value: assetData, renderItemContent: (isSelected: boolean) => ( @@ -234,7 +226,7 @@ export class ConfigGenerator extends React.Component - {token.symbol} — {token.name} + {metaData.symbol.toUpperCase()} — {metaData.name} ), -- cgit v1.2.3 From cd44dc7beb1697ec76dccba4711b3ce8057ba6e4 Mon Sep 17 00:00:00 2001 From: fragosti Date: Mon, 3 Dec 2018 17:37:32 -0800 Subject: polish: make configurator inputs look more like mocks --- packages/website/ts/components/ui/input.tsx | 4 +++- packages/website/ts/components/ui/select.tsx | 4 ++-- packages/website/ts/pages/instant/config_generator_address_input.tsx | 1 + 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/website/ts/components/ui/input.tsx b/packages/website/ts/components/ui/input.tsx index 1f56c814f..d21b9fd0e 100644 --- a/packages/website/ts/components/ui/input.tsx +++ b/packages/website/ts/components/ui/input.tsx @@ -9,6 +9,7 @@ export interface InputProps { fontSize?: string; fontColor?: string; border?: string; + padding?: string; placeholderColor?: string; placeholder?: string; backgroundColor?: string; @@ -22,7 +23,7 @@ const PlainInput: React.StatelessComponent = ({ value, className, pl export const Input = styled(PlainInput)` font-size: ${props => props.fontSize}; width: ${props => props.width}; - padding: 0.8em 1.2em; + padding: ${props => props.padding}; border-radius: 3px; box-sizing: border-box; font-family: 'Roboto Mono'; @@ -42,6 +43,7 @@ Input.defaultProps = { placeholderColor: colors.darkGrey, fontSize: '12px', border: 'none', + padding: '0.8em 1.2em', }; Input.displayName = 'Input'; diff --git a/packages/website/ts/components/ui/select.tsx b/packages/website/ts/components/ui/select.tsx index 743b082b0..e4fb50f59 100644 --- a/packages/website/ts/components/ui/select.tsx +++ b/packages/website/ts/components/ui/select.tsx @@ -65,7 +65,7 @@ export class Select extends React.Component { hasBoxShadow={isOpen} border={border} backgroundColor={backgroundColor} - padding="0.8em" + padding="0.5em 0.8em" width="100%" > @@ -79,7 +79,7 @@ export class Select extends React.Component { )} {hasItems && ( - + Date: Mon, 3 Dec 2018 17:52:35 -0800 Subject: feat: update relayer list --- .../website/ts/pages/instant/config_generator.tsx | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/website/ts/pages/instant/config_generator.tsx b/packages/website/ts/pages/instant/config_generator.tsx index 671b1f6df..4e25e3437 100644 --- a/packages/website/ts/pages/instant/config_generator.tsx +++ b/packages/website/ts/pages/instant/config_generator.tsx @@ -32,7 +32,11 @@ export interface ConfigGeneratorState { availableTokens?: ObjectMap; } -const SRA_ENDPOINTS = ['https://api.radarrelay.com/0x/v2/', 'https://api.openrelay.xyz/v2/']; +const SRA_ENDPOINTS = [ + 'https://api.radarrelay.com/0x/v2/', + 'https://api.sharkrelay.com/sra/v2/', + 'https://sra.bamboorelay.com/0x/v2/', +]; export class ConfigGenerator extends React.Component { public state: ConfigGeneratorState = { @@ -212,6 +216,20 @@ export class ConfigGenerator extends React.Component ); } + const availableAssetDatas = _.keys(availableTokens); + if (availableAssetDatas.length === 0) { + return ( + + No tokens available. Try another endpoint? + + ); + } const items = _.map(_.keys(availableTokens), assetData => { const metaData = availableTokens[assetData]; return { -- cgit v1.2.3 From ea4155e5fa69fe488eb183255f7ba64c9c07855b Mon Sep 17 00:00:00 2001 From: fragosti Date: Mon, 3 Dec 2018 18:00:30 -0800 Subject: fix: change liquiditySource to orderSource --- packages/website/ts/pages/instant/configurator.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/ts/pages/instant/configurator.tsx b/packages/website/ts/pages/instant/configurator.tsx index 725b02fae..9f1fa8359 100644 --- a/packages/website/ts/pages/instant/configurator.tsx +++ b/packages/website/ts/pages/instant/configurator.tsx @@ -71,7 +71,7 @@ export class Configurator extends React.Component { - - - + + + -`; + }, 'body'); + + +`; }; private readonly _renderAvailableAssetDatasString = (availableAssetDatas: string[]): string => { const stringAvailableAssetDatas = availableAssetDatas.map(assetData => `'${assetData}'`); if (availableAssetDatas.length < 2) { return `[${stringAvailableAssetDatas.join(', ')}]`; } - return `[\n\t\t${stringAvailableAssetDatas.join(', \n\t\t')}\n ]`; + return `[\n ${stringAvailableAssetDatas.join( + ', \n ', + )}\n ]`; }; } -- cgit v1.2.3 From f576f78b4a18e45ff2ed55ab840c0bacce87ad7c Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 4 Dec 2018 11:22:49 -0800 Subject: fix: do not show select all if no tokens are available --- packages/website/ts/pages/instant/config_generator.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/website/ts/pages/instant/config_generator.tsx b/packages/website/ts/pages/instant/config_generator.tsx index 4e25e3437..f2c9c698b 100644 --- a/packages/website/ts/pages/instant/config_generator.tsx +++ b/packages/website/ts/pages/instant/config_generator.tsx @@ -83,6 +83,11 @@ export class ConfigGenerator extends React.Component { + if (_.isEmpty(this.state.availableTokens)) { + return { + title: 'What tokens can users buy?', + }; + } if (_.isUndefined(this.props.value.availableAssetDatas)) { return { title: 'What tokens can users buy?', -- cgit v1.2.3 From b7d804e9491349ca2ec61b812e744e1739542edd Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 4 Dec 2018 11:26:51 -0800 Subject: fix: make select box-shadow darker --- packages/website/ts/components/ui/container.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/website/ts/components/ui/container.tsx b/packages/website/ts/components/ui/container.tsx index 4b76ce8be..c2ae9ad9c 100644 --- a/packages/website/ts/components/ui/container.tsx +++ b/packages/website/ts/components/ui/container.tsx @@ -77,15 +77,17 @@ export const PlainContainer: React.StatelessComponent = props => ); }; +const BOX_SHADOW = '0px 3px 10px rgba(0, 0, 0, 0.3)'; + export const Container = styled(PlainContainer)` box-sizing: border-box; - ${props => (props.hasBoxShadow ? `box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1)` : '')}; + ${props => (props.hasBoxShadow ? `box-shadow: ${BOX_SHADOW}` : '')}; &:hover { ${props => props.shouldDarkenOnHover ? `background-color: ${props.backgroundColor ? darken(0.05, props.backgroundColor) : 'none'} !important` : ''}; - ${props => (props.shouldAddBoxShadowOnHover ? 'box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1)' : '')}; + ${props => (props.shouldAddBoxShadowOnHover ? `box-shadow: ${BOX_SHADOW}` : '')}; } `; -- cgit v1.2.3 From 36f22ba069968ad50df9b6f1c2fda798dd5c95bb Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 4 Dec 2018 12:23:25 -0800 Subject: fix: reset available tokens when sra endpoint is changed --- packages/website/ts/pages/instant/config_generator.tsx | 7 ++++++- .../website/ts/pages/instant/config_generator_address_input.tsx | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/website/ts/pages/instant/config_generator.tsx b/packages/website/ts/pages/instant/config_generator.tsx index f2c9c698b..34c242348 100644 --- a/packages/website/ts/pages/instant/config_generator.tsx +++ b/packages/website/ts/pages/instant/config_generator.tsx @@ -48,6 +48,11 @@ export class ConfigGenerator extends React.Component { + private readonly _handleAffiliateAddressChange = (address: string, isValid: boolean) => { const oldConfig: ZeroExInstantBaseConfig = this.props.value; const newConfig: ZeroExInstantBaseConfig = { ...oldConfig, diff --git a/packages/website/ts/pages/instant/config_generator_address_input.tsx b/packages/website/ts/pages/instant/config_generator_address_input.tsx index c208fe52a..ccbaf4482 100644 --- a/packages/website/ts/pages/instant/config_generator_address_input.tsx +++ b/packages/website/ts/pages/instant/config_generator_address_input.tsx @@ -9,7 +9,7 @@ import { Text } from 'ts/components/ui/text'; export interface ConfigGeneratorAddressInputProps { value?: string; - onChange?: (address: string) => void; + onChange?: (address: string, isValid: boolean) => void; } export interface ConfigGeneratorAddressInputState { @@ -54,6 +54,6 @@ export class ConfigGeneratorAddressInput extends React.Component< this.setState({ errMsg, }); - this.props.onChange(address); + this.props.onChange(address, isValidAddress); }; } -- cgit v1.2.3 From 110d05f6457d2286c3a6d455d17745b1c8a2e0b4 Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 4 Dec 2018 12:43:39 -0800 Subject: fix: use WebsitePaths type and update wiki link to new link --- packages/website/ts/pages/instant/config_generator.tsx | 4 ++-- packages/website/ts/pages/instant/configurator.tsx | 7 ++++++- packages/website/ts/pages/instant/features.tsx | 8 ++++---- packages/website/ts/pages/instant/instant.tsx | 4 ++-- packages/website/ts/pages/instant/need_more.tsx | 4 ++-- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/packages/website/ts/pages/instant/config_generator.tsx b/packages/website/ts/pages/instant/config_generator.tsx index 34c242348..ac404718f 100644 --- a/packages/website/ts/pages/instant/config_generator.tsx +++ b/packages/website/ts/pages/instant/config_generator.tsx @@ -14,7 +14,7 @@ import { Text } from 'ts/components/ui/text'; import { ConfigGeneratorAddressInput } from 'ts/pages/instant/config_generator_address_input'; import { FeePercentageSlider } from 'ts/pages/instant/fee_percentage_slider'; import { colors } from 'ts/style/colors'; -import { WebsiteBackendTokenInfo } from 'ts/types'; +import { WebsitePaths } from 'ts/types'; import { backendClient } from 'ts/utils/backend_client'; import { constants } from 'ts/utils/constants'; @@ -113,7 +113,7 @@ export class ConfigGenerator extends React.Component { - window.open('/wiki#Learn-About-Affiliate-Fees', '_blank'); + window.open(`${WebsitePaths.Wiki}#Learn-About-Affiliate-Fees`, '_blank'); }; private readonly _handleSRASelection = (sraEndpoint: string) => { const newConfig: ZeroExInstantBaseConfig = { diff --git a/packages/website/ts/pages/instant/configurator.tsx b/packages/website/ts/pages/instant/configurator.tsx index e3766057e..d19a5b4fe 100644 --- a/packages/website/ts/pages/instant/configurator.tsx +++ b/packages/website/ts/pages/instant/configurator.tsx @@ -7,6 +7,7 @@ import { ActionLink } from 'ts/pages/instant/action_link'; import { CodeDemo } from 'ts/pages/instant/code_demo'; import { ConfigGenerator } from 'ts/pages/instant/config_generator'; import { colors } from 'ts/style/colors'; +import { WebsitePaths } from 'ts/types'; import { ZeroExInstantBaseConfig } from '../../../../instant/src/types'; @@ -51,7 +52,11 @@ export class Configurator extends React.Component { Code Snippet - + {codeToDisplay} diff --git a/packages/website/ts/pages/instant/features.tsx b/packages/website/ts/pages/instant/features.tsx index 230a8496b..6c2656021 100644 --- a/packages/website/ts/pages/instant/features.tsx +++ b/packages/website/ts/pages/instant/features.tsx @@ -6,7 +6,7 @@ import { Image } from 'ts/components/ui/image'; import { Text } from 'ts/components/ui/text'; import { ActionLink, ActionLinkProps } from 'ts/pages/instant/action_link'; import { colors } from 'ts/style/colors'; -import { ScreenWidths } from 'ts/types'; +import { ScreenWidths, WebsitePaths } from 'ts/types'; import { utils } from 'ts/utils/utils'; export interface FeatureProps { @@ -22,7 +22,7 @@ export const Features = (props: FeatureProps) => { }; const exploreTheDocsLinkInfo = { displayText: 'Explore the docs', - linkSrc: `${utils.getCurrentBaseUrl()}/wiki#Get-Started`, + linkSrc: `${WebsitePaths.Wiki}#Get-Started-With-Instant`, }; const tokenLinkInfos = isSmallScreen ? [getStartedLinkInfo] : [getStartedLinkInfo, exploreTheDocsLinkInfo]; return ( @@ -41,7 +41,7 @@ export const Features = (props: FeatureProps) => { linkInfos={[ { displayText: 'Learn about affiliate fees', - linkSrc: `${utils.getCurrentBaseUrl()}/wiki#Learn-About-Affiliate-Fees`, + linkSrc: `${WebsitePaths.Wiki}#Learn-About-Affiliate-Fees`, }, ]} screenWidth={props.screenWidth} @@ -53,7 +53,7 @@ export const Features = (props: FeatureProps) => { linkInfos={[ { displayText: 'Explore AssetBuyer', - linkSrc: `${utils.getCurrentBaseUrl()}/docs/asset-buyer`, + linkSrc: `${WebsitePaths.Docs}/asset-buyer`, }, ]} screenWidth={props.screenWidth} diff --git a/packages/website/ts/pages/instant/instant.tsx b/packages/website/ts/pages/instant/instant.tsx index fa6bd1c33..d72585bfa 100644 --- a/packages/website/ts/pages/instant/instant.tsx +++ b/packages/website/ts/pages/instant/instant.tsx @@ -14,7 +14,7 @@ import { NeedMore } from 'ts/pages/instant/need_more'; import { Screenshots } from 'ts/pages/instant/screenshots'; import { Dispatcher } from 'ts/redux/dispatcher'; import { colors } from 'ts/style/colors'; -import { ScreenWidths } from 'ts/types'; +import { ScreenWidths, WebsitePaths } from 'ts/types'; import { Translate } from 'ts/utils/translate'; import { utils } from 'ts/utils/utils'; @@ -67,7 +67,7 @@ export class Instant extends React.Component { } private readonly _onGetStartedClick = () => { if (this._isSmallScreen()) { - utils.openUrl(`${utils.getCurrentBaseUrl()}/wiki#Get-Started`); + utils.openUrl(`${WebsitePaths.Wiki}#Get-Started-With-Instant`); } else { this._scrollToConfigurator(); } diff --git a/packages/website/ts/pages/instant/need_more.tsx b/packages/website/ts/pages/instant/need_more.tsx index e6d5c3694..70aea7363 100644 --- a/packages/website/ts/pages/instant/need_more.tsx +++ b/packages/website/ts/pages/instant/need_more.tsx @@ -4,7 +4,7 @@ import { Button } from 'ts/components/ui/button'; import { Container } from 'ts/components/ui/container'; import { Text } from 'ts/components/ui/text'; import { colors } from 'ts/style/colors'; -import { ScreenWidths } from 'ts/types'; +import { ScreenWidths, WebsitePaths } from 'ts/types'; import { constants } from 'ts/utils/constants'; import { utils } from 'ts/utils/utils'; @@ -58,5 +58,5 @@ const onGetInTouchClick = () => { utils.openUrl(constants.URL_ZEROEX_CHAT); }; const onDocsClick = () => { - utils.openUrl(`${utils.getCurrentBaseUrl()}/wiki#Get-Started`); + utils.openUrl(`${WebsitePaths.Wiki}#Get-Started-With-Instant`); }; -- cgit v1.2.3 From 3e4b77757e3db5e79fc589475170d49a90c8375e Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 4 Dec 2018 12:49:37 -0800 Subject: hack: make min-height of line-number container 98% to prevent scrollbar from appearing in code demo --- packages/website/ts/pages/instant/code_demo.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/ts/pages/instant/code_demo.tsx b/packages/website/ts/pages/instant/code_demo.tsx index e57e39dff..4fe1928f5 100644 --- a/packages/website/ts/pages/instant/code_demo.tsx +++ b/packages/website/ts/pages/instant/code_demo.tsx @@ -20,7 +20,7 @@ const CustomPre = styled.pre` code:first-of-type { background-color: #2a2a2a !important; color: #999; - min-height: 100%; + min-height: 98%; text-align: center; padding-right: 5px !important; padding-left: 5px; -- cgit v1.2.3 From cb9f7a06646014511591bfb110c9013bb40ea667 Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 4 Dec 2018 12:53:09 -0800 Subject: chore: remove shark relay from possible SRA endpoints --- packages/website/ts/pages/instant/config_generator.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/website/ts/pages/instant/config_generator.tsx b/packages/website/ts/pages/instant/config_generator.tsx index ac404718f..b320d2600 100644 --- a/packages/website/ts/pages/instant/config_generator.tsx +++ b/packages/website/ts/pages/instant/config_generator.tsx @@ -32,11 +32,7 @@ export interface ConfigGeneratorState { availableTokens?: ObjectMap; } -const SRA_ENDPOINTS = [ - 'https://api.radarrelay.com/0x/v2/', - 'https://api.sharkrelay.com/sra/v2/', - 'https://sra.bamboorelay.com/0x/v2/', -]; +const SRA_ENDPOINTS = ['https://api.radarrelay.com/0x/v2/', 'https://sra.bamboorelay.com/0x/v2/']; export class ConfigGenerator extends React.Component { public state: ConfigGeneratorState = { -- cgit v1.2.3 From 01e1e5ac3b5ae970ade80efb5e71212ddb33eb93 Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 4 Dec 2018 12:59:16 -0800 Subject: chore: fix linter issues --- packages/website/ts/pages/instant/config_generator.tsx | 3 ++- packages/website/ts/pages/instant/features.tsx | 1 - packages/website/ts/pages/instant/fee_percentage_slider.tsx | 11 +---------- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/packages/website/ts/pages/instant/config_generator.tsx b/packages/website/ts/pages/instant/config_generator.tsx index b320d2600..fe70ef04c 100644 --- a/packages/website/ts/pages/instant/config_generator.tsx +++ b/packages/website/ts/pages/instant/config_generator.tsx @@ -15,7 +15,6 @@ import { ConfigGeneratorAddressInput } from 'ts/pages/instant/config_generator_a import { FeePercentageSlider } from 'ts/pages/instant/fee_percentage_slider'; import { colors } from 'ts/style/colors'; import { WebsitePaths } from 'ts/types'; -import { backendClient } from 'ts/utils/backend_client'; import { constants } from 'ts/utils/constants'; import { assetMetaDataMap } from '../../../../instant/src/data/asset_meta_data_map'; @@ -39,10 +38,12 @@ export class ConfigGenerator extends React.Component void; -- cgit v1.2.3 From 8ad114c5e585adb5afee2815b4afd0609830ebe3 Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 4 Dec 2018 14:11:28 -0800 Subject: fix: instant linting issues --- packages/instant/src/components/zero_ex_instant_provider.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx index bdc531617..8b75cdfc4 100644 --- a/packages/instant/src/components/zero_ex_instant_provider.tsx +++ b/packages/instant/src/components/zero_ex_instant_provider.tsx @@ -1,6 +1,4 @@ -import { ObjectMap } from '@0x/types'; import { BigNumber } from '@0x/utils'; -import { Provider } from 'ethereum-types'; import * as _ from 'lodash'; import * as React from 'react'; import { Provider as ReduxProvider } from 'react-redux'; @@ -13,8 +11,6 @@ import { store, Store } from '../redux/store'; import { fonts } from '../style/fonts'; import { AccountState, - AffiliateInfo, - AssetMetaData, Network, QuoteFetchOrigin, ZeroExInstantBaseConfig, -- cgit v1.2.3 From 57e7119c0d4f1ab7dd1d4c0118e72dc1706e2151 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Mon, 17 Sep 2018 11:27:38 -0700 Subject: Rebase pipeline branch off development --- packages/pipeline/.npmignore | 7 + packages/pipeline/README.md | 54 ++ packages/pipeline/package.json | 52 ++ packages/pipeline/src/global.d.ts | 6 + packages/pipeline/src/models/block.ts | 22 + packages/pipeline/src/models/event.ts | 84 +++ packages/pipeline/src/models/historical_prices.ts | 43 ++ packages/pipeline/src/models/order.ts | 30 + packages/pipeline/src/models/price.ts | 15 + packages/pipeline/src/models/relayer.ts | 75 +++ packages/pipeline/src/models/tokens.ts | 24 + packages/pipeline/src/models/transaction.ts | 36 ++ packages/pipeline/src/postgres.ts | 12 + packages/pipeline/src/run_jobs.ts | 87 +++ packages/pipeline/src/scrape_prices.ts | 10 + packages/pipeline/src/scripts/create_tables.ts | 258 ++++++++ packages/pipeline/src/scripts/join_tables.ts | 234 +++++++ packages/pipeline/src/scripts/query_data.ts | 87 +++ packages/pipeline/src/scripts/scrape_data.ts | 649 +++++++++++++++++++ packages/pipeline/src/types.ts | 6 + packages/pipeline/src/utils.ts | 181 ++++++ packages/pipeline/src/zrx.ts | 11 + packages/pipeline/tsconfig.json | 8 + packages/pipeline/tslint.json | 3 + packages/pipeline/typedoc-tsconfig.json | 7 + yarn.lock | 732 ++++++++++++++++++++-- 26 files changed, 2666 insertions(+), 67 deletions(-) create mode 100644 packages/pipeline/.npmignore create mode 100644 packages/pipeline/README.md create mode 100644 packages/pipeline/package.json create mode 100644 packages/pipeline/src/global.d.ts create mode 100644 packages/pipeline/src/models/block.ts create mode 100644 packages/pipeline/src/models/event.ts create mode 100644 packages/pipeline/src/models/historical_prices.ts create mode 100644 packages/pipeline/src/models/order.ts create mode 100644 packages/pipeline/src/models/price.ts create mode 100644 packages/pipeline/src/models/relayer.ts create mode 100644 packages/pipeline/src/models/tokens.ts create mode 100644 packages/pipeline/src/models/transaction.ts create mode 100644 packages/pipeline/src/postgres.ts create mode 100644 packages/pipeline/src/run_jobs.ts create mode 100644 packages/pipeline/src/scrape_prices.ts create mode 100644 packages/pipeline/src/scripts/create_tables.ts create mode 100644 packages/pipeline/src/scripts/join_tables.ts create mode 100644 packages/pipeline/src/scripts/query_data.ts create mode 100644 packages/pipeline/src/scripts/scrape_data.ts create mode 100644 packages/pipeline/src/types.ts create mode 100644 packages/pipeline/src/utils.ts create mode 100644 packages/pipeline/src/zrx.ts create mode 100644 packages/pipeline/tsconfig.json create mode 100644 packages/pipeline/tslint.json create mode 100644 packages/pipeline/typedoc-tsconfig.json diff --git a/packages/pipeline/.npmignore b/packages/pipeline/.npmignore new file mode 100644 index 000000000..89302c908 --- /dev/null +++ b/packages/pipeline/.npmignore @@ -0,0 +1,7 @@ +.* +yarn-error.log +/scripts/ +/generated_docs/ +/src/ +tsconfig.json +/lib/monorepo_scripts/ diff --git a/packages/pipeline/README.md b/packages/pipeline/README.md new file mode 100644 index 000000000..594454bd0 --- /dev/null +++ b/packages/pipeline/README.md @@ -0,0 +1,54 @@ +## @0xproject/pipeline + +This repository contains scripts used for scraping data from the Ethereum blockchain into SQL tables for analysis by the 0x team. + +## Contributing + +We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository. + +Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. + +## Local Dev Setup + +Requires Node version 6.9.5 or higher. + +Add the following to your `.env` file: + +``` +REDSHIFT_USER +REDSHIFT_DB +REDSHIFT_PASSWORD +REDSHIFT_PORT +REDSHIFT_HOST +WEB3_PROVIDER_URL +``` + +Running a script example: + +``` +node ./lib/scripts/scrape_data.js --type tokens +``` + +### Install dependencies: + +```bash +yarn install +``` + +### Build + +```bash +yarn build +``` + +### Clean + +```bash +yarn clean +``` + +### Lint + +```bash +yarn lint +``` diff --git a/packages/pipeline/package.json b/packages/pipeline/package.json new file mode 100644 index 000000000..9d47f69f3 --- /dev/null +++ b/packages/pipeline/package.json @@ -0,0 +1,52 @@ +{ + "name": "@0xproject/pipeline", + "version": "0.0.1", + "private": true, + "description": "Data pipeline for offline analysis", + "scripts": { + "build": "yarn tsc -b", + "test": "yarn run_mocha", + "rebuild_and_test": "run-s build test", + "test:circleci": "yarn test:coverage", + "run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js --bail --exit", + "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", + "coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info", + "clean": "shx rm -rf lib", + "lint": "tslint --project . --exclude **/src/generated_contract_wrappers/**/*" + }, + "repository": { + "type": "git", + "url": "https://github.com/0xProject/0x.js.git" + }, + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/0xProject/0x.js/issues" + }, + "homepage": "https://github.com/0xProject/0x.js/packages/pipeline/README.md", + "devDependencies": { + "@0xproject/tslint-config": "^1.0.7", + "@types/command-line-args": "^4.0.2", + "@types/dotenv": "^4.0.2", + "@types/glob": "^5.0.33", + "@types/lodash": "4.14.104", + "@types/node": "^8.0.53", + "@types/pg": "^7.4.1", + "@types/request": "^2.0.13", + "shx": "^0.2.2", + "tslint": "5.11.0", + "typescript": "3.0.1" + }, + "dependencies": { + "0x.js": "^0.38.3", + "@0xproject/connect": "^0.6.14", + "airtable": "^0.5.2", + "command-line-args": "^4.0.7", + "dotenv": "^4.0.0", + "lodash": "^4.17.4", + "node-redshift": "^0.1.5", + "pg": "^7.4.1", + "queue": "^4.4.2", + "request": "^2.83.0", + "web3": "^0.20.0" + } +} diff --git a/packages/pipeline/src/global.d.ts b/packages/pipeline/src/global.d.ts new file mode 100644 index 000000000..0af4ddb30 --- /dev/null +++ b/packages/pipeline/src/global.d.ts @@ -0,0 +1,6 @@ +declare module 'queue'; +declare module 'airtable'; +declare module '*.json' { + const value: any; + export default value; +} diff --git a/packages/pipeline/src/models/block.ts b/packages/pipeline/src/models/block.ts new file mode 100644 index 000000000..a81cdb293 --- /dev/null +++ b/packages/pipeline/src/models/block.ts @@ -0,0 +1,22 @@ +const block = { + tableName: 'blocks', + tableProperties: { + id: { + type: 'key', + }, + timestamp: { + type: 'timestamp', + required: true, + }, + block_number: { + type: 'bigint', + required: true, + }, + }, +}; +const logToBlockSchemaMapping: any = { + number: 'block_number', + hash: 'block_hash', + timestamp: 'timestamp', +}; +export { block, logToBlockSchemaMapping }; diff --git a/packages/pipeline/src/models/event.ts b/packages/pipeline/src/models/event.ts new file mode 100644 index 000000000..cb1c939e5 --- /dev/null +++ b/packages/pipeline/src/models/event.ts @@ -0,0 +1,84 @@ +const event = { + tableName: 'events', + tableProperties: { + id: { + type: 'key', + }, + timestamp: { + type: 'timestamp', + required: true, + }, + event_type: { + type: 'varchar', + required: true, + }, + error_id: { + type: 'varchar', + }, + order_hash: { + type: 'char(66)', + }, + maker: { + type: 'char(42)', + }, + maker_amount: { + type: 'varchar', + }, + maker_fee: { + type: 'varchar', + }, + maker_token: { + type: 'char(42)', + }, + taker_amount: { + type: 'varchar', + }, + taker_fee: { + type: 'varchar', + }, + taker_token: { + type: 'char(42)', + }, + txn_hash: { + type: 'char(66)', + }, + gas_used: { + type: 'varchar', + }, + gas_price: { + type: 'varchar', + }, + fee_recipient: { + type: 'char(42)', + }, + method_id: { + type: 'char(10)', + }, + salt: { + type: 'varchar', + }, + block_number: { + type: 'bigint', + }, + }, +}; +const logToEventSchemaMapping: any = { + blockNumber: 'block_number', + transactionHash: 'txn_hash', + event: 'event_type', + logIndex: 'log_index', + 'args.maker': 'maker', + 'args.taker': 'taker', + 'args.feeRecipient': 'fee_recipient', + 'args.makerToken': 'maker_token', + 'args.takerToken': 'taker_token', + 'args.filledMakerTokenAmount': 'maker_amount', + 'args.filledTakerTokenAmount': 'taker_amount', + 'args.paidMakerFee': 'maker_fee', + 'args.paidTakerFee': 'taker_fee', + 'args.orderHash': 'order_hash', + 'args.cancelledMakerTokenAmount': 'maker_amount', + 'args.cancelledTakerTokenAmount': 'taker_amount', + 'args.errorId': 'error_id', +}; +export { event, logToEventSchemaMapping }; diff --git a/packages/pipeline/src/models/historical_prices.ts b/packages/pipeline/src/models/historical_prices.ts new file mode 100644 index 000000000..cf49b579e --- /dev/null +++ b/packages/pipeline/src/models/historical_prices.ts @@ -0,0 +1,43 @@ +const historicalPrices = { + tableName: 'historical_prices', + tableProperties: { + token: { + type: 'varchar', + }, + base: { + type: 'varchar', + }, + timestamp: { + type: 'timestamp', + }, + close: { + type: 'numeric(50)', + }, + high: { + type: 'numeric(50)', + }, + low: { + type: 'numeric(50)', + }, + open: { + type: 'numeric(50)', + }, + volume_from: { + type: 'numeric(50)', + }, + volume_to: { + type: 'numeric(50)', + }, + }, +}; +const logToHistoricalPricesSchema: { [log: string]: string } = { + token: 'token', + time: 'timestamp', + close: 'close', + high: 'high', + low: 'low', + open: 'open', + volumefrom: 'volume_from', + volumeto: 'volume_to', +}; +export { historicalPrices, logToHistoricalPricesSchema }; diff --git a/packages/pipeline/src/models/order.ts b/packages/pipeline/src/models/order.ts new file mode 100644 index 000000000..dadae2883 --- /dev/null +++ b/packages/pipeline/src/models/order.ts @@ -0,0 +1,30 @@ +const order = { + tableName: 'orders', + tableProperties: { + id: { + type: 'key', + }, + timestamp: { + type: 'timestamp', + required: true, + }, + block_number: { + type: 'bigint', + required: true, + }, + }, +}; +const logToOrderSchemaMapping: any = { + exchangeContractAddress: 'exchange_contract_address', + maker: 'maker', + makerTokenAddress: 'maker_token', + makerTokenAmount: 'maker_amount', + makerFee: 'maker_fee', + taker: 'taker', + takerTokenAddress: 'taker_token', + takerTokenAmount: 'taker_amount', + takerFee: 'taker_fee', + expirationUnixTimestampSec: 'expiration_unix_timestamp_sec', + salt: 'salt', +}; +export { order, logToOrderSchemaMapping }; diff --git a/packages/pipeline/src/models/price.ts b/packages/pipeline/src/models/price.ts new file mode 100644 index 000000000..9e45cc2a9 --- /dev/null +++ b/packages/pipeline/src/models/price.ts @@ -0,0 +1,15 @@ +const price = { + tableName: 'prices', + tableProperties: { + address: { + type: 'char(42)', + }, + timestamp: { + type: 'timestamp', + }, + price: { + type: 'numeric(50)', + }, + }, +}; +export { price }; diff --git a/packages/pipeline/src/models/relayer.ts b/packages/pipeline/src/models/relayer.ts new file mode 100644 index 000000000..77a123e8b --- /dev/null +++ b/packages/pipeline/src/models/relayer.ts @@ -0,0 +1,75 @@ +// const relayer = { +// tableName: 'relayers', +// tableProperties: { +// id: { +// type: 'integer', +// }, +// name: { +// type: 'varchar', +// }, +// url : { +// type: 'varchar', +// }, +// model: { +// type: 'varchar[]', +// }, +// status: { +// type: 'varchar', +// }, +// sra_status: { +// type: 'varchar', +// }, +// sra_http_url: { +// type: 'varchar', +// }, +// known_fee_addresses: { +// type: 'char(42)[]', +// }, +// known_taker_addresses: { +// type: 'char(42)[]', +// }, +// relayer_type: { +// type: 'varchar', +// }, +// }, +// }; +const relayer = { + tableName: 'relayers', + tableProperties: { + name: { + type: 'varchar', + }, + url: { + type: 'varchar', + }, + sra_http_endpoint: { + type: 'varchar', + }, + sra_ws_endpoint: { + type: 'varchar', + }, + fee_recipient_addresses: { + type: 'char(42)[]', + }, + taker_addresses: { + type: 'char(42)[]', + }, + }, +}; +// const logToRelayerSchemaMapping: any = { +// 'id' : 'id', +// 'fields[\'Name\']': 'name', +// 'fields[\'URL\']': 'url', +// 'fields[\'Model\']': 'model', +// 'fields[\'Status\']': 'status', +// 'fields[\'SRA Status\']': 'sra_status', +// 'fields[\'SRA HTTP URL\']': 'sra_http_url', +// 'fields[\'Known Fee Addresses\']': 'known_fee_addresses', +// 'fields[\'Known Taker Addresses\']': 'known_taker_addresses', +// 'fields[\'Relayer Type\']': 'relayer_type', +// }; +const logToRelayerSchemaMapping: any = { + name: 'name', + homepage_url: 'url', +}; +export { relayer, logToRelayerSchemaMapping }; diff --git a/packages/pipeline/src/models/tokens.ts b/packages/pipeline/src/models/tokens.ts new file mode 100644 index 000000000..96e8a31af --- /dev/null +++ b/packages/pipeline/src/models/tokens.ts @@ -0,0 +1,24 @@ +const token = { + tableName: 'tokens', + tableProperties: { + address: { + type: 'char(66)', + }, + decimals: { + type: 'bigint', + }, + name: { + type: 'varchar', + }, + symbol: { + type: 'varchar', + }, + }, +}; +const logToTokenSchemaMapping: any = { + address: 'address', + decimals: 'decimals', + name: 'name', + symbol: 'symbol', +}; +export { token, logToTokenSchemaMapping }; diff --git a/packages/pipeline/src/models/transaction.ts b/packages/pipeline/src/models/transaction.ts new file mode 100644 index 000000000..715cc9480 --- /dev/null +++ b/packages/pipeline/src/models/transaction.ts @@ -0,0 +1,36 @@ +const transaction = { + tableName: 'transactions', + tableProperties: { + txn_hash: { + type: 'char(66)', + }, + block_hash: { + type: 'char(66)', + }, + block_number: { + type: 'bigint', + }, + gas_used: { + type: 'varchar', + }, + gas_price: { + type: 'varchar', + }, + method_id: { + type: 'char(10)', + }, + salt: { + type: 'varchar', + }, + }, +}; +const logToTransactionSchemaMapping: any = { + hash: 'txn_hash', + gas: 'gas_used', + gasPrice: 'gas_price', + blockHash: 'block_hash', + blockNumber: 'block_number', + method_id: 'method_id', + salt: 'salt', +}; +export { transaction, logToTransactionSchemaMapping }; diff --git a/packages/pipeline/src/postgres.ts b/packages/pipeline/src/postgres.ts new file mode 100644 index 000000000..d095e5c9e --- /dev/null +++ b/packages/pipeline/src/postgres.ts @@ -0,0 +1,12 @@ +import * as dotenv from 'dotenv'; +import { Pool, PoolConfig } from 'pg'; +dotenv.config(); +const client: PoolConfig = { + user: process.env.AURORA_USER, + database: process.env.AURORA_DB, + password: process.env.AURORA_PASSWORD, + port: parseInt(process.env.AURORA_PORT || '5432', 10), + host: process.env.AURORA_HOST, +}; +const postgresClient = new Pool(client); +export { postgresClient }; diff --git a/packages/pipeline/src/run_jobs.ts b/packages/pipeline/src/run_jobs.ts new file mode 100644 index 000000000..4d82d4e2d --- /dev/null +++ b/packages/pipeline/src/run_jobs.ts @@ -0,0 +1,87 @@ +import { exec } from 'child_process'; + +import { postgresClient } from './postgres.js'; +import { dataFetchingQueries } from './scripts/query_data.js'; +import { web3, zrx } from './zrx.js'; +const CUR_BLOCK_OFFSET = 20; +postgresClient.query(dataFetchingQueries.get_max_block, []).then((data: any) => { + const maxBlockNumber = data.rows[0].max; + const safeCurBlockNumber = web3.eth.blockNumber - CUR_BLOCK_OFFSET; + console.log('Scraping ' + maxBlockNumber + ' to ' + safeCurBlockNumber); + exec( + 'node ./lib/scripts/scrape_data --type events --from ' + maxBlockNumber + ' --to ' + safeCurBlockNumber, + (error, stdout, stderr) => { + if (error) { + console.log(error); + return; + } + console.log('Scraped events'); + console.log('Scraping blocks'); + exec( + 'node ./lib/scripts/scrape_data --type blocks --from ' + maxBlockNumber + ' --to ' + safeCurBlockNumber, + (error, stdout, stderr) => { + if (error) { + console.log(error); + return; + } + console.log('Scraped blocks'); + console.log('Scraping transactions'); + exec( + 'node ./lib/scripts/scrape_data --type transactions --from ' + + maxBlockNumber + + ' --to ' + + safeCurBlockNumber, + (error, stdout, stderr) => { + if (error) { + console.log(error); + return; + } + console.log('Scraped transactions'); + console.log('Joining events_staging'); + exec( + 'node ./lib/scripts/join_tables --name events_staging --from ' + + maxBlockNumber + + ' --to ' + + safeCurBlockNumber, + (error, stdout, stderr) => { + if (error) { + console.log(error); + return; + } + console.log('Joined events_staging'); + console.log('Joining events'); + exec( + 'node ./lib/scripts/join_tables --name events --from ' + + maxBlockNumber + + ' --to ' + + safeCurBlockNumber, + (error, stdout, stderr) => { + if (error) { + console.log(error); + return; + } + console.log('Joined events'); + console.log('Joining events_full'); + exec( + 'node ./lib/scripts/join_tables --name events_full --from ' + + maxBlockNumber + + ' --to ' + + safeCurBlockNumber, + (error, stdout, stderr) => { + if (error) { + console.log(error); + return; + } + }, + ); + }, + ); + }, + ); + }, + ); + }, + ); + }, + ); +}); diff --git a/packages/pipeline/src/scrape_prices.ts b/packages/pipeline/src/scrape_prices.ts new file mode 100644 index 000000000..c26062fac --- /dev/null +++ b/packages/pipeline/src/scrape_prices.ts @@ -0,0 +1,10 @@ +import { postgresClient } from './postgres.js'; +import { dataFetchingQueries } from './scripts/query_data.js'; +import { scrapeDataScripts } from './scripts/scrape_data.js'; +import { web3, zrx } from './zrx.js'; +const CUR_BLOCK_OFFSET = 20; +postgresClient.query(dataFetchingQueries.get_most_recent_pricing_date, []).then((data: any) => { + const curMaxScrapedDate = new Date(data.rows[0].max); + const curDate = new Date(); + scrapeDataScripts.scrapeAllPricesToDB(curMaxScrapedDate.getTime(), curDate.getTime()); +}); diff --git a/packages/pipeline/src/scripts/create_tables.ts b/packages/pipeline/src/scripts/create_tables.ts new file mode 100644 index 000000000..fd0d2b78b --- /dev/null +++ b/packages/pipeline/src/scripts/create_tables.ts @@ -0,0 +1,258 @@ +import * as commandLineArgs from 'command-line-args'; + +import { postgresClient } from '../postgres'; +import { formatters } from '../utils'; +const tableQueries: any = { + events_full: `CREATE TABLE IF NOT EXISTS events_full ( + timestamp TIMESTAMP WITH TIME ZONE, + event_type VARCHAR, + error_id VARCHAR, + order_hash CHAR(66), + maker CHAR(42), + maker_amount NUMERIC(78), + maker_fee NUMERIC(78), + maker_token CHAR(42), + taker CHAR(42), + taker_amount NUMERIC(78), + taker_fee NUMERIC(78), + taker_token CHAR(42), + txn_hash CHAR(66), + gas_used NUMERIC(78), + gas_price NUMERIC(78), + fee_recipient CHAR(42), + method_id CHAR(10), + salt VARCHAR, + block_number BIGINT, + log_index BIGINT, + taker_symbol VARCHAR, + taker_name VARCHAR, + taker_decimals BIGINT, + taker_usd_price NUMERIC(78), + taker_txn_usd_value NUMERIC(78), + maker_symbol VARCHAR, + maker_name VARCHAR, + maker_decimals BIGINT, + maker_usd_price NUMERIC(78), + maker_txn_usd_value NUMERIC(78), + PRIMARY KEY (txn_hash, order_hash, log_index) + )`, + events: `CREATE TABLE IF NOT EXISTS events ( + timestamp TIMESTAMP WITH TIME ZONE, + event_type VARCHAR, + error_id VARCHAR, + order_hash CHAR(66), + maker CHAR(42), + maker_amount NUMERIC(78), + maker_fee NUMERIC(78), + maker_token CHAR(42), + taker CHAR(42), + taker_amount NUMERIC(78), + taker_fee NUMERIC(78), + taker_token CHAR(42), + txn_hash CHAR(66), + gas_used NUMERIC(78), + gas_price NUMERIC(78), + fee_recipient CHAR(42), + method_id CHAR(10), + salt VARCHAR, + block_number BIGINT, + log_index BIGINT, + PRIMARY KEY (txn_hash, order_hash, log_index) + )`, + events_staging: `CREATE TABLE IF NOT EXISTS events_staging ( + timestamp TIMESTAMP WITH TIME ZONE, + event_type VARCHAR, + error_id VARCHAR, + order_hash CHAR(66), + maker CHAR(42), + maker_amount NUMERIC(78), + maker_fee NUMERIC(78), + maker_token CHAR(42), + taker CHAR(42), + taker_amount NUMERIC(78), + taker_fee NUMERIC(78), + taker_token CHAR(42), + txn_hash CHAR(66), + fee_recipient CHAR(42), + block_number BIGINT, + log_index BIGINT, + PRIMARY KEY (txn_hash, order_hash, log_index) + )`, + events_raw: `CREATE TABLE IF NOT EXISTS events_raw ( + event_type VARCHAR, + error_id VARCHAR, + order_hash CHAR(66), + maker CHAR(42), + maker_amount NUMERIC(78), + maker_fee NUMERIC(78), + maker_token CHAR(42), + taker CHAR(42), + taker_amount NUMERIC(78), + taker_fee NUMERIC(78), + taker_token CHAR(42), + txn_hash CHAR(66), + fee_recipient CHAR(42), + block_number BIGINT, + log_index BIGINT, + PRIMARY KEY (txn_hash, order_hash, log_index) + )`, + blocks: `CREATE TABLE IF NOT EXISTS blocks ( + timestamp TIMESTAMP WITH TIME ZONE, + block_hash CHAR(66) UNIQUE, + block_number BIGINT, + PRIMARY KEY (block_hash) + )`, + transactions: `CREATE TABLE IF NOT EXISTS transactions ( + txn_hash CHAR(66) UNIQUE, + block_hash CHAR(66), + block_number BIGINT, + gas_used NUMERIC(78), + gas_price NUMERIC(78), + method_id CHAR(10), + salt VARCHAR, + PRIMARY KEY (txn_hash) + )`, + tokens: `CREATE TABLE IF NOT EXISTS tokens ( + address CHAR(42) UNIQUE, + name VARCHAR, + symbol VARCHAR, + decimals INT, + PRIMARY KEY (address) + )`, + prices: `CREATE TABLE IF NOT EXISTS prices ( + address CHAR(42) UNIQUE, + timestamp TIMESTAMP WITH TIME ZONE, + price NUMERIC(78, 18), + PRIMARY KEY (address, timestamp) + )`, + relayers: `CREATE TABLE IF NOT EXISTS relayers ( + name VARCHAR UNIQUE, + url VARCHAR DEFAULT '', + sra_http_endpoint VARCHAR DEFAULT '', + sra_ws_endpoint VARCHAR DEFAULT '', + fee_recipient_addresses CHAR(42)[] DEFAULT '{}', + taker_addresses CHAR(42)[] DEFAULT '{}', + PRIMARY KEY(name)`, + historical_prices: `CREATE TABLE IF NOT EXISTS historical_prices ( + token VARCHAR, + base VARCHAR, + timestamp TIMESTAMP WITH TIME ZONE, + close NUMERIC(78, 18), + high NUMERIC(78, 18), + low NUMERIC(78, 18), + open NUMERIC(78, 18), + volume_from NUMERIC(78, 18), + volume_to NUMERIC(78, 18), + PRIMARY KEY (token, base, timestamp) + )`, + orders: `CREATE TABLE IF NOT EXISTS orders ( + relayer_id VARCHAR, + exchange_contract_address CHAR(42), + maker CHAR(42), + maker_amount NUMERIC(78), + maker_fee NUMERIC(78), + maker_token CHAR(42), + taker CHAR(42), + taker_amount NUMERIC(78), + taker_fee NUMERIC(78), + taker_token CHAR(42), + fee_recipient CHAR(42), + expiration_unix_timestamp_sec NUMERIC(78), + salt VARCHAR, + order_hash CHAR(66), + PRIMARY KEY (relayer_id, order_hash) + )`, +}; +function _safeQuery(query: string): any { + return new Promise((resolve, reject) => { + postgresClient + .query(query) + .then((data: any) => { + resolve(data); + }) + .catch((err: any) => { + reject(err); + }); + }); +} +export const tableScripts = { + createTable(query: string): any { + return _safeQuery(query); + }, + createAllTables(): any { + for (const tableName of tableQueries) { + _safeQuery(tableQueries[tableName]); + } + }, +}; +export const insertDataScripts = { + insertSingleRow(table: string, object: any): any { + return new Promise((resolve, reject) => { + const columns = Object.keys(object); + const safeArray: any = []; + for (const key of columns) { + if (key in object) { + if (key === 'timestamp') { + safeArray.push('to_timestamp(' + object[key] + ')'); + } else if (typeof object[key] === 'string' || object[key] instanceof String) { + safeArray.push(formatters.escapeSQLParam(object[key])); + } else { + safeArray.push(object[key]); + } + } else { + safeArray.push('default'); + } + } + const queryString = `INSERT INTO ${table} (${columns}) VALUES (${safeArray}) ON CONFLICT DO NOTHING`; + console.log(queryString); + postgresClient + .query(queryString) + .then((data: any) => { + resolve(data); + }) + .catch((err: any) => { + reject(err); + }); + }); + }, + insertMultipleRows(table: string, rows: any[], columns: any[]): any { + return new Promise((resolve, reject) => { + if (rows.length > 0) { + const rowsSplit = rows.map((value, index) => { + const safeArray: any = []; + for (const key of columns) { + if (key in value) { + if (key === 'timestamp') { + safeArray.push('to_timestamp(' + value[key] + ')'); + } else if (typeof value[key] === 'string' || value[key] instanceof String) { + safeArray.push(formatters.escapeSQLParam(value[key])); + } else if (value[key] instanceof Array) { + const escapedArray = value[key].map((subValue: string, subIndex: number) => { + return formatters.escapeSQLParam(subValue); + }); + safeArray.push('ARRAY[' + escapedArray.toString() + ']'); + } else { + safeArray.push(value[key]); + } + } else { + safeArray.push('default'); + } + } + return '(' + safeArray + ')'; + }); + const queryString = `INSERT INTO ${table} (${columns}) VALUES ${rowsSplit} ON CONFLICT DO NOTHING`; + postgresClient + .query(queryString) + .then((data: any) => { + resolve(data); + }) + .catch((err: any) => { + // console.log(err); + reject(err); + }); + } else { + resolve({}); + } + }); + }, +}; diff --git a/packages/pipeline/src/scripts/join_tables.ts b/packages/pipeline/src/scripts/join_tables.ts new file mode 100644 index 000000000..e7c05b39a --- /dev/null +++ b/packages/pipeline/src/scripts/join_tables.ts @@ -0,0 +1,234 @@ +import * as commandLineArgs from 'command-line-args'; + +import { postgresClient } from '../postgres'; +import { formatters } from '../utils'; +const optionDefinitions = [ + { name: 'name', alias: 'n', type: String }, + { name: 'from', alias: 'f', type: Number }, + { name: 'to', alias: 't', type: Number }, +]; +const cli = commandLineArgs(optionDefinitions); +const dataInsertionQueries: any = { + events_staging: `INSERT INTO events_staging ( + timestamp, + event_type, + error_id, + order_hash, + maker, + maker_amount, + maker_fee, + maker_token, + taker, + taker_amount, + taker_fee, + taker_token, + txn_hash, + fee_recipient, + block_number, + log_index + ) + (SELECT + b.timestamp, + a.event_type, + a.error_id, + a.order_hash, + a.maker, + a.maker_amount, + a.maker_fee, + a.maker_token, + a.taker, + a.taker_amount, + a.taker_fee, + a.taker_token, + a.txn_hash, + a.fee_recipient, + a.block_number, + a.log_index + FROM + events_raw a + JOIN + blocks b + ON + a.block_number = b.block_number + AND + b.block_number >= $1 + AND + b.block_number <= $2 + ) ON CONFLICT (order_hash, txn_hash, log_index) DO NOTHING`, + events: `INSERT INTO events ( + timestamp, + event_type, + error_id, + order_hash, + maker, + maker_amount, + maker_fee, + maker_token, + taker, + taker_amount, + taker_fee, + taker_token, + txn_hash, + fee_recipient, + block_number, + log_index, + gas_used, + gas_price, + method_id, + salt + ) + (SELECT + a.timestamp, + a.event_type, + a.error_id, + a.order_hash, + a.maker, + a.maker_amount, + a.maker_fee, + a.maker_token, + a.taker, + a.taker_amount, + a.taker_fee, + a.taker_token, + a.txn_hash, + a.fee_recipient, + a.block_number, + a.log_index, + b.gas_used, + b.gas_price, + b.method_id, + b.salt + FROM + events_staging a + JOIN + transactions b + ON + a.txn_hash = b.txn_hash + AND + a.block_number >= $1 + AND + a.block_number <= $2 + ) ON CONFLICT (order_hash, txn_hash, log_index) DO NOTHING`, + events_full: ` + INSERT INTO events_full ( + timestamp, + event_type, + error_id, + order_hash, + maker, + maker_amount, + maker_fee, + maker_token, + taker, + taker_amount, + taker_fee, + taker_token, + txn_hash, + fee_recipient, + block_number, + log_index, + gas_used, + gas_price, + method_id, + salt, + taker_symbol, + taker_name, + taker_decimals, + taker_usd_price, + taker_txn_usd_value, + maker_symbol, + maker_name, + maker_decimals, + maker_usd_price, + maker_txn_usd_value + ) + (SELECT + events.timestamp, + events.event_type, + events.error_id, + events.order_hash, + events.maker, + events.maker_amount, + events.maker_fee, + events.maker_token, + events.taker, + events.taker_amount, + events.taker_fee, + events.taker_token, + events.txn_hash, + events.fee_recipient, + events.block_number, + events.log_index, + events.gas_used, + events.gas_price, + events.method_id, + events.salt, + taker_token_prices.symbol, + taker_token_prices.name, + taker_token_prices.decimals, + taker_token_prices.price, + (events.taker_amount / (10 ^ taker_token_prices.decimals)) * taker_token_prices.price, + maker_token_prices.symbol, + maker_token_prices.name, + maker_token_prices.decimals, + maker_token_prices.price, + (events.maker_amount / (10 ^ maker_token_prices.decimals)) * maker_token_prices.price + FROM + events + LEFT JOIN + (SELECT + tokens.address, + tokens.name, + tokens.symbol, + tokens.decimals, + prices.timestamp, + prices.price + FROM + tokens + LEFT JOIN + prices + ON + tokens.symbol = prices.symbol) taker_token_prices + ON + (events.taker_token = taker_token_prices.address + AND + (DATE(events.timestamp) = DATE(taker_token_prices.timestamp) OR taker_token_prices.timestamp IS NULL)) + LEFT JOIN + (SELECT + tokens.address, + tokens.name, + tokens.symbol, + tokens.decimals, + prices.timestamp, + prices.price + FROM + tokens + LEFT JOIN + prices + ON + tokens.symbol = prices.symbol) maker_token_prices + ON + (events.maker_token = maker_token_prices.address + AND + (DATE(events.timestamp) = DATE(maker_token_prices.timestamp) OR maker_token_prices.timestamp IS NULL)) + WHERE + events.block_number >= $1 + AND + events.block_number <= $2 + ) ON CONFLICT (order_hash, txn_hash, log_index) DO NOTHING`, +}; +if (cli.name) { + const query = dataInsertionQueries[cli.name]; + if (query && cli.from) { + const fromBlock = cli.from; + const toBlock = cli.to ? cli.to : cli.from + 1; + postgresClient + .query(query, [fromBlock, toBlock]) + .then((data: any) => { + console.log(data); + }) + .catch((err: any) => { + console.error(err); + }); + } +} diff --git a/packages/pipeline/src/scripts/query_data.ts b/packages/pipeline/src/scripts/query_data.ts new file mode 100644 index 000000000..97e3749ea --- /dev/null +++ b/packages/pipeline/src/scripts/query_data.ts @@ -0,0 +1,87 @@ +import { formatters } from '../utils'; +export const dataFetchingQueries: any = { + get_missing_txn_hashes: ` + SELECT + a.txn_hash + FROM + events_raw a + WHERE NOT EXISTS + ( + SELECT + * + FROM + transactions b + WHERE + b.txn_hash = a.txn_hash + ) + AND + a.block_number >= $1 + AND + a.block_number < $2`, + get_used_block_numbers: ` + SELECT DISTINCT + a.block_number + FROM + events_raw a + WHERE NOT EXISTS + ( + SELECT + * + FROM + blocks b + WHERE + b.block_number = a.block_number + ) + AND + a.block_number >= $1 + AND + a.block_number < $2`, + get_token_registry: ` + SELECT + * + FROM + tokens`, + get_max_block: ` + SELECT + MAX(block_number) + FROM + events_raw`, + get_relayers: ` + SELECT + * + FROM + relayers`, + get_most_recent_pricing_date: ` + SELECT + MAX(DATE(timestamp)) + FROM + prices + `, + get_top_unknown_token_addresses: ` + SELECT a.token_address as address, a.txn_value / 2 as total_txn_value +FROM +(SELECT token_address, SUM(txn_value) as txn_value +FROM +(select a.timestamp, a.maker_token as token_address, (CASE WHEN a.taker_txn_usd_value > a.maker_txn_usd_value OR a.maker_txn_usd_value IS NULL + THEN a.taker_txn_usd_value + ELSE a.maker_txn_usd_value END) as txn_value + from events_full a + where a.event_type = 'LogFill' + and a.timestamp > (NOW() + INTERVAL '-24 hours') + union + select a.timestamp, a.taker_token as token_address, (CASE WHEN a.taker_txn_usd_value > a.maker_txn_usd_value OR a.maker_txn_usd_value IS NULL + THEN a.taker_txn_usd_value + ELSE a.maker_txn_usd_value END) as txn_value + from events_full a + where a.event_type = 'LogFill' + and a.timestamp > (NOW() + INTERVAL '-24 hours')) token_txn_values +WHERE token_address IS NOT NULL +AND txn_value > 0 +GROUP BY 1 +ORDER BY 2 DESC) a +LEFT JOIN tokens b +ON a.token_address = b.address +WHERE symbol is NULL +ORDER BY 2 DESC +`, +}; diff --git a/packages/pipeline/src/scripts/scrape_data.ts b/packages/pipeline/src/scripts/scrape_data.ts new file mode 100644 index 000000000..963670b47 --- /dev/null +++ b/packages/pipeline/src/scripts/scrape_data.ts @@ -0,0 +1,649 @@ +import { ExchangeEvents, ZeroEx } from '0x.js'; +import { HttpClient, Order, OrderbookRequest, OrderbookResponse, TokenPairsItem } from '@0xproject/connect'; +import * as Airtable from 'airtable'; +import * as commandLineArgs from 'command-line-args'; +import * as _ from 'lodash'; +import * as querystring from 'querystring'; +import * as queue from 'queue'; +import * as request from 'request'; +import * as rpn from 'request-promise-native'; + +import { HttpRequestOptions } from '../../../connect/lib/src/types.js'; +import { relayer } from '../models/relayer.js'; +import { token } from '../models/tokens.js'; +import { postgresClient } from '../postgres.js'; +import { typeConverters } from '../utils.js'; +import { web3, zrx } from '../zrx.js'; + +import { insertDataScripts } from './create_tables.js'; +import { dataFetchingQueries } from './query_data.js'; +const optionDefinitions = [ + { name: 'from', alias: 'f', type: Number }, + { name: 'to', alias: 't', type: Number }, + { name: 'type', type: String }, + { name: 'id', type: String }, + { name: 'force', type: Boolean }, + { name: 'token', type: String }, +]; +const cli = commandLineArgs(optionDefinitions); +const q = queue({ concurrency: 6, autostart: true }); +const airtableBase = new Airtable({ apiKey: process.env.AIRTABLE_API_KEY }).base(process.env.AIRTABLE_0X_BASE); +const BLOCK_INCREMENTS = 1000; +const BASE_SYMBOL = 'USD'; // use USD as base currency against +const API_HIST_LIMIT = 2000; // cryptocompare API limits histoday price query to 2000 days +const SECONDS_PER_DAY = 86400; +const PRICE_API_ENDPOINT = 'https://min-api.cryptocompare.com/data/pricehistorical'; +const RELAYER_REGISTRY_JSON = 'https://raw.githubusercontent.com/0xProject/0x-relayer-registry/master/relayers.json'; +const METAMASK_ETH_CONTRACT_METADATA_JSON = + 'https://raw.githubusercontent.com/MetaMask/eth-contract-metadata/master/contract-map.json'; +const ETHPLORER_BASE_URL = 'http://api.ethplorer.io'; +const ETHPLORER_TOP_TOKENS_JSON = `${ETHPLORER_BASE_URL}/getTopTokens?apiKey=dyijm5418TjOJe34`; +// const HIST_PRICE_API_ENDPOINT = 'https://min-api.cryptocompare.com/data/histoday'; +const AIRTABLE_RELAYER_INFO = 'Relayer Info'; +export const pullDataScripts = { + getAllEvents(fromBlockNumber: number, toBlockNumber: number): any { + return new Promise((resolve, reject) => { + const getLogsPromises: any[] = []; + getLogsPromises.push( + zrx.exchange.getLogsAsync( + ExchangeEvents.LogFill, + { fromBlock: fromBlockNumber, toBlock: toBlockNumber }, + {}, + ), + zrx.exchange.getLogsAsync( + ExchangeEvents.LogCancel, + { fromBlock: fromBlockNumber, toBlock: toBlockNumber }, + {}, + ), + zrx.exchange.getLogsAsync( + ExchangeEvents.LogError, + { fromBlock: fromBlockNumber, toBlock: toBlockNumber }, + {}, + ), + ); + Promise.all(getLogsPromises) + .then((data: any[]) => { + resolve(data); + }) + .catch((err: any) => { + reject(err); + }); + }); + }, + getBlockInfo(blockNumber: number): any { + return new Promise((resolve, reject) => { + web3.eth.getBlock(blockNumber, (err, result) => { + if (err) { + reject(err); + } else { + resolve(result); + } + }); + }); + }, + getTransactionInfo(transactionHash: string): any { + return new Promise((resolve, reject) => { + web3.eth.getTransaction(transactionHash, (err, result) => { + if (err) { + reject(err); + } else { + resolve(result); + } + }); + }); + }, + getTokenRegistry(): any { + return new Promise((resolve, reject) => { + zrx.tokenRegistry + .getTokensAsync() + .then((data: any) => { + resolve(data); + }) + .catch((err: any) => { + reject(err); + }); + }); + }, + getMetaMaskTokens(): any { + return new Promise((resolve, reject) => { + request(METAMASK_ETH_CONTRACT_METADATA_JSON, (error, response, body) => { + if (error) { + reject(error); + } else { + resolve(JSON.parse(body)); + } + }); + }); + }, + getEthplorerTopTokens(): any { + return new Promise((resolve, reject) => { + request(ETHPLORER_TOP_TOKENS_JSON, (error, response, body) => { + if (error) { + reject(error); + } else { + resolve(JSON.parse(body)); + } + }); + }); + }, + getEthplorerToken(tokenAddress: string): any { + return new Promise((resolve, reject) => { + const url = `${ETHPLORER_BASE_URL}/getTokenInfo/${tokenAddress}?apiKey=dyijm5418TjOJe34`; + request(url, (error, response, body) => { + if (error) { + reject(error); + } else { + try { + const json = JSON.parse(body); + resolve(json); + } catch (err) { + resolve({ error: 'error' }); + } + } + }); + }); + }, + getPriceData(symbol: string, timestamp: number, timeDelay?: number): any { + return new Promise((resolve, reject) => { + if (symbol === 'WETH') { + symbol = 'ETH'; + } + let parsedParams = querystring.stringify({ + fsym: symbol, + tsyms: 'USD', + ts: timestamp / 1000, + }); + console.debug(parsedParams); + setTimeout(() => { + request(PRICE_API_ENDPOINT + '?' + parsedParams, (error, response, body) => { + if (error) { + reject(error); + } else { + resolve(JSON.parse(body)); + } + }); + }, timeDelay); + }); + }, + getRelayers(): any { + return new Promise((resolve, reject) => { + request(RELAYER_REGISTRY_JSON, (error, response, body) => { + if (error) { + reject(error); + } else { + resolve(JSON.parse(body)); + } + }); + }); + }, + async getOrderBook(sraEndpoint: string): Promise { + const relayerClient = new HttpClient(sraEndpoint); + const tokenResponse: TokenPairsItem[] = await relayerClient.getTokenPairsAsync(); + const fullOrderBook: OrderbookResponse[] = []; + for (const tokenPair of tokenResponse) { + const orderBookRequest: OrderbookRequest = { + baseTokenAddress: tokenPair.tokenA.address, + quoteTokenAddress: tokenPair.tokenB.address, + }; + const orderBook: OrderbookResponse = await relayerClient.getOrderbookAsync(orderBookRequest); + fullOrderBook.push(orderBook); + } + return fullOrderBook; + }, + // async getHistoricalPrices( + // fromSymbol: string, + // toSymbol: string, + // fromTimestamp: number, + // toTimestamp: number, + // ): Promise { + // const daysInQueryPeriod = Math.round((toTimestamp - fromTimestamp) / (SECONDS_PER_DAY)); + // if(fromSymbol === 'WETH') { + // fromSymbol = 'ETH'; + // } + // var parsedParams = { + // fsym: fromSymbol, + // tsym: toSymbol, + // limit: Math.min(daysInQueryPeriod, API_HIST_LIMIT), + // toTs: toTimestamp, + // }; + // var options = { + // uri: HIST_PRICE_API_ENDPOINT, + // qs: parsedParams, + // json: false, + // }; + // try { + // const response = await rpn(options); + // return Promise.resolve(JSON.parse(response)); + // } catch (error) { + // console.debug(error); + // return Promise.reject(error); + // } + // }, +}; +export const scrapeDataScripts = { + scrapeAllPricesToDB(fromTime: number, toTime: number) { + const fromDate = new Date(fromTime); + fromDate.setUTCHours(0); + fromDate.setUTCMinutes(0); + fromDate.setUTCSeconds(0); + fromDate.setUTCMilliseconds(0); + const toDate = new Date(toTime); + postgresClient + .query(dataFetchingQueries.get_token_registry, []) + .then((result: any) => { + for (const curDate = fromDate; curDate < toDate; curDate.setDate(curDate.getDate() + 1)) { + for (const token of Object.values(result.rows)) { + console.debug('Scraping ' + curDate + ' ' + token); + q.push(_scrapePriceToDB(curDate.getTime(), token, 500)); + } + } + }) + .catch((err: any) => { + console.debug(err); + }); + }, +}; +function _scrapeEventsToDB(fromBlock: number, toBlock: number): any { + return (cb: () => void) => { + pullDataScripts + .getAllEvents(fromBlock, toBlock) + .then((data: any) => { + const parsedEvents: any = {}; + parsedEvents[ExchangeEvents.LogFill] = []; + parsedEvents[ExchangeEvents.LogCancel] = []; + parsedEvents[ExchangeEvents.LogError] = []; + for (const index in data) { + for (const datum of data[index]) { + const event = typeConverters.convertLogEventToEventObject(datum); + parsedEvents[event.event_type].push(event); + } + } + console.log(fromBlock + ' : ' + toBlock + ' ' + parsedEvents[ExchangeEvents.LogFill].length); + for (const event_type in parsedEvents) { + if (parsedEvents[event_type].length > 0) { + insertDataScripts + .insertMultipleRows( + 'events_raw', + parsedEvents[event_type], + Object.keys(parsedEvents[event_type][0]), + ) + .then(() => {}) + .catch((error: any) => {}); + } + } + cb(); + }) + .catch((err: any) => { + cb(); + }); + }; +} +function _scrapeBlockToDB(block: number): any { + return (cb: () => void) => { + pullDataScripts + .getBlockInfo(block) + .then((data: any) => { + const parsedBlock = typeConverters.convertLogBlockToBlockObject(data); + insertDataScripts + .insertSingleRow('blocks', parsedBlock) + .then((result: any) => { + cb(); + }) + .catch((err: any) => { + cb(); + }); + }) + .catch((err: any) => { + cb(); + }); + }; +} +// function _scrapeAllRelayersToDB(): any { +// return (cb: () => void) => { +// airtableBase(AIRTABLE_RELAYER_INFO) +// .select() +// .eachPage((records: any, fetchNextPage: () => void) => { +// const parsedRelayers: any[] = []; +// for(const record of records) { +// parsedRelayers.push(typeConverters.convertRelayerToRelayerObject(record)); +// } +// insertDataScripts.insertMultipleRows('relayers', parsedRelayers, Object.keys(parsedRelayers[0])) +// .then((result: any) => { +// cb(); +// }) +// .catch((err: any) => { +// cb(); +// }); +// }) +// .catch((err: any) => { +// cb(); +// }); +// }; +// } +function _scrapeAllRelayersToDB(): any { + return (cb: () => void) => { + pullDataScripts + .getRelayers() + .then((relayers: any[]) => { + console.log(relayers); + const parsedRelayers: any[] = []; + for (const relayer of relayers) { + parsedRelayers.push(typeConverters.convertRelayerToRelayerObject(relayer)); + } + console.log(parsedRelayers); + insertDataScripts + .insertMultipleRows('relayers', parsedRelayers, Object.keys(relayer.tableProperties)) + .then((result: any) => { + console.log(result); + cb(); + }) + .catch((err: any) => { + console.log(err); + cb(); + }); + }) + .catch((err: any) => { + cb(); + }); + }; +} +function _scrapeTransactionToDB(transactionHash: string): any { + return (cb: () => void) => { + pullDataScripts + .getTransactionInfo(transactionHash) + .then((data: any) => { + const parsedTransaction = typeConverters.convertLogTransactionToTransactionObject(data); + insertDataScripts + .insertSingleRow('transactions', parsedTransaction) + .then((result: any) => { + cb(); + }) + .catch((err: any) => { + cb(); + }); + }) + .catch((err: any) => { + cb(); + }); + }; +} +function _scrapeTokenRegistryToDB(): any { + return (cb: () => void) => { + pullDataScripts + .getTokenRegistry() + .then((data: any) => { + const parsedTokens: any = []; + for (const token of data) { + parsedTokens.push(typeConverters.convertLogTokenToTokenObject(token)); + } + insertDataScripts.insertMultipleRows('tokens', parsedTokens, Object.keys(parsedTokens[0])); + cb(); + }) + .catch((err: any) => { + cb(); + }); + }; +} +function _scrapeMetaMaskEthContractMetadataToDB(): any { + return (cb: () => void) => { + pullDataScripts + .getMetaMaskTokens() + .then((data: any) => { + const parsedTokens: any = []; + const dataArray = _.map(_.keys(data), (tokenAddress: string) => { + const value = _.get(data, tokenAddress); + return { + address: tokenAddress, + ...value, + }; + }); + const erc20TokensOnly = _.filter(dataArray, entry => { + const isErc20 = _.get(entry, 'erc20'); + return isErc20; + }); + for (const token of erc20TokensOnly) { + parsedTokens.push(typeConverters.convertMetaMaskTokenToTokenObject(token)); + } + insertDataScripts.insertMultipleRows('tokens', parsedTokens, Object.keys(parsedTokens[0])); + cb(); + }) + .catch((err: any) => { + cb(); + }); + }; +} +function _scrapeEthplorerTopTokensToDB(): any { + return (cb: () => void) => { + pullDataScripts + .getEthplorerTopTokens() + .then((data: any) => { + const parsedTokens: any = []; + const tokens = _.get(data, 'tokens'); + for (const token of tokens) { + parsedTokens.push(typeConverters.convertMetaMaskTokenToTokenObject(token)); + } + insertDataScripts.insertMultipleRows('tokens', parsedTokens, Object.keys(parsedTokens[0])); + cb(); + }) + .catch((err: any) => { + cb(); + }); + }; +} +function _scrapeUnknownTokenInformationToDB(): any { + return (cb: () => void) => { + postgresClient + .query(dataFetchingQueries.get_top_unknown_token_addresses) + .then(async (result: any) => { + const addresses = _.map(result.rows, row => _.get(row, 'address')); + const responses = await Promise.all( + _.map(addresses, address => pullDataScripts.getEthplorerToken(address)), + ); + const tokens = _.filter(responses, response => _.isUndefined(_.get(response, 'error'))); + const parsedTokens = _.map(tokens, tokenInfo => + typeConverters.convertEthplorerTokenToTokenObject(tokenInfo), + ); + insertDataScripts.insertMultipleRows('tokens', parsedTokens, Object.keys(parsedTokens[0])); + cb(); + }) + .catch((err: any) => { + cb(); + }); + }; +} +function _scrapePriceToDB(timestamp: number, token: any, timeDelay?: number): any { + return (cb: () => void) => { + pullDataScripts + .getPriceData(token.symbol, timestamp, timeDelay) + .then((data: any) => { + const safeSymbol = token.symbol === 'WETH' ? 'ETH' : token.symbol; + const parsedPrice = { + timestamp: timestamp / 1000, + symbol: token.symbol, + base: 'USD', + price: _.has(data[safeSymbol], 'USD') ? data[safeSymbol].USD : 0, + }; + console.debug('Inserting ' + timestamp); + console.debug(parsedPrice); + insertDataScripts.insertSingleRow('prices', parsedPrice); + cb(); + }) + .catch((err: any) => { + console.debug(err); + cb(); + }); + }; +} +// function _scrapeHistoricalPricesToDB(token: any, fromTimestamp: number, toTimestamp: number): any { +// return (cb: () => void) => { +// pullDataScripts +// .getHistoricalPrices(token, BASE_SYMBOL, fromTimestamp, toTimestamp) +// .then((data: any) => { +// const parsedHistoricalPrices: any = []; +// for (const historicalPrice of data['Data']) { +// const parsedHistoricalPrice = typeConverters.convertLogHistoricalPricesToHistoricalPricesObject(historicalPrice); +// parsedHistoricalPrice['token'] = token; +// parsedHistoricalPrice['base'] = BASE_SYMBOL; +// parsedHistoricalPrices.push(parsedHistoricalPrice); +// } +// if (parsedHistoricalPrices.length > 0) { +// insertDataScripts +// .insertMultipleRows( +// 'historical_prices', +// parsedHistoricalPrices, +// Object.keys(parsedHistoricalPrices[0]), +// ) +// .catch((error: any) => { +// console.error(error); +// }); +// } +// cb(); +// }) +// .catch((error: any) => { +// console.error(error); +// cb(); +// }); +// }; +// } +function _scrapeOrderBookToDB(id: string, sraEndpoint: string): any { + return (cb: () => void) => { + pullDataScripts + .getOrderBook(sraEndpoint) + .then((data: any) => { + for (const book of data) { + for (const order of book.bids) { + console.debug(order); + const parsedOrder = typeConverters.convertLogOrderToOrderObject(order); + parsedOrder.relayer_id = id; + parsedOrder.order_hash = ZeroEx.getOrderHashHex(order); + insertDataScripts.insertSingleRow('orders', parsedOrder).catch((error: any) => { + console.error(error); + }); + } + for (const order of book.asks) { + console.debug(order); + const parsedOrder = typeConverters.convertLogOrderToOrderObject(order); + parsedOrder.relayer_id = id; + parsedOrder.order_hash = ZeroEx.getOrderHashHex(order); + insertDataScripts.insertSingleRow('orders', parsedOrder).catch((error: any) => { + console.error(error); + }); + } + } + cb(); + }) + .catch((error: any) => { + console.error(error); + cb(); + }); + }; +} +if (cli.type === 'events') { + if (cli.from && cli.to) { + const destToBlock = cli.to ? cli.to : cli.from; + let curFromBlock = cli.from; + let curToBlock = curFromBlock; + do { + curToBlock += destToBlock - curToBlock < BLOCK_INCREMENTS ? destToBlock - curToBlock : BLOCK_INCREMENTS; + q.push(_scrapeEventsToDB(curFromBlock, curToBlock)); + curFromBlock = curToBlock + 1; + } while (curToBlock < destToBlock); + } +} else if (cli.type === 'blocks') { + if (cli.from && cli.to) { + if (cli.force) { + const destToBlock = cli.to ? cli.to : cli.from; + let curFromBlock = cli.from; + const curToBlock = curFromBlock; + for (; curFromBlock < destToBlock; curFromBlock++) { + q.push(_scrapeBlockToDB(curFromBlock)); + } + } else { + const fetchFrom = cli.from; + const fetchTo = cli.to ? cli.to : cli.from + 1; + postgresClient + .query(dataFetchingQueries.get_used_block_numbers, [fetchFrom, fetchTo]) + .then((data: any) => { + for (const row of data.rows) { + q.push(_scrapeBlockToDB(row.block_number)); + } + }) + .catch((err: any) => { + // console.debug(err); + }); + } + } +} else if (cli.type === 'transactions') { + if (cli.id) { + q.push(_scrapeTransactionToDB(cli.id)); + } else if (cli.from) { + const fetchFrom = cli.from; + const fetchTo = cli.to ? cli.to : cli.from + 1; + postgresClient + .query(dataFetchingQueries.get_missing_txn_hashes, [fetchFrom, fetchTo]) + .then((data: any) => { + for (const row of data.rows) { + q.push(_scrapeTransactionToDB(row.txn_hash)); + } + }) + .catch((err: any) => { + // console.debug(err); + }); + } +} else if (cli.type === 'tokens') { + q.push(_scrapeMetaMaskEthContractMetadataToDB()); + q.push(_scrapeEthplorerTopTokensToDB()); +} else if (cli.type === 'unknown_tokens') { + q.push(_scrapeUnknownTokenInformationToDB()); +} else if (cli.type === 'prices' && cli.from && cli.to) { + const fromDate = new Date(cli.from); + console.debug(fromDate); + fromDate.setUTCHours(0); + fromDate.setUTCMinutes(0); + fromDate.setUTCSeconds(0); + fromDate.setUTCMilliseconds(0); + console.debug(fromDate); + const toDate = new Date(cli.to); + postgresClient + .query(dataFetchingQueries.get_token_registry, []) + .then((result: any) => { + for (const curDate = fromDate; curDate < toDate; curDate.setDate(curDate.getDate() + 1)) { + for (const token of Object.values(result.rows)) { + console.debug('Scraping ' + curDate + ' ' + token); + q.push(_scrapePriceToDB(curDate.getTime(), token)); + } + } + }) + .catch((err: any) => { + console.debug(err); + }); + // } else if (cli.type === 'historical_prices') { + // if (cli.token && cli.from && cli.to) { + // q.push(_scrapeHistoricalPricesToDB(cli.token, cli.from, cli.to)); + // } + // } else if (cli.type === 'all_historical_prices') { + // if (cli.from && cli.to) { + // postgresClient + // .query(dataFetchingQueries.get_token_registry, []) + // .then((result: any) => { + // const curTokens: any = result.rows.map((a: any): any => a.symbol); + // for (const curToken of curTokens) { + // console.debug('Historical data backfill: Pushing coin ' + curToken); + // q.push(_scrapeHistoricalPricesToDB(curToken, cli.from, cli.to)); + // } + // }) + // .catch((err: any) => { + // console.debug(err); + // }); + // } +} else if (cli.type === 'relayers') { + q.push(_scrapeAllRelayersToDB()); +} else if (cli.type === 'orders') { + postgresClient.query(dataFetchingQueries.get_relayers, []).then((result: any) => { + for (const relayer of result.rows) { + if (relayer.sra_http_url) { + q.push(_scrapeOrderBookToDB(relayer.id, relayer.sra_http_url)); + } + } + }); +} diff --git a/packages/pipeline/src/types.ts b/packages/pipeline/src/types.ts new file mode 100644 index 000000000..1c76f12a7 --- /dev/null +++ b/packages/pipeline/src/types.ts @@ -0,0 +1,6 @@ +export interface HistoricalPriceResponse { + Response: string; + Type: number; + Aggregated: boolean; + Data: JSON[]; +} diff --git a/packages/pipeline/src/utils.ts b/packages/pipeline/src/utils.ts new file mode 100644 index 000000000..3474cd16b --- /dev/null +++ b/packages/pipeline/src/utils.ts @@ -0,0 +1,181 @@ +import * as _ from 'lodash'; + +import { block, logToBlockSchemaMapping } from './models/block'; +import { event, logToEventSchemaMapping } from './models/event'; +import { historicalPrices, logToHistoricalPricesSchema } from './models/historical_prices'; +import { logToOrderSchemaMapping, order } from './models/order'; +import { logToRelayerSchemaMapping } from './models/relayer'; +import { logToTokenSchemaMapping, token } from './models/tokens'; +import { logToTransactionSchemaMapping, transaction } from './models/transaction'; +export const typeConverters = { + convertLogEventToEventObject(log: any): any { + const newEvent: any = {}; + for (const key in logToEventSchemaMapping) { + if (_.has(log, key)) { + newEvent[logToEventSchemaMapping[key]] = _.get(log, key); + if (newEvent[logToEventSchemaMapping[key]].constructor.name === 'BigNumber') { + newEvent[logToEventSchemaMapping[key]] = newEvent[logToEventSchemaMapping[key]].toString(); + } + } + } + return newEvent; + }, + convertLogBlockToBlockObject(logBlock: any): any { + const newBlock: any = {}; + for (const key in logToBlockSchemaMapping) { + if (_.has(logBlock, key)) { + newBlock[logToBlockSchemaMapping[key]] = _.get(logBlock, key); + if (newBlock[logToBlockSchemaMapping[key]].constructor.name === 'BigNumber') { + newBlock[logToBlockSchemaMapping[key]] = newBlock[logToBlockSchemaMapping[key]].toString(); + } + } + } + return newBlock; + }, + convertLogTokenToTokenObject(logToken: any): any { + const newToken: any = {}; + for (const key in logToTokenSchemaMapping) { + if (_.has(logToken, key)) { + newToken[logToTokenSchemaMapping[key]] = _.get(logToken, key); + if (newToken[logToTokenSchemaMapping[key]].constructor.name === 'BigNumber') { + newToken[logToTokenSchemaMapping[key]] = newToken[logToTokenSchemaMapping[key]].toString(); + } + } + } + newToken[logToTokenSchemaMapping.address] = newToken[logToTokenSchemaMapping.address].toLowerCase(); + return newToken; + }, + convertMetaMaskTokenToTokenObject(metaMaskToken: any): any { + const newToken: any = {}; + for (const key in logToTokenSchemaMapping) { + if (_.has(metaMaskToken, key)) { + newToken[logToTokenSchemaMapping[key]] = _.get(metaMaskToken, key); + } + } + newToken[logToTokenSchemaMapping.address] = newToken[logToTokenSchemaMapping.address].toLowerCase(); + console.log(newToken); + return newToken; + }, + convertEthplorerTokenToTokenObject(ethplorerToken: any): any { + const newToken: any = {}; + for (const key in logToTokenSchemaMapping) { + if (_.has(ethplorerToken, key)) { + newToken[logToTokenSchemaMapping[key]] = _.get(ethplorerToken, key); + } + } + newToken[logToTokenSchemaMapping.address] = newToken[logToTokenSchemaMapping.address].toLowerCase(); + return newToken; + }, + convertLogTransactionToTransactionObject(logTransaction: any): any { + const newTransaction: any = {}; + for (const key in logToTransactionSchemaMapping) { + if (_.has(logTransaction, key)) { + newTransaction[logToTransactionSchemaMapping[key]] = _.get(logTransaction, key); + if (newTransaction[logToTransactionSchemaMapping[key]].constructor.name === 'BigNumber') { + newTransaction[logToTransactionSchemaMapping[key]] = newTransaction[ + logToTransactionSchemaMapping[key] + ].toString(); + } + } else { + if (key === 'method_id') { + newTransaction[logToTransactionSchemaMapping[key]] = logTransaction.input.substring(0, 10); + } else if (key === 'salt') { + newTransaction[logToTransactionSchemaMapping[key]] = + '0x' + logTransaction.input.substring(714, 778); // Only God can judge me + } + } + } + return newTransaction; + }, + // convertRelayerToRelayerObject(logRelayer: any): any { + // const newRelayer: any = {}; + // for (const key in logToRelayerSchemaMapping) { + // if (_.has(logRelayer, key)) { + // newRelayer[logToRelayerSchemaMapping[key]] = _.get(logRelayer, key); + // if (newRelayer[logToRelayerSchemaMapping[key]].constructor.name === 'BigNumber') { + // newRelayer[logToRelayerSchemaMapping[key]] = newRelayer[logToRelayerSchemaMapping[key]].toString(); + // } + // } else if((logToRelayerSchemaMapping[key] === 'known_fee_addresses' || logToRelayerSchemaMapping[key] === 'known_taker_addresses')) { + // newRelayer[logToRelayerSchemaMapping[key]] = '{}'; + // } else { + // newRelayer[logToRelayerSchemaMapping[key]] = ''; + // } + // } + // return newRelayer; + // }, + convertRelayerToRelayerObject(logRelayer: any): any { + const newRelayer: any = {}; + for (const key in logToRelayerSchemaMapping) { + if (_.has(logRelayer, key)) { + newRelayer[logToRelayerSchemaMapping[key]] = _.get(logRelayer, key); + if (newRelayer[logToRelayerSchemaMapping[key]].constructor.name === 'BigNumber') { + newRelayer[logToRelayerSchemaMapping[key]] = newRelayer[logToRelayerSchemaMapping[key]].toString(); + } + } else if ( + logToRelayerSchemaMapping[key] === 'known_fee_addresses' || + logToRelayerSchemaMapping[key] === 'known_taker_addresses' + ) { + newRelayer[logToRelayerSchemaMapping[key]] = '{}'; + } else { + newRelayer[logToRelayerSchemaMapping[key]] = ''; + } + } + if (_.has(logRelayer, 'networks')) { + for (const network of logRelayer.networks) { + if (network.networkId === 1) { + if (_.has(network, 'sra_http_endpoint')) { + newRelayer.sra_http_endpoint = network.sra_http_endpoint; + } + if (_.has(network, 'sra_ws_endpoint')) { + newRelayer.sra_ws_endpoint = network.sra_ws_endpoint; + } + if (_.has(network, 'static_order_fields')) { + if (_.has(network, 'static_order_fields.fee_recipient_addresses')) { + newRelayer.fee_recipient_addresses = network.static_order_fields.fee_recipient_addresses; + } + if (_.has(network, 'static_order_fields.taker_addresses')) { + newRelayer.taker_addresses = network.static_order_fields.taker_addresses; + } + } + } + } + } + return newRelayer; + }, + convertLogHistoricalPricesToHistoricalPricesObject(logHistoricalPrice: any): any { + const newHistoricalPrices: any = {}; + for (const key in logToHistoricalPricesSchema) { + if (_.has(logHistoricalPrice, key)) { + newHistoricalPrices[logToHistoricalPricesSchema[key]] = _.get(logHistoricalPrice, key); + } + } + return newHistoricalPrices; + }, + convertLogOrderToOrderObject(logOrder: any): any { + const newOrder: any = {}; + for (const key in logToOrderSchemaMapping) { + if (_.has(logOrder, key)) { + console.log(key); + console.log(logOrder[key]); + newOrder[logToOrderSchemaMapping[key]] = _.get(logOrder, key); + if (newOrder[logToOrderSchemaMapping[key]].constructor.name === 'BigNumber') { + newOrder[logToOrderSchemaMapping[key]] = newOrder[logToOrderSchemaMapping[key]].toString(); + } + } + } + console.log(newOrder); + return newOrder; + }, +}; +export const formatters = { + escapeSQLParams(params: any[]): string { + let escapedString = ''; + for (const i in params) { + escapedString += "'" + params[i] + "',"; + } + return escapedString.slice(0, -1); + }, + escapeSQLParam(param: string): string { + return "'" + param + "'"; + }, +}; diff --git a/packages/pipeline/src/zrx.ts b/packages/pipeline/src/zrx.ts new file mode 100644 index 000000000..cbe10a55a --- /dev/null +++ b/packages/pipeline/src/zrx.ts @@ -0,0 +1,11 @@ +import { ExchangeEvents, ZeroEx } from '0x.js'; +import * as dotenv from 'dotenv'; +import * as Web3 from 'web3'; +dotenv.config(); +const provider = new Web3.providers.HttpProvider(process.env.WEB3_PROVIDER_URL); +const web3 = new Web3(provider); +const MAINNET = 1; +const zrx = new ZeroEx(provider, { + networkId: MAINNET, +}); +export { web3, zrx }; diff --git a/packages/pipeline/tsconfig.json b/packages/pipeline/tsconfig.json new file mode 100644 index 000000000..718e623c7 --- /dev/null +++ b/packages/pipeline/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig", + "compilerOptions": { + "outDir": "lib", + "rootDir": "." + }, + "include": ["src/**/*", "test/**/*"] +} diff --git a/packages/pipeline/tslint.json b/packages/pipeline/tslint.json new file mode 100644 index 000000000..ffaefe83a --- /dev/null +++ b/packages/pipeline/tslint.json @@ -0,0 +1,3 @@ +{ + "extends": ["@0xproject/tslint-config"] +} diff --git a/packages/pipeline/typedoc-tsconfig.json b/packages/pipeline/typedoc-tsconfig.json new file mode 100644 index 000000000..b9c6b36f3 --- /dev/null +++ b/packages/pipeline/typedoc-tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../typedoc-tsconfig", + "compilerOptions": { + "outDir": "lib" + }, + "include": ["src/**/*", "test/**/*"] +} diff --git a/yarn.lock b/yarn.lock index e3f047296..cf9052f7c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,23 @@ # yarn lockfile v1 +"0x.js@^0.38.3": + version "0.38.6" + resolved "https://registry.yarnpkg.com/0x.js/-/0x.js-0.38.6.tgz#f6cb2b6ef9aea2ff7696c923095e0b794c1d84fc" + dependencies: + "@0xproject/assert" "^0.2.14" + "@0xproject/base-contract" "^0.3.6" + "@0xproject/contract-wrappers" "^0.1.1" + "@0xproject/order-utils" "^0.0.9" + "@0xproject/order-watcher" "^0.0.8" + "@0xproject/sol-compiler" "^0.5.4" + "@0xproject/types" "^0.8.2" + "@0xproject/typescript-typings" "^0.4.3" + "@0xproject/utils" "^0.7.3" + "@0xproject/web3-wrapper" "^0.7.3" + ethers "3.0.22" + lodash "4.17.10" + "@0x-lerna-fork/add@^3.0.0-beta.22": version "3.0.0-beta.22" resolved "https://registry.yarnpkg.com/@0x-lerna-fork/add/-/add-3.0.0-beta.22.tgz#b69d7cbf6a28af6abb3aabb47fb617e7c6c7ac2c" @@ -472,12 +489,220 @@ npmlog "^4.1.2" write-file-atomic "^2.3.0" +"@0xproject/assert@^0.2.14": + version "0.2.14" + resolved "https://registry.yarnpkg.com/@0xproject/assert/-/assert-0.2.14.tgz#7d5a373fedc8eb482716b730f4dddf3ef33bfa29" + dependencies: + "@0xproject/json-schemas" "^0.8.3" + "@0xproject/typescript-typings" "^0.4.3" + "@0xproject/utils" "^0.7.3" + lodash "4.17.10" + valid-url "1.0.9" + +"@0xproject/base-contract@^0.3.6": + version "0.3.6" + resolved "https://registry.yarnpkg.com/@0xproject/base-contract/-/base-contract-0.3.6.tgz#c6d742ce2904bdd9e77524d99ba2438d3aae8a40" + dependencies: + "@0xproject/types" "^0.8.2" + "@0xproject/typescript-typings" "^0.4.3" + "@0xproject/utils" "^0.7.3" + "@0xproject/web3-wrapper" "^0.7.3" + ethers "3.0.22" + lodash "4.17.10" + +"@0xproject/connect@^0.6.14": + version "0.6.17" + resolved "https://registry.yarnpkg.com/@0xproject/connect/-/connect-0.6.17.tgz#c281b1468d53b09adccbc87a9f1ee462dd6c0977" + dependencies: + "@0xproject/assert" "^0.2.14" + "@0xproject/json-schemas" "^0.8.3" + "@0xproject/types" "^0.8.2" + "@0xproject/typescript-typings" "^0.4.3" + "@0xproject/utils" "^0.7.3" + isomorphic-fetch "2.2.1" + lodash "4.17.10" + query-string "4.3.4" + websocket "1.0.25" + +"@0xproject/contract-wrappers@^0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@0xproject/contract-wrappers/-/contract-wrappers-0.1.1.tgz#8108d7ec051f202ef0cfa77c91c4ef994bf89881" + dependencies: + "@0xproject/assert" "^0.2.14" + "@0xproject/base-contract" "^0.3.6" + "@0xproject/fill-scenarios" "^0.0.6" + "@0xproject/json-schemas" "^0.8.3" + "@0xproject/order-utils" "^0.0.9" + "@0xproject/sol-compiler" "^0.5.4" + "@0xproject/types" "^0.8.2" + "@0xproject/typescript-typings" "^0.4.3" + "@0xproject/utils" "^0.7.3" + "@0xproject/web3-wrapper" "^0.7.3" + ethereumjs-blockstream "5.0.0" + ethereumjs-util "5.1.5" + ethers "3.0.22" + js-sha3 "0.7.0" + lodash "4.17.10" + uuid "3.2.1" + +"@0xproject/fill-scenarios@^0.0.6": + version "0.0.6" + resolved "https://registry.yarnpkg.com/@0xproject/fill-scenarios/-/fill-scenarios-0.0.6.tgz#969a2c49122650af5a1bf7fe7c6d286a0e6f2de2" + dependencies: + "@0xproject/base-contract" "^0.3.6" + "@0xproject/order-utils" "^0.0.9" + "@0xproject/types" "^0.8.2" + "@0xproject/typescript-typings" "^0.4.3" + "@0xproject/utils" "^0.7.3" + "@0xproject/web3-wrapper" "^0.7.3" + ethers "3.0.22" + lodash "4.17.10" + +"@0xproject/json-schemas@^0.8.3": + version "0.8.3" + resolved "https://registry.yarnpkg.com/@0xproject/json-schemas/-/json-schemas-0.8.3.tgz#455e6219a6bd05e990392165192a983a9ab89f26" + dependencies: + "@0xproject/typescript-typings" "^0.4.3" + "@types/node" "9.6.0" + jsonschema "1.2.2" + lodash.values "4.3.0" + "@0xproject/npm-cli-login@^0.0.11": version "0.0.11" resolved "https://registry.yarnpkg.com/@0xproject/npm-cli-login/-/npm-cli-login-0.0.11.tgz#3f1ec06112ce62aad300ff0575358f68aeecde2e" dependencies: npm-registry-client "7.0.9" +"@0xproject/order-utils@^0.0.9": + version "0.0.9" + resolved "https://registry.yarnpkg.com/@0xproject/order-utils/-/order-utils-0.0.9.tgz#75225dfbd87335d18810abf995d8e077b9a84868" + dependencies: + "@0xproject/assert" "^0.2.14" + "@0xproject/json-schemas" "^0.8.3" + "@0xproject/types" "^0.8.2" + "@0xproject/typescript-typings" "^0.4.3" + "@0xproject/utils" "^0.7.3" + "@0xproject/web3-wrapper" "^0.7.3" + "@types/node" "9.6.0" + bn.js "4.11.7" + ethereumjs-abi "0.6.5" + ethereumjs-util "5.1.5" + lodash "4.17.10" + +"@0xproject/order-watcher@^0.0.8": + version "0.0.8" + resolved "https://registry.yarnpkg.com/@0xproject/order-watcher/-/order-watcher-0.0.8.tgz#cfba4c39ca8c5d39e076276479383d0aae022c6d" + dependencies: + "@0xproject/assert" "^0.2.14" + "@0xproject/base-contract" "^0.3.6" + "@0xproject/contract-wrappers" "^0.1.1" + "@0xproject/fill-scenarios" "^0.0.6" + "@0xproject/json-schemas" "^0.8.3" + "@0xproject/order-utils" "^0.0.9" + "@0xproject/types" "^0.8.2" + "@0xproject/typescript-typings" "^0.4.3" + "@0xproject/utils" "^0.7.3" + "@0xproject/web3-wrapper" "^0.7.3" + bintrees "1.0.2" + ethereum-types "^0.0.2" + ethereumjs-blockstream "5.0.0" + ethers "3.0.22" + lodash "4.17.10" + +"@0xproject/sol-compiler@^0.5.4": + version "0.5.4" + resolved "https://registry.yarnpkg.com/@0xproject/sol-compiler/-/sol-compiler-0.5.4.tgz#3e0b04b0c02c5ec046ebb962b5ed20978c6b4cdd" + dependencies: + "@0xproject/json-schemas" "^0.8.3" + "@0xproject/sol-resolver" "^0.0.8" + "@0xproject/types" "^0.8.2" + "@0xproject/typescript-typings" "^0.4.3" + "@0xproject/utils" "^0.7.3" + "@0xproject/web3-wrapper" "^0.7.3" + "@types/yargs" "10.0.2" + chalk "2.3.2" + ethereumjs-util "5.1.5" + isomorphic-fetch "2.2.1" + lodash "4.17.10" + mkdirp "0.5.1" + require-from-string "2.0.1" + semver "5.5.0" + solc "0.4.23" + web3 "0.20.6" + web3-eth-abi "1.0.0-beta.33" + yargs "11.1.0" + +"@0xproject/sol-resolver@^0.0.8": + version "0.0.8" + resolved "https://registry.yarnpkg.com/@0xproject/sol-resolver/-/sol-resolver-0.0.8.tgz#5496ccebc9afa9eedfb5b62104f6478aa852bf66" + dependencies: + "@0xproject/types" "^0.8.2" + "@0xproject/typescript-typings" "^0.0.3" + lodash "4.17.10" + +"@0xproject/tslint-config@^1.0.7": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@0xproject/tslint-config/-/tslint-config-1.0.8.tgz#cfdce03b84dd2e108278b0024f89fe30218299e0" + dependencies: + lodash "^4.17.5" + tslint "5.11.0" + tslint-eslint-rules "5.4.0" + tslint-react "^3.2.0" + tsutils "3.0.0" + +"@0xproject/types@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@0xproject/types/-/types-0.5.0.tgz#ba3cfbc11a8c6344b57c9680aa7df2ea84b9bf05" + dependencies: + bignumber.js "~4.1.0" + +"@0xproject/types@^0.8.2": + version "0.8.2" + resolved "https://registry.yarnpkg.com/@0xproject/types/-/types-0.8.2.tgz#6f936b73bfb6f017b5102002d97da0881da92d1b" + dependencies: + "@types/node" "9.6.0" + bignumber.js "~4.1.0" + +"@0xproject/typescript-typings@^0.0.3": + version "0.0.3" + resolved "https://registry.yarnpkg.com/@0xproject/typescript-typings/-/typescript-typings-0.0.3.tgz#3272080bde00ade0a970b0d236686b483b08a1d0" + dependencies: + "@0xproject/types" "^0.5.0" + bignumber.js "~4.1.0" + +"@0xproject/typescript-typings@^0.4.3": + version "0.4.3" + resolved "https://registry.yarnpkg.com/@0xproject/typescript-typings/-/typescript-typings-0.4.3.tgz#f99f939a43f2764ad7182fcd78a71212a1d76d96" + dependencies: + "@0xproject/types" "^0.8.2" + bignumber.js "~4.1.0" + ethereum-types "^0.0.2" + +"@0xproject/utils@^0.7.3": + version "0.7.3" + resolved "https://registry.yarnpkg.com/@0xproject/utils/-/utils-0.7.3.tgz#ffa7c6da9bf0dd3e13694f185dcfc48a8981ff05" + dependencies: + "@0xproject/typescript-typings" "^0.4.3" + "@types/node" "9.6.0" + bignumber.js "~4.1.0" + ethereum-types "^0.0.2" + ethereumjs-util "^5.1.1" + ethers "3.0.22" + js-sha3 "0.7.0" + lodash "4.17.10" + web3 "0.20.6" + +"@0xproject/web3-wrapper@^0.7.3": + version "0.7.3" + resolved "https://registry.yarnpkg.com/@0xproject/web3-wrapper/-/web3-wrapper-0.7.3.tgz#9bd50b034b92fd505b6766b6e225f014b6d08b08" + dependencies: + "@0xproject/types" "^0.8.2" + "@0xproject/typescript-typings" "^0.4.3" + "@0xproject/utils" "^0.7.3" + ethers "3.0.22" + lodash "4.17.10" + web3 "0.20.6" + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.0.0-beta.35": version "7.0.0" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8" @@ -1202,10 +1427,18 @@ "@types/express" "*" "@types/node" "*" +"@types/caseless@*": + version "0.12.1" + resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.1.tgz#9794c69c8385d0192acc471a540d1f8e0d16218a" + "@types/cheerio@*": version "0.22.9" resolved "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.9.tgz#b5990152604c2ada749b7f88cab3476f21f39d7b" +"@types/command-line-args@^4.0.2": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/command-line-args/-/command-line-args-4.0.3.tgz#6f09c9873beb00013496941f4d73b98410bc1802" + "@types/compare-versions@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/compare-versions/-/compare-versions-3.0.0.tgz#4a45dffe0ebbc00d0f2daef8a0e96ffc66cf5955" @@ -1222,6 +1455,12 @@ version "2.0.0" resolved "https://registry.npmjs.org/@types/detect-node/-/detect-node-2.0.0.tgz#696e024ddd105c72bbc6a2e3f97902a2886f2c3f" +"@types/dotenv@^4.0.2": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/dotenv/-/dotenv-4.0.3.tgz#ebcfc40da7bc0728b705945b7db48485ec5b4b67" + dependencies: + "@types/node" "*" + "@types/enzyme-adapter-react-16@^1.0.3": version "1.0.3" resolved "https://registry.npmjs.org/@types/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.0.3.tgz#0cf7025b036694ca8d596fe38f24162e7117acf1" @@ -1286,6 +1525,12 @@ version "2.0.0" resolved "https://registry.yarnpkg.com/@types/find-versions/-/find-versions-2.0.0.tgz#a976ca032ab8dd1161116604eded0620d2b85df2" +"@types/form-data@*": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@types/form-data/-/form-data-2.2.1.tgz#ee2b3b8eaa11c0938289953606b745b738c54b1e" + dependencies: + "@types/node" "*" + "@types/fs-extra@^5.0.3": version "5.0.4" resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-5.0.4.tgz#b971134d162cc0497d221adde3dbb67502225599" @@ -1429,6 +1674,10 @@ version "9.6.5" resolved "https://registry.yarnpkg.com/@types/node/-/node-9.6.5.tgz#ee700810fdf49ac1c399fc5980b7559b3e5a381d" +"@types/node@9.6.0": + version "9.6.0" + resolved "http://registry.npmjs.org/@types/node/-/node-9.6.0.tgz#d3480ee666df9784b1001a1872a2f6ccefb6c2d7" + "@types/node@^10.3.2": version "10.9.4" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.9.4.tgz#0f4cb2dc7c1de6096055357f70179043c33e9897" @@ -1437,6 +1686,10 @@ version "10.5.7" resolved "https://registry.npmjs.org/@types/node/-/node-10.5.7.tgz#960d9feb3ade2233bcc9843c918d740b4f78a7cf" +"@types/node@^8.0.53": + version "8.10.36" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.36.tgz#eac05d576fbcd0b4ea3c912dc58c20475c08d9e4" + "@types/numeral@^0.0.22": version "0.0.22" resolved "https://registry.yarnpkg.com/@types/numeral/-/numeral-0.0.22.tgz#86bef1f0a2d743afdc2ef3168d45f2905e1a0b93" @@ -1447,6 +1700,20 @@ dependencies: "@types/node" "*" +"@types/pg-types@*": + version "1.11.4" + resolved "https://registry.yarnpkg.com/@types/pg-types/-/pg-types-1.11.4.tgz#8d7c59fb509ce3dca3f8bae589252051c639a9a8" + dependencies: + moment ">=2.14.0" + +"@types/pg@^7.4.1": + version "7.4.10" + resolved "https://registry.yarnpkg.com/@types/pg/-/pg-7.4.10.tgz#1cf4d6c5d32e621566eca82981161b371fb90ace" + dependencies: + "@types/events" "*" + "@types/node" "*" + "@types/pg-types" "*" + "@types/prop-types@*": version "15.5.5" resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.5.5.tgz#17038dd322c2325f5da650a94d5f9974943625e3" @@ -1561,6 +1828,15 @@ dependencies: redux "*" +"@types/request@^2.0.13": + version "2.47.1" + resolved "https://registry.yarnpkg.com/@types/request/-/request-2.47.1.tgz#25410d3afbdac04c91a94ad9efc9824100735824" + dependencies: + "@types/caseless" "*" + "@types/form-data" "*" + "@types/node" "*" + "@types/tough-cookie" "*" + "@types/require-from-string@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@types/require-from-string/-/require-from-string-1.2.0.tgz#c18cfc8a2c1a0259e5841d1fef2b5e9d01c64242" @@ -1620,6 +1896,10 @@ version "0.0.33" resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.0.33.tgz#1073c4bc824754ae3d10cfab88ab0237ba964e4d" +"@types/tough-cookie@*": + version "2.3.3" + resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-2.3.3.tgz#7f226d67d654ec9070e755f46daebf014628e9d9" + "@types/uuid@^3.4.2", "@types/uuid@^3.4.3": version "3.4.3" resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-3.4.3.tgz#121ace265f5569ce40f4f6d0ff78a338c732a754" @@ -1643,7 +1923,7 @@ "@types/events" "*" "@types/node" "*" -"@types/yargs@^10.0.0": +"@types/yargs@10.0.2", "@types/yargs@^10.0.0": version "10.0.2" resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-10.0.2.tgz#cc4ea921877874d1261e2c44b89807bc836e1b12" @@ -1916,6 +2196,15 @@ agentkeepalive@^3.4.1: dependencies: humanize-ms "^1.2.1" +airtable@^0.5.2: + version "0.5.6" + resolved "https://registry.yarnpkg.com/airtable/-/airtable-0.5.6.tgz#7f1a3cc038f77c9734c89deacdad500e25ad6fb1" + dependencies: + async "1.5.2" + lodash "4.17.10" + request "2.85.0" + xhr "2.3.3" + ajv-errors@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.0.tgz#ecf021fa108fd17dfb5e6b383f2dd233e31ffc59" @@ -2117,6 +2406,18 @@ arr-union@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" +array-back@^1.0.3, array-back@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/array-back/-/array-back-1.0.4.tgz#644ba7f095f7ffcf7c43b5f0dc39d3c1f03c063b" + dependencies: + typical "^2.6.0" + +array-back@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/array-back/-/array-back-2.0.0.tgz#6877471d51ecc9c9bfa6136fb6c7d5fe69748022" + dependencies: + typical "^2.6.1" + array-differ@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" @@ -2259,7 +2560,7 @@ async-limiter@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" -async@1.x, async@^1.4.0, async@^1.4.2, async@^1.5.2: +async@1.5.2, async@1.x, async@^1.4.0, async@^1.4.2, async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" @@ -3051,7 +3352,7 @@ bindings@^1.2.1, bindings@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.3.0.tgz#b346f6ecf6a95f5a815c5839fc7cdb22502f1ed7" -bintrees@^1.0.2: +bintrees@1.0.2, bintrees@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/bintrees/-/bintrees-1.0.2.tgz#49f896d6e858a4a499df85c38fb399b9aff840f8" @@ -3423,6 +3724,10 @@ buffer-to-arraybuffer@^0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz#6064a40fa76eb43c723aba9ef8f6e1216d10511a" +buffer-writer@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-1.0.1.tgz#22a936901e3029afcd7547eb4487ceb697a3bf08" + buffer-xor@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" @@ -3701,6 +4006,14 @@ chain-function@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/chain-function/-/chain-function-1.0.0.tgz#0d4ab37e7e18ead0bdc47b920764118ce58733dc" +chalk@2.3.2: + version "2.3.2" + resolved "http://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -4090,6 +4403,14 @@ combined-stream@1.0.6, combined-stream@^1.0.5, combined-stream@~1.0.5, combined- dependencies: delayed-stream "~1.0.0" +command-line-args@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-4.0.7.tgz#f8d1916ecb90e9e121eda6428e41300bfb64cc46" + dependencies: + array-back "^2.0.0" + find-replace "^1.0.3" + typical "^2.6.1" + commander@2.11.0: version "2.11.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" @@ -4098,7 +4419,7 @@ commander@2.15.1, commander@^2.12.1, commander@^2.8.1: version "2.15.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" -commander@2.18.0: +commander@2.18.0, commander@^2.9.0: version "2.18.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970" @@ -4403,6 +4724,10 @@ cookie@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" +cookiejar@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c" + copy-concurrently@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" @@ -4793,6 +5118,13 @@ date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" +dateformat@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" + dependencies: + get-stdin "^4.0.1" + meow "^3.3.0" + dateformat@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-2.2.0.tgz#4065e2013cf9fb916ddfd82efb506ad4c6769062" @@ -5850,6 +6182,13 @@ ethereum-common@^0.0.18: version "0.0.18" resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.0.18.tgz#2fdc3576f232903358976eb39da783213ff9523f" +ethereum-types@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/ethereum-types/-/ethereum-types-0.0.2.tgz#6ef6faf46a24697cbf66b6c8a0ecf2095ce58c38" + dependencies: + "@types/node" "^8.0.53" + bignumber.js "~4.1.0" + ethereumjs-abi@0.6.5, ethereumjs-abi@^0.6.5: version "0.6.5" resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.5.tgz#5a637ef16ab43473fa72a29ad90871405b3f5241" @@ -5891,6 +6230,14 @@ ethereumjs-block@~1.2.2: ethereumjs-util "^4.0.1" merkle-patricia-tree "^2.1.2" +ethereumjs-blockstream@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ethereumjs-blockstream/-/ethereumjs-blockstream-5.0.0.tgz#63bfe9185757329a32822d5815562eb1dcd75d71" + dependencies: + immutable "3.8.2" + source-map-support "0.5.6" + uuid "3.2.1" + ethereumjs-blockstream@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/ethereumjs-blockstream/-/ethereumjs-blockstream-6.0.0.tgz#79d726d1f358935eb65195e91d40344c31e87eff" @@ -5913,17 +6260,7 @@ ethereumjs-tx@^1.0.0, ethereumjs-tx@^1.1.1, ethereumjs-tx@^1.2.0, ethereumjs-tx@ ethereum-common "^0.0.18" ethereumjs-util "^5.0.0" -ethereumjs-util@^4.0.1, ethereumjs-util@^4.3.0, ethereumjs-util@^4.4.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-4.5.0.tgz#3e9428b317eebda3d7260d854fddda954b1f1bc6" - dependencies: - bn.js "^4.8.0" - create-hash "^1.1.2" - keccakjs "^0.2.0" - rlp "^2.0.0" - secp256k1 "^3.0.1" - -ethereumjs-util@^5.0.0, ethereumjs-util@^5.0.1, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.2, ethereumjs-util@^5.1.3, ethereumjs-util@^5.1.5: +ethereumjs-util@5.1.5, ethereumjs-util@^5.0.0, ethereumjs-util@^5.0.1, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.2, ethereumjs-util@^5.1.3, ethereumjs-util@^5.1.5: version "5.1.5" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-5.1.5.tgz#2f02575852627d45622426f25ee4a0b5f377f27a" dependencies: @@ -5935,6 +6272,16 @@ ethereumjs-util@^5.0.0, ethereumjs-util@^5.0.1, ethereumjs-util@^5.1.1, ethereum safe-buffer "^5.1.1" secp256k1 "^3.0.1" +ethereumjs-util@^4.0.1, ethereumjs-util@^4.3.0, ethereumjs-util@^4.4.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-4.5.0.tgz#3e9428b317eebda3d7260d854fddda954b1f1bc6" + dependencies: + bn.js "^4.8.0" + create-hash "^1.1.2" + keccakjs "^0.2.0" + rlp "^2.0.0" + secp256k1 "^3.0.1" + ethereumjs-util@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz#3e0c0d1741471acf1036052d048623dee54ad642" @@ -6004,6 +6351,21 @@ ethereumjs-wallet@~0.6.0: utf8 "^3.0.0" uuid "^3.3.2" +ethers@3.0.22: + version "3.0.22" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-3.0.22.tgz#7fab1ea16521705837aa43c15831877b2716b436" + dependencies: + aes-js "3.0.0" + bn.js "^4.4.0" + elliptic "6.3.3" + hash.js "^1.0.0" + inherits "2.0.1" + js-sha3 "0.5.7" + scrypt-js "2.0.3" + setimmediate "1.0.4" + uuid "2.0.1" + xmlhttprequest "1.8.0" + ethers@~4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.4.tgz#d3f85e8b27f4b59537e06526439b0fb15b44dc65" @@ -6523,6 +6885,13 @@ find-index@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/find-index/-/find-index-0.1.1.tgz#675d358b2ca3892d795a1ab47232f8b6e2e0dde4" +find-replace@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/find-replace/-/find-replace-1.0.3.tgz#b88e7364d2d9c959559f388c66670d6130441fa0" + dependencies: + array-back "^1.0.4" + test-value "^2.1.0" + find-up@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" @@ -6827,7 +7196,7 @@ ganache-core@0xProject/ganache-core#monorepo-dep: ethereumjs-tx "0xProject/ethereumjs-tx#fake-tx-include-signature-by-default" ethereumjs-util "^5.2.0" ethereumjs-vm "2.3.5" - ethereumjs-wallet "~0.6.0" + ethereumjs-wallet "0.6.0" fake-merkle-patricia-tree "~1.0.1" heap "~0.2.6" js-scrypt "^0.2.0" @@ -6917,6 +7286,10 @@ gaze@^0.5.1: dependencies: globule "~0.1.0" +generic-pool@2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-2.4.3.tgz#780c36f69dfad05a5a045dd37be7adca11a4f6ff" + generic-pool@~2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-2.0.4.tgz#f9718deda82fa125ed5c43e341c9a215a766d9a3" @@ -8933,13 +9306,17 @@ js-sha3@0.5.7: version "0.5.7" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" +js-sha3@0.7.0, js-sha3@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.7.0.tgz#0a5c57b36f79882573b2d84051f8bb85dd1bd63a" + js-sha3@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.3.1.tgz#86122802142f0828502a0d1dee1d95e253bb0243" -js-sha3@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.7.0.tgz#0a5c57b36f79882573b2d84051f8bb85dd1bd63a" +js-string-escape@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" js-tokens@^3.0.0, js-tokens@^3.0.2: version "3.0.2" @@ -9124,6 +9501,10 @@ jsonschema@*, jsonschema@1.2.4, jsonschema@^1.2.0: version "1.2.4" resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.2.4.tgz#a46bac5d3506a254465bc548876e267c6d0d6464" +jsonschema@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.2.2.tgz#83ab9c63d65bf4d596f91d81195e78772f6452bc" + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -9814,7 +10195,7 @@ lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" -lodash.values@^4.3.0: +lodash.values@4.3.0, lodash.values@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.values/-/lodash.values-4.3.0.tgz#a3a6c2b0ebecc5c2cba1c17e6e620fe81b53d347" @@ -9824,6 +10205,10 @@ lodash.words@^3.0.0: dependencies: lodash._root "^3.0.0" +lodash@4.17.10, lodash@^4.17.10: + version "4.17.10" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" + lodash@=4.17.4: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -9840,10 +10225,6 @@ lodash@^4.14.0, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1, lodash@^4.3.0, lo version "4.17.5" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" -lodash@^4.17.10: - version "4.17.10" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" - lodash@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/lodash/-/lodash-1.0.2.tgz#8f57560c83b59fc270bd3d561b690043430e2551" @@ -10260,6 +10641,12 @@ micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8, mic snapdragon "^0.8.1" to-regex "^3.0.2" +migrate@^0.2.2: + version "0.2.4" + resolved "https://registry.yarnpkg.com/migrate/-/migrate-0.2.4.tgz#30a54dac5c269b7321cec56b2aff4cccaea6418d" + dependencies: + dateformat "^1.0.12" + miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" @@ -10479,7 +10866,7 @@ moment@2.21.0: version "2.21.0" resolved "https://registry.yarnpkg.com/moment/-/moment-2.21.0.tgz#2a114b51d2a6ec9e6d83cf803f838a878d8a023a" -moment@^2.6.0: +moment@>=2.14.0, moment@^2.6.0: version "2.22.2" resolved "http://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66" @@ -10769,6 +11156,16 @@ node-pre-gyp@^0.6.39: tar "^2.2.1" tar-pack "^3.4.0" +node-redshift@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/node-redshift/-/node-redshift-0.1.5.tgz#c6c7cff16d230a148094225f20072939b66582f0" + dependencies: + bluebird "^3.5.0" + commander "^2.9.0" + migrate "^0.2.2" + pg "^6.1.2" + sql-bricks "^1.2.3" + nodemon@^1.11.0: version "1.17.3" resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.17.3.tgz#3b0bbc2ee05ccb43b1aef15ba05c63c7bc9b8530" @@ -11049,6 +11446,10 @@ oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" +object-assign@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" + object-assign@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" @@ -11422,6 +11823,10 @@ package-json@^4.0.0, package-json@^4.0.1: registry-url "^3.0.3" semver "^5.1.0" +packet-reader@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/packet-reader/-/packet-reader-0.3.1.tgz#cd62e60af8d7fea8a705ec4ff990871c46871f27" + pacote@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/pacote/-/pacote-9.1.0.tgz#59810859bbd72984dcb267269259375d32f391e5" @@ -11683,6 +12088,75 @@ performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" +pg-connection-string@0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-0.1.3.tgz#da1847b20940e42ee1492beaf65d49d91b245df7" + +pg-int8@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" + +pg-pool@1.*: + version "1.8.0" + resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-1.8.0.tgz#f7ec73824c37a03f076f51bfdf70e340147c4f37" + dependencies: + generic-pool "2.4.3" + object-assign "4.1.0" + +pg-pool@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-2.0.3.tgz#c022032c8949f312a4f91fb6409ce04076be3257" + +pg-types@1.*: + version "1.13.0" + resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-1.13.0.tgz#75f490b8a8abf75f1386ef5ec4455ecf6b345c63" + dependencies: + pg-int8 "1.0.1" + postgres-array "~1.0.0" + postgres-bytea "~1.0.0" + postgres-date "~1.0.0" + postgres-interval "^1.1.0" + +pg-types@~1.12.1: + version "1.12.1" + resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-1.12.1.tgz#d64087e3903b58ffaad279e7595c52208a14c3d2" + dependencies: + postgres-array "~1.0.0" + postgres-bytea "~1.0.0" + postgres-date "~1.0.0" + postgres-interval "^1.1.0" + +pg@^6.1.2: + version "6.4.2" + resolved "https://registry.yarnpkg.com/pg/-/pg-6.4.2.tgz#c364011060eac7a507a2ae063eb857ece910e27f" + dependencies: + buffer-writer "1.0.1" + js-string-escape "1.0.1" + packet-reader "0.3.1" + pg-connection-string "0.1.3" + pg-pool "1.*" + pg-types "1.*" + pgpass "1.*" + semver "4.3.2" + +pg@^7.4.1: + version "7.4.3" + resolved "https://registry.yarnpkg.com/pg/-/pg-7.4.3.tgz#f7b6f93f5340ecc2596afbb94a13e3d6b609834b" + dependencies: + buffer-writer "1.0.1" + packet-reader "0.3.1" + pg-connection-string "0.1.3" + pg-pool "~2.0.3" + pg-types "~1.12.1" + pgpass "1.x" + semver "4.3.2" + +pgpass@1.*, pgpass@1.x: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pgpass/-/pgpass-1.0.2.tgz#2a7bb41b6065b67907e91da1b07c1847c877b306" + dependencies: + split "^1.0.0" + pify@^2.0.0, pify@^2.2.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -12011,6 +12485,24 @@ postcss@^6.0.1: source-map "^0.6.1" supports-color "^5.3.0" +postgres-array@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-1.0.2.tgz#8e0b32eb03bf77a5c0a7851e0441c169a256a238" + +postgres-bytea@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35" + +postgres-date@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.3.tgz#e2d89702efdb258ff9d9cee0fe91bd06975257a8" + +postgres-interval@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-1.1.2.tgz#bf71ff902635f21cb241a013fc421d81d1db15a9" + dependencies: + xtend "^4.0.0" + prebuild-install@^2.2.2: version "2.5.3" resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-2.5.3.tgz#9f65f242782d370296353710e9bc843490c19f69" @@ -12368,7 +12860,7 @@ qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" -query-string@^4.1.0: +query-string@4.3.4, query-string@^4.1.0: version "4.3.4" resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" dependencies: @@ -12406,6 +12898,12 @@ querystringify@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-1.0.0.tgz#6286242112c5b712fa654e526652bf6a13ff05cb" +queue@^4.4.2: + version "4.5.0" + resolved "https://registry.yarnpkg.com/queue/-/queue-4.5.0.tgz#0f125191a983e3c38fcc0c0c75087d358d0857f4" + dependencies: + inherits "~2.0.0" + quick-lru@^1.0.0: version "1.1.0" resolved "http://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" @@ -13169,32 +13667,7 @@ request@2.81.0, "request@>=2.9.0 <2.82.0": tunnel-agent "^0.6.0" uuid "^3.0.0" -request@^2.47.0, request@^2.87.0: - version "2.88.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.0" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.4.3" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - -request@^2.54.0, request@^2.67.0, request@^2.79.0: +request@2.85.0, request@^2.54.0, request@^2.67.0, request@^2.79.0: version "2.85.0" resolved "https://registry.yarnpkg.com/request/-/request-2.85.0.tgz#5a03615a47c61420b3eb99b7dba204f83603e1fa" dependencies: @@ -13221,6 +13694,31 @@ request@^2.54.0, request@^2.67.0, request@^2.79.0: tunnel-agent "^0.6.0" uuid "^3.1.0" +request@^2.47.0, request@^2.83.0, request@^2.87.0: + version "2.88.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.0" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.4.3" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + request@^2.85.0: version "2.87.0" resolved "https://registry.npmjs.org/request/-/request-2.87.0.tgz#32f00235cd08d482b4d0d68db93a829c0ed5756e" @@ -13250,6 +13748,10 @@ require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" +require-from-string@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.1.tgz#c545233e9d7da6616e9d59adfb39fc9f588676ff" + require-from-string@^1.1.0: version "1.2.1" resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418" @@ -13598,6 +14100,10 @@ scrypt-async@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/scrypt-async/-/scrypt-async-1.3.1.tgz#a11fd6fac981b4b823ee01dee0221169500ddae9" +scrypt-js@2.0.3: + version "2.0.3" + resolved "http://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.3.tgz#bb0040be03043da9a012a2cea9fc9f852cfc87d4" + scrypt-js@2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.4.tgz#32f8c5149f0797672e551c07e230f834b6af5f16" @@ -13695,6 +14201,10 @@ semver-sort@0.0.4: version "5.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" +semver@4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.2.tgz#c7a07158a80bedd052355b770d82d6640f803be7" + semver@^4.1.0: version "4.3.6" resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" @@ -14042,9 +14552,9 @@ socks@~2.2.0: ip "^1.1.5" smart-buffer "^4.0.1" -solc@0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/solc/-/solc-0.4.24.tgz#354f14b269b38cbaa82a47d1ff151723502b954e" +solc@0.4.23, solc@^0.4.23: + version "0.4.23" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.4.23.tgz#54a0ff4015827b32fddb62c0a418b5247310a58e" dependencies: fs-extra "^0.30.0" memorystream "^0.3.1" @@ -14052,9 +14562,9 @@ solc@0.4.24: semver "^5.3.0" yargs "^4.7.1" -solc@^0.4.2: - version "0.4.21" - resolved "https://registry.yarnpkg.com/solc/-/solc-0.4.21.tgz#6a7ecd505bfa0fc268330d5de6b9ae65c8c68264" +solc@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.4.24.tgz#354f14b269b38cbaa82a47d1ff151723502b954e" dependencies: fs-extra "^0.30.0" memorystream "^0.3.1" @@ -14062,9 +14572,9 @@ solc@^0.4.2: semver "^5.3.0" yargs "^4.7.1" -solc@^0.4.23: - version "0.4.23" - resolved "https://registry.yarnpkg.com/solc/-/solc-0.4.23.tgz#54a0ff4015827b32fddb62c0a418b5247310a58e" +solc@^0.4.2: + version "0.4.21" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.4.21.tgz#6a7ecd505bfa0fc268330d5de6b9ae65c8c68264" dependencies: fs-extra "^0.30.0" memorystream "^0.3.1" @@ -14295,6 +14805,12 @@ sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" +sql-bricks@^1.2.3: + version "1.5.1" + resolved "https://registry.yarnpkg.com/sql-bricks/-/sql-bricks-1.5.1.tgz#68dc6d77b21a696f4885184a031b861ea5575981" + dependencies: + underscore "1.4.x" + sshpk@^1.7.0: version "1.14.1" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.1.tgz#130f5975eddad963f1d56f92b9ac6c51fa9f83eb" @@ -14915,6 +15431,13 @@ test-exclude@^4.2.1: read-pkg-up "^1.0.1" require-main-filename "^1.0.1" +test-value@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/test-value/-/test-value-2.1.0.tgz#11da6ff670f3471a73b625ca4f3fdcf7bb748291" + dependencies: + array-back "^1.0.3" + typical "^2.6.0" + text-encoding@^0.6.4: version "0.6.4" resolved "https://registry.yarnpkg.com/text-encoding/-/text-encoding-0.6.4.tgz#e399a982257a276dae428bb92845cb71bdc26d19" @@ -15419,6 +15942,10 @@ typewiselite@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/typewiselite/-/typewiselite-1.0.0.tgz#c8882fa1bb1092c06005a97f34ef5c8508e3664e" +typical@^2.6.0, typical@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/typical/-/typical-2.6.1.tgz#5c080e5d661cbbe38259d2e70a3c7253e873881d" + u2f-api@0.2.7: version "0.2.7" resolved "https://registry.yarnpkg.com/u2f-api/-/u2f-api-0.2.7.tgz#17bf196b242f6bf72353d9858e6a7566cc192720" @@ -15509,14 +16036,14 @@ undefsafe@^2.0.2: dependencies: debug "^2.2.0" +underscore@1.4.x, underscore@~1.4.4: + version "1.4.4" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.4.4.tgz#61a6a32010622afa07963bf325203cf12239d604" + underscore@1.8.3: version "1.8.3" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" -underscore@~1.4.4: - version "1.4.4" - resolved "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz#61a6a32010622afa07963bf325203cf12239d604" - unherit@^1.0.4: version "1.1.0" resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.0.tgz#6b9aaedfbf73df1756ad9e316dd981885840cd7d" @@ -15806,7 +16333,7 @@ v8flags@^2.0.2: dependencies: user-home "^1.1.1" -valid-url@^1.0.9: +valid-url@1.0.9, valid-url@^1.0.9: version "1.0.9" resolved "https://registry.yarnpkg.com/valid-url/-/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200" @@ -16012,6 +16539,14 @@ web3-bzz@1.0.0-beta.34: swarm-js "0.1.37" underscore "1.8.3" +web3-core-helpers@1.0.0-beta.33: + version "1.0.0-beta.33" + resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.0.0-beta.33.tgz#2af733e504db05e7c3648c1dacf577b0ec15dc43" + dependencies: + underscore "1.8.3" + web3-eth-iban "1.0.0-beta.33" + web3-utils "1.0.0-beta.33" + web3-core-helpers@1.0.0-beta.34: version "1.0.0-beta.34" resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.0.0-beta.34.tgz#b168da00d3e19e156bc15ae203203dd4dfee2d03" @@ -16064,6 +16599,15 @@ web3-core@1.0.0-beta.34: web3-core-requestmanager "1.0.0-beta.34" web3-utils "1.0.0-beta.34" +web3-eth-abi@1.0.0-beta.33: + version "1.0.0-beta.33" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.0.0-beta.33.tgz#2221f7151643660032a4df340f612349168c824a" + dependencies: + bn.js "4.11.6" + underscore "1.8.3" + web3-core-helpers "1.0.0-beta.33" + web3-utils "1.0.0-beta.33" + web3-eth-abi@1.0.0-beta.34, web3-eth-abi@^1.0.0-beta.24: version "1.0.0-beta.34" resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.0.0-beta.34.tgz#034533e3aa2f7e59ff31793eaea685c0ed5af67a" @@ -16101,6 +16645,13 @@ web3-eth-contract@1.0.0-beta.34: web3-eth-abi "1.0.0-beta.34" web3-utils "1.0.0-beta.34" +web3-eth-iban@1.0.0-beta.33: + version "1.0.0-beta.33" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.0.0-beta.33.tgz#1d73d0c5288a4565b1754a75b5fb3ea0b77a532f" + dependencies: + bn.js "4.11.6" + web3-utils "1.0.0-beta.33" + web3-eth-iban@1.0.0-beta.34: version "1.0.0-beta.34" resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.0.0-beta.34.tgz#9af458605867ccf74ea979aaf326b38ba6a5ba0c" @@ -16231,6 +16782,18 @@ web3-typescript-typings@^0.10.2: dependencies: bignumber.js "~4.1.0" +web3-utils@1.0.0-beta.33: + version "1.0.0-beta.33" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.0.0-beta.33.tgz#e091b7994f09b714b0198a4057d3ad2eb8cbe238" + dependencies: + bn.js "4.11.6" + eth-lib "0.1.27" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randomhex "0.1.5" + underscore "1.8.3" + utf8 "2.1.1" + web3-utils@1.0.0-beta.34: version "1.0.0-beta.34" resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.0.0-beta.34.tgz#9411fc39aaef39ca4e06169f762297d9ff020970" @@ -16253,6 +16816,16 @@ web3@0.20.2: xhr2 "*" xmlhttprequest "*" +web3@0.20.6: + version "0.20.6" + resolved "http://registry.npmjs.org/web3/-/web3-0.20.6.tgz#3e97306ae024fb24e10a3d75c884302562215120" + dependencies: + bignumber.js "git+https://github.com/frozeman/bignumber.js-nolookahead.git" + crypto-js "^3.1.4" + utf8 "^2.1.1" + xhr2 "*" + xmlhttprequest "*" + web3@^0.18.0: version "0.18.4" resolved "https://registry.yarnpkg.com/web3/-/web3-0.18.4.tgz#81ec1784145491f2eaa8955b31c06049e07c5e7d" @@ -16263,6 +16836,16 @@ web3@^0.18.0: xhr2 "*" xmlhttprequest "*" +web3@^0.20.0: + version "0.20.7" + resolved "https://registry.yarnpkg.com/web3/-/web3-0.20.7.tgz#1605e6d81399ed6f85a471a4f3da0c8be57df2f7" + dependencies: + bignumber.js "git+https://github.com/frozeman/bignumber.js-nolookahead.git" + crypto-js "^3.1.4" + utf8 "^2.1.1" + xhr2-cookies "^1.1.0" + xmlhttprequest "*" + web3@^1.0.0-beta.34: version "1.0.0-beta.34" resolved "https://registry.yarnpkg.com/web3/-/web3-1.0.0-beta.34.tgz#347e561b784098cb5563315f490479a1d91f2ab1" @@ -16410,7 +16993,7 @@ websocket-extensions@>=0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" -websocket@^1.0.24, websocket@^1.0.25: +websocket@1.0.25, websocket@^1.0.24, websocket@^1.0.25: version "1.0.25" resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.25.tgz#998ec790f0a3eacb8b08b50a4350026692a11958" dependencies: @@ -16653,10 +17236,25 @@ xhr-request@^1.0.1: url-set-query "^1.0.0" xhr "^2.0.4" +xhr2-cookies@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz#7d77449d0999197f155cb73b23df72505ed89d48" + dependencies: + cookiejar "^2.1.1" + xhr2@*, xhr2@0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/xhr2/-/xhr2-0.1.4.tgz#7f87658847716db5026323812f818cadab387a5f" +xhr@2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.3.3.tgz#ad6b810e0917ce72b5ec704f5d41f1503b8e7524" + dependencies: + global "~4.3.0" + is-function "^1.0.1" + parse-headers "^2.0.0" + xtend "^4.0.0" + xhr@^2.0.4, xhr@^2.2.0, xhr@^2.3.3: version "2.4.1" resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.4.1.tgz#ba982cced205ae5eec387169ac9dc77ca4853d38" -- cgit v1.2.3 From cd73a047efbc1b7afa884ef27a1cc4991e20efd8 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Wed, 19 Sep 2018 10:51:12 -0700 Subject: Remove old code. Create function for getting contract events via etherscan --- packages/pipeline/package.json | 45 +- packages/pipeline/src/artifacts.ts | 7 + packages/pipeline/src/artifacts/Exchange.json | 2262 +++++++++++++++++++++ packages/pipeline/src/events.ts | 75 + packages/pipeline/src/global.d.ts | 8 +- packages/pipeline/src/models/block.ts | 22 - packages/pipeline/src/models/event.ts | 84 - packages/pipeline/src/models/historical_prices.ts | 43 - packages/pipeline/src/models/order.ts | 30 - packages/pipeline/src/models/price.ts | 15 - packages/pipeline/src/models/relayer.ts | 75 - packages/pipeline/src/models/tokens.ts | 24 - packages/pipeline/src/models/transaction.ts | 36 - packages/pipeline/src/postgres.ts | 12 - packages/pipeline/src/run_jobs.ts | 87 - packages/pipeline/src/scrape_prices.ts | 10 - packages/pipeline/src/scripts/create_tables.ts | 258 --- packages/pipeline/src/scripts/join_tables.ts | 234 --- packages/pipeline/src/scripts/query_data.ts | 87 - packages/pipeline/src/scripts/scrape_data.ts | 649 ------ packages/pipeline/src/types.ts | 6 - packages/pipeline/src/utils.ts | 181 -- packages/pipeline/src/zrx.ts | 11 - packages/pipeline/tsconfig.json | 2 +- tsconfig.json | 1 + yarn.lock | 899 +++----- 26 files changed, 2634 insertions(+), 2529 deletions(-) create mode 100644 packages/pipeline/src/artifacts.ts create mode 100644 packages/pipeline/src/artifacts/Exchange.json create mode 100644 packages/pipeline/src/events.ts delete mode 100644 packages/pipeline/src/models/block.ts delete mode 100644 packages/pipeline/src/models/event.ts delete mode 100644 packages/pipeline/src/models/historical_prices.ts delete mode 100644 packages/pipeline/src/models/order.ts delete mode 100644 packages/pipeline/src/models/price.ts delete mode 100644 packages/pipeline/src/models/relayer.ts delete mode 100644 packages/pipeline/src/models/tokens.ts delete mode 100644 packages/pipeline/src/models/transaction.ts delete mode 100644 packages/pipeline/src/postgres.ts delete mode 100644 packages/pipeline/src/run_jobs.ts delete mode 100644 packages/pipeline/src/scrape_prices.ts delete mode 100644 packages/pipeline/src/scripts/create_tables.ts delete mode 100644 packages/pipeline/src/scripts/join_tables.ts delete mode 100644 packages/pipeline/src/scripts/query_data.ts delete mode 100644 packages/pipeline/src/scripts/scrape_data.ts delete mode 100644 packages/pipeline/src/types.ts delete mode 100644 packages/pipeline/src/utils.ts delete mode 100644 packages/pipeline/src/zrx.ts diff --git a/packages/pipeline/package.json b/packages/pipeline/package.json index 9d47f69f3..b4ed33d8d 100644 --- a/packages/pipeline/package.json +++ b/packages/pipeline/package.json @@ -4,49 +4,42 @@ "private": true, "description": "Data pipeline for offline analysis", "scripts": { - "build": "yarn tsc -b", + "build": "yarn pre_build && tsc -b", + "pre_build": "run-s update_artifacts copy_artifacts", + "copy_artifacts": "copyfiles -u 2 './src/artifacts/**/*.json' ./lib/src/artifacts", + "update_artifacts": "for i in ${npm_package_config_contracts_v2}; do copyfiles -u 4 ../migrations/artifacts/2.0.0/$i.json src/artifacts; done;", "test": "yarn run_mocha", "rebuild_and_test": "run-s build test", "test:circleci": "yarn test:coverage", "run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js --bail --exit", "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", "coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info", - "clean": "shx rm -rf lib", + "clean": "shx rm -rf lib src/artifacts", "lint": "tslint --project . --exclude **/src/generated_contract_wrappers/**/*" }, + "config": { + "contracts_v2": "Exchange", + "postpublish": { + "assets": [] + } + }, "repository": { "type": "git", - "url": "https://github.com/0xProject/0x.js.git" + "url": "https://github.com/0xProject/0x-monorepo" }, "license": "Apache-2.0", - "bugs": { - "url": "https://github.com/0xProject/0x.js/issues" - }, - "homepage": "https://github.com/0xProject/0x.js/packages/pipeline/README.md", "devDependencies": { "@0xproject/tslint-config": "^1.0.7", - "@types/command-line-args": "^4.0.2", - "@types/dotenv": "^4.0.2", - "@types/glob": "^5.0.33", - "@types/lodash": "4.14.104", - "@types/node": "^8.0.53", - "@types/pg": "^7.4.1", - "@types/request": "^2.0.13", - "shx": "^0.2.2", "tslint": "5.11.0", "typescript": "3.0.1" }, "dependencies": { - "0x.js": "^0.38.3", - "@0xproject/connect": "^0.6.14", - "airtable": "^0.5.2", - "command-line-args": "^4.0.7", - "dotenv": "^4.0.0", - "lodash": "^4.17.4", - "node-redshift": "^0.1.5", - "pg": "^7.4.1", - "queue": "^4.4.2", - "request": "^2.83.0", - "web3": "^0.20.0" + "@0xproject/contract-wrappers": "^1.0.1", + "@0xproject/subproviders": "^2.0.2", + "@0xproject/utils": "^1.0.8", + "@types/ramda": "^0.25.38", + "axios": "^0.18.0", + "ethereum-types": "^1.0.6", + "ramda": "^0.25.0" } } diff --git a/packages/pipeline/src/artifacts.ts b/packages/pipeline/src/artifacts.ts new file mode 100644 index 000000000..c0d7e11f6 --- /dev/null +++ b/packages/pipeline/src/artifacts.ts @@ -0,0 +1,7 @@ +import { ContractArtifact } from 'ethereum-types'; + +import * as Exchange from './artifacts/Exchange.json'; + +export const artifacts = { + Exchange: (Exchange as any) as ContractArtifact, +}; diff --git a/packages/pipeline/src/artifacts/Exchange.json b/packages/pipeline/src/artifacts/Exchange.json new file mode 100644 index 000000000..631bba974 --- /dev/null +++ b/packages/pipeline/src/artifacts/Exchange.json @@ -0,0 +1,2262 @@ +{ + "schemaVersion": "2.0.0", + "contractName": "Exchange", + "compilerOutput": { + "abi": [ + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "name": "filled", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "orders", + "type": "tuple[]" + }, + { + "name": "takerAssetFillAmounts", + "type": "uint256[]" + }, + { + "name": "signatures", + "type": "bytes[]" + } + ], + "name": "batchFillOrders", + "outputs": [ + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "totalFillResults", + "type": "tuple" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "name": "cancelled", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "hash", + "type": "bytes32" + }, + { + "name": "signerAddress", + "type": "address" + }, + { + "name": "signature", + "type": "bytes" + } + ], + "name": "preSign", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "leftOrder", + "type": "tuple" + }, + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "rightOrder", + "type": "tuple" + }, + { + "name": "leftSignature", + "type": "bytes" + }, + { + "name": "rightSignature", + "type": "bytes" + } + ], + "name": "matchOrders", + "outputs": [ + { + "components": [ + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "left", + "type": "tuple" + }, + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "right", + "type": "tuple" + }, + { + "name": "leftMakerAssetSpreadAmount", + "type": "uint256" + } + ], + "name": "matchedFillResults", + "type": "tuple" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "order", + "type": "tuple" + }, + { + "name": "takerAssetFillAmount", + "type": "uint256" + }, + { + "name": "signature", + "type": "bytes" + } + ], + "name": "fillOrderNoThrow", + "outputs": [ + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "fillResults", + "type": "tuple" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "bytes4" + } + ], + "name": "assetProxies", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "orders", + "type": "tuple[]" + } + ], + "name": "batchCancelOrders", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "orders", + "type": "tuple[]" + }, + { + "name": "takerAssetFillAmounts", + "type": "uint256[]" + }, + { + "name": "signatures", + "type": "bytes[]" + } + ], + "name": "batchFillOrKillOrders", + "outputs": [ + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "totalFillResults", + "type": "tuple" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "targetOrderEpoch", + "type": "uint256" + } + ], + "name": "cancelOrdersUpTo", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "orders", + "type": "tuple[]" + }, + { + "name": "takerAssetFillAmounts", + "type": "uint256[]" + }, + { + "name": "signatures", + "type": "bytes[]" + } + ], + "name": "batchFillOrdersNoThrow", + "outputs": [ + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "totalFillResults", + "type": "tuple" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "assetProxyId", + "type": "bytes4" + } + ], + "name": "getAssetProxy", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "name": "transactions", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "order", + "type": "tuple" + }, + { + "name": "takerAssetFillAmount", + "type": "uint256" + }, + { + "name": "signature", + "type": "bytes" + } + ], + "name": "fillOrKillOrder", + "outputs": [ + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "fillResults", + "type": "tuple" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "validatorAddress", + "type": "address" + }, + { + "name": "approval", + "type": "bool" + } + ], + "name": "setSignatureValidatorApproval", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + }, + { + "name": "", + "type": "address" + } + ], + "name": "allowedValidators", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "orders", + "type": "tuple[]" + }, + { + "name": "takerAssetFillAmount", + "type": "uint256" + }, + { + "name": "signatures", + "type": "bytes[]" + } + ], + "name": "marketSellOrders", + "outputs": [ + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "totalFillResults", + "type": "tuple" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "orders", + "type": "tuple[]" + } + ], + "name": "getOrdersInfo", + "outputs": [ + { + "components": [ + { + "name": "orderStatus", + "type": "uint8" + }, + { + "name": "orderHash", + "type": "bytes32" + }, + { + "name": "orderTakerAssetFilledAmount", + "type": "uint256" + } + ], + "name": "", + "type": "tuple[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "bytes32" + }, + { + "name": "", + "type": "address" + } + ], + "name": "preSigned", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "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": "hash", + "type": "bytes32" + }, + { + "name": "signerAddress", + "type": "address" + }, + { + "name": "signature", + "type": "bytes" + } + ], + "name": "isValidSignature", + "outputs": [ + { + "name": "isValid", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "orders", + "type": "tuple[]" + }, + { + "name": "makerAssetFillAmount", + "type": "uint256" + }, + { + "name": "signatures", + "type": "bytes[]" + } + ], + "name": "marketBuyOrdersNoThrow", + "outputs": [ + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "totalFillResults", + "type": "tuple" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "order", + "type": "tuple" + }, + { + "name": "takerAssetFillAmount", + "type": "uint256" + }, + { + "name": "signature", + "type": "bytes" + } + ], + "name": "fillOrder", + "outputs": [ + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "fillResults", + "type": "tuple" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "salt", + "type": "uint256" + }, + { + "name": "signerAddress", + "type": "address" + }, + { + "name": "data", + "type": "bytes" + }, + { + "name": "signature", + "type": "bytes" + } + ], + "name": "executeTransaction", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "assetProxy", + "type": "address" + } + ], + "name": "registerAssetProxy", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "order", + "type": "tuple" + } + ], + "name": "getOrderInfo", + "outputs": [ + { + "components": [ + { + "name": "orderStatus", + "type": "uint8" + }, + { + "name": "orderHash", + "type": "bytes32" + }, + { + "name": "orderTakerAssetFilledAmount", + "type": "uint256" + } + ], + "name": "orderInfo", + "type": "tuple" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "order", + "type": "tuple" + } + ], + "name": "cancelOrder", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + }, + { + "name": "", + "type": "address" + } + ], + "name": "orderEpoch", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "ZRX_ASSET_DATA", + "outputs": [ + { + "name": "", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "orders", + "type": "tuple[]" + }, + { + "name": "takerAssetFillAmount", + "type": "uint256" + }, + { + "name": "signatures", + "type": "bytes[]" + } + ], + "name": "marketSellOrdersNoThrow", + "outputs": [ + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "totalFillResults", + "type": "tuple" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "EIP712_DOMAIN_HASH", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "orders", + "type": "tuple[]" + }, + { + "name": "makerAssetFillAmount", + "type": "uint256" + }, + { + "name": "signatures", + "type": "bytes[]" + } + ], + "name": "marketBuyOrders", + "outputs": [ + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "totalFillResults", + "type": "tuple" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "currentContextAddress", + "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": "VERSION", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "signerAddress", + "type": "address" + }, + { + "indexed": true, + "name": "validatorAddress", + "type": "address" + }, + { + "indexed": false, + "name": "approved", + "type": "bool" + } + ], + "name": "SignatureValidatorApproval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "makerAddress", + "type": "address" + }, + { + "indexed": true, + "name": "feeRecipientAddress", + "type": "address" + }, + { + "indexed": false, + "name": "takerAddress", + "type": "address" + }, + { + "indexed": false, + "name": "senderAddress", + "type": "address" + }, + { + "indexed": false, + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "indexed": false, + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "indexed": false, + "name": "makerFeePaid", + "type": "uint256" + }, + { + "indexed": false, + "name": "takerFeePaid", + "type": "uint256" + }, + { + "indexed": true, + "name": "orderHash", + "type": "bytes32" + }, + { + "indexed": false, + "name": "makerAssetData", + "type": "bytes" + }, + { + "indexed": false, + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "Fill", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "makerAddress", + "type": "address" + }, + { + "indexed": true, + "name": "feeRecipientAddress", + "type": "address" + }, + { + "indexed": false, + "name": "senderAddress", + "type": "address" + }, + { + "indexed": true, + "name": "orderHash", + "type": "bytes32" + }, + { + "indexed": false, + "name": "makerAssetData", + "type": "bytes" + }, + { + "indexed": false, + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "Cancel", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "makerAddress", + "type": "address" + }, + { + "indexed": true, + "name": "senderAddress", + "type": "address" + }, + { + "indexed": false, + "name": "orderEpoch", + "type": "uint256" + } + ], + "name": "CancelUpTo", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "id", + "type": "bytes4" + }, + { + "indexed": false, + "name": "assetProxy", + "type": "address" + } + ], + "name": "AssetProxyRegistered", + "type": "event" + } + ], + "evm": { + "bytecode": { + "linkReferences": {}, + "object": "0x60806040526000805460ff191690553480156200001b57600080fd5b50604080517f454950373132446f6d61696e28000000000000000000000000000000000000006020808301919091527f737472696e67206e616d652c0000000000000000000000000000000000000000602d8301527f737472696e672076657273696f6e2c000000000000000000000000000000000060398301527f6164647265737320766572696679696e67436f6e74726163740000000000000060488301527f2900000000000000000000000000000000000000000000000000000000000000606183015282516042818403018152606290920192839052815191929182918401908083835b60208310620001245780518252601f19909201916020918201910162000103565b51815160209384036101000a6000190180199092169116179052604080519290940182900382208285018552600b8084527f30782050726f746f636f6c000000000000000000000000000000000000000000928401928352945190965091945090928392508083835b60208310620001ae5780518252601f1990920191602091820191016200018d565b51815160209384036101000a600019018019909216911617905260408051929094018290038220828501855260018084527f3200000000000000000000000000000000000000000000000000000000000000928401928352945190965091945090928392508083835b60208310620002385780518252601f19909201916020918201910162000217565b51815160209384036101000a6000190180199092169116179052604080519290940182900382208282019890985281840196909652606081019690965250306080808701919091528151808703909101815260a09095019081905284519093849350850191508083835b60208310620002c35780518252601f199092019160209182019101620002a2565b5181516000196020949094036101000a939093019283169219169190911790526040519201829003909120600155505060028054600160a060020a03191633179055506158b380620003166000396000f3006080604052600436106101b65763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663288cdc9181146101bb578063297bb70b146101f15780632ac126221461021e5780633683ef8e1461024b5780633c28d8611461026d5780633e228bae1461029a5780633fd3c997146102ba5780634ac14782146102e75780634d0ae546146103075780634f9559b11461032757806350dde190146103475780636070410814610367578063642f2eaf1461039457806364a3bc15146103b457806377fcce68146103d45780637b8e3514146103f45780637e1d9808146104145780637e9d74dc1461043457806382c174d0146104615780638da5cb5b146104815780639363470214610496578063a3e20380146104b6578063b4be83d5146104d6578063bfc8bfce146104f6578063c585bb9314610516578063c75e0a8114610536578063d46b02c314610563578063d9bfa73e14610583578063db123b1a146105a3578063dd1c7d18146105c5578063e306f779146105e5578063e5fa431b146105fa578063eea086ba1461061a578063f2fde38b1461062f578063ffa1ad741461064f575b600080fd5b3480156101c757600080fd5b506101db6101d6366004614800565b610664565b6040516101e89190615425565b60405180910390f35b3480156101fd57600080fd5b5061021161020c366004614723565b610676565b6040516101e891906156ff565b34801561022a57600080fd5b5061023e610239366004614800565b6107a1565b6040516101e89190615417565b34801561025757600080fd5b5061026b61026636600461483d565b6107b6565b005b34801561027957600080fd5b5061028d610288366004614971565b6108a3565b6040516101e8919061570d565b3480156102a657600080fd5b506102116102b5366004614a31565b610a3a565b3480156102c657600080fd5b506102da6102d5366004614900565b610a90565b6040516101e891906154e1565b3480156102f357600080fd5b5061026b6103023660046146ee565b610ab8565b34801561031357600080fd5b50610211610322366004614723565b610b85565b34801561033357600080fd5b5061026b610342366004614800565b610c75565b34801561035357600080fd5b50610211610362366004614723565b610e2a565b34801561037357600080fd5b50610387610382366004614900565b610ebe565b6040516101e89190615337565b3480156103a057600080fd5b5061023e6103af366004614800565b610f0c565b3480156103c057600080fd5b506102116103cf366004614a31565b610f21565b3480156103e057600080fd5b5061026b6103ef3660046146be565b610fcc565b34801561040057600080fd5b5061023e61040f366004614684565b611106565b34801561042057600080fd5b5061021161042f3660046147b7565b611126565b34801561044057600080fd5b5061045461044f3660046146ee565b61128a565b6040516101e89190615406565b34801561046d57600080fd5b5061023e61047c36600461481e565b61131f565b34801561048d57600080fd5b5061038761133f565b3480156104a257600080fd5b5061023e6104b13660046148a5565b61135b565b3480156104c257600080fd5b506102116104d13660046147b7565b6118de565b3480156104e257600080fd5b506102116104f1366004614a31565b6119f1565b34801561050257600080fd5b5061026b610511366004614a7a565b611a6c565b34801561052257600080fd5b5061026b610531366004614666565b611d05565b34801561054257600080fd5b5061055661055136600461493c565b611f30565b6040516101e8919061571c565b34801561056f57600080fd5b5061026b61057e36600461493c565b61202a565b34801561058f57600080fd5b506101db61059e366004614684565b6120c6565b3480156105af57600080fd5b506105b86120e3565b6040516101e891906154d0565b3480156105d157600080fd5b506102116105e03660046147b7565b612143565b3480156105f157600080fd5b506101db612218565b34801561060657600080fd5b506102116106153660046147b7565b61221e565b34801561062657600080fd5b50610387612390565b34801561063b57600080fd5b5061026b61064a366004614666565b6123ac565b34801561065b57600080fd5b506105b861245d565b60036020526000908152604090205481565b61067e614298565b600080610689614298565b60005460ff16156106cf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b60405180910390fd5b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011781558751935091505b81831461076f57610758878381518110151561071957fe5b90602001906020020151878481518110151561073157fe5b90602001906020020151878581518110151561074957fe5b90602001906020020151612494565b90506107648482612532565b600190910190610701565b5050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055509392505050565b60046020526000908152604090205460ff1681565b73ffffffffffffffffffffffffffffffffffffffff831633146108465761080e848484848080601f0160208091040260200160405190810160405280939291908181526020018383808284375061135b945050505050565b1515610846576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906155af565b5050600091825260066020908152604080842073ffffffffffffffffffffffffffffffffffffffff9093168452919052902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b6108ab6142c1565b6108b36142f0565b6108bb6142f0565b6000805460ff16156108f9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905561016080890151610140808a01919091528901519088015261094588611f30565b925061095087611f30565b915061095a612594565b9050610968888483896125c6565b610974878383886125c6565b61097e888861275e565b6109928888856040015185604001516127be565b8051602081015190519195506109ad918a9186918190612945565b6020808501519081015190516109c99189918591908190612945565b6109e28882856020015186604001518860000151612a5e565b6109fb8782846020015185604001518860200151612a5e565b610a0788888387612b0a565b5050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905550949350505050565b610a42614298565b6060610a4f858585612c92565b9050608081825160208401305af48015610a8657815183526020820151602084015260408201516040840152606082015160608401525b505b509392505050565b600a6020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b60008054819060ff1615610af8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b5050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011781558151905b808214610b5857610b508382815181101515610b4157fe5b90602001906020020151612e64565b600101610b29565b5050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905550565b610b8d614298565b600080610b98614298565b60005460ff1615610bd5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011781558751935091505b81831461076f57610c5e8783815181101515610c1f57fe5b906020019060200201518784815181101515610c3757fe5b906020019060200201518785815181101515610c4f57fe5b90602001906020020151612e8f565b9050610c6a8482612532565b600190910190610c07565b6000805481908190819060ff1615610cb9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055610cec612594565b935073ffffffffffffffffffffffffffffffffffffffff84163314610d115733610d14565b60005b73ffffffffffffffffffffffffffffffffffffffff8086166000908152600560209081526040808320938516835292905220549093506001860192509050808211610d8b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061563f565b73ffffffffffffffffffffffffffffffffffffffff80851660008181526005602090815260408083209488168084529490915290819020859055517f82af639571738f4ebd4268fb0363d8957ebe1bbb9e78dba5ebd69eed39b154f090610df3908690615425565b60405180910390a35050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055505050565b610e32614298565b600080610e3d614298565b86519250600091505b818314610eb457610e9d8783815181101515610e5e57fe5b906020019060200201518784815181101515610e7657fe5b906020019060200201518785815181101515610e8e57fe5b90602001906020020151610a3a565b9050610ea98482612532565b600190910190610e46565b5050509392505050565b7fffffffff0000000000000000000000000000000000000000000000000000000081166000908152600a602052604090205473ffffffffffffffffffffffffffffffffffffffff165b919050565b60086020526000908152604090205460ff1681565b610f29614298565b60005460ff1615610f66576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055610f9c848484612e8f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055949350505050565b6000805460ff161561100a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905561103d612594565b73ffffffffffffffffffffffffffffffffffffffff8181166000818152600760209081526040808320948916808452949091529081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168715151790555192935090917fa8656e308026eeabce8f0bc18048433252318ab80ac79da0b3d3d8697dfba891906110d1908690615417565b60405180910390a35050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905550565b600760209081526000928352604080842090915290825290205460ff1681565b61112e614298565b6060600080600061113d614298565b60005460ff161561117a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117815589518a919081106111b257fe5b906020019060200201516101600151945088519350600092505b828414611255578489848151811015156111e257fe5b906020019060200201516101600181905250611202888760200151612ee2565b915061122e898481518110151561121557fe5b9060200190602002015183898681518110151561074957fe5b905061123a8682612532565b6020860151881161124a57611255565b6001909201916111cc565b5050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055509195945050505050565b606060006060600084519250826040519080825280602002602001820160405280156112d057816020015b6112bd6142f0565b8152602001906001900390816112b55790505b509150600090505b808314610a88576112ff85828151811015156112f057fe5b90602001906020020151611f30565b828281518110151561130d57fe5b602090810290910101526001016112d8565b600660209081526000928352604080842090915290825290205460ff1681565b60025473ffffffffffffffffffffffffffffffffffffffff1681565b600080600080600080600080600089511115156113a4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061562f565b6113ad89612f29565b7f010000000000000000000000000000000000000000000000000000000000000090049650600760ff88161061140f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061554f565b8660ff16600781111561141e57fe5b9550600086600781111561142e57fe5b1415611466576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061561f565b600186600781111561147457fe5b14156114bc578851156114b3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906156ef565b600097506118d0565b60028660078111156114ca57fe5b141561160557885160411461150b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906154ef565b88600081518110151561151a57fe5b01602001517f010000000000000000000000000000000000000000000000000000000000000090819004810204945061155a89600163ffffffff612fed16565b935061156d89602163ffffffff612fed16565b925060018b868686604051600081526020016040526040516115929493929190615480565b60206040516020810390808403906000865af11580156115b6573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015173ffffffffffffffffffffffffffffffffffffffff8c811690821614995092506118d09050565b600386600781111561161357fe5b14156117b9578851604114611654576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906154ef565b88600081518110151561166357fe5b01602001517f01000000000000000000000000000000000000000000000000000000000000009081900481020494506116a389600163ffffffff612fed16565b93506116b689602163ffffffff612fed16565b925060018b60405160200180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c0182600019166000191681526020019150506040516020818303038152906040526040518082805190602001908083835b6020831061175757805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161171a565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018019909216911617905260408051929094018290038220600083529101928390526115929450925089918991508890615480565b60048660078111156117c757fe5b14156117df576117d88b8b8b613038565b97506118d0565b60058660078111156117ed57fe5b1415611850576117fc8961318d565b73ffffffffffffffffffffffffffffffffffffffff808c1660009081526007602090815260408083209385168352929052205490915060ff16151561184457600097506118d0565b6117d8818c8c8c613206565b600686600781111561185e57fe5b141561189e5760008b815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff8e16845290915290205460ff1697506118d0565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061554f565b505050505050509392505050565b6118e6614298565b60606000806000806118f6614298565b89600081518110151561190557fe5b906020019060200201516101400151955089519450600093505b8385146119e457858a8581518110151561193557fe5b6020908102909101015161014001528651611951908a90612ee2565b92506119948a8581518110151561196457fe5b9060200190602002015160a001518b8681518110151561198057fe5b906020019060200201516080015185613362565b91506119c08a858151811015156119a757fe5b90602001906020020151838a87815181101515610e8e57fe5b90506119cc8782612532565b865189116119d9576119e4565b60019093019261191f565b5050505050509392505050565b6119f9614298565b60005460ff1615611a36576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055610f9c848484612494565b60095460009073ffffffffffffffffffffffffffffffffffffffff1615611abf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b611b02611afd888888888080601f016020809104026020016040519081016040528093929190818152602001838380828437506133b8945050505050565b6135f9565b60008181526008602052604090205490915060ff1615611b4e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061559f565b73ffffffffffffffffffffffffffffffffffffffff86163314611c1f57611ba6818785858080601f0160208091040260200160405190810160405280939291908181526020018383808284375061135b945050505050565b1515611bde576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906156df565b600980547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff88161790555b6000818152600860205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555130908690869080838380828437820191505092505050600060405180830381855af49150501515611cb6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906155cf565b73ffffffffffffffffffffffffffffffffffffffff86163314611cfc57600980547fffffffffffffffffffffffff00000000000000000000000000000000000000001690555b50505050505050565b6002546000908190819073ffffffffffffffffffffffffffffffffffffffff163314611d5d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061568f565b8392508273ffffffffffffffffffffffffffffffffffffffff1663ae25532e6040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b158015611dc457600080fd5b505af1158015611dd8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611dfc919081019061491e565b7fffffffff0000000000000000000000000000000000000000000000000000000081166000908152600a602052604090205490925073ffffffffffffffffffffffffffffffffffffffff1690508015611e81576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061552f565b7fffffffff0000000000000000000000000000000000000000000000000000000082166000908152600a60205260409081902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8616179055517fd2c6b762299c609bdb96520b58a49bfb80186934d4f71a86a367571a15c0319490611f2290849087906154b5565b60405180910390a150505050565b611f386142f0565b611f4182613639565b6020808301829052600091825260039052604090819020549082015260808201511515611f755760015b60ff168152610f07565b60a08201511515611f87576002611f6b565b60a0820151604082015110611f9d576005611f6b565b6101008201514210611fb0576004611f6b565b60208082015160009081526004909152604090205460ff1615611fd4576006611f6b565b610120820151825173ffffffffffffffffffffffffffffffffffffffff90811660009081526005602090815260408083206060880151909416835292905220541115612021576006611f6b565b60038152919050565b60005460ff1615612067576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905561209b81612e64565b50600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055565b600560209081526000928352604080842090915290825290205481565b606060405190810160405280602481526020017ff47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f81526020017f4699f4980000000000000000000000000000000000000000000000000000000081525081565b61214b614298565b6060600080600061215a614298565b88600081518110151561216957fe5b906020019060200201516101600151945088519350600092505b82841461220c5784898481518110151561219957fe5b9060200190602002015161016001819052506121b9888760200151612ee2565b91506121e589848151811015156121cc57fe5b90602001906020020151838986815181101515610e8e57fe5b90506121f18682612532565b602086015188116122015761220c565b600190920191612183565b50505050509392505050565b60015481565b612226614298565b6060600080600080612236614298565b60005460ff1615612273576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011781558a518b919081106122ab57fe5b906020019060200201516101400151955089519450600093505b83851461235a57858a858151811015156122db57fe5b60209081029091010151610140015286516122f7908a90612ee2565b925061230a8a8581518110151561196457fe5b91506123368a8581518110151561231d57fe5b90602001906020020151838a8781518110151561074957fe5b90506123428782612532565b8651891161234f5761235a565b6001909301926122c5565b5050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905550929695505050505050565b60095473ffffffffffffffffffffffffffffffffffffffff1681565b60025473ffffffffffffffffffffffffffffffffffffffff1633146123fd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061568f565b73ffffffffffffffffffffffffffffffffffffffff81161561245a57600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83161790555b50565b60408051808201909152600581527f322e302e30000000000000000000000000000000000000000000000000000000602082015281565b61249c614298565b6124a46142f0565b60008060006124b288611f30565b93506124bc612594565b92506124ca888585896125c6565b6124dc8860a001518560400151612ee2565b91506124e88783613647565b90506124fb888589848960000151612945565b612505888261365d565b945061251c88848660200151876040015189612a5e565b6125278884876136be565b505050509392505050565b815181516125409190613776565b8252602080830151908201516125569190613776565b60208301526040808301519082015161256f9190613776565b6040830152606080830151908201516125889190613776565b60609092019190915250565b60095460009073ffffffffffffffffffffffffffffffffffffffff168181156125bd57816125bf565b335b9392505050565b825160ff16600314612604576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906156af565b606084015173ffffffffffffffffffffffffffffffffffffffff161561267757606084015173ffffffffffffffffffffffffffffffffffffffff163314612677576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906156bf565b602084015173ffffffffffffffffffffffffffffffffffffffff1615612702578173ffffffffffffffffffffffffffffffffffffffff16846020015173ffffffffffffffffffffffffffffffffffffffff16141515612702576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906154ff565b6040830151151561275857612720836020015185600001518361135b565b1515612758576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061556f565b50505050565b6127708260a001518260a001516137c0565b612782836080015183608001516137c0565b10156127ba576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906156cf565b5050565b6127c66142c1565b6000806000806127da8960a0015188612ee2565b93506127ef89608001518a60a001518661381b565b92506127ff8860a0015187612ee2565b915061281488608001518960a001518461381b565b90508084106128575760208086018051839052805182018490525151865182015260808a015160a08b015187519092015161284f929061381b565b855152612894565b845183905284516020908101859052855181015190860180519190915260a089015160808a015191515161288b9290613898565b60208087015101525b84515160208087015101516128a99190612ee2565b604086015284515160808a015160c08b01516128c692919061381b565b85516040015284516020015160a08a015160e08b01516128e792919061381b565b855160600152602085015151608089015160c08a015161290892919061381b565b856020015160400181815250506129308560200151602001518960a001518a60e0015161381b565b60208601516060015250505050949350505050565b82151561297e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906155ef565b828211156129b8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906155df565b8460a001516129cb856040015184613776565b1115612a03576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061550f565b612a118560800151836137c0565b612a1f828760a001516137c0565b1115612a57576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061566f565b5050505050565b612a6c828260200151613776565b600084815260036020908152604091829020929092558681015187518451938501518584015160608701516101408c01516101608d015196518b9873ffffffffffffffffffffffffffffffffffffffff9788169897909616967f0bcc4c97732e47d9946f229edb95f5b6323f601300e4690de719993f3c37112996612afb968f96339692959194909390615345565b60405180910390a45050505050565b60608060405190810160405280602481526020017ff47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f81526020017f4699f498000000000000000000000000000000000000000000000000000000008152509050612b8b85610140015186600001518660000151856020015160200151613935565b61014084015184518651845160200151612ba793929190613935565b612bc08561014001518660000151858560400151613935565b612bdc8186600001518760400151856000015160400151613935565b612bf88185600001518660400151856020015160400151613935565b836040015173ffffffffffffffffffffffffffffffffffffffff16856040015173ffffffffffffffffffffffffffffffffffffffff161415612c6257612c5d81848760400151612c58866000015160600151876020015160600151613776565b613935565b612a57565b612c7a81848760400151856000015160600151613935565b612a5781848660400151856020015160600151613935565b604080517fb4be83d5000000000000000000000000000000000000000000000000000000006020808301919091526060602483018181528751608485019081528884015160a48601529488015160c48501529087015160e4840152608087015161010484015260a087015161012484015260c087015161014484015260e08701516101648401526101008701516101848401526101208701516101a4840152610140870180516101c485019081526101608901516101e4860152610180905251805161020485018190529394919384936044870192849261022489019291820191601f82010460005b81811015612d99578351855260209485019490930192600101612d7b565b50505050818103610160808401919091528a0151805180835260209283019291820191601f82010460005b81811015612de2578351855260209485019490930192600101612dc4565b50505089845250848103602093840190815288518083529093918201918981019190601f82010460005b81811015612e2a578351855260209485019490930192600101612e0c565b5050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08883030188525060405250505050509392505050565b612e6c6142f0565b612e7582611f30565b9050612e818282613aff565b6127ba828260200151613c16565b612e97614298565b612ea2848484612494565b602081015190915083146125bf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061565f565b600082821115612f1e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061551f565b508082035b92915050565b6000808251111515612f67576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061560f565b815182907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101908110612f9757fe5b016020015182517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01909252507f0100000000000000000000000000000000000000000000000000000000000000908190040290565b60008160200183511015151561302f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061553f565b50016020015190565b6040516000906060907f1626ba7e00000000000000000000000000000000000000000000000000000000906130739087908690602401615460565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093178352815191935090829081885afa808015613110576001811461318157612527565b7f08c379a0000000000000000000000000000000000000000000000000000000006000527c20000000000000000000000000000000000000000000000000000000006020527c0c57414c4c45545f4552524f5200000000000000000000000000000000604052600060605260646000fd5b50505195945050505050565b600060148251101515156131cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061569f565b6131db826014845103613cbd565b82517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec019092525090565b6040516000906060907f93634702000000000000000000000000000000000000000000000000000000009061324390879087908790602401615433565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931783528151919350908290818a5afa8080156132e0576001811461335157613356565b7f08c379a0000000000000000000000000000000000000000000000000000000006000527c20000000000000000000000000000000000000000000000000000000006020527c0f56414c494441544f525f4552524f5200000000000000000000000000604052600060605260646000fd5b825194505b50505050949350505050565b600080831161339d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061555f565b6133b06133aa85846137c0565b84613d1e565b949350505050565b604080517f5a65726f45785472616e73616374696f6e2800000000000000000000000000006020808301919091527f75696e743235362073616c742c0000000000000000000000000000000000000060328301527f61646472657373207369676e6572416464726573732c00000000000000000000603f8301527f627974657320646174610000000000000000000000000000000000000000000060558301527f2900000000000000000000000000000000000000000000000000000000000000605f830152825180830384018152606090920192839052815160009384938493909282918401908083835b602083106134e157805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016134a4565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018019909216911617905260405191909301819003812089519097508995509093508392850191508083835b6020831061357757805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161353a565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040805192909401829003822097825281019a909a525073ffffffffffffffffffffffffffffffffffffffff97909716968801969096525050606085015250506080909120919050565b6001546040517f19010000000000000000000000000000000000000000000000000000000000008152600281019190915260228101919091526042902090565b6000612f23611afd83613d35565b600081831061365657816125bf565b5090919050565b613665614298565b6020810182905260a0830151608084015161368191849161381b565b808252608084015160c085015161369992919061381b565b604082015260a083015160e08401516136b391849161381b565b606082015292915050565b60408051606081018252602481527ff47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f60208201527f4699f4980000000000000000000000000000000000000000000000000000000091810191909152610140840151845183516137319291908690613935565b61374a8461016001518486600001518560200151613935565b61376281856000015186604001518560400151613935565b612758818486604001518560600151613935565b6000828201838110156137b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061558f565b8091505b5092915050565b6000808315156137d357600091506137b9565b508282028284828115156137e357fe5b04146137b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061558f565b6000808311613856576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061555f565b61386184848461418e565b1561339d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906155bf565b60008083116138d3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061555f565b6138de848484614213565b15613915576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906155bf565b6133b06133aa61392586856137c0565b613930866001612ee2565b613776565b60008060008311801561397457508373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614155b15613af75785516003106139b4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061564f565b50506020848101517fffffffff00000000000000000000000000000000000000000000000000000000166000818152600a90925260409091205473ffffffffffffffffffffffffffffffffffffffff16801515613a3d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906155ff565b604051660fffffffffffe0603f885101168060840182017fa85e59e40000000000000000000000000000000000000000000000000000000083526080600484015273ffffffffffffffffffffffffffffffffffffffff8816602484015273ffffffffffffffffffffffffffffffffffffffff87166044840152856064840152608483015b81811015613ad957895181526020998a019901613ac1565b61020084858403866000895af1801515613af1573d85fd5b50505050505b505050505050565b805160009060ff16600314613b40576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906156af565b606083015173ffffffffffffffffffffffffffffffffffffffff1615613bb357606083015173ffffffffffffffffffffffffffffffffffffffff163314613bb3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906156bf565b613bbb612594565b835190915073ffffffffffffffffffffffffffffffffffffffff808316911614613c11576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061557f565b505050565b6000818152600460205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558281015183516101408501516101608601519351859473ffffffffffffffffffffffffffffffffffffffff9485169493909316927fdc47b3613d9fe400085f6dbdc99453462279057e6207385042827ed6b1a62cf792613cb1923392906153c9565b60405180910390a45050565b600081601401835110151515613cff576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061569f565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b6000808284811515613d2c57fe5b04949350505050565b604080517f4f726465722800000000000000000000000000000000000000000000000000006020808301919091527f61646472657373206d616b6572416464726573732c000000000000000000000060268301527f616464726573732074616b6572416464726573732c0000000000000000000000603b8301527f6164647265737320666565526563697069656e74416464726573732c0000000060508301527f616464726573732073656e646572416464726573732c00000000000000000000606c8301527f75696e74323536206d616b65724173736574416d6f756e742c0000000000000060828301527f75696e743235362074616b65724173736574416d6f756e742c00000000000000609b8301527f75696e74323536206d616b65724665652c00000000000000000000000000000060b48301527f75696e743235362074616b65724665652c00000000000000000000000000000060c58301527f75696e743235362065787069726174696f6e54696d655365636f6e64732c000060d68301527f75696e743235362073616c742c0000000000000000000000000000000000000060f48301527f6279746573206d616b65724173736574446174612c00000000000000000000006101018301527f62797465732074616b65724173736574446174610000000000000000000000006101168301527f290000000000000000000000000000000000000000000000000000000000000061012a830152825161010b81840301815261012b90920192839052815160009384938493849391929182918401908083835b60208310613fbd57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101613f80565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930181900381206101408b0151805191995095509093508392850191508083835b6020831061405857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161401b565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930181900381206101608b0151805191985095509093508392850191508083835b602083106140f357805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016140b6565b5181516020939093036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018019909116921691909117905260405192018290039091207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0890180516101408b018051610160909c0180519a84529881529288526101a0822091529890525050509190525090919050565b6000808084116141ca576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061555f565b8215806141d5575084155b156141e35760009150610a88565b838015156141ed57fe5b85840990506141fc85846137c0565b6142086103e8836137c0565b101595945050505050565b60008080841161424f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061555f565b82158061425a575084155b156142685760009150610a88565b8380151561427257fe5b8584099050836142828583612ee2565b81151561428b57fe5b0690506141fc85846137c0565b608060405190810160405280600081526020016000815260200160008152602001600081525090565b610120604051908101604052806142d6614298565b81526020016142e3614298565b8152602001600081525090565b604080516060810182526000808252602082018190529181019190915290565b60006125bf82356157c2565b6000601f8201831361432d57600080fd5b813561434061433b82615751565b61572a565b81815260209384019390925082018360005b8381101561437e578135860161436888826144ce565b8452506020928301929190910190600101614352565b5050505092915050565b6000601f8201831361439957600080fd5b81356143a761433b82615751565b81815260209384019390925082018360005b8381101561437e57813586016143cf888261451d565b84525060209283019291909101906001016143b9565b6000601f820183136143f657600080fd5b813561440461433b82615751565b9150818183526020840193506020810190508385602084028201111561442957600080fd5b60005b8381101561437e578161443f8882614461565b845250602092830192919091019060010161442c565b60006125bf82356157db565b60006125bf82356157e0565b60006125bf82356157e3565b60006125bf82516157e3565b600080601f8301841361449757600080fd5b50813567ffffffffffffffff8111156144af57600080fd5b6020830191508360018202830111156144c757600080fd5b9250929050565b6000601f820183136144df57600080fd5b81356144ed61433b82615772565b9150808252602083016020830185838301111561450957600080fd5b614514838284615819565b50505092915050565b6000610180828403121561453057600080fd5b61453b61018061572a565b905060006145498484614310565b825250602061455a84848301614310565b602083015250604061456e84828501614310565b604083015250606061458284828501614310565b606083015250608061459684828501614461565b60808301525060a06145aa84828501614461565b60a08301525060c06145be84828501614461565b60c08301525060e06145d284828501614461565b60e0830152506101006145e784828501614461565b610100830152506101206145fd84828501614461565b6101208301525061014082013567ffffffffffffffff81111561461f57600080fd5b61462b848285016144ce565b6101408301525061016082013567ffffffffffffffff81111561464d57600080fd5b614659848285016144ce565b6101608301525092915050565b60006020828403121561467857600080fd5b60006133b08484614310565b6000806040838503121561469757600080fd5b60006146a38585614310565b92505060206146b485828601614310565b9150509250929050565b600080604083850312156146d157600080fd5b60006146dd8585614310565b92505060206146b485828601614455565b60006020828403121561470057600080fd5b813567ffffffffffffffff81111561471757600080fd5b6133b084828501614388565b60008060006060848603121561473857600080fd5b833567ffffffffffffffff81111561474f57600080fd5b61475b86828701614388565b935050602084013567ffffffffffffffff81111561477857600080fd5b614784868287016143e5565b925050604084013567ffffffffffffffff8111156147a157600080fd5b6147ad8682870161431c565b9150509250925092565b6000806000606084860312156147cc57600080fd5b833567ffffffffffffffff8111156147e357600080fd5b6147ef86828701614388565b935050602061478486828701614461565b60006020828403121561481257600080fd5b60006133b08484614461565b6000806040838503121561483157600080fd5b60006146a38585614461565b6000806000806060858703121561485357600080fd5b600061485f8787614461565b945050602061487087828801614310565b935050604085013567ffffffffffffffff81111561488d57600080fd5b61489987828801614485565b95989497509550505050565b6000806000606084860312156148ba57600080fd5b60006148c68686614461565b93505060206148d786828701614310565b925050604084013567ffffffffffffffff8111156148f457600080fd5b6147ad868287016144ce565b60006020828403121561491257600080fd5b60006133b0848461446d565b60006020828403121561493057600080fd5b60006133b08484614479565b60006020828403121561494e57600080fd5b813567ffffffffffffffff81111561496557600080fd5b6133b08482850161451d565b6000806000806080858703121561498757600080fd5b843567ffffffffffffffff81111561499e57600080fd5b6149aa8782880161451d565b945050602085013567ffffffffffffffff8111156149c757600080fd5b6149d38782880161451d565b935050604085013567ffffffffffffffff8111156149f057600080fd5b6149fc878288016144ce565b925050606085013567ffffffffffffffff811115614a1957600080fd5b614a25878288016144ce565b91505092959194509250565b600080600060608486031215614a4657600080fd5b833567ffffffffffffffff811115614a5d57600080fd5b614a698682870161451d565b93505060206148d786828701614461565b60008060008060008060808789031215614a9357600080fd5b6000614a9f8989614461565b9650506020614ab089828a01614310565b955050604087013567ffffffffffffffff811115614acd57600080fd5b614ad989828a01614485565b9450945050606087013567ffffffffffffffff811115614af857600080fd5b614b0489828a01614485565b92509250509295509295509295565b614b1c816157c2565b82525050565b6000614b2d826157be565b808452602084019350614b3f836157b8565b60005b82811015614b6f57614b558683516152f7565b614b5e826157b8565b606096909601959150600101614b42565b5093949350505050565b614b1c816157db565b614b1c816157e0565b614b1c816157e3565b6000614b9f826157be565b808452614bb3816020860160208601615825565b614bbc81615851565b9093016020019392505050565b614b1c8161580e565b601281527f4c454e4754485f36355f52455155495245440000000000000000000000000000602082015260400190565b600d81527f494e56414c49445f54414b455200000000000000000000000000000000000000602082015260400190565b600e81527f4f524445525f4f56455246494c4c000000000000000000000000000000000000602082015260400190565b601181527f55494e543235365f554e444552464c4f57000000000000000000000000000000602082015260400190565b601a81527f41535345545f50524f58595f414c52454144595f455849535453000000000000602082015260400190565b602681527f475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b601581527f5349474e41545552455f554e535550504f525445440000000000000000000000602082015260400190565b601081527f4449564953494f4e5f42595f5a45524f00000000000000000000000000000000602082015260400190565b601781527f494e56414c49445f4f524445525f5349474e4154555245000000000000000000602082015260400190565b600d81527f494e56414c49445f4d414b455200000000000000000000000000000000000000602082015260400190565b601081527f55494e543235365f4f564552464c4f5700000000000000000000000000000000602082015260400190565b600f81527f494e56414c49445f54585f484153480000000000000000000000000000000000602082015260400190565b601181527f494e56414c49445f5349474e4154555245000000000000000000000000000000602082015260400190565b600e81527f524f554e44494e475f4552524f52000000000000000000000000000000000000602082015260400190565b601081527f4641494c45445f455845435554494f4e00000000000000000000000000000000602082015260400190565b600d81527f54414b45525f4f56455250415900000000000000000000000000000000000000602082015260400190565b601481527f494e56414c49445f54414b45525f414d4f554e54000000000000000000000000602082015260400190565b601a81527f41535345545f50524f58595f444f45535f4e4f545f4558495354000000000000602082015260400190565b602181527f475245415445525f5448414e5f5a45524f5f4c454e4754485f5245515549524560208201527f4400000000000000000000000000000000000000000000000000000000000000604082015260600190565b601181527f5349474e41545552455f494c4c4547414c000000000000000000000000000000602082015260400190565b601e81527f4c454e4754485f475245415445525f5448414e5f305f52455155495245440000602082015260400190565b601781527f494e56414c49445f4e45575f4f524445525f45504f4348000000000000000000602082015260400190565b601e81527f4c454e4754485f475245415445525f5448414e5f335f52455155495245440000602082015260400190565b601481527f434f4d504c4554455f46494c4c5f4641494c4544000000000000000000000000602082015260400190565b601281527f494e56414c49445f46494c4c5f50524943450000000000000000000000000000602082015260400190565b601281527f5245454e5452414e43595f494c4c4547414c0000000000000000000000000000602082015260400190565b601381527f4f4e4c595f434f4e54524143545f4f574e455200000000000000000000000000602082015260400190565b602681527f475245415445525f4f525f455155414c5f544f5f32305f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b601081527f4f524445525f554e46494c4c41424c4500000000000000000000000000000000602082015260400190565b600e81527f494e56414c49445f53454e444552000000000000000000000000000000000000602082015260400190565b601881527f4e454741544956455f5350524541445f52455155495245440000000000000000602082015260400190565b601481527f494e56414c49445f54585f5349474e4154555245000000000000000000000000602082015260400190565b601181527f4c454e4754485f305f5245515549524544000000000000000000000000000000602082015260400190565b805160808301906152858482614b82565b5060208201516152986020850182614b82565b5060408201516152ab6040850182614b82565b5060608201516127586060850182614b82565b80516101208301906152d08482615274565b5060208201516152e36080850182615274565b506040820151612758610100850182614b82565b80516060830190615308848261532e565b50602082015161531b6020850182614b82565b5060408201516127586040850182614b82565b614b1c81615808565b60208101612f238284614b13565b6101008101615354828b614b13565b615361602083018a614b13565b61536e6040830189614b82565b61537b6060830188614b82565b6153886080830187614b82565b61539560a0830186614b82565b81810360c08301526153a78185614b94565b905081810360e08301526153bb8184614b94565b9a9950505050505050505050565b606081016153d78286614b13565b81810360208301526153e98185614b94565b905081810360408301526153fd8184614b94565b95945050505050565b602080825281016125bf8184614b22565b60208101612f238284614b79565b60208101612f238284614b82565b606081016154418286614b82565b61544e6020830185614b13565b81810360408301526153fd8184614b94565b6040810161546e8285614b82565b81810360208301526133b08184614b94565b6080810161548e8287614b82565b61549b602083018661532e565b6154a86040830185614b82565b6153fd6060830184614b82565b604081016154c38285614b8b565b6125bf6020830184614b13565b602080825281016125bf8184614b94565b60208101612f238284614bc9565b60208082528101612f2381614bd2565b60208082528101612f2381614c02565b60208082528101612f2381614c32565b60208082528101612f2381614c62565b60208082528101612f2381614c92565b60208082528101612f2381614cc2565b60208082528101612f2381614d18565b60208082528101612f2381614d48565b60208082528101612f2381614d78565b60208082528101612f2381614da8565b60208082528101612f2381614dd8565b60208082528101612f2381614e08565b60208082528101612f2381614e38565b60208082528101612f2381614e68565b60208082528101612f2381614e98565b60208082528101612f2381614ec8565b60208082528101612f2381614ef8565b60208082528101612f2381614f28565b60208082528101612f2381614f58565b60208082528101612f2381614fae565b60208082528101612f2381614fde565b60208082528101612f238161500e565b60208082528101612f238161503e565b60208082528101612f238161506e565b60208082528101612f238161509e565b60208082528101612f23816150ce565b60208082528101612f23816150fe565b60208082528101612f238161512e565b60208082528101612f2381615184565b60208082528101612f23816151b4565b60208082528101612f23816151e4565b60208082528101612f2381615214565b60208082528101612f2381615244565b60808101612f238284615274565b6101208101612f2382846152be565b60608101612f2382846152f7565b60405181810167ffffffffffffffff8111828210171561574957600080fd5b604052919050565b600067ffffffffffffffff82111561576857600080fd5b5060209081020190565b600067ffffffffffffffff82111561578957600080fd5b506020601f919091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160190565b60200190565b5190565b73ffffffffffffffffffffffffffffffffffffffff1690565b151590565b90565b7fffffffff000000000000000000000000000000000000000000000000000000001690565b60ff1690565b6000612f23826157c2565b82818337506000910152565b60005b83811015615840578181015183820152602001615828565b838111156127585750506000910152565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016905600a265627a7a72305820b19c92201032603398c007dccf80930c626ef89fa6c848c8b7ef5267d07cd4ed6c6578706572696d656e74616cf50037", + "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x0 DUP1 SLOAD PUSH1 0xFF NOT AND SWAP1 SSTORE CALLVALUE DUP1 ISZERO PUSH3 0x1B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x40 DUP1 MLOAD PUSH32 0x454950373132446F6D61696E2800000000000000000000000000000000000000 PUSH1 0x20 DUP1 DUP4 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH32 0x737472696E67206E616D652C0000000000000000000000000000000000000000 PUSH1 0x2D DUP4 ADD MSTORE PUSH32 0x737472696E672076657273696F6E2C0000000000000000000000000000000000 PUSH1 0x39 DUP4 ADD MSTORE PUSH32 0x6164647265737320766572696679696E67436F6E747261637400000000000000 PUSH1 0x48 DUP4 ADD MSTORE PUSH32 0x2900000000000000000000000000000000000000000000000000000000000000 PUSH1 0x61 DUP4 ADD MSTORE DUP3 MLOAD PUSH1 0x42 DUP2 DUP5 SUB ADD DUP2 MSTORE PUSH1 0x62 SWAP1 SWAP3 ADD SWAP3 DUP4 SWAP1 MSTORE DUP2 MLOAD SWAP2 SWAP3 SWAP2 DUP3 SWAP2 DUP5 ADD SWAP1 DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH3 0x124 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH1 0x1F NOT SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH3 0x103 JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 DUP5 SUB PUSH2 0x100 EXP PUSH1 0x0 NOT ADD DUP1 NOT SWAP1 SWAP3 AND SWAP2 AND OR SWAP1 MSTORE PUSH1 0x40 DUP1 MLOAD SWAP3 SWAP1 SWAP5 ADD DUP3 SWAP1 SUB DUP3 KECCAK256 DUP3 DUP6 ADD DUP6 MSTORE PUSH1 0xB DUP1 DUP5 MSTORE PUSH32 0x30782050726F746F636F6C000000000000000000000000000000000000000000 SWAP3 DUP5 ADD SWAP3 DUP4 MSTORE SWAP5 MLOAD SWAP1 SWAP7 POP SWAP2 SWAP5 POP SWAP1 SWAP3 DUP4 SWAP3 POP DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH3 0x1AE JUMPI DUP1 MLOAD DUP3 MSTORE PUSH1 0x1F NOT SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH3 0x18D JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 DUP5 SUB PUSH2 0x100 EXP PUSH1 0x0 NOT ADD DUP1 NOT SWAP1 SWAP3 AND SWAP2 AND OR SWAP1 MSTORE PUSH1 0x40 DUP1 MLOAD SWAP3 SWAP1 SWAP5 ADD DUP3 SWAP1 SUB DUP3 KECCAK256 DUP3 DUP6 ADD DUP6 MSTORE PUSH1 0x1 DUP1 DUP5 MSTORE PUSH32 0x3200000000000000000000000000000000000000000000000000000000000000 SWAP3 DUP5 ADD SWAP3 DUP4 MSTORE SWAP5 MLOAD SWAP1 SWAP7 POP SWAP2 SWAP5 POP SWAP1 SWAP3 DUP4 SWAP3 POP DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH3 0x238 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH1 0x1F NOT SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH3 0x217 JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 DUP5 SUB PUSH2 0x100 EXP PUSH1 0x0 NOT ADD DUP1 NOT SWAP1 SWAP3 AND SWAP2 AND OR SWAP1 MSTORE PUSH1 0x40 DUP1 MLOAD SWAP3 SWAP1 SWAP5 ADD DUP3 SWAP1 SUB DUP3 KECCAK256 DUP3 DUP3 ADD SWAP9 SWAP1 SWAP9 MSTORE DUP2 DUP5 ADD SWAP7 SWAP1 SWAP7 MSTORE PUSH1 0x60 DUP2 ADD SWAP7 SWAP1 SWAP7 MSTORE POP ADDRESS PUSH1 0x80 DUP1 DUP8 ADD SWAP2 SWAP1 SWAP2 MSTORE DUP2 MLOAD DUP1 DUP8 SUB SWAP1 SWAP2 ADD DUP2 MSTORE PUSH1 0xA0 SWAP1 SWAP6 ADD SWAP1 DUP2 SWAP1 MSTORE DUP5 MLOAD SWAP1 SWAP4 DUP5 SWAP4 POP DUP6 ADD SWAP2 POP DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH3 0x2C3 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH1 0x1F NOT SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH3 0x2A2 JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x0 NOT PUSH1 0x20 SWAP5 SWAP1 SWAP5 SUB PUSH2 0x100 EXP SWAP4 SWAP1 SWAP4 ADD SWAP3 DUP4 AND SWAP3 NOT AND SWAP2 SWAP1 SWAP2 OR SWAP1 MSTORE PUSH1 0x40 MLOAD SWAP3 ADD DUP3 SWAP1 SUB SWAP1 SWAP2 KECCAK256 PUSH1 0x1 SSTORE POP POP PUSH1 0x2 DUP1 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB NOT AND CALLER OR SWAP1 SSTORE POP PUSH2 0x58B3 DUP1 PUSH3 0x316 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0x1B6 JUMPI PUSH4 0xFFFFFFFF PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0x288CDC91 DUP2 EQ PUSH2 0x1BB JUMPI DUP1 PUSH4 0x297BB70B EQ PUSH2 0x1F1 JUMPI DUP1 PUSH4 0x2AC12622 EQ PUSH2 0x21E JUMPI DUP1 PUSH4 0x3683EF8E EQ PUSH2 0x24B JUMPI DUP1 PUSH4 0x3C28D861 EQ PUSH2 0x26D JUMPI DUP1 PUSH4 0x3E228BAE EQ PUSH2 0x29A JUMPI DUP1 PUSH4 0x3FD3C997 EQ PUSH2 0x2BA JUMPI DUP1 PUSH4 0x4AC14782 EQ PUSH2 0x2E7 JUMPI DUP1 PUSH4 0x4D0AE546 EQ PUSH2 0x307 JUMPI DUP1 PUSH4 0x4F9559B1 EQ PUSH2 0x327 JUMPI DUP1 PUSH4 0x50DDE190 EQ PUSH2 0x347 JUMPI DUP1 PUSH4 0x60704108 EQ PUSH2 0x367 JUMPI DUP1 PUSH4 0x642F2EAF EQ PUSH2 0x394 JUMPI DUP1 PUSH4 0x64A3BC15 EQ PUSH2 0x3B4 JUMPI DUP1 PUSH4 0x77FCCE68 EQ PUSH2 0x3D4 JUMPI DUP1 PUSH4 0x7B8E3514 EQ PUSH2 0x3F4 JUMPI DUP1 PUSH4 0x7E1D9808 EQ PUSH2 0x414 JUMPI DUP1 PUSH4 0x7E9D74DC EQ PUSH2 0x434 JUMPI DUP1 PUSH4 0x82C174D0 EQ PUSH2 0x461 JUMPI DUP1 PUSH4 0x8DA5CB5B EQ PUSH2 0x481 JUMPI DUP1 PUSH4 0x93634702 EQ PUSH2 0x496 JUMPI DUP1 PUSH4 0xA3E20380 EQ PUSH2 0x4B6 JUMPI DUP1 PUSH4 0xB4BE83D5 EQ PUSH2 0x4D6 JUMPI DUP1 PUSH4 0xBFC8BFCE EQ PUSH2 0x4F6 JUMPI DUP1 PUSH4 0xC585BB93 EQ PUSH2 0x516 JUMPI DUP1 PUSH4 0xC75E0A81 EQ PUSH2 0x536 JUMPI DUP1 PUSH4 0xD46B02C3 EQ PUSH2 0x563 JUMPI DUP1 PUSH4 0xD9BFA73E EQ PUSH2 0x583 JUMPI DUP1 PUSH4 0xDB123B1A EQ PUSH2 0x5A3 JUMPI DUP1 PUSH4 0xDD1C7D18 EQ PUSH2 0x5C5 JUMPI DUP1 PUSH4 0xE306F779 EQ PUSH2 0x5E5 JUMPI DUP1 PUSH4 0xE5FA431B EQ PUSH2 0x5FA JUMPI DUP1 PUSH4 0xEEA086BA EQ PUSH2 0x61A JUMPI DUP1 PUSH4 0xF2FDE38B EQ PUSH2 0x62F JUMPI DUP1 PUSH4 0xFFA1AD74 EQ PUSH2 0x64F JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1C7 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1DB PUSH2 0x1D6 CALLDATASIZE PUSH1 0x4 PUSH2 0x4800 JUMP JUMPDEST PUSH2 0x664 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x5425 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1FD JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x20C CALLDATASIZE PUSH1 0x4 PUSH2 0x4723 JUMP JUMPDEST PUSH2 0x676 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x56FF JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x22A JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x23E PUSH2 0x239 CALLDATASIZE PUSH1 0x4 PUSH2 0x4800 JUMP JUMPDEST PUSH2 0x7A1 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x5417 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x257 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x266 CALLDATASIZE PUSH1 0x4 PUSH2 0x483D JUMP JUMPDEST PUSH2 0x7B6 JUMP JUMPDEST STOP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x279 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x28D PUSH2 0x288 CALLDATASIZE PUSH1 0x4 PUSH2 0x4971 JUMP JUMPDEST PUSH2 0x8A3 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x570D JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x2A6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x2B5 CALLDATASIZE PUSH1 0x4 PUSH2 0x4A31 JUMP JUMPDEST PUSH2 0xA3A JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x2C6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x2DA PUSH2 0x2D5 CALLDATASIZE PUSH1 0x4 PUSH2 0x4900 JUMP JUMPDEST PUSH2 0xA90 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x54E1 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x2F3 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x302 CALLDATASIZE PUSH1 0x4 PUSH2 0x46EE JUMP JUMPDEST PUSH2 0xAB8 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x313 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x322 CALLDATASIZE PUSH1 0x4 PUSH2 0x4723 JUMP JUMPDEST PUSH2 0xB85 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x333 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x342 CALLDATASIZE PUSH1 0x4 PUSH2 0x4800 JUMP JUMPDEST PUSH2 0xC75 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x353 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x362 CALLDATASIZE PUSH1 0x4 PUSH2 0x4723 JUMP JUMPDEST PUSH2 0xE2A JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x373 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x387 PUSH2 0x382 CALLDATASIZE PUSH1 0x4 PUSH2 0x4900 JUMP JUMPDEST PUSH2 0xEBE JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x5337 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x3A0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x23E PUSH2 0x3AF CALLDATASIZE PUSH1 0x4 PUSH2 0x4800 JUMP JUMPDEST PUSH2 0xF0C JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x3C0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x3CF CALLDATASIZE PUSH1 0x4 PUSH2 0x4A31 JUMP JUMPDEST PUSH2 0xF21 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x3E0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x3EF CALLDATASIZE PUSH1 0x4 PUSH2 0x46BE JUMP JUMPDEST PUSH2 0xFCC JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x400 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x23E PUSH2 0x40F CALLDATASIZE PUSH1 0x4 PUSH2 0x4684 JUMP JUMPDEST PUSH2 0x1106 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x420 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x42F CALLDATASIZE PUSH1 0x4 PUSH2 0x47B7 JUMP JUMPDEST PUSH2 0x1126 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x440 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x454 PUSH2 0x44F CALLDATASIZE PUSH1 0x4 PUSH2 0x46EE JUMP JUMPDEST PUSH2 0x128A JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x5406 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x46D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x23E PUSH2 0x47C CALLDATASIZE PUSH1 0x4 PUSH2 0x481E JUMP JUMPDEST PUSH2 0x131F JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x48D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x387 PUSH2 0x133F JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x4A2 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x23E PUSH2 0x4B1 CALLDATASIZE PUSH1 0x4 PUSH2 0x48A5 JUMP JUMPDEST PUSH2 0x135B JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x4C2 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x4D1 CALLDATASIZE PUSH1 0x4 PUSH2 0x47B7 JUMP JUMPDEST PUSH2 0x18DE JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x4E2 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x4F1 CALLDATASIZE PUSH1 0x4 PUSH2 0x4A31 JUMP JUMPDEST PUSH2 0x19F1 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x502 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x511 CALLDATASIZE PUSH1 0x4 PUSH2 0x4A7A JUMP JUMPDEST PUSH2 0x1A6C JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x522 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x531 CALLDATASIZE PUSH1 0x4 PUSH2 0x4666 JUMP JUMPDEST PUSH2 0x1D05 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x542 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x556 PUSH2 0x551 CALLDATASIZE PUSH1 0x4 PUSH2 0x493C JUMP JUMPDEST PUSH2 0x1F30 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x571C JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x56F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x57E CALLDATASIZE PUSH1 0x4 PUSH2 0x493C JUMP JUMPDEST PUSH2 0x202A JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x58F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1DB PUSH2 0x59E CALLDATASIZE PUSH1 0x4 PUSH2 0x4684 JUMP JUMPDEST PUSH2 0x20C6 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x5AF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x5B8 PUSH2 0x20E3 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x54D0 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x5D1 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x5E0 CALLDATASIZE PUSH1 0x4 PUSH2 0x47B7 JUMP JUMPDEST PUSH2 0x2143 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x5F1 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1DB PUSH2 0x2218 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x606 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x615 CALLDATASIZE PUSH1 0x4 PUSH2 0x47B7 JUMP JUMPDEST PUSH2 0x221E JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x626 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x387 PUSH2 0x2390 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x63B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x64A CALLDATASIZE PUSH1 0x4 PUSH2 0x4666 JUMP JUMPDEST PUSH2 0x23AC JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x65B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x5B8 PUSH2 0x245D JUMP JUMPDEST PUSH1 0x3 PUSH1 0x20 MSTORE PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP2 JUMP JUMPDEST PUSH2 0x67E PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH2 0x689 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x6CF JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR DUP2 SSTORE DUP8 MLOAD SWAP4 POP SWAP2 POP JUMPDEST DUP2 DUP4 EQ PUSH2 0x76F JUMPI PUSH2 0x758 DUP8 DUP4 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x719 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP8 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x731 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP8 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x749 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x2494 JUMP JUMPDEST SWAP1 POP PUSH2 0x764 DUP5 DUP3 PUSH2 0x2532 JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP2 ADD SWAP1 PUSH2 0x701 JUMP JUMPDEST POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x4 PUSH1 0x20 MSTORE PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND CALLER EQ PUSH2 0x846 JUMPI PUSH2 0x80E DUP5 DUP5 DUP5 DUP5 DUP1 DUP1 PUSH1 0x1F ADD PUSH1 0x20 DUP1 SWAP2 DIV MUL PUSH1 0x20 ADD PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 SWAP4 SWAP3 SWAP2 SWAP1 DUP2 DUP2 MSTORE PUSH1 0x20 ADD DUP4 DUP4 DUP1 DUP3 DUP5 CALLDATACOPY POP PUSH2 0x135B SWAP5 POP POP POP POP POP JUMP JUMPDEST ISZERO ISZERO PUSH2 0x846 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x55AF JUMP JUMPDEST POP POP PUSH1 0x0 SWAP2 DUP3 MSTORE PUSH1 0x6 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP4 AND DUP5 MSTORE SWAP2 SWAP1 MSTORE SWAP1 KECCAK256 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE JUMP JUMPDEST PUSH2 0x8AB PUSH2 0x42C1 JUMP JUMPDEST PUSH2 0x8B3 PUSH2 0x42F0 JUMP JUMPDEST PUSH2 0x8BB PUSH2 0x42F0 JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x8F9 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE PUSH2 0x160 DUP1 DUP10 ADD MLOAD PUSH2 0x140 DUP1 DUP11 ADD SWAP2 SWAP1 SWAP2 MSTORE DUP10 ADD MLOAD SWAP1 DUP9 ADD MSTORE PUSH2 0x945 DUP9 PUSH2 0x1F30 JUMP JUMPDEST SWAP3 POP PUSH2 0x950 DUP8 PUSH2 0x1F30 JUMP JUMPDEST SWAP2 POP PUSH2 0x95A PUSH2 0x2594 JUMP JUMPDEST SWAP1 POP PUSH2 0x968 DUP9 DUP5 DUP4 DUP10 PUSH2 0x25C6 JUMP JUMPDEST PUSH2 0x974 DUP8 DUP4 DUP4 DUP9 PUSH2 0x25C6 JUMP JUMPDEST PUSH2 0x97E DUP9 DUP9 PUSH2 0x275E JUMP JUMPDEST PUSH2 0x992 DUP9 DUP9 DUP6 PUSH1 0x40 ADD MLOAD DUP6 PUSH1 0x40 ADD MLOAD PUSH2 0x27BE JUMP JUMPDEST DUP1 MLOAD PUSH1 0x20 DUP2 ADD MLOAD SWAP1 MLOAD SWAP2 SWAP6 POP PUSH2 0x9AD SWAP2 DUP11 SWAP2 DUP7 SWAP2 DUP2 SWAP1 PUSH2 0x2945 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP6 ADD MLOAD SWAP1 DUP2 ADD MLOAD SWAP1 MLOAD PUSH2 0x9C9 SWAP2 DUP10 SWAP2 DUP6 SWAP2 SWAP1 DUP2 SWAP1 PUSH2 0x2945 JUMP JUMPDEST PUSH2 0x9E2 DUP9 DUP3 DUP6 PUSH1 0x20 ADD MLOAD DUP7 PUSH1 0x40 ADD MLOAD DUP9 PUSH1 0x0 ADD MLOAD PUSH2 0x2A5E JUMP JUMPDEST PUSH2 0x9FB DUP8 DUP3 DUP5 PUSH1 0x20 ADD MLOAD DUP6 PUSH1 0x40 ADD MLOAD DUP9 PUSH1 0x20 ADD MLOAD PUSH2 0x2A5E JUMP JUMPDEST PUSH2 0xA07 DUP9 DUP9 DUP4 DUP8 PUSH2 0x2B0A JUMP JUMPDEST POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE POP SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH2 0xA42 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x60 PUSH2 0xA4F DUP6 DUP6 DUP6 PUSH2 0x2C92 JUMP JUMPDEST SWAP1 POP PUSH1 0x80 DUP2 DUP3 MLOAD PUSH1 0x20 DUP5 ADD ADDRESS GAS DELEGATECALL DUP1 ISZERO PUSH2 0xA86 JUMPI DUP2 MLOAD DUP4 MSTORE PUSH1 0x20 DUP3 ADD MLOAD PUSH1 0x20 DUP5 ADD MSTORE PUSH1 0x40 DUP3 ADD MLOAD PUSH1 0x40 DUP5 ADD MSTORE PUSH1 0x60 DUP3 ADD MLOAD PUSH1 0x60 DUP5 ADD MSTORE JUMPDEST POP JUMPDEST POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0xA PUSH1 0x20 MSTORE PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD DUP2 SWAP1 PUSH1 0xFF AND ISZERO PUSH2 0xAF8 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR DUP2 SSTORE DUP2 MLOAD SWAP1 JUMPDEST DUP1 DUP3 EQ PUSH2 0xB58 JUMPI PUSH2 0xB50 DUP4 DUP3 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xB41 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x2E64 JUMP JUMPDEST PUSH1 0x1 ADD PUSH2 0xB29 JUMP JUMPDEST POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE POP JUMP JUMPDEST PUSH2 0xB8D PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH2 0xB98 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0xBD5 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR DUP2 SSTORE DUP8 MLOAD SWAP4 POP SWAP2 POP JUMPDEST DUP2 DUP4 EQ PUSH2 0x76F JUMPI PUSH2 0xC5E DUP8 DUP4 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xC1F JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP8 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xC37 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP8 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xC4F JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x2E8F JUMP JUMPDEST SWAP1 POP PUSH2 0xC6A DUP5 DUP3 PUSH2 0x2532 JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP2 ADD SWAP1 PUSH2 0xC07 JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD DUP2 SWAP1 DUP2 SWAP1 DUP2 SWAP1 PUSH1 0xFF AND ISZERO PUSH2 0xCB9 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE PUSH2 0xCEC PUSH2 0x2594 JUMP JUMPDEST SWAP4 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 AND CALLER EQ PUSH2 0xD11 JUMPI CALLER PUSH2 0xD14 JUMP JUMPDEST PUSH1 0x0 JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP7 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x5 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP4 DUP6 AND DUP4 MSTORE SWAP3 SWAP1 MSTORE KECCAK256 SLOAD SWAP1 SWAP4 POP PUSH1 0x1 DUP7 ADD SWAP3 POP SWAP1 POP DUP1 DUP3 GT PUSH2 0xD8B JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x563F JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP6 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x5 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP5 DUP9 AND DUP1 DUP5 MSTORE SWAP5 SWAP1 SWAP2 MSTORE SWAP1 DUP2 SWAP1 KECCAK256 DUP6 SWAP1 SSTORE MLOAD PUSH32 0x82AF639571738F4EBD4268FB0363D8957EBE1BBB9E78DBA5EBD69EED39B154F0 SWAP1 PUSH2 0xDF3 SWAP1 DUP7 SWAP1 PUSH2 0x5425 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE POP POP POP JUMP JUMPDEST PUSH2 0xE32 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH2 0xE3D PUSH2 0x4298 JUMP JUMPDEST DUP7 MLOAD SWAP3 POP PUSH1 0x0 SWAP2 POP JUMPDEST DUP2 DUP4 EQ PUSH2 0xEB4 JUMPI PUSH2 0xE9D DUP8 DUP4 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xE5E JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP8 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xE76 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP8 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xE8E JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0xA3A JUMP JUMPDEST SWAP1 POP PUSH2 0xEA9 DUP5 DUP3 PUSH2 0x2532 JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP2 ADD SWAP1 PUSH2 0xE46 JUMP JUMPDEST POP POP POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 DUP2 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0xA PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND JUMPDEST SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x8 PUSH1 0x20 MSTORE PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND DUP2 JUMP JUMPDEST PUSH2 0xF29 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0xF66 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE PUSH2 0xF9C DUP5 DUP5 DUP5 PUSH2 0x2E8F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x100A JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE PUSH2 0x103D PUSH2 0x2594 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 DUP2 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x7 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP5 DUP10 AND DUP1 DUP5 MSTORE SWAP5 SWAP1 SWAP2 MSTORE SWAP1 DUP2 SWAP1 KECCAK256 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND DUP8 ISZERO ISZERO OR SWAP1 SSTORE MLOAD SWAP3 SWAP4 POP SWAP1 SWAP2 PUSH32 0xA8656E308026EEABCE8F0BC18048433252318AB80AC79DA0B3D3D8697DFBA891 SWAP1 PUSH2 0x10D1 SWAP1 DUP7 SWAP1 PUSH2 0x5417 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE POP JUMP JUMPDEST PUSH1 0x7 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x0 SWAP3 DUP4 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 SWAP1 SWAP2 MSTORE SWAP1 DUP3 MSTORE SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND DUP2 JUMP JUMPDEST PUSH2 0x112E PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x60 PUSH1 0x0 DUP1 PUSH1 0x0 PUSH2 0x113D PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x117A JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR DUP2 SSTORE DUP10 MLOAD DUP11 SWAP2 SWAP1 DUP2 LT PUSH2 0x11B2 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x160 ADD MLOAD SWAP5 POP DUP9 MLOAD SWAP4 POP PUSH1 0x0 SWAP3 POP JUMPDEST DUP3 DUP5 EQ PUSH2 0x1255 JUMPI DUP5 DUP10 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x11E2 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x160 ADD DUP2 SWAP1 MSTORE POP PUSH2 0x1202 DUP9 DUP8 PUSH1 0x20 ADD MLOAD PUSH2 0x2EE2 JUMP JUMPDEST SWAP2 POP PUSH2 0x122E DUP10 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x1215 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP4 DUP10 DUP7 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x749 JUMPI INVALID JUMPDEST SWAP1 POP PUSH2 0x123A DUP7 DUP3 PUSH2 0x2532 JUMP JUMPDEST PUSH1 0x20 DUP7 ADD MLOAD DUP9 GT PUSH2 0x124A JUMPI PUSH2 0x1255 JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP3 ADD SWAP2 PUSH2 0x11CC JUMP JUMPDEST POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE POP SWAP2 SWAP6 SWAP5 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x60 PUSH1 0x0 PUSH1 0x60 PUSH1 0x0 DUP5 MLOAD SWAP3 POP DUP3 PUSH1 0x40 MLOAD SWAP1 DUP1 DUP3 MSTORE DUP1 PUSH1 0x20 MUL PUSH1 0x20 ADD DUP3 ADD PUSH1 0x40 MSTORE DUP1 ISZERO PUSH2 0x12D0 JUMPI DUP2 PUSH1 0x20 ADD JUMPDEST PUSH2 0x12BD PUSH2 0x42F0 JUMP JUMPDEST DUP2 MSTORE PUSH1 0x20 ADD SWAP1 PUSH1 0x1 SWAP1 SUB SWAP1 DUP2 PUSH2 0x12B5 JUMPI SWAP1 POP JUMPDEST POP SWAP2 POP PUSH1 0x0 SWAP1 POP JUMPDEST DUP1 DUP4 EQ PUSH2 0xA88 JUMPI PUSH2 0x12FF DUP6 DUP3 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x12F0 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x1F30 JUMP JUMPDEST DUP3 DUP3 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x130D JUMPI INVALID JUMPDEST PUSH1 0x20 SWAP1 DUP2 MUL SWAP1 SWAP2 ADD ADD MSTORE PUSH1 0x1 ADD PUSH2 0x12D8 JUMP JUMPDEST PUSH1 0x6 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x0 SWAP3 DUP4 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 SWAP1 SWAP2 MSTORE SWAP1 DUP3 MSTORE SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND DUP2 JUMP JUMPDEST PUSH1 0x2 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x0 DUP10 MLOAD GT ISZERO ISZERO PUSH2 0x13A4 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x562F JUMP JUMPDEST PUSH2 0x13AD DUP10 PUSH2 0x2F29 JUMP JUMPDEST PUSH32 0x100000000000000000000000000000000000000000000000000000000000000 SWAP1 DIV SWAP7 POP PUSH1 0x7 PUSH1 0xFF DUP9 AND LT PUSH2 0x140F JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x554F JUMP JUMPDEST DUP7 PUSH1 0xFF AND PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x141E JUMPI INVALID JUMPDEST SWAP6 POP PUSH1 0x0 DUP7 PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x142E JUMPI INVALID JUMPDEST EQ ISZERO PUSH2 0x1466 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x561F JUMP JUMPDEST PUSH1 0x1 DUP7 PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x1474 JUMPI INVALID JUMPDEST EQ ISZERO PUSH2 0x14BC JUMPI DUP9 MLOAD ISZERO PUSH2 0x14B3 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x56EF JUMP JUMPDEST PUSH1 0x0 SWAP8 POP PUSH2 0x18D0 JUMP JUMPDEST PUSH1 0x2 DUP7 PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x14CA JUMPI INVALID JUMPDEST EQ ISZERO PUSH2 0x1605 JUMPI DUP9 MLOAD PUSH1 0x41 EQ PUSH2 0x150B JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x54EF JUMP JUMPDEST DUP9 PUSH1 0x0 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x151A JUMPI INVALID JUMPDEST ADD PUSH1 0x20 ADD MLOAD PUSH32 0x100000000000000000000000000000000000000000000000000000000000000 SWAP1 DUP2 SWAP1 DIV DUP2 MUL DIV SWAP5 POP PUSH2 0x155A DUP10 PUSH1 0x1 PUSH4 0xFFFFFFFF PUSH2 0x2FED AND JUMP JUMPDEST SWAP4 POP PUSH2 0x156D DUP10 PUSH1 0x21 PUSH4 0xFFFFFFFF PUSH2 0x2FED AND JUMP JUMPDEST SWAP3 POP PUSH1 0x1 DUP12 DUP7 DUP7 DUP7 PUSH1 0x40 MLOAD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x40 MSTORE PUSH1 0x40 MLOAD PUSH2 0x1592 SWAP5 SWAP4 SWAP3 SWAP2 SWAP1 PUSH2 0x5480 JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD PUSH1 0x20 DUP2 SUB SWAP1 DUP1 DUP5 SUB SWAP1 PUSH1 0x0 DUP7 GAS CALL ISZERO DUP1 ISZERO PUSH2 0x15B6 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP PUSH1 0x40 MLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP13 DUP2 AND SWAP1 DUP3 AND EQ SWAP10 POP SWAP3 POP PUSH2 0x18D0 SWAP1 POP JUMP JUMPDEST PUSH1 0x3 DUP7 PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x1613 JUMPI INVALID JUMPDEST EQ ISZERO PUSH2 0x17B9 JUMPI DUP9 MLOAD PUSH1 0x41 EQ PUSH2 0x1654 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x54EF JUMP JUMPDEST DUP9 PUSH1 0x0 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x1663 JUMPI INVALID JUMPDEST ADD PUSH1 0x20 ADD MLOAD PUSH32 0x100000000000000000000000000000000000000000000000000000000000000 SWAP1 DUP2 SWAP1 DIV DUP2 MUL DIV SWAP5 POP PUSH2 0x16A3 DUP10 PUSH1 0x1 PUSH4 0xFFFFFFFF PUSH2 0x2FED AND JUMP JUMPDEST SWAP4 POP PUSH2 0x16B6 DUP10 PUSH1 0x21 PUSH4 0xFFFFFFFF PUSH2 0x2FED AND JUMP JUMPDEST SWAP3 POP PUSH1 0x1 DUP12 PUSH1 0x40 MLOAD PUSH1 0x20 ADD DUP1 DUP1 PUSH32 0x19457468657265756D205369676E6564204D6573736167653A0A333200000000 DUP2 MSTORE POP PUSH1 0x1C ADD DUP3 PUSH1 0x0 NOT AND PUSH1 0x0 NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP POP PUSH1 0x40 MLOAD PUSH1 0x20 DUP2 DUP4 SUB SUB DUP2 MSTORE SWAP1 PUSH1 0x40 MSTORE PUSH1 0x40 MLOAD DUP1 DUP3 DUP1 MLOAD SWAP1 PUSH1 0x20 ADD SWAP1 DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH2 0x1757 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0x171A JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 DUP5 SUB PUSH2 0x100 EXP PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ADD DUP1 NOT SWAP1 SWAP3 AND SWAP2 AND OR SWAP1 MSTORE PUSH1 0x40 DUP1 MLOAD SWAP3 SWAP1 SWAP5 ADD DUP3 SWAP1 SUB DUP3 KECCAK256 PUSH1 0x0 DUP4 MSTORE SWAP2 ADD SWAP3 DUP4 SWAP1 MSTORE PUSH2 0x1592 SWAP5 POP SWAP3 POP DUP10 SWAP2 DUP10 SWAP2 POP DUP9 SWAP1 PUSH2 0x5480 JUMP JUMPDEST PUSH1 0x4 DUP7 PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x17C7 JUMPI INVALID JUMPDEST EQ ISZERO PUSH2 0x17DF JUMPI PUSH2 0x17D8 DUP12 DUP12 DUP12 PUSH2 0x3038 JUMP JUMPDEST SWAP8 POP PUSH2 0x18D0 JUMP JUMPDEST PUSH1 0x5 DUP7 PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x17ED JUMPI INVALID JUMPDEST EQ ISZERO PUSH2 0x1850 JUMPI PUSH2 0x17FC DUP10 PUSH2 0x318D JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP13 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x7 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP4 DUP6 AND DUP4 MSTORE SWAP3 SWAP1 MSTORE KECCAK256 SLOAD SWAP1 SWAP2 POP PUSH1 0xFF AND ISZERO ISZERO PUSH2 0x1844 JUMPI PUSH1 0x0 SWAP8 POP PUSH2 0x18D0 JUMP JUMPDEST PUSH2 0x17D8 DUP2 DUP13 DUP13 DUP13 PUSH2 0x3206 JUMP JUMPDEST PUSH1 0x6 DUP7 PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x185E JUMPI INVALID JUMPDEST EQ ISZERO PUSH2 0x189E JUMPI PUSH1 0x0 DUP12 DUP2 MSTORE PUSH1 0x6 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP15 AND DUP5 MSTORE SWAP1 SWAP2 MSTORE SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND SWAP8 POP PUSH2 0x18D0 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x554F JUMP JUMPDEST POP POP POP POP POP POP POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH2 0x18E6 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x60 PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH2 0x18F6 PUSH2 0x4298 JUMP JUMPDEST DUP10 PUSH1 0x0 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x1905 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x140 ADD MLOAD SWAP6 POP DUP10 MLOAD SWAP5 POP PUSH1 0x0 SWAP4 POP JUMPDEST DUP4 DUP6 EQ PUSH2 0x19E4 JUMPI DUP6 DUP11 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x1935 JUMPI INVALID JUMPDEST PUSH1 0x20 SWAP1 DUP2 MUL SWAP1 SWAP2 ADD ADD MLOAD PUSH2 0x140 ADD MSTORE DUP7 MLOAD PUSH2 0x1951 SWAP1 DUP11 SWAP1 PUSH2 0x2EE2 JUMP JUMPDEST SWAP3 POP PUSH2 0x1994 DUP11 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x1964 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH1 0xA0 ADD MLOAD DUP12 DUP7 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x1980 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH1 0x80 ADD MLOAD DUP6 PUSH2 0x3362 JUMP JUMPDEST SWAP2 POP PUSH2 0x19C0 DUP11 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x19A7 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP4 DUP11 DUP8 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xE8E JUMPI INVALID JUMPDEST SWAP1 POP PUSH2 0x19CC DUP8 DUP3 PUSH2 0x2532 JUMP JUMPDEST DUP7 MLOAD DUP10 GT PUSH2 0x19D9 JUMPI PUSH2 0x19E4 JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP4 ADD SWAP3 PUSH2 0x191F JUMP JUMPDEST POP POP POP POP POP POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH2 0x19F9 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x1A36 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE PUSH2 0xF9C DUP5 DUP5 DUP5 PUSH2 0x2494 JUMP JUMPDEST PUSH1 0x9 SLOAD PUSH1 0x0 SWAP1 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND ISZERO PUSH2 0x1ABF JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH2 0x1B02 PUSH2 0x1AFD DUP9 DUP9 DUP9 DUP9 DUP1 DUP1 PUSH1 0x1F ADD PUSH1 0x20 DUP1 SWAP2 DIV MUL PUSH1 0x20 ADD PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 SWAP4 SWAP3 SWAP2 SWAP1 DUP2 DUP2 MSTORE PUSH1 0x20 ADD DUP4 DUP4 DUP1 DUP3 DUP5 CALLDATACOPY POP PUSH2 0x33B8 SWAP5 POP POP POP POP POP JUMP JUMPDEST PUSH2 0x35F9 JUMP JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x8 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD SWAP1 SWAP2 POP PUSH1 0xFF AND ISZERO PUSH2 0x1B4E JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x559F JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP7 AND CALLER EQ PUSH2 0x1C1F JUMPI PUSH2 0x1BA6 DUP2 DUP8 DUP6 DUP6 DUP1 DUP1 PUSH1 0x1F ADD PUSH1 0x20 DUP1 SWAP2 DIV MUL PUSH1 0x20 ADD PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 SWAP4 SWAP3 SWAP2 SWAP1 DUP2 DUP2 MSTORE PUSH1 0x20 ADD DUP4 DUP4 DUP1 DUP3 DUP5 CALLDATACOPY POP PUSH2 0x135B SWAP5 POP POP POP POP POP JUMP JUMPDEST ISZERO ISZERO PUSH2 0x1BDE JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x56DF JUMP JUMPDEST PUSH1 0x9 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP9 AND OR SWAP1 SSTORE JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x8 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 DUP2 SWAP1 KECCAK256 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE MLOAD ADDRESS SWAP1 DUP7 SWAP1 DUP7 SWAP1 DUP1 DUP4 DUP4 DUP1 DUP3 DUP5 CALLDATACOPY DUP3 ADD SWAP2 POP POP SWAP3 POP POP POP PUSH1 0x0 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 DUP6 GAS DELEGATECALL SWAP2 POP POP ISZERO ISZERO PUSH2 0x1CB6 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x55CF JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP7 AND CALLER EQ PUSH2 0x1CFC JUMPI PUSH1 0x9 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 AND SWAP1 SSTORE JUMPDEST POP POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x2 SLOAD PUSH1 0x0 SWAP1 DUP2 SWAP1 DUP2 SWAP1 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER EQ PUSH2 0x1D5D JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x568F JUMP JUMPDEST DUP4 SWAP3 POP DUP3 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH4 0xAE25532E PUSH1 0x40 MLOAD DUP2 PUSH4 0xFFFFFFFF AND PUSH29 0x100000000000000000000000000000000000000000000000000000000 MUL DUP2 MSTORE PUSH1 0x4 ADD PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x1DC4 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x1DD8 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x1DFC SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x491E JUMP JUMPDEST PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 DUP2 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0xA PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD SWAP1 SWAP3 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 POP DUP1 ISZERO PUSH2 0x1E81 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x552F JUMP JUMPDEST PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 DUP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0xA PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 DUP2 SWAP1 KECCAK256 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP7 AND OR SWAP1 SSTORE MLOAD PUSH32 0xD2C6B762299C609BDB96520B58A49BFB80186934D4F71A86A367571A15C03194 SWAP1 PUSH2 0x1F22 SWAP1 DUP5 SWAP1 DUP8 SWAP1 PUSH2 0x54B5 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG1 POP POP POP POP JUMP JUMPDEST PUSH2 0x1F38 PUSH2 0x42F0 JUMP JUMPDEST PUSH2 0x1F41 DUP3 PUSH2 0x3639 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP4 ADD DUP3 SWAP1 MSTORE PUSH1 0x0 SWAP2 DUP3 MSTORE PUSH1 0x3 SWAP1 MSTORE PUSH1 0x40 SWAP1 DUP2 SWAP1 KECCAK256 SLOAD SWAP1 DUP3 ADD MSTORE PUSH1 0x80 DUP3 ADD MLOAD ISZERO ISZERO PUSH2 0x1F75 JUMPI PUSH1 0x1 JUMPDEST PUSH1 0xFF AND DUP2 MSTORE PUSH2 0xF07 JUMP JUMPDEST PUSH1 0xA0 DUP3 ADD MLOAD ISZERO ISZERO PUSH2 0x1F87 JUMPI PUSH1 0x2 PUSH2 0x1F6B JUMP JUMPDEST PUSH1 0xA0 DUP3 ADD MLOAD PUSH1 0x40 DUP3 ADD MLOAD LT PUSH2 0x1F9D JUMPI PUSH1 0x5 PUSH2 0x1F6B JUMP JUMPDEST PUSH2 0x100 DUP3 ADD MLOAD TIMESTAMP LT PUSH2 0x1FB0 JUMPI PUSH1 0x4 PUSH2 0x1F6B JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 ADD MLOAD PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x4 SWAP1 SWAP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x1FD4 JUMPI PUSH1 0x6 PUSH2 0x1F6B JUMP JUMPDEST PUSH2 0x120 DUP3 ADD MLOAD DUP3 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 DUP2 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x5 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 PUSH1 0x60 DUP9 ADD MLOAD SWAP1 SWAP5 AND DUP4 MSTORE SWAP3 SWAP1 MSTORE KECCAK256 SLOAD GT ISZERO PUSH2 0x2021 JUMPI PUSH1 0x6 PUSH2 0x1F6B JUMP JUMPDEST PUSH1 0x3 DUP2 MSTORE SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x2067 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE PUSH2 0x209B DUP2 PUSH2 0x2E64 JUMP JUMPDEST POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE JUMP JUMPDEST PUSH1 0x5 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x0 SWAP3 DUP4 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 SWAP1 SWAP2 MSTORE SWAP1 DUP3 MSTORE SWAP1 KECCAK256 SLOAD DUP2 JUMP JUMPDEST PUSH1 0x60 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x24 DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0xF47261B0000000000000000000000000E41D2489571D322189246DAFA5EBDE1F DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0x4699F49800000000000000000000000000000000000000000000000000000000 DUP2 MSTORE POP DUP2 JUMP JUMPDEST PUSH2 0x214B PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x60 PUSH1 0x0 DUP1 PUSH1 0x0 PUSH2 0x215A PUSH2 0x4298 JUMP JUMPDEST DUP9 PUSH1 0x0 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x2169 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x160 ADD MLOAD SWAP5 POP DUP9 MLOAD SWAP4 POP PUSH1 0x0 SWAP3 POP JUMPDEST DUP3 DUP5 EQ PUSH2 0x220C JUMPI DUP5 DUP10 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x2199 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x160 ADD DUP2 SWAP1 MSTORE POP PUSH2 0x21B9 DUP9 DUP8 PUSH1 0x20 ADD MLOAD PUSH2 0x2EE2 JUMP JUMPDEST SWAP2 POP PUSH2 0x21E5 DUP10 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x21CC JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP4 DUP10 DUP7 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xE8E JUMPI INVALID JUMPDEST SWAP1 POP PUSH2 0x21F1 DUP7 DUP3 PUSH2 0x2532 JUMP JUMPDEST PUSH1 0x20 DUP7 ADD MLOAD DUP9 GT PUSH2 0x2201 JUMPI PUSH2 0x220C JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP3 ADD SWAP2 PUSH2 0x2183 JUMP JUMPDEST POP POP POP POP POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x1 SLOAD DUP2 JUMP JUMPDEST PUSH2 0x2226 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x60 PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH2 0x2236 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x2273 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR DUP2 SSTORE DUP11 MLOAD DUP12 SWAP2 SWAP1 DUP2 LT PUSH2 0x22AB JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x140 ADD MLOAD SWAP6 POP DUP10 MLOAD SWAP5 POP PUSH1 0x0 SWAP4 POP JUMPDEST DUP4 DUP6 EQ PUSH2 0x235A JUMPI DUP6 DUP11 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x22DB JUMPI INVALID JUMPDEST PUSH1 0x20 SWAP1 DUP2 MUL SWAP1 SWAP2 ADD ADD MLOAD PUSH2 0x140 ADD MSTORE DUP7 MLOAD PUSH2 0x22F7 SWAP1 DUP11 SWAP1 PUSH2 0x2EE2 JUMP JUMPDEST SWAP3 POP PUSH2 0x230A DUP11 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x1964 JUMPI INVALID JUMPDEST SWAP2 POP PUSH2 0x2336 DUP11 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x231D JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP4 DUP11 DUP8 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x749 JUMPI INVALID JUMPDEST SWAP1 POP PUSH2 0x2342 DUP8 DUP3 PUSH2 0x2532 JUMP JUMPDEST DUP7 MLOAD DUP10 GT PUSH2 0x234F JUMPI PUSH2 0x235A JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP4 ADD SWAP3 PUSH2 0x22C5 JUMP JUMPDEST POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE POP SWAP3 SWAP7 SWAP6 POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x9 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x2 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER EQ PUSH2 0x23FD JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x568F JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 AND ISZERO PUSH2 0x245A JUMPI PUSH1 0x2 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND OR SWAP1 SSTORE JUMPDEST POP JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP1 DUP3 ADD SWAP1 SWAP2 MSTORE PUSH1 0x5 DUP2 MSTORE PUSH32 0x322E302E30000000000000000000000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE DUP2 JUMP JUMPDEST PUSH2 0x249C PUSH2 0x4298 JUMP JUMPDEST PUSH2 0x24A4 PUSH2 0x42F0 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 PUSH2 0x24B2 DUP9 PUSH2 0x1F30 JUMP JUMPDEST SWAP4 POP PUSH2 0x24BC PUSH2 0x2594 JUMP JUMPDEST SWAP3 POP PUSH2 0x24CA DUP9 DUP6 DUP6 DUP10 PUSH2 0x25C6 JUMP JUMPDEST PUSH2 0x24DC DUP9 PUSH1 0xA0 ADD MLOAD DUP6 PUSH1 0x40 ADD MLOAD PUSH2 0x2EE2 JUMP JUMPDEST SWAP2 POP PUSH2 0x24E8 DUP8 DUP4 PUSH2 0x3647 JUMP JUMPDEST SWAP1 POP PUSH2 0x24FB DUP9 DUP6 DUP10 DUP5 DUP10 PUSH1 0x0 ADD MLOAD PUSH2 0x2945 JUMP JUMPDEST PUSH2 0x2505 DUP9 DUP3 PUSH2 0x365D JUMP JUMPDEST SWAP5 POP PUSH2 0x251C DUP9 DUP5 DUP7 PUSH1 0x20 ADD MLOAD DUP8 PUSH1 0x40 ADD MLOAD DUP10 PUSH2 0x2A5E JUMP JUMPDEST PUSH2 0x2527 DUP9 DUP5 DUP8 PUSH2 0x36BE JUMP JUMPDEST POP POP POP POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST DUP2 MLOAD DUP2 MLOAD PUSH2 0x2540 SWAP2 SWAP1 PUSH2 0x3776 JUMP JUMPDEST DUP3 MSTORE PUSH1 0x20 DUP1 DUP4 ADD MLOAD SWAP1 DUP3 ADD MLOAD PUSH2 0x2556 SWAP2 SWAP1 PUSH2 0x3776 JUMP JUMPDEST PUSH1 0x20 DUP4 ADD MSTORE PUSH1 0x40 DUP1 DUP4 ADD MLOAD SWAP1 DUP3 ADD MLOAD PUSH2 0x256F SWAP2 SWAP1 PUSH2 0x3776 JUMP JUMPDEST PUSH1 0x40 DUP4 ADD MSTORE PUSH1 0x60 DUP1 DUP4 ADD MLOAD SWAP1 DUP3 ADD MLOAD PUSH2 0x2588 SWAP2 SWAP1 PUSH2 0x3776 JUMP JUMPDEST PUSH1 0x60 SWAP1 SWAP3 ADD SWAP2 SWAP1 SWAP2 MSTORE POP JUMP JUMPDEST PUSH1 0x9 SLOAD PUSH1 0x0 SWAP1 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 DUP2 ISZERO PUSH2 0x25BD JUMPI DUP2 PUSH2 0x25BF JUMP JUMPDEST CALLER JUMPDEST SWAP4 SWAP3 POP POP POP JUMP JUMPDEST DUP3 MLOAD PUSH1 0xFF AND PUSH1 0x3 EQ PUSH2 0x2604 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x56AF JUMP JUMPDEST PUSH1 0x60 DUP5 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND ISZERO PUSH2 0x2677 JUMPI PUSH1 0x60 DUP5 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER EQ PUSH2 0x2677 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x56BF JUMP JUMPDEST PUSH1 0x20 DUP5 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND ISZERO PUSH2 0x2702 JUMPI DUP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP5 PUSH1 0x20 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ ISZERO ISZERO PUSH2 0x2702 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x54FF JUMP JUMPDEST PUSH1 0x40 DUP4 ADD MLOAD ISZERO ISZERO PUSH2 0x2758 JUMPI PUSH2 0x2720 DUP4 PUSH1 0x20 ADD MLOAD DUP6 PUSH1 0x0 ADD MLOAD DUP4 PUSH2 0x135B JUMP JUMPDEST ISZERO ISZERO PUSH2 0x2758 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x556F JUMP JUMPDEST POP POP POP POP JUMP JUMPDEST PUSH2 0x2770 DUP3 PUSH1 0xA0 ADD MLOAD DUP3 PUSH1 0xA0 ADD MLOAD PUSH2 0x37C0 JUMP JUMPDEST PUSH2 0x2782 DUP4 PUSH1 0x80 ADD MLOAD DUP4 PUSH1 0x80 ADD MLOAD PUSH2 0x37C0 JUMP JUMPDEST LT ISZERO PUSH2 0x27BA JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x56CF JUMP JUMPDEST POP POP JUMP JUMPDEST PUSH2 0x27C6 PUSH2 0x42C1 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH2 0x27DA DUP10 PUSH1 0xA0 ADD MLOAD DUP9 PUSH2 0x2EE2 JUMP JUMPDEST SWAP4 POP PUSH2 0x27EF DUP10 PUSH1 0x80 ADD MLOAD DUP11 PUSH1 0xA0 ADD MLOAD DUP7 PUSH2 0x381B JUMP JUMPDEST SWAP3 POP PUSH2 0x27FF DUP9 PUSH1 0xA0 ADD MLOAD DUP8 PUSH2 0x2EE2 JUMP JUMPDEST SWAP2 POP PUSH2 0x2814 DUP9 PUSH1 0x80 ADD MLOAD DUP10 PUSH1 0xA0 ADD MLOAD DUP5 PUSH2 0x381B JUMP JUMPDEST SWAP1 POP DUP1 DUP5 LT PUSH2 0x2857 JUMPI PUSH1 0x20 DUP1 DUP7 ADD DUP1 MLOAD DUP4 SWAP1 MSTORE DUP1 MLOAD DUP3 ADD DUP5 SWAP1 MSTORE MLOAD MLOAD DUP7 MLOAD DUP3 ADD MSTORE PUSH1 0x80 DUP11 ADD MLOAD PUSH1 0xA0 DUP12 ADD MLOAD DUP8 MLOAD SWAP1 SWAP3 ADD MLOAD PUSH2 0x284F SWAP3 SWAP1 PUSH2 0x381B JUMP JUMPDEST DUP6 MLOAD MSTORE PUSH2 0x2894 JUMP JUMPDEST DUP5 MLOAD DUP4 SWAP1 MSTORE DUP5 MLOAD PUSH1 0x20 SWAP1 DUP2 ADD DUP6 SWAP1 MSTORE DUP6 MLOAD DUP2 ADD MLOAD SWAP1 DUP7 ADD DUP1 MLOAD SWAP2 SWAP1 SWAP2 MSTORE PUSH1 0xA0 DUP10 ADD MLOAD PUSH1 0x80 DUP11 ADD MLOAD SWAP2 MLOAD MLOAD PUSH2 0x288B SWAP3 SWAP1 PUSH2 0x3898 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP8 ADD MLOAD ADD MSTORE JUMPDEST DUP5 MLOAD MLOAD PUSH1 0x20 DUP1 DUP8 ADD MLOAD ADD MLOAD PUSH2 0x28A9 SWAP2 SWAP1 PUSH2 0x2EE2 JUMP JUMPDEST PUSH1 0x40 DUP7 ADD MSTORE DUP5 MLOAD MLOAD PUSH1 0x80 DUP11 ADD MLOAD PUSH1 0xC0 DUP12 ADD MLOAD PUSH2 0x28C6 SWAP3 SWAP2 SWAP1 PUSH2 0x381B JUMP JUMPDEST DUP6 MLOAD PUSH1 0x40 ADD MSTORE DUP5 MLOAD PUSH1 0x20 ADD MLOAD PUSH1 0xA0 DUP11 ADD MLOAD PUSH1 0xE0 DUP12 ADD MLOAD PUSH2 0x28E7 SWAP3 SWAP2 SWAP1 PUSH2 0x381B JUMP JUMPDEST DUP6 MLOAD PUSH1 0x60 ADD MSTORE PUSH1 0x20 DUP6 ADD MLOAD MLOAD PUSH1 0x80 DUP10 ADD MLOAD PUSH1 0xC0 DUP11 ADD MLOAD PUSH2 0x2908 SWAP3 SWAP2 SWAP1 PUSH2 0x381B JUMP JUMPDEST DUP6 PUSH1 0x20 ADD MLOAD PUSH1 0x40 ADD DUP2 DUP2 MSTORE POP POP PUSH2 0x2930 DUP6 PUSH1 0x20 ADD MLOAD PUSH1 0x20 ADD MLOAD DUP10 PUSH1 0xA0 ADD MLOAD DUP11 PUSH1 0xE0 ADD MLOAD PUSH2 0x381B JUMP JUMPDEST PUSH1 0x20 DUP7 ADD MLOAD PUSH1 0x60 ADD MSTORE POP POP POP POP SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST DUP3 ISZERO ISZERO PUSH2 0x297E JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x55EF JUMP JUMPDEST DUP3 DUP3 GT ISZERO PUSH2 0x29B8 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x55DF JUMP JUMPDEST DUP5 PUSH1 0xA0 ADD MLOAD PUSH2 0x29CB DUP6 PUSH1 0x40 ADD MLOAD DUP5 PUSH2 0x3776 JUMP JUMPDEST GT ISZERO PUSH2 0x2A03 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x550F JUMP JUMPDEST PUSH2 0x2A11 DUP6 PUSH1 0x80 ADD MLOAD DUP4 PUSH2 0x37C0 JUMP JUMPDEST PUSH2 0x2A1F DUP3 DUP8 PUSH1 0xA0 ADD MLOAD PUSH2 0x37C0 JUMP JUMPDEST GT ISZERO PUSH2 0x2A57 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x566F JUMP JUMPDEST POP POP POP POP POP JUMP JUMPDEST PUSH2 0x2A6C DUP3 DUP3 PUSH1 0x20 ADD MLOAD PUSH2 0x3776 JUMP JUMPDEST PUSH1 0x0 DUP5 DUP2 MSTORE PUSH1 0x3 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP2 DUP3 SWAP1 KECCAK256 SWAP3 SWAP1 SWAP3 SSTORE DUP7 DUP2 ADD MLOAD DUP8 MLOAD DUP5 MLOAD SWAP4 DUP6 ADD MLOAD DUP6 DUP5 ADD MLOAD PUSH1 0x60 DUP8 ADD MLOAD PUSH2 0x140 DUP13 ADD MLOAD PUSH2 0x160 DUP14 ADD MLOAD SWAP7 MLOAD DUP12 SWAP9 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP8 DUP9 AND SWAP9 SWAP8 SWAP1 SWAP7 AND SWAP7 PUSH32 0xBCC4C97732E47D9946F229EDB95F5B6323F601300E4690DE719993F3C371129 SWAP7 PUSH2 0x2AFB SWAP7 DUP16 SWAP7 CALLER SWAP7 SWAP3 SWAP6 SWAP2 SWAP5 SWAP1 SWAP4 SWAP1 PUSH2 0x5345 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG4 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x60 DUP1 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x24 DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0xF47261B0000000000000000000000000E41D2489571D322189246DAFA5EBDE1F DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0x4699F49800000000000000000000000000000000000000000000000000000000 DUP2 MSTORE POP SWAP1 POP PUSH2 0x2B8B DUP6 PUSH2 0x140 ADD MLOAD DUP7 PUSH1 0x0 ADD MLOAD DUP7 PUSH1 0x0 ADD MLOAD DUP6 PUSH1 0x20 ADD MLOAD PUSH1 0x20 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x140 DUP5 ADD MLOAD DUP5 MLOAD DUP7 MLOAD DUP5 MLOAD PUSH1 0x20 ADD MLOAD PUSH2 0x2BA7 SWAP4 SWAP3 SWAP2 SWAP1 PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x2BC0 DUP6 PUSH2 0x140 ADD MLOAD DUP7 PUSH1 0x0 ADD MLOAD DUP6 DUP6 PUSH1 0x40 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x2BDC DUP2 DUP7 PUSH1 0x0 ADD MLOAD DUP8 PUSH1 0x40 ADD MLOAD DUP6 PUSH1 0x0 ADD MLOAD PUSH1 0x40 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x2BF8 DUP2 DUP6 PUSH1 0x0 ADD MLOAD DUP7 PUSH1 0x40 ADD MLOAD DUP6 PUSH1 0x20 ADD MLOAD PUSH1 0x40 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST DUP4 PUSH1 0x40 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP6 PUSH1 0x40 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ ISZERO PUSH2 0x2C62 JUMPI PUSH2 0x2C5D DUP2 DUP5 DUP8 PUSH1 0x40 ADD MLOAD PUSH2 0x2C58 DUP7 PUSH1 0x0 ADD MLOAD PUSH1 0x60 ADD MLOAD DUP8 PUSH1 0x20 ADD MLOAD PUSH1 0x60 ADD MLOAD PUSH2 0x3776 JUMP JUMPDEST PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x2A57 JUMP JUMPDEST PUSH2 0x2C7A DUP2 DUP5 DUP8 PUSH1 0x40 ADD MLOAD DUP6 PUSH1 0x0 ADD MLOAD PUSH1 0x60 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x2A57 DUP2 DUP5 DUP7 PUSH1 0x40 ADD MLOAD DUP6 PUSH1 0x20 ADD MLOAD PUSH1 0x60 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0xB4BE83D500000000000000000000000000000000000000000000000000000000 PUSH1 0x20 DUP1 DUP4 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH1 0x60 PUSH1 0x24 DUP4 ADD DUP2 DUP2 MSTORE DUP8 MLOAD PUSH1 0x84 DUP6 ADD SWAP1 DUP2 MSTORE DUP9 DUP5 ADD MLOAD PUSH1 0xA4 DUP7 ADD MSTORE SWAP5 DUP9 ADD MLOAD PUSH1 0xC4 DUP6 ADD MSTORE SWAP1 DUP8 ADD MLOAD PUSH1 0xE4 DUP5 ADD MSTORE PUSH1 0x80 DUP8 ADD MLOAD PUSH2 0x104 DUP5 ADD MSTORE PUSH1 0xA0 DUP8 ADD MLOAD PUSH2 0x124 DUP5 ADD MSTORE PUSH1 0xC0 DUP8 ADD MLOAD PUSH2 0x144 DUP5 ADD MSTORE PUSH1 0xE0 DUP8 ADD MLOAD PUSH2 0x164 DUP5 ADD MSTORE PUSH2 0x100 DUP8 ADD MLOAD PUSH2 0x184 DUP5 ADD MSTORE PUSH2 0x120 DUP8 ADD MLOAD PUSH2 0x1A4 DUP5 ADD MSTORE PUSH2 0x140 DUP8 ADD DUP1 MLOAD PUSH2 0x1C4 DUP6 ADD SWAP1 DUP2 MSTORE PUSH2 0x160 DUP10 ADD MLOAD PUSH2 0x1E4 DUP7 ADD MSTORE PUSH2 0x180 SWAP1 MSTORE MLOAD DUP1 MLOAD PUSH2 0x204 DUP6 ADD DUP2 SWAP1 MSTORE SWAP4 SWAP5 SWAP2 SWAP4 DUP5 SWAP4 PUSH1 0x44 DUP8 ADD SWAP3 DUP5 SWAP3 PUSH2 0x224 DUP10 ADD SWAP3 SWAP2 DUP3 ADD SWAP2 PUSH1 0x1F DUP3 ADD DIV PUSH1 0x0 JUMPDEST DUP2 DUP2 LT ISZERO PUSH2 0x2D99 JUMPI DUP4 MLOAD DUP6 MSTORE PUSH1 0x20 SWAP5 DUP6 ADD SWAP5 SWAP1 SWAP4 ADD SWAP3 PUSH1 0x1 ADD PUSH2 0x2D7B JUMP JUMPDEST POP POP POP POP DUP2 DUP2 SUB PUSH2 0x160 DUP1 DUP5 ADD SWAP2 SWAP1 SWAP2 MSTORE DUP11 ADD MLOAD DUP1 MLOAD DUP1 DUP4 MSTORE PUSH1 0x20 SWAP3 DUP4 ADD SWAP3 SWAP2 DUP3 ADD SWAP2 PUSH1 0x1F DUP3 ADD DIV PUSH1 0x0 JUMPDEST DUP2 DUP2 LT ISZERO PUSH2 0x2DE2 JUMPI DUP4 MLOAD DUP6 MSTORE PUSH1 0x20 SWAP5 DUP6 ADD SWAP5 SWAP1 SWAP4 ADD SWAP3 PUSH1 0x1 ADD PUSH2 0x2DC4 JUMP JUMPDEST POP POP POP DUP10 DUP5 MSTORE POP DUP5 DUP2 SUB PUSH1 0x20 SWAP4 DUP5 ADD SWAP1 DUP2 MSTORE DUP9 MLOAD DUP1 DUP4 MSTORE SWAP1 SWAP4 SWAP2 DUP3 ADD SWAP2 DUP10 DUP2 ADD SWAP2 SWAP1 PUSH1 0x1F DUP3 ADD DIV PUSH1 0x0 JUMPDEST DUP2 DUP2 LT ISZERO PUSH2 0x2E2A JUMPI DUP4 MLOAD DUP6 MSTORE PUSH1 0x20 SWAP5 DUP6 ADD SWAP5 SWAP1 SWAP4 ADD SWAP3 PUSH1 0x1 ADD PUSH2 0x2E0C JUMP JUMPDEST POP POP POP PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 DUP9 DUP4 SUB ADD DUP9 MSTORE POP PUSH1 0x40 MSTORE POP POP POP POP POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH2 0x2E6C PUSH2 0x42F0 JUMP JUMPDEST PUSH2 0x2E75 DUP3 PUSH2 0x1F30 JUMP JUMPDEST SWAP1 POP PUSH2 0x2E81 DUP3 DUP3 PUSH2 0x3AFF JUMP JUMPDEST PUSH2 0x27BA DUP3 DUP3 PUSH1 0x20 ADD MLOAD PUSH2 0x3C16 JUMP JUMPDEST PUSH2 0x2E97 PUSH2 0x4298 JUMP JUMPDEST PUSH2 0x2EA2 DUP5 DUP5 DUP5 PUSH2 0x2494 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD MLOAD SWAP1 SWAP2 POP DUP4 EQ PUSH2 0x25BF JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x565F JUMP JUMPDEST PUSH1 0x0 DUP3 DUP3 GT ISZERO PUSH2 0x2F1E JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x551F JUMP JUMPDEST POP DUP1 DUP3 SUB JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP3 MLOAD GT ISZERO ISZERO PUSH2 0x2F67 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x560F JUMP JUMPDEST DUP2 MLOAD DUP3 SWAP1 PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 ADD SWAP1 DUP2 LT PUSH2 0x2F97 JUMPI INVALID JUMPDEST ADD PUSH1 0x20 ADD MLOAD DUP3 MLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ADD SWAP1 SWAP3 MSTORE POP PUSH32 0x100000000000000000000000000000000000000000000000000000000000000 SWAP1 DUP2 SWAP1 DIV MUL SWAP1 JUMP JUMPDEST PUSH1 0x0 DUP2 PUSH1 0x20 ADD DUP4 MLOAD LT ISZERO ISZERO ISZERO PUSH2 0x302F JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x553F JUMP JUMPDEST POP ADD PUSH1 0x20 ADD MLOAD SWAP1 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH1 0x0 SWAP1 PUSH1 0x60 SWAP1 PUSH32 0x1626BA7E00000000000000000000000000000000000000000000000000000000 SWAP1 PUSH2 0x3073 SWAP1 DUP8 SWAP1 DUP7 SWAP1 PUSH1 0x24 ADD PUSH2 0x5460 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 DUP2 DUP5 SUB ADD DUP2 MSTORE SWAP2 SWAP1 MSTORE PUSH1 0x20 DUP1 DUP3 ADD DUP1 MLOAD PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 SWAP1 SWAP5 AND SWAP4 SWAP1 SWAP4 OR DUP4 MSTORE DUP2 MLOAD SWAP2 SWAP4 POP SWAP1 DUP3 SWAP1 DUP2 DUP9 GAS STATICCALL DUP1 DUP1 ISZERO PUSH2 0x3110 JUMPI PUSH1 0x1 DUP2 EQ PUSH2 0x3181 JUMPI PUSH2 0x2527 JUMP JUMPDEST PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 PUSH1 0x0 MSTORE PUSH29 0x2000000000000000000000000000000000000000000000000000000000 PUSH1 0x20 MSTORE PUSH29 0xC57414C4C45545F4552524F5200000000000000000000000000000000 PUSH1 0x40 MSTORE PUSH1 0x0 PUSH1 0x60 MSTORE PUSH1 0x64 PUSH1 0x0 REVERT JUMPDEST POP POP MLOAD SWAP6 SWAP5 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x14 DUP3 MLOAD LT ISZERO ISZERO ISZERO PUSH2 0x31CD JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x569F JUMP JUMPDEST PUSH2 0x31DB DUP3 PUSH1 0x14 DUP5 MLOAD SUB PUSH2 0x3CBD JUMP JUMPDEST DUP3 MLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC ADD SWAP1 SWAP3 MSTORE POP SWAP1 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH1 0x0 SWAP1 PUSH1 0x60 SWAP1 PUSH32 0x9363470200000000000000000000000000000000000000000000000000000000 SWAP1 PUSH2 0x3243 SWAP1 DUP8 SWAP1 DUP8 SWAP1 DUP8 SWAP1 PUSH1 0x24 ADD PUSH2 0x5433 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 DUP2 DUP5 SUB ADD DUP2 MSTORE SWAP2 SWAP1 MSTORE PUSH1 0x20 DUP1 DUP3 ADD DUP1 MLOAD PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 SWAP1 SWAP5 AND SWAP4 SWAP1 SWAP4 OR DUP4 MSTORE DUP2 MLOAD SWAP2 SWAP4 POP SWAP1 DUP3 SWAP1 DUP2 DUP11 GAS STATICCALL DUP1 DUP1 ISZERO PUSH2 0x32E0 JUMPI PUSH1 0x1 DUP2 EQ PUSH2 0x3351 JUMPI PUSH2 0x3356 JUMP JUMPDEST PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 PUSH1 0x0 MSTORE PUSH29 0x2000000000000000000000000000000000000000000000000000000000 PUSH1 0x20 MSTORE PUSH29 0xF56414C494441544F525F4552524F5200000000000000000000000000 PUSH1 0x40 MSTORE PUSH1 0x0 PUSH1 0x60 MSTORE PUSH1 0x64 PUSH1 0x0 REVERT JUMPDEST DUP3 MLOAD SWAP5 POP JUMPDEST POP POP POP POP SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP4 GT PUSH2 0x339D JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x555F JUMP JUMPDEST PUSH2 0x33B0 PUSH2 0x33AA DUP6 DUP5 PUSH2 0x37C0 JUMP JUMPDEST DUP5 PUSH2 0x3D1E JUMP JUMPDEST SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0x5A65726F45785472616E73616374696F6E280000000000000000000000000000 PUSH1 0x20 DUP1 DUP4 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH32 0x75696E743235362073616C742C00000000000000000000000000000000000000 PUSH1 0x32 DUP4 ADD MSTORE PUSH32 0x61646472657373207369676E6572416464726573732C00000000000000000000 PUSH1 0x3F DUP4 ADD MSTORE PUSH32 0x6279746573206461746100000000000000000000000000000000000000000000 PUSH1 0x55 DUP4 ADD MSTORE PUSH32 0x2900000000000000000000000000000000000000000000000000000000000000 PUSH1 0x5F DUP4 ADD MSTORE DUP3 MLOAD DUP1 DUP4 SUB DUP5 ADD DUP2 MSTORE PUSH1 0x60 SWAP1 SWAP3 ADD SWAP3 DUP4 SWAP1 MSTORE DUP2 MLOAD PUSH1 0x0 SWAP4 DUP5 SWAP4 DUP5 SWAP4 SWAP1 SWAP3 DUP3 SWAP2 DUP5 ADD SWAP1 DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH2 0x34E1 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0x34A4 JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 DUP5 SUB PUSH2 0x100 EXP PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ADD DUP1 NOT SWAP1 SWAP3 AND SWAP2 AND OR SWAP1 MSTORE PUSH1 0x40 MLOAD SWAP2 SWAP1 SWAP4 ADD DUP2 SWAP1 SUB DUP2 KECCAK256 DUP10 MLOAD SWAP1 SWAP8 POP DUP10 SWAP6 POP SWAP1 SWAP4 POP DUP4 SWAP3 DUP6 ADD SWAP2 POP DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH2 0x3577 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0x353A JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 DUP5 SUB PUSH2 0x100 EXP PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ADD DUP1 NOT SWAP1 SWAP3 AND SWAP2 AND OR SWAP1 MSTORE PUSH1 0x40 DUP1 MLOAD SWAP3 SWAP1 SWAP5 ADD DUP3 SWAP1 SUB DUP3 KECCAK256 SWAP8 DUP3 MSTORE DUP2 ADD SWAP11 SWAP1 SWAP11 MSTORE POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP8 SWAP1 SWAP8 AND SWAP7 DUP9 ADD SWAP7 SWAP1 SWAP7 MSTORE POP POP PUSH1 0x60 DUP6 ADD MSTORE POP POP PUSH1 0x80 SWAP1 SWAP2 KECCAK256 SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x1 SLOAD PUSH1 0x40 MLOAD PUSH32 0x1901000000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x2 DUP2 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH1 0x22 DUP2 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH1 0x42 SWAP1 KECCAK256 SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x2F23 PUSH2 0x1AFD DUP4 PUSH2 0x3D35 JUMP JUMPDEST PUSH1 0x0 DUP2 DUP4 LT PUSH2 0x3656 JUMPI DUP2 PUSH2 0x25BF JUMP JUMPDEST POP SWAP1 SWAP2 SWAP1 POP JUMP JUMPDEST PUSH2 0x3665 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD DUP3 SWAP1 MSTORE PUSH1 0xA0 DUP4 ADD MLOAD PUSH1 0x80 DUP5 ADD MLOAD PUSH2 0x3681 SWAP2 DUP5 SWAP2 PUSH2 0x381B JUMP JUMPDEST DUP1 DUP3 MSTORE PUSH1 0x80 DUP5 ADD MLOAD PUSH1 0xC0 DUP6 ADD MLOAD PUSH2 0x3699 SWAP3 SWAP2 SWAP1 PUSH2 0x381B JUMP JUMPDEST PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0xA0 DUP4 ADD MLOAD PUSH1 0xE0 DUP5 ADD MLOAD PUSH2 0x36B3 SWAP2 DUP5 SWAP2 PUSH2 0x381B JUMP JUMPDEST PUSH1 0x60 DUP3 ADD MSTORE SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x60 DUP2 ADD DUP3 MSTORE PUSH1 0x24 DUP2 MSTORE PUSH32 0xF47261B0000000000000000000000000E41D2489571D322189246DAFA5EBDE1F PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x4699F49800000000000000000000000000000000000000000000000000000000 SWAP2 DUP2 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH2 0x140 DUP5 ADD MLOAD DUP5 MLOAD DUP4 MLOAD PUSH2 0x3731 SWAP3 SWAP2 SWAP1 DUP7 SWAP1 PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x374A DUP5 PUSH2 0x160 ADD MLOAD DUP5 DUP7 PUSH1 0x0 ADD MLOAD DUP6 PUSH1 0x20 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x3762 DUP2 DUP6 PUSH1 0x0 ADD MLOAD DUP7 PUSH1 0x40 ADD MLOAD DUP6 PUSH1 0x40 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x2758 DUP2 DUP5 DUP7 PUSH1 0x40 ADD MLOAD DUP6 PUSH1 0x60 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH1 0x0 DUP3 DUP3 ADD DUP4 DUP2 LT ISZERO PUSH2 0x37B5 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x558F JUMP JUMPDEST DUP1 SWAP2 POP JUMPDEST POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP4 ISZERO ISZERO PUSH2 0x37D3 JUMPI PUSH1 0x0 SWAP2 POP PUSH2 0x37B9 JUMP JUMPDEST POP DUP3 DUP3 MUL DUP3 DUP5 DUP3 DUP2 ISZERO ISZERO PUSH2 0x37E3 JUMPI INVALID JUMPDEST DIV EQ PUSH2 0x37B5 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x558F JUMP JUMPDEST PUSH1 0x0 DUP1 DUP4 GT PUSH2 0x3856 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x555F JUMP JUMPDEST PUSH2 0x3861 DUP5 DUP5 DUP5 PUSH2 0x418E JUMP JUMPDEST ISZERO PUSH2 0x339D JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x55BF JUMP JUMPDEST PUSH1 0x0 DUP1 DUP4 GT PUSH2 0x38D3 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x555F JUMP JUMPDEST PUSH2 0x38DE DUP5 DUP5 DUP5 PUSH2 0x4213 JUMP JUMPDEST ISZERO PUSH2 0x3915 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x55BF JUMP JUMPDEST PUSH2 0x33B0 PUSH2 0x33AA PUSH2 0x3925 DUP7 DUP6 PUSH2 0x37C0 JUMP JUMPDEST PUSH2 0x3930 DUP7 PUSH1 0x1 PUSH2 0x2EE2 JUMP JUMPDEST PUSH2 0x3776 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP4 GT DUP1 ISZERO PUSH2 0x3974 JUMPI POP DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP6 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ ISZERO JUMPDEST ISZERO PUSH2 0x3AF7 JUMPI DUP6 MLOAD PUSH1 0x3 LT PUSH2 0x39B4 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x564F JUMP JUMPDEST POP POP PUSH1 0x20 DUP5 DUP2 ADD MLOAD PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0xA SWAP1 SWAP3 MSTORE PUSH1 0x40 SWAP1 SWAP2 KECCAK256 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP1 ISZERO ISZERO PUSH2 0x3A3D JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x55FF JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH7 0xFFFFFFFFFFFE0 PUSH1 0x3F DUP9 MLOAD ADD AND DUP1 PUSH1 0x84 ADD DUP3 ADD PUSH32 0xA85E59E400000000000000000000000000000000000000000000000000000000 DUP4 MSTORE PUSH1 0x80 PUSH1 0x4 DUP5 ADD MSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP9 AND PUSH1 0x24 DUP5 ADD MSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP8 AND PUSH1 0x44 DUP5 ADD MSTORE DUP6 PUSH1 0x64 DUP5 ADD MSTORE PUSH1 0x84 DUP4 ADD JUMPDEST DUP2 DUP2 LT ISZERO PUSH2 0x3AD9 JUMPI DUP10 MLOAD DUP2 MSTORE PUSH1 0x20 SWAP10 DUP11 ADD SWAP10 ADD PUSH2 0x3AC1 JUMP JUMPDEST PUSH2 0x200 DUP5 DUP6 DUP5 SUB DUP7 PUSH1 0x0 DUP10 GAS CALL DUP1 ISZERO ISZERO PUSH2 0x3AF1 JUMPI RETURNDATASIZE DUP6 REVERT JUMPDEST POP POP POP POP POP JUMPDEST POP POP POP POP POP POP JUMP JUMPDEST DUP1 MLOAD PUSH1 0x0 SWAP1 PUSH1 0xFF AND PUSH1 0x3 EQ PUSH2 0x3B40 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x56AF JUMP JUMPDEST PUSH1 0x60 DUP4 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND ISZERO PUSH2 0x3BB3 JUMPI PUSH1 0x60 DUP4 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER EQ PUSH2 0x3BB3 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x56BF JUMP JUMPDEST PUSH2 0x3BBB PUSH2 0x2594 JUMP JUMPDEST DUP4 MLOAD SWAP1 SWAP2 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP4 AND SWAP2 AND EQ PUSH2 0x3C11 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x557F JUMP JUMPDEST POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x4 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 DUP2 SWAP1 KECCAK256 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE DUP3 DUP2 ADD MLOAD DUP4 MLOAD PUSH2 0x140 DUP6 ADD MLOAD PUSH2 0x160 DUP7 ADD MLOAD SWAP4 MLOAD DUP6 SWAP5 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP5 DUP6 AND SWAP5 SWAP4 SWAP1 SWAP4 AND SWAP3 PUSH32 0xDC47B3613D9FE400085F6DBDC99453462279057E6207385042827ED6B1A62CF7 SWAP3 PUSH2 0x3CB1 SWAP3 CALLER SWAP3 SWAP1 PUSH2 0x53C9 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG4 POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 PUSH1 0x14 ADD DUP4 MLOAD LT ISZERO ISZERO ISZERO PUSH2 0x3CFF JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x569F JUMP JUMPDEST POP ADD PUSH1 0x14 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 JUMP JUMPDEST PUSH1 0x0 DUP1 DUP3 DUP5 DUP2 ISZERO ISZERO PUSH2 0x3D2C JUMPI INVALID JUMPDEST DIV SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0x4F72646572280000000000000000000000000000000000000000000000000000 PUSH1 0x20 DUP1 DUP4 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH32 0x61646472657373206D616B6572416464726573732C0000000000000000000000 PUSH1 0x26 DUP4 ADD MSTORE PUSH32 0x616464726573732074616B6572416464726573732C0000000000000000000000 PUSH1 0x3B DUP4 ADD MSTORE PUSH32 0x6164647265737320666565526563697069656E74416464726573732C00000000 PUSH1 0x50 DUP4 ADD MSTORE PUSH32 0x616464726573732073656E646572416464726573732C00000000000000000000 PUSH1 0x6C DUP4 ADD MSTORE PUSH32 0x75696E74323536206D616B65724173736574416D6F756E742C00000000000000 PUSH1 0x82 DUP4 ADD MSTORE PUSH32 0x75696E743235362074616B65724173736574416D6F756E742C00000000000000 PUSH1 0x9B DUP4 ADD MSTORE PUSH32 0x75696E74323536206D616B65724665652C000000000000000000000000000000 PUSH1 0xB4 DUP4 ADD MSTORE PUSH32 0x75696E743235362074616B65724665652C000000000000000000000000000000 PUSH1 0xC5 DUP4 ADD MSTORE PUSH32 0x75696E743235362065787069726174696F6E54696D655365636F6E64732C0000 PUSH1 0xD6 DUP4 ADD MSTORE PUSH32 0x75696E743235362073616C742C00000000000000000000000000000000000000 PUSH1 0xF4 DUP4 ADD MSTORE PUSH32 0x6279746573206D616B65724173736574446174612C0000000000000000000000 PUSH2 0x101 DUP4 ADD MSTORE PUSH32 0x62797465732074616B6572417373657444617461000000000000000000000000 PUSH2 0x116 DUP4 ADD MSTORE PUSH32 0x2900000000000000000000000000000000000000000000000000000000000000 PUSH2 0x12A DUP4 ADD MSTORE DUP3 MLOAD PUSH2 0x10B DUP2 DUP5 SUB ADD DUP2 MSTORE PUSH2 0x12B SWAP1 SWAP3 ADD SWAP3 DUP4 SWAP1 MSTORE DUP2 MLOAD PUSH1 0x0 SWAP4 DUP5 SWAP4 DUP5 SWAP4 DUP5 SWAP4 SWAP2 SWAP3 SWAP2 DUP3 SWAP2 DUP5 ADD SWAP1 DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH2 0x3FBD JUMPI DUP1 MLOAD DUP3 MSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0x3F80 JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 DUP5 SUB PUSH2 0x100 EXP PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ADD DUP1 NOT SWAP1 SWAP3 AND SWAP2 AND OR SWAP1 MSTORE PUSH1 0x40 MLOAD SWAP2 SWAP1 SWAP4 ADD DUP2 SWAP1 SUB DUP2 KECCAK256 PUSH2 0x140 DUP12 ADD MLOAD DUP1 MLOAD SWAP2 SWAP10 POP SWAP6 POP SWAP1 SWAP4 POP DUP4 SWAP3 DUP6 ADD SWAP2 POP DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH2 0x4058 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0x401B JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 DUP5 SUB PUSH2 0x100 EXP PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ADD DUP1 NOT SWAP1 SWAP3 AND SWAP2 AND OR SWAP1 MSTORE PUSH1 0x40 MLOAD SWAP2 SWAP1 SWAP4 ADD DUP2 SWAP1 SUB DUP2 KECCAK256 PUSH2 0x160 DUP12 ADD MLOAD DUP1 MLOAD SWAP2 SWAP9 POP SWAP6 POP SWAP1 SWAP4 POP DUP4 SWAP3 DUP6 ADD SWAP2 POP DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH2 0x40F3 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0x40B6 JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 SWAP1 SWAP4 SUB PUSH2 0x100 EXP PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ADD DUP1 NOT SWAP1 SWAP2 AND SWAP3 AND SWAP2 SWAP1 SWAP2 OR SWAP1 MSTORE PUSH1 0x40 MLOAD SWAP3 ADD DUP3 SWAP1 SUB SWAP1 SWAP2 KECCAK256 PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 DUP10 ADD DUP1 MLOAD PUSH2 0x140 DUP12 ADD DUP1 MLOAD PUSH2 0x160 SWAP1 SWAP13 ADD DUP1 MLOAD SWAP11 DUP5 MSTORE SWAP9 DUP2 MSTORE SWAP3 DUP9 MSTORE PUSH2 0x1A0 DUP3 KECCAK256 SWAP2 MSTORE SWAP9 SWAP1 MSTORE POP POP POP SWAP2 SWAP1 MSTORE POP SWAP1 SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP1 DUP5 GT PUSH2 0x41CA JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x555F JUMP JUMPDEST DUP3 ISZERO DUP1 PUSH2 0x41D5 JUMPI POP DUP5 ISZERO JUMPDEST ISZERO PUSH2 0x41E3 JUMPI PUSH1 0x0 SWAP2 POP PUSH2 0xA88 JUMP JUMPDEST DUP4 DUP1 ISZERO ISZERO PUSH2 0x41ED JUMPI INVALID JUMPDEST DUP6 DUP5 MULMOD SWAP1 POP PUSH2 0x41FC DUP6 DUP5 PUSH2 0x37C0 JUMP JUMPDEST PUSH2 0x4208 PUSH2 0x3E8 DUP4 PUSH2 0x37C0 JUMP JUMPDEST LT ISZERO SWAP6 SWAP5 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP1 DUP5 GT PUSH2 0x424F JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x555F JUMP JUMPDEST DUP3 ISZERO DUP1 PUSH2 0x425A JUMPI POP DUP5 ISZERO JUMPDEST ISZERO PUSH2 0x4268 JUMPI PUSH1 0x0 SWAP2 POP PUSH2 0xA88 JUMP JUMPDEST DUP4 DUP1 ISZERO ISZERO PUSH2 0x4272 JUMPI INVALID JUMPDEST DUP6 DUP5 MULMOD SWAP1 POP DUP4 PUSH2 0x4282 DUP6 DUP4 PUSH2 0x2EE2 JUMP JUMPDEST DUP2 ISZERO ISZERO PUSH2 0x428B JUMPI INVALID JUMPDEST MOD SWAP1 POP PUSH2 0x41FC DUP6 DUP5 PUSH2 0x37C0 JUMP JUMPDEST PUSH1 0x80 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE POP SWAP1 JUMP JUMPDEST PUSH2 0x120 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH2 0x42D6 PUSH2 0x4298 JUMP JUMPDEST DUP2 MSTORE PUSH1 0x20 ADD PUSH2 0x42E3 PUSH2 0x4298 JUMP JUMPDEST DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE POP SWAP1 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x60 DUP2 ADD DUP3 MSTORE PUSH1 0x0 DUP1 DUP3 MSTORE PUSH1 0x20 DUP3 ADD DUP2 SWAP1 MSTORE SWAP2 DUP2 ADD SWAP2 SWAP1 SWAP2 MSTORE SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x25BF DUP3 CALLDATALOAD PUSH2 0x57C2 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0x432D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0x4340 PUSH2 0x433B DUP3 PUSH2 0x5751 JUMP JUMPDEST PUSH2 0x572A JUMP JUMPDEST DUP2 DUP2 MSTORE PUSH1 0x20 SWAP4 DUP5 ADD SWAP4 SWAP1 SWAP3 POP DUP3 ADD DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x437E JUMPI DUP2 CALLDATALOAD DUP7 ADD PUSH2 0x4368 DUP9 DUP3 PUSH2 0x44CE JUMP JUMPDEST DUP5 MSTORE POP PUSH1 0x20 SWAP3 DUP4 ADD SWAP3 SWAP2 SWAP1 SWAP2 ADD SWAP1 PUSH1 0x1 ADD PUSH2 0x4352 JUMP JUMPDEST POP POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0x4399 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0x43A7 PUSH2 0x433B DUP3 PUSH2 0x5751 JUMP JUMPDEST DUP2 DUP2 MSTORE PUSH1 0x20 SWAP4 DUP5 ADD SWAP4 SWAP1 SWAP3 POP DUP3 ADD DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x437E JUMPI DUP2 CALLDATALOAD DUP7 ADD PUSH2 0x43CF DUP9 DUP3 PUSH2 0x451D JUMP JUMPDEST DUP5 MSTORE POP PUSH1 0x20 SWAP3 DUP4 ADD SWAP3 SWAP2 SWAP1 SWAP2 ADD SWAP1 PUSH1 0x1 ADD PUSH2 0x43B9 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0x43F6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0x4404 PUSH2 0x433B DUP3 PUSH2 0x5751 JUMP JUMPDEST SWAP2 POP DUP2 DUP2 DUP4 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP PUSH1 0x20 DUP2 ADD SWAP1 POP DUP4 DUP6 PUSH1 0x20 DUP5 MUL DUP3 ADD GT ISZERO PUSH2 0x4429 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x437E JUMPI DUP2 PUSH2 0x443F DUP9 DUP3 PUSH2 0x4461 JUMP JUMPDEST DUP5 MSTORE POP PUSH1 0x20 SWAP3 DUP4 ADD SWAP3 SWAP2 SWAP1 SWAP2 ADD SWAP1 PUSH1 0x1 ADD PUSH2 0x442C JUMP JUMPDEST PUSH1 0x0 PUSH2 0x25BF DUP3 CALLDATALOAD PUSH2 0x57DB JUMP JUMPDEST PUSH1 0x0 PUSH2 0x25BF DUP3 CALLDATALOAD PUSH2 0x57E0 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x25BF DUP3 CALLDATALOAD PUSH2 0x57E3 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x25BF DUP3 MLOAD PUSH2 0x57E3 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x1F DUP4 ADD DUP5 SGT PUSH2 0x4497 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP DUP2 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x44AF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x20 DUP4 ADD SWAP2 POP DUP4 PUSH1 0x1 DUP3 MUL DUP4 ADD GT ISZERO PUSH2 0x44C7 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST SWAP3 POP SWAP3 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0x44DF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0x44ED PUSH2 0x433B DUP3 PUSH2 0x5772 JUMP JUMPDEST SWAP2 POP DUP1 DUP3 MSTORE PUSH1 0x20 DUP4 ADD PUSH1 0x20 DUP4 ADD DUP6 DUP4 DUP4 ADD GT ISZERO PUSH2 0x4509 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4514 DUP4 DUP3 DUP5 PUSH2 0x5819 JUMP JUMPDEST POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x180 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x4530 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x453B PUSH2 0x180 PUSH2 0x572A JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0x4549 DUP5 DUP5 PUSH2 0x4310 JUMP JUMPDEST DUP3 MSTORE POP PUSH1 0x20 PUSH2 0x455A DUP5 DUP5 DUP4 ADD PUSH2 0x4310 JUMP JUMPDEST PUSH1 0x20 DUP4 ADD MSTORE POP PUSH1 0x40 PUSH2 0x456E DUP5 DUP3 DUP6 ADD PUSH2 0x4310 JUMP JUMPDEST PUSH1 0x40 DUP4 ADD MSTORE POP PUSH1 0x60 PUSH2 0x4582 DUP5 DUP3 DUP6 ADD PUSH2 0x4310 JUMP JUMPDEST PUSH1 0x60 DUP4 ADD MSTORE POP PUSH1 0x80 PUSH2 0x4596 DUP5 DUP3 DUP6 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH1 0x80 DUP4 ADD MSTORE POP PUSH1 0xA0 PUSH2 0x45AA DUP5 DUP3 DUP6 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH1 0xA0 DUP4 ADD MSTORE POP PUSH1 0xC0 PUSH2 0x45BE DUP5 DUP3 DUP6 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH1 0xC0 DUP4 ADD MSTORE POP PUSH1 0xE0 PUSH2 0x45D2 DUP5 DUP3 DUP6 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH1 0xE0 DUP4 ADD MSTORE POP PUSH2 0x100 PUSH2 0x45E7 DUP5 DUP3 DUP6 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH2 0x100 DUP4 ADD MSTORE POP PUSH2 0x120 PUSH2 0x45FD DUP5 DUP3 DUP6 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH2 0x120 DUP4 ADD MSTORE POP PUSH2 0x140 DUP3 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x461F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x462B DUP5 DUP3 DUP6 ADD PUSH2 0x44CE JUMP JUMPDEST PUSH2 0x140 DUP4 ADD MSTORE POP PUSH2 0x160 DUP3 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x464D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4659 DUP5 DUP3 DUP6 ADD PUSH2 0x44CE JUMP JUMPDEST PUSH2 0x160 DUP4 ADD MSTORE POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x4678 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x33B0 DUP5 DUP5 PUSH2 0x4310 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x4697 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x46A3 DUP6 DUP6 PUSH2 0x4310 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 PUSH2 0x46B4 DUP6 DUP3 DUP7 ADD PUSH2 0x4310 JUMP JUMPDEST SWAP2 POP POP SWAP3 POP SWAP3 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x46D1 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x46DD DUP6 DUP6 PUSH2 0x4310 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 PUSH2 0x46B4 DUP6 DUP3 DUP7 ADD PUSH2 0x4455 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x4700 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x4717 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x33B0 DUP5 DUP3 DUP6 ADD PUSH2 0x4388 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 PUSH1 0x60 DUP5 DUP7 SUB SLT ISZERO PUSH2 0x4738 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP4 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x474F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x475B DUP7 DUP3 DUP8 ADD PUSH2 0x4388 JUMP JUMPDEST SWAP4 POP POP PUSH1 0x20 DUP5 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x4778 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4784 DUP7 DUP3 DUP8 ADD PUSH2 0x43E5 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x40 DUP5 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x47A1 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x47AD DUP7 DUP3 DUP8 ADD PUSH2 0x431C JUMP JUMPDEST SWAP2 POP POP SWAP3 POP SWAP3 POP SWAP3 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 PUSH1 0x60 DUP5 DUP7 SUB SLT ISZERO PUSH2 0x47CC JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP4 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x47E3 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x47EF DUP7 DUP3 DUP8 ADD PUSH2 0x4388 JUMP JUMPDEST SWAP4 POP POP PUSH1 0x20 PUSH2 0x4784 DUP7 DUP3 DUP8 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x4812 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x33B0 DUP5 DUP5 PUSH2 0x4461 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x4831 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x46A3 DUP6 DUP6 PUSH2 0x4461 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x60 DUP6 DUP8 SUB SLT ISZERO PUSH2 0x4853 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x485F DUP8 DUP8 PUSH2 0x4461 JUMP JUMPDEST SWAP5 POP POP PUSH1 0x20 PUSH2 0x4870 DUP8 DUP3 DUP9 ADD PUSH2 0x4310 JUMP JUMPDEST SWAP4 POP POP PUSH1 0x40 DUP6 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x488D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4899 DUP8 DUP3 DUP9 ADD PUSH2 0x4485 JUMP JUMPDEST SWAP6 SWAP9 SWAP5 SWAP8 POP SWAP6 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 PUSH1 0x60 DUP5 DUP7 SUB SLT ISZERO PUSH2 0x48BA JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x48C6 DUP7 DUP7 PUSH2 0x4461 JUMP JUMPDEST SWAP4 POP POP PUSH1 0x20 PUSH2 0x48D7 DUP7 DUP3 DUP8 ADD PUSH2 0x4310 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x40 DUP5 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x48F4 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x47AD DUP7 DUP3 DUP8 ADD PUSH2 0x44CE JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x4912 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x33B0 DUP5 DUP5 PUSH2 0x446D JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x4930 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x33B0 DUP5 DUP5 PUSH2 0x4479 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x494E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x4965 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x33B0 DUP5 DUP3 DUP6 ADD PUSH2 0x451D JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x80 DUP6 DUP8 SUB SLT ISZERO PUSH2 0x4987 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP5 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x499E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x49AA DUP8 DUP3 DUP9 ADD PUSH2 0x451D JUMP JUMPDEST SWAP5 POP POP PUSH1 0x20 DUP6 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x49C7 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x49D3 DUP8 DUP3 DUP9 ADD PUSH2 0x451D JUMP JUMPDEST SWAP4 POP POP PUSH1 0x40 DUP6 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x49F0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x49FC DUP8 DUP3 DUP9 ADD PUSH2 0x44CE JUMP JUMPDEST SWAP3 POP POP PUSH1 0x60 DUP6 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x4A19 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4A25 DUP8 DUP3 DUP9 ADD PUSH2 0x44CE JUMP JUMPDEST SWAP2 POP POP SWAP3 SWAP6 SWAP2 SWAP5 POP SWAP3 POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 PUSH1 0x60 DUP5 DUP7 SUB SLT ISZERO PUSH2 0x4A46 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP4 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x4A5D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4A69 DUP7 DUP3 DUP8 ADD PUSH2 0x451D JUMP JUMPDEST SWAP4 POP POP PUSH1 0x20 PUSH2 0x48D7 DUP7 DUP3 DUP8 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x80 DUP8 DUP10 SUB SLT ISZERO PUSH2 0x4A93 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x4A9F DUP10 DUP10 PUSH2 0x4461 JUMP JUMPDEST SWAP7 POP POP PUSH1 0x20 PUSH2 0x4AB0 DUP10 DUP3 DUP11 ADD PUSH2 0x4310 JUMP JUMPDEST SWAP6 POP POP PUSH1 0x40 DUP8 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x4ACD JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4AD9 DUP10 DUP3 DUP11 ADD PUSH2 0x4485 JUMP JUMPDEST SWAP5 POP SWAP5 POP POP PUSH1 0x60 DUP8 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x4AF8 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4B04 DUP10 DUP3 DUP11 ADD PUSH2 0x4485 JUMP JUMPDEST SWAP3 POP SWAP3 POP POP SWAP3 SWAP6 POP SWAP3 SWAP6 POP SWAP3 SWAP6 JUMP JUMPDEST PUSH2 0x4B1C DUP2 PUSH2 0x57C2 JUMP JUMPDEST DUP3 MSTORE POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x4B2D DUP3 PUSH2 0x57BE JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP PUSH2 0x4B3F DUP4 PUSH2 0x57B8 JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP3 DUP2 LT ISZERO PUSH2 0x4B6F JUMPI PUSH2 0x4B55 DUP7 DUP4 MLOAD PUSH2 0x52F7 JUMP JUMPDEST PUSH2 0x4B5E DUP3 PUSH2 0x57B8 JUMP JUMPDEST PUSH1 0x60 SWAP7 SWAP1 SWAP7 ADD SWAP6 SWAP2 POP PUSH1 0x1 ADD PUSH2 0x4B42 JUMP JUMPDEST POP SWAP4 SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH2 0x4B1C DUP2 PUSH2 0x57DB JUMP JUMPDEST PUSH2 0x4B1C DUP2 PUSH2 0x57E0 JUMP JUMPDEST PUSH2 0x4B1C DUP2 PUSH2 0x57E3 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x4B9F DUP3 PUSH2 0x57BE JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH2 0x4BB3 DUP2 PUSH1 0x20 DUP7 ADD PUSH1 0x20 DUP7 ADD PUSH2 0x5825 JUMP JUMPDEST PUSH2 0x4BBC DUP2 PUSH2 0x5851 JUMP JUMPDEST SWAP1 SWAP4 ADD PUSH1 0x20 ADD SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH2 0x4B1C DUP2 PUSH2 0x580E JUMP JUMPDEST PUSH1 0x12 DUP2 MSTORE PUSH32 0x4C454E4754485F36355F52455155495245440000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xD DUP2 MSTORE PUSH32 0x494E56414C49445F54414B455200000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xE DUP2 MSTORE PUSH32 0x4F524445525F4F56455246494C4C000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x11 DUP2 MSTORE PUSH32 0x55494E543235365F554E444552464C4F57000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x1A DUP2 MSTORE PUSH32 0x41535345545F50524F58595F414C52454144595F455849535453000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x26 DUP2 MSTORE PUSH32 0x475245415445525F4F525F455155414C5F544F5F33325F4C454E4754485F5245 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x5155495245440000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x15 DUP2 MSTORE PUSH32 0x5349474E41545552455F554E535550504F525445440000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x10 DUP2 MSTORE PUSH32 0x4449564953494F4E5F42595F5A45524F00000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x17 DUP2 MSTORE PUSH32 0x494E56414C49445F4F524445525F5349474E4154555245000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xD DUP2 MSTORE PUSH32 0x494E56414C49445F4D414B455200000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x10 DUP2 MSTORE PUSH32 0x55494E543235365F4F564552464C4F5700000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xF DUP2 MSTORE PUSH32 0x494E56414C49445F54585F484153480000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x11 DUP2 MSTORE PUSH32 0x494E56414C49445F5349474E4154555245000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xE DUP2 MSTORE PUSH32 0x524F554E44494E475F4552524F52000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x10 DUP2 MSTORE PUSH32 0x4641494C45445F455845435554494F4E00000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xD DUP2 MSTORE PUSH32 0x54414B45525F4F56455250415900000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x14 DUP2 MSTORE PUSH32 0x494E56414C49445F54414B45525F414D4F554E54000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x1A DUP2 MSTORE PUSH32 0x41535345545F50524F58595F444F45535F4E4F545F4558495354000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x21 DUP2 MSTORE PUSH32 0x475245415445525F5448414E5F5A45524F5F4C454E4754485F52455155495245 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x4400000000000000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x11 DUP2 MSTORE PUSH32 0x5349474E41545552455F494C4C4547414C000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x1E DUP2 MSTORE PUSH32 0x4C454E4754485F475245415445525F5448414E5F305F52455155495245440000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x17 DUP2 MSTORE PUSH32 0x494E56414C49445F4E45575F4F524445525F45504F4348000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x1E DUP2 MSTORE PUSH32 0x4C454E4754485F475245415445525F5448414E5F335F52455155495245440000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x14 DUP2 MSTORE PUSH32 0x434F4D504C4554455F46494C4C5F4641494C4544000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x12 DUP2 MSTORE PUSH32 0x494E56414C49445F46494C4C5F50524943450000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x12 DUP2 MSTORE PUSH32 0x5245454E5452414E43595F494C4C4547414C0000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x13 DUP2 MSTORE PUSH32 0x4F4E4C595F434F4E54524143545F4F574E455200000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x26 DUP2 MSTORE PUSH32 0x475245415445525F4F525F455155414C5F544F5F32305F4C454E4754485F5245 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x5155495245440000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x10 DUP2 MSTORE PUSH32 0x4F524445525F554E46494C4C41424C4500000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xE DUP2 MSTORE PUSH32 0x494E56414C49445F53454E444552000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x18 DUP2 MSTORE PUSH32 0x4E454741544956455F5350524541445F52455155495245440000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x14 DUP2 MSTORE PUSH32 0x494E56414C49445F54585F5349474E4154555245000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x11 DUP2 MSTORE PUSH32 0x4C454E4754485F305F5245515549524544000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST DUP1 MLOAD PUSH1 0x80 DUP4 ADD SWAP1 PUSH2 0x5285 DUP5 DUP3 PUSH2 0x4B82 JUMP JUMPDEST POP PUSH1 0x20 DUP3 ADD MLOAD PUSH2 0x5298 PUSH1 0x20 DUP6 ADD DUP3 PUSH2 0x4B82 JUMP JUMPDEST POP PUSH1 0x40 DUP3 ADD MLOAD PUSH2 0x52AB PUSH1 0x40 DUP6 ADD DUP3 PUSH2 0x4B82 JUMP JUMPDEST POP PUSH1 0x60 DUP3 ADD MLOAD PUSH2 0x2758 PUSH1 0x60 DUP6 ADD DUP3 PUSH2 0x4B82 JUMP JUMPDEST DUP1 MLOAD PUSH2 0x120 DUP4 ADD SWAP1 PUSH2 0x52D0 DUP5 DUP3 PUSH2 0x5274 JUMP JUMPDEST POP PUSH1 0x20 DUP3 ADD MLOAD PUSH2 0x52E3 PUSH1 0x80 DUP6 ADD DUP3 PUSH2 0x5274 JUMP JUMPDEST POP PUSH1 0x40 DUP3 ADD MLOAD PUSH2 0x2758 PUSH2 0x100 DUP6 ADD DUP3 PUSH2 0x4B82 JUMP JUMPDEST DUP1 MLOAD PUSH1 0x60 DUP4 ADD SWAP1 PUSH2 0x5308 DUP5 DUP3 PUSH2 0x532E JUMP JUMPDEST POP PUSH1 0x20 DUP3 ADD MLOAD PUSH2 0x531B PUSH1 0x20 DUP6 ADD DUP3 PUSH2 0x4B82 JUMP JUMPDEST POP PUSH1 0x40 DUP3 ADD MLOAD PUSH2 0x2758 PUSH1 0x40 DUP6 ADD DUP3 PUSH2 0x4B82 JUMP JUMPDEST PUSH2 0x4B1C DUP2 PUSH2 0x5808 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x2F23 DUP3 DUP5 PUSH2 0x4B13 JUMP JUMPDEST PUSH2 0x100 DUP2 ADD PUSH2 0x5354 DUP3 DUP12 PUSH2 0x4B13 JUMP JUMPDEST PUSH2 0x5361 PUSH1 0x20 DUP4 ADD DUP11 PUSH2 0x4B13 JUMP JUMPDEST PUSH2 0x536E PUSH1 0x40 DUP4 ADD DUP10 PUSH2 0x4B82 JUMP JUMPDEST PUSH2 0x537B PUSH1 0x60 DUP4 ADD DUP9 PUSH2 0x4B82 JUMP JUMPDEST PUSH2 0x5388 PUSH1 0x80 DUP4 ADD DUP8 PUSH2 0x4B82 JUMP JUMPDEST PUSH2 0x5395 PUSH1 0xA0 DUP4 ADD DUP7 PUSH2 0x4B82 JUMP JUMPDEST DUP2 DUP2 SUB PUSH1 0xC0 DUP4 ADD MSTORE PUSH2 0x53A7 DUP2 DUP6 PUSH2 0x4B94 JUMP JUMPDEST SWAP1 POP DUP2 DUP2 SUB PUSH1 0xE0 DUP4 ADD MSTORE PUSH2 0x53BB DUP2 DUP5 PUSH2 0x4B94 JUMP JUMPDEST SWAP11 SWAP10 POP POP POP POP POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x60 DUP2 ADD PUSH2 0x53D7 DUP3 DUP7 PUSH2 0x4B13 JUMP JUMPDEST DUP2 DUP2 SUB PUSH1 0x20 DUP4 ADD MSTORE PUSH2 0x53E9 DUP2 DUP6 PUSH2 0x4B94 JUMP JUMPDEST SWAP1 POP DUP2 DUP2 SUB PUSH1 0x40 DUP4 ADD MSTORE PUSH2 0x53FD DUP2 DUP5 PUSH2 0x4B94 JUMP JUMPDEST SWAP6 SWAP5 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x25BF DUP2 DUP5 PUSH2 0x4B22 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x2F23 DUP3 DUP5 PUSH2 0x4B79 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x2F23 DUP3 DUP5 PUSH2 0x4B82 JUMP JUMPDEST PUSH1 0x60 DUP2 ADD PUSH2 0x5441 DUP3 DUP7 PUSH2 0x4B82 JUMP JUMPDEST PUSH2 0x544E PUSH1 0x20 DUP4 ADD DUP6 PUSH2 0x4B13 JUMP JUMPDEST DUP2 DUP2 SUB PUSH1 0x40 DUP4 ADD MSTORE PUSH2 0x53FD DUP2 DUP5 PUSH2 0x4B94 JUMP JUMPDEST PUSH1 0x40 DUP2 ADD PUSH2 0x546E DUP3 DUP6 PUSH2 0x4B82 JUMP JUMPDEST DUP2 DUP2 SUB PUSH1 0x20 DUP4 ADD MSTORE PUSH2 0x33B0 DUP2 DUP5 PUSH2 0x4B94 JUMP JUMPDEST PUSH1 0x80 DUP2 ADD PUSH2 0x548E DUP3 DUP8 PUSH2 0x4B82 JUMP JUMPDEST PUSH2 0x549B PUSH1 0x20 DUP4 ADD DUP7 PUSH2 0x532E JUMP JUMPDEST PUSH2 0x54A8 PUSH1 0x40 DUP4 ADD DUP6 PUSH2 0x4B82 JUMP JUMPDEST PUSH2 0x53FD PUSH1 0x60 DUP4 ADD DUP5 PUSH2 0x4B82 JUMP JUMPDEST PUSH1 0x40 DUP2 ADD PUSH2 0x54C3 DUP3 DUP6 PUSH2 0x4B8B JUMP JUMPDEST PUSH2 0x25BF PUSH1 0x20 DUP4 ADD DUP5 PUSH2 0x4B13 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x25BF DUP2 DUP5 PUSH2 0x4B94 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x2F23 DUP3 DUP5 PUSH2 0x4BC9 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4BD2 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4C02 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4C32 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4C62 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4C92 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4CC2 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4D18 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4D48 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4D78 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4DA8 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4DD8 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4E08 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4E38 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4E68 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4E98 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4EC8 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4EF8 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4F28 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4F58 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4FAE JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4FDE JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x500E JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x503E JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x506E JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x509E JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x50CE JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x50FE JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x512E JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x5184 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x51B4 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x51E4 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x5214 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x5244 JUMP JUMPDEST PUSH1 0x80 DUP2 ADD PUSH2 0x2F23 DUP3 DUP5 PUSH2 0x5274 JUMP JUMPDEST PUSH2 0x120 DUP2 ADD PUSH2 0x2F23 DUP3 DUP5 PUSH2 0x52BE JUMP JUMPDEST PUSH1 0x60 DUP2 ADD PUSH2 0x2F23 DUP3 DUP5 PUSH2 0x52F7 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP2 DUP2 ADD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT DUP3 DUP3 LT OR ISZERO PUSH2 0x5749 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x40 MSTORE SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 PUSH8 0xFFFFFFFFFFFFFFFF DUP3 GT ISZERO PUSH2 0x5768 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x20 SWAP1 DUP2 MUL ADD SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH8 0xFFFFFFFFFFFFFFFF DUP3 GT ISZERO PUSH2 0x5789 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x20 PUSH1 0x1F SWAP2 SWAP1 SWAP2 ADD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 AND ADD SWAP1 JUMP JUMPDEST PUSH1 0x20 ADD SWAP1 JUMP JUMPDEST MLOAD SWAP1 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 JUMP JUMPDEST ISZERO ISZERO SWAP1 JUMP JUMPDEST SWAP1 JUMP JUMPDEST PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 AND SWAP1 JUMP JUMPDEST PUSH1 0xFF AND SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x2F23 DUP3 PUSH2 0x57C2 JUMP JUMPDEST DUP3 DUP2 DUP4 CALLDATACOPY POP PUSH1 0x0 SWAP2 ADD MSTORE JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x5840 JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0x5828 JUMP JUMPDEST DUP4 DUP2 GT ISZERO PUSH2 0x2758 JUMPI POP POP PUSH1 0x0 SWAP2 ADD MSTORE JUMP JUMPDEST PUSH1 0x1F ADD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 AND SWAP1 JUMP STOP LOG2 PUSH6 0x627A7A723058 KECCAK256 0xb1 SWAP13 SWAP3 KECCAK256 LT ORIGIN PUSH1 0x33 SWAP9 0xc0 SMOD 0xdc 0xcf DUP1 SWAP4 0xc PUSH3 0x6EF89F 0xa6 0xc8 0x48 0xc8 0xb7 0xef MSTORE PUSH8 0xD07CD4ED6C657870 PUSH6 0x72696D656E74 PUSH2 0x6CF5 STOP CALLDATACOPY ", + "sourceMap": "897:514:21:-;;;689:5:56;667:27;;-1:-1:-1;;667:27:56;;;1188:221:21;5:2:-1;;;;30:1;27;20:12;5:2;-1:-1;1045:148:39;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;22:32:-1;26:21;;;22:32;6:49;;1045:148:39;;;;;;;;1035:159;;1045:148;;;;;1035:159;;;;1045:148;1035:159;36:153:-1;66:2;58:11;;36:153;;176:10;;164:23;;-1:-1;;139:12;;;;98:2;89:12;;;;114;36:153;;;299:10;344;;263:2;259:12;;;254:3;250:22;-1:-1;;246:30;311:9;;295:26;;;340:21;;377:20;365:33;;1035:159:39;;;;;;;;;;;;1511:18;;;;;;;;;;;;;;;;1495:36;;1035:159;;-1:-1:-1;1035:159:39;;-1:-1:-1;1495:36:39;;;;-1:-1:-1;1511:18:39;1495:36;1511:18;36:153:-1;66:2;58:11;;36:153;;176:10;;164:23;;-1:-1;;139:12;;;;98:2;89:12;;;;114;36:153;;;299:10;344;;263:2;259:12;;;254:3;250:22;-1:-1;;246:30;311:9;;295:26;;;340:21;;377:20;365:33;;1495:36:39;;;;;;;;;;;;1561:21;;;;;274:1:-1;1561:21:39;;;;;;;;;;1545:39;;1495:36;;-1:-1:-1;1495:36:39;;-1:-1:-1;1545:39:39;;;;-1:-1:-1;274:1;1545:39:39;1561:21;36:153:-1;66:2;58:11;;36:153;;176:10;;164:23;;-1:-1;;139:12;;;;98:2;89:12;;;;114;36:153;;;299:10;344;;263:2;259:12;;;254:3;250:22;-1:-1;;246:30;311:9;;295:26;;;340:21;;377:20;365:33;;1545:39:39;;;;;;;;;;;;1416:214;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1614:4:39;1416:214;;;;;;;;;;26:21:-1;;;22:32;;;6:49;;1416:214:39;;;;;;;;1406:225;;1416:214;;;;-1:-1:-1;1406:225:39;;;-1:-1:-1;1406:225:39;1416:214;1406:225;36:153:-1;66:2;58:11;;36:153;;176:10;;164:23;;-1:-1;;139:12;;;;98:2;89:12;;;;114;36:153;;;299:10;344;;-1:-1;;263:2;259:12;;;;254:3;250:22;246:30;;;;340:21;;;311:9;;295:26;377:20;;;;365:33;;1406:225:39;;;;;;;;;;274:1:-1;1385:246:39;-1:-1:-1;;162:5:55;:18;;-1:-1:-1;;;;;;162:18:55;170:10;162:18;;;-1:-1:-1;897:514:21;;;;;;" + }, + "deployedBytecode": { + "linkReferences": {}, + "object": "0x6080604052600436106101b65763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663288cdc9181146101bb578063297bb70b146101f15780632ac126221461021e5780633683ef8e1461024b5780633c28d8611461026d5780633e228bae1461029a5780633fd3c997146102ba5780634ac14782146102e75780634d0ae546146103075780634f9559b11461032757806350dde190146103475780636070410814610367578063642f2eaf1461039457806364a3bc15146103b457806377fcce68146103d45780637b8e3514146103f45780637e1d9808146104145780637e9d74dc1461043457806382c174d0146104615780638da5cb5b146104815780639363470214610496578063a3e20380146104b6578063b4be83d5146104d6578063bfc8bfce146104f6578063c585bb9314610516578063c75e0a8114610536578063d46b02c314610563578063d9bfa73e14610583578063db123b1a146105a3578063dd1c7d18146105c5578063e306f779146105e5578063e5fa431b146105fa578063eea086ba1461061a578063f2fde38b1461062f578063ffa1ad741461064f575b600080fd5b3480156101c757600080fd5b506101db6101d6366004614800565b610664565b6040516101e89190615425565b60405180910390f35b3480156101fd57600080fd5b5061021161020c366004614723565b610676565b6040516101e891906156ff565b34801561022a57600080fd5b5061023e610239366004614800565b6107a1565b6040516101e89190615417565b34801561025757600080fd5b5061026b61026636600461483d565b6107b6565b005b34801561027957600080fd5b5061028d610288366004614971565b6108a3565b6040516101e8919061570d565b3480156102a657600080fd5b506102116102b5366004614a31565b610a3a565b3480156102c657600080fd5b506102da6102d5366004614900565b610a90565b6040516101e891906154e1565b3480156102f357600080fd5b5061026b6103023660046146ee565b610ab8565b34801561031357600080fd5b50610211610322366004614723565b610b85565b34801561033357600080fd5b5061026b610342366004614800565b610c75565b34801561035357600080fd5b50610211610362366004614723565b610e2a565b34801561037357600080fd5b50610387610382366004614900565b610ebe565b6040516101e89190615337565b3480156103a057600080fd5b5061023e6103af366004614800565b610f0c565b3480156103c057600080fd5b506102116103cf366004614a31565b610f21565b3480156103e057600080fd5b5061026b6103ef3660046146be565b610fcc565b34801561040057600080fd5b5061023e61040f366004614684565b611106565b34801561042057600080fd5b5061021161042f3660046147b7565b611126565b34801561044057600080fd5b5061045461044f3660046146ee565b61128a565b6040516101e89190615406565b34801561046d57600080fd5b5061023e61047c36600461481e565b61131f565b34801561048d57600080fd5b5061038761133f565b3480156104a257600080fd5b5061023e6104b13660046148a5565b61135b565b3480156104c257600080fd5b506102116104d13660046147b7565b6118de565b3480156104e257600080fd5b506102116104f1366004614a31565b6119f1565b34801561050257600080fd5b5061026b610511366004614a7a565b611a6c565b34801561052257600080fd5b5061026b610531366004614666565b611d05565b34801561054257600080fd5b5061055661055136600461493c565b611f30565b6040516101e8919061571c565b34801561056f57600080fd5b5061026b61057e36600461493c565b61202a565b34801561058f57600080fd5b506101db61059e366004614684565b6120c6565b3480156105af57600080fd5b506105b86120e3565b6040516101e891906154d0565b3480156105d157600080fd5b506102116105e03660046147b7565b612143565b3480156105f157600080fd5b506101db612218565b34801561060657600080fd5b506102116106153660046147b7565b61221e565b34801561062657600080fd5b50610387612390565b34801561063b57600080fd5b5061026b61064a366004614666565b6123ac565b34801561065b57600080fd5b506105b861245d565b60036020526000908152604090205481565b61067e614298565b600080610689614298565b60005460ff16156106cf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b60405180910390fd5b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011781558751935091505b81831461076f57610758878381518110151561071957fe5b90602001906020020151878481518110151561073157fe5b90602001906020020151878581518110151561074957fe5b90602001906020020151612494565b90506107648482612532565b600190910190610701565b5050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055509392505050565b60046020526000908152604090205460ff1681565b73ffffffffffffffffffffffffffffffffffffffff831633146108465761080e848484848080601f0160208091040260200160405190810160405280939291908181526020018383808284375061135b945050505050565b1515610846576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906155af565b5050600091825260066020908152604080842073ffffffffffffffffffffffffffffffffffffffff9093168452919052902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b6108ab6142c1565b6108b36142f0565b6108bb6142f0565b6000805460ff16156108f9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905561016080890151610140808a01919091528901519088015261094588611f30565b925061095087611f30565b915061095a612594565b9050610968888483896125c6565b610974878383886125c6565b61097e888861275e565b6109928888856040015185604001516127be565b8051602081015190519195506109ad918a9186918190612945565b6020808501519081015190516109c99189918591908190612945565b6109e28882856020015186604001518860000151612a5e565b6109fb8782846020015185604001518860200151612a5e565b610a0788888387612b0a565b5050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905550949350505050565b610a42614298565b6060610a4f858585612c92565b9050608081825160208401305af48015610a8657815183526020820151602084015260408201516040840152606082015160608401525b505b509392505050565b600a6020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b60008054819060ff1615610af8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b5050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011781558151905b808214610b5857610b508382815181101515610b4157fe5b90602001906020020151612e64565b600101610b29565b5050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905550565b610b8d614298565b600080610b98614298565b60005460ff1615610bd5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011781558751935091505b81831461076f57610c5e8783815181101515610c1f57fe5b906020019060200201518784815181101515610c3757fe5b906020019060200201518785815181101515610c4f57fe5b90602001906020020151612e8f565b9050610c6a8482612532565b600190910190610c07565b6000805481908190819060ff1615610cb9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055610cec612594565b935073ffffffffffffffffffffffffffffffffffffffff84163314610d115733610d14565b60005b73ffffffffffffffffffffffffffffffffffffffff8086166000908152600560209081526040808320938516835292905220549093506001860192509050808211610d8b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061563f565b73ffffffffffffffffffffffffffffffffffffffff80851660008181526005602090815260408083209488168084529490915290819020859055517f82af639571738f4ebd4268fb0363d8957ebe1bbb9e78dba5ebd69eed39b154f090610df3908690615425565b60405180910390a35050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055505050565b610e32614298565b600080610e3d614298565b86519250600091505b818314610eb457610e9d8783815181101515610e5e57fe5b906020019060200201518784815181101515610e7657fe5b906020019060200201518785815181101515610e8e57fe5b90602001906020020151610a3a565b9050610ea98482612532565b600190910190610e46565b5050509392505050565b7fffffffff0000000000000000000000000000000000000000000000000000000081166000908152600a602052604090205473ffffffffffffffffffffffffffffffffffffffff165b919050565b60086020526000908152604090205460ff1681565b610f29614298565b60005460ff1615610f66576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055610f9c848484612e8f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055949350505050565b6000805460ff161561100a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905561103d612594565b73ffffffffffffffffffffffffffffffffffffffff8181166000818152600760209081526040808320948916808452949091529081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168715151790555192935090917fa8656e308026eeabce8f0bc18048433252318ab80ac79da0b3d3d8697dfba891906110d1908690615417565b60405180910390a35050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905550565b600760209081526000928352604080842090915290825290205460ff1681565b61112e614298565b6060600080600061113d614298565b60005460ff161561117a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117815589518a919081106111b257fe5b906020019060200201516101600151945088519350600092505b828414611255578489848151811015156111e257fe5b906020019060200201516101600181905250611202888760200151612ee2565b915061122e898481518110151561121557fe5b9060200190602002015183898681518110151561074957fe5b905061123a8682612532565b6020860151881161124a57611255565b6001909201916111cc565b5050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055509195945050505050565b606060006060600084519250826040519080825280602002602001820160405280156112d057816020015b6112bd6142f0565b8152602001906001900390816112b55790505b509150600090505b808314610a88576112ff85828151811015156112f057fe5b90602001906020020151611f30565b828281518110151561130d57fe5b602090810290910101526001016112d8565b600660209081526000928352604080842090915290825290205460ff1681565b60025473ffffffffffffffffffffffffffffffffffffffff1681565b600080600080600080600080600089511115156113a4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061562f565b6113ad89612f29565b7f010000000000000000000000000000000000000000000000000000000000000090049650600760ff88161061140f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061554f565b8660ff16600781111561141e57fe5b9550600086600781111561142e57fe5b1415611466576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061561f565b600186600781111561147457fe5b14156114bc578851156114b3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906156ef565b600097506118d0565b60028660078111156114ca57fe5b141561160557885160411461150b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906154ef565b88600081518110151561151a57fe5b01602001517f010000000000000000000000000000000000000000000000000000000000000090819004810204945061155a89600163ffffffff612fed16565b935061156d89602163ffffffff612fed16565b925060018b868686604051600081526020016040526040516115929493929190615480565b60206040516020810390808403906000865af11580156115b6573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015173ffffffffffffffffffffffffffffffffffffffff8c811690821614995092506118d09050565b600386600781111561161357fe5b14156117b9578851604114611654576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906154ef565b88600081518110151561166357fe5b01602001517f01000000000000000000000000000000000000000000000000000000000000009081900481020494506116a389600163ffffffff612fed16565b93506116b689602163ffffffff612fed16565b925060018b60405160200180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c0182600019166000191681526020019150506040516020818303038152906040526040518082805190602001908083835b6020831061175757805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161171a565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018019909216911617905260408051929094018290038220600083529101928390526115929450925089918991508890615480565b60048660078111156117c757fe5b14156117df576117d88b8b8b613038565b97506118d0565b60058660078111156117ed57fe5b1415611850576117fc8961318d565b73ffffffffffffffffffffffffffffffffffffffff808c1660009081526007602090815260408083209385168352929052205490915060ff16151561184457600097506118d0565b6117d8818c8c8c613206565b600686600781111561185e57fe5b141561189e5760008b815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff8e16845290915290205460ff1697506118d0565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061554f565b505050505050509392505050565b6118e6614298565b60606000806000806118f6614298565b89600081518110151561190557fe5b906020019060200201516101400151955089519450600093505b8385146119e457858a8581518110151561193557fe5b6020908102909101015161014001528651611951908a90612ee2565b92506119948a8581518110151561196457fe5b9060200190602002015160a001518b8681518110151561198057fe5b906020019060200201516080015185613362565b91506119c08a858151811015156119a757fe5b90602001906020020151838a87815181101515610e8e57fe5b90506119cc8782612532565b865189116119d9576119e4565b60019093019261191f565b5050505050509392505050565b6119f9614298565b60005460ff1615611a36576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055610f9c848484612494565b60095460009073ffffffffffffffffffffffffffffffffffffffff1615611abf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b611b02611afd888888888080601f016020809104026020016040519081016040528093929190818152602001838380828437506133b8945050505050565b6135f9565b60008181526008602052604090205490915060ff1615611b4e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061559f565b73ffffffffffffffffffffffffffffffffffffffff86163314611c1f57611ba6818785858080601f0160208091040260200160405190810160405280939291908181526020018383808284375061135b945050505050565b1515611bde576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906156df565b600980547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff88161790555b6000818152600860205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555130908690869080838380828437820191505092505050600060405180830381855af49150501515611cb6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906155cf565b73ffffffffffffffffffffffffffffffffffffffff86163314611cfc57600980547fffffffffffffffffffffffff00000000000000000000000000000000000000001690555b50505050505050565b6002546000908190819073ffffffffffffffffffffffffffffffffffffffff163314611d5d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061568f565b8392508273ffffffffffffffffffffffffffffffffffffffff1663ae25532e6040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b158015611dc457600080fd5b505af1158015611dd8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611dfc919081019061491e565b7fffffffff0000000000000000000000000000000000000000000000000000000081166000908152600a602052604090205490925073ffffffffffffffffffffffffffffffffffffffff1690508015611e81576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061552f565b7fffffffff0000000000000000000000000000000000000000000000000000000082166000908152600a60205260409081902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8616179055517fd2c6b762299c609bdb96520b58a49bfb80186934d4f71a86a367571a15c0319490611f2290849087906154b5565b60405180910390a150505050565b611f386142f0565b611f4182613639565b6020808301829052600091825260039052604090819020549082015260808201511515611f755760015b60ff168152610f07565b60a08201511515611f87576002611f6b565b60a0820151604082015110611f9d576005611f6b565b6101008201514210611fb0576004611f6b565b60208082015160009081526004909152604090205460ff1615611fd4576006611f6b565b610120820151825173ffffffffffffffffffffffffffffffffffffffff90811660009081526005602090815260408083206060880151909416835292905220541115612021576006611f6b565b60038152919050565b60005460ff1615612067576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905561209b81612e64565b50600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055565b600560209081526000928352604080842090915290825290205481565b606060405190810160405280602481526020017ff47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f81526020017f4699f4980000000000000000000000000000000000000000000000000000000081525081565b61214b614298565b6060600080600061215a614298565b88600081518110151561216957fe5b906020019060200201516101600151945088519350600092505b82841461220c5784898481518110151561219957fe5b9060200190602002015161016001819052506121b9888760200151612ee2565b91506121e589848151811015156121cc57fe5b90602001906020020151838986815181101515610e8e57fe5b90506121f18682612532565b602086015188116122015761220c565b600190920191612183565b50505050509392505050565b60015481565b612226614298565b6060600080600080612236614298565b60005460ff1615612273576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011781558a518b919081106122ab57fe5b906020019060200201516101400151955089519450600093505b83851461235a57858a858151811015156122db57fe5b60209081029091010151610140015286516122f7908a90612ee2565b925061230a8a8581518110151561196457fe5b91506123368a8581518110151561231d57fe5b90602001906020020151838a8781518110151561074957fe5b90506123428782612532565b8651891161234f5761235a565b6001909301926122c5565b5050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905550929695505050505050565b60095473ffffffffffffffffffffffffffffffffffffffff1681565b60025473ffffffffffffffffffffffffffffffffffffffff1633146123fd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061568f565b73ffffffffffffffffffffffffffffffffffffffff81161561245a57600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83161790555b50565b60408051808201909152600581527f322e302e30000000000000000000000000000000000000000000000000000000602082015281565b61249c614298565b6124a46142f0565b60008060006124b288611f30565b93506124bc612594565b92506124ca888585896125c6565b6124dc8860a001518560400151612ee2565b91506124e88783613647565b90506124fb888589848960000151612945565b612505888261365d565b945061251c88848660200151876040015189612a5e565b6125278884876136be565b505050509392505050565b815181516125409190613776565b8252602080830151908201516125569190613776565b60208301526040808301519082015161256f9190613776565b6040830152606080830151908201516125889190613776565b60609092019190915250565b60095460009073ffffffffffffffffffffffffffffffffffffffff168181156125bd57816125bf565b335b9392505050565b825160ff16600314612604576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906156af565b606084015173ffffffffffffffffffffffffffffffffffffffff161561267757606084015173ffffffffffffffffffffffffffffffffffffffff163314612677576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906156bf565b602084015173ffffffffffffffffffffffffffffffffffffffff1615612702578173ffffffffffffffffffffffffffffffffffffffff16846020015173ffffffffffffffffffffffffffffffffffffffff16141515612702576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906154ff565b6040830151151561275857612720836020015185600001518361135b565b1515612758576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061556f565b50505050565b6127708260a001518260a001516137c0565b612782836080015183608001516137c0565b10156127ba576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906156cf565b5050565b6127c66142c1565b6000806000806127da8960a0015188612ee2565b93506127ef89608001518a60a001518661381b565b92506127ff8860a0015187612ee2565b915061281488608001518960a001518461381b565b90508084106128575760208086018051839052805182018490525151865182015260808a015160a08b015187519092015161284f929061381b565b855152612894565b845183905284516020908101859052855181015190860180519190915260a089015160808a015191515161288b9290613898565b60208087015101525b84515160208087015101516128a99190612ee2565b604086015284515160808a015160c08b01516128c692919061381b565b85516040015284516020015160a08a015160e08b01516128e792919061381b565b855160600152602085015151608089015160c08a015161290892919061381b565b856020015160400181815250506129308560200151602001518960a001518a60e0015161381b565b60208601516060015250505050949350505050565b82151561297e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906155ef565b828211156129b8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906155df565b8460a001516129cb856040015184613776565b1115612a03576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061550f565b612a118560800151836137c0565b612a1f828760a001516137c0565b1115612a57576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061566f565b5050505050565b612a6c828260200151613776565b600084815260036020908152604091829020929092558681015187518451938501518584015160608701516101408c01516101608d015196518b9873ffffffffffffffffffffffffffffffffffffffff9788169897909616967f0bcc4c97732e47d9946f229edb95f5b6323f601300e4690de719993f3c37112996612afb968f96339692959194909390615345565b60405180910390a45050505050565b60608060405190810160405280602481526020017ff47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f81526020017f4699f498000000000000000000000000000000000000000000000000000000008152509050612b8b85610140015186600001518660000151856020015160200151613935565b61014084015184518651845160200151612ba793929190613935565b612bc08561014001518660000151858560400151613935565b612bdc8186600001518760400151856000015160400151613935565b612bf88185600001518660400151856020015160400151613935565b836040015173ffffffffffffffffffffffffffffffffffffffff16856040015173ffffffffffffffffffffffffffffffffffffffff161415612c6257612c5d81848760400151612c58866000015160600151876020015160600151613776565b613935565b612a57565b612c7a81848760400151856000015160600151613935565b612a5781848660400151856020015160600151613935565b604080517fb4be83d5000000000000000000000000000000000000000000000000000000006020808301919091526060602483018181528751608485019081528884015160a48601529488015160c48501529087015160e4840152608087015161010484015260a087015161012484015260c087015161014484015260e08701516101648401526101008701516101848401526101208701516101a4840152610140870180516101c485019081526101608901516101e4860152610180905251805161020485018190529394919384936044870192849261022489019291820191601f82010460005b81811015612d99578351855260209485019490930192600101612d7b565b50505050818103610160808401919091528a0151805180835260209283019291820191601f82010460005b81811015612de2578351855260209485019490930192600101612dc4565b50505089845250848103602093840190815288518083529093918201918981019190601f82010460005b81811015612e2a578351855260209485019490930192600101612e0c565b5050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08883030188525060405250505050509392505050565b612e6c6142f0565b612e7582611f30565b9050612e818282613aff565b6127ba828260200151613c16565b612e97614298565b612ea2848484612494565b602081015190915083146125bf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061565f565b600082821115612f1e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061551f565b508082035b92915050565b6000808251111515612f67576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061560f565b815182907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101908110612f9757fe5b016020015182517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01909252507f0100000000000000000000000000000000000000000000000000000000000000908190040290565b60008160200183511015151561302f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061553f565b50016020015190565b6040516000906060907f1626ba7e00000000000000000000000000000000000000000000000000000000906130739087908690602401615460565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093178352815191935090829081885afa808015613110576001811461318157612527565b7f08c379a0000000000000000000000000000000000000000000000000000000006000527c20000000000000000000000000000000000000000000000000000000006020527c0c57414c4c45545f4552524f5200000000000000000000000000000000604052600060605260646000fd5b50505195945050505050565b600060148251101515156131cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061569f565b6131db826014845103613cbd565b82517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec019092525090565b6040516000906060907f93634702000000000000000000000000000000000000000000000000000000009061324390879087908790602401615433565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931783528151919350908290818a5afa8080156132e0576001811461335157613356565b7f08c379a0000000000000000000000000000000000000000000000000000000006000527c20000000000000000000000000000000000000000000000000000000006020527c0f56414c494441544f525f4552524f5200000000000000000000000000604052600060605260646000fd5b825194505b50505050949350505050565b600080831161339d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061555f565b6133b06133aa85846137c0565b84613d1e565b949350505050565b604080517f5a65726f45785472616e73616374696f6e2800000000000000000000000000006020808301919091527f75696e743235362073616c742c0000000000000000000000000000000000000060328301527f61646472657373207369676e6572416464726573732c00000000000000000000603f8301527f627974657320646174610000000000000000000000000000000000000000000060558301527f2900000000000000000000000000000000000000000000000000000000000000605f830152825180830384018152606090920192839052815160009384938493909282918401908083835b602083106134e157805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016134a4565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018019909216911617905260405191909301819003812089519097508995509093508392850191508083835b6020831061357757805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161353a565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040805192909401829003822097825281019a909a525073ffffffffffffffffffffffffffffffffffffffff97909716968801969096525050606085015250506080909120919050565b6001546040517f19010000000000000000000000000000000000000000000000000000000000008152600281019190915260228101919091526042902090565b6000612f23611afd83613d35565b600081831061365657816125bf565b5090919050565b613665614298565b6020810182905260a0830151608084015161368191849161381b565b808252608084015160c085015161369992919061381b565b604082015260a083015160e08401516136b391849161381b565b606082015292915050565b60408051606081018252602481527ff47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f60208201527f4699f4980000000000000000000000000000000000000000000000000000000091810191909152610140840151845183516137319291908690613935565b61374a8461016001518486600001518560200151613935565b61376281856000015186604001518560400151613935565b612758818486604001518560600151613935565b6000828201838110156137b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061558f565b8091505b5092915050565b6000808315156137d357600091506137b9565b508282028284828115156137e357fe5b04146137b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061558f565b6000808311613856576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061555f565b61386184848461418e565b1561339d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906155bf565b60008083116138d3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061555f565b6138de848484614213565b15613915576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906155bf565b6133b06133aa61392586856137c0565b613930866001612ee2565b613776565b60008060008311801561397457508373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614155b15613af75785516003106139b4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061564f565b50506020848101517fffffffff00000000000000000000000000000000000000000000000000000000166000818152600a90925260409091205473ffffffffffffffffffffffffffffffffffffffff16801515613a3d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906155ff565b604051660fffffffffffe0603f885101168060840182017fa85e59e40000000000000000000000000000000000000000000000000000000083526080600484015273ffffffffffffffffffffffffffffffffffffffff8816602484015273ffffffffffffffffffffffffffffffffffffffff87166044840152856064840152608483015b81811015613ad957895181526020998a019901613ac1565b61020084858403866000895af1801515613af1573d85fd5b50505050505b505050505050565b805160009060ff16600314613b40576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906156af565b606083015173ffffffffffffffffffffffffffffffffffffffff1615613bb357606083015173ffffffffffffffffffffffffffffffffffffffff163314613bb3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906156bf565b613bbb612594565b835190915073ffffffffffffffffffffffffffffffffffffffff808316911614613c11576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061557f565b505050565b6000818152600460205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558281015183516101408501516101608601519351859473ffffffffffffffffffffffffffffffffffffffff9485169493909316927fdc47b3613d9fe400085f6dbdc99453462279057e6207385042827ed6b1a62cf792613cb1923392906153c9565b60405180910390a45050565b600081601401835110151515613cff576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061569f565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b6000808284811515613d2c57fe5b04949350505050565b604080517f4f726465722800000000000000000000000000000000000000000000000000006020808301919091527f61646472657373206d616b6572416464726573732c000000000000000000000060268301527f616464726573732074616b6572416464726573732c0000000000000000000000603b8301527f6164647265737320666565526563697069656e74416464726573732c0000000060508301527f616464726573732073656e646572416464726573732c00000000000000000000606c8301527f75696e74323536206d616b65724173736574416d6f756e742c0000000000000060828301527f75696e743235362074616b65724173736574416d6f756e742c00000000000000609b8301527f75696e74323536206d616b65724665652c00000000000000000000000000000060b48301527f75696e743235362074616b65724665652c00000000000000000000000000000060c58301527f75696e743235362065787069726174696f6e54696d655365636f6e64732c000060d68301527f75696e743235362073616c742c0000000000000000000000000000000000000060f48301527f6279746573206d616b65724173736574446174612c00000000000000000000006101018301527f62797465732074616b65724173736574446174610000000000000000000000006101168301527f290000000000000000000000000000000000000000000000000000000000000061012a830152825161010b81840301815261012b90920192839052815160009384938493849391929182918401908083835b60208310613fbd57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101613f80565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930181900381206101408b0151805191995095509093508392850191508083835b6020831061405857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161401b565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930181900381206101608b0151805191985095509093508392850191508083835b602083106140f357805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016140b6565b5181516020939093036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018019909116921691909117905260405192018290039091207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0890180516101408b018051610160909c0180519a84529881529288526101a0822091529890525050509190525090919050565b6000808084116141ca576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061555f565b8215806141d5575084155b156141e35760009150610a88565b838015156141ed57fe5b85840990506141fc85846137c0565b6142086103e8836137c0565b101595945050505050565b60008080841161424f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061555f565b82158061425a575084155b156142685760009150610a88565b8380151561427257fe5b8584099050836142828583612ee2565b81151561428b57fe5b0690506141fc85846137c0565b608060405190810160405280600081526020016000815260200160008152602001600081525090565b610120604051908101604052806142d6614298565b81526020016142e3614298565b8152602001600081525090565b604080516060810182526000808252602082018190529181019190915290565b60006125bf82356157c2565b6000601f8201831361432d57600080fd5b813561434061433b82615751565b61572a565b81815260209384019390925082018360005b8381101561437e578135860161436888826144ce565b8452506020928301929190910190600101614352565b5050505092915050565b6000601f8201831361439957600080fd5b81356143a761433b82615751565b81815260209384019390925082018360005b8381101561437e57813586016143cf888261451d565b84525060209283019291909101906001016143b9565b6000601f820183136143f657600080fd5b813561440461433b82615751565b9150818183526020840193506020810190508385602084028201111561442957600080fd5b60005b8381101561437e578161443f8882614461565b845250602092830192919091019060010161442c565b60006125bf82356157db565b60006125bf82356157e0565b60006125bf82356157e3565b60006125bf82516157e3565b600080601f8301841361449757600080fd5b50813567ffffffffffffffff8111156144af57600080fd5b6020830191508360018202830111156144c757600080fd5b9250929050565b6000601f820183136144df57600080fd5b81356144ed61433b82615772565b9150808252602083016020830185838301111561450957600080fd5b614514838284615819565b50505092915050565b6000610180828403121561453057600080fd5b61453b61018061572a565b905060006145498484614310565b825250602061455a84848301614310565b602083015250604061456e84828501614310565b604083015250606061458284828501614310565b606083015250608061459684828501614461565b60808301525060a06145aa84828501614461565b60a08301525060c06145be84828501614461565b60c08301525060e06145d284828501614461565b60e0830152506101006145e784828501614461565b610100830152506101206145fd84828501614461565b6101208301525061014082013567ffffffffffffffff81111561461f57600080fd5b61462b848285016144ce565b6101408301525061016082013567ffffffffffffffff81111561464d57600080fd5b614659848285016144ce565b6101608301525092915050565b60006020828403121561467857600080fd5b60006133b08484614310565b6000806040838503121561469757600080fd5b60006146a38585614310565b92505060206146b485828601614310565b9150509250929050565b600080604083850312156146d157600080fd5b60006146dd8585614310565b92505060206146b485828601614455565b60006020828403121561470057600080fd5b813567ffffffffffffffff81111561471757600080fd5b6133b084828501614388565b60008060006060848603121561473857600080fd5b833567ffffffffffffffff81111561474f57600080fd5b61475b86828701614388565b935050602084013567ffffffffffffffff81111561477857600080fd5b614784868287016143e5565b925050604084013567ffffffffffffffff8111156147a157600080fd5b6147ad8682870161431c565b9150509250925092565b6000806000606084860312156147cc57600080fd5b833567ffffffffffffffff8111156147e357600080fd5b6147ef86828701614388565b935050602061478486828701614461565b60006020828403121561481257600080fd5b60006133b08484614461565b6000806040838503121561483157600080fd5b60006146a38585614461565b6000806000806060858703121561485357600080fd5b600061485f8787614461565b945050602061487087828801614310565b935050604085013567ffffffffffffffff81111561488d57600080fd5b61489987828801614485565b95989497509550505050565b6000806000606084860312156148ba57600080fd5b60006148c68686614461565b93505060206148d786828701614310565b925050604084013567ffffffffffffffff8111156148f457600080fd5b6147ad868287016144ce565b60006020828403121561491257600080fd5b60006133b0848461446d565b60006020828403121561493057600080fd5b60006133b08484614479565b60006020828403121561494e57600080fd5b813567ffffffffffffffff81111561496557600080fd5b6133b08482850161451d565b6000806000806080858703121561498757600080fd5b843567ffffffffffffffff81111561499e57600080fd5b6149aa8782880161451d565b945050602085013567ffffffffffffffff8111156149c757600080fd5b6149d38782880161451d565b935050604085013567ffffffffffffffff8111156149f057600080fd5b6149fc878288016144ce565b925050606085013567ffffffffffffffff811115614a1957600080fd5b614a25878288016144ce565b91505092959194509250565b600080600060608486031215614a4657600080fd5b833567ffffffffffffffff811115614a5d57600080fd5b614a698682870161451d565b93505060206148d786828701614461565b60008060008060008060808789031215614a9357600080fd5b6000614a9f8989614461565b9650506020614ab089828a01614310565b955050604087013567ffffffffffffffff811115614acd57600080fd5b614ad989828a01614485565b9450945050606087013567ffffffffffffffff811115614af857600080fd5b614b0489828a01614485565b92509250509295509295509295565b614b1c816157c2565b82525050565b6000614b2d826157be565b808452602084019350614b3f836157b8565b60005b82811015614b6f57614b558683516152f7565b614b5e826157b8565b606096909601959150600101614b42565b5093949350505050565b614b1c816157db565b614b1c816157e0565b614b1c816157e3565b6000614b9f826157be565b808452614bb3816020860160208601615825565b614bbc81615851565b9093016020019392505050565b614b1c8161580e565b601281527f4c454e4754485f36355f52455155495245440000000000000000000000000000602082015260400190565b600d81527f494e56414c49445f54414b455200000000000000000000000000000000000000602082015260400190565b600e81527f4f524445525f4f56455246494c4c000000000000000000000000000000000000602082015260400190565b601181527f55494e543235365f554e444552464c4f57000000000000000000000000000000602082015260400190565b601a81527f41535345545f50524f58595f414c52454144595f455849535453000000000000602082015260400190565b602681527f475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b601581527f5349474e41545552455f554e535550504f525445440000000000000000000000602082015260400190565b601081527f4449564953494f4e5f42595f5a45524f00000000000000000000000000000000602082015260400190565b601781527f494e56414c49445f4f524445525f5349474e4154555245000000000000000000602082015260400190565b600d81527f494e56414c49445f4d414b455200000000000000000000000000000000000000602082015260400190565b601081527f55494e543235365f4f564552464c4f5700000000000000000000000000000000602082015260400190565b600f81527f494e56414c49445f54585f484153480000000000000000000000000000000000602082015260400190565b601181527f494e56414c49445f5349474e4154555245000000000000000000000000000000602082015260400190565b600e81527f524f554e44494e475f4552524f52000000000000000000000000000000000000602082015260400190565b601081527f4641494c45445f455845435554494f4e00000000000000000000000000000000602082015260400190565b600d81527f54414b45525f4f56455250415900000000000000000000000000000000000000602082015260400190565b601481527f494e56414c49445f54414b45525f414d4f554e54000000000000000000000000602082015260400190565b601a81527f41535345545f50524f58595f444f45535f4e4f545f4558495354000000000000602082015260400190565b602181527f475245415445525f5448414e5f5a45524f5f4c454e4754485f5245515549524560208201527f4400000000000000000000000000000000000000000000000000000000000000604082015260600190565b601181527f5349474e41545552455f494c4c4547414c000000000000000000000000000000602082015260400190565b601e81527f4c454e4754485f475245415445525f5448414e5f305f52455155495245440000602082015260400190565b601781527f494e56414c49445f4e45575f4f524445525f45504f4348000000000000000000602082015260400190565b601e81527f4c454e4754485f475245415445525f5448414e5f335f52455155495245440000602082015260400190565b601481527f434f4d504c4554455f46494c4c5f4641494c4544000000000000000000000000602082015260400190565b601281527f494e56414c49445f46494c4c5f50524943450000000000000000000000000000602082015260400190565b601281527f5245454e5452414e43595f494c4c4547414c0000000000000000000000000000602082015260400190565b601381527f4f4e4c595f434f4e54524143545f4f574e455200000000000000000000000000602082015260400190565b602681527f475245415445525f4f525f455155414c5f544f5f32305f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b601081527f4f524445525f554e46494c4c41424c4500000000000000000000000000000000602082015260400190565b600e81527f494e56414c49445f53454e444552000000000000000000000000000000000000602082015260400190565b601881527f4e454741544956455f5350524541445f52455155495245440000000000000000602082015260400190565b601481527f494e56414c49445f54585f5349474e4154555245000000000000000000000000602082015260400190565b601181527f4c454e4754485f305f5245515549524544000000000000000000000000000000602082015260400190565b805160808301906152858482614b82565b5060208201516152986020850182614b82565b5060408201516152ab6040850182614b82565b5060608201516127586060850182614b82565b80516101208301906152d08482615274565b5060208201516152e36080850182615274565b506040820151612758610100850182614b82565b80516060830190615308848261532e565b50602082015161531b6020850182614b82565b5060408201516127586040850182614b82565b614b1c81615808565b60208101612f238284614b13565b6101008101615354828b614b13565b615361602083018a614b13565b61536e6040830189614b82565b61537b6060830188614b82565b6153886080830187614b82565b61539560a0830186614b82565b81810360c08301526153a78185614b94565b905081810360e08301526153bb8184614b94565b9a9950505050505050505050565b606081016153d78286614b13565b81810360208301526153e98185614b94565b905081810360408301526153fd8184614b94565b95945050505050565b602080825281016125bf8184614b22565b60208101612f238284614b79565b60208101612f238284614b82565b606081016154418286614b82565b61544e6020830185614b13565b81810360408301526153fd8184614b94565b6040810161546e8285614b82565b81810360208301526133b08184614b94565b6080810161548e8287614b82565b61549b602083018661532e565b6154a86040830185614b82565b6153fd6060830184614b82565b604081016154c38285614b8b565b6125bf6020830184614b13565b602080825281016125bf8184614b94565b60208101612f238284614bc9565b60208082528101612f2381614bd2565b60208082528101612f2381614c02565b60208082528101612f2381614c32565b60208082528101612f2381614c62565b60208082528101612f2381614c92565b60208082528101612f2381614cc2565b60208082528101612f2381614d18565b60208082528101612f2381614d48565b60208082528101612f2381614d78565b60208082528101612f2381614da8565b60208082528101612f2381614dd8565b60208082528101612f2381614e08565b60208082528101612f2381614e38565b60208082528101612f2381614e68565b60208082528101612f2381614e98565b60208082528101612f2381614ec8565b60208082528101612f2381614ef8565b60208082528101612f2381614f28565b60208082528101612f2381614f58565b60208082528101612f2381614fae565b60208082528101612f2381614fde565b60208082528101612f238161500e565b60208082528101612f238161503e565b60208082528101612f238161506e565b60208082528101612f238161509e565b60208082528101612f23816150ce565b60208082528101612f23816150fe565b60208082528101612f238161512e565b60208082528101612f2381615184565b60208082528101612f23816151b4565b60208082528101612f23816151e4565b60208082528101612f2381615214565b60208082528101612f2381615244565b60808101612f238284615274565b6101208101612f2382846152be565b60608101612f2382846152f7565b60405181810167ffffffffffffffff8111828210171561574957600080fd5b604052919050565b600067ffffffffffffffff82111561576857600080fd5b5060209081020190565b600067ffffffffffffffff82111561578957600080fd5b506020601f919091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160190565b60200190565b5190565b73ffffffffffffffffffffffffffffffffffffffff1690565b151590565b90565b7fffffffff000000000000000000000000000000000000000000000000000000001690565b60ff1690565b6000612f23826157c2565b82818337506000910152565b60005b83811015615840578181015183820152602001615828565b838111156127585750506000910152565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016905600a265627a7a72305820b19c92201032603398c007dccf80930c626ef89fa6c848c8b7ef5267d07cd4ed6c6578706572696d656e74616cf50037", + "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0x1B6 JUMPI PUSH4 0xFFFFFFFF PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0x288CDC91 DUP2 EQ PUSH2 0x1BB JUMPI DUP1 PUSH4 0x297BB70B EQ PUSH2 0x1F1 JUMPI DUP1 PUSH4 0x2AC12622 EQ PUSH2 0x21E JUMPI DUP1 PUSH4 0x3683EF8E EQ PUSH2 0x24B JUMPI DUP1 PUSH4 0x3C28D861 EQ PUSH2 0x26D JUMPI DUP1 PUSH4 0x3E228BAE EQ PUSH2 0x29A JUMPI DUP1 PUSH4 0x3FD3C997 EQ PUSH2 0x2BA JUMPI DUP1 PUSH4 0x4AC14782 EQ PUSH2 0x2E7 JUMPI DUP1 PUSH4 0x4D0AE546 EQ PUSH2 0x307 JUMPI DUP1 PUSH4 0x4F9559B1 EQ PUSH2 0x327 JUMPI DUP1 PUSH4 0x50DDE190 EQ PUSH2 0x347 JUMPI DUP1 PUSH4 0x60704108 EQ PUSH2 0x367 JUMPI DUP1 PUSH4 0x642F2EAF EQ PUSH2 0x394 JUMPI DUP1 PUSH4 0x64A3BC15 EQ PUSH2 0x3B4 JUMPI DUP1 PUSH4 0x77FCCE68 EQ PUSH2 0x3D4 JUMPI DUP1 PUSH4 0x7B8E3514 EQ PUSH2 0x3F4 JUMPI DUP1 PUSH4 0x7E1D9808 EQ PUSH2 0x414 JUMPI DUP1 PUSH4 0x7E9D74DC EQ PUSH2 0x434 JUMPI DUP1 PUSH4 0x82C174D0 EQ PUSH2 0x461 JUMPI DUP1 PUSH4 0x8DA5CB5B EQ PUSH2 0x481 JUMPI DUP1 PUSH4 0x93634702 EQ PUSH2 0x496 JUMPI DUP1 PUSH4 0xA3E20380 EQ PUSH2 0x4B6 JUMPI DUP1 PUSH4 0xB4BE83D5 EQ PUSH2 0x4D6 JUMPI DUP1 PUSH4 0xBFC8BFCE EQ PUSH2 0x4F6 JUMPI DUP1 PUSH4 0xC585BB93 EQ PUSH2 0x516 JUMPI DUP1 PUSH4 0xC75E0A81 EQ PUSH2 0x536 JUMPI DUP1 PUSH4 0xD46B02C3 EQ PUSH2 0x563 JUMPI DUP1 PUSH4 0xD9BFA73E EQ PUSH2 0x583 JUMPI DUP1 PUSH4 0xDB123B1A EQ PUSH2 0x5A3 JUMPI DUP1 PUSH4 0xDD1C7D18 EQ PUSH2 0x5C5 JUMPI DUP1 PUSH4 0xE306F779 EQ PUSH2 0x5E5 JUMPI DUP1 PUSH4 0xE5FA431B EQ PUSH2 0x5FA JUMPI DUP1 PUSH4 0xEEA086BA EQ PUSH2 0x61A JUMPI DUP1 PUSH4 0xF2FDE38B EQ PUSH2 0x62F JUMPI DUP1 PUSH4 0xFFA1AD74 EQ PUSH2 0x64F JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1C7 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1DB PUSH2 0x1D6 CALLDATASIZE PUSH1 0x4 PUSH2 0x4800 JUMP JUMPDEST PUSH2 0x664 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x5425 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1FD JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x20C CALLDATASIZE PUSH1 0x4 PUSH2 0x4723 JUMP JUMPDEST PUSH2 0x676 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x56FF JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x22A JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x23E PUSH2 0x239 CALLDATASIZE PUSH1 0x4 PUSH2 0x4800 JUMP JUMPDEST PUSH2 0x7A1 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x5417 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x257 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x266 CALLDATASIZE PUSH1 0x4 PUSH2 0x483D JUMP JUMPDEST PUSH2 0x7B6 JUMP JUMPDEST STOP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x279 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x28D PUSH2 0x288 CALLDATASIZE PUSH1 0x4 PUSH2 0x4971 JUMP JUMPDEST PUSH2 0x8A3 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x570D JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x2A6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x2B5 CALLDATASIZE PUSH1 0x4 PUSH2 0x4A31 JUMP JUMPDEST PUSH2 0xA3A JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x2C6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x2DA PUSH2 0x2D5 CALLDATASIZE PUSH1 0x4 PUSH2 0x4900 JUMP JUMPDEST PUSH2 0xA90 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x54E1 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x2F3 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x302 CALLDATASIZE PUSH1 0x4 PUSH2 0x46EE JUMP JUMPDEST PUSH2 0xAB8 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x313 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x322 CALLDATASIZE PUSH1 0x4 PUSH2 0x4723 JUMP JUMPDEST PUSH2 0xB85 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x333 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x342 CALLDATASIZE PUSH1 0x4 PUSH2 0x4800 JUMP JUMPDEST PUSH2 0xC75 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x353 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x362 CALLDATASIZE PUSH1 0x4 PUSH2 0x4723 JUMP JUMPDEST PUSH2 0xE2A JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x373 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x387 PUSH2 0x382 CALLDATASIZE PUSH1 0x4 PUSH2 0x4900 JUMP JUMPDEST PUSH2 0xEBE JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x5337 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x3A0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x23E PUSH2 0x3AF CALLDATASIZE PUSH1 0x4 PUSH2 0x4800 JUMP JUMPDEST PUSH2 0xF0C JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x3C0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x3CF CALLDATASIZE PUSH1 0x4 PUSH2 0x4A31 JUMP JUMPDEST PUSH2 0xF21 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x3E0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x3EF CALLDATASIZE PUSH1 0x4 PUSH2 0x46BE JUMP JUMPDEST PUSH2 0xFCC JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x400 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x23E PUSH2 0x40F CALLDATASIZE PUSH1 0x4 PUSH2 0x4684 JUMP JUMPDEST PUSH2 0x1106 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x420 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x42F CALLDATASIZE PUSH1 0x4 PUSH2 0x47B7 JUMP JUMPDEST PUSH2 0x1126 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x440 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x454 PUSH2 0x44F CALLDATASIZE PUSH1 0x4 PUSH2 0x46EE JUMP JUMPDEST PUSH2 0x128A JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x5406 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x46D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x23E PUSH2 0x47C CALLDATASIZE PUSH1 0x4 PUSH2 0x481E JUMP JUMPDEST PUSH2 0x131F JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x48D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x387 PUSH2 0x133F JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x4A2 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x23E PUSH2 0x4B1 CALLDATASIZE PUSH1 0x4 PUSH2 0x48A5 JUMP JUMPDEST PUSH2 0x135B JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x4C2 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x4D1 CALLDATASIZE PUSH1 0x4 PUSH2 0x47B7 JUMP JUMPDEST PUSH2 0x18DE JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x4E2 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x4F1 CALLDATASIZE PUSH1 0x4 PUSH2 0x4A31 JUMP JUMPDEST PUSH2 0x19F1 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x502 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x511 CALLDATASIZE PUSH1 0x4 PUSH2 0x4A7A JUMP JUMPDEST PUSH2 0x1A6C JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x522 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x531 CALLDATASIZE PUSH1 0x4 PUSH2 0x4666 JUMP JUMPDEST PUSH2 0x1D05 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x542 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x556 PUSH2 0x551 CALLDATASIZE PUSH1 0x4 PUSH2 0x493C JUMP JUMPDEST PUSH2 0x1F30 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x571C JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x56F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x57E CALLDATASIZE PUSH1 0x4 PUSH2 0x493C JUMP JUMPDEST PUSH2 0x202A JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x58F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1DB PUSH2 0x59E CALLDATASIZE PUSH1 0x4 PUSH2 0x4684 JUMP JUMPDEST PUSH2 0x20C6 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x5AF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x5B8 PUSH2 0x20E3 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x54D0 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x5D1 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x5E0 CALLDATASIZE PUSH1 0x4 PUSH2 0x47B7 JUMP JUMPDEST PUSH2 0x2143 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x5F1 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1DB PUSH2 0x2218 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x606 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x615 CALLDATASIZE PUSH1 0x4 PUSH2 0x47B7 JUMP JUMPDEST PUSH2 0x221E JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x626 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x387 PUSH2 0x2390 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x63B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x64A CALLDATASIZE PUSH1 0x4 PUSH2 0x4666 JUMP JUMPDEST PUSH2 0x23AC JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x65B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x5B8 PUSH2 0x245D JUMP JUMPDEST PUSH1 0x3 PUSH1 0x20 MSTORE PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP2 JUMP JUMPDEST PUSH2 0x67E PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH2 0x689 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x6CF JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR DUP2 SSTORE DUP8 MLOAD SWAP4 POP SWAP2 POP JUMPDEST DUP2 DUP4 EQ PUSH2 0x76F JUMPI PUSH2 0x758 DUP8 DUP4 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x719 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP8 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x731 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP8 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x749 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x2494 JUMP JUMPDEST SWAP1 POP PUSH2 0x764 DUP5 DUP3 PUSH2 0x2532 JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP2 ADD SWAP1 PUSH2 0x701 JUMP JUMPDEST POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x4 PUSH1 0x20 MSTORE PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND CALLER EQ PUSH2 0x846 JUMPI PUSH2 0x80E DUP5 DUP5 DUP5 DUP5 DUP1 DUP1 PUSH1 0x1F ADD PUSH1 0x20 DUP1 SWAP2 DIV MUL PUSH1 0x20 ADD PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 SWAP4 SWAP3 SWAP2 SWAP1 DUP2 DUP2 MSTORE PUSH1 0x20 ADD DUP4 DUP4 DUP1 DUP3 DUP5 CALLDATACOPY POP PUSH2 0x135B SWAP5 POP POP POP POP POP JUMP JUMPDEST ISZERO ISZERO PUSH2 0x846 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x55AF JUMP JUMPDEST POP POP PUSH1 0x0 SWAP2 DUP3 MSTORE PUSH1 0x6 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP4 AND DUP5 MSTORE SWAP2 SWAP1 MSTORE SWAP1 KECCAK256 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE JUMP JUMPDEST PUSH2 0x8AB PUSH2 0x42C1 JUMP JUMPDEST PUSH2 0x8B3 PUSH2 0x42F0 JUMP JUMPDEST PUSH2 0x8BB PUSH2 0x42F0 JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x8F9 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE PUSH2 0x160 DUP1 DUP10 ADD MLOAD PUSH2 0x140 DUP1 DUP11 ADD SWAP2 SWAP1 SWAP2 MSTORE DUP10 ADD MLOAD SWAP1 DUP9 ADD MSTORE PUSH2 0x945 DUP9 PUSH2 0x1F30 JUMP JUMPDEST SWAP3 POP PUSH2 0x950 DUP8 PUSH2 0x1F30 JUMP JUMPDEST SWAP2 POP PUSH2 0x95A PUSH2 0x2594 JUMP JUMPDEST SWAP1 POP PUSH2 0x968 DUP9 DUP5 DUP4 DUP10 PUSH2 0x25C6 JUMP JUMPDEST PUSH2 0x974 DUP8 DUP4 DUP4 DUP9 PUSH2 0x25C6 JUMP JUMPDEST PUSH2 0x97E DUP9 DUP9 PUSH2 0x275E JUMP JUMPDEST PUSH2 0x992 DUP9 DUP9 DUP6 PUSH1 0x40 ADD MLOAD DUP6 PUSH1 0x40 ADD MLOAD PUSH2 0x27BE JUMP JUMPDEST DUP1 MLOAD PUSH1 0x20 DUP2 ADD MLOAD SWAP1 MLOAD SWAP2 SWAP6 POP PUSH2 0x9AD SWAP2 DUP11 SWAP2 DUP7 SWAP2 DUP2 SWAP1 PUSH2 0x2945 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP6 ADD MLOAD SWAP1 DUP2 ADD MLOAD SWAP1 MLOAD PUSH2 0x9C9 SWAP2 DUP10 SWAP2 DUP6 SWAP2 SWAP1 DUP2 SWAP1 PUSH2 0x2945 JUMP JUMPDEST PUSH2 0x9E2 DUP9 DUP3 DUP6 PUSH1 0x20 ADD MLOAD DUP7 PUSH1 0x40 ADD MLOAD DUP9 PUSH1 0x0 ADD MLOAD PUSH2 0x2A5E JUMP JUMPDEST PUSH2 0x9FB DUP8 DUP3 DUP5 PUSH1 0x20 ADD MLOAD DUP6 PUSH1 0x40 ADD MLOAD DUP9 PUSH1 0x20 ADD MLOAD PUSH2 0x2A5E JUMP JUMPDEST PUSH2 0xA07 DUP9 DUP9 DUP4 DUP8 PUSH2 0x2B0A JUMP JUMPDEST POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE POP SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH2 0xA42 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x60 PUSH2 0xA4F DUP6 DUP6 DUP6 PUSH2 0x2C92 JUMP JUMPDEST SWAP1 POP PUSH1 0x80 DUP2 DUP3 MLOAD PUSH1 0x20 DUP5 ADD ADDRESS GAS DELEGATECALL DUP1 ISZERO PUSH2 0xA86 JUMPI DUP2 MLOAD DUP4 MSTORE PUSH1 0x20 DUP3 ADD MLOAD PUSH1 0x20 DUP5 ADD MSTORE PUSH1 0x40 DUP3 ADD MLOAD PUSH1 0x40 DUP5 ADD MSTORE PUSH1 0x60 DUP3 ADD MLOAD PUSH1 0x60 DUP5 ADD MSTORE JUMPDEST POP JUMPDEST POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0xA PUSH1 0x20 MSTORE PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD DUP2 SWAP1 PUSH1 0xFF AND ISZERO PUSH2 0xAF8 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR DUP2 SSTORE DUP2 MLOAD SWAP1 JUMPDEST DUP1 DUP3 EQ PUSH2 0xB58 JUMPI PUSH2 0xB50 DUP4 DUP3 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xB41 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x2E64 JUMP JUMPDEST PUSH1 0x1 ADD PUSH2 0xB29 JUMP JUMPDEST POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE POP JUMP JUMPDEST PUSH2 0xB8D PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH2 0xB98 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0xBD5 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR DUP2 SSTORE DUP8 MLOAD SWAP4 POP SWAP2 POP JUMPDEST DUP2 DUP4 EQ PUSH2 0x76F JUMPI PUSH2 0xC5E DUP8 DUP4 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xC1F JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP8 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xC37 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP8 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xC4F JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x2E8F JUMP JUMPDEST SWAP1 POP PUSH2 0xC6A DUP5 DUP3 PUSH2 0x2532 JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP2 ADD SWAP1 PUSH2 0xC07 JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD DUP2 SWAP1 DUP2 SWAP1 DUP2 SWAP1 PUSH1 0xFF AND ISZERO PUSH2 0xCB9 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE PUSH2 0xCEC PUSH2 0x2594 JUMP JUMPDEST SWAP4 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 AND CALLER EQ PUSH2 0xD11 JUMPI CALLER PUSH2 0xD14 JUMP JUMPDEST PUSH1 0x0 JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP7 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x5 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP4 DUP6 AND DUP4 MSTORE SWAP3 SWAP1 MSTORE KECCAK256 SLOAD SWAP1 SWAP4 POP PUSH1 0x1 DUP7 ADD SWAP3 POP SWAP1 POP DUP1 DUP3 GT PUSH2 0xD8B JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x563F JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP6 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x5 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP5 DUP9 AND DUP1 DUP5 MSTORE SWAP5 SWAP1 SWAP2 MSTORE SWAP1 DUP2 SWAP1 KECCAK256 DUP6 SWAP1 SSTORE MLOAD PUSH32 0x82AF639571738F4EBD4268FB0363D8957EBE1BBB9E78DBA5EBD69EED39B154F0 SWAP1 PUSH2 0xDF3 SWAP1 DUP7 SWAP1 PUSH2 0x5425 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE POP POP POP JUMP JUMPDEST PUSH2 0xE32 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH2 0xE3D PUSH2 0x4298 JUMP JUMPDEST DUP7 MLOAD SWAP3 POP PUSH1 0x0 SWAP2 POP JUMPDEST DUP2 DUP4 EQ PUSH2 0xEB4 JUMPI PUSH2 0xE9D DUP8 DUP4 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xE5E JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP8 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xE76 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP8 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xE8E JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0xA3A JUMP JUMPDEST SWAP1 POP PUSH2 0xEA9 DUP5 DUP3 PUSH2 0x2532 JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP2 ADD SWAP1 PUSH2 0xE46 JUMP JUMPDEST POP POP POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 DUP2 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0xA PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND JUMPDEST SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x8 PUSH1 0x20 MSTORE PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND DUP2 JUMP JUMPDEST PUSH2 0xF29 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0xF66 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE PUSH2 0xF9C DUP5 DUP5 DUP5 PUSH2 0x2E8F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x100A JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE PUSH2 0x103D PUSH2 0x2594 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 DUP2 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x7 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP5 DUP10 AND DUP1 DUP5 MSTORE SWAP5 SWAP1 SWAP2 MSTORE SWAP1 DUP2 SWAP1 KECCAK256 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND DUP8 ISZERO ISZERO OR SWAP1 SSTORE MLOAD SWAP3 SWAP4 POP SWAP1 SWAP2 PUSH32 0xA8656E308026EEABCE8F0BC18048433252318AB80AC79DA0B3D3D8697DFBA891 SWAP1 PUSH2 0x10D1 SWAP1 DUP7 SWAP1 PUSH2 0x5417 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE POP JUMP JUMPDEST PUSH1 0x7 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x0 SWAP3 DUP4 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 SWAP1 SWAP2 MSTORE SWAP1 DUP3 MSTORE SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND DUP2 JUMP JUMPDEST PUSH2 0x112E PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x60 PUSH1 0x0 DUP1 PUSH1 0x0 PUSH2 0x113D PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x117A JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR DUP2 SSTORE DUP10 MLOAD DUP11 SWAP2 SWAP1 DUP2 LT PUSH2 0x11B2 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x160 ADD MLOAD SWAP5 POP DUP9 MLOAD SWAP4 POP PUSH1 0x0 SWAP3 POP JUMPDEST DUP3 DUP5 EQ PUSH2 0x1255 JUMPI DUP5 DUP10 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x11E2 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x160 ADD DUP2 SWAP1 MSTORE POP PUSH2 0x1202 DUP9 DUP8 PUSH1 0x20 ADD MLOAD PUSH2 0x2EE2 JUMP JUMPDEST SWAP2 POP PUSH2 0x122E DUP10 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x1215 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP4 DUP10 DUP7 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x749 JUMPI INVALID JUMPDEST SWAP1 POP PUSH2 0x123A DUP7 DUP3 PUSH2 0x2532 JUMP JUMPDEST PUSH1 0x20 DUP7 ADD MLOAD DUP9 GT PUSH2 0x124A JUMPI PUSH2 0x1255 JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP3 ADD SWAP2 PUSH2 0x11CC JUMP JUMPDEST POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE POP SWAP2 SWAP6 SWAP5 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x60 PUSH1 0x0 PUSH1 0x60 PUSH1 0x0 DUP5 MLOAD SWAP3 POP DUP3 PUSH1 0x40 MLOAD SWAP1 DUP1 DUP3 MSTORE DUP1 PUSH1 0x20 MUL PUSH1 0x20 ADD DUP3 ADD PUSH1 0x40 MSTORE DUP1 ISZERO PUSH2 0x12D0 JUMPI DUP2 PUSH1 0x20 ADD JUMPDEST PUSH2 0x12BD PUSH2 0x42F0 JUMP JUMPDEST DUP2 MSTORE PUSH1 0x20 ADD SWAP1 PUSH1 0x1 SWAP1 SUB SWAP1 DUP2 PUSH2 0x12B5 JUMPI SWAP1 POP JUMPDEST POP SWAP2 POP PUSH1 0x0 SWAP1 POP JUMPDEST DUP1 DUP4 EQ PUSH2 0xA88 JUMPI PUSH2 0x12FF DUP6 DUP3 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x12F0 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x1F30 JUMP JUMPDEST DUP3 DUP3 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x130D JUMPI INVALID JUMPDEST PUSH1 0x20 SWAP1 DUP2 MUL SWAP1 SWAP2 ADD ADD MSTORE PUSH1 0x1 ADD PUSH2 0x12D8 JUMP JUMPDEST PUSH1 0x6 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x0 SWAP3 DUP4 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 SWAP1 SWAP2 MSTORE SWAP1 DUP3 MSTORE SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND DUP2 JUMP JUMPDEST PUSH1 0x2 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x0 DUP10 MLOAD GT ISZERO ISZERO PUSH2 0x13A4 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x562F JUMP JUMPDEST PUSH2 0x13AD DUP10 PUSH2 0x2F29 JUMP JUMPDEST PUSH32 0x100000000000000000000000000000000000000000000000000000000000000 SWAP1 DIV SWAP7 POP PUSH1 0x7 PUSH1 0xFF DUP9 AND LT PUSH2 0x140F JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x554F JUMP JUMPDEST DUP7 PUSH1 0xFF AND PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x141E JUMPI INVALID JUMPDEST SWAP6 POP PUSH1 0x0 DUP7 PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x142E JUMPI INVALID JUMPDEST EQ ISZERO PUSH2 0x1466 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x561F JUMP JUMPDEST PUSH1 0x1 DUP7 PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x1474 JUMPI INVALID JUMPDEST EQ ISZERO PUSH2 0x14BC JUMPI DUP9 MLOAD ISZERO PUSH2 0x14B3 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x56EF JUMP JUMPDEST PUSH1 0x0 SWAP8 POP PUSH2 0x18D0 JUMP JUMPDEST PUSH1 0x2 DUP7 PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x14CA JUMPI INVALID JUMPDEST EQ ISZERO PUSH2 0x1605 JUMPI DUP9 MLOAD PUSH1 0x41 EQ PUSH2 0x150B JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x54EF JUMP JUMPDEST DUP9 PUSH1 0x0 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x151A JUMPI INVALID JUMPDEST ADD PUSH1 0x20 ADD MLOAD PUSH32 0x100000000000000000000000000000000000000000000000000000000000000 SWAP1 DUP2 SWAP1 DIV DUP2 MUL DIV SWAP5 POP PUSH2 0x155A DUP10 PUSH1 0x1 PUSH4 0xFFFFFFFF PUSH2 0x2FED AND JUMP JUMPDEST SWAP4 POP PUSH2 0x156D DUP10 PUSH1 0x21 PUSH4 0xFFFFFFFF PUSH2 0x2FED AND JUMP JUMPDEST SWAP3 POP PUSH1 0x1 DUP12 DUP7 DUP7 DUP7 PUSH1 0x40 MLOAD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x40 MSTORE PUSH1 0x40 MLOAD PUSH2 0x1592 SWAP5 SWAP4 SWAP3 SWAP2 SWAP1 PUSH2 0x5480 JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD PUSH1 0x20 DUP2 SUB SWAP1 DUP1 DUP5 SUB SWAP1 PUSH1 0x0 DUP7 GAS CALL ISZERO DUP1 ISZERO PUSH2 0x15B6 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP PUSH1 0x40 MLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP13 DUP2 AND SWAP1 DUP3 AND EQ SWAP10 POP SWAP3 POP PUSH2 0x18D0 SWAP1 POP JUMP JUMPDEST PUSH1 0x3 DUP7 PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x1613 JUMPI INVALID JUMPDEST EQ ISZERO PUSH2 0x17B9 JUMPI DUP9 MLOAD PUSH1 0x41 EQ PUSH2 0x1654 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x54EF JUMP JUMPDEST DUP9 PUSH1 0x0 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x1663 JUMPI INVALID JUMPDEST ADD PUSH1 0x20 ADD MLOAD PUSH32 0x100000000000000000000000000000000000000000000000000000000000000 SWAP1 DUP2 SWAP1 DIV DUP2 MUL DIV SWAP5 POP PUSH2 0x16A3 DUP10 PUSH1 0x1 PUSH4 0xFFFFFFFF PUSH2 0x2FED AND JUMP JUMPDEST SWAP4 POP PUSH2 0x16B6 DUP10 PUSH1 0x21 PUSH4 0xFFFFFFFF PUSH2 0x2FED AND JUMP JUMPDEST SWAP3 POP PUSH1 0x1 DUP12 PUSH1 0x40 MLOAD PUSH1 0x20 ADD DUP1 DUP1 PUSH32 0x19457468657265756D205369676E6564204D6573736167653A0A333200000000 DUP2 MSTORE POP PUSH1 0x1C ADD DUP3 PUSH1 0x0 NOT AND PUSH1 0x0 NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP POP PUSH1 0x40 MLOAD PUSH1 0x20 DUP2 DUP4 SUB SUB DUP2 MSTORE SWAP1 PUSH1 0x40 MSTORE PUSH1 0x40 MLOAD DUP1 DUP3 DUP1 MLOAD SWAP1 PUSH1 0x20 ADD SWAP1 DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH2 0x1757 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0x171A JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 DUP5 SUB PUSH2 0x100 EXP PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ADD DUP1 NOT SWAP1 SWAP3 AND SWAP2 AND OR SWAP1 MSTORE PUSH1 0x40 DUP1 MLOAD SWAP3 SWAP1 SWAP5 ADD DUP3 SWAP1 SUB DUP3 KECCAK256 PUSH1 0x0 DUP4 MSTORE SWAP2 ADD SWAP3 DUP4 SWAP1 MSTORE PUSH2 0x1592 SWAP5 POP SWAP3 POP DUP10 SWAP2 DUP10 SWAP2 POP DUP9 SWAP1 PUSH2 0x5480 JUMP JUMPDEST PUSH1 0x4 DUP7 PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x17C7 JUMPI INVALID JUMPDEST EQ ISZERO PUSH2 0x17DF JUMPI PUSH2 0x17D8 DUP12 DUP12 DUP12 PUSH2 0x3038 JUMP JUMPDEST SWAP8 POP PUSH2 0x18D0 JUMP JUMPDEST PUSH1 0x5 DUP7 PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x17ED JUMPI INVALID JUMPDEST EQ ISZERO PUSH2 0x1850 JUMPI PUSH2 0x17FC DUP10 PUSH2 0x318D JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP13 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x7 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP4 DUP6 AND DUP4 MSTORE SWAP3 SWAP1 MSTORE KECCAK256 SLOAD SWAP1 SWAP2 POP PUSH1 0xFF AND ISZERO ISZERO PUSH2 0x1844 JUMPI PUSH1 0x0 SWAP8 POP PUSH2 0x18D0 JUMP JUMPDEST PUSH2 0x17D8 DUP2 DUP13 DUP13 DUP13 PUSH2 0x3206 JUMP JUMPDEST PUSH1 0x6 DUP7 PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x185E JUMPI INVALID JUMPDEST EQ ISZERO PUSH2 0x189E JUMPI PUSH1 0x0 DUP12 DUP2 MSTORE PUSH1 0x6 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP15 AND DUP5 MSTORE SWAP1 SWAP2 MSTORE SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND SWAP8 POP PUSH2 0x18D0 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x554F JUMP JUMPDEST POP POP POP POP POP POP POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH2 0x18E6 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x60 PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH2 0x18F6 PUSH2 0x4298 JUMP JUMPDEST DUP10 PUSH1 0x0 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x1905 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x140 ADD MLOAD SWAP6 POP DUP10 MLOAD SWAP5 POP PUSH1 0x0 SWAP4 POP JUMPDEST DUP4 DUP6 EQ PUSH2 0x19E4 JUMPI DUP6 DUP11 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x1935 JUMPI INVALID JUMPDEST PUSH1 0x20 SWAP1 DUP2 MUL SWAP1 SWAP2 ADD ADD MLOAD PUSH2 0x140 ADD MSTORE DUP7 MLOAD PUSH2 0x1951 SWAP1 DUP11 SWAP1 PUSH2 0x2EE2 JUMP JUMPDEST SWAP3 POP PUSH2 0x1994 DUP11 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x1964 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH1 0xA0 ADD MLOAD DUP12 DUP7 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x1980 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH1 0x80 ADD MLOAD DUP6 PUSH2 0x3362 JUMP JUMPDEST SWAP2 POP PUSH2 0x19C0 DUP11 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x19A7 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP4 DUP11 DUP8 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xE8E JUMPI INVALID JUMPDEST SWAP1 POP PUSH2 0x19CC DUP8 DUP3 PUSH2 0x2532 JUMP JUMPDEST DUP7 MLOAD DUP10 GT PUSH2 0x19D9 JUMPI PUSH2 0x19E4 JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP4 ADD SWAP3 PUSH2 0x191F JUMP JUMPDEST POP POP POP POP POP POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH2 0x19F9 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x1A36 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE PUSH2 0xF9C DUP5 DUP5 DUP5 PUSH2 0x2494 JUMP JUMPDEST PUSH1 0x9 SLOAD PUSH1 0x0 SWAP1 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND ISZERO PUSH2 0x1ABF JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH2 0x1B02 PUSH2 0x1AFD DUP9 DUP9 DUP9 DUP9 DUP1 DUP1 PUSH1 0x1F ADD PUSH1 0x20 DUP1 SWAP2 DIV MUL PUSH1 0x20 ADD PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 SWAP4 SWAP3 SWAP2 SWAP1 DUP2 DUP2 MSTORE PUSH1 0x20 ADD DUP4 DUP4 DUP1 DUP3 DUP5 CALLDATACOPY POP PUSH2 0x33B8 SWAP5 POP POP POP POP POP JUMP JUMPDEST PUSH2 0x35F9 JUMP JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x8 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD SWAP1 SWAP2 POP PUSH1 0xFF AND ISZERO PUSH2 0x1B4E JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x559F JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP7 AND CALLER EQ PUSH2 0x1C1F JUMPI PUSH2 0x1BA6 DUP2 DUP8 DUP6 DUP6 DUP1 DUP1 PUSH1 0x1F ADD PUSH1 0x20 DUP1 SWAP2 DIV MUL PUSH1 0x20 ADD PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 SWAP4 SWAP3 SWAP2 SWAP1 DUP2 DUP2 MSTORE PUSH1 0x20 ADD DUP4 DUP4 DUP1 DUP3 DUP5 CALLDATACOPY POP PUSH2 0x135B SWAP5 POP POP POP POP POP JUMP JUMPDEST ISZERO ISZERO PUSH2 0x1BDE JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x56DF JUMP JUMPDEST PUSH1 0x9 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP9 AND OR SWAP1 SSTORE JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x8 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 DUP2 SWAP1 KECCAK256 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE MLOAD ADDRESS SWAP1 DUP7 SWAP1 DUP7 SWAP1 DUP1 DUP4 DUP4 DUP1 DUP3 DUP5 CALLDATACOPY DUP3 ADD SWAP2 POP POP SWAP3 POP POP POP PUSH1 0x0 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 DUP6 GAS DELEGATECALL SWAP2 POP POP ISZERO ISZERO PUSH2 0x1CB6 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x55CF JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP7 AND CALLER EQ PUSH2 0x1CFC JUMPI PUSH1 0x9 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 AND SWAP1 SSTORE JUMPDEST POP POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x2 SLOAD PUSH1 0x0 SWAP1 DUP2 SWAP1 DUP2 SWAP1 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER EQ PUSH2 0x1D5D JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x568F JUMP JUMPDEST DUP4 SWAP3 POP DUP3 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH4 0xAE25532E PUSH1 0x40 MLOAD DUP2 PUSH4 0xFFFFFFFF AND PUSH29 0x100000000000000000000000000000000000000000000000000000000 MUL DUP2 MSTORE PUSH1 0x4 ADD PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x1DC4 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x1DD8 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x1DFC SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x491E JUMP JUMPDEST PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 DUP2 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0xA PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD SWAP1 SWAP3 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 POP DUP1 ISZERO PUSH2 0x1E81 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x552F JUMP JUMPDEST PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 DUP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0xA PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 DUP2 SWAP1 KECCAK256 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP7 AND OR SWAP1 SSTORE MLOAD PUSH32 0xD2C6B762299C609BDB96520B58A49BFB80186934D4F71A86A367571A15C03194 SWAP1 PUSH2 0x1F22 SWAP1 DUP5 SWAP1 DUP8 SWAP1 PUSH2 0x54B5 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG1 POP POP POP POP JUMP JUMPDEST PUSH2 0x1F38 PUSH2 0x42F0 JUMP JUMPDEST PUSH2 0x1F41 DUP3 PUSH2 0x3639 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP4 ADD DUP3 SWAP1 MSTORE PUSH1 0x0 SWAP2 DUP3 MSTORE PUSH1 0x3 SWAP1 MSTORE PUSH1 0x40 SWAP1 DUP2 SWAP1 KECCAK256 SLOAD SWAP1 DUP3 ADD MSTORE PUSH1 0x80 DUP3 ADD MLOAD ISZERO ISZERO PUSH2 0x1F75 JUMPI PUSH1 0x1 JUMPDEST PUSH1 0xFF AND DUP2 MSTORE PUSH2 0xF07 JUMP JUMPDEST PUSH1 0xA0 DUP3 ADD MLOAD ISZERO ISZERO PUSH2 0x1F87 JUMPI PUSH1 0x2 PUSH2 0x1F6B JUMP JUMPDEST PUSH1 0xA0 DUP3 ADD MLOAD PUSH1 0x40 DUP3 ADD MLOAD LT PUSH2 0x1F9D JUMPI PUSH1 0x5 PUSH2 0x1F6B JUMP JUMPDEST PUSH2 0x100 DUP3 ADD MLOAD TIMESTAMP LT PUSH2 0x1FB0 JUMPI PUSH1 0x4 PUSH2 0x1F6B JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 ADD MLOAD PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x4 SWAP1 SWAP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x1FD4 JUMPI PUSH1 0x6 PUSH2 0x1F6B JUMP JUMPDEST PUSH2 0x120 DUP3 ADD MLOAD DUP3 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 DUP2 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x5 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 PUSH1 0x60 DUP9 ADD MLOAD SWAP1 SWAP5 AND DUP4 MSTORE SWAP3 SWAP1 MSTORE KECCAK256 SLOAD GT ISZERO PUSH2 0x2021 JUMPI PUSH1 0x6 PUSH2 0x1F6B JUMP JUMPDEST PUSH1 0x3 DUP2 MSTORE SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x2067 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE PUSH2 0x209B DUP2 PUSH2 0x2E64 JUMP JUMPDEST POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE JUMP JUMPDEST PUSH1 0x5 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x0 SWAP3 DUP4 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 SWAP1 SWAP2 MSTORE SWAP1 DUP3 MSTORE SWAP1 KECCAK256 SLOAD DUP2 JUMP JUMPDEST PUSH1 0x60 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x24 DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0xF47261B0000000000000000000000000E41D2489571D322189246DAFA5EBDE1F DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0x4699F49800000000000000000000000000000000000000000000000000000000 DUP2 MSTORE POP DUP2 JUMP JUMPDEST PUSH2 0x214B PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x60 PUSH1 0x0 DUP1 PUSH1 0x0 PUSH2 0x215A PUSH2 0x4298 JUMP JUMPDEST DUP9 PUSH1 0x0 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x2169 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x160 ADD MLOAD SWAP5 POP DUP9 MLOAD SWAP4 POP PUSH1 0x0 SWAP3 POP JUMPDEST DUP3 DUP5 EQ PUSH2 0x220C JUMPI DUP5 DUP10 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x2199 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x160 ADD DUP2 SWAP1 MSTORE POP PUSH2 0x21B9 DUP9 DUP8 PUSH1 0x20 ADD MLOAD PUSH2 0x2EE2 JUMP JUMPDEST SWAP2 POP PUSH2 0x21E5 DUP10 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x21CC JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP4 DUP10 DUP7 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xE8E JUMPI INVALID JUMPDEST SWAP1 POP PUSH2 0x21F1 DUP7 DUP3 PUSH2 0x2532 JUMP JUMPDEST PUSH1 0x20 DUP7 ADD MLOAD DUP9 GT PUSH2 0x2201 JUMPI PUSH2 0x220C JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP3 ADD SWAP2 PUSH2 0x2183 JUMP JUMPDEST POP POP POP POP POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x1 SLOAD DUP2 JUMP JUMPDEST PUSH2 0x2226 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x60 PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH2 0x2236 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x2273 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR DUP2 SSTORE DUP11 MLOAD DUP12 SWAP2 SWAP1 DUP2 LT PUSH2 0x22AB JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x140 ADD MLOAD SWAP6 POP DUP10 MLOAD SWAP5 POP PUSH1 0x0 SWAP4 POP JUMPDEST DUP4 DUP6 EQ PUSH2 0x235A JUMPI DUP6 DUP11 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x22DB JUMPI INVALID JUMPDEST PUSH1 0x20 SWAP1 DUP2 MUL SWAP1 SWAP2 ADD ADD MLOAD PUSH2 0x140 ADD MSTORE DUP7 MLOAD PUSH2 0x22F7 SWAP1 DUP11 SWAP1 PUSH2 0x2EE2 JUMP JUMPDEST SWAP3 POP PUSH2 0x230A DUP11 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x1964 JUMPI INVALID JUMPDEST SWAP2 POP PUSH2 0x2336 DUP11 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x231D JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP4 DUP11 DUP8 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x749 JUMPI INVALID JUMPDEST SWAP1 POP PUSH2 0x2342 DUP8 DUP3 PUSH2 0x2532 JUMP JUMPDEST DUP7 MLOAD DUP10 GT PUSH2 0x234F JUMPI PUSH2 0x235A JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP4 ADD SWAP3 PUSH2 0x22C5 JUMP JUMPDEST POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE POP SWAP3 SWAP7 SWAP6 POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x9 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x2 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER EQ PUSH2 0x23FD JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x568F JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 AND ISZERO PUSH2 0x245A JUMPI PUSH1 0x2 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND OR SWAP1 SSTORE JUMPDEST POP JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP1 DUP3 ADD SWAP1 SWAP2 MSTORE PUSH1 0x5 DUP2 MSTORE PUSH32 0x322E302E30000000000000000000000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE DUP2 JUMP JUMPDEST PUSH2 0x249C PUSH2 0x4298 JUMP JUMPDEST PUSH2 0x24A4 PUSH2 0x42F0 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 PUSH2 0x24B2 DUP9 PUSH2 0x1F30 JUMP JUMPDEST SWAP4 POP PUSH2 0x24BC PUSH2 0x2594 JUMP JUMPDEST SWAP3 POP PUSH2 0x24CA DUP9 DUP6 DUP6 DUP10 PUSH2 0x25C6 JUMP JUMPDEST PUSH2 0x24DC DUP9 PUSH1 0xA0 ADD MLOAD DUP6 PUSH1 0x40 ADD MLOAD PUSH2 0x2EE2 JUMP JUMPDEST SWAP2 POP PUSH2 0x24E8 DUP8 DUP4 PUSH2 0x3647 JUMP JUMPDEST SWAP1 POP PUSH2 0x24FB DUP9 DUP6 DUP10 DUP5 DUP10 PUSH1 0x0 ADD MLOAD PUSH2 0x2945 JUMP JUMPDEST PUSH2 0x2505 DUP9 DUP3 PUSH2 0x365D JUMP JUMPDEST SWAP5 POP PUSH2 0x251C DUP9 DUP5 DUP7 PUSH1 0x20 ADD MLOAD DUP8 PUSH1 0x40 ADD MLOAD DUP10 PUSH2 0x2A5E JUMP JUMPDEST PUSH2 0x2527 DUP9 DUP5 DUP8 PUSH2 0x36BE JUMP JUMPDEST POP POP POP POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST DUP2 MLOAD DUP2 MLOAD PUSH2 0x2540 SWAP2 SWAP1 PUSH2 0x3776 JUMP JUMPDEST DUP3 MSTORE PUSH1 0x20 DUP1 DUP4 ADD MLOAD SWAP1 DUP3 ADD MLOAD PUSH2 0x2556 SWAP2 SWAP1 PUSH2 0x3776 JUMP JUMPDEST PUSH1 0x20 DUP4 ADD MSTORE PUSH1 0x40 DUP1 DUP4 ADD MLOAD SWAP1 DUP3 ADD MLOAD PUSH2 0x256F SWAP2 SWAP1 PUSH2 0x3776 JUMP JUMPDEST PUSH1 0x40 DUP4 ADD MSTORE PUSH1 0x60 DUP1 DUP4 ADD MLOAD SWAP1 DUP3 ADD MLOAD PUSH2 0x2588 SWAP2 SWAP1 PUSH2 0x3776 JUMP JUMPDEST PUSH1 0x60 SWAP1 SWAP3 ADD SWAP2 SWAP1 SWAP2 MSTORE POP JUMP JUMPDEST PUSH1 0x9 SLOAD PUSH1 0x0 SWAP1 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 DUP2 ISZERO PUSH2 0x25BD JUMPI DUP2 PUSH2 0x25BF JUMP JUMPDEST CALLER JUMPDEST SWAP4 SWAP3 POP POP POP JUMP JUMPDEST DUP3 MLOAD PUSH1 0xFF AND PUSH1 0x3 EQ PUSH2 0x2604 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x56AF JUMP JUMPDEST PUSH1 0x60 DUP5 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND ISZERO PUSH2 0x2677 JUMPI PUSH1 0x60 DUP5 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER EQ PUSH2 0x2677 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x56BF JUMP JUMPDEST PUSH1 0x20 DUP5 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND ISZERO PUSH2 0x2702 JUMPI DUP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP5 PUSH1 0x20 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ ISZERO ISZERO PUSH2 0x2702 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x54FF JUMP JUMPDEST PUSH1 0x40 DUP4 ADD MLOAD ISZERO ISZERO PUSH2 0x2758 JUMPI PUSH2 0x2720 DUP4 PUSH1 0x20 ADD MLOAD DUP6 PUSH1 0x0 ADD MLOAD DUP4 PUSH2 0x135B JUMP JUMPDEST ISZERO ISZERO PUSH2 0x2758 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x556F JUMP JUMPDEST POP POP POP POP JUMP JUMPDEST PUSH2 0x2770 DUP3 PUSH1 0xA0 ADD MLOAD DUP3 PUSH1 0xA0 ADD MLOAD PUSH2 0x37C0 JUMP JUMPDEST PUSH2 0x2782 DUP4 PUSH1 0x80 ADD MLOAD DUP4 PUSH1 0x80 ADD MLOAD PUSH2 0x37C0 JUMP JUMPDEST LT ISZERO PUSH2 0x27BA JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x56CF JUMP JUMPDEST POP POP JUMP JUMPDEST PUSH2 0x27C6 PUSH2 0x42C1 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH2 0x27DA DUP10 PUSH1 0xA0 ADD MLOAD DUP9 PUSH2 0x2EE2 JUMP JUMPDEST SWAP4 POP PUSH2 0x27EF DUP10 PUSH1 0x80 ADD MLOAD DUP11 PUSH1 0xA0 ADD MLOAD DUP7 PUSH2 0x381B JUMP JUMPDEST SWAP3 POP PUSH2 0x27FF DUP9 PUSH1 0xA0 ADD MLOAD DUP8 PUSH2 0x2EE2 JUMP JUMPDEST SWAP2 POP PUSH2 0x2814 DUP9 PUSH1 0x80 ADD MLOAD DUP10 PUSH1 0xA0 ADD MLOAD DUP5 PUSH2 0x381B JUMP JUMPDEST SWAP1 POP DUP1 DUP5 LT PUSH2 0x2857 JUMPI PUSH1 0x20 DUP1 DUP7 ADD DUP1 MLOAD DUP4 SWAP1 MSTORE DUP1 MLOAD DUP3 ADD DUP5 SWAP1 MSTORE MLOAD MLOAD DUP7 MLOAD DUP3 ADD MSTORE PUSH1 0x80 DUP11 ADD MLOAD PUSH1 0xA0 DUP12 ADD MLOAD DUP8 MLOAD SWAP1 SWAP3 ADD MLOAD PUSH2 0x284F SWAP3 SWAP1 PUSH2 0x381B JUMP JUMPDEST DUP6 MLOAD MSTORE PUSH2 0x2894 JUMP JUMPDEST DUP5 MLOAD DUP4 SWAP1 MSTORE DUP5 MLOAD PUSH1 0x20 SWAP1 DUP2 ADD DUP6 SWAP1 MSTORE DUP6 MLOAD DUP2 ADD MLOAD SWAP1 DUP7 ADD DUP1 MLOAD SWAP2 SWAP1 SWAP2 MSTORE PUSH1 0xA0 DUP10 ADD MLOAD PUSH1 0x80 DUP11 ADD MLOAD SWAP2 MLOAD MLOAD PUSH2 0x288B SWAP3 SWAP1 PUSH2 0x3898 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP8 ADD MLOAD ADD MSTORE JUMPDEST DUP5 MLOAD MLOAD PUSH1 0x20 DUP1 DUP8 ADD MLOAD ADD MLOAD PUSH2 0x28A9 SWAP2 SWAP1 PUSH2 0x2EE2 JUMP JUMPDEST PUSH1 0x40 DUP7 ADD MSTORE DUP5 MLOAD MLOAD PUSH1 0x80 DUP11 ADD MLOAD PUSH1 0xC0 DUP12 ADD MLOAD PUSH2 0x28C6 SWAP3 SWAP2 SWAP1 PUSH2 0x381B JUMP JUMPDEST DUP6 MLOAD PUSH1 0x40 ADD MSTORE DUP5 MLOAD PUSH1 0x20 ADD MLOAD PUSH1 0xA0 DUP11 ADD MLOAD PUSH1 0xE0 DUP12 ADD MLOAD PUSH2 0x28E7 SWAP3 SWAP2 SWAP1 PUSH2 0x381B JUMP JUMPDEST DUP6 MLOAD PUSH1 0x60 ADD MSTORE PUSH1 0x20 DUP6 ADD MLOAD MLOAD PUSH1 0x80 DUP10 ADD MLOAD PUSH1 0xC0 DUP11 ADD MLOAD PUSH2 0x2908 SWAP3 SWAP2 SWAP1 PUSH2 0x381B JUMP JUMPDEST DUP6 PUSH1 0x20 ADD MLOAD PUSH1 0x40 ADD DUP2 DUP2 MSTORE POP POP PUSH2 0x2930 DUP6 PUSH1 0x20 ADD MLOAD PUSH1 0x20 ADD MLOAD DUP10 PUSH1 0xA0 ADD MLOAD DUP11 PUSH1 0xE0 ADD MLOAD PUSH2 0x381B JUMP JUMPDEST PUSH1 0x20 DUP7 ADD MLOAD PUSH1 0x60 ADD MSTORE POP POP POP POP SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST DUP3 ISZERO ISZERO PUSH2 0x297E JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x55EF JUMP JUMPDEST DUP3 DUP3 GT ISZERO PUSH2 0x29B8 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x55DF JUMP JUMPDEST DUP5 PUSH1 0xA0 ADD MLOAD PUSH2 0x29CB DUP6 PUSH1 0x40 ADD MLOAD DUP5 PUSH2 0x3776 JUMP JUMPDEST GT ISZERO PUSH2 0x2A03 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x550F JUMP JUMPDEST PUSH2 0x2A11 DUP6 PUSH1 0x80 ADD MLOAD DUP4 PUSH2 0x37C0 JUMP JUMPDEST PUSH2 0x2A1F DUP3 DUP8 PUSH1 0xA0 ADD MLOAD PUSH2 0x37C0 JUMP JUMPDEST GT ISZERO PUSH2 0x2A57 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x566F JUMP JUMPDEST POP POP POP POP POP JUMP JUMPDEST PUSH2 0x2A6C DUP3 DUP3 PUSH1 0x20 ADD MLOAD PUSH2 0x3776 JUMP JUMPDEST PUSH1 0x0 DUP5 DUP2 MSTORE PUSH1 0x3 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP2 DUP3 SWAP1 KECCAK256 SWAP3 SWAP1 SWAP3 SSTORE DUP7 DUP2 ADD MLOAD DUP8 MLOAD DUP5 MLOAD SWAP4 DUP6 ADD MLOAD DUP6 DUP5 ADD MLOAD PUSH1 0x60 DUP8 ADD MLOAD PUSH2 0x140 DUP13 ADD MLOAD PUSH2 0x160 DUP14 ADD MLOAD SWAP7 MLOAD DUP12 SWAP9 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP8 DUP9 AND SWAP9 SWAP8 SWAP1 SWAP7 AND SWAP7 PUSH32 0xBCC4C97732E47D9946F229EDB95F5B6323F601300E4690DE719993F3C371129 SWAP7 PUSH2 0x2AFB SWAP7 DUP16 SWAP7 CALLER SWAP7 SWAP3 SWAP6 SWAP2 SWAP5 SWAP1 SWAP4 SWAP1 PUSH2 0x5345 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG4 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x60 DUP1 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x24 DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0xF47261B0000000000000000000000000E41D2489571D322189246DAFA5EBDE1F DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0x4699F49800000000000000000000000000000000000000000000000000000000 DUP2 MSTORE POP SWAP1 POP PUSH2 0x2B8B DUP6 PUSH2 0x140 ADD MLOAD DUP7 PUSH1 0x0 ADD MLOAD DUP7 PUSH1 0x0 ADD MLOAD DUP6 PUSH1 0x20 ADD MLOAD PUSH1 0x20 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x140 DUP5 ADD MLOAD DUP5 MLOAD DUP7 MLOAD DUP5 MLOAD PUSH1 0x20 ADD MLOAD PUSH2 0x2BA7 SWAP4 SWAP3 SWAP2 SWAP1 PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x2BC0 DUP6 PUSH2 0x140 ADD MLOAD DUP7 PUSH1 0x0 ADD MLOAD DUP6 DUP6 PUSH1 0x40 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x2BDC DUP2 DUP7 PUSH1 0x0 ADD MLOAD DUP8 PUSH1 0x40 ADD MLOAD DUP6 PUSH1 0x0 ADD MLOAD PUSH1 0x40 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x2BF8 DUP2 DUP6 PUSH1 0x0 ADD MLOAD DUP7 PUSH1 0x40 ADD MLOAD DUP6 PUSH1 0x20 ADD MLOAD PUSH1 0x40 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST DUP4 PUSH1 0x40 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP6 PUSH1 0x40 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ ISZERO PUSH2 0x2C62 JUMPI PUSH2 0x2C5D DUP2 DUP5 DUP8 PUSH1 0x40 ADD MLOAD PUSH2 0x2C58 DUP7 PUSH1 0x0 ADD MLOAD PUSH1 0x60 ADD MLOAD DUP8 PUSH1 0x20 ADD MLOAD PUSH1 0x60 ADD MLOAD PUSH2 0x3776 JUMP JUMPDEST PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x2A57 JUMP JUMPDEST PUSH2 0x2C7A DUP2 DUP5 DUP8 PUSH1 0x40 ADD MLOAD DUP6 PUSH1 0x0 ADD MLOAD PUSH1 0x60 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x2A57 DUP2 DUP5 DUP7 PUSH1 0x40 ADD MLOAD DUP6 PUSH1 0x20 ADD MLOAD PUSH1 0x60 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0xB4BE83D500000000000000000000000000000000000000000000000000000000 PUSH1 0x20 DUP1 DUP4 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH1 0x60 PUSH1 0x24 DUP4 ADD DUP2 DUP2 MSTORE DUP8 MLOAD PUSH1 0x84 DUP6 ADD SWAP1 DUP2 MSTORE DUP9 DUP5 ADD MLOAD PUSH1 0xA4 DUP7 ADD MSTORE SWAP5 DUP9 ADD MLOAD PUSH1 0xC4 DUP6 ADD MSTORE SWAP1 DUP8 ADD MLOAD PUSH1 0xE4 DUP5 ADD MSTORE PUSH1 0x80 DUP8 ADD MLOAD PUSH2 0x104 DUP5 ADD MSTORE PUSH1 0xA0 DUP8 ADD MLOAD PUSH2 0x124 DUP5 ADD MSTORE PUSH1 0xC0 DUP8 ADD MLOAD PUSH2 0x144 DUP5 ADD MSTORE PUSH1 0xE0 DUP8 ADD MLOAD PUSH2 0x164 DUP5 ADD MSTORE PUSH2 0x100 DUP8 ADD MLOAD PUSH2 0x184 DUP5 ADD MSTORE PUSH2 0x120 DUP8 ADD MLOAD PUSH2 0x1A4 DUP5 ADD MSTORE PUSH2 0x140 DUP8 ADD DUP1 MLOAD PUSH2 0x1C4 DUP6 ADD SWAP1 DUP2 MSTORE PUSH2 0x160 DUP10 ADD MLOAD PUSH2 0x1E4 DUP7 ADD MSTORE PUSH2 0x180 SWAP1 MSTORE MLOAD DUP1 MLOAD PUSH2 0x204 DUP6 ADD DUP2 SWAP1 MSTORE SWAP4 SWAP5 SWAP2 SWAP4 DUP5 SWAP4 PUSH1 0x44 DUP8 ADD SWAP3 DUP5 SWAP3 PUSH2 0x224 DUP10 ADD SWAP3 SWAP2 DUP3 ADD SWAP2 PUSH1 0x1F DUP3 ADD DIV PUSH1 0x0 JUMPDEST DUP2 DUP2 LT ISZERO PUSH2 0x2D99 JUMPI DUP4 MLOAD DUP6 MSTORE PUSH1 0x20 SWAP5 DUP6 ADD SWAP5 SWAP1 SWAP4 ADD SWAP3 PUSH1 0x1 ADD PUSH2 0x2D7B JUMP JUMPDEST POP POP POP POP DUP2 DUP2 SUB PUSH2 0x160 DUP1 DUP5 ADD SWAP2 SWAP1 SWAP2 MSTORE DUP11 ADD MLOAD DUP1 MLOAD DUP1 DUP4 MSTORE PUSH1 0x20 SWAP3 DUP4 ADD SWAP3 SWAP2 DUP3 ADD SWAP2 PUSH1 0x1F DUP3 ADD DIV PUSH1 0x0 JUMPDEST DUP2 DUP2 LT ISZERO PUSH2 0x2DE2 JUMPI DUP4 MLOAD DUP6 MSTORE PUSH1 0x20 SWAP5 DUP6 ADD SWAP5 SWAP1 SWAP4 ADD SWAP3 PUSH1 0x1 ADD PUSH2 0x2DC4 JUMP JUMPDEST POP POP POP DUP10 DUP5 MSTORE POP DUP5 DUP2 SUB PUSH1 0x20 SWAP4 DUP5 ADD SWAP1 DUP2 MSTORE DUP9 MLOAD DUP1 DUP4 MSTORE SWAP1 SWAP4 SWAP2 DUP3 ADD SWAP2 DUP10 DUP2 ADD SWAP2 SWAP1 PUSH1 0x1F DUP3 ADD DIV PUSH1 0x0 JUMPDEST DUP2 DUP2 LT ISZERO PUSH2 0x2E2A JUMPI DUP4 MLOAD DUP6 MSTORE PUSH1 0x20 SWAP5 DUP6 ADD SWAP5 SWAP1 SWAP4 ADD SWAP3 PUSH1 0x1 ADD PUSH2 0x2E0C JUMP JUMPDEST POP POP POP PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 DUP9 DUP4 SUB ADD DUP9 MSTORE POP PUSH1 0x40 MSTORE POP POP POP POP POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH2 0x2E6C PUSH2 0x42F0 JUMP JUMPDEST PUSH2 0x2E75 DUP3 PUSH2 0x1F30 JUMP JUMPDEST SWAP1 POP PUSH2 0x2E81 DUP3 DUP3 PUSH2 0x3AFF JUMP JUMPDEST PUSH2 0x27BA DUP3 DUP3 PUSH1 0x20 ADD MLOAD PUSH2 0x3C16 JUMP JUMPDEST PUSH2 0x2E97 PUSH2 0x4298 JUMP JUMPDEST PUSH2 0x2EA2 DUP5 DUP5 DUP5 PUSH2 0x2494 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD MLOAD SWAP1 SWAP2 POP DUP4 EQ PUSH2 0x25BF JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x565F JUMP JUMPDEST PUSH1 0x0 DUP3 DUP3 GT ISZERO PUSH2 0x2F1E JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x551F JUMP JUMPDEST POP DUP1 DUP3 SUB JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP3 MLOAD GT ISZERO ISZERO PUSH2 0x2F67 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x560F JUMP JUMPDEST DUP2 MLOAD DUP3 SWAP1 PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 ADD SWAP1 DUP2 LT PUSH2 0x2F97 JUMPI INVALID JUMPDEST ADD PUSH1 0x20 ADD MLOAD DUP3 MLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ADD SWAP1 SWAP3 MSTORE POP PUSH32 0x100000000000000000000000000000000000000000000000000000000000000 SWAP1 DUP2 SWAP1 DIV MUL SWAP1 JUMP JUMPDEST PUSH1 0x0 DUP2 PUSH1 0x20 ADD DUP4 MLOAD LT ISZERO ISZERO ISZERO PUSH2 0x302F JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x553F JUMP JUMPDEST POP ADD PUSH1 0x20 ADD MLOAD SWAP1 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH1 0x0 SWAP1 PUSH1 0x60 SWAP1 PUSH32 0x1626BA7E00000000000000000000000000000000000000000000000000000000 SWAP1 PUSH2 0x3073 SWAP1 DUP8 SWAP1 DUP7 SWAP1 PUSH1 0x24 ADD PUSH2 0x5460 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 DUP2 DUP5 SUB ADD DUP2 MSTORE SWAP2 SWAP1 MSTORE PUSH1 0x20 DUP1 DUP3 ADD DUP1 MLOAD PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 SWAP1 SWAP5 AND SWAP4 SWAP1 SWAP4 OR DUP4 MSTORE DUP2 MLOAD SWAP2 SWAP4 POP SWAP1 DUP3 SWAP1 DUP2 DUP9 GAS STATICCALL DUP1 DUP1 ISZERO PUSH2 0x3110 JUMPI PUSH1 0x1 DUP2 EQ PUSH2 0x3181 JUMPI PUSH2 0x2527 JUMP JUMPDEST PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 PUSH1 0x0 MSTORE PUSH29 0x2000000000000000000000000000000000000000000000000000000000 PUSH1 0x20 MSTORE PUSH29 0xC57414C4C45545F4552524F5200000000000000000000000000000000 PUSH1 0x40 MSTORE PUSH1 0x0 PUSH1 0x60 MSTORE PUSH1 0x64 PUSH1 0x0 REVERT JUMPDEST POP POP MLOAD SWAP6 SWAP5 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x14 DUP3 MLOAD LT ISZERO ISZERO ISZERO PUSH2 0x31CD JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x569F JUMP JUMPDEST PUSH2 0x31DB DUP3 PUSH1 0x14 DUP5 MLOAD SUB PUSH2 0x3CBD JUMP JUMPDEST DUP3 MLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC ADD SWAP1 SWAP3 MSTORE POP SWAP1 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH1 0x0 SWAP1 PUSH1 0x60 SWAP1 PUSH32 0x9363470200000000000000000000000000000000000000000000000000000000 SWAP1 PUSH2 0x3243 SWAP1 DUP8 SWAP1 DUP8 SWAP1 DUP8 SWAP1 PUSH1 0x24 ADD PUSH2 0x5433 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 DUP2 DUP5 SUB ADD DUP2 MSTORE SWAP2 SWAP1 MSTORE PUSH1 0x20 DUP1 DUP3 ADD DUP1 MLOAD PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 SWAP1 SWAP5 AND SWAP4 SWAP1 SWAP4 OR DUP4 MSTORE DUP2 MLOAD SWAP2 SWAP4 POP SWAP1 DUP3 SWAP1 DUP2 DUP11 GAS STATICCALL DUP1 DUP1 ISZERO PUSH2 0x32E0 JUMPI PUSH1 0x1 DUP2 EQ PUSH2 0x3351 JUMPI PUSH2 0x3356 JUMP JUMPDEST PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 PUSH1 0x0 MSTORE PUSH29 0x2000000000000000000000000000000000000000000000000000000000 PUSH1 0x20 MSTORE PUSH29 0xF56414C494441544F525F4552524F5200000000000000000000000000 PUSH1 0x40 MSTORE PUSH1 0x0 PUSH1 0x60 MSTORE PUSH1 0x64 PUSH1 0x0 REVERT JUMPDEST DUP3 MLOAD SWAP5 POP JUMPDEST POP POP POP POP SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP4 GT PUSH2 0x339D JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x555F JUMP JUMPDEST PUSH2 0x33B0 PUSH2 0x33AA DUP6 DUP5 PUSH2 0x37C0 JUMP JUMPDEST DUP5 PUSH2 0x3D1E JUMP JUMPDEST SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0x5A65726F45785472616E73616374696F6E280000000000000000000000000000 PUSH1 0x20 DUP1 DUP4 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH32 0x75696E743235362073616C742C00000000000000000000000000000000000000 PUSH1 0x32 DUP4 ADD MSTORE PUSH32 0x61646472657373207369676E6572416464726573732C00000000000000000000 PUSH1 0x3F DUP4 ADD MSTORE PUSH32 0x6279746573206461746100000000000000000000000000000000000000000000 PUSH1 0x55 DUP4 ADD MSTORE PUSH32 0x2900000000000000000000000000000000000000000000000000000000000000 PUSH1 0x5F DUP4 ADD MSTORE DUP3 MLOAD DUP1 DUP4 SUB DUP5 ADD DUP2 MSTORE PUSH1 0x60 SWAP1 SWAP3 ADD SWAP3 DUP4 SWAP1 MSTORE DUP2 MLOAD PUSH1 0x0 SWAP4 DUP5 SWAP4 DUP5 SWAP4 SWAP1 SWAP3 DUP3 SWAP2 DUP5 ADD SWAP1 DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH2 0x34E1 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0x34A4 JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 DUP5 SUB PUSH2 0x100 EXP PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ADD DUP1 NOT SWAP1 SWAP3 AND SWAP2 AND OR SWAP1 MSTORE PUSH1 0x40 MLOAD SWAP2 SWAP1 SWAP4 ADD DUP2 SWAP1 SUB DUP2 KECCAK256 DUP10 MLOAD SWAP1 SWAP8 POP DUP10 SWAP6 POP SWAP1 SWAP4 POP DUP4 SWAP3 DUP6 ADD SWAP2 POP DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH2 0x3577 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0x353A JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 DUP5 SUB PUSH2 0x100 EXP PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ADD DUP1 NOT SWAP1 SWAP3 AND SWAP2 AND OR SWAP1 MSTORE PUSH1 0x40 DUP1 MLOAD SWAP3 SWAP1 SWAP5 ADD DUP3 SWAP1 SUB DUP3 KECCAK256 SWAP8 DUP3 MSTORE DUP2 ADD SWAP11 SWAP1 SWAP11 MSTORE POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP8 SWAP1 SWAP8 AND SWAP7 DUP9 ADD SWAP7 SWAP1 SWAP7 MSTORE POP POP PUSH1 0x60 DUP6 ADD MSTORE POP POP PUSH1 0x80 SWAP1 SWAP2 KECCAK256 SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x1 SLOAD PUSH1 0x40 MLOAD PUSH32 0x1901000000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x2 DUP2 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH1 0x22 DUP2 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH1 0x42 SWAP1 KECCAK256 SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x2F23 PUSH2 0x1AFD DUP4 PUSH2 0x3D35 JUMP JUMPDEST PUSH1 0x0 DUP2 DUP4 LT PUSH2 0x3656 JUMPI DUP2 PUSH2 0x25BF JUMP JUMPDEST POP SWAP1 SWAP2 SWAP1 POP JUMP JUMPDEST PUSH2 0x3665 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD DUP3 SWAP1 MSTORE PUSH1 0xA0 DUP4 ADD MLOAD PUSH1 0x80 DUP5 ADD MLOAD PUSH2 0x3681 SWAP2 DUP5 SWAP2 PUSH2 0x381B JUMP JUMPDEST DUP1 DUP3 MSTORE PUSH1 0x80 DUP5 ADD MLOAD PUSH1 0xC0 DUP6 ADD MLOAD PUSH2 0x3699 SWAP3 SWAP2 SWAP1 PUSH2 0x381B JUMP JUMPDEST PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0xA0 DUP4 ADD MLOAD PUSH1 0xE0 DUP5 ADD MLOAD PUSH2 0x36B3 SWAP2 DUP5 SWAP2 PUSH2 0x381B JUMP JUMPDEST PUSH1 0x60 DUP3 ADD MSTORE SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x60 DUP2 ADD DUP3 MSTORE PUSH1 0x24 DUP2 MSTORE PUSH32 0xF47261B0000000000000000000000000E41D2489571D322189246DAFA5EBDE1F PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x4699F49800000000000000000000000000000000000000000000000000000000 SWAP2 DUP2 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH2 0x140 DUP5 ADD MLOAD DUP5 MLOAD DUP4 MLOAD PUSH2 0x3731 SWAP3 SWAP2 SWAP1 DUP7 SWAP1 PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x374A DUP5 PUSH2 0x160 ADD MLOAD DUP5 DUP7 PUSH1 0x0 ADD MLOAD DUP6 PUSH1 0x20 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x3762 DUP2 DUP6 PUSH1 0x0 ADD MLOAD DUP7 PUSH1 0x40 ADD MLOAD DUP6 PUSH1 0x40 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x2758 DUP2 DUP5 DUP7 PUSH1 0x40 ADD MLOAD DUP6 PUSH1 0x60 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH1 0x0 DUP3 DUP3 ADD DUP4 DUP2 LT ISZERO PUSH2 0x37B5 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x558F JUMP JUMPDEST DUP1 SWAP2 POP JUMPDEST POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP4 ISZERO ISZERO PUSH2 0x37D3 JUMPI PUSH1 0x0 SWAP2 POP PUSH2 0x37B9 JUMP JUMPDEST POP DUP3 DUP3 MUL DUP3 DUP5 DUP3 DUP2 ISZERO ISZERO PUSH2 0x37E3 JUMPI INVALID JUMPDEST DIV EQ PUSH2 0x37B5 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x558F JUMP JUMPDEST PUSH1 0x0 DUP1 DUP4 GT PUSH2 0x3856 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x555F JUMP JUMPDEST PUSH2 0x3861 DUP5 DUP5 DUP5 PUSH2 0x418E JUMP JUMPDEST ISZERO PUSH2 0x339D JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x55BF JUMP JUMPDEST PUSH1 0x0 DUP1 DUP4 GT PUSH2 0x38D3 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x555F JUMP JUMPDEST PUSH2 0x38DE DUP5 DUP5 DUP5 PUSH2 0x4213 JUMP JUMPDEST ISZERO PUSH2 0x3915 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x55BF JUMP JUMPDEST PUSH2 0x33B0 PUSH2 0x33AA PUSH2 0x3925 DUP7 DUP6 PUSH2 0x37C0 JUMP JUMPDEST PUSH2 0x3930 DUP7 PUSH1 0x1 PUSH2 0x2EE2 JUMP JUMPDEST PUSH2 0x3776 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP4 GT DUP1 ISZERO PUSH2 0x3974 JUMPI POP DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP6 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ ISZERO JUMPDEST ISZERO PUSH2 0x3AF7 JUMPI DUP6 MLOAD PUSH1 0x3 LT PUSH2 0x39B4 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x564F JUMP JUMPDEST POP POP PUSH1 0x20 DUP5 DUP2 ADD MLOAD PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0xA SWAP1 SWAP3 MSTORE PUSH1 0x40 SWAP1 SWAP2 KECCAK256 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP1 ISZERO ISZERO PUSH2 0x3A3D JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x55FF JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH7 0xFFFFFFFFFFFE0 PUSH1 0x3F DUP9 MLOAD ADD AND DUP1 PUSH1 0x84 ADD DUP3 ADD PUSH32 0xA85E59E400000000000000000000000000000000000000000000000000000000 DUP4 MSTORE PUSH1 0x80 PUSH1 0x4 DUP5 ADD MSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP9 AND PUSH1 0x24 DUP5 ADD MSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP8 AND PUSH1 0x44 DUP5 ADD MSTORE DUP6 PUSH1 0x64 DUP5 ADD MSTORE PUSH1 0x84 DUP4 ADD JUMPDEST DUP2 DUP2 LT ISZERO PUSH2 0x3AD9 JUMPI DUP10 MLOAD DUP2 MSTORE PUSH1 0x20 SWAP10 DUP11 ADD SWAP10 ADD PUSH2 0x3AC1 JUMP JUMPDEST PUSH2 0x200 DUP5 DUP6 DUP5 SUB DUP7 PUSH1 0x0 DUP10 GAS CALL DUP1 ISZERO ISZERO PUSH2 0x3AF1 JUMPI RETURNDATASIZE DUP6 REVERT JUMPDEST POP POP POP POP POP JUMPDEST POP POP POP POP POP POP JUMP JUMPDEST DUP1 MLOAD PUSH1 0x0 SWAP1 PUSH1 0xFF AND PUSH1 0x3 EQ PUSH2 0x3B40 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x56AF JUMP JUMPDEST PUSH1 0x60 DUP4 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND ISZERO PUSH2 0x3BB3 JUMPI PUSH1 0x60 DUP4 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER EQ PUSH2 0x3BB3 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x56BF JUMP JUMPDEST PUSH2 0x3BBB PUSH2 0x2594 JUMP JUMPDEST DUP4 MLOAD SWAP1 SWAP2 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP4 AND SWAP2 AND EQ PUSH2 0x3C11 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x557F JUMP JUMPDEST POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x4 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 DUP2 SWAP1 KECCAK256 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE DUP3 DUP2 ADD MLOAD DUP4 MLOAD PUSH2 0x140 DUP6 ADD MLOAD PUSH2 0x160 DUP7 ADD MLOAD SWAP4 MLOAD DUP6 SWAP5 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP5 DUP6 AND SWAP5 SWAP4 SWAP1 SWAP4 AND SWAP3 PUSH32 0xDC47B3613D9FE400085F6DBDC99453462279057E6207385042827ED6B1A62CF7 SWAP3 PUSH2 0x3CB1 SWAP3 CALLER SWAP3 SWAP1 PUSH2 0x53C9 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG4 POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 PUSH1 0x14 ADD DUP4 MLOAD LT ISZERO ISZERO ISZERO PUSH2 0x3CFF JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x569F JUMP JUMPDEST POP ADD PUSH1 0x14 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 JUMP JUMPDEST PUSH1 0x0 DUP1 DUP3 DUP5 DUP2 ISZERO ISZERO PUSH2 0x3D2C JUMPI INVALID JUMPDEST DIV SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0x4F72646572280000000000000000000000000000000000000000000000000000 PUSH1 0x20 DUP1 DUP4 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH32 0x61646472657373206D616B6572416464726573732C0000000000000000000000 PUSH1 0x26 DUP4 ADD MSTORE PUSH32 0x616464726573732074616B6572416464726573732C0000000000000000000000 PUSH1 0x3B DUP4 ADD MSTORE PUSH32 0x6164647265737320666565526563697069656E74416464726573732C00000000 PUSH1 0x50 DUP4 ADD MSTORE PUSH32 0x616464726573732073656E646572416464726573732C00000000000000000000 PUSH1 0x6C DUP4 ADD MSTORE PUSH32 0x75696E74323536206D616B65724173736574416D6F756E742C00000000000000 PUSH1 0x82 DUP4 ADD MSTORE PUSH32 0x75696E743235362074616B65724173736574416D6F756E742C00000000000000 PUSH1 0x9B DUP4 ADD MSTORE PUSH32 0x75696E74323536206D616B65724665652C000000000000000000000000000000 PUSH1 0xB4 DUP4 ADD MSTORE PUSH32 0x75696E743235362074616B65724665652C000000000000000000000000000000 PUSH1 0xC5 DUP4 ADD MSTORE PUSH32 0x75696E743235362065787069726174696F6E54696D655365636F6E64732C0000 PUSH1 0xD6 DUP4 ADD MSTORE PUSH32 0x75696E743235362073616C742C00000000000000000000000000000000000000 PUSH1 0xF4 DUP4 ADD MSTORE PUSH32 0x6279746573206D616B65724173736574446174612C0000000000000000000000 PUSH2 0x101 DUP4 ADD MSTORE PUSH32 0x62797465732074616B6572417373657444617461000000000000000000000000 PUSH2 0x116 DUP4 ADD MSTORE PUSH32 0x2900000000000000000000000000000000000000000000000000000000000000 PUSH2 0x12A DUP4 ADD MSTORE DUP3 MLOAD PUSH2 0x10B DUP2 DUP5 SUB ADD DUP2 MSTORE PUSH2 0x12B SWAP1 SWAP3 ADD SWAP3 DUP4 SWAP1 MSTORE DUP2 MLOAD PUSH1 0x0 SWAP4 DUP5 SWAP4 DUP5 SWAP4 DUP5 SWAP4 SWAP2 SWAP3 SWAP2 DUP3 SWAP2 DUP5 ADD SWAP1 DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH2 0x3FBD JUMPI DUP1 MLOAD DUP3 MSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0x3F80 JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 DUP5 SUB PUSH2 0x100 EXP PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ADD DUP1 NOT SWAP1 SWAP3 AND SWAP2 AND OR SWAP1 MSTORE PUSH1 0x40 MLOAD SWAP2 SWAP1 SWAP4 ADD DUP2 SWAP1 SUB DUP2 KECCAK256 PUSH2 0x140 DUP12 ADD MLOAD DUP1 MLOAD SWAP2 SWAP10 POP SWAP6 POP SWAP1 SWAP4 POP DUP4 SWAP3 DUP6 ADD SWAP2 POP DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH2 0x4058 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0x401B JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 DUP5 SUB PUSH2 0x100 EXP PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ADD DUP1 NOT SWAP1 SWAP3 AND SWAP2 AND OR SWAP1 MSTORE PUSH1 0x40 MLOAD SWAP2 SWAP1 SWAP4 ADD DUP2 SWAP1 SUB DUP2 KECCAK256 PUSH2 0x160 DUP12 ADD MLOAD DUP1 MLOAD SWAP2 SWAP9 POP SWAP6 POP SWAP1 SWAP4 POP DUP4 SWAP3 DUP6 ADD SWAP2 POP DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH2 0x40F3 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0x40B6 JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 SWAP1 SWAP4 SUB PUSH2 0x100 EXP PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ADD DUP1 NOT SWAP1 SWAP2 AND SWAP3 AND SWAP2 SWAP1 SWAP2 OR SWAP1 MSTORE PUSH1 0x40 MLOAD SWAP3 ADD DUP3 SWAP1 SUB SWAP1 SWAP2 KECCAK256 PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 DUP10 ADD DUP1 MLOAD PUSH2 0x140 DUP12 ADD DUP1 MLOAD PUSH2 0x160 SWAP1 SWAP13 ADD DUP1 MLOAD SWAP11 DUP5 MSTORE SWAP9 DUP2 MSTORE SWAP3 DUP9 MSTORE PUSH2 0x1A0 DUP3 KECCAK256 SWAP2 MSTORE SWAP9 SWAP1 MSTORE POP POP POP SWAP2 SWAP1 MSTORE POP SWAP1 SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP1 DUP5 GT PUSH2 0x41CA JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x555F JUMP JUMPDEST DUP3 ISZERO DUP1 PUSH2 0x41D5 JUMPI POP DUP5 ISZERO JUMPDEST ISZERO PUSH2 0x41E3 JUMPI PUSH1 0x0 SWAP2 POP PUSH2 0xA88 JUMP JUMPDEST DUP4 DUP1 ISZERO ISZERO PUSH2 0x41ED JUMPI INVALID JUMPDEST DUP6 DUP5 MULMOD SWAP1 POP PUSH2 0x41FC DUP6 DUP5 PUSH2 0x37C0 JUMP JUMPDEST PUSH2 0x4208 PUSH2 0x3E8 DUP4 PUSH2 0x37C0 JUMP JUMPDEST LT ISZERO SWAP6 SWAP5 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP1 DUP5 GT PUSH2 0x424F JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x555F JUMP JUMPDEST DUP3 ISZERO DUP1 PUSH2 0x425A JUMPI POP DUP5 ISZERO JUMPDEST ISZERO PUSH2 0x4268 JUMPI PUSH1 0x0 SWAP2 POP PUSH2 0xA88 JUMP JUMPDEST DUP4 DUP1 ISZERO ISZERO PUSH2 0x4272 JUMPI INVALID JUMPDEST DUP6 DUP5 MULMOD SWAP1 POP DUP4 PUSH2 0x4282 DUP6 DUP4 PUSH2 0x2EE2 JUMP JUMPDEST DUP2 ISZERO ISZERO PUSH2 0x428B JUMPI INVALID JUMPDEST MOD SWAP1 POP PUSH2 0x41FC DUP6 DUP5 PUSH2 0x37C0 JUMP JUMPDEST PUSH1 0x80 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE POP SWAP1 JUMP JUMPDEST PUSH2 0x120 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH2 0x42D6 PUSH2 0x4298 JUMP JUMPDEST DUP2 MSTORE PUSH1 0x20 ADD PUSH2 0x42E3 PUSH2 0x4298 JUMP JUMPDEST DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE POP SWAP1 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x60 DUP2 ADD DUP3 MSTORE PUSH1 0x0 DUP1 DUP3 MSTORE PUSH1 0x20 DUP3 ADD DUP2 SWAP1 MSTORE SWAP2 DUP2 ADD SWAP2 SWAP1 SWAP2 MSTORE SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x25BF DUP3 CALLDATALOAD PUSH2 0x57C2 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0x432D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0x4340 PUSH2 0x433B DUP3 PUSH2 0x5751 JUMP JUMPDEST PUSH2 0x572A JUMP JUMPDEST DUP2 DUP2 MSTORE PUSH1 0x20 SWAP4 DUP5 ADD SWAP4 SWAP1 SWAP3 POP DUP3 ADD DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x437E JUMPI DUP2 CALLDATALOAD DUP7 ADD PUSH2 0x4368 DUP9 DUP3 PUSH2 0x44CE JUMP JUMPDEST DUP5 MSTORE POP PUSH1 0x20 SWAP3 DUP4 ADD SWAP3 SWAP2 SWAP1 SWAP2 ADD SWAP1 PUSH1 0x1 ADD PUSH2 0x4352 JUMP JUMPDEST POP POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0x4399 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0x43A7 PUSH2 0x433B DUP3 PUSH2 0x5751 JUMP JUMPDEST DUP2 DUP2 MSTORE PUSH1 0x20 SWAP4 DUP5 ADD SWAP4 SWAP1 SWAP3 POP DUP3 ADD DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x437E JUMPI DUP2 CALLDATALOAD DUP7 ADD PUSH2 0x43CF DUP9 DUP3 PUSH2 0x451D JUMP JUMPDEST DUP5 MSTORE POP PUSH1 0x20 SWAP3 DUP4 ADD SWAP3 SWAP2 SWAP1 SWAP2 ADD SWAP1 PUSH1 0x1 ADD PUSH2 0x43B9 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0x43F6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0x4404 PUSH2 0x433B DUP3 PUSH2 0x5751 JUMP JUMPDEST SWAP2 POP DUP2 DUP2 DUP4 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP PUSH1 0x20 DUP2 ADD SWAP1 POP DUP4 DUP6 PUSH1 0x20 DUP5 MUL DUP3 ADD GT ISZERO PUSH2 0x4429 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x437E JUMPI DUP2 PUSH2 0x443F DUP9 DUP3 PUSH2 0x4461 JUMP JUMPDEST DUP5 MSTORE POP PUSH1 0x20 SWAP3 DUP4 ADD SWAP3 SWAP2 SWAP1 SWAP2 ADD SWAP1 PUSH1 0x1 ADD PUSH2 0x442C JUMP JUMPDEST PUSH1 0x0 PUSH2 0x25BF DUP3 CALLDATALOAD PUSH2 0x57DB JUMP JUMPDEST PUSH1 0x0 PUSH2 0x25BF DUP3 CALLDATALOAD PUSH2 0x57E0 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x25BF DUP3 CALLDATALOAD PUSH2 0x57E3 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x25BF DUP3 MLOAD PUSH2 0x57E3 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x1F DUP4 ADD DUP5 SGT PUSH2 0x4497 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP DUP2 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x44AF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x20 DUP4 ADD SWAP2 POP DUP4 PUSH1 0x1 DUP3 MUL DUP4 ADD GT ISZERO PUSH2 0x44C7 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST SWAP3 POP SWAP3 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0x44DF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0x44ED PUSH2 0x433B DUP3 PUSH2 0x5772 JUMP JUMPDEST SWAP2 POP DUP1 DUP3 MSTORE PUSH1 0x20 DUP4 ADD PUSH1 0x20 DUP4 ADD DUP6 DUP4 DUP4 ADD GT ISZERO PUSH2 0x4509 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4514 DUP4 DUP3 DUP5 PUSH2 0x5819 JUMP JUMPDEST POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x180 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x4530 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x453B PUSH2 0x180 PUSH2 0x572A JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0x4549 DUP5 DUP5 PUSH2 0x4310 JUMP JUMPDEST DUP3 MSTORE POP PUSH1 0x20 PUSH2 0x455A DUP5 DUP5 DUP4 ADD PUSH2 0x4310 JUMP JUMPDEST PUSH1 0x20 DUP4 ADD MSTORE POP PUSH1 0x40 PUSH2 0x456E DUP5 DUP3 DUP6 ADD PUSH2 0x4310 JUMP JUMPDEST PUSH1 0x40 DUP4 ADD MSTORE POP PUSH1 0x60 PUSH2 0x4582 DUP5 DUP3 DUP6 ADD PUSH2 0x4310 JUMP JUMPDEST PUSH1 0x60 DUP4 ADD MSTORE POP PUSH1 0x80 PUSH2 0x4596 DUP5 DUP3 DUP6 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH1 0x80 DUP4 ADD MSTORE POP PUSH1 0xA0 PUSH2 0x45AA DUP5 DUP3 DUP6 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH1 0xA0 DUP4 ADD MSTORE POP PUSH1 0xC0 PUSH2 0x45BE DUP5 DUP3 DUP6 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH1 0xC0 DUP4 ADD MSTORE POP PUSH1 0xE0 PUSH2 0x45D2 DUP5 DUP3 DUP6 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH1 0xE0 DUP4 ADD MSTORE POP PUSH2 0x100 PUSH2 0x45E7 DUP5 DUP3 DUP6 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH2 0x100 DUP4 ADD MSTORE POP PUSH2 0x120 PUSH2 0x45FD DUP5 DUP3 DUP6 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH2 0x120 DUP4 ADD MSTORE POP PUSH2 0x140 DUP3 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x461F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x462B DUP5 DUP3 DUP6 ADD PUSH2 0x44CE JUMP JUMPDEST PUSH2 0x140 DUP4 ADD MSTORE POP PUSH2 0x160 DUP3 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x464D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4659 DUP5 DUP3 DUP6 ADD PUSH2 0x44CE JUMP JUMPDEST PUSH2 0x160 DUP4 ADD MSTORE POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x4678 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x33B0 DUP5 DUP5 PUSH2 0x4310 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x4697 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x46A3 DUP6 DUP6 PUSH2 0x4310 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 PUSH2 0x46B4 DUP6 DUP3 DUP7 ADD PUSH2 0x4310 JUMP JUMPDEST SWAP2 POP POP SWAP3 POP SWAP3 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x46D1 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x46DD DUP6 DUP6 PUSH2 0x4310 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 PUSH2 0x46B4 DUP6 DUP3 DUP7 ADD PUSH2 0x4455 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x4700 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x4717 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x33B0 DUP5 DUP3 DUP6 ADD PUSH2 0x4388 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 PUSH1 0x60 DUP5 DUP7 SUB SLT ISZERO PUSH2 0x4738 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP4 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x474F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x475B DUP7 DUP3 DUP8 ADD PUSH2 0x4388 JUMP JUMPDEST SWAP4 POP POP PUSH1 0x20 DUP5 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x4778 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4784 DUP7 DUP3 DUP8 ADD PUSH2 0x43E5 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x40 DUP5 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x47A1 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x47AD DUP7 DUP3 DUP8 ADD PUSH2 0x431C JUMP JUMPDEST SWAP2 POP POP SWAP3 POP SWAP3 POP SWAP3 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 PUSH1 0x60 DUP5 DUP7 SUB SLT ISZERO PUSH2 0x47CC JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP4 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x47E3 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x47EF DUP7 DUP3 DUP8 ADD PUSH2 0x4388 JUMP JUMPDEST SWAP4 POP POP PUSH1 0x20 PUSH2 0x4784 DUP7 DUP3 DUP8 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x4812 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x33B0 DUP5 DUP5 PUSH2 0x4461 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x4831 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x46A3 DUP6 DUP6 PUSH2 0x4461 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x60 DUP6 DUP8 SUB SLT ISZERO PUSH2 0x4853 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x485F DUP8 DUP8 PUSH2 0x4461 JUMP JUMPDEST SWAP5 POP POP PUSH1 0x20 PUSH2 0x4870 DUP8 DUP3 DUP9 ADD PUSH2 0x4310 JUMP JUMPDEST SWAP4 POP POP PUSH1 0x40 DUP6 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x488D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4899 DUP8 DUP3 DUP9 ADD PUSH2 0x4485 JUMP JUMPDEST SWAP6 SWAP9 SWAP5 SWAP8 POP SWAP6 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 PUSH1 0x60 DUP5 DUP7 SUB SLT ISZERO PUSH2 0x48BA JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x48C6 DUP7 DUP7 PUSH2 0x4461 JUMP JUMPDEST SWAP4 POP POP PUSH1 0x20 PUSH2 0x48D7 DUP7 DUP3 DUP8 ADD PUSH2 0x4310 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x40 DUP5 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x48F4 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x47AD DUP7 DUP3 DUP8 ADD PUSH2 0x44CE JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x4912 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x33B0 DUP5 DUP5 PUSH2 0x446D JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x4930 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x33B0 DUP5 DUP5 PUSH2 0x4479 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x494E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x4965 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x33B0 DUP5 DUP3 DUP6 ADD PUSH2 0x451D JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x80 DUP6 DUP8 SUB SLT ISZERO PUSH2 0x4987 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP5 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x499E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x49AA DUP8 DUP3 DUP9 ADD PUSH2 0x451D JUMP JUMPDEST SWAP5 POP POP PUSH1 0x20 DUP6 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x49C7 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x49D3 DUP8 DUP3 DUP9 ADD PUSH2 0x451D JUMP JUMPDEST SWAP4 POP POP PUSH1 0x40 DUP6 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x49F0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x49FC DUP8 DUP3 DUP9 ADD PUSH2 0x44CE JUMP JUMPDEST SWAP3 POP POP PUSH1 0x60 DUP6 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x4A19 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4A25 DUP8 DUP3 DUP9 ADD PUSH2 0x44CE JUMP JUMPDEST SWAP2 POP POP SWAP3 SWAP6 SWAP2 SWAP5 POP SWAP3 POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 PUSH1 0x60 DUP5 DUP7 SUB SLT ISZERO PUSH2 0x4A46 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP4 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x4A5D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4A69 DUP7 DUP3 DUP8 ADD PUSH2 0x451D JUMP JUMPDEST SWAP4 POP POP PUSH1 0x20 PUSH2 0x48D7 DUP7 DUP3 DUP8 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x80 DUP8 DUP10 SUB SLT ISZERO PUSH2 0x4A93 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x4A9F DUP10 DUP10 PUSH2 0x4461 JUMP JUMPDEST SWAP7 POP POP PUSH1 0x20 PUSH2 0x4AB0 DUP10 DUP3 DUP11 ADD PUSH2 0x4310 JUMP JUMPDEST SWAP6 POP POP PUSH1 0x40 DUP8 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x4ACD JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4AD9 DUP10 DUP3 DUP11 ADD PUSH2 0x4485 JUMP JUMPDEST SWAP5 POP SWAP5 POP POP PUSH1 0x60 DUP8 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x4AF8 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4B04 DUP10 DUP3 DUP11 ADD PUSH2 0x4485 JUMP JUMPDEST SWAP3 POP SWAP3 POP POP SWAP3 SWAP6 POP SWAP3 SWAP6 POP SWAP3 SWAP6 JUMP JUMPDEST PUSH2 0x4B1C DUP2 PUSH2 0x57C2 JUMP JUMPDEST DUP3 MSTORE POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x4B2D DUP3 PUSH2 0x57BE JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP PUSH2 0x4B3F DUP4 PUSH2 0x57B8 JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP3 DUP2 LT ISZERO PUSH2 0x4B6F JUMPI PUSH2 0x4B55 DUP7 DUP4 MLOAD PUSH2 0x52F7 JUMP JUMPDEST PUSH2 0x4B5E DUP3 PUSH2 0x57B8 JUMP JUMPDEST PUSH1 0x60 SWAP7 SWAP1 SWAP7 ADD SWAP6 SWAP2 POP PUSH1 0x1 ADD PUSH2 0x4B42 JUMP JUMPDEST POP SWAP4 SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH2 0x4B1C DUP2 PUSH2 0x57DB JUMP JUMPDEST PUSH2 0x4B1C DUP2 PUSH2 0x57E0 JUMP JUMPDEST PUSH2 0x4B1C DUP2 PUSH2 0x57E3 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x4B9F DUP3 PUSH2 0x57BE JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH2 0x4BB3 DUP2 PUSH1 0x20 DUP7 ADD PUSH1 0x20 DUP7 ADD PUSH2 0x5825 JUMP JUMPDEST PUSH2 0x4BBC DUP2 PUSH2 0x5851 JUMP JUMPDEST SWAP1 SWAP4 ADD PUSH1 0x20 ADD SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH2 0x4B1C DUP2 PUSH2 0x580E JUMP JUMPDEST PUSH1 0x12 DUP2 MSTORE PUSH32 0x4C454E4754485F36355F52455155495245440000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xD DUP2 MSTORE PUSH32 0x494E56414C49445F54414B455200000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xE DUP2 MSTORE PUSH32 0x4F524445525F4F56455246494C4C000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x11 DUP2 MSTORE PUSH32 0x55494E543235365F554E444552464C4F57000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x1A DUP2 MSTORE PUSH32 0x41535345545F50524F58595F414C52454144595F455849535453000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x26 DUP2 MSTORE PUSH32 0x475245415445525F4F525F455155414C5F544F5F33325F4C454E4754485F5245 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x5155495245440000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x15 DUP2 MSTORE PUSH32 0x5349474E41545552455F554E535550504F525445440000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x10 DUP2 MSTORE PUSH32 0x4449564953494F4E5F42595F5A45524F00000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x17 DUP2 MSTORE PUSH32 0x494E56414C49445F4F524445525F5349474E4154555245000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xD DUP2 MSTORE PUSH32 0x494E56414C49445F4D414B455200000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x10 DUP2 MSTORE PUSH32 0x55494E543235365F4F564552464C4F5700000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xF DUP2 MSTORE PUSH32 0x494E56414C49445F54585F484153480000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x11 DUP2 MSTORE PUSH32 0x494E56414C49445F5349474E4154555245000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xE DUP2 MSTORE PUSH32 0x524F554E44494E475F4552524F52000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x10 DUP2 MSTORE PUSH32 0x4641494C45445F455845435554494F4E00000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xD DUP2 MSTORE PUSH32 0x54414B45525F4F56455250415900000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x14 DUP2 MSTORE PUSH32 0x494E56414C49445F54414B45525F414D4F554E54000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x1A DUP2 MSTORE PUSH32 0x41535345545F50524F58595F444F45535F4E4F545F4558495354000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x21 DUP2 MSTORE PUSH32 0x475245415445525F5448414E5F5A45524F5F4C454E4754485F52455155495245 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x4400000000000000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x11 DUP2 MSTORE PUSH32 0x5349474E41545552455F494C4C4547414C000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x1E DUP2 MSTORE PUSH32 0x4C454E4754485F475245415445525F5448414E5F305F52455155495245440000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x17 DUP2 MSTORE PUSH32 0x494E56414C49445F4E45575F4F524445525F45504F4348000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x1E DUP2 MSTORE PUSH32 0x4C454E4754485F475245415445525F5448414E5F335F52455155495245440000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x14 DUP2 MSTORE PUSH32 0x434F4D504C4554455F46494C4C5F4641494C4544000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x12 DUP2 MSTORE PUSH32 0x494E56414C49445F46494C4C5F50524943450000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x12 DUP2 MSTORE PUSH32 0x5245454E5452414E43595F494C4C4547414C0000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x13 DUP2 MSTORE PUSH32 0x4F4E4C595F434F4E54524143545F4F574E455200000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x26 DUP2 MSTORE PUSH32 0x475245415445525F4F525F455155414C5F544F5F32305F4C454E4754485F5245 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x5155495245440000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x10 DUP2 MSTORE PUSH32 0x4F524445525F554E46494C4C41424C4500000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xE DUP2 MSTORE PUSH32 0x494E56414C49445F53454E444552000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x18 DUP2 MSTORE PUSH32 0x4E454741544956455F5350524541445F52455155495245440000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x14 DUP2 MSTORE PUSH32 0x494E56414C49445F54585F5349474E4154555245000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x11 DUP2 MSTORE PUSH32 0x4C454E4754485F305F5245515549524544000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST DUP1 MLOAD PUSH1 0x80 DUP4 ADD SWAP1 PUSH2 0x5285 DUP5 DUP3 PUSH2 0x4B82 JUMP JUMPDEST POP PUSH1 0x20 DUP3 ADD MLOAD PUSH2 0x5298 PUSH1 0x20 DUP6 ADD DUP3 PUSH2 0x4B82 JUMP JUMPDEST POP PUSH1 0x40 DUP3 ADD MLOAD PUSH2 0x52AB PUSH1 0x40 DUP6 ADD DUP3 PUSH2 0x4B82 JUMP JUMPDEST POP PUSH1 0x60 DUP3 ADD MLOAD PUSH2 0x2758 PUSH1 0x60 DUP6 ADD DUP3 PUSH2 0x4B82 JUMP JUMPDEST DUP1 MLOAD PUSH2 0x120 DUP4 ADD SWAP1 PUSH2 0x52D0 DUP5 DUP3 PUSH2 0x5274 JUMP JUMPDEST POP PUSH1 0x20 DUP3 ADD MLOAD PUSH2 0x52E3 PUSH1 0x80 DUP6 ADD DUP3 PUSH2 0x5274 JUMP JUMPDEST POP PUSH1 0x40 DUP3 ADD MLOAD PUSH2 0x2758 PUSH2 0x100 DUP6 ADD DUP3 PUSH2 0x4B82 JUMP JUMPDEST DUP1 MLOAD PUSH1 0x60 DUP4 ADD SWAP1 PUSH2 0x5308 DUP5 DUP3 PUSH2 0x532E JUMP JUMPDEST POP PUSH1 0x20 DUP3 ADD MLOAD PUSH2 0x531B PUSH1 0x20 DUP6 ADD DUP3 PUSH2 0x4B82 JUMP JUMPDEST POP PUSH1 0x40 DUP3 ADD MLOAD PUSH2 0x2758 PUSH1 0x40 DUP6 ADD DUP3 PUSH2 0x4B82 JUMP JUMPDEST PUSH2 0x4B1C DUP2 PUSH2 0x5808 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x2F23 DUP3 DUP5 PUSH2 0x4B13 JUMP JUMPDEST PUSH2 0x100 DUP2 ADD PUSH2 0x5354 DUP3 DUP12 PUSH2 0x4B13 JUMP JUMPDEST PUSH2 0x5361 PUSH1 0x20 DUP4 ADD DUP11 PUSH2 0x4B13 JUMP JUMPDEST PUSH2 0x536E PUSH1 0x40 DUP4 ADD DUP10 PUSH2 0x4B82 JUMP JUMPDEST PUSH2 0x537B PUSH1 0x60 DUP4 ADD DUP9 PUSH2 0x4B82 JUMP JUMPDEST PUSH2 0x5388 PUSH1 0x80 DUP4 ADD DUP8 PUSH2 0x4B82 JUMP JUMPDEST PUSH2 0x5395 PUSH1 0xA0 DUP4 ADD DUP7 PUSH2 0x4B82 JUMP JUMPDEST DUP2 DUP2 SUB PUSH1 0xC0 DUP4 ADD MSTORE PUSH2 0x53A7 DUP2 DUP6 PUSH2 0x4B94 JUMP JUMPDEST SWAP1 POP DUP2 DUP2 SUB PUSH1 0xE0 DUP4 ADD MSTORE PUSH2 0x53BB DUP2 DUP5 PUSH2 0x4B94 JUMP JUMPDEST SWAP11 SWAP10 POP POP POP POP POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x60 DUP2 ADD PUSH2 0x53D7 DUP3 DUP7 PUSH2 0x4B13 JUMP JUMPDEST DUP2 DUP2 SUB PUSH1 0x20 DUP4 ADD MSTORE PUSH2 0x53E9 DUP2 DUP6 PUSH2 0x4B94 JUMP JUMPDEST SWAP1 POP DUP2 DUP2 SUB PUSH1 0x40 DUP4 ADD MSTORE PUSH2 0x53FD DUP2 DUP5 PUSH2 0x4B94 JUMP JUMPDEST SWAP6 SWAP5 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x25BF DUP2 DUP5 PUSH2 0x4B22 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x2F23 DUP3 DUP5 PUSH2 0x4B79 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x2F23 DUP3 DUP5 PUSH2 0x4B82 JUMP JUMPDEST PUSH1 0x60 DUP2 ADD PUSH2 0x5441 DUP3 DUP7 PUSH2 0x4B82 JUMP JUMPDEST PUSH2 0x544E PUSH1 0x20 DUP4 ADD DUP6 PUSH2 0x4B13 JUMP JUMPDEST DUP2 DUP2 SUB PUSH1 0x40 DUP4 ADD MSTORE PUSH2 0x53FD DUP2 DUP5 PUSH2 0x4B94 JUMP JUMPDEST PUSH1 0x40 DUP2 ADD PUSH2 0x546E DUP3 DUP6 PUSH2 0x4B82 JUMP JUMPDEST DUP2 DUP2 SUB PUSH1 0x20 DUP4 ADD MSTORE PUSH2 0x33B0 DUP2 DUP5 PUSH2 0x4B94 JUMP JUMPDEST PUSH1 0x80 DUP2 ADD PUSH2 0x548E DUP3 DUP8 PUSH2 0x4B82 JUMP JUMPDEST PUSH2 0x549B PUSH1 0x20 DUP4 ADD DUP7 PUSH2 0x532E JUMP JUMPDEST PUSH2 0x54A8 PUSH1 0x40 DUP4 ADD DUP6 PUSH2 0x4B82 JUMP JUMPDEST PUSH2 0x53FD PUSH1 0x60 DUP4 ADD DUP5 PUSH2 0x4B82 JUMP JUMPDEST PUSH1 0x40 DUP2 ADD PUSH2 0x54C3 DUP3 DUP6 PUSH2 0x4B8B JUMP JUMPDEST PUSH2 0x25BF PUSH1 0x20 DUP4 ADD DUP5 PUSH2 0x4B13 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x25BF DUP2 DUP5 PUSH2 0x4B94 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x2F23 DUP3 DUP5 PUSH2 0x4BC9 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4BD2 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4C02 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4C32 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4C62 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4C92 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4CC2 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4D18 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4D48 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4D78 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4DA8 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4DD8 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4E08 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4E38 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4E68 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4E98 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4EC8 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4EF8 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4F28 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4F58 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4FAE JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4FDE JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x500E JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x503E JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x506E JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x509E JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x50CE JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x50FE JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x512E JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x5184 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x51B4 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x51E4 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x5214 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x5244 JUMP JUMPDEST PUSH1 0x80 DUP2 ADD PUSH2 0x2F23 DUP3 DUP5 PUSH2 0x5274 JUMP JUMPDEST PUSH2 0x120 DUP2 ADD PUSH2 0x2F23 DUP3 DUP5 PUSH2 0x52BE JUMP JUMPDEST PUSH1 0x60 DUP2 ADD PUSH2 0x2F23 DUP3 DUP5 PUSH2 0x52F7 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP2 DUP2 ADD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT DUP3 DUP3 LT OR ISZERO PUSH2 0x5749 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x40 MSTORE SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 PUSH8 0xFFFFFFFFFFFFFFFF DUP3 GT ISZERO PUSH2 0x5768 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x20 SWAP1 DUP2 MUL ADD SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH8 0xFFFFFFFFFFFFFFFF DUP3 GT ISZERO PUSH2 0x5789 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x20 PUSH1 0x1F SWAP2 SWAP1 SWAP2 ADD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 AND ADD SWAP1 JUMP JUMPDEST PUSH1 0x20 ADD SWAP1 JUMP JUMPDEST MLOAD SWAP1 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 JUMP JUMPDEST ISZERO ISZERO SWAP1 JUMP JUMPDEST SWAP1 JUMP JUMPDEST PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 AND SWAP1 JUMP JUMPDEST PUSH1 0xFF AND SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x2F23 DUP3 PUSH2 0x57C2 JUMP JUMPDEST DUP3 DUP2 DUP4 CALLDATACOPY POP PUSH1 0x0 SWAP2 ADD MSTORE JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x5840 JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0x5828 JUMP JUMPDEST DUP4 DUP2 GT ISZERO PUSH2 0x2758 JUMPI POP POP PUSH1 0x0 SWAP2 ADD MSTORE JUMP JUMPDEST PUSH1 0x1F ADD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 AND SWAP1 JUMP STOP LOG2 PUSH6 0x627A7A723058 KECCAK256 0xb1 SWAP13 SWAP3 KECCAK256 LT ORIGIN PUSH1 0x33 SWAP9 0xc0 SMOD 0xdc 0xcf DUP1 SWAP4 0xc PUSH3 0x6EF89F 0xa6 0xc8 0x48 0xc8 0xb7 0xef MSTORE PUSH8 0xD07CD4ED6C657870 PUSH6 0x72696D656E74 PUSH2 0x6CF5 STOP CALLDATACOPY ", + "sourceMap": "897:514:21:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1277:42:23;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1277:42:23;;;;;;;;;;;;;;;;;;;;;;;;;4133:647:27;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;4133:647:27;;;;;;;;;;;;;;;;;1367:42:23;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1367:42:23;;;;;;;;;;;;;;;;;1572:445:25;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1572:445:25;;;;;;;;;;;1870:2891:24;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1870:2891:24;;;;;;;;;;;;;;;;;2139:1515:27;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;2139:1515:27;;;;;;;;;897:51:22;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;897:51:22;;;;;;;;;;;;;;;;;15747:260:27;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;15747:260:27;;;;;;;;;5260:659;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;5260:659:27;;;;;;;;;2018:1079:23;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;2018:1079:23;;;;;;;;;6478:632:27;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;6478:632:27;;;;;;;;;2013:154:22;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;2013:154:22;;;;;;;;;;;;;;;;;967:45:26;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;967:45:26;;;;;;;;;1357:395:27;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1357:395:27;;;;;;;;;2254:412:25;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;2254:412:25;;;;;;;;;1166:71;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1166:71:25;;;;;;;;;7498:1507:27;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;7498:1507:27;;;;;;;;;16201:419;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;16201:419:27;;;;;;;;;;;;;;;;;1046:63:25;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1046:63:25;;;;;;;;;91:20:55;;8:9:-1;5:2;;;30:1;27;20:12;5:2;91:20:55;;;;3038:4848:25;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;3038:4848:25;;;;;;;;;13713:1899:27;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;13713:1899:27;;;;;;;;;3414:374:23;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;3414:374:23;;;;;;;;;1426:1456:26;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1426:1456:26;;;;;;;;;1157:666:22;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1157:666:22;;;;;;;;;4417:2261:23;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;4417:2261:23;;;;;;;;;;;;;;;;;4019:124;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;4019:124:23;;;;;;;;;1627:67;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1627:67:23;;;;;;;;;1528:185:38;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1528:185:38;;;;;;;;;;;;9462:1488:27;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;9462:1488:27;;;;;;;;;1301:33:39;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1301:33:39;;;;11338:1914:27;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;11338:1914:27;;;;;;;;;1064:36:26;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1064:36:26;;;;333:167:55;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;333:167:55;;;;;;;;;1078:40:21;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1078:40:21;;;;1277:42:23;;;;;;;;;;;;;:::o;4133:647:27:-;4340:35;;:::i;:::-;4391:20;4442:9;4495:36;;:::i;:::-;939:6:56;;;;938:7;917:72;;;;;;;;;;;;;;;;;;;;;;1043:6;:13;;;;1052:4;1043:13;;;4414::27;;;-1:-1:-1;1043:6:56;-1:-1:-1;4437:304:27;4457:17;;;4437:304;;4534:131;4569:6;4576:1;4569:9;;;;;;;;;;;;;;;;;;4596:21;4618:1;4596:24;;;;;;;;;;;;;;;;;;4638:10;4649:1;4638:13;;;;;;;;;;;;;;;;;;4534:17;:131::i;:::-;4495:170;;4679:51;4694:16;4712:17;4679:14;:51::i;:::-;4476:3;;;;;4437:304;;;-1:-1:-1;;1165:5:56;1156:14;;;;;;-1:-1:-1;4133:647:27;;-1:-1:-1;;;4133:647:27:o;1367:42:23:-;;;;;;;;;;;;;;;:::o;1572:445:25:-;1708:27;;;1725:10;1708:27;1704:260;;1776:126;1814:4;1840:13;1875:9;;1776:126;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1776:16:25;;-1:-1:-1;;;;;1776:126:25:i;:::-;1751:202;;;;;;;;;;;;;;;;-1:-1:-1;;1973:15:25;;;;:9;:15;;;;;;;;:30;;;;;;;;;;;:37;;;;2006:4;1973:37;;;1572:445::o;1870:2891:24:-;2106:59;;:::i;:::-;2571:39;;:::i;:::-;2646:40;;:::i;:::-;2755:20;939:6:56;;;;938:7;917:72;;;;;;;;;;;;;;1043:6;:13;;;;1052:4;1043:13;;;2435:24:24;;;;;2407:25;;;;:52;;;;2497:24;;;2469:25;;;:52;2613:23;2435:9;2613:12;:23::i;:::-;2571:65;;2689:24;2702:10;2689:12;:24::i;:::-;2646:67;;2778:26;:24;:26::i;:::-;2755:49;;2875:132;2908:9;2931:13;2958:12;2984:13;2875:19;:132::i;:::-;3017:135;3050:10;3074:14;3102:12;3128:14;3017:19;:135::i;:::-;3162:39;3179:9;3190:10;3162:16;:39::i;:::-;3278:195;3319:9;3342:10;3366:13;:41;;;3421:14;:42;;;3278:27;:195::i;:::-;3597:23;;:46;;;;3717;;3257:216;;-1:-1:-1;3518:255:24;;3547:9;;3570:13;;3597:46;;3518:15;:255::i;:::-;3864:24;;;;;:47;;;;3986;;3783:260;;3812:10;;3836:14;;3864:47;;;3783:15;:260::i;:::-;4095:205;4126:9;4149:12;4175:13;:23;;;4212:13;:41;;;4267:18;:23;;;4095:17;:205::i;:::-;4310:209;4341:10;4365:12;4391:14;:24;;;4429:14;:42;;;4485:18;:24;;;4310:17;:209::i;:::-;4584:134;4617:9;4640:10;4664:12;4690:18;4584:19;:134::i;:::-;-1:-1:-1;;1165:5:56;1156:14;;;;;;-1:-1:-1;1870:2891:24;;-1:-1:-1;;;;1870:2891:24:o;2139:1515:27:-;2310:30;;:::i;:::-;2403;2436:104;2468:5;2487:20;2521:9;2436:18;:104::i;:::-;2403:137;;3116:3;3037:17;2972;2966:24;2870:2;2851:17;2847:26;2762:7;2691:3;2661:532;3209:7;3206:2;;;3261:17;3255:24;3242:11;3235:45;3355:2;3336:17;3332:26;3326:33;3321:2;3308:11;3304:20;3297:63;3435:2;3416:17;3412:26;3406:33;3401:2;3388:11;3384:20;3377:63;3515:2;3496:17;3492:26;3486:33;3481:2;3468:11;3464:20;3457:63;3206:2;-1:-1:-1;2139:1515:27;;;;;;;:::o;897:51:22:-;;;;;;;;;;;;;;;:::o;15747:260:27:-;15856:20;939:6:56;;15856:20:27;;939:6:56;;938:7;917:72;;;;;;;;;;;;;;-1:-1:-1;;1043:6:56;:13;;;;1052:4;1043:13;;;15879::27;;;15902:99;15922:17;;;15902:99;;15960:30;15980:6;15987:1;15980:9;;;;;;;;;;;;;;;;;;15960:19;:30::i;:::-;15941:3;;15902:99;;;-1:-1:-1;;1165:5:56;1156:14;;;;;;-1:-1:-1;15747:260:27:o;5260:659::-;5473:35;;:::i;:::-;5524:20;5575:9;5628:36;;:::i;:::-;939:6:56;;;;938:7;917:72;;;;;;;;;;;;;;1043:6;:13;;;;1052:4;1043:13;;;5547::27;;;-1:-1:-1;1043:6:56;-1:-1:-1;5570:310:27;5590:17;;;5570:310;;5667:137;5708:6;5715:1;5708:9;;;;;;;;;;;;;;;;;;5735:21;5757:1;5735:24;;;;;;;;;;;;;;;;;;5777:10;5788:1;5777:13;;;;;;;;;;;;;;;;;;5667:23;:137::i;:::-;5628:176;;5818:51;5833:16;5851:17;5818:14;:51::i;:::-;5609:3;;;;;5570:310;;2018:1079:23;2122:20;939:6:56;;2122:20:23;;;;;;939:6:56;;938:7;917:72;;;;;;;;;;;;;;1043:6;:13;;;;1052:4;1043:13;;;2145:26:23;:24;:26::i;:::-;2122:49;-1:-1:-1;2453:26:23;;;2469:10;2453:26;:52;;2495:10;2453:52;;;2490:1;2453:52;2673:24;;;;;;;;:10;:24;;;;;;;;:39;;;;;;;;;;2429:76;;-1:-1:-1;2636:1:23;2617:20;;;-1:-1:-1;2673:39:23;-1:-1:-1;2801:29:23;;;2780:100;;;;;;;;;;;;;;2920:24;;;;;;;;:10;:24;;;;;;;;:39;;;;;;;;;;;;;;:55;;;2990:100;;;;;2962:13;;2990:100;;;;;;;;;;-1:-1:-1;;1165:5:56;1156:14;;;;;;-1:-1:-1;;;2018:1079:23:o;6478:632:27:-;6671:35;;:::i;:::-;6722:20;6773:9;6826:36;;:::i;:::-;6745:6;:13;6722:36;;6785:1;6773:13;;6768:303;6788:17;;;6768:303;;6865:130;6899:6;6906:1;6899:9;;;;;;;;;;;;;;;;;;6926:21;6948:1;6926:24;;;;;;;;;;;;;;;;;;6968:10;6979:1;6968:13;;;;;;;;;;;;;;;;;;6865:16;:130::i;:::-;6826:169;;7009:51;7024:16;7042:17;7009:14;:51::i;:::-;6807:3;;;;;6768:303;;;6478:632;;;;;;;;:::o;2013:154:22:-;2134:26;;;2104:7;2134:26;;;:12;:26;;;;;;;;2013:154;;;;:::o;967:45:26:-;;;;;;;;;;;;;;;:::o;1357:395:27:-;1548:30;;:::i;:::-;939:6:56;;;;938:7;917:72;;;;;;;;;;;;;;1043:6;:13;;;;1052:4;1043:13;;;1608:109:27;1645:5;1664:20;1698:9;1608:23;:109::i;:::-;1165:5:56;1156:14;;;;;;1594:123:27;1357:395;-1:-1:-1;;;;1357:395:27:o;2254:412:25:-;2408:21;939:6:56;;;;938:7;917:72;;;;;;;;;;;;;;1043:6;:13;;;;1052:4;1043:13;;;2432:26:25;:24;:26::i;:::-;2468:32;;;;;;;;:17;:32;;;;;;;;:50;;;;;;;;;;;;;;:61;;;;;;;;;;2544:115;2468:32;;-1:-1:-1;2468:50:25;;2544:115;;;;2468:61;;2544:115;;;;;;;;;;-1:-1:-1;;1165:5:56;1156:14;;;;;;-1:-1:-1;2254:412:25:o;1166:71::-;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;7498:1507:27:-;7696:35;;:::i;:::-;7747:27;7816:20;7867:9;8253:37;8444:36;;:::i;:::-;939:6:56;;;;938:7;917:72;;;;;;;;;;;;;;1043:6;:13;;;;1052:4;1043:13;;;7777:9:27;;:6;;1043::56;7777:9:27;;;;;;;;;;;;;;:24;;;7747:54;;7839:6;:13;7816:36;;7879:1;7867:13;;7862:1104;7882:17;;;7862:1104;;8156:14;8129:6;8136:1;8129:9;;;;;;;;;;;;;;;;;;:24;;:41;;;;8293:70;8301:20;8323:16;:39;;;8293:7;:70::i;:::-;8253:110;;8483:136;8518:6;8525:1;8518:9;;;;;;;;;;;;;;;;;;8545:29;8592:10;8603:1;8592:13;;;;;;;;;8483:136;8444:175;;8704:51;8719:16;8737:17;8704:14;:51::i;:::-;8853:39;;;;:63;-1:-1:-1;8849:107:27;;8936:5;;8849:107;7901:3;;;;;7862:1104;;;-1:-1:-1;;1165:5:56;1156:14;;;;;;-1:-1:-1;7498:1507:27;;;-1:-1:-1;;;;;7498:1507:27:o;16201:419::-;16301:20;16344;16390:38;16484:9;16367:6;:13;16344:36;;16456:12;16431:38;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;16390:79;;16496:1;16484:13;;16479:108;16499:17;;;16479:108;;16553:23;16566:6;16573:1;16566:9;;;;;;;;;;;;;;;;;;16553:12;:23::i;:::-;16537:10;16548:1;16537:13;;;;;;;;;;;;;;;;;;:39;16518:3;;16479:108;;1046:63:25;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;91:20:55:-;;;;;;:::o;3038:4848:25:-;3200:12;3390:22;3631:27;3752:7;3769:9;3788;3807:17;6871:24;3268:1;3249:9;:16;:20;3228:97;;;;;;;;;;;;;;;;3421:23;:9;:21;:23::i;:::-;3415:30;;;;-1:-1:-1;3543:29:25;3518:55;;;;3497:123;;;;;;;;;;;;;;3675:16;3661:31;;;;;;;;;;3631:61;-1:-1:-1;4174:21:25;4157:13;:38;;;;;;;;;4153:3383;;;4211:27;;;;;;;;;;;4153:3383;4520:21;4503:13;:38;;;;;;;;;4499:3037;;;4582:16;;:21;4557:97;;;;;;;;;;;;;;4678:5;;-1:-1:-1;4697:14:25;;4499:3037;4784:20;4767:13;:37;;;;;;;;;4763:2773;;;4845:16;;4865:2;4845:22;4820:99;;;;;;;;;;;;;;4943:9;4953:1;4943:12;;;;;;;;;;;;;;;;;;;;;4937:19;;-1:-1:-1;4974:24:25;:9;4996:1;4974:24;:21;:24;:::i;:::-;4970:28;-1:-1:-1;5016:25:25;:9;5038:2;5016:25;:21;:25;:::i;:::-;5012:29;;5067:102;5094:4;5116:1;5135;5154;5067:102;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;5067:102:25;;;;;5193:26;;;;;;;;;-1:-1:-1;5067:102:25;-1:-1:-1;5233:14:25;;-1:-1:-1;5233:14:25;4763:2773;5324:21;5307:13;:38;;;;;;;;;5303:2233;;;5386:16;;5406:2;5386:22;5361:99;;;;;;;;;;;;;;5484:9;5494:1;5484:12;;;;;;;;;;;;;;;;;;;;;5478:19;;-1:-1:-1;5515:24:25;:9;5537:1;5515:24;:21;:24;:::i;:::-;5511:28;-1:-1:-1;5557:25:25;:9;5579:2;5557:25;:21;:25;:::i;:::-;5553:29;;5608:225;5739:4;5645:116;;;;;;;;;;;;;;;;;;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;5645:116:25;;;5635:127;;;;;;;;;;;;;36:153:-1;66:2;58:11;;36:153;;176:10;;164:23;;139:12;;;;;98:2;89:12;;;;114;36:153;;;299:10;344;;263:2;259:12;;;254:3;250:22;246:30;;311:9;;295:26;;;340:21;;377:20;365:33;;5635:127:25;;;;;;;;;;;;-1:-1:-1;5608:225:25;;;;;;;;;;-1:-1:-1;5635:127:25;-1:-1:-1;5780:1:25;;5799;;-1:-1:-1;5818:1:25;;5608:225;;5303:2233;6081:20;6064:13;:37;;;;;;;;;6060:1476;;;6127:116;6167:4;6189:13;6220:9;6127:22;:116::i;:::-;6117:126;-1:-1:-1;6257:14:25;;6060:1476;6770:23;6753:13;:40;;;;;;;;;6749:787;;;6898:26;:9;:24;:26::i;:::-;7009:32;;;;;;;;:17;:32;;;;;;;;:50;;;;;;;;;;6871:53;;-1:-1:-1;7009:50:25;;7008:51;7004:102;;;7086:5;7079:12;;;;7004:102;7129:153;7172:16;7206:4;7228:13;7259:9;7129:25;:153::i;6749:787::-;7418:23;7401:13;:40;;;;;;;;;7397:139;;;7467:15;;;;:9;:15;;;;;;;;:30;;;;;;;;;;;;;;-1:-1:-1;7511:14:25;;7397:139;7848:31;;;;;;;;;;;3038:4848;;;;;;;;;;;;;:::o;13713:1899:27:-;13896:35;;:::i;:::-;13947:27;14012:20;14063:9;14453:37;14759;15050:36;;:::i;:::-;13977:6;13984:1;13977:9;;;;;;;;;;;;;;;;;;:24;;;13947:54;;14035:6;:13;14012:36;;14075:1;14063:13;;14058:1515;14078:17;;;14058:1515;;14357:14;14330:6;14337:1;14330:9;;;;;;;;;;;;;;;;;;;:24;;:41;14523:39;;14493:70;;14501:20;;14493:7;:70::i;:::-;14453:110;;14799:170;14838:6;14845:1;14838:9;;;;;;;;;;;;;;;;;;:26;;;14882:6;14889:1;14882:9;;;;;;;;;;;;;;;;;;:26;;;14926:29;14799:21;:170::i;:::-;14759:210;;15089:135;15123:6;15130:1;15123:9;;;;;;;;;;;;;;;;;;15150:29;15197:10;15208:1;15197:13;;;;;;;;;15089:135;15050:174;;15309:51;15324:16;15342:17;15309:14;:51::i;:::-;15460:39;;:63;-1:-1:-1;15456:107:27;;15543:5;;15456:107;14097:3;;;;;14058:1515;;;13713:1899;;;;;;;;;;;:::o;3414:374:23:-;3590:30;;:::i;:::-;939:6:56;;;;938:7;917:72;;;;;;;;;;;;;;1043:6;:13;;;;1052:4;1043:13;;;3650:103:23;3681:5;3700:20;3734:9;3650:17;:103::i;1426:1456:26:-;1640:21;;1730:23;;1640:35;:21;:35;1619:100;;;;;;;;;;;;;;1756:113;1774:94;1809:4;1827:13;1854:4;;1774:94;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1774:21:26;;-1:-1:-1;;;;;1774:94:26:i;:::-;1756:17;:113::i;:::-;1956:29;;;;:12;:29;;;;;;1730:139;;-1:-1:-1;1956:29:26;;1955:30;1934:92;;;;;;;;;;;;;;2112:27;;;2129:10;2112:27;2108:410;;2214:137;2252:15;2289:13;2324:9;;2214:137;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;2214:16:26;;-1:-1:-1;;;;;2214:137:26:i;:::-;2189:216;;;;;;;;;;;;;;;;2470:21;:37;;;;;;;;;;2108:410;2559:29;;;;:12;:29;;;;;;;:36;;;;2591:4;2559:36;;;2626:32;2634:4;;2653;;;;2626:32;2653:4;;;;2626:32;;;;;;;;;;;;;;;;;;;;;;;;2605:95;;;;;;;;;;;;;;;;2788:27;;;2805:10;2788:27;2784:92;;2831:21;:34;;;;;;2784:92;1426:1456;;;;;;;:::o;1157:666:22:-;259:5:55;;1254:30:22;;;;;;259:5:55;;245:10;:19;224:85;;;;;;;;;;;;;;1299:10:22;1254:56;;1405:18;:29;;;:31;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1405:31:22;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;1405:31:22;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;1405:31:22;;;;;;;;;1474:26;;;;;;;:12;:26;;;;;;1383:53;;-1:-1:-1;1474:26:22;;;-1:-1:-1;1531:31:22;;1510:104;;;;;;;;;;;;;;1674:26;;;;;;;:12;:26;;;;;;;:47;;;;;;;;;;1736:80;;;;;1674:26;;1796:10;;1736:80;;;;;;;;;;1157:666;;;;:::o;4417:2261:23:-;4504:26;;:::i;:::-;4602:19;4615:5;4602:12;:19::i;:::-;4580;;;;:41;;;;4703:27;;;:6;:27;;;;;;;;4663:37;;;:67;5032:22;;;;:27;5028:157;;;5105:38;5099:45;5075:69;;;;5158:16;;5028:157;5498:22;;;;:27;5494:157;;;5571:38;5565:45;;5494:157;5745:22;;;;5704:37;;;;:63;5700:179;;5813:24;5807:31;;5700:179;6003:27;;;;5984:15;:46;5980:157;;6076:19;6070:26;;5980:157;6206:19;;;;;6196:30;;;;:9;:30;;;;;;;;;6192:143;;;6272:21;6266:28;;6192:143;6402:10;;;;6359:18;;6348:30;;;;;;;;:10;:30;;;;;;;;6379:19;;;;6348:51;;;;;;;;;;:64;6344:177;;;6458:21;6452:28;;6344:177;6624:20;6594:51;;;4417:2261;-1:-1:-1;4417:2261:23:o;4019:124::-;939:6:56;;;;938:7;917:72;;;;;;;;;;;;;;1043:6;:13;;;;1052:4;1043:13;;;4110:26:23;4130:5;4110:19;:26::i;:::-;-1:-1:-1;1165:5:56;1156:14;;;;;;4019:124:23:o;1627:67::-;;;;;;;;;;;;;;;;;;;;;;;;:::o;1528:185:38:-;;;;;;;;;;;;;;;;;;;;;;;;;:::o;9462:1488:27:-;9646:35;;:::i;:::-;9697:27;9762:20;9813:9;10199:37;10390:36;;:::i;:::-;9727:6;9734:1;9727:9;;;;;;;;;;;;;;;;;;:24;;;9697:54;;9785:6;:13;9762:36;;9825:1;9813:13;;9808:1103;9828:17;;;9808:1103;;10102:14;10075:6;10082:1;10075:9;;;;;;;;;;;;;;;;;;:24;;:41;;;;10239:70;10247:20;10269:16;:39;;;10239:7;:70::i;:::-;10199:110;;10429:135;10463:6;10470:1;10463:9;;;;;;;;;;;;;;;;;;10490:29;10537:10;10548:1;10537:13;;;;;;;;;10429:135;10390:174;;10649:51;10664:16;10682:17;10649:14;:51::i;:::-;10798:39;;;;:63;-1:-1:-1;10794:107:27;;10881:5;;10794:107;9847:3;;;;;9808:1103;;;9462:1488;;;;;;;;;;:::o;1301:33:39:-;;;;:::o;11338:1914:27:-;11535:35;;:::i;:::-;11586:27;11651:20;11702:9;12092:37;12398;12689:36;;:::i;:::-;939:6:56;;;;938:7;917:72;;;;;;;;;;;;;;1043:6;:13;;;;1052:4;1043:13;;;11616:9:27;;:6;;1043::56;11616:9:27;;;;;;;;;;;;;;:24;;;11586:54;;11674:6;:13;11651:36;;11714:1;11702:13;;11697:1516;11717:17;;;11697:1516;;11996:14;11969:6;11976:1;11969:9;;;;;;;;;;;;;;;;;;;:24;;:41;12162:39;;12132:70;;12140:20;;12132:7;:70::i;:::-;12092:110;;12438:170;12477:6;12484:1;12477:9;;;;;;;;;12438:170;12398:210;;12728:136;12763:6;12770:1;12763:9;;;;;;;;;;;;;;;;;;12790:29;12837:10;12848:1;12837:13;;;;;;;;;12728:136;12689:175;;12949:51;12964:16;12982:17;12949:14;:51::i;:::-;13100:39;;:63;-1:-1:-1;13096:107:27;;13183:5;;13096:107;11736:3;;;;;11697:1516;;;-1:-1:-1;;1165:5:56;1156:14;;;;;;-1:-1:-1;11338:1914:27;;;-1:-1:-1;;;;;;11338:1914:27:o;1064:36:26:-;;;;;;:::o;333:167:55:-;259:5;;;;245:10;:19;224:85;;;;;;;;;;;;;;429:22;;;;425:69;;467:5;:16;;;;;;;;;;425:69;333:167;:::o;1078:40:21:-;;;;;;;;;;;;;;;;;;;:::o;6995:1583:23:-;7160:30;;:::i;:::-;7234:26;;:::i;:::-;7324:20;7629:33;7745:30;7263:19;7276:5;7263:12;:19::i;:::-;7234:48;;7347:26;:24;:26::i;:::-;7324:49;;7446:120;7479:5;7498:9;7521:12;7547:9;7446:19;:120::i;:::-;7665:70;7673:5;:22;;;7697:9;:37;;;7665:7;:70::i;:::-;7629:106;;7778:55;7785:20;7807:25;7778:6;:55::i;:::-;7745:88;;7872:185;7901:5;7920:9;7943:20;7977:22;8013:11;:34;;;7872:15;:185::i;:::-;8127:51;8148:5;8155:22;8127:20;:51::i;:::-;8113:65;;8231:181;8262:5;8281:12;8307:9;:19;;;8340:9;:37;;;8391:11;8231:17;:181::i;:::-;8451:91;8476:5;8495:12;8521:11;8451;:91::i;:::-;6995:1583;;;;;;;;;:::o;1771:648:41:-;1964:39;;2005:40;;1956:90;;1964:39;1956:7;:90::i;:::-;1914:132;;2106:39;;;;;2147:40;;;;2098:90;;2106:39;2098:7;:90::i;:::-;2056:39;;;:132;2238:29;;;;;2269:30;;;;2230:70;;2238:29;2230:7;:70::i;:::-;2198:29;;;:102;2350:29;;;;;2381:30;;;;2342:70;;2350:29;2342:7;:70::i;:::-;2310:29;;;;:102;;;;-1:-1:-1;1771:648:41:o;4841:307:26:-;4980:21;;4924:7;;4980:21;;4924:7;5036:36;;:74;;5088:22;5036:74;;;5075:10;5036:74;5011:99;4841:307;-1:-1:-1;;;4841:307:26:o;11221:1268:23:-;11513:21;;:52;;11544:20;11513:52;11492:115;;;;;;;;;;;;;;11687:19;;;;:33;;;11683:170;;11761:19;;;;:33;;11784:10;11761:33;11736:106;;;;;;;;;;;;;;11931:18;;;;:32;;;11927:169;;12026:12;12004:34;;:5;:18;;;:34;;;11979:106;;;;;;;;;;;;;;;;12186:37;;;;:42;12182:301;;;12269:146;12307:9;:19;;;12348:5;:18;;;12388:9;12269:16;:146::i;:::-;12244:228;;;;;;;;;;;;;;;;11221:1268;;;;:::o;4931:1075:24:-;5885:64;5893:9;:26;;;5921:10;:27;;;5885:7;:64::i;:::-;5805;5813:9;:26;;;5841:10;:27;;;5805:7;:64::i;:::-;:144;;5784:215;;;;;;;;;;;;;;4931:1075;;:::o;6742:4784::-;7014:59;;:::i;:::-;7185:37;7303;7511:38;7632;7225:68;7233:9;:26;;;7261:31;7225:7;:68::i;:::-;7185:108;;7343:158;7382:9;:26;;;7422:9;:26;;;7462:29;7343:25;:158::i;:::-;7303:198;;7552:70;7560:10;:27;;;7589:32;7552:7;:70::i;:::-;7511:111;;7673:161;7712:10;:27;;;7753:10;:27;;;7794:30;7673:25;:161::i;:::-;7632:202;-1:-1:-1;8592:63:24;;;8588:1672;;8722:24;;;;;;:80;;;8816:24;;:47;;:80;;;8959:24;:47;8910:23;;:46;;:96;9299:26;;;;9343;;;;9387:23;;:46;;;;9256:191;;9343:26;9256:25;:191::i;:::-;9207:23;;:240;8588:1672;;;9528:23;;:78;;;9620:23;;:46;;;;:78;;;9762:23;;:46;;;9712:24;;;;;:96;;;;10098:27;;;;10143;;;;10188:24;;:47;10056:193;;10143:27;10056:24;:193::i;:::-;10006:24;;;;;:47;:243;8588:1672;10382:23;;:46;10442:24;;;;;:47;;10361:138;;10382:46;10361:7;:138::i;:::-;10313:45;;;:186;10627:23;;:46;10687:26;;;;10727:18;;;;10588:167;;10627:46;10687:26;10588:25;:167::i;:::-;10549:23;;:36;;:206;10843:23;;:46;;;10903:26;;;;10943:18;;;;10804:167;;10843:46;10903:26;10804:25;:167::i;:::-;10765:23;;:36;;:206;11101:24;;;;:47;11162:27;;;;11203:19;;;;11062:170;;11101:47;11162:27;11062:25;:170::i;:::-;11022:18;:24;;;:37;;:210;;;;;11282:170;11321:18;:24;;;:47;;;11382:10;:27;;;11423:10;:19;;;11282:25;:170::i;:::-;11242:24;;;;:37;;:210;6742:4784;;;;;;;;;;:::o;12927:2440:23:-;13322:25;;;13301:92;;;;;;;;;;;;;;13621:46;;;;13600:106;;;;;;;;;;;;;;13988:5;:22;;;13914:70;13922:9;:37;;;13961:22;13914:7;:70::i;:::-;:96;;13893:157;;;;;;;;;;;;;;15261:55;15269:5;:22;;;15293;15261:7;:55::i;:::-;15177;15185:22;15209:5;:22;;;15177:7;:55::i;:::-;:139;;15156:204;;;;;;;;;;;;;;12927:2440;;;;;:::o;9384:792::-;9662:72;9670:27;9699:11;:34;;;9662:7;:72::i;:::-;9642:17;;;;:6;:17;;;;;;;;;:92;;;;9821:25;;;;9789:18;;9910:34;;9958;;;;10006:24;;;;10044;;;;10105:20;;;;10139;;;;9771:398;;9642:17;;9771:398;;;;;;;;;;;;;;9860:12;;9886:10;;9910:34;;9958;;10006:24;;10139:20;9771:398;;;;;;;;;;9384:792;;;;;:::o;11972:2233:24:-;12219:25;12247:14;;;;;;;;;;;;;;;;;;;;;;;12219:42;;12305:202;12339:9;:24;;;12377:9;:22;;;12413:10;:23;;;12450:18;:24;;;:47;;;12305:20;:202::i;:::-;12551:25;;;;12590:23;;12627:22;;12663:23;;:46;;;12517:202;;12551:25;12590:23;12627:22;12517:20;:202::i;:::-;12729:189;12763:9;:24;;;12801:9;:22;;;12837:12;12863:18;:45;;;12729:20;:189::i;:::-;12951:185;12985:12;13011:9;:22;;;13047:9;:29;;;13090:18;:23;;;:36;;;12951:20;:185::i;:::-;13146:188;13180:12;13206:10;:23;;;13243:10;:30;;;13287:18;:24;;;:37;;;13146:20;:188::i;:::-;13404:10;:30;;;13371:63;;:9;:29;;;:63;;;13367:832;;;13450:301;13488:12;13518;13548:9;:29;;;13595:142;13624:18;:23;;;:36;;;13682:18;:24;;;:37;;;13595:7;:142::i;:::-;13450:20;:301::i;:::-;13367:832;;;13782:195;13820:12;13850;13880:9;:29;;;13927:18;:23;;;:36;;;13782:20;:195::i;:::-;13991:197;14029:12;14059;14089:10;:30;;;14137:18;:24;;;:37;;;13991:20;:197::i;1011:10099:37:-;5031:4;5025:11;;5321:66;5314:4;5291:28;;;5284:104;;;;1199:30;5445:4;5422:28;;6444:59;;;6770:19;;5736:26;;;6750:40;;;6883:23;;;6877:30;6853:22;;;6846:62;6979:23;;;6973:30;6949:22;;;6942:62;7082:23;;;7076:30;7052:22;;;7045:62;7197:4;7179:23;;7173:30;7149:22;;;7142:62;7297:4;7279:23;;7273:30;7249:22;;;7242:62;7397:4;7379:23;;7373:30;7349:22;;;7342:62;7495:4;7477:23;;7471:30;7447:22;;;7440:62;7594:5;7576:24;;7570:31;7545:23;;;7538:64;7699:5;7681:24;;7675:31;7650:23;;;7643:64;7787:5;7769:24;;7763:31;;7738:23;;;7731:64;;;7895:5;7877:24;;7871:31;7846:23;;;7839:64;7979:5;8106:74;;8268:24;8340:19;;7962:23;;;8544:34;;;5025:11;;5422:28;;;;6536:27;;;;5736:26;;8606:22;;;;8388:23;;;;8464:4;8445:24;;8441:35;6190:1;8698:237;8721:13;8718:1;8715:20;8698:237;;;8791:19;;8771:40;;8860:4;8843:22;;;;8898:23;;;;8749:1;8742:9;8698:237;;;-1:-1:-1;;;;9045:31:37;;;9029:13;9010:33;;;9003:74;;;;9171:17;;9165:24;9237:19;;9441:34;;;9037:4;9503:22;;;;9285:23;;;;9361:4;9342:24;;9338:35;9610:1;9596:237;9619:13;9616:1;9613:20;9596:237;;;9689:19;;9669:40;;9758:4;9741:22;;;;9796:23;;;;9647:1;9640:9;9596:237;;;-1:-1:-1;;;9902:46:37;;;-1:-1:-1;10133:33:37;;;10003:4;9981:27;;;10108:59;;;10281:19;;10472:34;;;9981:27;;10534:22;;;;10329:23;;;;10281:19;10405:4;10386:24;;10382:35;-1:-1:-1;10613:237:37;10636:13;10633:1;10630:20;10613:237;;;10706:19;;10686:40;;10775:4;10758:22;;;;10813:23;;;;10664:1;10657:9;10613:237;;;-1:-1:-1;;;10928:46:37;;;;;10902:73;;-1:-1:-1;11041:4:37;11034:25;-1:-1:-1;;;;;1011:10099:37;;;;;:::o;8809:332:23:-;8927:26;;:::i;:::-;8956:19;8969:5;8956:12;:19::i;:::-;8927:48;;9014:35;9032:5;9039:9;9014:17;:35::i;:::-;9086:48;9107:5;9114:9;:19;;;9086:20;:48::i;16922:513:27:-;17102:30;;:::i;:::-;17162:103;17193:5;17212:20;17246:9;17162:17;:103::i;:::-;17296:34;;;;17148:117;;-1:-1:-1;17296:58:27;;17275:125;;;;;;;;;;;;;502:208:57;588:7;632:6;;;;611:70;;;;;;;;;;;;;;-1:-1:-1;698:5:57;;;502:208;;;;;:::o;8313:448:53:-;8397:13;8458:1;8447;:8;:12;8426:92;;;;;;;;;;;;;;;;8568:8;;8566:1;;8568:12;;;;8566:15;;;;;;;;;;8680:8;;8676:16;;8705:17;;;-1:-1:-1;8566:15:53;;;;;;;8313:448::o;13290:490::-;13411:14;13474:5;13482:2;13474:10;13462:1;:8;:22;;13441:107;;;;;;;;;;;;;;;;-1:-1:-1;13727:13:53;13629:2;13727:13;13721:20;;13290:490::o;8292:1482:25:-;8507:135;;8455:12;;8483:21;;8543:48;;8507:135;;8605:4;;8623:9;;8507:135;;;;;;;;22:32:-1;26:21;;;22:32;6:49;;8507:135:25;;;49:4:-1;25:18;;;61:17;;8507:135:25;182:15:-1;8507:135:25;;;;179:29:-1;;;;160:49;;8943:15:25;;8507:135;;-1:-1:-1;49:4;25:18;;;8816:13:25;8763:3;8735:380;9136:7;9156:422;;;;9596:1;9591:143;;;;9129:605;;9156:422;9246:66;9243:1;9236:77;9341:66;9337:2;9330:78;9436:66;9432:2;9425:78;9531:1;9527:2;9520:13;9560:3;9557:1;9550:14;9591:143;-1:-1:-1;;9706:14:25;;8292:1482;-1:-1:-1;;;;;8292:1482:25:o;8956:482:53:-;9043:14;9106:2;9094:1;:8;:14;;9073:99;;;;;;;;;;;;;;;;9224:29;9236:1;9250:2;9239:1;:8;:13;9224:11;:29::i;:::-;9356:8;;9352:17;;9382;;;-1:-1:-1;9215:38:53;8956:482::o;10213:1561:25:-;10465:165;;10413:12;;10441:21;;10501:51;;10465:165;;10566:4;;10512:13;;10611:9;;10465:165;;;;;;;;22:32:-1;26:21;;;22:32;6:49;;10465:165:25;;;49:4:-1;25:18;;;61:17;;10465:165:25;182:15:-1;10465:165:25;;;;179:29:-1;;;;160:49;;10937:15:25;;10465:165;;-1:-1:-1;49:4;25:18;;;10805:16:25;10751:3;10723:389;11133:7;11153:425;;;;11596:1;11591:143;;;;11126:608;;11153:425;11246:66;11243:1;11236:77;11341:66;11337:2;11330:78;11436:66;11432:2;11425:78;11531:1;11527:2;11520:13;11560:3;11557:1;11550:14;11591:143;11712:7;11706:14;11695:25;;11126:608;-1:-1:-1;;;10213:1561:25;;;;;;;:::o;3123:425:42:-;3287:21;3345:15;;;3324:78;;;;;;;;;;;;;;3429:82;3450:26;3458:9;3469:6;3450:7;:26::i;:::-;3490:11;3429:7;:82::i;:::-;3413:98;3123:425;-1:-1:-1;;;;3123:425:42:o;3175:1222:26:-;827:146:48;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;26:21:-1;;;22:32;;6:49;;827:146:48;;;;;;;;817:157;;3339:14:26;;;;;;827:146:48;;;;817:157;;;;827:146;817:157;36:153:-1;66:2;58:11;;36:153;;176:10;;164:23;;139:12;;;;;98:2;89:12;;;;114;36:153;;;299:10;344;;263:2;259:12;;;254:3;250:22;246:30;;311:9;;295:26;;;340:21;;377:20;365:33;;817:157:48;;;;;;;;;;;3456:15:26;;817:157:48;;-1:-1:-1;3456:15:26;;-1:-1:-1;817:157:48;;-1:-1:-1;817:157:48;;3456:15:26;;;-1:-1:-1;3456:15:26;817:157:48;3456:15:26;36:153:-1;66:2;58:11;;36:153;;176:10;;164:23;;139:12;;;;;98:2;89:12;;;;114;36:153;;;299:10;344;;263:2;259:12;;;254:3;250:22;246:30;;311:9;;295:26;;;340:21;;377:20;365:33;;3456:15:26;;;;;;;;;;;;3834:26;;;3960:15;;3953:29;;;;-1:-1:-1;4105:42:26;4086:62;;;;4069:15;;;4062:87;;;;-1:-1:-1;;4199:2:26;4187:15;;4180:33;-1:-1:-1;;4354:3:26;4336:22;;;;3175:1222;-1:-1:-1;3175:1222:26:o;1834:924:39:-;1985:18;;2297:2;2291:9;2329:66;2314:82;;2446:1;2434:14;;2427:40;;;;2564:2;2552:15;;2545:35;;;;2716:2;2698:21;;;1834:924::o;4211:202:43:-;4300:17;4345:35;4363:16;4373:5;4363:9;:16::i;1371:135:57:-;1456:7;1490:1;1486;:5;:13;;1498:1;1486:13;;;-1:-1:-1;1494:1:57;;1479:20;-1:-1:-1;1371:135:57:o;16651:887:23:-;16802:30;;:::i;:::-;16897:34;;;:59;;;17078:22;;;;17114;;;;17003:143;;16934:22;;17003:25;:143::i;:::-;16966:180;;;17270:22;;;;17306:14;;;;17183:147;;16966:180;17270:22;17183:25;:147::i;:::-;17156:24;;;:174;17442:22;;;;17478:14;;;;17367:135;;17406:22;;17367:25;:135::i;:::-;17340:24;;;:162;16651:887;;;;:::o;17848:932::-;18055:14;;;18027:25;18055:14;;;;;;;;;;;;;;;;;;;;18113:20;;;;18147:18;;18205:34;;18079:170;;18113:20;18147:18;18179:12;;18079:20;:170::i;:::-;18259;18293:5;:20;;;18327:12;18353:5;:18;;;18385:11;:34;;;18259:20;:170::i;:::-;18439:165;18473:12;18499:5;:18;;;18531:5;:25;;;18570:11;:24;;;18439:20;:165::i;:::-;18614:159;18648:12;18674;18700:5;:25;;;18739:11;:24;;;18614:20;:159::i;716:230:57:-;802:7;837:5;;;873:6;;;;852:69;;;;;;;;;;;;;;938:1;931:8;;716:230;;;;;;:::o;51:288::-;137:7;;164:6;;160:45;;;193:1;186:8;;;;160:45;-1:-1:-1;226:5:57;;;230:1;226;:5;262;;;;;;;;:10;241:73;;;;;;;;;;;;;1012:624:42;1180:21;1238:15;;;1217:78;;;;;;;;;;;;;;1328:114;1366:9;1393:11;1422:6;1328:20;:114::i;:::-;1327:115;1306:176;;;;;;;;;;;;;1959:889;2126:21;2184:15;;;2163:78;;;;;;;;;;;;;;2274:113;2311:9;2338:11;2367:6;2274:19;:113::i;:::-;2273:114;2252:175;;;;;;;;;;;;;;2649:162;2670:106;2695:26;2703:9;2714:6;2695:7;:26::i;:::-;2739:23;2747:11;2760:1;2739:7;:23::i;:::-;2670:7;:106::i;2482:5147:22:-;3031:19;3290:18;2718:1;2709:6;:10;:24;;;;;2731:2;2723:10;;:4;:10;;;;2709:24;2705:4918;;;2822:16;;2841:1;-1:-1:-1;2797:109:22;;;;;;;;;;;;;;-1:-1:-1;;3153:2:22;3138:18;;;3111:46;3179:66;3107:156;3311:26;;;;:12;:26;;;;;;;;;;3422:24;;;3397:109;;;;;;;;;;;;;;4923:2;4917:9;5239:15;5234:2;5222:9;5216:16;5212:25;5208:47;5392:14;5387:3;5383:24;5374:7;5370:38;5676:66;5667:7;5660:83;6160:3;6156:1;6147:7;6143:15;6136:28;6216:42;6210:4;6206:53;6201:2;6192:7;6188:16;6181:79;6310:42;6306:2;6302:51;6297:2;6288:7;6284:16;6277:77;6397:6;6391:3;6382:7;6378:17;6371:33;6563:3;6554:7;6550:17;6645:206;6665:5;6655:8;6652:19;6645:206;;;6714:16;;6697:34;;6778:2;6815:18;;;;6764:17;6645:206;;;7417:3;7346:7;7292;7285:5;7281:19;7208:7;7142:1;7067:10;7004:3;6978:512;7517:7;7510:15;7507:2;;;7564:16;7555:7;7548:33;7507:2;4724:2889;;;;;;2482:5147;;;;;;:::o;15564:816:23:-;15828:21;;16220:20;;15828:52;;15859:20;15828:52;15807:115;;;;;;;;;;;;;;15996:19;;;;:33;;;15992:170;;16070:19;;;;:33;;16093:10;16070:33;16045:106;;;;;;;;;;;;;;16243:26;:24;:26::i;:::-;16300:18;;16220:49;;-1:-1:-1;16300:34:23;;;;;;;16279:94;;;;;;;;;;;;;;15564:816;;;:::o;10478:422::-;10626:20;;;;:9;:20;;;;;;;:27;;;;10649:4;10626:27;;;10743:25;;;;10711:18;;10829:20;;;;10863;;;;10691:202;;10636:9;;10691:202;;;;;;;;;;;;;;10782:10;;10863:20;10691:202;;;;;;;;;;10478:422;;:::o;10268:886:53:-;10389:14;10452:5;10460:2;10452:10;10440:1;:8;:22;;10419:135;;;;;;;;;;;;;;;;-1:-1:-1;11056:13:53;10801:2;11056:13;11050:20;11072:42;11046:69;;10268:886::o;345:151:57:-;431:7;454:9;470:1;466;:5;;;;;;;;;345:151;-1:-1:-1;;;;345:151:57:o;4553:1678:43:-;777:457;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;22:32:-1;26:21;;;22:32;6:49;;777:457:43;;;;;;;;767:468;;4639:14;;;;;;;;777:457;;;;;767:468;;;;777:457;767:468;36:153:-1;66:2;58:11;;36:153;;176:10;;164:23;;139:12;;;;;98:2;89:12;;;;114;36:153;;;299:10;344;;263:2;259:12;;;254:3;250:22;246:30;;311:9;;295:26;;;340:21;;377:20;365:33;;767:468:43;;;;;;;;;;;4763:20;;;;4753:31;;767:468;;-1:-1:-1;4763:20:43;-1:-1:-1;767:468:43;;-1:-1:-1;767:468:43;;4753:31;;;-1:-1:-1;4753:31:43;767:468;4753:31;36:153:-1;66:2;58:11;;36:153;;176:10;;164:23;;139:12;;;;;98:2;89:12;;;;114;36:153;;;299:10;344;;263:2;259:12;;;254:3;250:22;246:30;;311:9;;295:26;;;340:21;;377:20;365:33;;4753:31:43;;;;;;;;;;;4833:20;;;;4823:31;;4753;;-1:-1:-1;4833:20:43;-1:-1:-1;4753:31:43;;-1:-1:-1;4753:31:43;;4823;;;-1:-1:-1;4823:31:43;4753;4823;36:153:-1;66:2;58:11;;36:153;;176:10;;164:23;;139:12;;;;;98:2;89:12;;;;114;36:153;;;299:10;344;;263:2;259:12;;;;254:3;250:22;246:30;;311:9;;295:26;;;340:21;;377:20;;;;365:33;;4823:31:43;;;;;;;;;;5620:14;;;5763:11;;5670:3;5659:15;;5800:11;;5710:3;5699:15;;;5837:11;;5903:24;;;5940:32;;;5985;;;6056:3;6040:20;;6109:19;;6141;;;-1:-1:-1;;;6173:19:43;;;-1:-1:-1;6040:20:43;;4553:1678;-1:-1:-1;4553:1678:43:o;4779:1869:42:-;4942:12;;4991:15;;;4970:78;;;;;;;;;;;;;;5868:11;;;:29;;-1:-1:-1;5883:14:42;;5868:29;5864:72;;;5920:5;5913:12;;;;5864:72;6522:11;6459:84;;;;;;;6499:9;6479:6;6459:84;6439:104;;6591:26;6599:9;6610:6;6591:7;:26::i;:::-;6563:24;6571:4;6577:9;6563:7;:24::i;:::-;:54;;;4779:1869;-1:-1:-1;;;;;4779:1869:42:o;6909:918::-;7071:12;;7120:15;;;7099:78;;;;;;;;;;;;;;7250:11;;;:29;;-1:-1:-1;7265:14:42;;7250:29;7246:257;;;7487:5;7480:12;;;;7246:257;7634:11;7571:84;;;;;;;7611:9;7591:6;7571:84;7551:104;;7711:11;7677:31;7685:11;7698:9;7677:7;:31::i;:::-;:45;;;;;;;;7665:57;;7770:26;7778:9;7789:6;7770:7;:26::i;897:514:21:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;;:::o;:::-;;;;;;;;;-1:-1:-1;897:514:21;;;;;;;;;;;;;;;;;:::o;5:118:-1:-;;72:46;110:6;97:20;72:46;;146:693;;261:4;249:17;;245:27;-1:-1;235:2;;286:1;283;276:12;235:2;323:6;310:20;345:85;360:69;422:6;360:69;;;345:85;;;458:21;;;502:4;490:17;;;;336:94;;-1:-1;515:14;;490:17;610:1;595:238;620:6;617:1;614:13;595:238;;;703:3;690:17;682:6;678:30;727:42;765:3;753:10;727:42;;;715:55;;-1:-1;793:4;784:14;;;;812;;;;;642:1;635:9;595:238;;;599:14;228:611;;;;;;;;879:735;;1008:4;996:17;;992:27;-1:-1;982:2;;1033:1;1030;1023:12;982:2;1070:6;1057:20;1092:99;1107:83;1183:6;1107:83;;1092:99;1219:21;;;1263:4;1251:17;;;;1083:108;;-1:-1;1276:14;;1251:17;1371:1;1356:252;1381:6;1378:1;1375:13;1356:252;;;1464:3;1451:17;1443:6;1439:30;1488:56;1540:3;1528:10;1488:56;;;1476:69;;-1:-1;1568:4;1559:14;;;;1587;;;;;1403:1;1396:9;1356:252;;1640:707;;1750:4;1738:17;;1734:27;-1:-1;1724:2;;1775:1;1772;1765:12;1724:2;1812:6;1799:20;1834:80;1849:64;1906:6;1849:64;;1834:80;1825:89;;1931:5;1956:6;1949:5;1942:21;1986:4;1978:6;1974:17;1964:27;;2008:4;2003:3;1999:14;1992:21;;2061:6;2108:3;2100:4;2092:6;2088:17;2083:3;2079:27;2076:36;2073:2;;;2125:1;2122;2115:12;2073:2;2150:1;2135:206;2160:6;2157:1;2154:13;2135:206;;;2218:3;2240:37;2273:3;2261:10;2240:37;;;2228:50;;-1:-1;2301:4;2292:14;;;;2320;;;;;2182:1;2175:9;2135:206;;2355:112;;2419:43;2454:6;2441:20;2419:43;;2474:118;;2541:46;2579:6;2566:20;2541:46;;2599:116;;2665:45;2702:6;2689:20;2665:45;;2722:120;;2799:38;2829:6;2823:13;2799:38;;2863:335;;;2970:4;2958:17;;2954:27;-1:-1;2944:2;;2995:1;2992;2985:12;2944:2;-1:-1;3015:20;;3055:18;3044:30;;3041:2;;;3087:1;3084;3077:12;3041:2;3121:4;3113:6;3109:17;3097:29;;3171:3;3164;3156:6;3152:16;3142:8;3138:31;3135:40;3132:2;;;3188:1;3185;3178:12;3132:2;2937:261;;;;;;3207:432;;3297:4;3285:17;;3281:27;-1:-1;3271:2;;3322:1;3319;3312:12;3271:2;3359:6;3346:20;3381:60;3396:44;3433:6;3396:44;;3381:60;3372:69;;3461:6;3454:5;3447:21;3497:4;3489:6;3485:17;3530:4;3523:5;3519:16;3565:3;3556:6;3551:3;3547:16;3544:25;3541:2;;;3582:1;3579;3572:12;3541:2;3592:41;3626:6;3621:3;3616;3592:41;;;3264:375;;;;;;;;4124:2205;;4232:5;4220:9;4215:3;4211:19;4207:31;4204:2;;;4251:1;4248;4241:12;4204:2;4269:21;4284:5;4269:21;;;4260:30;-1:-1;4348:1;4379:49;4424:3;4404:9;4379:49;;;4355:74;;-1:-1;4498:2;4531:49;4576:3;4552:22;;;4531:49;;;4524:4;4517:5;4513:16;4506:75;4450:142;4657:2;4690:49;4735:3;4726:6;4715:9;4711:22;4690:49;;;4683:4;4676:5;4672:16;4665:75;4602:149;4810:2;4843:49;4888:3;4879:6;4868:9;4864:22;4843:49;;;4836:4;4829:5;4825:16;4818:75;4761:143;4966:3;5000:49;5045:3;5036:6;5025:9;5021:22;5000:49;;;4993:4;4986:5;4982:16;4975:75;4914:147;5123:3;5157:49;5202:3;5193:6;5182:9;5178:22;5157:49;;;5150:4;5143:5;5139:16;5132:75;5071:147;5272:3;5306:49;5351:3;5342:6;5331:9;5327:22;5306:49;;;5299:4;5292:5;5288:16;5281:75;5228:139;5421:3;5455:49;5500:3;5491:6;5480:9;5476:22;5455:49;;;5448:4;5441:5;5437:16;5430:75;5377:139;5583:3;5618:49;5663:3;5654:6;5643:9;5639:22;5618:49;;;5610:5;5603;5599:17;5592:76;5526:153;5729:3;5764:49;5809:3;5800:6;5789:9;5785:22;5764:49;;;5756:5;5749;5745:17;5738:76;5689:136;5913:3;5902:9;5898:19;5885:33;5938:18;5930:6;5927:30;5924:2;;;5970:1;5967;5960:12;5924:2;6006:54;6056:3;6047:6;6036:9;6032:22;6006:54;;;5998:5;5991;5987:17;5980:81;5835:237;6160:3;6149:9;6145:19;6132:33;6185:18;6177:6;6174:30;6171:2;;;6217:1;6214;6207:12;6171:2;6253:54;6303:3;6294:6;6283:9;6279:22;6253:54;;;6245:5;6238;6234:17;6227:81;6082:237;4198:2131;;;;;8705:241;;8809:2;8797:9;8788:7;8784:23;8780:32;8777:2;;;8825:1;8822;8815:12;8777:2;8860:1;8877:53;8922:7;8902:9;8877:53;;8953:366;;;9074:2;9062:9;9053:7;9049:23;9045:32;9042:2;;;9090:1;9087;9080:12;9042:2;9125:1;9142:53;9187:7;9167:9;9142:53;;;9132:63;;9104:97;9232:2;9250:53;9295:7;9286:6;9275:9;9271:22;9250:53;;;9240:63;;9211:98;9036:283;;;;;;9326:360;;;9444:2;9432:9;9423:7;9419:23;9415:32;9412:2;;;9460:1;9457;9450:12;9412:2;9495:1;9512:53;9557:7;9537:9;9512:53;;;9502:63;;9474:97;9602:2;9620:50;9662:7;9653:6;9642:9;9638:22;9620:50;;9693:415;;9841:2;9829:9;9820:7;9816:23;9812:32;9809:2;;;9857:1;9854;9847:12;9809:2;9892:31;;9943:18;9932:30;;9929:2;;;9975:1;9972;9965:12;9929:2;9995:97;10084:7;10075:6;10064:9;10060:22;9995:97;;10115:947;;;;10352:2;10340:9;10331:7;10327:23;10323:32;10320:2;;;10368:1;10365;10358:12;10320:2;10403:31;;10454:18;10443:30;;10440:2;;;10486:1;10483;10476:12;10440:2;10506:97;10595:7;10586:6;10575:9;10571:22;10506:97;;;10496:107;;10382:227;10668:2;10657:9;10653:18;10640:32;10692:18;10684:6;10681:30;10678:2;;;10724:1;10721;10714:12;10678:2;10744:78;10814:7;10805:6;10794:9;10790:22;10744:78;;;10734:88;;10619:209;10887:2;10876:9;10872:18;10859:32;10911:18;10903:6;10900:30;10897:2;;;10943:1;10940;10933:12;10897:2;10963:83;11038:7;11029:6;11018:9;11014:22;10963:83;;;10953:93;;10838:214;10314:748;;;;;;11069:811;;;;11281:2;11269:9;11260:7;11256:23;11252:32;11249:2;;;11297:1;11294;11287:12;11249:2;11332:31;;11383:18;11372:30;;11369:2;;;11415:1;11412;11405:12;11369:2;11435:97;11524:7;11515:6;11504:9;11500:22;11435:97;;;11425:107;;11311:227;11569:2;11587:53;11632:7;11623:6;11612:9;11608:22;11587:53;;11887:241;;11991:2;11979:9;11970:7;11966:23;11962:32;11959:2;;;12007:1;12004;11997:12;11959:2;12042:1;12059:53;12104:7;12084:9;12059:53;;12135:366;;;12256:2;12244:9;12235:7;12231:23;12227:32;12224:2;;;12272:1;12269;12262:12;12224:2;12307:1;12324:53;12369:7;12349:9;12324:53;;12508:615;;;;;12665:2;12653:9;12644:7;12640:23;12636:32;12633:2;;;12681:1;12678;12671:12;12633:2;12716:1;12733:53;12778:7;12758:9;12733:53;;;12723:63;;12695:97;12823:2;12841:53;12886:7;12877:6;12866:9;12862:22;12841:53;;;12831:63;;12802:98;12959:2;12948:9;12944:18;12931:32;12983:18;12975:6;12972:30;12969:2;;;13015:1;13012;13005:12;12969:2;13043:64;13099:7;13090:6;13079:9;13075:22;13043:64;;;12627:496;;;;-1:-1;13025:82;-1:-1;;;;12627:496;13130:595;;;;13277:2;13265:9;13256:7;13252:23;13248:32;13245:2;;;13293:1;13290;13283:12;13245:2;13328:1;13345:53;13390:7;13370:9;13345:53;;;13335:63;;13307:97;13435:2;13453:53;13498:7;13489:6;13478:9;13474:22;13453:53;;;13443:63;;13414:98;13571:2;13560:9;13556:18;13543:32;13595:18;13587:6;13584:30;13581:2;;;13627:1;13624;13617:12;13581:2;13647:62;13701:7;13692:6;13681:9;13677:22;13647:62;;13732:239;;13835:2;13823:9;13814:7;13810:23;13806:32;13803:2;;;13851:1;13848;13841:12;13803:2;13886:1;13903:52;13947:7;13927:9;13903:52;;13978:261;;14092:2;14080:9;14071:7;14067:23;14063:32;14060:2;;;14108:1;14105;14098:12;14060:2;14143:1;14160:63;14215:7;14195:9;14160:63;;14246:373;;14373:2;14361:9;14352:7;14348:23;14344:32;14341:2;;;14389:1;14386;14379:12;14341:2;14424:31;;14475:18;14464:30;;14461:2;;;14507:1;14504;14497:12;14461:2;14527:76;14595:7;14586:6;14575:9;14571:22;14527:76;;14626:1089;;;;;14845:3;14833:9;14824:7;14820:23;14816:33;14813:2;;;14862:1;14859;14852:12;14813:2;14897:31;;14948:18;14937:30;;14934:2;;;14980:1;14977;14970:12;14934:2;15000:76;15068:7;15059:6;15048:9;15044:22;15000:76;;;14990:86;;14876:206;15141:2;15130:9;15126:18;15113:32;15165:18;15157:6;15154:30;15151:2;;;15197:1;15194;15187:12;15151:2;15217:76;15285:7;15276:6;15265:9;15261:22;15217:76;;;15207:86;;15092:207;15358:2;15347:9;15343:18;15330:32;15382:18;15374:6;15371:30;15368:2;;;15414:1;15411;15404:12;15368:2;15434:62;15488:7;15479:6;15468:9;15464:22;15434:62;;;15424:72;;15309:193;15561:2;15550:9;15546:18;15533:32;15585:18;15577:6;15574:30;15571:2;;;15617:1;15614;15607:12;15571:2;15637:62;15691:7;15682:6;15671:9;15667:22;15637:62;;;15627:72;;15512:193;14807:908;;;;;;;;15722:727;;;;15892:2;15880:9;15871:7;15867:23;15863:32;15860:2;;;15908:1;15905;15898:12;15860:2;15943:31;;15994:18;15983:30;;15980:2;;;16026:1;16023;16016:12;15980:2;16046:76;16114:7;16105:6;16094:9;16090:22;16046:76;;;16036:86;;15922:206;16159:2;16177:53;16222:7;16213:6;16202:9;16198:22;16177:53;;16704:865;;;;;;;16897:3;16885:9;16876:7;16872:23;16868:33;16865:2;;;16914:1;16911;16904:12;16865:2;16949:1;16966:53;17011:7;16991:9;16966:53;;;16956:63;;16928:97;17056:2;17074:53;17119:7;17110:6;17099:9;17095:22;17074:53;;;17064:63;;17035:98;17192:2;17181:9;17177:18;17164:32;17216:18;17208:6;17205:30;17202:2;;;17248:1;17245;17238:12;17202:2;17276:64;17332:7;17323:6;17312:9;17308:22;17276:64;;;17258:82;;;;17143:203;17405:2;17394:9;17390:18;17377:32;17429:18;17421:6;17418:30;17415:2;;;17461:1;17458;17451:12;17415:2;17489:64;17545:7;17536:6;17525:9;17521:22;17489:64;;;17471:82;;;;17356:203;16859:710;;;;;;;;;17576:110;17649:31;17674:5;17649:31;;;17644:3;17637:44;17631:55;;;17760:755;;17941:77;18012:5;17941:77;;;18036:6;18031:3;18024:19;18060:4;18055:3;18051:14;18044:21;;18105:79;18178:5;18105:79;;;18205:1;18190:303;18215:6;18212:1;18209:13;18190:303;;;18255:103;18354:3;18345:6;18339:13;18255:103;;;18375:83;18451:6;18375:83;;;18481:4;18472:14;;;;;18365:93;-1:-1;18237:1;18230:9;18190:303;;;-1:-1;18506:3;;17920:595;-1:-1;;;;17920:595;18523:101;18590:28;18612:5;18590:28;;18631:110;18704:31;18729:5;18704:31;;18748:107;18819:30;18843:5;18819:30;;18862:297;;18962:38;18994:5;18962:38;;;19017:6;19012:3;19005:19;19029:63;19085:6;19078:4;19073:3;19069:14;19062:4;19055:5;19051:16;19029:63;;;19124:29;19146:6;19124:29;;;19104:50;;;19117:4;19104:50;;18942:217;-1:-1;;;18942:217;19462:156;19555:57;19606:5;19555:57;;19925:296;20080:2;20068:15;;20117:66;20112:2;20103:12;;20096:88;20212:2;20203:12;;20061:160;20230:296;20385:2;20373:15;;20422:66;20417:2;20408:12;;20401:88;20517:2;20508:12;;20366:160;20535:296;20690:2;20678:15;;20727:66;20722:2;20713:12;;20706:88;20822:2;20813:12;;20671:160;20840:296;20995:2;20983:15;;21032:66;21027:2;21018:12;;21011:88;21127:2;21118:12;;20976:160;21145:296;21300:2;21288:15;;21337:66;21332:2;21323:12;;21316:88;21432:2;21423:12;;21281:160;21450:397;21605:2;21593:15;;21642:66;21637:2;21628:12;;21621:88;21743:66;21738:2;21729:12;;21722:88;21838:2;21829:12;;21586:261;21856:296;22011:2;21999:15;;22048:66;22043:2;22034:12;;22027:88;22143:2;22134:12;;21992:160;22161:296;22316:2;22304:15;;22353:66;22348:2;22339:12;;22332:88;22448:2;22439:12;;22297:160;22466:296;22621:2;22609:15;;22658:66;22653:2;22644:12;;22637:88;22753:2;22744:12;;22602:160;22771:296;22926:2;22914:15;;22963:66;22958:2;22949:12;;22942:88;23058:2;23049:12;;22907:160;23076:296;23231:2;23219:15;;23268:66;23263:2;23254:12;;23247:88;23363:2;23354:12;;23212:160;23381:296;23536:2;23524:15;;23573:66;23568:2;23559:12;;23552:88;23668:2;23659:12;;23517:160;23686:296;23841:2;23829:15;;23878:66;23873:2;23864:12;;23857:88;23973:2;23964:12;;23822:160;23991:296;24146:2;24134:15;;24183:66;24178:2;24169:12;;24162:88;24278:2;24269:12;;24127:160;24296:296;24451:2;24439:15;;24488:66;24483:2;24474:12;;24467:88;24583:2;24574:12;;24432:160;24601:296;24756:2;24744:15;;24793:66;24788:2;24779:12;;24772:88;24888:2;24879:12;;24737:160;24906:296;25061:2;25049:15;;25098:66;25093:2;25084:12;;25077:88;25193:2;25184:12;;25042:160;25211:296;25366:2;25354:15;;25403:66;25398:2;25389:12;;25382:88;25498:2;25489:12;;25347:160;25516:397;25671:2;25659:15;;25708:66;25703:2;25694:12;;25687:88;25809:66;25804:2;25795:12;;25788:88;25904:2;25895:12;;25652:261;25922:296;26077:2;26065:15;;26114:66;26109:2;26100:12;;26093:88;26209:2;26200:12;;26058:160;26227:296;26382:2;26370:15;;26419:66;26414:2;26405:12;;26398:88;26514:2;26505:12;;26363:160;26532:296;26687:2;26675:15;;26724:66;26719:2;26710:12;;26703:88;26819:2;26810:12;;26668:160;26837:296;26992:2;26980:15;;27029:66;27024:2;27015:12;;27008:88;27124:2;27115:12;;26973:160;27142:296;27297:2;27285:15;;27334:66;27329:2;27320:12;;27313:88;27429:2;27420:12;;27278:160;27447:296;27602:2;27590:15;;27639:66;27634:2;27625:12;;27618:88;27734:2;27725:12;;27583:160;27752:296;27907:2;27895:15;;27944:66;27939:2;27930:12;;27923:88;28039:2;28030:12;;27888:160;28057:296;28212:2;28200:15;;28249:66;28244:2;28235:12;;28228:88;28344:2;28335:12;;28193:160;28362:397;28517:2;28505:15;;28554:66;28549:2;28540:12;;28533:88;28655:66;28650:2;28641:12;;28634:88;28750:2;28741:12;;28498:261;28768:296;28923:2;28911:15;;28960:66;28955:2;28946:12;;28939:88;29055:2;29046:12;;28904:160;29073:296;29228:2;29216:15;;29265:66;29260:2;29251:12;;29244:88;29360:2;29351:12;;29209:160;29378:296;29533:2;29521:15;;29570:66;29565:2;29556:12;;29549:88;29665:2;29656:12;;29514:160;29683:296;29838:2;29826:15;;29875:66;29870:2;29861:12;;29854:88;29970:2;29961:12;;29819:160;29988:296;30143:2;30131:15;;30180:66;30175:2;30166:12;;30159:88;30275:2;30266:12;;30124:160;30369:888;30597:22;;30510:4;30501:14;;;30631:61;30505:3;30597:22;30631:61;;;30530:174;30798:4;30791:5;30787:16;30781:23;30816:62;30872:4;30867:3;30863:14;30850:11;30816:62;;;30714:176;30974:4;30967:5;30963:16;30957:23;30992:62;31048:4;31043:3;31039:14;31026:11;30992:62;;;30900:166;31150:4;31143:5;31139:16;31133:23;31168:62;31224:4;31219:3;31215:14;31202:11;31168:62;;32323:815;32548:22;;32478:5;32469:15;;;32582:115;32473:3;32548:22;32582:115;;;32499:210;32786:4;32779:5;32775:16;32769:23;32804:116;32914:4;32909:3;32905:14;32892:11;32804:116;;;32719:213;33030:4;33023:5;33019:16;33013:23;33048:63;33104:5;33099:3;33095:15;33082:11;33048:63;;33206:695;33419:22;;33343:4;33334:14;;;33453:57;33338:3;33419:22;33453:57;;;33363:159;33603:4;33596:5;33592:16;33586:23;33621:62;33677:4;33672:3;33668:14;33655:11;33621:62;;;33532:163;33794:4;33787:5;33783:16;33777:23;33812:62;33868:4;33863:3;33859:14;33846:11;33812:62;;34784:104;34853:29;34876:5;34853:29;;34895:193;35003:2;34988:18;;35017:61;34992:9;35051:6;35017:61;;35095:1057;35427:3;35412:19;;35442:61;35416:9;35476:6;35442:61;;;35514:62;35572:2;35561:9;35557:18;35548:6;35514:62;;;35587;35645:2;35634:9;35630:18;35621:6;35587:62;;;35660;35718:2;35707:9;35703:18;35694:6;35660:62;;;35733:63;35791:3;35780:9;35776:19;35767:6;35733:63;;;35807;35865:3;35854:9;35850:19;35841:6;35807:63;;;35919:9;35913:4;35909:20;35903:3;35892:9;35888:19;35881:49;35944:62;36001:4;35992:6;35944:62;;;35936:70;;36055:9;36049:4;36045:20;36039:3;36028:9;36024:19;36017:49;36080:62;36137:4;36128:6;36080:62;;;36072:70;35398:754;-1:-1;;;;;;;;;;35398:754;36159:547;36351:2;36336:18;;36365:61;36340:9;36399:6;36365:61;;;36474:9;36468:4;36464:20;36459:2;36448:9;36444:18;36437:48;36499:62;36556:4;36547:6;36499:62;;;36491:70;;36609:9;36603:4;36599:20;36594:2;36583:9;36579:18;36572:48;36634:62;36691:4;36682:6;36634:62;;;36626:70;36322:384;-1:-1;;;;;36322:384;36713:433;36917:2;36931:47;;;36902:18;;36992:144;36902:18;37122:6;36992:144;;37153:181;37255:2;37240:18;;37269:55;37244:9;37297:6;37269:55;;37341:193;37449:2;37434:18;;37463:61;37438:9;37497:6;37463:61;;37541:479;37723:2;37708:18;;37737:61;37712:9;37771:6;37737:61;;;37809:62;37867:2;37856:9;37852:18;37843:6;37809:62;;;37919:9;37913:4;37909:20;37904:2;37893:9;37889:18;37882:48;37944:66;38005:4;37996:6;37944:66;;38027:378;38181:2;38166:18;;38195:61;38170:9;38229:6;38195:61;;;38304:9;38298:4;38294:20;38289:2;38278:9;38274:18;38267:48;38329:66;38390:4;38381:6;38329:66;;38412:489;38600:3;38585:19;;38615:61;38589:9;38649:6;38615:61;;;38687:58;38741:2;38730:9;38726:18;38717:6;38687:58;;;38756:62;38814:2;38803:9;38799:18;38790:6;38756:62;;;38829;38887:2;38876:9;38872:18;38863:6;38829:62;;38908:290;39042:2;39027:18;;39056:59;39031:9;39088:6;39056:59;;;39126:62;39184:2;39173:9;39169:18;39160:6;39126:62;;39205:269;39327:2;39341:47;;;39312:18;;39402:62;39312:18;39450:6;39402:62;;39481:233;39609:2;39594:18;;39623:81;39598:9;39677:6;39623:81;;40001:387;40182:2;40196:47;;;40167:18;;40257:121;40167:18;40257:121;;40395:387;40576:2;40590:47;;;40561:18;;40651:121;40561:18;40651:121;;40789:387;40970:2;40984:47;;;40955:18;;41045:121;40955:18;41045:121;;41183:387;41364:2;41378:47;;;41349:18;;41439:121;41349:18;41439:121;;41577:387;41758:2;41772:47;;;41743:18;;41833:121;41743:18;41833:121;;41971:387;42152:2;42166:47;;;42137:18;;42227:121;42137:18;42227:121;;42365:387;42546:2;42560:47;;;42531:18;;42621:121;42531:18;42621:121;;42759:387;42940:2;42954:47;;;42925:18;;43015:121;42925:18;43015:121;;43153:387;43334:2;43348:47;;;43319:18;;43409:121;43319:18;43409:121;;43547:387;43728:2;43742:47;;;43713:18;;43803:121;43713:18;43803:121;;43941:387;44122:2;44136:47;;;44107:18;;44197:121;44107:18;44197:121;;44335:387;44516:2;44530:47;;;44501:18;;44591:121;44501:18;44591:121;;44729:387;44910:2;44924:47;;;44895:18;;44985:121;44895:18;44985:121;;45123:387;45304:2;45318:47;;;45289:18;;45379:121;45289:18;45379:121;;45517:387;45698:2;45712:47;;;45683:18;;45773:121;45683:18;45773:121;;45911:387;46092:2;46106:47;;;46077:18;;46167:121;46077:18;46167:121;;46305:387;46486:2;46500:47;;;46471:18;;46561:121;46471:18;46561:121;;46699:387;46880:2;46894:47;;;46865:18;;46955:121;46865:18;46955:121;;47093:387;47274:2;47288:47;;;47259:18;;47349:121;47259:18;47349:121;;47487:387;47668:2;47682:47;;;47653:18;;47743:121;47653:18;47743:121;;47881:387;48062:2;48076:47;;;48047:18;;48137:121;48047:18;48137:121;;48275:387;48456:2;48470:47;;;48441:18;;48531:121;48441:18;48531:121;;48669:387;48850:2;48864:47;;;48835:18;;48925:121;48835:18;48925:121;;49063:387;49244:2;49258:47;;;49229:18;;49319:121;49229:18;49319:121;;49457:387;49638:2;49652:47;;;49623:18;;49713:121;49623:18;49713:121;;49851:387;50032:2;50046:47;;;50017:18;;50107:121;50017:18;50107:121;;50245:387;50426:2;50440:47;;;50411:18;;50501:121;50411:18;50501:121;;50639:387;50820:2;50834:47;;;50805:18;;50895:121;50805:18;50895:121;;51033:387;51214:2;51228:47;;;51199:18;;51289:121;51199:18;51289:121;;51427:387;51608:2;51622:47;;;51593:18;;51683:121;51593:18;51683:121;;51821:387;52002:2;52016:47;;;51987:18;;52077:121;51987:18;52077:121;;52215:387;52396:2;52410:47;;;52381:18;;52471:121;52381:18;52471:121;;52609:387;52790:2;52804:47;;;52775:18;;52865:121;52775:18;52865:121;;53003:310;53169:3;53154:19;;53184:119;53158:9;53276:6;53184:119;;53320:338;53500:3;53485:19;;53515:133;53489:9;53621:6;53515:133;;53665:301;53827:2;53812:18;;53841:115;53816:9;53929:6;53841:115;;54173:256;54235:2;54229:9;54261:17;;;54336:18;54321:34;;54357:22;;;54318:62;54315:2;;;54393:1;54390;54383:12;54315:2;54409;54402:22;54213:216;;-1:-1;54213:216;54436:263;;54600:18;54592:6;54589:30;54586:2;;;54632:1;54629;54622:12;54586:2;-1:-1;54661:4;54649:17;;;54679:15;;54523:176;55255:254;;55394:18;55386:6;55383:30;55380:2;;;55426:1;55423;55416:12;55380:2;-1:-1;55499:4;55470;55447:17;;;;55466:9;55443:33;55489:15;;55317:192;55783:144;55915:4;55903:17;;55884:43;55936:130;56049:12;;56033:33;56514:128;56594:42;56583:54;;56566:76;56649:92;56722:13;56715:21;;56698:43;56748:79;56817:5;56800:27;56834:151;56913:66;56902:78;;56885:100;57078:88;57156:4;57145:16;;57128:38;57737:135;;57836:31;57861:5;57836:31;;57880:145;57961:6;57956:3;57951;57938:30;-1:-1;58017:1;57999:16;;57992:27;57931:94;58034:268;58099:1;58106:101;58120:6;58117:1;58114:13;58106:101;;;58187:11;;;58181:18;58168:11;;;58161:39;58142:2;58135:10;58106:101;;;58222:6;58219:1;58216:13;58213:2;;;-1:-1;;58287:1;58269:16;;58262:27;58083:219;58310:97;58398:2;58378:14;58394:7;58374:28;;58358:49" + } + } + }, + "sources": { + "2.0.0/extensions/Forwarder/Forwarder.sol": { + "id": 0 + }, + "2.0.0/extensions/Forwarder/MixinAssets.sol": { + "id": 1 + }, + "2.0.0/extensions/Forwarder/MixinExchangeWrapper.sol": { + "id": 2 + }, + "2.0.0/extensions/Forwarder/MixinForwarderCore.sol": { + "id": 3 + }, + "2.0.0/extensions/Forwarder/MixinWeth.sol": { + "id": 4 + }, + "2.0.0/extensions/Forwarder/interfaces/IAssets.sol": { + "id": 5 + }, + "2.0.0/extensions/Forwarder/interfaces/IForwarderCore.sol": { + "id": 6 + }, + "2.0.0/extensions/Forwarder/libs/LibConstants.sol": { + "id": 7 + }, + "2.0.0/extensions/Forwarder/mixins/MAssets.sol": { + "id": 8 + }, + "2.0.0/extensions/Forwarder/mixins/MExchangeWrapper.sol": { + "id": 9 + }, + "2.0.0/extensions/Forwarder/mixins/MWeth.sol": { + "id": 10 + }, + "2.0.0/extensions/OrderValidator/OrderValidator.sol": { + "id": 11 + }, + "2.0.0/multisig/MultiSigWallet.sol": { + "id": 12 + }, + "2.0.0/multisig/MultiSigWalletWithTimeLock.sol": { + "id": 13 + }, + "2.0.0/protocol/AssetProxy/ERC20Proxy.sol": { + "id": 14 + }, + "2.0.0/protocol/AssetProxy/ERC721Proxy.sol": { + "id": 15 + }, + "2.0.0/protocol/AssetProxy/MixinAuthorizable.sol": { + "id": 16 + }, + "2.0.0/protocol/AssetProxy/interfaces/IAssetProxy.sol": { + "id": 17 + }, + "2.0.0/protocol/AssetProxy/interfaces/IAuthorizable.sol": { + "id": 18 + }, + "2.0.0/protocol/AssetProxy/mixins/MAuthorizable.sol": { + "id": 19 + }, + "2.0.0/protocol/AssetProxyOwner/AssetProxyOwner.sol": { + "id": 20 + }, + "2.0.0/protocol/Exchange/Exchange.sol": { + "id": 21 + }, + "2.0.0/protocol/Exchange/MixinAssetProxyDispatcher.sol": { + "id": 22 + }, + "2.0.0/protocol/Exchange/MixinExchangeCore.sol": { + "id": 23 + }, + "2.0.0/protocol/Exchange/MixinMatchOrders.sol": { + "id": 24 + }, + "2.0.0/protocol/Exchange/MixinSignatureValidator.sol": { + "id": 25 + }, + "2.0.0/protocol/Exchange/MixinTransactions.sol": { + "id": 26 + }, + "2.0.0/protocol/Exchange/MixinWrapperFunctions.sol": { + "id": 27 + }, + "2.0.0/protocol/Exchange/interfaces/IAssetProxyDispatcher.sol": { + "id": 28 + }, + "2.0.0/protocol/Exchange/interfaces/IExchange.sol": { + "id": 29 + }, + "2.0.0/protocol/Exchange/interfaces/IExchangeCore.sol": { + "id": 30 + }, + "2.0.0/protocol/Exchange/interfaces/IMatchOrders.sol": { + "id": 31 + }, + "2.0.0/protocol/Exchange/interfaces/ISignatureValidator.sol": { + "id": 32 + }, + "2.0.0/protocol/Exchange/interfaces/ITransactions.sol": { + "id": 33 + }, + "2.0.0/protocol/Exchange/interfaces/IValidator.sol": { + "id": 34 + }, + "2.0.0/protocol/Exchange/interfaces/IWallet.sol": { + "id": 35 + }, + "2.0.0/protocol/Exchange/interfaces/IWrapperFunctions.sol": { + "id": 36 + }, + "2.0.0/protocol/Exchange/libs/LibAbiEncoder.sol": { + "id": 37 + }, + "2.0.0/protocol/Exchange/libs/LibConstants.sol": { + "id": 38 + }, + "2.0.0/protocol/Exchange/libs/LibEIP712.sol": { + "id": 39 + }, + "2.0.0/protocol/Exchange/libs/LibExchangeErrors.sol": { + "id": 40 + }, + "2.0.0/protocol/Exchange/libs/LibFillResults.sol": { + "id": 41 + }, + "2.0.0/protocol/Exchange/libs/LibMath.sol": { + "id": 42 + }, + "2.0.0/protocol/Exchange/libs/LibOrder.sol": { + "id": 43 + }, + "2.0.0/protocol/Exchange/mixins/MAssetProxyDispatcher.sol": { + "id": 44 + }, + "2.0.0/protocol/Exchange/mixins/MExchangeCore.sol": { + "id": 45 + }, + "2.0.0/protocol/Exchange/mixins/MMatchOrders.sol": { + "id": 46 + }, + "2.0.0/protocol/Exchange/mixins/MSignatureValidator.sol": { + "id": 47 + }, + "2.0.0/protocol/Exchange/mixins/MTransactions.sol": { + "id": 48 + }, + "2.0.0/protocol/Exchange/mixins/MWrapperFunctions.sol": { + "id": 49 + }, + "2.0.0/tokens/ERC20Token/IERC20Token.sol": { + "id": 50 + }, + "2.0.0/tokens/ERC721Token/IERC721Token.sol": { + "id": 51 + }, + "2.0.0/tokens/EtherToken/IEtherToken.sol": { + "id": 52 + }, + "2.0.0/utils/LibBytes/LibBytes.sol": { + "id": 53 + }, + "2.0.0/utils/Ownable/IOwnable.sol": { + "id": 54 + }, + "2.0.0/utils/Ownable/Ownable.sol": { + "id": 55 + }, + "2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol": { + "id": 56 + }, + "2.0.0/utils/SafeMath/SafeMath.sol": { + "id": 57 + } + }, + "sourceCodes": { + "2.0.0/extensions/Forwarder/Forwarder.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"./MixinWeth.sol\";\nimport \"./MixinForwarderCore.sol\";\nimport \"./libs/LibConstants.sol\";\nimport \"./MixinAssets.sol\";\nimport \"./MixinExchangeWrapper.sol\";\n\n\n// solhint-disable no-empty-blocks\ncontract Forwarder is\n LibConstants,\n MixinWeth,\n MixinAssets,\n MixinExchangeWrapper,\n MixinForwarderCore\n{\n constructor (\n address _exchange,\n bytes memory _zrxAssetData,\n bytes memory _wethAssetData\n )\n public\n LibConstants(\n _exchange,\n _zrxAssetData,\n _wethAssetData\n )\n MixinForwarderCore()\n {}\n}\n", + "2.0.0/extensions/Forwarder/MixinAssets.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../utils/LibBytes/LibBytes.sol\";\nimport \"../../utils/Ownable/Ownable.sol\";\nimport \"../../tokens/ERC20Token/IERC20Token.sol\";\nimport \"../../tokens/ERC721Token/IERC721Token.sol\";\nimport \"./libs/LibConstants.sol\";\nimport \"./mixins/MAssets.sol\";\n\n\ncontract MixinAssets is\n Ownable,\n LibConstants,\n MAssets\n{\n using LibBytes for bytes;\n\n bytes4 constant internal ERC20_TRANSFER_SELECTOR = bytes4(keccak256(\"transfer(address,uint256)\"));\n\n /// @dev Withdraws assets from this contract. The contract requires a ZRX balance in order to \n /// function optimally, and this function allows the ZRX to be withdrawn by owner. It may also be\n /// used to withdraw assets that were accidentally sent to this contract.\n /// @param assetData Byte array encoded for the respective asset proxy.\n /// @param amount Amount of ERC20 token to withdraw.\n function withdrawAsset(\n bytes assetData,\n uint256 amount\n )\n external\n onlyOwner\n {\n transferAssetToSender(assetData, amount);\n }\n\n /// @dev Transfers given amount of asset to sender.\n /// @param assetData Byte array encoded for the respective asset proxy.\n /// @param amount Amount of asset to transfer to sender.\n function transferAssetToSender(\n bytes memory assetData,\n uint256 amount\n )\n internal\n {\n bytes4 proxyId = assetData.readBytes4(0);\n\n if (proxyId == ERC20_DATA_ID) {\n transferERC20Token(assetData, amount);\n } else if (proxyId == ERC721_DATA_ID) {\n transferERC721Token(assetData, amount);\n } else {\n revert(\"UNSUPPORTED_ASSET_PROXY\");\n }\n }\n\n /// @dev Decodes ERC20 assetData and transfers given amount to sender.\n /// @param assetData Byte array encoded for the respective asset proxy.\n /// @param amount Amount of asset to transfer to sender.\n function transferERC20Token(\n bytes memory assetData,\n uint256 amount\n )\n internal\n {\n address token = assetData.readAddress(16);\n\n // Transfer tokens.\n // We do a raw call so we can check the success separate\n // from the return data.\n bool success = token.call(abi.encodeWithSelector(\n ERC20_TRANSFER_SELECTOR,\n msg.sender,\n amount\n ));\n require(\n success,\n \"TRANSFER_FAILED\"\n );\n \n // Check return data.\n // If there is no return data, we assume the token incorrectly\n // does not return a bool. In this case we expect it to revert\n // on failure, which was handled above.\n // If the token does return data, we require that it is a single\n // value that evaluates to true.\n assembly {\n if returndatasize {\n success := 0\n if eq(returndatasize, 32) {\n // First 64 bytes of memory are reserved scratch space\n returndatacopy(0, 0, 32)\n success := mload(0)\n }\n }\n }\n require(\n success,\n \"TRANSFER_FAILED\"\n );\n }\n\n /// @dev Decodes ERC721 assetData and transfers given amount to sender.\n /// @param assetData Byte array encoded for the respective asset proxy.\n /// @param amount Amount of asset to transfer to sender.\n function transferERC721Token(\n bytes memory assetData,\n uint256 amount\n )\n internal\n {\n require(\n amount == 1,\n \"INVALID_AMOUNT\"\n );\n // Decode asset data.\n address token = assetData.readAddress(16);\n uint256 tokenId = assetData.readUint256(36);\n\n // Perform transfer.\n IERC721Token(token).transferFrom(\n address(this),\n msg.sender,\n tokenId\n );\n }\n}\n", + "2.0.0/extensions/Forwarder/MixinExchangeWrapper.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"./libs/LibConstants.sol\";\nimport \"./mixins/MExchangeWrapper.sol\";\nimport \"../../protocol/Exchange/libs/LibAbiEncoder.sol\";\nimport \"../../protocol/Exchange/libs/LibOrder.sol\";\nimport \"../../protocol/Exchange/libs/LibFillResults.sol\";\nimport \"../../protocol/Exchange/libs/LibMath.sol\";\n\n\ncontract MixinExchangeWrapper is\n LibAbiEncoder,\n LibFillResults,\n LibMath,\n LibConstants,\n MExchangeWrapper\n{\n /// @dev Fills the input order.\n /// Returns false if the transaction would otherwise revert.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n /// @return Amounts filled and fees paid by maker and taker.\n function fillOrderNoThrow(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n internal\n returns (FillResults memory fillResults)\n {\n // ABI encode calldata for `fillOrder`\n bytes memory fillOrderCalldata = abiEncodeFillOrder(\n order,\n takerAssetFillAmount,\n signature\n );\n\n address exchange = address(EXCHANGE);\n\n // Call `fillOrder` and handle any exceptions gracefully\n assembly {\n let success := call(\n gas, // forward all gas\n exchange, // call address of Exchange contract\n 0, // transfer 0 wei\n add(fillOrderCalldata, 32), // pointer to start of input (skip array length in first 32 bytes)\n mload(fillOrderCalldata), // length of input\n fillOrderCalldata, // write output over input\n 128 // output size is 128 bytes\n )\n if success {\n mstore(fillResults, mload(fillOrderCalldata))\n mstore(add(fillResults, 32), mload(add(fillOrderCalldata, 32)))\n mstore(add(fillResults, 64), mload(add(fillOrderCalldata, 64)))\n mstore(add(fillResults, 96), mload(add(fillOrderCalldata, 96)))\n }\n }\n // fillResults values will be 0 by default if call was unsuccessful\n return fillResults;\n }\n\n /// @dev Synchronously executes multiple calls of fillOrder until total amount of WETH has been sold by taker.\n /// Returns false if the transaction would otherwise revert.\n /// @param orders Array of order specifications.\n /// @param wethSellAmount Desired amount of WETH to sell.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketSellWeth(\n LibOrder.Order[] memory orders,\n uint256 wethSellAmount,\n bytes[] memory signatures\n )\n internal\n returns (FillResults memory totalFillResults)\n {\n bytes memory makerAssetData = orders[0].makerAssetData;\n bytes memory wethAssetData = WETH_ASSET_DATA;\n\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n\n // We assume that asset being bought by taker is the same for each order.\n // We assume that asset being sold by taker is WETH for each order.\n orders[i].makerAssetData = makerAssetData;\n orders[i].takerAssetData = wethAssetData;\n\n // Calculate the remaining amount of WETH to sell\n uint256 remainingTakerAssetFillAmount = safeSub(wethSellAmount, totalFillResults.takerAssetFilledAmount);\n\n // Attempt to sell the remaining amount of WETH\n FillResults memory singleFillResults = fillOrderNoThrow(\n orders[i],\n remainingTakerAssetFillAmount,\n signatures[i]\n );\n\n // Update amounts filled and fees paid by maker and taker\n addFillResults(totalFillResults, singleFillResults);\n\n // Stop execution if the entire amount of takerAsset has been sold\n if (totalFillResults.takerAssetFilledAmount >= wethSellAmount) {\n break;\n }\n }\n return totalFillResults;\n }\n\n /// @dev Synchronously executes multiple fill orders in a single transaction until total amount is bought by taker.\n /// Returns false if the transaction would otherwise revert.\n /// The asset being sold by taker must always be WETH.\n /// @param orders Array of order specifications.\n /// @param makerAssetFillAmount Desired amount of makerAsset to buy.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketBuyExactAmountWithWeth(\n LibOrder.Order[] memory orders,\n uint256 makerAssetFillAmount,\n bytes[] memory signatures\n )\n internal\n returns (FillResults memory totalFillResults)\n {\n bytes memory makerAssetData = orders[0].makerAssetData;\n bytes memory wethAssetData = WETH_ASSET_DATA;\n\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n\n // We assume that asset being bought by taker is the same for each order.\n // We assume that asset being sold by taker is WETH for each order.\n orders[i].makerAssetData = makerAssetData;\n orders[i].takerAssetData = wethAssetData;\n\n // Calculate the remaining amount of makerAsset to buy\n uint256 remainingMakerAssetFillAmount = safeSub(makerAssetFillAmount, totalFillResults.makerAssetFilledAmount);\n\n // Convert the remaining amount of makerAsset to buy into remaining amount\n // of takerAsset to sell, assuming entire amount can be sold in the current order\n uint256 remainingTakerAssetFillAmount = getPartialAmountFloor(\n orders[i].takerAssetAmount,\n orders[i].makerAssetAmount,\n remainingMakerAssetFillAmount\n );\n\n // Attempt to sell the remaining amount of takerAsset\n FillResults memory singleFillResults = fillOrderNoThrow(\n orders[i],\n remainingTakerAssetFillAmount,\n signatures[i]\n );\n\n // Update amounts filled and fees paid by maker and taker\n addFillResults(totalFillResults, singleFillResults);\n\n // Stop execution if the entire amount of makerAsset has been bought\n uint256 makerAssetFilledAmount = totalFillResults.makerAssetFilledAmount;\n if (makerAssetFilledAmount >= makerAssetFillAmount) {\n break;\n }\n }\n\n require(\n makerAssetFilledAmount >= makerAssetFillAmount,\n \"COMPLETE_FILL_FAILED\"\n );\n return totalFillResults;\n }\n\n /// @dev Buys zrxBuyAmount of ZRX fee tokens, taking into account ZRX fees for each order. This will guarantee\n /// that at least zrxBuyAmount of ZRX is purchased (sometimes slightly over due to rounding issues).\n /// It is possible that a request to buy 200 ZRX will require purchasing 202 ZRX\n /// as 2 ZRX is required to purchase the 200 ZRX fee tokens. This guarantees at least 200 ZRX for future purchases.\n /// The asset being sold by taker must always be WETH. \n /// @param orders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset.\n /// @param zrxBuyAmount Desired amount of ZRX to buy.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return totalFillResults Amounts filled and fees paid by maker and taker.\n function marketBuyExactZrxWithWeth(\n LibOrder.Order[] memory orders,\n uint256 zrxBuyAmount,\n bytes[] memory signatures\n )\n internal\n returns (FillResults memory totalFillResults)\n {\n // Do nothing if zrxBuyAmount == 0\n if (zrxBuyAmount == 0) {\n return totalFillResults;\n }\n\n bytes memory zrxAssetData = ZRX_ASSET_DATA;\n bytes memory wethAssetData = WETH_ASSET_DATA;\n uint256 zrxPurchased = 0;\n\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n\n // All of these are ZRX/WETH, so we can drop the respective assetData from calldata.\n orders[i].makerAssetData = zrxAssetData;\n orders[i].takerAssetData = wethAssetData;\n\n // Calculate the remaining amount of ZRX to buy.\n uint256 remainingZrxBuyAmount = safeSub(zrxBuyAmount, zrxPurchased);\n\n // Convert the remaining amount of ZRX to buy into remaining amount\n // of WETH to sell, assuming entire amount can be sold in the current order.\n uint256 remainingWethSellAmount = getPartialAmountFloor(\n orders[i].takerAssetAmount,\n safeSub(orders[i].makerAssetAmount, orders[i].takerFee), // our exchange rate after fees \n remainingZrxBuyAmount\n );\n\n // Attempt to sell the remaining amount of WETH.\n FillResults memory singleFillResult = fillOrderNoThrow(\n orders[i],\n safeAdd(remainingWethSellAmount, 1), // we add 1 wei to the fill amount to make up for rounding errors\n signatures[i]\n );\n\n // Update amounts filled and fees paid by maker and taker.\n addFillResults(totalFillResults, singleFillResult);\n zrxPurchased = safeSub(totalFillResults.makerAssetFilledAmount, totalFillResults.takerFeePaid);\n\n // Stop execution if the entire amount of ZRX has been bought.\n if (zrxPurchased >= zrxBuyAmount) {\n break;\n }\n }\n\n require(\n zrxPurchased >= zrxBuyAmount,\n \"COMPLETE_FILL_FAILED\"\n );\n return totalFillResults;\n }\n}\n", + "2.0.0/extensions/Forwarder/MixinForwarderCore.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"./libs/LibConstants.sol\";\nimport \"./mixins/MWeth.sol\";\nimport \"./mixins/MAssets.sol\";\nimport \"./mixins/MExchangeWrapper.sol\";\nimport \"./interfaces/IForwarderCore.sol\";\nimport \"../../utils/LibBytes/LibBytes.sol\";\nimport \"../../protocol/Exchange/libs/LibOrder.sol\";\nimport \"../../protocol/Exchange/libs/LibFillResults.sol\";\nimport \"../../protocol/Exchange/libs/LibMath.sol\";\n\n\ncontract MixinForwarderCore is\n LibFillResults,\n LibMath,\n LibConstants,\n MWeth,\n MAssets,\n MExchangeWrapper,\n IForwarderCore\n{\n using LibBytes for bytes;\n\n /// @dev Constructor approves ERC20 proxy to transfer ZRX and WETH on this contract's behalf.\n constructor ()\n public\n {\n address proxyAddress = EXCHANGE.getAssetProxy(ERC20_DATA_ID);\n require(\n proxyAddress != address(0),\n \"UNREGISTERED_ASSET_PROXY\"\n );\n ETHER_TOKEN.approve(proxyAddress, MAX_UINT);\n ZRX_TOKEN.approve(proxyAddress, MAX_UINT);\n }\n\n /// @dev Purchases as much of orders' makerAssets as possible by selling up to 95% of transaction's ETH value.\n /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract.\n /// 5% of ETH value is reserved for paying fees to order feeRecipients (in ZRX) and forwarding contract feeRecipient (in ETH).\n /// Any ETH not spent will be refunded to sender.\n /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset. \n /// @param signatures Proofs that orders have been created by makers.\n /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees.\n /// @param feeSignatures Proofs that feeOrders have been created by makers.\n /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.\n /// @param feeRecipient Address that will receive ETH when orders are filled.\n /// @return Amounts filled and fees paid by maker and taker for both sets of orders.\n function marketSellOrdersWithEth(\n LibOrder.Order[] memory orders,\n bytes[] memory signatures,\n LibOrder.Order[] memory feeOrders,\n bytes[] memory feeSignatures,\n uint256 feePercentage,\n address feeRecipient\n )\n public\n payable\n returns (\n FillResults memory orderFillResults,\n FillResults memory feeOrderFillResults\n )\n {\n // Convert ETH to WETH.\n convertEthToWeth();\n\n uint256 wethSellAmount;\n uint256 zrxBuyAmount;\n uint256 makerAssetAmountPurchased;\n if (orders[0].makerAssetData.equals(ZRX_ASSET_DATA)) {\n // Calculate amount of WETH that won't be spent on ETH fees.\n wethSellAmount = getPartialAmountFloor(\n PERCENTAGE_DENOMINATOR,\n safeAdd(PERCENTAGE_DENOMINATOR, feePercentage),\n msg.value\n );\n // Market sell available WETH.\n // ZRX fees are paid with this contract's balance.\n orderFillResults = marketSellWeth(\n orders,\n wethSellAmount,\n signatures\n );\n // The fee amount must be deducted from the amount transfered back to sender.\n makerAssetAmountPurchased = safeSub(orderFillResults.makerAssetFilledAmount, orderFillResults.takerFeePaid);\n } else {\n // 5% of WETH is reserved for filling feeOrders and paying feeRecipient.\n wethSellAmount = getPartialAmountFloor(\n MAX_WETH_FILL_PERCENTAGE,\n PERCENTAGE_DENOMINATOR,\n msg.value\n );\n // Market sell 95% of WETH.\n // ZRX fees are payed with this contract's balance.\n orderFillResults = marketSellWeth(\n orders,\n wethSellAmount,\n signatures\n );\n // Buy back all ZRX spent on fees.\n zrxBuyAmount = orderFillResults.takerFeePaid;\n feeOrderFillResults = marketBuyExactZrxWithWeth(\n feeOrders,\n zrxBuyAmount,\n feeSignatures\n );\n makerAssetAmountPurchased = orderFillResults.makerAssetFilledAmount;\n }\n\n // Transfer feePercentage of total ETH spent on primary orders to feeRecipient.\n // Refund remaining ETH to msg.sender.\n transferEthFeeAndRefund(\n orderFillResults.takerAssetFilledAmount,\n feeOrderFillResults.takerAssetFilledAmount,\n feePercentage,\n feeRecipient\n );\n\n // Transfer purchased assets to msg.sender.\n transferAssetToSender(orders[0].makerAssetData, makerAssetAmountPurchased);\n }\n\n /// @dev Attempt to purchase makerAssetFillAmount of makerAsset by selling ETH provided with transaction.\n /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract.\n /// Any ETH not spent will be refunded to sender.\n /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset. \n /// @param makerAssetFillAmount Desired amount of makerAsset to purchase.\n /// @param signatures Proofs that orders have been created by makers.\n /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees.\n /// @param feeSignatures Proofs that feeOrders have been created by makers.\n /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.\n /// @param feeRecipient Address that will receive ETH when orders are filled.\n /// @return Amounts filled and fees paid by maker and taker for both sets of orders.\n function marketBuyOrdersWithEth(\n LibOrder.Order[] memory orders,\n uint256 makerAssetFillAmount,\n bytes[] memory signatures,\n LibOrder.Order[] memory feeOrders,\n bytes[] memory feeSignatures,\n uint256 feePercentage,\n address feeRecipient\n )\n public\n payable\n returns (\n FillResults memory orderFillResults,\n FillResults memory feeOrderFillResults\n )\n {\n // Convert ETH to WETH.\n convertEthToWeth();\n\n uint256 zrxBuyAmount;\n uint256 makerAssetAmountPurchased;\n if (orders[0].makerAssetData.equals(ZRX_ASSET_DATA)) {\n // If the makerAsset is ZRX, it is not necessary to pay fees out of this\n // contracts's ZRX balance because fees are factored into the price of the order.\n orderFillResults = marketBuyExactZrxWithWeth(\n orders,\n makerAssetFillAmount,\n signatures\n );\n // The fee amount must be deducted from the amount transfered back to sender.\n makerAssetAmountPurchased = safeSub(orderFillResults.makerAssetFilledAmount, orderFillResults.takerFeePaid);\n } else {\n // Attemp to purchase desired amount of makerAsset.\n // ZRX fees are payed with this contract's balance.\n orderFillResults = marketBuyExactAmountWithWeth(\n orders,\n makerAssetFillAmount,\n signatures\n );\n // Buy back all ZRX spent on fees.\n zrxBuyAmount = orderFillResults.takerFeePaid;\n feeOrderFillResults = marketBuyExactZrxWithWeth(\n feeOrders,\n zrxBuyAmount,\n feeSignatures\n );\n makerAssetAmountPurchased = orderFillResults.makerAssetFilledAmount;\n }\n\n // Transfer feePercentage of total ETH spent on primary orders to feeRecipient.\n // Refund remaining ETH to msg.sender.\n transferEthFeeAndRefund(\n orderFillResults.takerAssetFilledAmount,\n feeOrderFillResults.takerAssetFilledAmount,\n feePercentage,\n feeRecipient\n );\n\n // Transfer purchased assets to msg.sender.\n transferAssetToSender(orders[0].makerAssetData, makerAssetAmountPurchased);\n }\n}\n", + "2.0.0/extensions/Forwarder/MixinWeth.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../protocol/Exchange/libs/LibMath.sol\";\nimport \"./libs/LibConstants.sol\";\nimport \"./mixins/MWeth.sol\";\n\n\ncontract MixinWeth is\n LibMath,\n LibConstants,\n MWeth\n{\n /// @dev Default payabale function, this allows us to withdraw WETH\n function ()\n public\n payable\n {\n require(\n msg.sender == address(ETHER_TOKEN),\n \"DEFAULT_FUNCTION_WETH_CONTRACT_ONLY\"\n );\n }\n\n /// @dev Converts message call's ETH value into WETH.\n function convertEthToWeth()\n internal\n {\n require(\n msg.value > 0,\n \"INVALID_MSG_VALUE\"\n );\n ETHER_TOKEN.deposit.value(msg.value)();\n }\n\n /// @dev Transfers feePercentage of WETH spent on primary orders to feeRecipient.\n /// Refunds any excess ETH to msg.sender.\n /// @param wethSoldExcludingFeeOrders Amount of WETH sold when filling primary orders.\n /// @param wethSoldForZrx Amount of WETH sold when purchasing ZRX required for primary order fees.\n /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.\n /// @param feeRecipient Address that will receive ETH when orders are filled.\n function transferEthFeeAndRefund(\n uint256 wethSoldExcludingFeeOrders,\n uint256 wethSoldForZrx,\n uint256 feePercentage,\n address feeRecipient\n )\n internal\n {\n // Ensure feePercentage is less than 5%.\n require(\n feePercentage <= MAX_FEE_PERCENTAGE,\n \"FEE_PERCENTAGE_TOO_LARGE\"\n );\n\n // Ensure that no extra WETH owned by this contract has been sold.\n uint256 wethSold = safeAdd(wethSoldExcludingFeeOrders, wethSoldForZrx);\n require(\n wethSold <= msg.value,\n \"OVERSOLD_WETH\"\n );\n\n // Calculate amount of WETH that hasn't been sold.\n uint256 wethRemaining = safeSub(msg.value, wethSold);\n\n // Calculate ETH fee to pay to feeRecipient.\n uint256 ethFee = getPartialAmountFloor(\n feePercentage,\n PERCENTAGE_DENOMINATOR,\n wethSoldExcludingFeeOrders\n );\n\n // Ensure fee is less than amount of WETH remaining.\n require(\n ethFee <= wethRemaining,\n \"INSUFFICIENT_ETH_REMAINING\"\n );\n \n // Do nothing if no WETH remaining\n if (wethRemaining > 0) {\n // Convert remaining WETH to ETH\n ETHER_TOKEN.withdraw(wethRemaining);\n\n // Pay ETH to feeRecipient\n if (ethFee > 0) {\n feeRecipient.transfer(ethFee);\n }\n\n // Refund remaining ETH to msg.sender.\n uint256 ethRefund = safeSub(wethRemaining, ethFee);\n if (ethRefund > 0) {\n msg.sender.transfer(ethRefund);\n }\n }\n }\n}\n", + "2.0.0/extensions/Forwarder/interfaces/IAssets.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\ncontract IAssets {\n\n /// @dev Withdraws assets from this contract. The contract requires a ZRX balance in order to \n /// function optimally, and this function allows the ZRX to be withdrawn by owner. It may also be\n /// used to withdraw assets that were accidentally sent to this contract.\n /// @param assetData Byte array encoded for the respective asset proxy.\n /// @param amount Amount of ERC20 token to withdraw.\n function withdrawAsset(\n bytes assetData,\n uint256 amount\n )\n external;\n}\n", + "2.0.0/extensions/Forwarder/interfaces/IForwarderCore.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../../../protocol/Exchange/libs/LibOrder.sol\";\nimport \"../../../protocol/Exchange/libs/LibFillResults.sol\";\n\n\ncontract IForwarderCore {\n\n /// @dev Purchases as much of orders' makerAssets as possible by selling up to 95% of transaction's ETH value.\n /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract.\n /// 5% of ETH value is reserved for paying fees to order feeRecipients (in ZRX) and forwarding contract feeRecipient (in ETH).\n /// Any ETH not spent will be refunded to sender.\n /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset. \n /// @param signatures Proofs that orders have been created by makers.\n /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees.\n /// @param feeSignatures Proofs that feeOrders have been created by makers.\n /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.\n /// @param feeRecipient Address that will receive ETH when orders are filled.\n /// @return Amounts filled and fees paid by maker and taker for both sets of orders.\n function marketSellOrdersWithEth(\n LibOrder.Order[] memory orders,\n bytes[] memory signatures,\n LibOrder.Order[] memory feeOrders,\n bytes[] memory feeSignatures,\n uint256 feePercentage,\n address feeRecipient\n )\n public\n payable\n returns (\n LibFillResults.FillResults memory orderFillResults,\n LibFillResults.FillResults memory feeOrderFillResults\n );\n\n /// @dev Attempt to purchase makerAssetFillAmount of makerAsset by selling ETH provided with transaction.\n /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract.\n /// Any ETH not spent will be refunded to sender.\n /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset. \n /// @param makerAssetFillAmount Desired amount of makerAsset to purchase.\n /// @param signatures Proofs that orders have been created by makers.\n /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees.\n /// @param feeSignatures Proofs that feeOrders have been created by makers.\n /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.\n /// @param feeRecipient Address that will receive ETH when orders are filled.\n /// @return Amounts filled and fees paid by maker and taker for both sets of orders.\n function marketBuyOrdersWithEth(\n LibOrder.Order[] memory orders,\n uint256 makerAssetFillAmount,\n bytes[] memory signatures,\n LibOrder.Order[] memory feeOrders,\n bytes[] memory feeSignatures,\n uint256 feePercentage,\n address feeRecipient\n )\n public\n payable\n returns (\n LibFillResults.FillResults memory orderFillResults,\n LibFillResults.FillResults memory feeOrderFillResults\n );\n}\n", + "2.0.0/extensions/Forwarder/libs/LibConstants.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../../utils/LibBytes/LibBytes.sol\";\nimport \"../../../protocol/Exchange/interfaces/IExchange.sol\";\nimport \"../../../tokens/EtherToken/IEtherToken.sol\";\nimport \"../../../tokens/ERC20Token/IERC20Token.sol\";\n\n\ncontract LibConstants {\n\n using LibBytes for bytes;\n\n bytes4 constant internal ERC20_DATA_ID = bytes4(keccak256(\"ERC20Token(address)\"));\n bytes4 constant internal ERC721_DATA_ID = bytes4(keccak256(\"ERC721Token(address,uint256)\"));\n uint256 constant internal MAX_UINT = 2**256 - 1;\n uint256 constant internal PERCENTAGE_DENOMINATOR = 10**18; \n uint256 constant internal MAX_FEE_PERCENTAGE = 5 * PERCENTAGE_DENOMINATOR / 100; // 5%\n uint256 constant internal MAX_WETH_FILL_PERCENTAGE = 95 * PERCENTAGE_DENOMINATOR / 100; // 95%\n \n // solhint-disable var-name-mixedcase\n IExchange internal EXCHANGE;\n IEtherToken internal ETHER_TOKEN;\n IERC20Token internal ZRX_TOKEN;\n bytes internal ZRX_ASSET_DATA;\n bytes internal WETH_ASSET_DATA;\n // solhint-enable var-name-mixedcase\n\n constructor (\n address _exchange,\n bytes memory _zrxAssetData,\n bytes memory _wethAssetData\n )\n public\n {\n EXCHANGE = IExchange(_exchange);\n ZRX_ASSET_DATA = _zrxAssetData;\n WETH_ASSET_DATA = _wethAssetData;\n\n address etherToken = _wethAssetData.readAddress(16);\n address zrxToken = _zrxAssetData.readAddress(16);\n ETHER_TOKEN = IEtherToken(etherToken);\n ZRX_TOKEN = IERC20Token(zrxToken);\n }\n}\n", + "2.0.0/extensions/Forwarder/mixins/MAssets.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../interfaces/IAssets.sol\";\n\n\ncontract MAssets is\n IAssets\n{\n /// @dev Transfers given amount of asset to sender.\n /// @param assetData Byte array encoded for the respective asset proxy.\n /// @param amount Amount of asset to transfer to sender.\n function transferAssetToSender(\n bytes memory assetData,\n uint256 amount\n )\n internal;\n\n /// @dev Decodes ERC20 assetData and transfers given amount to sender.\n /// @param assetData Byte array encoded for the respective asset proxy.\n /// @param amount Amount of asset to transfer to sender.\n function transferERC20Token(\n bytes memory assetData,\n uint256 amount\n )\n internal;\n\n /// @dev Decodes ERC721 assetData and transfers given amount to sender.\n /// @param assetData Byte array encoded for the respective asset proxy.\n /// @param amount Amount of asset to transfer to sender.\n function transferERC721Token(\n bytes memory assetData,\n uint256 amount\n )\n internal;\n}\n", + "2.0.0/extensions/Forwarder/mixins/MExchangeWrapper.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../../../protocol/Exchange/libs/LibOrder.sol\";\nimport \"../../../protocol/Exchange/libs/LibFillResults.sol\";\n\n\ncontract MExchangeWrapper {\n\n /// @dev Fills the input order.\n /// Returns false if the transaction would otherwise revert.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n /// @return Amounts filled and fees paid by maker and taker.\n function fillOrderNoThrow(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n internal\n returns (LibFillResults.FillResults memory fillResults);\n\n /// @dev Synchronously executes multiple calls of fillOrder until total amount of WETH has been sold by taker.\n /// Returns false if the transaction would otherwise revert.\n /// @param orders Array of order specifications.\n /// @param wethSellAmount Desired amount of WETH to sell.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketSellWeth(\n LibOrder.Order[] memory orders,\n uint256 wethSellAmount,\n bytes[] memory signatures\n )\n internal\n returns (LibFillResults.FillResults memory totalFillResults);\n\n /// @dev Synchronously executes multiple fill orders in a single transaction until total amount is bought by taker.\n /// Returns false if the transaction would otherwise revert.\n /// The asset being sold by taker must always be WETH.\n /// @param orders Array of order specifications.\n /// @param makerAssetFillAmount Desired amount of makerAsset to buy.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketBuyExactAmountWithWeth(\n LibOrder.Order[] memory orders,\n uint256 makerAssetFillAmount,\n bytes[] memory signatures\n )\n internal\n returns (LibFillResults.FillResults memory totalFillResults);\n\n /// @dev Buys zrxBuyAmount of ZRX fee tokens, taking into account ZRX fees for each order. This will guarantee\n /// that at least zrxBuyAmount of ZRX is purchased (sometimes slightly over due to rounding issues).\n /// It is possible that a request to buy 200 ZRX will require purchasing 202 ZRX\n /// as 2 ZRX is required to purchase the 200 ZRX fee tokens. This guarantees at least 200 ZRX for future purchases.\n /// The asset being sold by taker must always be WETH. \n /// @param orders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset.\n /// @param zrxBuyAmount Desired amount of ZRX to buy.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return totalFillResults Amounts filled and fees paid by maker and taker.\n function marketBuyExactZrxWithWeth(\n LibOrder.Order[] memory orders,\n uint256 zrxBuyAmount,\n bytes[] memory signatures\n )\n internal\n returns (LibFillResults.FillResults memory totalFillResults);\n}\n", + "2.0.0/extensions/Forwarder/mixins/MWeth.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\ncontract MWeth {\n\n /// @dev Converts message call's ETH value into WETH.\n function convertEthToWeth()\n internal;\n\n /// @dev Transfers feePercentage of WETH spent on primary orders to feeRecipient.\n /// Refunds any excess ETH to msg.sender.\n /// @param wethSoldExcludingFeeOrders Amount of WETH sold when filling primary orders.\n /// @param wethSoldForZrx Amount of WETH sold when purchasing ZRX required for primary order fees.\n /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.\n /// @param feeRecipient Address that will receive ETH when orders are filled.\n function transferEthFeeAndRefund(\n uint256 wethSoldExcludingFeeOrders,\n uint256 wethSoldForZrx,\n uint256 feePercentage,\n address feeRecipient\n )\n internal;\n}\n", + "2.0.0/extensions/OrderValidator/OrderValidator.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../../protocol/Exchange/interfaces/IExchange.sol\";\nimport \"../../protocol/Exchange/libs/LibOrder.sol\";\nimport \"../../tokens/ERC20Token/IERC20Token.sol\";\nimport \"../../tokens/ERC721Token/IERC721Token.sol\";\nimport \"../../utils/LibBytes/LibBytes.sol\";\n\n\ncontract OrderValidator {\n\n using LibBytes for bytes;\n\n bytes4 constant internal ERC20_DATA_ID = bytes4(keccak256(\"ERC20Token(address)\"));\n bytes4 constant internal ERC721_DATA_ID = bytes4(keccak256(\"ERC721Token(address,uint256)\"));\n\n struct TraderInfo {\n uint256 makerBalance; // Maker's balance of makerAsset\n uint256 makerAllowance; // Maker's allowance to corresponding AssetProxy\n uint256 takerBalance; // Taker's balance of takerAsset\n uint256 takerAllowance; // Taker's allowance to corresponding AssetProxy\n uint256 makerZrxBalance; // Maker's balance of ZRX\n uint256 makerZrxAllowance; // Maker's allowance of ZRX to ERC20Proxy\n uint256 takerZrxBalance; // Taker's balance of ZRX\n uint256 takerZrxAllowance; // Taker's allowance of ZRX to ERC20Proxy\n }\n\n // solhint-disable var-name-mixedcase\n IExchange internal EXCHANGE;\n bytes internal ZRX_ASSET_DATA;\n // solhint-enable var-name-mixedcase\n\n constructor (address _exchange, bytes memory _zrxAssetData)\n public\n {\n EXCHANGE = IExchange(_exchange);\n ZRX_ASSET_DATA = _zrxAssetData;\n }\n\n /// @dev Fetches information for order and maker/taker of order.\n /// @param order The order structure.\n /// @param takerAddress Address that will be filling the order.\n /// @return OrderInfo and TraderInfo instances for given order.\n function getOrderAndTraderInfo(LibOrder.Order memory order, address takerAddress)\n public\n view\n returns (LibOrder.OrderInfo memory orderInfo, TraderInfo memory traderInfo)\n {\n orderInfo = EXCHANGE.getOrderInfo(order);\n traderInfo = getTraderInfo(order, takerAddress);\n return (orderInfo, traderInfo);\n }\n\n /// @dev Fetches information for all passed in orders and the makers/takers of each order.\n /// @param orders Array of order specifications.\n /// @param takerAddresses Array of taker addresses corresponding to each order.\n /// @return Arrays of OrderInfo and TraderInfo instances that correspond to each order.\n function getOrdersAndTradersInfo(LibOrder.Order[] memory orders, address[] memory takerAddresses)\n public\n view\n returns (LibOrder.OrderInfo[] memory ordersInfo, TraderInfo[] memory tradersInfo)\n {\n ordersInfo = EXCHANGE.getOrdersInfo(orders);\n tradersInfo = getTradersInfo(orders, takerAddresses);\n return (ordersInfo, tradersInfo);\n }\n\n /// @dev Fetches balance and allowances for maker and taker of order.\n /// @param order The order structure.\n /// @param takerAddress Address that will be filling the order.\n /// @return Balances and allowances of maker and taker of order.\n function getTraderInfo(LibOrder.Order memory order, address takerAddress)\n public\n view\n returns (TraderInfo memory traderInfo)\n {\n (traderInfo.makerBalance, traderInfo.makerAllowance) = getBalanceAndAllowance(order.makerAddress, order.makerAssetData);\n (traderInfo.takerBalance, traderInfo.takerAllowance) = getBalanceAndAllowance(takerAddress, order.takerAssetData);\n bytes memory zrxAssetData = ZRX_ASSET_DATA;\n (traderInfo.makerZrxBalance, traderInfo.makerZrxAllowance) = getBalanceAndAllowance(order.makerAddress, zrxAssetData);\n (traderInfo.takerZrxBalance, traderInfo.takerZrxAllowance) = getBalanceAndAllowance(takerAddress, zrxAssetData);\n return traderInfo;\n }\n\n /// @dev Fetches balances and allowances of maker and taker for each provided order.\n /// @param orders Array of order specifications.\n /// @param takerAddresses Array of taker addresses corresponding to each order.\n /// @return Array of balances and allowances for maker and taker of each order.\n function getTradersInfo(LibOrder.Order[] memory orders, address[] memory takerAddresses)\n public\n view\n returns (TraderInfo[] memory)\n {\n uint256 ordersLength = orders.length;\n TraderInfo[] memory tradersInfo = new TraderInfo[](ordersLength);\n for (uint256 i = 0; i != ordersLength; i++) {\n tradersInfo[i] = getTraderInfo(orders[i], takerAddresses[i]);\n }\n return tradersInfo;\n }\n\n /// @dev Fetches token balances and allowances of an address to given assetProxy. Supports ERC20 and ERC721.\n /// @param target Address to fetch balances and allowances of.\n /// @param assetData Encoded data that can be decoded by a specified proxy contract when transferring asset.\n /// @return Balance of asset and allowance set to given proxy of asset.\n /// For ERC721 tokens, these values will always be 1 or 0.\n function getBalanceAndAllowance(address target, bytes memory assetData)\n public\n view\n returns (uint256 balance, uint256 allowance)\n {\n bytes4 assetProxyId = assetData.readBytes4(0);\n address token = assetData.readAddress(16);\n address assetProxy = EXCHANGE.getAssetProxy(assetProxyId);\n\n if (assetProxyId == ERC20_DATA_ID) {\n // Query balance\n balance = IERC20Token(token).balanceOf(target);\n\n // Query allowance\n allowance = IERC20Token(token).allowance(target, assetProxy);\n } else if (assetProxyId == ERC721_DATA_ID) {\n uint256 tokenId = assetData.readUint256(36);\n\n // Query owner of tokenId\n address owner = getERC721TokenOwner(token, tokenId);\n\n // Set balance to 1 if tokenId is owned by target\n balance = target == owner ? 1 : 0;\n\n // Check if ERC721Proxy is approved to spend tokenId\n bool isApproved = IERC721Token(token).isApprovedForAll(target, assetProxy) || IERC721Token(token).getApproved(tokenId) == assetProxy;\n \n // Set alowance to 1 if ERC721Proxy is approved to spend tokenId\n allowance = isApproved ? 1 : 0;\n } else {\n revert(\"UNSUPPORTED_ASSET_PROXY\");\n }\n return (balance, allowance);\n }\n\n /// @dev Fetches token balances and allowances of an address for each given assetProxy. Supports ERC20 and ERC721.\n /// @param target Address to fetch balances and allowances of.\n /// @param assetData Array of encoded byte arrays that can be decoded by a specified proxy contract when transferring asset.\n /// @return Balances and allowances of assets.\n /// For ERC721 tokens, these values will always be 1 or 0.\n function getBalancesAndAllowances(address target, bytes[] memory assetData)\n public\n view\n returns (uint256[] memory, uint256[] memory)\n {\n uint256 length = assetData.length;\n uint256[] memory balances = new uint256[](length);\n uint256[] memory allowances = new uint256[](length);\n for (uint256 i = 0; i != length; i++) {\n (balances[i], allowances[i]) = getBalanceAndAllowance(target, assetData[i]);\n }\n return (balances, allowances);\n }\n\n /// @dev Calls `token.ownerOf(tokenId)`, but returns a null owner instead of reverting on an unowned token.\n /// @param token Address of ERC721 token.\n /// @param tokenId The identifier for the specific NFT.\n /// @return Owner of tokenId or null address if unowned.\n function getERC721TokenOwner(address token, uint256 tokenId)\n public\n view\n returns (address owner)\n {\n assembly {\n // load free memory pointer\n let cdStart := mload(64)\n\n // bytes4(keccak256(ownerOf(uint256))) = 0x6352211e\n mstore(cdStart, 0x6352211e00000000000000000000000000000000000000000000000000000000)\n mstore(add(cdStart, 4), tokenId)\n\n // staticcall `ownerOf(tokenId)`\n // `ownerOf` will revert if tokenId is not owned\n let success := staticcall(\n gas, // forward all gas\n token, // call token contract\n cdStart, // start of calldata\n 36, // length of input is 36 bytes\n cdStart, // write output over input\n 32 // size of output is 32 bytes\n )\n\n // Success implies that tokenId is owned\n // Copy owner from return data if successful\n if success {\n owner := mload(cdStart)\n } \n }\n\n // Owner initialized to address(0), no need to modify if call is unsuccessful\n return owner;\n }\n}\n", + "2.0.0/multisig/MultiSigWallet.sol": "// solhint-disable\npragma solidity ^0.4.15;\n\n\n/// @title Multisignature wallet - Allows multiple parties to agree on transactions before execution.\n/// @author Stefan George - \ncontract MultiSigWallet {\n\n /*\n * Events\n */\n event Confirmation(address indexed sender, uint indexed transactionId);\n event Revocation(address indexed sender, uint indexed transactionId);\n event Submission(uint indexed transactionId);\n event Execution(uint indexed transactionId);\n event ExecutionFailure(uint indexed transactionId);\n event Deposit(address indexed sender, uint value);\n event OwnerAddition(address indexed owner);\n event OwnerRemoval(address indexed owner);\n event RequirementChange(uint required);\n\n /*\n * Constants\n */\n uint constant public MAX_OWNER_COUNT = 50;\n\n /*\n * Storage\n */\n mapping (uint => Transaction) public transactions;\n mapping (uint => mapping (address => bool)) public confirmations;\n mapping (address => bool) public isOwner;\n address[] public owners;\n uint public required;\n uint public transactionCount;\n\n struct Transaction {\n address destination;\n uint value;\n bytes data;\n bool executed;\n }\n\n /*\n * Modifiers\n */\n modifier onlyWallet() {\n require(msg.sender == address(this));\n _;\n }\n\n modifier ownerDoesNotExist(address owner) {\n require(!isOwner[owner]);\n _;\n }\n\n modifier ownerExists(address owner) {\n require(isOwner[owner]);\n _;\n }\n\n modifier transactionExists(uint transactionId) {\n require(transactions[transactionId].destination != 0);\n _;\n }\n\n modifier confirmed(uint transactionId, address owner) {\n require(confirmations[transactionId][owner]);\n _;\n }\n\n modifier notConfirmed(uint transactionId, address owner) {\n require(!confirmations[transactionId][owner]);\n _;\n }\n\n modifier notExecuted(uint transactionId) {\n require(!transactions[transactionId].executed);\n _;\n }\n\n modifier notNull(address _address) {\n require(_address != 0);\n _;\n }\n\n modifier validRequirement(uint ownerCount, uint _required) {\n require(ownerCount <= MAX_OWNER_COUNT\n && _required <= ownerCount\n && _required != 0\n && ownerCount != 0);\n _;\n }\n\n /// @dev Fallback function allows to deposit ether.\n function()\n payable\n {\n if (msg.value > 0)\n Deposit(msg.sender, msg.value);\n }\n\n /*\n * Public functions\n */\n /// @dev Contract constructor sets initial owners and required number of confirmations.\n /// @param _owners List of initial owners.\n /// @param _required Number of required confirmations.\n function MultiSigWallet(address[] _owners, uint _required)\n public\n validRequirement(_owners.length, _required)\n {\n for (uint i=0; i<_owners.length; i++) {\n require(!isOwner[_owners[i]] && _owners[i] != 0);\n isOwner[_owners[i]] = true;\n }\n owners = _owners;\n required = _required;\n }\n\n /// @dev Allows to add a new owner. Transaction has to be sent by wallet.\n /// @param owner Address of new owner.\n function addOwner(address owner)\n public\n onlyWallet\n ownerDoesNotExist(owner)\n notNull(owner)\n validRequirement(owners.length + 1, required)\n {\n isOwner[owner] = true;\n owners.push(owner);\n OwnerAddition(owner);\n }\n\n /// @dev Allows to remove an owner. Transaction has to be sent by wallet.\n /// @param owner Address of owner.\n function removeOwner(address owner)\n public\n onlyWallet\n ownerExists(owner)\n {\n isOwner[owner] = false;\n for (uint i=0; i owners.length)\n changeRequirement(owners.length);\n OwnerRemoval(owner);\n }\n\n /// @dev Allows to replace an owner with a new owner. Transaction has to be sent by wallet.\n /// @param owner Address of owner to be replaced.\n /// @param newOwner Address of new owner.\n function replaceOwner(address owner, address newOwner)\n public\n onlyWallet\n ownerExists(owner)\n ownerDoesNotExist(newOwner)\n {\n for (uint i=0; i\n// solhint-disable not-rely-on-time\ncontract MultiSigWalletWithTimeLock is\n MultiSigWallet\n{\n event ConfirmationTimeSet(uint256 indexed transactionId, uint256 confirmationTime);\n event TimeLockChange(uint256 secondsTimeLocked);\n\n uint256 public secondsTimeLocked;\n\n mapping (uint256 => uint256) public confirmationTimes;\n\n modifier notFullyConfirmed(uint256 transactionId) {\n require(\n !isConfirmed(transactionId),\n \"TX_FULLY_CONFIRMED\"\n );\n _;\n }\n\n modifier fullyConfirmed(uint256 transactionId) {\n require(\n isConfirmed(transactionId),\n \"TX_NOT_FULLY_CONFIRMED\"\n );\n _;\n }\n\n modifier pastTimeLock(uint256 transactionId) {\n require(\n block.timestamp >= confirmationTimes[transactionId] + secondsTimeLocked,\n \"TIME_LOCK_INCOMPLETE\"\n );\n _;\n }\n\n /// @dev Contract constructor sets initial owners, required number of confirmations, and time lock.\n /// @param _owners List of initial owners.\n /// @param _required Number of required confirmations.\n /// @param _secondsTimeLocked Duration needed after a transaction is confirmed and before it becomes executable, in seconds.\n constructor (\n address[] _owners,\n uint256 _required,\n uint256 _secondsTimeLocked\n )\n public\n MultiSigWallet(_owners, _required)\n {\n secondsTimeLocked = _secondsTimeLocked;\n }\n\n /// @dev Changes the duration of the time lock for transactions.\n /// @param _secondsTimeLocked Duration needed after a transaction is confirmed and before it becomes executable, in seconds.\n function changeTimeLock(uint256 _secondsTimeLocked)\n public\n onlyWallet\n {\n secondsTimeLocked = _secondsTimeLocked;\n emit TimeLockChange(_secondsTimeLocked);\n }\n\n /// @dev Allows an owner to confirm a transaction.\n /// @param transactionId Transaction ID.\n function confirmTransaction(uint256 transactionId)\n public\n ownerExists(msg.sender)\n transactionExists(transactionId)\n notConfirmed(transactionId, msg.sender)\n notFullyConfirmed(transactionId)\n {\n confirmations[transactionId][msg.sender] = true;\n emit Confirmation(msg.sender, transactionId);\n if (isConfirmed(transactionId)) {\n setConfirmationTime(transactionId, block.timestamp);\n }\n }\n\n /// @dev Allows anyone to execute a confirmed transaction.\n /// @param transactionId Transaction ID.\n function executeTransaction(uint256 transactionId)\n public\n notExecuted(transactionId)\n fullyConfirmed(transactionId)\n pastTimeLock(transactionId)\n {\n Transaction storage txn = transactions[transactionId];\n txn.executed = true;\n if (external_call(txn.destination, txn.value, txn.data.length, txn.data)) {\n emit Execution(transactionId);\n } else {\n emit ExecutionFailure(transactionId);\n txn.executed = false;\n }\n }\n\n /// @dev Sets the time of when a submission first passed.\n function setConfirmationTime(uint256 transactionId, uint256 confirmationTime)\n internal\n {\n confirmationTimes[transactionId] = confirmationTime;\n emit ConfirmationTimeSet(transactionId, confirmationTime);\n }\n}\n", + "2.0.0/protocol/AssetProxy/ERC20Proxy.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"./MixinAuthorizable.sol\";\n\n\ncontract ERC20Proxy is\n MixinAuthorizable\n{\n // Id of this proxy.\n bytes4 constant internal PROXY_ID = bytes4(keccak256(\"ERC20Token(address)\"));\n \n // solhint-disable-next-line payable-fallback\n function () \n external\n {\n assembly {\n // The first 4 bytes of calldata holds the function selector\n let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000)\n\n // `transferFrom` will be called with the following parameters:\n // assetData Encoded byte array.\n // from Address to transfer asset from.\n // to Address to transfer asset to.\n // amount Amount of asset to transfer.\n // bytes4(keccak256(\"transferFrom(bytes,address,address,uint256)\")) = 0xa85e59e4\n if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) {\n\n // To lookup a value in a mapping, we load from the storage location keccak256(k, p),\n // where k is the key left padded to 32 bytes and p is the storage slot\n let start := mload(64)\n mstore(start, and(caller, 0xffffffffffffffffffffffffffffffffffffffff))\n mstore(add(start, 32), authorized_slot)\n\n // Revert if authorized[msg.sender] == false\n if iszero(sload(keccak256(start, 64))) {\n // Revert with `Error(\"SENDER_NOT_AUTHORIZED\")`\n mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)\n mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)\n mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000)\n mstore(96, 0)\n revert(0, 100)\n }\n\n // `transferFrom`.\n // The function is marked `external`, so no abi decodeding is done for\n // us. Instead, we expect the `calldata` memory to contain the\n // following:\n //\n // | Area | Offset | Length | Contents |\n // |----------|--------|---------|-------------------------------------|\n // | Header | 0 | 4 | function selector |\n // | Params | | 4 * 32 | function parameters: |\n // | | 4 | | 1. offset to assetData (*) |\n // | | 36 | | 2. from |\n // | | 68 | | 3. to |\n // | | 100 | | 4. amount |\n // | Data | | | assetData: |\n // | | 132 | 32 | assetData Length |\n // | | 164 | ** | assetData Contents |\n //\n // (*): offset is computed from start of function parameters, so offset\n // by an additional 4 bytes in the calldata.\n //\n // (**): see table below to compute length of assetData Contents\n //\n // WARNING: The ABIv2 specification allows additional padding between\n // the Params and Data section. This will result in a larger\n // offset to assetData.\n\n // Asset data itself is encoded as follows:\n //\n // | Area | Offset | Length | Contents |\n // |----------|--------|---------|-------------------------------------|\n // | Header | 0 | 4 | function selector |\n // | Params | | 1 * 32 | function parameters: |\n // | | 4 | 12 + 20 | 1. token address |\n\n // We construct calldata for the `token.transferFrom` ABI.\n // The layout of this calldata is in the table below.\n //\n // | Area | Offset | Length | Contents |\n // |----------|--------|---------|-------------------------------------|\n // | Header | 0 | 4 | function selector |\n // | Params | | 3 * 32 | function parameters: |\n // | | 4 | | 1. from |\n // | | 36 | | 2. to |\n // | | 68 | | 3. amount |\n\n /////// Read token address from calldata ///////\n // * The token address is stored in `assetData`.\n //\n // * The \"offset to assetData\" is stored at offset 4 in the calldata (table 1).\n // [assetDataOffsetFromParams = calldataload(4)]\n //\n // * Notes that the \"offset to assetData\" is relative to the \"Params\" area of calldata;\n // add 4 bytes to account for the length of the \"Header\" area (table 1).\n // [assetDataOffsetFromHeader = assetDataOffsetFromParams + 4]\n //\n // * The \"token address\" is offset 32+4=36 bytes into \"assetData\" (tables 1 & 2).\n // [tokenOffset = assetDataOffsetFromHeader + 36 = calldataload(4) + 4 + 36]\n let token := calldataload(add(calldataload(4), 40))\n \n /////// Setup Header Area ///////\n // This area holds the 4-byte `transferFrom` selector.\n // Any trailing data in transferFromSelector will be\n // overwritten in the next `mstore` call.\n mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000)\n \n /////// Setup Params Area ///////\n // We copy the fields `from`, `to` and `amount` in bulk\n // from our own calldata to the new calldata.\n calldatacopy(4, 36, 96)\n\n /////// Call `token.transferFrom` using the calldata ///////\n let success := call(\n gas, // forward all gas\n token, // call address of token contract\n 0, // don't send any ETH\n 0, // pointer to start of input\n 100, // length of input\n 0, // write output over input\n 32 // output size should be 32 bytes\n )\n\n /////// Check return data. ///////\n // If there is no return data, we assume the token incorrectly\n // does not return a bool. In this case we expect it to revert\n // on failure, which was handled above.\n // If the token does return data, we require that it is a single\n // nonzero 32 bytes value.\n // So the transfer succeeded if the call succeeded and either\n // returned nothing, or returned a non-zero 32 byte value. \n success := and(success, or(\n iszero(returndatasize),\n and(\n eq(returndatasize, 32),\n gt(mload(0), 0)\n )\n ))\n if success {\n return(0, 0)\n }\n \n // Revert with `Error(\"TRANSFER_FAILED\")`\n mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)\n mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)\n mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000)\n mstore(96, 0)\n revert(0, 100)\n }\n\n // Revert if undefined function is called\n revert(0, 0)\n }\n }\n\n /// @dev Gets the proxy id associated with the proxy address.\n /// @return Proxy id.\n function getProxyId()\n external\n pure\n returns (bytes4)\n {\n return PROXY_ID;\n }\n}\n", + "2.0.0/protocol/AssetProxy/ERC721Proxy.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"./MixinAuthorizable.sol\";\n\n\ncontract ERC721Proxy is\n MixinAuthorizable\n{\n // Id of this proxy.\n bytes4 constant internal PROXY_ID = bytes4(keccak256(\"ERC721Token(address,uint256)\"));\n\n // solhint-disable-next-line payable-fallback\n function () \n external\n {\n assembly {\n // The first 4 bytes of calldata holds the function selector\n let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000)\n\n // `transferFrom` will be called with the following parameters:\n // assetData Encoded byte array.\n // from Address to transfer asset from.\n // to Address to transfer asset to.\n // amount Amount of asset to transfer.\n // bytes4(keccak256(\"transferFrom(bytes,address,address,uint256)\")) = 0xa85e59e4\n if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) {\n\n // To lookup a value in a mapping, we load from the storage location keccak256(k, p),\n // where k is the key left padded to 32 bytes and p is the storage slot\n let start := mload(64)\n mstore(start, and(caller, 0xffffffffffffffffffffffffffffffffffffffff))\n mstore(add(start, 32), authorized_slot)\n\n // Revert if authorized[msg.sender] == false\n if iszero(sload(keccak256(start, 64))) {\n // Revert with `Error(\"SENDER_NOT_AUTHORIZED\")`\n mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)\n mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)\n mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000)\n mstore(96, 0)\n revert(0, 100)\n }\n\n // `transferFrom`.\n // The function is marked `external`, so no abi decodeding is done for\n // us. Instead, we expect the `calldata` memory to contain the\n // following:\n //\n // | Area | Offset | Length | Contents |\n // |----------|--------|---------|-------------------------------------|\n // | Header | 0 | 4 | function selector |\n // | Params | | 4 * 32 | function parameters: |\n // | | 4 | | 1. offset to assetData (*) |\n // | | 36 | | 2. from |\n // | | 68 | | 3. to |\n // | | 100 | | 4. amount |\n // | Data | | | assetData: |\n // | | 132 | 32 | assetData Length |\n // | | 164 | ** | assetData Contents |\n //\n // (*): offset is computed from start of function parameters, so offset\n // by an additional 4 bytes in the calldata.\n //\n // (**): see table below to compute length of assetData Contents\n //\n // WARNING: The ABIv2 specification allows additional padding between\n // the Params and Data section. This will result in a larger\n // offset to assetData.\n\n // Asset data itself is encoded as follows:\n //\n // | Area | Offset | Length | Contents |\n // |----------|--------|---------|-------------------------------------|\n // | Header | 0 | 4 | function selector |\n // | Params | | 2 * 32 | function parameters: |\n // | | 4 | 12 + 20 | 1. token address |\n // | | 36 | | 2. tokenId |\n \n // We construct calldata for the `token.transferFrom` ABI.\n // The layout of this calldata is in the table below.\n // \n // | Area | Offset | Length | Contents |\n // |----------|--------|---------|-------------------------------------|\n // | Header | 0 | 4 | function selector |\n // | Params | | 3 * 32 | function parameters: |\n // | | 4 | | 1. from |\n // | | 36 | | 2. to |\n // | | 68 | | 3. tokenId |\n\n // There exists only 1 of each token.\n // require(amount == 1, \"INVALID_AMOUNT\")\n if sub(calldataload(100), 1) {\n // Revert with `Error(\"INVALID_AMOUNT\")`\n mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)\n mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)\n mstore(64, 0x0000000e494e56414c49445f414d4f554e540000000000000000000000000000)\n mstore(96, 0)\n revert(0, 100)\n }\n\n /////// Setup Header Area ///////\n // This area holds the 4-byte `transferFrom` selector.\n // Any trailing data in transferFromSelector will be\n // overwritten in the next `mstore` call.\n mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000)\n \n /////// Setup Params Area ///////\n // We copy the fields `from` and `to` in bulk\n // from our own calldata to the new calldata.\n calldatacopy(4, 36, 64)\n\n // Copy `tokenId` field from our own calldata to the new calldata.\n let assetDataOffset := calldataload(4)\n calldatacopy(68, add(assetDataOffset, 72), 32)\n\n /////// Call `token.transferFrom` using the calldata ///////\n let token := calldataload(add(assetDataOffset, 40))\n let success := call(\n gas, // forward all gas\n token, // call address of token contract\n 0, // don't send any ETH\n 0, // pointer to start of input\n 100, // length of input\n 0, // write output to null\n 0 // output size is 0 bytes\n )\n if success {\n return(0, 0)\n }\n \n // Revert with `Error(\"TRANSFER_FAILED\")`\n mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)\n mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)\n mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000)\n mstore(96, 0)\n revert(0, 100)\n }\n\n // Revert if undefined function is called\n revert(0, 0)\n }\n }\n\n /// @dev Gets the proxy id associated with the proxy address.\n /// @return Proxy id.\n function getProxyId()\n external\n pure\n returns (bytes4)\n {\n return PROXY_ID;\n }\n}\n", + "2.0.0/protocol/AssetProxy/MixinAuthorizable.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../utils/Ownable/Ownable.sol\";\nimport \"./mixins/MAuthorizable.sol\";\n\n\ncontract MixinAuthorizable is\n Ownable,\n MAuthorizable\n{\n /// @dev Only authorized addresses can invoke functions with this modifier.\n modifier onlyAuthorized {\n require(\n authorized[msg.sender],\n \"SENDER_NOT_AUTHORIZED\"\n );\n _;\n }\n\n mapping (address => bool) public authorized;\n address[] public authorities;\n\n /// @dev Authorizes an address.\n /// @param target Address to authorize.\n function addAuthorizedAddress(address target)\n external\n onlyOwner\n {\n require(\n !authorized[target],\n \"TARGET_ALREADY_AUTHORIZED\"\n );\n\n authorized[target] = true;\n authorities.push(target);\n emit AuthorizedAddressAdded(target, msg.sender);\n }\n\n /// @dev Removes authorizion of an address.\n /// @param target Address to remove authorization from.\n function removeAuthorizedAddress(address target)\n external\n onlyOwner\n {\n require(\n authorized[target],\n \"TARGET_NOT_AUTHORIZED\"\n );\n\n delete authorized[target];\n for (uint256 i = 0; i < authorities.length; i++) {\n if (authorities[i] == target) {\n authorities[i] = authorities[authorities.length - 1];\n authorities.length -= 1;\n break;\n }\n }\n emit AuthorizedAddressRemoved(target, msg.sender);\n }\n\n /// @dev Removes authorizion of an address.\n /// @param target Address to remove authorization from.\n /// @param index Index of target in authorities array.\n function removeAuthorizedAddressAtIndex(\n address target,\n uint256 index\n )\n external\n onlyOwner\n {\n require(\n authorized[target],\n \"TARGET_NOT_AUTHORIZED\"\n );\n require(\n index < authorities.length,\n \"INDEX_OUT_OF_BOUNDS\"\n );\n require(\n authorities[index] == target,\n \"AUTHORIZED_ADDRESS_MISMATCH\"\n );\n\n delete authorized[target];\n authorities[index] = authorities[authorities.length - 1];\n authorities.length -= 1;\n emit AuthorizedAddressRemoved(target, msg.sender);\n }\n\n /// @dev Gets all authorized addresses.\n /// @return Array of authorized addresses.\n function getAuthorizedAddresses()\n external\n view\n returns (address[] memory)\n {\n return authorities;\n }\n}\n", + "2.0.0/protocol/AssetProxy/interfaces/IAssetProxy.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"./IAuthorizable.sol\";\n\n\ncontract IAssetProxy is\n IAuthorizable\n{\n /// @dev Transfers assets. Either succeeds or throws.\n /// @param assetData Byte array encoded for the respective asset proxy.\n /// @param from Address to transfer asset from.\n /// @param to Address to transfer asset to.\n /// @param amount Amount of asset to transfer.\n function transferFrom(\n bytes assetData,\n address from,\n address to,\n uint256 amount\n )\n external;\n \n /// @dev Gets the proxy id associated with the proxy address.\n /// @return Proxy id.\n function getProxyId()\n external\n pure\n returns (bytes4);\n}\n", + "2.0.0/protocol/AssetProxy/interfaces/IAuthorizable.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../../utils/Ownable/IOwnable.sol\";\n\n\ncontract IAuthorizable is\n IOwnable\n{\n /// @dev Authorizes an address.\n /// @param target Address to authorize.\n function addAuthorizedAddress(address target)\n external;\n\n /// @dev Removes authorizion of an address.\n /// @param target Address to remove authorization from.\n function removeAuthorizedAddress(address target)\n external;\n\n /// @dev Removes authorizion of an address.\n /// @param target Address to remove authorization from.\n /// @param index Index of target in authorities array.\n function removeAuthorizedAddressAtIndex(\n address target,\n uint256 index\n )\n external;\n \n /// @dev Gets all authorized addresses.\n /// @return Array of authorized addresses.\n function getAuthorizedAddresses()\n external\n view\n returns (address[] memory);\n}\n", + "2.0.0/protocol/AssetProxy/mixins/MAuthorizable.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../interfaces/IAuthorizable.sol\";\n\n\ncontract MAuthorizable is\n IAuthorizable\n{\n // Event logged when a new address is authorized.\n event AuthorizedAddressAdded(\n address indexed target,\n address indexed caller\n );\n\n // Event logged when a currently authorized address is unauthorized.\n event AuthorizedAddressRemoved(\n address indexed target,\n address indexed caller\n );\n\n /// @dev Only authorized addresses can invoke functions with this modifier.\n modifier onlyAuthorized { revert(); _; }\n}\n", + "2.0.0/protocol/AssetProxyOwner/AssetProxyOwner.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../multisig/MultiSigWalletWithTimeLock.sol\";\nimport \"../../utils/LibBytes/LibBytes.sol\";\n\n\ncontract AssetProxyOwner is\n MultiSigWalletWithTimeLock\n{\n using LibBytes for bytes;\n\n event AssetProxyRegistration(address assetProxyContract, bool isRegistered);\n\n // Mapping of AssetProxy contract address =>\n // if this contract is allowed to call the AssetProxy's `removeAuthorizedAddressAtIndex` method without a time lock.\n mapping (address => bool) public isAssetProxyRegistered;\n\n bytes4 constant internal REMOVE_AUTHORIZED_ADDRESS_AT_INDEX_SELECTOR = bytes4(keccak256(\"removeAuthorizedAddressAtIndex(address,uint256)\"));\n\n /// @dev Function will revert if the transaction does not call `removeAuthorizedAddressAtIndex`\n /// on an approved AssetProxy contract.\n modifier validRemoveAuthorizedAddressAtIndexTx(uint256 transactionId) {\n Transaction storage txn = transactions[transactionId];\n require(\n isAssetProxyRegistered[txn.destination],\n \"UNREGISTERED_ASSET_PROXY\"\n );\n require(\n txn.data.readBytes4(0) == REMOVE_AUTHORIZED_ADDRESS_AT_INDEX_SELECTOR,\n \"INVALID_FUNCTION_SELECTOR\"\n );\n _;\n }\n\n /// @dev Contract constructor sets initial owners, required number of confirmations,\n /// time lock, and list of AssetProxy addresses.\n /// @param _owners List of initial owners.\n /// @param _assetProxyContracts Array of AssetProxy contract addresses.\n /// @param _required Number of required confirmations.\n /// @param _secondsTimeLocked Duration needed after a transaction is confirmed and before it becomes executable, in seconds.\n constructor (\n address[] memory _owners,\n address[] memory _assetProxyContracts,\n uint256 _required,\n uint256 _secondsTimeLocked\n )\n public\n MultiSigWalletWithTimeLock(_owners, _required, _secondsTimeLocked)\n {\n for (uint256 i = 0; i < _assetProxyContracts.length; i++) {\n address assetProxy = _assetProxyContracts[i];\n require(\n assetProxy != address(0),\n \"INVALID_ASSET_PROXY\"\n );\n isAssetProxyRegistered[assetProxy] = true;\n }\n }\n\n /// @dev Registers or deregisters an AssetProxy to be able to execute\n /// `removeAuthorizedAddressAtIndex` without a timelock.\n /// @param assetProxyContract Address of AssetProxy contract.\n /// @param isRegistered Status of approval for AssetProxy contract.\n function registerAssetProxy(address assetProxyContract, bool isRegistered)\n public\n onlyWallet\n notNull(assetProxyContract)\n {\n isAssetProxyRegistered[assetProxyContract] = isRegistered;\n emit AssetProxyRegistration(assetProxyContract, isRegistered);\n }\n\n /// @dev Allows execution of `removeAuthorizedAddressAtIndex` without time lock.\n /// @param transactionId Transaction ID.\n function executeRemoveAuthorizedAddressAtIndex(uint256 transactionId)\n public\n notExecuted(transactionId)\n fullyConfirmed(transactionId)\n validRemoveAuthorizedAddressAtIndexTx(transactionId)\n {\n Transaction storage txn = transactions[transactionId];\n txn.executed = true;\n if (external_call(txn.destination, txn.value, txn.data.length, txn.data)) {\n emit Execution(transactionId);\n } else {\n emit ExecutionFailure(transactionId);\n txn.executed = false;\n }\n }\n}\n", + "2.0.0/protocol/Exchange/Exchange.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"./MixinExchangeCore.sol\";\nimport \"./MixinSignatureValidator.sol\";\nimport \"./MixinWrapperFunctions.sol\";\nimport \"./MixinAssetProxyDispatcher.sol\";\nimport \"./MixinTransactions.sol\";\nimport \"./MixinMatchOrders.sol\";\n\n\n// solhint-disable no-empty-blocks\ncontract Exchange is\n MixinExchangeCore,\n MixinMatchOrders,\n MixinSignatureValidator,\n MixinTransactions,\n MixinAssetProxyDispatcher,\n MixinWrapperFunctions\n{\n string constant public VERSION = \"2.0.0\";\n\n // Mixins are instantiated in the order they are inherited\n constructor ()\n public\n MixinExchangeCore()\n MixinMatchOrders()\n MixinSignatureValidator()\n MixinTransactions()\n MixinAssetProxyDispatcher()\n MixinWrapperFunctions()\n {}\n}\n", + "2.0.0/protocol/Exchange/MixinAssetProxyDispatcher.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../utils/Ownable/Ownable.sol\";\nimport \"./mixins/MAssetProxyDispatcher.sol\";\nimport \"../AssetProxy/interfaces/IAssetProxy.sol\";\n\n\ncontract MixinAssetProxyDispatcher is\n Ownable,\n MAssetProxyDispatcher\n{\n // Mapping from Asset Proxy Id's to their respective Asset Proxy\n mapping (bytes4 => IAssetProxy) public assetProxies;\n\n /// @dev Registers an asset proxy to its asset proxy id.\n /// Once an asset proxy is registered, it cannot be unregistered.\n /// @param assetProxy Address of new asset proxy to register.\n function registerAssetProxy(address assetProxy)\n external\n onlyOwner\n {\n IAssetProxy assetProxyContract = IAssetProxy(assetProxy);\n\n // Ensure that no asset proxy exists with current id.\n bytes4 assetProxyId = assetProxyContract.getProxyId();\n address currentAssetProxy = assetProxies[assetProxyId];\n require(\n currentAssetProxy == address(0),\n \"ASSET_PROXY_ALREADY_EXISTS\"\n );\n\n // Add asset proxy and log registration.\n assetProxies[assetProxyId] = assetProxyContract;\n emit AssetProxyRegistered(\n assetProxyId,\n assetProxy\n );\n }\n\n /// @dev Gets an asset proxy.\n /// @param assetProxyId Id of the asset proxy.\n /// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered.\n function getAssetProxy(bytes4 assetProxyId)\n external\n view\n returns (address)\n {\n return assetProxies[assetProxyId];\n }\n\n /// @dev Forwards arguments to assetProxy and calls `transferFrom`. Either succeeds or throws.\n /// @param assetData Byte array encoded for the asset.\n /// @param from Address to transfer token from.\n /// @param to Address to transfer token to.\n /// @param amount Amount of token to transfer.\n function dispatchTransferFrom(\n bytes memory assetData,\n address from,\n address to,\n uint256 amount\n )\n internal\n {\n // Do nothing if no amount should be transferred.\n if (amount > 0 && from != to) {\n // Ensure assetData length is valid\n require(\n assetData.length > 3,\n \"LENGTH_GREATER_THAN_3_REQUIRED\"\n );\n \n // Lookup assetProxy. We do not use `LibBytes.readBytes4` for gas efficiency reasons.\n bytes4 assetProxyId;\n assembly {\n assetProxyId := and(mload(\n add(assetData, 32)),\n 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000\n )\n }\n address assetProxy = assetProxies[assetProxyId];\n\n // Ensure that assetProxy exists\n require(\n assetProxy != address(0),\n \"ASSET_PROXY_DOES_NOT_EXIST\"\n );\n \n // We construct calldata for the `assetProxy.transferFrom` ABI.\n // The layout of this calldata is in the table below.\n // \n // | Area | Offset | Length | Contents |\n // | -------- |--------|---------|-------------------------------------------- |\n // | Header | 0 | 4 | function selector |\n // | Params | | 4 * 32 | function parameters: |\n // | | 4 | | 1. offset to assetData (*) |\n // | | 36 | | 2. from |\n // | | 68 | | 3. to |\n // | | 100 | | 4. amount |\n // | Data | | | assetData: |\n // | | 132 | 32 | assetData Length |\n // | | 164 | ** | assetData Contents |\n\n assembly {\n /////// Setup State ///////\n // `cdStart` is the start of the calldata for `assetProxy.transferFrom` (equal to free memory ptr).\n let cdStart := mload(64)\n // `dataAreaLength` is the total number of words needed to store `assetData`\n // As-per the ABI spec, this value is padded up to the nearest multiple of 32,\n // and includes 32-bytes for length.\n let dataAreaLength := and(add(mload(assetData), 63), 0xFFFFFFFFFFFE0)\n // `cdEnd` is the end of the calldata for `assetProxy.transferFrom`.\n let cdEnd := add(cdStart, add(132, dataAreaLength))\n\n \n /////// Setup Header Area ///////\n // This area holds the 4-byte `transferFromSelector`.\n // bytes4(keccak256(\"transferFrom(bytes,address,address,uint256)\")) = 0xa85e59e4\n mstore(cdStart, 0xa85e59e400000000000000000000000000000000000000000000000000000000)\n \n /////// Setup Params Area ///////\n // Each parameter is padded to 32-bytes. The entire Params Area is 128 bytes.\n // Notes:\n // 1. The offset to `assetData` is the length of the Params Area (128 bytes).\n // 2. A 20-byte mask is applied to addresses to zero-out the unused bytes.\n mstore(add(cdStart, 4), 128)\n mstore(add(cdStart, 36), and(from, 0xffffffffffffffffffffffffffffffffffffffff))\n mstore(add(cdStart, 68), and(to, 0xffffffffffffffffffffffffffffffffffffffff))\n mstore(add(cdStart, 100), amount)\n \n /////// Setup Data Area ///////\n // This area holds `assetData`.\n let dataArea := add(cdStart, 132)\n // solhint-disable-next-line no-empty-blocks\n for {} lt(dataArea, cdEnd) {} {\n mstore(dataArea, mload(assetData))\n dataArea := add(dataArea, 32)\n assetData := add(assetData, 32)\n }\n\n /////// Call `assetProxy.transferFrom` using the constructed calldata ///////\n let success := call(\n gas, // forward all gas\n assetProxy, // call address of asset proxy\n 0, // don't send any ETH\n cdStart, // pointer to start of input\n sub(cdEnd, cdStart), // length of input \n cdStart, // write output over input\n 512 // reserve 512 bytes for output\n )\n if iszero(success) {\n revert(cdStart, returndatasize())\n }\n }\n }\n }\n}\n", + "2.0.0/protocol/Exchange/MixinExchangeCore.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../../utils/ReentrancyGuard/ReentrancyGuard.sol\";\nimport \"./libs/LibConstants.sol\";\nimport \"./libs/LibFillResults.sol\";\nimport \"./libs/LibOrder.sol\";\nimport \"./libs/LibMath.sol\";\nimport \"./mixins/MExchangeCore.sol\";\nimport \"./mixins/MSignatureValidator.sol\";\nimport \"./mixins/MTransactions.sol\";\nimport \"./mixins/MAssetProxyDispatcher.sol\";\n\n\ncontract MixinExchangeCore is\n ReentrancyGuard,\n LibConstants,\n LibMath,\n LibOrder,\n LibFillResults,\n MAssetProxyDispatcher,\n MExchangeCore,\n MSignatureValidator,\n MTransactions\n{\n // Mapping of orderHash => amount of takerAsset already bought by maker\n mapping (bytes32 => uint256) public filled;\n\n // Mapping of orderHash => cancelled\n mapping (bytes32 => bool) public cancelled;\n\n // Mapping of makerAddress => senderAddress => lowest salt an order can have in order to be fillable\n // Orders with specified senderAddress and with a salt less than their epoch are considered cancelled\n mapping (address => mapping (address => uint256)) public orderEpoch;\n\n /// @dev Cancels all orders created by makerAddress with a salt less than or equal to the targetOrderEpoch\n /// and senderAddress equal to msg.sender (or null address if msg.sender == makerAddress).\n /// @param targetOrderEpoch Orders created with a salt less or equal to this value will be cancelled.\n function cancelOrdersUpTo(uint256 targetOrderEpoch)\n external\n nonReentrant\n {\n address makerAddress = getCurrentContextAddress();\n // If this function is called via `executeTransaction`, we only update the orderEpoch for the makerAddress/msg.sender combination.\n // This allows external filter contracts to add rules to how orders are cancelled via this function.\n address senderAddress = makerAddress == msg.sender ? address(0) : msg.sender;\n\n // orderEpoch is initialized to 0, so to cancelUpTo we need salt + 1\n uint256 newOrderEpoch = targetOrderEpoch + 1; \n uint256 oldOrderEpoch = orderEpoch[makerAddress][senderAddress];\n\n // Ensure orderEpoch is monotonically increasing\n require(\n newOrderEpoch > oldOrderEpoch, \n \"INVALID_NEW_ORDER_EPOCH\"\n );\n\n // Update orderEpoch\n orderEpoch[makerAddress][senderAddress] = newOrderEpoch;\n emit CancelUpTo(\n makerAddress,\n senderAddress,\n newOrderEpoch\n );\n }\n\n /// @dev Fills the input order.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n /// @return Amounts filled and fees paid by maker and taker.\n function fillOrder(\n Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n public\n nonReentrant\n returns (FillResults memory fillResults)\n {\n fillResults = fillOrderInternal(\n order,\n takerAssetFillAmount,\n signature\n );\n return fillResults;\n }\n\n /// @dev After calling, the order can not be filled anymore.\n /// Throws if order is invalid or sender does not have permission to cancel.\n /// @param order Order to cancel. Order must be OrderStatus.FILLABLE.\n function cancelOrder(Order memory order)\n public\n nonReentrant\n {\n cancelOrderInternal(order);\n }\n\n /// @dev Gets information about an order: status, hash, and amount filled.\n /// @param order Order to gather information on.\n /// @return OrderInfo Information about the order and its state.\n /// See LibOrder.OrderInfo for a complete description.\n function getOrderInfo(Order memory order)\n public\n view\n returns (OrderInfo memory orderInfo)\n {\n // Compute the order hash\n orderInfo.orderHash = getOrderHash(order);\n\n // Fetch filled amount\n orderInfo.orderTakerAssetFilledAmount = filled[orderInfo.orderHash];\n\n // If order.makerAssetAmount is zero, we also reject the order.\n // While the Exchange contract handles them correctly, they create\n // edge cases in the supporting infrastructure because they have\n // an 'infinite' price when computed by a simple division.\n if (order.makerAssetAmount == 0) {\n orderInfo.orderStatus = uint8(OrderStatus.INVALID_MAKER_ASSET_AMOUNT);\n return orderInfo;\n }\n\n // If order.takerAssetAmount is zero, then the order will always\n // be considered filled because 0 == takerAssetAmount == orderTakerAssetFilledAmount\n // Instead of distinguishing between unfilled and filled zero taker\n // amount orders, we choose not to support them.\n if (order.takerAssetAmount == 0) {\n orderInfo.orderStatus = uint8(OrderStatus.INVALID_TAKER_ASSET_AMOUNT);\n return orderInfo;\n }\n\n // Validate order availability\n if (orderInfo.orderTakerAssetFilledAmount >= order.takerAssetAmount) {\n orderInfo.orderStatus = uint8(OrderStatus.FULLY_FILLED);\n return orderInfo;\n }\n\n // Validate order expiration\n // solhint-disable-next-line not-rely-on-time\n if (block.timestamp >= order.expirationTimeSeconds) {\n orderInfo.orderStatus = uint8(OrderStatus.EXPIRED);\n return orderInfo;\n }\n\n // Check if order has been cancelled\n if (cancelled[orderInfo.orderHash]) {\n orderInfo.orderStatus = uint8(OrderStatus.CANCELLED);\n return orderInfo;\n }\n if (orderEpoch[order.makerAddress][order.senderAddress] > order.salt) {\n orderInfo.orderStatus = uint8(OrderStatus.CANCELLED);\n return orderInfo;\n }\n\n // All other statuses are ruled out: order is Fillable\n orderInfo.orderStatus = uint8(OrderStatus.FILLABLE);\n return orderInfo;\n }\n\n /// @dev Fills the input order.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n /// @return Amounts filled and fees paid by maker and taker.\n function fillOrderInternal(\n Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n internal\n returns (FillResults memory fillResults)\n {\n // Fetch order info\n OrderInfo memory orderInfo = getOrderInfo(order);\n\n // Fetch taker address\n address takerAddress = getCurrentContextAddress();\n \n // Assert that the order is fillable by taker\n assertFillableOrder(\n order,\n orderInfo,\n takerAddress,\n signature\n );\n \n // Get amount of takerAsset to fill\n uint256 remainingTakerAssetAmount = safeSub(order.takerAssetAmount, orderInfo.orderTakerAssetFilledAmount);\n uint256 takerAssetFilledAmount = min256(takerAssetFillAmount, remainingTakerAssetAmount);\n\n // Validate context\n assertValidFill(\n order,\n orderInfo,\n takerAssetFillAmount,\n takerAssetFilledAmount,\n fillResults.makerAssetFilledAmount\n );\n\n // Compute proportional fill amounts\n fillResults = calculateFillResults(order, takerAssetFilledAmount);\n\n // Update exchange internal state\n updateFilledState(\n order,\n takerAddress,\n orderInfo.orderHash,\n orderInfo.orderTakerAssetFilledAmount,\n fillResults\n );\n \n // Settle order\n settleOrder(\n order,\n takerAddress,\n fillResults\n );\n\n return fillResults;\n }\n\n /// @dev After calling, the order can not be filled anymore.\n /// Throws if order is invalid or sender does not have permission to cancel.\n /// @param order Order to cancel. Order must be OrderStatus.FILLABLE.\n function cancelOrderInternal(Order memory order)\n internal\n {\n // Fetch current order status\n OrderInfo memory orderInfo = getOrderInfo(order);\n\n // Validate context\n assertValidCancel(order, orderInfo);\n\n // Perform cancel\n updateCancelledState(order, orderInfo.orderHash);\n }\n\n /// @dev Updates state with results of a fill order.\n /// @param order that was filled.\n /// @param takerAddress Address of taker who filled the order.\n /// @param orderTakerAssetFilledAmount Amount of order already filled.\n function updateFilledState(\n Order memory order,\n address takerAddress,\n bytes32 orderHash,\n uint256 orderTakerAssetFilledAmount,\n FillResults memory fillResults\n )\n internal\n {\n // Update state\n filled[orderHash] = safeAdd(orderTakerAssetFilledAmount, fillResults.takerAssetFilledAmount);\n\n // Log order\n emit Fill(\n order.makerAddress,\n order.feeRecipientAddress,\n takerAddress,\n msg.sender,\n fillResults.makerAssetFilledAmount,\n fillResults.takerAssetFilledAmount,\n fillResults.makerFeePaid,\n fillResults.takerFeePaid,\n orderHash,\n order.makerAssetData,\n order.takerAssetData\n );\n }\n\n /// @dev Updates state with results of cancelling an order.\n /// State is only updated if the order is currently fillable.\n /// Otherwise, updating state would have no effect.\n /// @param order that was cancelled.\n /// @param orderHash Hash of order that was cancelled.\n function updateCancelledState(\n Order memory order,\n bytes32 orderHash\n )\n internal\n {\n // Perform cancel\n cancelled[orderHash] = true;\n\n // Log cancel\n emit Cancel(\n order.makerAddress,\n order.feeRecipientAddress,\n msg.sender,\n orderHash,\n order.makerAssetData,\n order.takerAssetData\n );\n }\n \n /// @dev Validates context for fillOrder. Succeeds or throws.\n /// @param order to be filled.\n /// @param orderInfo OrderStatus, orderHash, and amount already filled of order.\n /// @param takerAddress Address of order taker.\n /// @param signature Proof that the orders was created by its maker.\n function assertFillableOrder(\n Order memory order,\n OrderInfo memory orderInfo,\n address takerAddress,\n bytes memory signature\n )\n internal\n view\n {\n // An order can only be filled if its status is FILLABLE.\n require(\n orderInfo.orderStatus == uint8(OrderStatus.FILLABLE),\n \"ORDER_UNFILLABLE\"\n );\n \n // Validate sender is allowed to fill this order\n if (order.senderAddress != address(0)) {\n require(\n order.senderAddress == msg.sender,\n \"INVALID_SENDER\"\n );\n }\n \n // Validate taker is allowed to fill this order\n if (order.takerAddress != address(0)) {\n require(\n order.takerAddress == takerAddress,\n \"INVALID_TAKER\"\n );\n }\n \n // Validate Maker signature (check only if first time seen)\n if (orderInfo.orderTakerAssetFilledAmount == 0) {\n require(\n isValidSignature(\n orderInfo.orderHash,\n order.makerAddress,\n signature\n ),\n \"INVALID_ORDER_SIGNATURE\"\n );\n }\n }\n \n /// @dev Validates context for fillOrder. Succeeds or throws.\n /// @param order to be filled.\n /// @param orderInfo OrderStatus, orderHash, and amount already filled of order.\n /// @param takerAssetFillAmount Desired amount of order to fill by taker.\n /// @param takerAssetFilledAmount Amount of takerAsset that will be filled.\n /// @param makerAssetFilledAmount Amount of makerAsset that will be transfered.\n function assertValidFill(\n Order memory order,\n OrderInfo memory orderInfo,\n uint256 takerAssetFillAmount, // TODO: use FillResults\n uint256 takerAssetFilledAmount,\n uint256 makerAssetFilledAmount\n )\n internal\n view\n {\n // Revert if fill amount is invalid\n // TODO: reconsider necessity for v2.1\n require(\n takerAssetFillAmount != 0,\n \"INVALID_TAKER_AMOUNT\"\n );\n \n // Make sure taker does not pay more than desired amount\n // NOTE: This assertion should never fail, it is here\n // as an extra defence against potential bugs.\n require(\n takerAssetFilledAmount <= takerAssetFillAmount,\n \"TAKER_OVERPAY\"\n );\n \n // Make sure order is not overfilled\n // NOTE: This assertion should never fail, it is here\n // as an extra defence against potential bugs.\n require(\n safeAdd(orderInfo.orderTakerAssetFilledAmount, takerAssetFilledAmount) <= order.takerAssetAmount,\n \"ORDER_OVERFILL\"\n );\n \n // Make sure order is filled at acceptable price.\n // The order has an implied price from the makers perspective:\n // order price = order.makerAssetAmount / order.takerAssetAmount\n // i.e. the number of makerAsset maker is paying per takerAsset. The\n // maker is guaranteed to get this price or a better (lower) one. The\n // actual price maker is getting in this fill is:\n // fill price = makerAssetFilledAmount / takerAssetFilledAmount\n // We need `fill price <= order price` for the fill to be fair to maker.\n // This amounts to:\n // makerAssetFilledAmount order.makerAssetAmount\n // ------------------------ <= -----------------------\n // takerAssetFilledAmount order.takerAssetAmount\n // or, equivalently:\n // makerAssetFilledAmount * order.takerAssetAmount <=\n // order.makerAssetAmount * takerAssetFilledAmount\n // NOTE: This assertion should never fail, it is here\n // as an extra defence against potential bugs.\n require(\n safeMul(makerAssetFilledAmount, order.takerAssetAmount)\n <= \n safeMul(order.makerAssetAmount, takerAssetFilledAmount),\n \"INVALID_FILL_PRICE\"\n );\n }\n\n /// @dev Validates context for cancelOrder. Succeeds or throws.\n /// @param order to be cancelled.\n /// @param orderInfo OrderStatus, orderHash, and amount already filled of order.\n function assertValidCancel(\n Order memory order,\n OrderInfo memory orderInfo\n )\n internal\n view\n {\n // Ensure order is valid\n // An order can only be cancelled if its status is FILLABLE.\n require(\n orderInfo.orderStatus == uint8(OrderStatus.FILLABLE),\n \"ORDER_UNFILLABLE\"\n );\n\n // Validate sender is allowed to cancel this order\n if (order.senderAddress != address(0)) {\n require(\n order.senderAddress == msg.sender,\n \"INVALID_SENDER\"\n );\n }\n\n // Validate transaction signed by maker\n address makerAddress = getCurrentContextAddress();\n require(\n order.makerAddress == makerAddress,\n \"INVALID_MAKER\"\n );\n }\n\n /// @dev Calculates amounts filled and fees paid by maker and taker.\n /// @param order to be filled.\n /// @param takerAssetFilledAmount Amount of takerAsset that will be filled.\n /// @return fillResults Amounts filled and fees paid by maker and taker.\n function calculateFillResults(\n Order memory order,\n uint256 takerAssetFilledAmount\n )\n internal\n pure\n returns (FillResults memory fillResults)\n {\n // Compute proportional transfer amounts\n fillResults.takerAssetFilledAmount = takerAssetFilledAmount;\n fillResults.makerAssetFilledAmount = safeGetPartialAmountFloor(\n takerAssetFilledAmount,\n order.takerAssetAmount,\n order.makerAssetAmount\n );\n fillResults.makerFeePaid = safeGetPartialAmountFloor(\n fillResults.makerAssetFilledAmount,\n order.makerAssetAmount,\n order.makerFee\n );\n fillResults.takerFeePaid = safeGetPartialAmountFloor(\n takerAssetFilledAmount,\n order.takerAssetAmount,\n order.takerFee\n );\n\n return fillResults;\n }\n\n /// @dev Settles an order by transferring assets between counterparties.\n /// @param order Order struct containing order specifications.\n /// @param takerAddress Address selling takerAsset and buying makerAsset.\n /// @param fillResults Amounts to be filled and fees paid by maker and taker.\n function settleOrder(\n LibOrder.Order memory order,\n address takerAddress,\n LibFillResults.FillResults memory fillResults\n )\n private\n {\n bytes memory zrxAssetData = ZRX_ASSET_DATA;\n dispatchTransferFrom(\n order.makerAssetData,\n order.makerAddress,\n takerAddress,\n fillResults.makerAssetFilledAmount\n );\n dispatchTransferFrom(\n order.takerAssetData,\n takerAddress,\n order.makerAddress,\n fillResults.takerAssetFilledAmount\n );\n dispatchTransferFrom(\n zrxAssetData,\n order.makerAddress,\n order.feeRecipientAddress,\n fillResults.makerFeePaid\n );\n dispatchTransferFrom(\n zrxAssetData,\n takerAddress,\n order.feeRecipientAddress,\n fillResults.takerFeePaid\n );\n }\n}\n", + "2.0.0/protocol/Exchange/MixinMatchOrders.sol": "/*\n Copyright 2018 ZeroEx Intl.\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n http://www.apache.org/licenses/LICENSE-2.0\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../../utils/ReentrancyGuard/ReentrancyGuard.sol\";\nimport \"./libs/LibConstants.sol\";\nimport \"./libs/LibMath.sol\";\nimport \"./libs/LibOrder.sol\";\nimport \"./libs/LibFillResults.sol\";\nimport \"./mixins/MExchangeCore.sol\";\nimport \"./mixins/MMatchOrders.sol\";\nimport \"./mixins/MTransactions.sol\";\nimport \"./mixins/MAssetProxyDispatcher.sol\";\n\n\ncontract MixinMatchOrders is\n ReentrancyGuard,\n LibConstants,\n LibMath,\n MAssetProxyDispatcher,\n MExchangeCore,\n MMatchOrders,\n MTransactions\n{\n /// @dev Match two complementary orders that have a profitable spread.\n /// Each order is filled at their respective price point. However, the calculations are\n /// carried out as though the orders are both being filled at the right order's price point.\n /// The profit made by the left order goes to the taker (who matched the two orders).\n /// @param leftOrder First order to match.\n /// @param rightOrder Second order to match.\n /// @param leftSignature Proof that order was created by the left maker.\n /// @param rightSignature Proof that order was created by the right maker.\n /// @return matchedFillResults Amounts filled and fees paid by maker and taker of matched orders.\n function matchOrders(\n LibOrder.Order memory leftOrder,\n LibOrder.Order memory rightOrder,\n bytes memory leftSignature,\n bytes memory rightSignature\n )\n public\n nonReentrant\n returns (LibFillResults.MatchedFillResults memory matchedFillResults)\n {\n // We assume that rightOrder.takerAssetData == leftOrder.makerAssetData and rightOrder.makerAssetData == leftOrder.takerAssetData.\n // If this assumption isn't true, the match will fail at signature validation.\n rightOrder.makerAssetData = leftOrder.takerAssetData;\n rightOrder.takerAssetData = leftOrder.makerAssetData;\n\n // Get left & right order info\n LibOrder.OrderInfo memory leftOrderInfo = getOrderInfo(leftOrder);\n LibOrder.OrderInfo memory rightOrderInfo = getOrderInfo(rightOrder);\n\n // Fetch taker address\n address takerAddress = getCurrentContextAddress();\n \n // Either our context is valid or we revert\n assertFillableOrder(\n leftOrder,\n leftOrderInfo,\n takerAddress,\n leftSignature\n );\n assertFillableOrder(\n rightOrder,\n rightOrderInfo,\n takerAddress,\n rightSignature\n );\n assertValidMatch(leftOrder, rightOrder);\n\n // Compute proportional fill amounts\n matchedFillResults = calculateMatchedFillResults(\n leftOrder,\n rightOrder,\n leftOrderInfo.orderTakerAssetFilledAmount,\n rightOrderInfo.orderTakerAssetFilledAmount\n );\n\n // Validate fill contexts\n assertValidFill(\n leftOrder,\n leftOrderInfo,\n matchedFillResults.left.takerAssetFilledAmount,\n matchedFillResults.left.takerAssetFilledAmount,\n matchedFillResults.left.makerAssetFilledAmount\n );\n assertValidFill(\n rightOrder,\n rightOrderInfo,\n matchedFillResults.right.takerAssetFilledAmount,\n matchedFillResults.right.takerAssetFilledAmount,\n matchedFillResults.right.makerAssetFilledAmount\n );\n \n // Update exchange state\n updateFilledState(\n leftOrder,\n takerAddress,\n leftOrderInfo.orderHash,\n leftOrderInfo.orderTakerAssetFilledAmount,\n matchedFillResults.left\n );\n updateFilledState(\n rightOrder,\n takerAddress,\n rightOrderInfo.orderHash,\n rightOrderInfo.orderTakerAssetFilledAmount,\n matchedFillResults.right\n );\n\n // Settle matched orders. Succeeds or throws.\n settleMatchedOrders(\n leftOrder,\n rightOrder,\n takerAddress,\n matchedFillResults\n );\n\n return matchedFillResults;\n }\n\n /// @dev Validates context for matchOrders. Succeeds or throws.\n /// @param leftOrder First order to match.\n /// @param rightOrder Second order to match.\n function assertValidMatch(\n LibOrder.Order memory leftOrder,\n LibOrder.Order memory rightOrder\n )\n internal\n pure\n {\n // Make sure there is a profitable spread.\n // There is a profitable spread iff the cost per unit bought (OrderA.MakerAmount/OrderA.TakerAmount) for each order is greater\n // than the profit per unit sold of the matched order (OrderB.TakerAmount/OrderB.MakerAmount).\n // This is satisfied by the equations below:\n // / >= / \n // AND\n // / >= / \n // These equations can be combined to get the following:\n require(\n safeMul(leftOrder.makerAssetAmount, rightOrder.makerAssetAmount) >=\n safeMul(leftOrder.takerAssetAmount, rightOrder.takerAssetAmount),\n \"NEGATIVE_SPREAD_REQUIRED\"\n );\n }\n\n /// @dev Calculates fill amounts for the matched orders.\n /// Each order is filled at their respective price point. However, the calculations are\n /// carried out as though the orders are both being filled at the right order's price point.\n /// The profit made by the leftOrder order goes to the taker (who matched the two orders).\n /// @param leftOrder First order to match.\n /// @param rightOrder Second order to match.\n /// @param leftOrderTakerAssetFilledAmount Amount of left order already filled.\n /// @param rightOrderTakerAssetFilledAmount Amount of right order already filled.\n /// @param matchedFillResults Amounts to fill and fees to pay by maker and taker of matched orders.\n function calculateMatchedFillResults(\n LibOrder.Order memory leftOrder,\n LibOrder.Order memory rightOrder,\n uint256 leftOrderTakerAssetFilledAmount,\n uint256 rightOrderTakerAssetFilledAmount\n )\n internal\n pure\n returns (LibFillResults.MatchedFillResults memory matchedFillResults)\n {\n // Derive maker asset amounts for left & right orders, given store taker assert amounts\n uint256 leftTakerAssetAmountRemaining = safeSub(leftOrder.takerAssetAmount, leftOrderTakerAssetFilledAmount);\n uint256 leftMakerAssetAmountRemaining = safeGetPartialAmountFloor(\n leftOrder.makerAssetAmount,\n leftOrder.takerAssetAmount,\n leftTakerAssetAmountRemaining\n );\n uint256 rightTakerAssetAmountRemaining = safeSub(rightOrder.takerAssetAmount, rightOrderTakerAssetFilledAmount);\n uint256 rightMakerAssetAmountRemaining = safeGetPartialAmountFloor(\n rightOrder.makerAssetAmount,\n rightOrder.takerAssetAmount,\n rightTakerAssetAmountRemaining\n );\n\n // Calculate fill results for maker and taker assets: at least one order will be fully filled.\n // The maximum amount the left maker can buy is `leftTakerAssetAmountRemaining`\n // The maximum amount the right maker can sell is `rightMakerAssetAmountRemaining`\n // We have two distinct cases for calculating the fill results:\n // Case 1.\n // If the left maker can buy more than the right maker can sell, then only the right order is fully filled.\n // If the left maker can buy exactly what the right maker can sell, then both orders are fully filled.\n // Case 2.\n // If the left maker cannot buy more than the right maker can sell, then only the left order is fully filled.\n if (leftTakerAssetAmountRemaining >= rightMakerAssetAmountRemaining) {\n // Case 1: Right order is fully filled\n matchedFillResults.right.makerAssetFilledAmount = rightMakerAssetAmountRemaining;\n matchedFillResults.right.takerAssetFilledAmount = rightTakerAssetAmountRemaining;\n matchedFillResults.left.takerAssetFilledAmount = matchedFillResults.right.makerAssetFilledAmount;\n // Round down to ensure the maker's exchange rate does not exceed the price specified by the order. \n // We favor the maker when the exchange rate must be rounded.\n matchedFillResults.left.makerAssetFilledAmount = safeGetPartialAmountFloor(\n leftOrder.makerAssetAmount,\n leftOrder.takerAssetAmount,\n matchedFillResults.left.takerAssetFilledAmount\n );\n } else {\n // Case 2: Left order is fully filled\n matchedFillResults.left.makerAssetFilledAmount = leftMakerAssetAmountRemaining;\n matchedFillResults.left.takerAssetFilledAmount = leftTakerAssetAmountRemaining;\n matchedFillResults.right.makerAssetFilledAmount = matchedFillResults.left.takerAssetFilledAmount;\n // Round up to ensure the maker's exchange rate does not exceed the price specified by the order.\n // We favor the maker when the exchange rate must be rounded.\n matchedFillResults.right.takerAssetFilledAmount = safeGetPartialAmountCeil(\n rightOrder.takerAssetAmount,\n rightOrder.makerAssetAmount,\n matchedFillResults.right.makerAssetFilledAmount\n );\n }\n\n // Calculate amount given to taker\n matchedFillResults.leftMakerAssetSpreadAmount = safeSub(\n matchedFillResults.left.makerAssetFilledAmount,\n matchedFillResults.right.takerAssetFilledAmount\n );\n\n // Compute fees for left order\n matchedFillResults.left.makerFeePaid = safeGetPartialAmountFloor(\n matchedFillResults.left.makerAssetFilledAmount,\n leftOrder.makerAssetAmount,\n leftOrder.makerFee\n );\n matchedFillResults.left.takerFeePaid = safeGetPartialAmountFloor(\n matchedFillResults.left.takerAssetFilledAmount,\n leftOrder.takerAssetAmount,\n leftOrder.takerFee\n );\n\n // Compute fees for right order\n matchedFillResults.right.makerFeePaid = safeGetPartialAmountFloor(\n matchedFillResults.right.makerAssetFilledAmount,\n rightOrder.makerAssetAmount,\n rightOrder.makerFee\n );\n matchedFillResults.right.takerFeePaid = safeGetPartialAmountFloor(\n matchedFillResults.right.takerAssetFilledAmount,\n rightOrder.takerAssetAmount,\n rightOrder.takerFee\n );\n\n // Return fill results\n return matchedFillResults;\n }\n\n /// @dev Settles matched order by transferring appropriate funds between order makers, taker, and fee recipient.\n /// @param leftOrder First matched order.\n /// @param rightOrder Second matched order.\n /// @param takerAddress Address that matched the orders. The taker receives the spread between orders as profit.\n /// @param matchedFillResults Struct holding amounts to transfer between makers, taker, and fee recipients.\n function settleMatchedOrders(\n LibOrder.Order memory leftOrder,\n LibOrder.Order memory rightOrder,\n address takerAddress,\n LibFillResults.MatchedFillResults memory matchedFillResults\n )\n private\n {\n bytes memory zrxAssetData = ZRX_ASSET_DATA;\n // Order makers and taker\n dispatchTransferFrom(\n leftOrder.makerAssetData,\n leftOrder.makerAddress,\n rightOrder.makerAddress,\n matchedFillResults.right.takerAssetFilledAmount\n );\n dispatchTransferFrom(\n rightOrder.makerAssetData,\n rightOrder.makerAddress,\n leftOrder.makerAddress,\n matchedFillResults.left.takerAssetFilledAmount\n );\n dispatchTransferFrom(\n leftOrder.makerAssetData,\n leftOrder.makerAddress,\n takerAddress,\n matchedFillResults.leftMakerAssetSpreadAmount\n );\n\n // Maker fees\n dispatchTransferFrom(\n zrxAssetData,\n leftOrder.makerAddress,\n leftOrder.feeRecipientAddress,\n matchedFillResults.left.makerFeePaid\n );\n dispatchTransferFrom(\n zrxAssetData,\n rightOrder.makerAddress,\n rightOrder.feeRecipientAddress,\n matchedFillResults.right.makerFeePaid\n );\n\n // Taker fees\n if (leftOrder.feeRecipientAddress == rightOrder.feeRecipientAddress) {\n dispatchTransferFrom(\n zrxAssetData,\n takerAddress,\n leftOrder.feeRecipientAddress,\n safeAdd(\n matchedFillResults.left.takerFeePaid,\n matchedFillResults.right.takerFeePaid\n )\n );\n } else {\n dispatchTransferFrom(\n zrxAssetData,\n takerAddress,\n leftOrder.feeRecipientAddress,\n matchedFillResults.left.takerFeePaid\n );\n dispatchTransferFrom(\n zrxAssetData,\n takerAddress,\n rightOrder.feeRecipientAddress,\n matchedFillResults.right.takerFeePaid\n );\n }\n }\n}\n", + "2.0.0/protocol/Exchange/MixinSignatureValidator.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../utils/LibBytes/LibBytes.sol\";\nimport \"../../utils/ReentrancyGuard/ReentrancyGuard.sol\";\nimport \"./mixins/MSignatureValidator.sol\";\nimport \"./mixins/MTransactions.sol\";\nimport \"./interfaces/IWallet.sol\";\nimport \"./interfaces/IValidator.sol\";\n\n\ncontract MixinSignatureValidator is\n ReentrancyGuard,\n MSignatureValidator,\n MTransactions\n{\n using LibBytes for bytes;\n \n // Mapping of hash => signer => signed\n mapping (bytes32 => mapping (address => bool)) public preSigned;\n\n // Mapping of signer => validator => approved\n mapping (address => mapping (address => bool)) public allowedValidators;\n\n /// @dev Approves a hash on-chain using any valid signature type.\n /// After presigning a hash, the preSign signature type will become valid for that hash and signer.\n /// @param signerAddress Address that should have signed the given hash.\n /// @param signature Proof that the hash has been signed by signer.\n function preSign(\n bytes32 hash,\n address signerAddress,\n bytes signature\n )\n external\n {\n if (signerAddress != msg.sender) {\n require(\n isValidSignature(\n hash,\n signerAddress,\n signature\n ),\n \"INVALID_SIGNATURE\"\n );\n }\n preSigned[hash][signerAddress] = true;\n }\n\n /// @dev Approves/unnapproves a Validator contract to verify signatures on signer's behalf.\n /// @param validatorAddress Address of Validator contract.\n /// @param approval Approval or disapproval of Validator contract.\n function setSignatureValidatorApproval(\n address validatorAddress,\n bool approval\n )\n external\n nonReentrant\n {\n address signerAddress = getCurrentContextAddress();\n allowedValidators[signerAddress][validatorAddress] = approval;\n emit SignatureValidatorApproval(\n signerAddress,\n validatorAddress,\n approval\n );\n }\n\n /// @dev Verifies that a hash has been signed by the given signer.\n /// @param hash Any 32 byte hash.\n /// @param signerAddress Address that should have signed the given hash.\n /// @param signature Proof that the hash has been signed by signer.\n /// @return True if the address recovered from the provided signature matches the input signer address.\n function isValidSignature(\n bytes32 hash,\n address signerAddress,\n bytes memory signature\n )\n public\n view\n returns (bool isValid)\n {\n require(\n signature.length > 0,\n \"LENGTH_GREATER_THAN_0_REQUIRED\"\n );\n\n // Pop last byte off of signature byte array.\n uint8 signatureTypeRaw = uint8(signature.popLastByte());\n\n // Ensure signature is supported\n require(\n signatureTypeRaw < uint8(SignatureType.NSignatureTypes),\n \"SIGNATURE_UNSUPPORTED\"\n );\n\n SignatureType signatureType = SignatureType(signatureTypeRaw);\n\n // Variables are not scoped in Solidity.\n uint8 v;\n bytes32 r;\n bytes32 s;\n address recovered;\n\n // Always illegal signature.\n // This is always an implicit option since a signer can create a\n // signature array with invalid type or length. We may as well make\n // it an explicit option. This aids testing and analysis. It is\n // also the initialization value for the enum type.\n if (signatureType == SignatureType.Illegal) {\n revert(\"SIGNATURE_ILLEGAL\");\n\n // Always invalid signature.\n // Like Illegal, this is always implicitly available and therefore\n // offered explicitly. It can be implicitly created by providing\n // a correctly formatted but incorrect signature.\n } else if (signatureType == SignatureType.Invalid) {\n require(\n signature.length == 0,\n \"LENGTH_0_REQUIRED\"\n );\n isValid = false;\n return isValid;\n\n // Signature using EIP712\n } else if (signatureType == SignatureType.EIP712) {\n require(\n signature.length == 65,\n \"LENGTH_65_REQUIRED\"\n );\n v = uint8(signature[0]);\n r = signature.readBytes32(1);\n s = signature.readBytes32(33);\n recovered = ecrecover(\n hash,\n v,\n r,\n s\n );\n isValid = signerAddress == recovered;\n return isValid;\n\n // Signed using web3.eth_sign\n } else if (signatureType == SignatureType.EthSign) {\n require(\n signature.length == 65,\n \"LENGTH_65_REQUIRED\"\n );\n v = uint8(signature[0]);\n r = signature.readBytes32(1);\n s = signature.readBytes32(33);\n recovered = ecrecover(\n keccak256(abi.encodePacked(\n \"\\x19Ethereum Signed Message:\\n32\",\n hash\n )),\n v,\n r,\n s\n );\n isValid = signerAddress == recovered;\n return isValid;\n\n // Signature verified by wallet contract.\n // If used with an order, the maker of the order is the wallet contract.\n } else if (signatureType == SignatureType.Wallet) {\n isValid = isValidWalletSignature(\n hash,\n signerAddress,\n signature\n );\n return isValid;\n\n // Signature verified by validator contract.\n // If used with an order, the maker of the order can still be an EOA.\n // A signature using this type should be encoded as:\n // | Offset | Length | Contents |\n // | 0x00 | x | Signature to validate |\n // | 0x00 + x | 20 | Address of validator contract |\n // | 0x14 + x | 1 | Signature type is always \"\\x06\" |\n } else if (signatureType == SignatureType.Validator) {\n // Pop last 20 bytes off of signature byte array.\n address validatorAddress = signature.popLast20Bytes();\n \n // Ensure signer has approved validator.\n if (!allowedValidators[signerAddress][validatorAddress]) {\n return false;\n }\n isValid = isValidValidatorSignature(\n validatorAddress,\n hash,\n signerAddress,\n signature\n );\n return isValid;\n\n // Signer signed hash previously using the preSign function.\n } else if (signatureType == SignatureType.PreSigned) {\n isValid = preSigned[hash][signerAddress];\n return isValid;\n }\n\n // Anything else is illegal (We do not return false because\n // the signature may actually be valid, just not in a format\n // that we currently support. In this case returning false\n // may lead the caller to incorrectly believe that the\n // signature was invalid.)\n revert(\"SIGNATURE_UNSUPPORTED\");\n }\n\n /// @dev Verifies signature using logic defined by Wallet contract.\n /// @param hash Any 32 byte hash.\n /// @param walletAddress Address that should have signed the given hash\n /// and defines its own signature verification method.\n /// @param signature Proof that the hash has been signed by signer.\n /// @return True if signature is valid for given wallet..\n function isValidWalletSignature(\n bytes32 hash,\n address walletAddress,\n bytes signature\n )\n internal\n view\n returns (bool isValid)\n {\n bytes memory calldata = abi.encodeWithSelector(\n IWallet(walletAddress).isValidSignature.selector,\n hash,\n signature\n );\n assembly {\n let cdStart := add(calldata, 32)\n let success := staticcall(\n gas, // forward all gas\n walletAddress, // address of Wallet contract\n cdStart, // pointer to start of input\n mload(calldata), // length of input\n cdStart, // write output over input\n 32 // output size is 32 bytes\n )\n\n switch success\n case 0 {\n // Revert with `Error(\"WALLET_ERROR\")`\n mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)\n mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)\n mstore(64, 0x0000000c57414c4c45545f4552524f5200000000000000000000000000000000)\n mstore(96, 0)\n revert(0, 100)\n }\n case 1 {\n // Signature is valid if call did not revert and returned true\n isValid := mload(cdStart)\n }\n }\n return isValid;\n }\n\n /// @dev Verifies signature using logic defined by Validator contract.\n /// @param validatorAddress Address of validator contract.\n /// @param hash Any 32 byte hash.\n /// @param signerAddress Address that should have signed the given hash.\n /// @param signature Proof that the hash has been signed by signer.\n /// @return True if the address recovered from the provided signature matches the input signer address.\n function isValidValidatorSignature(\n address validatorAddress,\n bytes32 hash,\n address signerAddress,\n bytes signature\n )\n internal\n view\n returns (bool isValid)\n {\n bytes memory calldata = abi.encodeWithSelector(\n IValidator(signerAddress).isValidSignature.selector,\n hash,\n signerAddress,\n signature\n );\n assembly {\n let cdStart := add(calldata, 32)\n let success := staticcall(\n gas, // forward all gas\n validatorAddress, // address of Validator contract\n cdStart, // pointer to start of input\n mload(calldata), // length of input\n cdStart, // write output over input\n 32 // output size is 32 bytes\n )\n\n switch success\n case 0 {\n // Revert with `Error(\"VALIDATOR_ERROR\")`\n mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)\n mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)\n mstore(64, 0x0000000f56414c494441544f525f4552524f5200000000000000000000000000)\n mstore(96, 0)\n revert(0, 100)\n }\n case 1 {\n // Signature is valid if call did not revert and returned true\n isValid := mload(cdStart)\n }\n }\n return isValid;\n }\n}\n", + "2.0.0/protocol/Exchange/MixinTransactions.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\npragma solidity 0.4.24;\n\nimport \"./libs/LibExchangeErrors.sol\";\nimport \"./mixins/MSignatureValidator.sol\";\nimport \"./mixins/MTransactions.sol\";\nimport \"./libs/LibEIP712.sol\";\n\n\ncontract MixinTransactions is\n LibEIP712,\n MSignatureValidator,\n MTransactions\n{\n // Mapping of transaction hash => executed\n // This prevents transactions from being executed more than once.\n mapping (bytes32 => bool) public transactions;\n\n // Address of current transaction signer\n address public currentContextAddress;\n\n /// @dev Executes an exchange method call in the context of signer.\n /// @param salt Arbitrary number to ensure uniqueness of transaction hash.\n /// @param signerAddress Address of transaction signer.\n /// @param data AbiV2 encoded calldata.\n /// @param signature Proof of signer transaction by signer.\n function executeTransaction(\n uint256 salt,\n address signerAddress,\n bytes data,\n bytes signature\n )\n external\n {\n // Prevent reentrancy\n require(\n currentContextAddress == address(0),\n \"REENTRANCY_ILLEGAL\"\n );\n\n bytes32 transactionHash = hashEIP712Message(hashZeroExTransaction(\n salt,\n signerAddress,\n data\n ));\n\n // Validate transaction has not been executed\n require(\n !transactions[transactionHash],\n \"INVALID_TX_HASH\"\n );\n\n // Transaction always valid if signer is sender of transaction\n if (signerAddress != msg.sender) {\n // Validate signature\n require(\n isValidSignature(\n transactionHash,\n signerAddress,\n signature\n ),\n \"INVALID_TX_SIGNATURE\"\n );\n\n // Set the current transaction signer\n currentContextAddress = signerAddress;\n }\n\n // Execute transaction\n transactions[transactionHash] = true;\n require(\n address(this).delegatecall(data),\n \"FAILED_EXECUTION\"\n );\n\n // Reset current transaction signer if it was previously updated\n if (signerAddress != msg.sender) {\n currentContextAddress = address(0);\n }\n }\n\n /// @dev Calculates EIP712 hash of the Transaction.\n /// @param salt Arbitrary number to ensure uniqueness of transaction hash.\n /// @param signerAddress Address of transaction signer.\n /// @param data AbiV2 encoded calldata.\n /// @return EIP712 hash of the Transaction.\n function hashZeroExTransaction(\n uint256 salt,\n address signerAddress,\n bytes memory data\n )\n internal\n pure\n returns (bytes32 result)\n {\n bytes32 schemaHash = EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH;\n bytes32 dataHash = keccak256(data);\n\n // Assembly for more efficiently computing:\n // keccak256(abi.encodePacked(\n // EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH,\n // salt,\n // bytes32(signerAddress),\n // keccak256(data)\n // ));\n\n assembly {\n // Load free memory pointer\n let memPtr := mload(64)\n\n mstore(memPtr, schemaHash) // hash of schema\n mstore(add(memPtr, 32), salt) // salt\n mstore(add(memPtr, 64), and(signerAddress, 0xffffffffffffffffffffffffffffffffffffffff)) // signerAddress\n mstore(add(memPtr, 96), dataHash) // hash of data\n\n // Compute hash\n result := keccak256(memPtr, 128)\n }\n return result;\n }\n\n /// @dev The current function will be called in the context of this address (either 0x transaction signer or `msg.sender`).\n /// If calling a fill function, this address will represent the taker.\n /// If calling a cancel function, this address will represent the maker.\n /// @return Signer of 0x transaction if entry point is `executeTransaction`.\n /// `msg.sender` if entry point is any other function.\n function getCurrentContextAddress()\n internal\n view\n returns (address)\n {\n address currentContextAddress_ = currentContextAddress;\n address contextAddress = currentContextAddress_ == address(0) ? msg.sender : currentContextAddress_;\n return contextAddress;\n }\n}\n", + "2.0.0/protocol/Exchange/MixinWrapperFunctions.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../../utils/ReentrancyGuard/ReentrancyGuard.sol\";\nimport \"./libs/LibMath.sol\";\nimport \"./libs/LibOrder.sol\";\nimport \"./libs/LibFillResults.sol\";\nimport \"./libs/LibAbiEncoder.sol\";\nimport \"./mixins/MExchangeCore.sol\";\nimport \"./mixins/MWrapperFunctions.sol\";\n\n\ncontract MixinWrapperFunctions is\n ReentrancyGuard,\n LibMath,\n LibFillResults,\n LibAbiEncoder,\n MExchangeCore,\n MWrapperFunctions\n{\n /// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n function fillOrKillOrder(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n public\n nonReentrant\n returns (FillResults memory fillResults)\n {\n fillResults = fillOrKillOrderInternal(\n order,\n takerAssetFillAmount,\n signature\n );\n return fillResults;\n }\n\n /// @dev Fills the input order.\n /// Returns false if the transaction would otherwise revert.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n /// @return Amounts filled and fees paid by maker and taker.\n function fillOrderNoThrow(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n public\n returns (FillResults memory fillResults)\n {\n // ABI encode calldata for `fillOrder`\n bytes memory fillOrderCalldata = abiEncodeFillOrder(\n order,\n takerAssetFillAmount,\n signature\n );\n\n // Delegate to `fillOrder` and handle any exceptions gracefully\n assembly {\n let success := delegatecall(\n gas, // forward all gas\n address, // call address of this contract\n add(fillOrderCalldata, 32), // pointer to start of input (skip array length in first 32 bytes)\n mload(fillOrderCalldata), // length of input\n fillOrderCalldata, // write output over input\n 128 // output size is 128 bytes\n )\n if success {\n mstore(fillResults, mload(fillOrderCalldata))\n mstore(add(fillResults, 32), mload(add(fillOrderCalldata, 32)))\n mstore(add(fillResults, 64), mload(add(fillOrderCalldata, 64)))\n mstore(add(fillResults, 96), mload(add(fillOrderCalldata, 96)))\n }\n }\n // fillResults values will be 0 by default if call was unsuccessful\n return fillResults;\n }\n\n /// @dev Synchronously executes multiple calls of fillOrder.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n /// NOTE: makerAssetFilledAmount and takerAssetFilledAmount may include amounts filled of different assets.\n function batchFillOrders(\n LibOrder.Order[] memory orders,\n uint256[] memory takerAssetFillAmounts,\n bytes[] memory signatures\n )\n public\n nonReentrant\n returns (FillResults memory totalFillResults)\n {\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n FillResults memory singleFillResults = fillOrderInternal(\n orders[i],\n takerAssetFillAmounts[i],\n signatures[i]\n );\n addFillResults(totalFillResults, singleFillResults);\n }\n return totalFillResults;\n }\n\n /// @dev Synchronously executes multiple calls of fillOrKill.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n /// NOTE: makerAssetFilledAmount and takerAssetFilledAmount may include amounts filled of different assets.\n function batchFillOrKillOrders(\n LibOrder.Order[] memory orders,\n uint256[] memory takerAssetFillAmounts,\n bytes[] memory signatures\n )\n public\n nonReentrant\n returns (FillResults memory totalFillResults)\n {\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n FillResults memory singleFillResults = fillOrKillOrderInternal(\n orders[i],\n takerAssetFillAmounts[i],\n signatures[i]\n );\n addFillResults(totalFillResults, singleFillResults);\n }\n return totalFillResults;\n }\n\n /// @dev Fills an order with specified parameters and ECDSA signature.\n /// Returns false if the transaction would otherwise revert.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n /// NOTE: makerAssetFilledAmount and takerAssetFilledAmount may include amounts filled of different assets.\n function batchFillOrdersNoThrow(\n LibOrder.Order[] memory orders,\n uint256[] memory takerAssetFillAmounts,\n bytes[] memory signatures\n )\n public\n returns (FillResults memory totalFillResults)\n {\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n FillResults memory singleFillResults = fillOrderNoThrow(\n orders[i],\n takerAssetFillAmounts[i],\n signatures[i]\n );\n addFillResults(totalFillResults, singleFillResults);\n }\n return totalFillResults;\n }\n\n /// @dev Synchronously executes multiple calls of fillOrder until total amount of takerAsset is sold by taker.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketSellOrders(\n LibOrder.Order[] memory orders,\n uint256 takerAssetFillAmount,\n bytes[] memory signatures\n )\n public\n nonReentrant\n returns (FillResults memory totalFillResults)\n {\n bytes memory takerAssetData = orders[0].takerAssetData;\n \n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n\n // We assume that asset being sold by taker is the same for each order.\n // Rather than passing this in as calldata, we use the takerAssetData from the first order in all later orders.\n orders[i].takerAssetData = takerAssetData;\n\n // Calculate the remaining amount of takerAsset to sell\n uint256 remainingTakerAssetFillAmount = safeSub(takerAssetFillAmount, totalFillResults.takerAssetFilledAmount);\n\n // Attempt to sell the remaining amount of takerAsset\n FillResults memory singleFillResults = fillOrderInternal(\n orders[i],\n remainingTakerAssetFillAmount,\n signatures[i]\n );\n\n // Update amounts filled and fees paid by maker and taker\n addFillResults(totalFillResults, singleFillResults);\n\n // Stop execution if the entire amount of takerAsset has been sold\n if (totalFillResults.takerAssetFilledAmount >= takerAssetFillAmount) {\n break;\n }\n }\n return totalFillResults;\n }\n\n /// @dev Synchronously executes multiple calls of fillOrder until total amount of takerAsset is sold by taker.\n /// Returns false if the transaction would otherwise revert.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketSellOrdersNoThrow(\n LibOrder.Order[] memory orders,\n uint256 takerAssetFillAmount,\n bytes[] memory signatures\n )\n public\n returns (FillResults memory totalFillResults)\n {\n bytes memory takerAssetData = orders[0].takerAssetData;\n\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n\n // We assume that asset being sold by taker is the same for each order.\n // Rather than passing this in as calldata, we use the takerAssetData from the first order in all later orders.\n orders[i].takerAssetData = takerAssetData;\n\n // Calculate the remaining amount of takerAsset to sell\n uint256 remainingTakerAssetFillAmount = safeSub(takerAssetFillAmount, totalFillResults.takerAssetFilledAmount);\n\n // Attempt to sell the remaining amount of takerAsset\n FillResults memory singleFillResults = fillOrderNoThrow(\n orders[i],\n remainingTakerAssetFillAmount,\n signatures[i]\n );\n\n // Update amounts filled and fees paid by maker and taker\n addFillResults(totalFillResults, singleFillResults);\n\n // Stop execution if the entire amount of takerAsset has been sold\n if (totalFillResults.takerAssetFilledAmount >= takerAssetFillAmount) {\n break;\n }\n }\n return totalFillResults;\n }\n\n /// @dev Synchronously executes multiple calls of fillOrder until total amount of makerAsset is bought by taker.\n /// @param orders Array of order specifications.\n /// @param makerAssetFillAmount Desired amount of makerAsset to buy.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketBuyOrders(\n LibOrder.Order[] memory orders,\n uint256 makerAssetFillAmount,\n bytes[] memory signatures\n )\n public\n nonReentrant\n returns (FillResults memory totalFillResults)\n {\n bytes memory makerAssetData = orders[0].makerAssetData;\n\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n\n // We assume that asset being bought by taker is the same for each order.\n // Rather than passing this in as calldata, we copy the makerAssetData from the first order onto all later orders.\n orders[i].makerAssetData = makerAssetData;\n\n // Calculate the remaining amount of makerAsset to buy\n uint256 remainingMakerAssetFillAmount = safeSub(makerAssetFillAmount, totalFillResults.makerAssetFilledAmount);\n\n // Convert the remaining amount of makerAsset to buy into remaining amount\n // of takerAsset to sell, assuming entire amount can be sold in the current order\n uint256 remainingTakerAssetFillAmount = getPartialAmountFloor(\n orders[i].takerAssetAmount,\n orders[i].makerAssetAmount,\n remainingMakerAssetFillAmount\n );\n\n // Attempt to sell the remaining amount of takerAsset\n FillResults memory singleFillResults = fillOrderInternal(\n orders[i],\n remainingTakerAssetFillAmount,\n signatures[i]\n );\n\n // Update amounts filled and fees paid by maker and taker\n addFillResults(totalFillResults, singleFillResults);\n\n // Stop execution if the entire amount of makerAsset has been bought\n if (totalFillResults.makerAssetFilledAmount >= makerAssetFillAmount) {\n break;\n }\n }\n return totalFillResults;\n }\n\n /// @dev Synchronously executes multiple fill orders in a single transaction until total amount is bought by taker.\n /// Returns false if the transaction would otherwise revert.\n /// @param orders Array of order specifications.\n /// @param makerAssetFillAmount Desired amount of makerAsset to buy.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketBuyOrdersNoThrow(\n LibOrder.Order[] memory orders,\n uint256 makerAssetFillAmount,\n bytes[] memory signatures\n )\n public\n returns (FillResults memory totalFillResults)\n {\n bytes memory makerAssetData = orders[0].makerAssetData;\n\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n\n // We assume that asset being bought by taker is the same for each order.\n // Rather than passing this in as calldata, we copy the makerAssetData from the first order onto all later orders.\n orders[i].makerAssetData = makerAssetData;\n\n // Calculate the remaining amount of makerAsset to buy\n uint256 remainingMakerAssetFillAmount = safeSub(makerAssetFillAmount, totalFillResults.makerAssetFilledAmount);\n\n // Convert the remaining amount of makerAsset to buy into remaining amount\n // of takerAsset to sell, assuming entire amount can be sold in the current order\n uint256 remainingTakerAssetFillAmount = getPartialAmountFloor(\n orders[i].takerAssetAmount,\n orders[i].makerAssetAmount,\n remainingMakerAssetFillAmount\n );\n\n // Attempt to sell the remaining amount of takerAsset\n FillResults memory singleFillResults = fillOrderNoThrow(\n orders[i],\n remainingTakerAssetFillAmount,\n signatures[i]\n );\n\n // Update amounts filled and fees paid by maker and taker\n addFillResults(totalFillResults, singleFillResults);\n\n // Stop execution if the entire amount of makerAsset has been bought\n if (totalFillResults.makerAssetFilledAmount >= makerAssetFillAmount) {\n break;\n }\n }\n return totalFillResults;\n }\n\n /// @dev Synchronously cancels multiple orders in a single transaction.\n /// @param orders Array of order specifications.\n function batchCancelOrders(LibOrder.Order[] memory orders)\n public\n nonReentrant\n {\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n cancelOrderInternal(orders[i]);\n }\n }\n\n /// @dev Fetches information for all passed in orders.\n /// @param orders Array of order specifications.\n /// @return Array of OrderInfo instances that correspond to each order.\n function getOrdersInfo(LibOrder.Order[] memory orders)\n public\n view\n returns (LibOrder.OrderInfo[] memory)\n {\n uint256 ordersLength = orders.length;\n LibOrder.OrderInfo[] memory ordersInfo = new LibOrder.OrderInfo[](ordersLength);\n for (uint256 i = 0; i != ordersLength; i++) {\n ordersInfo[i] = getOrderInfo(orders[i]);\n }\n return ordersInfo;\n }\n\n /// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n function fillOrKillOrderInternal(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n internal\n returns (FillResults memory fillResults)\n {\n fillResults = fillOrderInternal(\n order,\n takerAssetFillAmount,\n signature\n );\n require(\n fillResults.takerAssetFilledAmount == takerAssetFillAmount,\n \"COMPLETE_FILL_FAILED\"\n );\n return fillResults;\n }\n}\n", + "2.0.0/protocol/Exchange/interfaces/IAssetProxyDispatcher.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\ncontract IAssetProxyDispatcher {\n\n /// @dev Registers an asset proxy to its asset proxy id.\n /// Once an asset proxy is registered, it cannot be unregistered.\n /// @param assetProxy Address of new asset proxy to register.\n function registerAssetProxy(address assetProxy)\n external;\n\n /// @dev Gets an asset proxy.\n /// @param assetProxyId Id of the asset proxy.\n /// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered.\n function getAssetProxy(bytes4 assetProxyId)\n external\n view\n returns (address);\n}\n", + "2.0.0/protocol/Exchange/interfaces/IExchange.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"./IExchangeCore.sol\";\nimport \"./IMatchOrders.sol\";\nimport \"./ISignatureValidator.sol\";\nimport \"./ITransactions.sol\";\nimport \"./IAssetProxyDispatcher.sol\";\nimport \"./IWrapperFunctions.sol\";\n\n\n// solhint-disable no-empty-blocks\ncontract IExchange is\n IExchangeCore,\n IMatchOrders,\n ISignatureValidator,\n ITransactions,\n IAssetProxyDispatcher,\n IWrapperFunctions\n{}\n", + "2.0.0/protocol/Exchange/interfaces/IExchangeCore.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../libs/LibOrder.sol\";\nimport \"../libs/LibFillResults.sol\";\n\n\ncontract IExchangeCore {\n\n /// @dev Cancels all orders created by makerAddress with a salt less than or equal to the targetOrderEpoch\n /// and senderAddress equal to msg.sender (or null address if msg.sender == makerAddress).\n /// @param targetOrderEpoch Orders created with a salt less or equal to this value will be cancelled.\n function cancelOrdersUpTo(uint256 targetOrderEpoch)\n external;\n\n /// @dev Fills the input order.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n /// @return Amounts filled and fees paid by maker and taker.\n function fillOrder(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n public\n returns (LibFillResults.FillResults memory fillResults);\n\n /// @dev After calling, the order can not be filled anymore.\n /// @param order Order struct containing order specifications.\n function cancelOrder(LibOrder.Order memory order)\n public;\n\n /// @dev Gets information about an order: status, hash, and amount filled.\n /// @param order Order to gather information on.\n /// @return OrderInfo Information about the order and its state.\n /// See LibOrder.OrderInfo for a complete description.\n function getOrderInfo(LibOrder.Order memory order)\n public\n view\n returns (LibOrder.OrderInfo memory orderInfo);\n}\n", + "2.0.0/protocol/Exchange/interfaces/IMatchOrders.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../libs/LibOrder.sol\";\nimport \"../libs/LibFillResults.sol\";\n\n\ncontract IMatchOrders {\n\n /// @dev Match two complementary orders that have a profitable spread.\n /// Each order is filled at their respective price point. However, the calculations are\n /// carried out as though the orders are both being filled at the right order's price point.\n /// The profit made by the left order goes to the taker (who matched the two orders).\n /// @param leftOrder First order to match.\n /// @param rightOrder Second order to match.\n /// @param leftSignature Proof that order was created by the left maker.\n /// @param rightSignature Proof that order was created by the right maker.\n /// @return matchedFillResults Amounts filled and fees paid by maker and taker of matched orders.\n function matchOrders(\n LibOrder.Order memory leftOrder,\n LibOrder.Order memory rightOrder,\n bytes memory leftSignature,\n bytes memory rightSignature\n )\n public\n returns (LibFillResults.MatchedFillResults memory matchedFillResults);\n}\n", + "2.0.0/protocol/Exchange/interfaces/ISignatureValidator.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\ncontract ISignatureValidator {\n\n /// @dev Approves a hash on-chain using any valid signature type.\n /// After presigning a hash, the preSign signature type will become valid for that hash and signer.\n /// @param signerAddress Address that should have signed the given hash.\n /// @param signature Proof that the hash has been signed by signer.\n function preSign(\n bytes32 hash,\n address signerAddress,\n bytes signature\n )\n external;\n \n /// @dev Approves/unnapproves a Validator contract to verify signatures on signer's behalf.\n /// @param validatorAddress Address of Validator contract.\n /// @param approval Approval or disapproval of Validator contract.\n function setSignatureValidatorApproval(\n address validatorAddress,\n bool approval\n )\n external;\n\n /// @dev Verifies that a signature is valid.\n /// @param hash Message hash that is signed.\n /// @param signerAddress Address of signer.\n /// @param signature Proof of signing.\n /// @return Validity of order signature.\n function isValidSignature(\n bytes32 hash,\n address signerAddress,\n bytes memory signature\n )\n public\n view\n returns (bool isValid);\n}\n", + "2.0.0/protocol/Exchange/interfaces/ITransactions.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\npragma solidity 0.4.24;\n\n\ncontract ITransactions {\n\n /// @dev Executes an exchange method call in the context of signer.\n /// @param salt Arbitrary number to ensure uniqueness of transaction hash.\n /// @param signerAddress Address of transaction signer.\n /// @param data AbiV2 encoded calldata.\n /// @param signature Proof of signer transaction by signer.\n function executeTransaction(\n uint256 salt,\n address signerAddress,\n bytes data,\n bytes signature\n )\n external;\n}\n", + "2.0.0/protocol/Exchange/interfaces/IValidator.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\ncontract IValidator {\n\n /// @dev Verifies that a signature is valid.\n /// @param hash Message hash that is signed.\n /// @param signerAddress Address that should have signed the given hash.\n /// @param signature Proof of signing.\n /// @return Validity of order signature.\n function isValidSignature(\n bytes32 hash,\n address signerAddress,\n bytes signature\n )\n external\n view\n returns (bool isValid);\n}\n", + "2.0.0/protocol/Exchange/interfaces/IWallet.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\ncontract IWallet {\n\n /// @dev Verifies that a signature is valid.\n /// @param hash Message hash that is signed.\n /// @param signature Proof of signing.\n /// @return Validity of order signature.\n function isValidSignature(\n bytes32 hash,\n bytes signature\n )\n external\n view\n returns (bool isValid);\n}\n", + "2.0.0/protocol/Exchange/interfaces/IWrapperFunctions.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../libs/LibOrder.sol\";\nimport \"../libs/LibFillResults.sol\";\n\n\ncontract IWrapperFunctions {\n\n /// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.\n /// @param order LibOrder.Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n function fillOrKillOrder(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n public\n returns (LibFillResults.FillResults memory fillResults);\n\n /// @dev Fills an order with specified parameters and ECDSA signature.\n /// Returns false if the transaction would otherwise revert.\n /// @param order LibOrder.Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n /// @return Amounts filled and fees paid by maker and taker.\n function fillOrderNoThrow(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n public\n returns (LibFillResults.FillResults memory fillResults);\n\n /// @dev Synchronously executes multiple calls of fillOrder.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function batchFillOrders(\n LibOrder.Order[] memory orders,\n uint256[] memory takerAssetFillAmounts,\n bytes[] memory signatures\n )\n public\n returns (LibFillResults.FillResults memory totalFillResults);\n\n /// @dev Synchronously executes multiple calls of fillOrKill.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function batchFillOrKillOrders(\n LibOrder.Order[] memory orders,\n uint256[] memory takerAssetFillAmounts,\n bytes[] memory signatures\n )\n public\n returns (LibFillResults.FillResults memory totalFillResults);\n\n /// @dev Fills an order with specified parameters and ECDSA signature.\n /// Returns false if the transaction would otherwise revert.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function batchFillOrdersNoThrow(\n LibOrder.Order[] memory orders,\n uint256[] memory takerAssetFillAmounts,\n bytes[] memory signatures\n )\n public\n returns (LibFillResults.FillResults memory totalFillResults);\n\n /// @dev Synchronously executes multiple calls of fillOrder until total amount of takerAsset is sold by taker.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketSellOrders(\n LibOrder.Order[] memory orders,\n uint256 takerAssetFillAmount,\n bytes[] memory signatures\n )\n public\n returns (LibFillResults.FillResults memory totalFillResults);\n\n /// @dev Synchronously executes multiple calls of fillOrder until total amount of takerAsset is sold by taker.\n /// Returns false if the transaction would otherwise revert.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketSellOrdersNoThrow(\n LibOrder.Order[] memory orders,\n uint256 takerAssetFillAmount,\n bytes[] memory signatures\n )\n public\n returns (LibFillResults.FillResults memory totalFillResults);\n\n /// @dev Synchronously executes multiple calls of fillOrder until total amount of makerAsset is bought by taker.\n /// @param orders Array of order specifications.\n /// @param makerAssetFillAmount Desired amount of makerAsset to buy.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketBuyOrders(\n LibOrder.Order[] memory orders,\n uint256 makerAssetFillAmount,\n bytes[] memory signatures\n )\n public\n returns (LibFillResults.FillResults memory totalFillResults);\n\n /// @dev Synchronously executes multiple fill orders in a single transaction until total amount is bought by taker.\n /// Returns false if the transaction would otherwise revert.\n /// @param orders Array of order specifications.\n /// @param makerAssetFillAmount Desired amount of makerAsset to buy.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketBuyOrdersNoThrow(\n LibOrder.Order[] memory orders,\n uint256 makerAssetFillAmount,\n bytes[] memory signatures\n )\n public\n returns (LibFillResults.FillResults memory totalFillResults);\n\n /// @dev Synchronously cancels multiple orders in a single transaction.\n /// @param orders Array of order specifications.\n function batchCancelOrders(LibOrder.Order[] memory orders)\n public;\n\n /// @dev Fetches information for all passed in orders\n /// @param orders Array of order specifications.\n /// @return Array of OrderInfo instances that correspond to each order.\n function getOrdersInfo(LibOrder.Order[] memory orders)\n public\n view\n returns (LibOrder.OrderInfo[] memory);\n}\n", + "2.0.0/protocol/Exchange/libs/LibAbiEncoder.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"./LibOrder.sol\";\n\n\ncontract LibAbiEncoder {\n\n /// @dev ABI encodes calldata for `fillOrder`.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n /// @return ABI encoded calldata for `fillOrder`.\n function abiEncodeFillOrder(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n internal\n pure\n returns (bytes memory fillOrderCalldata)\n {\n // We need to call MExchangeCore.fillOrder using a delegatecall in\n // assembly so that we can intercept a call that throws. For this, we\n // need the input encoded in memory in the Ethereum ABIv2 format [1].\n\n // | Area | Offset | Length | Contents |\n // | -------- |--------|---------|-------------------------------------------- |\n // | Header | 0x00 | 4 | function selector |\n // | Params | | 3 * 32 | function parameters: |\n // | | 0x00 | | 1. offset to order (*) |\n // | | 0x20 | | 2. takerAssetFillAmount |\n // | | 0x40 | | 3. offset to signature (*) |\n // | Data | | 12 * 32 | order: |\n // | | 0x000 | | 1. senderAddress |\n // | | 0x020 | | 2. makerAddress |\n // | | 0x040 | | 3. takerAddress |\n // | | 0x060 | | 4. feeRecipientAddress |\n // | | 0x080 | | 5. makerAssetAmount |\n // | | 0x0A0 | | 6. takerAssetAmount |\n // | | 0x0C0 | | 7. makerFeeAmount |\n // | | 0x0E0 | | 8. takerFeeAmount |\n // | | 0x100 | | 9. expirationTimeSeconds |\n // | | 0x120 | | 10. salt |\n // | | 0x140 | | 11. Offset to makerAssetData (*) |\n // | | 0x160 | | 12. Offset to takerAssetData (*) |\n // | | 0x180 | 32 | makerAssetData Length |\n // | | 0x1A0 | ** | makerAssetData Contents |\n // | | 0x1C0 | 32 | takerAssetData Length |\n // | | 0x1E0 | ** | takerAssetData Contents |\n // | | 0x200 | 32 | signature Length |\n // | | 0x220 | ** | signature Contents |\n\n // * Offsets are calculated from the beginning of the current area: Header, Params, Data:\n // An offset stored in the Params area is calculated from the beginning of the Params section.\n // An offset stored in the Data area is calculated from the beginning of the Data section.\n\n // ** The length of dynamic array contents are stored in the field immediately preceeding the contents.\n\n // [1]: https://solidity.readthedocs.io/en/develop/abi-spec.html\n\n assembly {\n\n // Areas below may use the following variables:\n // 1. Start -- Start of this area in memory\n // 2. End -- End of this area in memory. This value may\n // be precomputed (before writing contents),\n // or it may be computed as contents are written.\n // 3. Offset -- Current offset into area. If an area's End\n // is precomputed, this variable tracks the\n // offsets of contents as they are written.\n\n /////// Setup Header Area ///////\n // Load free memory pointer\n fillOrderCalldata := mload(0x40)\n // bytes4(keccak256(\"fillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes)\"))\n // = 0xb4be83d5\n // Leave 0x20 bytes to store the length\n mstore(add(fillOrderCalldata, 0x20), 0xb4be83d500000000000000000000000000000000000000000000000000000000)\n let headerAreaEnd := add(fillOrderCalldata, 0x24)\n\n /////// Setup Params Area ///////\n // This area is preallocated and written to later.\n // This is because we need to fill in offsets that have not yet been calculated.\n let paramsAreaStart := headerAreaEnd\n let paramsAreaEnd := add(paramsAreaStart, 0x60)\n let paramsAreaOffset := paramsAreaStart\n\n /////// Setup Data Area ///////\n let dataAreaStart := paramsAreaEnd\n let dataAreaEnd := dataAreaStart\n\n // Offset from the source data we're reading from\n let sourceOffset := order\n // arrayLenBytes and arrayLenWords track the length of a dynamically-allocated bytes array.\n let arrayLenBytes := 0\n let arrayLenWords := 0\n\n /////// Write order Struct ///////\n // Write memory location of Order, relative to the start of the\n // parameter list, then increment the paramsAreaOffset respectively.\n mstore(paramsAreaOffset, sub(dataAreaEnd, paramsAreaStart))\n paramsAreaOffset := add(paramsAreaOffset, 0x20)\n\n // Write values for each field in the order\n // It would be nice to use a loop, but we save on gas by writing\n // the stores sequentially.\n mstore(dataAreaEnd, mload(sourceOffset)) // makerAddress\n mstore(add(dataAreaEnd, 0x20), mload(add(sourceOffset, 0x20))) // takerAddress\n mstore(add(dataAreaEnd, 0x40), mload(add(sourceOffset, 0x40))) // feeRecipientAddress\n mstore(add(dataAreaEnd, 0x60), mload(add(sourceOffset, 0x60))) // senderAddress\n mstore(add(dataAreaEnd, 0x80), mload(add(sourceOffset, 0x80))) // makerAssetAmount\n mstore(add(dataAreaEnd, 0xA0), mload(add(sourceOffset, 0xA0))) // takerAssetAmount\n mstore(add(dataAreaEnd, 0xC0), mload(add(sourceOffset, 0xC0))) // makerFeeAmount\n mstore(add(dataAreaEnd, 0xE0), mload(add(sourceOffset, 0xE0))) // takerFeeAmount\n mstore(add(dataAreaEnd, 0x100), mload(add(sourceOffset, 0x100))) // expirationTimeSeconds\n mstore(add(dataAreaEnd, 0x120), mload(add(sourceOffset, 0x120))) // salt\n mstore(add(dataAreaEnd, 0x140), mload(add(sourceOffset, 0x140))) // Offset to makerAssetData\n mstore(add(dataAreaEnd, 0x160), mload(add(sourceOffset, 0x160))) // Offset to takerAssetData\n dataAreaEnd := add(dataAreaEnd, 0x180)\n sourceOffset := add(sourceOffset, 0x180)\n\n // Write offset to \n mstore(add(dataAreaStart, mul(10, 0x20)), sub(dataAreaEnd, dataAreaStart))\n\n // Calculate length of \n sourceOffset := mload(add(order, 0x140)) // makerAssetData\n arrayLenBytes := mload(sourceOffset)\n sourceOffset := add(sourceOffset, 0x20)\n arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20)\n\n // Write length of \n mstore(dataAreaEnd, arrayLenBytes)\n dataAreaEnd := add(dataAreaEnd, 0x20)\n\n // Write contents of \n for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} {\n mstore(dataAreaEnd, mload(sourceOffset))\n dataAreaEnd := add(dataAreaEnd, 0x20)\n sourceOffset := add(sourceOffset, 0x20)\n }\n\n // Write offset to \n mstore(add(dataAreaStart, mul(11, 0x20)), sub(dataAreaEnd, dataAreaStart))\n\n // Calculate length of \n sourceOffset := mload(add(order, 0x160)) // takerAssetData\n arrayLenBytes := mload(sourceOffset)\n sourceOffset := add(sourceOffset, 0x20)\n arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20)\n\n // Write length of \n mstore(dataAreaEnd, arrayLenBytes)\n dataAreaEnd := add(dataAreaEnd, 0x20)\n\n // Write contents of \n for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} {\n mstore(dataAreaEnd, mload(sourceOffset))\n dataAreaEnd := add(dataAreaEnd, 0x20)\n sourceOffset := add(sourceOffset, 0x20)\n }\n\n /////// Write takerAssetFillAmount ///////\n mstore(paramsAreaOffset, takerAssetFillAmount)\n paramsAreaOffset := add(paramsAreaOffset, 0x20)\n\n /////// Write signature ///////\n // Write offset to paramsArea\n mstore(paramsAreaOffset, sub(dataAreaEnd, paramsAreaStart))\n\n // Calculate length of signature\n sourceOffset := signature\n arrayLenBytes := mload(sourceOffset)\n sourceOffset := add(sourceOffset, 0x20)\n arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20)\n\n // Write length of signature\n mstore(dataAreaEnd, arrayLenBytes)\n dataAreaEnd := add(dataAreaEnd, 0x20)\n\n // Write contents of signature\n for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} {\n mstore(dataAreaEnd, mload(sourceOffset))\n dataAreaEnd := add(dataAreaEnd, 0x20)\n sourceOffset := add(sourceOffset, 0x20)\n }\n\n // Set length of calldata\n mstore(fillOrderCalldata, sub(dataAreaEnd, add(fillOrderCalldata, 0x20)))\n\n // Increment free memory pointer\n mstore(0x40, dataAreaEnd)\n }\n\n return fillOrderCalldata;\n }\n}\n", + "2.0.0/protocol/Exchange/libs/LibConstants.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\n// solhint-disable max-line-length\ncontract LibConstants {\n \n // Asset data for ZRX token. Used for fee transfers.\n // @TODO: Hardcode constant when we deploy. Currently \n // not constant to make testing easier.\n\n // The proxyId for ZRX_ASSET_DATA is bytes4(keccak256(\"ERC20Token(address)\")) = 0xf47261b0\n \n // Kovan ZRX address is 0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570.\n // The ABI encoded proxyId and address is 0xf47261b00000000000000000000000006ff6c0ff1d68b964901f986d4c9fa3ac68346570\n // bytes constant public ZRX_ASSET_DATA = \"\\xf4\\x72\\x61\\xb0\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x6f\\xf6\\xc0\\xff\\x1d\\x68\\xb9\\x64\\x90\\x1f\\x98\\x6d\\x4c\\x9f\\xa3\\xac\\x68\\x34\\x65\\x70\";\n \n // Mainnet ZRX address is 0xe41d2489571d322189246dafa5ebde1f4699f498.\n // The ABI encoded proxyId and address is 0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498\n bytes constant public ZRX_ASSET_DATA = \"\\xf4\\x72\\x61\\xb0\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xe4\\x1d\\x24\\x89\\x57\\x1d\\x32\\x21\\x89\\x24\\x6d\\xaf\\xa5\\xeb\\xde\\x1f\\x46\\x99\\xf4\\x98\";\n}\n// solhint-enable max-line-length\n", + "2.0.0/protocol/Exchange/libs/LibEIP712.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\ncontract LibEIP712 {\n\n // EIP191 header for EIP712 prefix\n string constant internal EIP191_HEADER = \"\\x19\\x01\";\n\n // EIP712 Domain Name value\n string constant internal EIP712_DOMAIN_NAME = \"0x Protocol\";\n\n // EIP712 Domain Version value\n string constant internal EIP712_DOMAIN_VERSION = \"2\";\n\n // Hash of the EIP712 Domain Separator Schema\n bytes32 constant internal EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256(abi.encodePacked(\n \"EIP712Domain(\",\n \"string name,\",\n \"string version,\",\n \"address verifyingContract\",\n \")\"\n ));\n\n // Hash of the EIP712 Domain Separator data\n // solhint-disable-next-line var-name-mixedcase\n bytes32 public EIP712_DOMAIN_HASH;\n\n constructor ()\n public\n {\n EIP712_DOMAIN_HASH = keccak256(abi.encodePacked(\n EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH,\n keccak256(bytes(EIP712_DOMAIN_NAME)),\n keccak256(bytes(EIP712_DOMAIN_VERSION)),\n bytes32(address(this))\n ));\n }\n\n /// @dev Calculates EIP712 encoding for a hash struct in this EIP712 Domain.\n /// @param hashStruct The EIP712 hash struct.\n /// @return EIP712 hash applied to this EIP712 Domain.\n function hashEIP712Message(bytes32 hashStruct)\n internal\n view\n returns (bytes32 result)\n {\n bytes32 eip712DomainHash = EIP712_DOMAIN_HASH;\n\n // Assembly for more efficient computing:\n // keccak256(abi.encodePacked(\n // EIP191_HEADER,\n // EIP712_DOMAIN_HASH,\n // hashStruct \n // ));\n\n assembly {\n // Load free memory pointer\n let memPtr := mload(64)\n\n mstore(memPtr, 0x1901000000000000000000000000000000000000000000000000000000000000) // EIP191 header\n mstore(add(memPtr, 2), eip712DomainHash) // EIP712 domain hash\n mstore(add(memPtr, 34), hashStruct) // Hash of struct\n\n // Compute hash\n result := keccak256(memPtr, 66)\n }\n return result;\n }\n}\n", + "2.0.0/protocol/Exchange/libs/LibExchangeErrors.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\n// solhint-disable\npragma solidity 0.4.24;\n\n\n/// @dev This contract documents the revert reasons used in the Exchange contract.\n/// This contract is intended to serve as a reference, but is not actually used for efficiency reasons.\ncontract LibExchangeErrors {\n\n /// Order validation errors ///\n string constant ORDER_UNFILLABLE = \"ORDER_UNFILLABLE\"; // Order cannot be filled.\n string constant INVALID_MAKER = \"INVALID_MAKER\"; // Invalid makerAddress.\n string constant INVALID_TAKER = \"INVALID_TAKER\"; // Invalid takerAddress.\n string constant INVALID_SENDER = \"INVALID_SENDER\"; // Invalid `msg.sender`.\n string constant INVALID_ORDER_SIGNATURE = \"INVALID_ORDER_SIGNATURE\"; // Signature validation failed. \n \n /// fillOrder validation errors ///\n string constant INVALID_TAKER_AMOUNT = \"INVALID_TAKER_AMOUNT\"; // takerAssetFillAmount cannot equal 0.\n string constant ROUNDING_ERROR = \"ROUNDING_ERROR\"; // Rounding error greater than 0.1% of takerAssetFillAmount. \n \n /// Signature validation errors ///\n string constant INVALID_SIGNATURE = \"INVALID_SIGNATURE\"; // Signature validation failed. \n string constant SIGNATURE_ILLEGAL = \"SIGNATURE_ILLEGAL\"; // Signature type is illegal.\n string constant SIGNATURE_UNSUPPORTED = \"SIGNATURE_UNSUPPORTED\"; // Signature type unsupported.\n \n /// cancelOrdersUptTo errors ///\n string constant INVALID_NEW_ORDER_EPOCH = \"INVALID_NEW_ORDER_EPOCH\"; // Specified salt must be greater than or equal to existing orderEpoch.\n\n /// fillOrKillOrder errors ///\n string constant COMPLETE_FILL_FAILED = \"COMPLETE_FILL_FAILED\"; // Desired takerAssetFillAmount could not be completely filled. \n\n /// matchOrders errors ///\n string constant NEGATIVE_SPREAD_REQUIRED = \"NEGATIVE_SPREAD_REQUIRED\"; // Matched orders must have a negative spread.\n\n /// Transaction errors ///\n string constant REENTRANCY_ILLEGAL = \"REENTRANCY_ILLEGAL\"; // Recursive reentrancy is not allowed. \n string constant INVALID_TX_HASH = \"INVALID_TX_HASH\"; // Transaction has already been executed. \n string constant INVALID_TX_SIGNATURE = \"INVALID_TX_SIGNATURE\"; // Signature validation failed. \n string constant FAILED_EXECUTION = \"FAILED_EXECUTION\"; // Transaction execution failed. \n \n /// registerAssetProxy errors ///\n string constant ASSET_PROXY_ALREADY_EXISTS = \"ASSET_PROXY_ALREADY_EXISTS\"; // AssetProxy with same id already exists.\n\n /// dispatchTransferFrom errors ///\n string constant ASSET_PROXY_DOES_NOT_EXIST = \"ASSET_PROXY_DOES_NOT_EXIST\"; // No assetProxy registered at given id.\n string constant TRANSFER_FAILED = \"TRANSFER_FAILED\"; // Asset transfer unsuccesful.\n\n /// Length validation errors ///\n string constant LENGTH_GREATER_THAN_0_REQUIRED = \"LENGTH_GREATER_THAN_0_REQUIRED\"; // Byte array must have a length greater than 0.\n string constant LENGTH_GREATER_THAN_3_REQUIRED = \"LENGTH_GREATER_THAN_3_REQUIRED\"; // Byte array must have a length greater than 3.\n string constant LENGTH_0_REQUIRED = \"LENGTH_0_REQUIRED\"; // Byte array must have a length of 0.\n string constant LENGTH_65_REQUIRED = \"LENGTH_65_REQUIRED\"; // Byte array must have a length of 65.\n}\n", + "2.0.0/protocol/Exchange/libs/LibFillResults.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../../utils/SafeMath/SafeMath.sol\";\n\n\ncontract LibFillResults is\n SafeMath\n{\n struct FillResults {\n uint256 makerAssetFilledAmount; // Total amount of makerAsset(s) filled.\n uint256 takerAssetFilledAmount; // Total amount of takerAsset(s) filled.\n uint256 makerFeePaid; // Total amount of ZRX paid by maker(s) to feeRecipient(s).\n uint256 takerFeePaid; // Total amount of ZRX paid by taker to feeRecipients(s).\n }\n\n struct MatchedFillResults {\n FillResults left; // Amounts filled and fees paid of left order.\n FillResults right; // Amounts filled and fees paid of right order.\n uint256 leftMakerAssetSpreadAmount; // Spread between price of left and right order, denominated in the left order's makerAsset, paid to taker.\n }\n\n /// @dev Adds properties of both FillResults instances.\n /// Modifies the first FillResults instance specified.\n /// @param totalFillResults Fill results instance that will be added onto.\n /// @param singleFillResults Fill results instance that will be added to totalFillResults.\n function addFillResults(FillResults memory totalFillResults, FillResults memory singleFillResults)\n internal\n pure\n {\n totalFillResults.makerAssetFilledAmount = safeAdd(totalFillResults.makerAssetFilledAmount, singleFillResults.makerAssetFilledAmount);\n totalFillResults.takerAssetFilledAmount = safeAdd(totalFillResults.takerAssetFilledAmount, singleFillResults.takerAssetFilledAmount);\n totalFillResults.makerFeePaid = safeAdd(totalFillResults.makerFeePaid, singleFillResults.makerFeePaid);\n totalFillResults.takerFeePaid = safeAdd(totalFillResults.takerFeePaid, singleFillResults.takerFeePaid);\n }\n}\n", + "2.0.0/protocol/Exchange/libs/LibMath.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../../utils/SafeMath/SafeMath.sol\";\n\n\ncontract LibMath is\n SafeMath\n{\n /// @dev Calculates partial value given a numerator and denominator rounded down.\n /// Reverts if rounding error is >= 0.1%\n /// @param numerator Numerator.\n /// @param denominator Denominator.\n /// @param target Value to calculate partial of.\n /// @return Partial value of target rounded down.\n function safeGetPartialAmountFloor(\n uint256 numerator,\n uint256 denominator,\n uint256 target\n )\n internal\n pure\n returns (uint256 partialAmount)\n {\n require(\n denominator > 0,\n \"DIVISION_BY_ZERO\"\n );\n\n require(\n !isRoundingErrorFloor(\n numerator,\n denominator,\n target\n ),\n \"ROUNDING_ERROR\"\n );\n \n partialAmount = safeDiv(\n safeMul(numerator, target),\n denominator\n );\n return partialAmount;\n }\n\n /// @dev Calculates partial value given a numerator and denominator rounded down.\n /// Reverts if rounding error is >= 0.1%\n /// @param numerator Numerator.\n /// @param denominator Denominator.\n /// @param target Value to calculate partial of.\n /// @return Partial value of target rounded up.\n function safeGetPartialAmountCeil(\n uint256 numerator,\n uint256 denominator,\n uint256 target\n )\n internal\n pure\n returns (uint256 partialAmount)\n {\n require(\n denominator > 0,\n \"DIVISION_BY_ZERO\"\n );\n\n require(\n !isRoundingErrorCeil(\n numerator,\n denominator,\n target\n ),\n \"ROUNDING_ERROR\"\n );\n \n // safeDiv computes `floor(a / b)`. We use the identity (a, b integer):\n // ceil(a / b) = floor((a + b - 1) / b)\n // To implement `ceil(a / b)` using safeDiv.\n partialAmount = safeDiv(\n safeAdd(\n safeMul(numerator, target),\n safeSub(denominator, 1)\n ),\n denominator\n );\n return partialAmount;\n }\n\n /// @dev Calculates partial value given a numerator and denominator rounded down.\n /// @param numerator Numerator.\n /// @param denominator Denominator.\n /// @param target Value to calculate partial of.\n /// @return Partial value of target rounded down.\n function getPartialAmountFloor(\n uint256 numerator,\n uint256 denominator,\n uint256 target\n )\n internal\n pure\n returns (uint256 partialAmount)\n {\n require(\n denominator > 0,\n \"DIVISION_BY_ZERO\"\n );\n\n partialAmount = safeDiv(\n safeMul(numerator, target),\n denominator\n );\n return partialAmount;\n }\n \n /// @dev Calculates partial value given a numerator and denominator rounded down.\n /// @param numerator Numerator.\n /// @param denominator Denominator.\n /// @param target Value to calculate partial of.\n /// @return Partial value of target rounded up.\n function getPartialAmountCeil(\n uint256 numerator,\n uint256 denominator,\n uint256 target\n )\n internal\n pure\n returns (uint256 partialAmount)\n {\n require(\n denominator > 0,\n \"DIVISION_BY_ZERO\"\n );\n\n // safeDiv computes `floor(a / b)`. We use the identity (a, b integer):\n // ceil(a / b) = floor((a + b - 1) / b)\n // To implement `ceil(a / b)` using safeDiv.\n partialAmount = safeDiv(\n safeAdd(\n safeMul(numerator, target),\n safeSub(denominator, 1)\n ),\n denominator\n );\n return partialAmount;\n }\n \n /// @dev Checks if rounding error >= 0.1% when rounding down.\n /// @param numerator Numerator.\n /// @param denominator Denominator.\n /// @param target Value to multiply with numerator/denominator.\n /// @return Rounding error is present.\n function isRoundingErrorFloor(\n uint256 numerator,\n uint256 denominator,\n uint256 target\n )\n internal\n pure\n returns (bool isError)\n {\n require(\n denominator > 0,\n \"DIVISION_BY_ZERO\"\n );\n \n // The absolute rounding error is the difference between the rounded\n // value and the ideal value. The relative rounding error is the\n // absolute rounding error divided by the absolute value of the\n // ideal value. This is undefined when the ideal value is zero.\n //\n // The ideal value is `numerator * target / denominator`.\n // Let's call `numerator * target % denominator` the remainder.\n // The absolute error is `remainder / denominator`.\n //\n // When the ideal value is zero, we require the absolute error to\n // be zero. Fortunately, this is always the case. The ideal value is\n // zero iff `numerator == 0` and/or `target == 0`. In this case the\n // remainder and absolute error are also zero. \n if (target == 0 || numerator == 0) {\n return false;\n }\n \n // Otherwise, we want the relative rounding error to be strictly\n // less than 0.1%.\n // The relative error is `remainder / (numerator * target)`.\n // We want the relative error less than 1 / 1000:\n // remainder / (numerator * denominator) < 1 / 1000\n // or equivalently:\n // 1000 * remainder < numerator * target\n // so we have a rounding error iff:\n // 1000 * remainder >= numerator * target\n uint256 remainder = mulmod(\n target,\n numerator,\n denominator\n );\n isError = safeMul(1000, remainder) >= safeMul(numerator, target);\n return isError;\n }\n \n /// @dev Checks if rounding error >= 0.1% when rounding up.\n /// @param numerator Numerator.\n /// @param denominator Denominator.\n /// @param target Value to multiply with numerator/denominator.\n /// @return Rounding error is present.\n function isRoundingErrorCeil(\n uint256 numerator,\n uint256 denominator,\n uint256 target\n )\n internal\n pure\n returns (bool isError)\n {\n require(\n denominator > 0,\n \"DIVISION_BY_ZERO\"\n );\n \n // See the comments in `isRoundingError`.\n if (target == 0 || numerator == 0) {\n // When either is zero, the ideal value and rounded value are zero\n // and there is no rounding error. (Although the relative error\n // is undefined.)\n return false;\n }\n // Compute remainder as before\n uint256 remainder = mulmod(\n target,\n numerator,\n denominator\n );\n remainder = safeSub(denominator, remainder) % denominator;\n isError = safeMul(1000, remainder) >= safeMul(numerator, target);\n return isError;\n }\n}\n", + "2.0.0/protocol/Exchange/libs/LibOrder.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"./LibEIP712.sol\";\n\n\ncontract LibOrder is\n LibEIP712\n{\n // Hash for the EIP712 Order Schema\n bytes32 constant internal EIP712_ORDER_SCHEMA_HASH = keccak256(abi.encodePacked(\n \"Order(\",\n \"address makerAddress,\",\n \"address takerAddress,\",\n \"address feeRecipientAddress,\",\n \"address senderAddress,\",\n \"uint256 makerAssetAmount,\",\n \"uint256 takerAssetAmount,\",\n \"uint256 makerFee,\",\n \"uint256 takerFee,\",\n \"uint256 expirationTimeSeconds,\",\n \"uint256 salt,\",\n \"bytes makerAssetData,\",\n \"bytes takerAssetData\",\n \")\"\n ));\n\n // A valid order remains fillable until it is expired, fully filled, or cancelled.\n // An order's state is unaffected by external factors, like account balances.\n enum OrderStatus {\n INVALID, // Default value\n INVALID_MAKER_ASSET_AMOUNT, // Order does not have a valid maker asset amount\n INVALID_TAKER_ASSET_AMOUNT, // Order does not have a valid taker asset amount\n FILLABLE, // Order is fillable\n EXPIRED, // Order has already expired\n FULLY_FILLED, // Order is fully filled\n CANCELLED // Order has been cancelled\n }\n\n // solhint-disable max-line-length\n struct Order {\n address makerAddress; // Address that created the order. \n address takerAddress; // Address that is allowed to fill the order. If set to 0, any address is allowed to fill the order. \n address feeRecipientAddress; // Address that will recieve fees when order is filled. \n address senderAddress; // Address that is allowed to call Exchange contract methods that affect this order. If set to 0, any address is allowed to call these methods.\n uint256 makerAssetAmount; // Amount of makerAsset being offered by maker. Must be greater than 0. \n uint256 takerAssetAmount; // Amount of takerAsset being bid on by maker. Must be greater than 0. \n uint256 makerFee; // Amount of ZRX paid to feeRecipient by maker when order is filled. If set to 0, no transfer of ZRX from maker to feeRecipient will be attempted.\n uint256 takerFee; // Amount of ZRX paid to feeRecipient by taker when order is filled. If set to 0, no transfer of ZRX from taker to feeRecipient will be attempted.\n uint256 expirationTimeSeconds; // Timestamp in seconds at which order expires. \n uint256 salt; // Arbitrary number to facilitate uniqueness of the order's hash. \n bytes makerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring makerAsset. The last byte references the id of this proxy.\n bytes takerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring takerAsset. The last byte references the id of this proxy.\n }\n // solhint-enable max-line-length\n\n struct OrderInfo {\n uint8 orderStatus; // Status that describes order's validity and fillability.\n bytes32 orderHash; // EIP712 hash of the order (see LibOrder.getOrderHash).\n uint256 orderTakerAssetFilledAmount; // Amount of order that has already been filled.\n }\n\n /// @dev Calculates Keccak-256 hash of the order.\n /// @param order The order structure.\n /// @return Keccak-256 EIP712 hash of the order.\n function getOrderHash(Order memory order)\n internal\n view\n returns (bytes32 orderHash)\n {\n orderHash = hashEIP712Message(hashOrder(order));\n return orderHash;\n }\n\n /// @dev Calculates EIP712 hash of the order.\n /// @param order The order structure.\n /// @return EIP712 hash of the order.\n function hashOrder(Order memory order)\n internal\n pure\n returns (bytes32 result)\n {\n bytes32 schemaHash = EIP712_ORDER_SCHEMA_HASH;\n bytes32 makerAssetDataHash = keccak256(order.makerAssetData);\n bytes32 takerAssetDataHash = keccak256(order.takerAssetData);\n\n // Assembly for more efficiently computing:\n // keccak256(abi.encodePacked(\n // EIP712_ORDER_SCHEMA_HASH,\n // bytes32(order.makerAddress),\n // bytes32(order.takerAddress),\n // bytes32(order.feeRecipientAddress),\n // bytes32(order.senderAddress),\n // order.makerAssetAmount,\n // order.takerAssetAmount,\n // order.makerFee,\n // order.takerFee,\n // order.expirationTimeSeconds,\n // order.salt,\n // keccak256(order.makerAssetData),\n // keccak256(order.takerAssetData)\n // ));\n\n assembly {\n // Calculate memory addresses that will be swapped out before hashing\n let pos1 := sub(order, 32)\n let pos2 := add(order, 320)\n let pos3 := add(order, 352)\n\n // Backup\n let temp1 := mload(pos1)\n let temp2 := mload(pos2)\n let temp3 := mload(pos3)\n \n // Hash in place\n mstore(pos1, schemaHash)\n mstore(pos2, makerAssetDataHash)\n mstore(pos3, takerAssetDataHash)\n result := keccak256(pos1, 416)\n \n // Restore\n mstore(pos1, temp1)\n mstore(pos2, temp2)\n mstore(pos3, temp3)\n }\n return result;\n }\n}\n", + "2.0.0/protocol/Exchange/mixins/MAssetProxyDispatcher.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../interfaces/IAssetProxyDispatcher.sol\";\n\n\ncontract MAssetProxyDispatcher is\n IAssetProxyDispatcher\n{\n // Logs registration of new asset proxy\n event AssetProxyRegistered(\n bytes4 id, // Id of new registered AssetProxy.\n address assetProxy // Address of new registered AssetProxy.\n );\n\n /// @dev Forwards arguments to assetProxy and calls `transferFrom`. Either succeeds or throws.\n /// @param assetData Byte array encoded for the asset.\n /// @param from Address to transfer token from.\n /// @param to Address to transfer token to.\n /// @param amount Amount of token to transfer.\n function dispatchTransferFrom(\n bytes memory assetData,\n address from,\n address to,\n uint256 amount\n )\n internal;\n}\n", + "2.0.0/protocol/Exchange/mixins/MExchangeCore.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../libs/LibOrder.sol\";\nimport \"../libs/LibFillResults.sol\";\nimport \"../interfaces/IExchangeCore.sol\";\n\n\ncontract MExchangeCore is\n IExchangeCore\n{\n // Fill event is emitted whenever an order is filled.\n event Fill(\n address indexed makerAddress, // Address that created the order. \n address indexed feeRecipientAddress, // Address that received fees.\n address takerAddress, // Address that filled the order.\n address senderAddress, // Address that called the Exchange contract (msg.sender).\n uint256 makerAssetFilledAmount, // Amount of makerAsset sold by maker and bought by taker. \n uint256 takerAssetFilledAmount, // Amount of takerAsset sold by taker and bought by maker.\n uint256 makerFeePaid, // Amount of ZRX paid to feeRecipient by maker.\n uint256 takerFeePaid, // Amount of ZRX paid to feeRecipient by taker.\n bytes32 indexed orderHash, // EIP712 hash of order (see LibOrder.getOrderHash).\n bytes makerAssetData, // Encoded data specific to makerAsset. \n bytes takerAssetData // Encoded data specific to takerAsset.\n );\n\n // Cancel event is emitted whenever an individual order is cancelled.\n event Cancel(\n address indexed makerAddress, // Address that created the order. \n address indexed feeRecipientAddress, // Address that would have recieved fees if order was filled. \n address senderAddress, // Address that called the Exchange contract (msg.sender).\n bytes32 indexed orderHash, // EIP712 hash of order (see LibOrder.getOrderHash).\n bytes makerAssetData, // Encoded data specific to makerAsset. \n bytes takerAssetData // Encoded data specific to takerAsset.\n );\n\n // CancelUpTo event is emitted whenever `cancelOrdersUpTo` is executed succesfully.\n event CancelUpTo(\n address indexed makerAddress, // Orders cancelled must have been created by this address.\n address indexed senderAddress, // Orders cancelled must have a `senderAddress` equal to this address.\n uint256 orderEpoch // Orders with specified makerAddress and senderAddress with a salt less than this value are considered cancelled.\n );\n\n /// @dev Fills the input order.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n /// @return Amounts filled and fees paid by maker and taker.\n function fillOrderInternal(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n internal\n returns (LibFillResults.FillResults memory fillResults);\n\n /// @dev After calling, the order can not be filled anymore.\n /// @param order Order struct containing order specifications.\n function cancelOrderInternal(LibOrder.Order memory order)\n internal;\n\n /// @dev Updates state with results of a fill order.\n /// @param order that was filled.\n /// @param takerAddress Address of taker who filled the order.\n /// @param orderTakerAssetFilledAmount Amount of order already filled.\n /// @return fillResults Amounts filled and fees paid by maker and taker.\n function updateFilledState(\n LibOrder.Order memory order,\n address takerAddress,\n bytes32 orderHash,\n uint256 orderTakerAssetFilledAmount,\n LibFillResults.FillResults memory fillResults\n )\n internal;\n\n /// @dev Updates state with results of cancelling an order.\n /// State is only updated if the order is currently fillable.\n /// Otherwise, updating state would have no effect.\n /// @param order that was cancelled.\n /// @param orderHash Hash of order that was cancelled.\n function updateCancelledState(\n LibOrder.Order memory order,\n bytes32 orderHash\n )\n internal;\n \n /// @dev Validates context for fillOrder. Succeeds or throws.\n /// @param order to be filled.\n /// @param orderInfo OrderStatus, orderHash, and amount already filled of order.\n /// @param takerAddress Address of order taker.\n /// @param signature Proof that the orders was created by its maker.\n function assertFillableOrder(\n LibOrder.Order memory order,\n LibOrder.OrderInfo memory orderInfo,\n address takerAddress,\n bytes memory signature\n )\n internal\n view;\n \n /// @dev Validates context for fillOrder. Succeeds or throws.\n /// @param order to be filled.\n /// @param orderInfo Status, orderHash, and amount already filled of order.\n /// @param takerAssetFillAmount Desired amount of order to fill by taker.\n /// @param takerAssetFilledAmount Amount of takerAsset that will be filled.\n /// @param makerAssetFilledAmount Amount of makerAsset that will be transfered.\n function assertValidFill(\n LibOrder.Order memory order,\n LibOrder.OrderInfo memory orderInfo,\n uint256 takerAssetFillAmount,\n uint256 takerAssetFilledAmount,\n uint256 makerAssetFilledAmount\n )\n internal\n view;\n\n /// @dev Validates context for cancelOrder. Succeeds or throws.\n /// @param order to be cancelled.\n /// @param orderInfo OrderStatus, orderHash, and amount already filled of order.\n function assertValidCancel(\n LibOrder.Order memory order,\n LibOrder.OrderInfo memory orderInfo\n )\n internal\n view;\n\n /// @dev Calculates amounts filled and fees paid by maker and taker.\n /// @param order to be filled.\n /// @param takerAssetFilledAmount Amount of takerAsset that will be filled.\n /// @return fillResults Amounts filled and fees paid by maker and taker.\n function calculateFillResults(\n LibOrder.Order memory order,\n uint256 takerAssetFilledAmount\n )\n internal\n pure\n returns (LibFillResults.FillResults memory fillResults);\n\n}\n", + "2.0.0/protocol/Exchange/mixins/MMatchOrders.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../libs/LibOrder.sol\";\nimport \"../libs/LibFillResults.sol\";\nimport \"../interfaces/IMatchOrders.sol\";\n\n\ncontract MMatchOrders is\n IMatchOrders\n{\n /// @dev Validates context for matchOrders. Succeeds or throws.\n /// @param leftOrder First order to match.\n /// @param rightOrder Second order to match.\n function assertValidMatch(\n LibOrder.Order memory leftOrder,\n LibOrder.Order memory rightOrder\n )\n internal\n pure;\n\n /// @dev Calculates fill amounts for the matched orders.\n /// Each order is filled at their respective price point. However, the calculations are\n /// carried out as though the orders are both being filled at the right order's price point.\n /// The profit made by the leftOrder order goes to the taker (who matched the two orders).\n /// @param leftOrder First order to match.\n /// @param rightOrder Second order to match.\n /// @param leftOrderTakerAssetFilledAmount Amount of left order already filled.\n /// @param rightOrderTakerAssetFilledAmount Amount of right order already filled.\n /// @param matchedFillResults Amounts to fill and fees to pay by maker and taker of matched orders.\n function calculateMatchedFillResults(\n LibOrder.Order memory leftOrder,\n LibOrder.Order memory rightOrder,\n uint256 leftOrderTakerAssetFilledAmount,\n uint256 rightOrderTakerAssetFilledAmount\n )\n internal\n pure\n returns (LibFillResults.MatchedFillResults memory matchedFillResults);\n\n}\n", + "2.0.0/protocol/Exchange/mixins/MSignatureValidator.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../interfaces/ISignatureValidator.sol\";\n\n\ncontract MSignatureValidator is\n ISignatureValidator\n{\n event SignatureValidatorApproval(\n address indexed signerAddress, // Address that approves or disapproves a contract to verify signatures.\n address indexed validatorAddress, // Address of signature validator contract.\n bool approved // Approval or disapproval of validator contract.\n );\n\n // Allowed signature types.\n enum SignatureType {\n Illegal, // 0x00, default value\n Invalid, // 0x01\n EIP712, // 0x02\n EthSign, // 0x03\n Wallet, // 0x04\n Validator, // 0x05\n PreSigned, // 0x06\n NSignatureTypes // 0x07, number of signature types. Always leave at end.\n }\n\n /// @dev Verifies signature using logic defined by Wallet contract.\n /// @param hash Any 32 byte hash.\n /// @param walletAddress Address that should have signed the given hash\n /// and defines its own signature verification method.\n /// @param signature Proof that the hash has been signed by signer.\n /// @return True if the address recovered from the provided signature matches the input signer address.\n function isValidWalletSignature(\n bytes32 hash,\n address walletAddress,\n bytes signature\n )\n internal\n view\n returns (bool isValid);\n\n /// @dev Verifies signature using logic defined by Validator contract.\n /// @param validatorAddress Address of validator contract.\n /// @param hash Any 32 byte hash.\n /// @param signerAddress Address that should have signed the given hash.\n /// @param signature Proof that the hash has been signed by signer.\n /// @return True if the address recovered from the provided signature matches the input signer address.\n function isValidValidatorSignature(\n address validatorAddress,\n bytes32 hash,\n address signerAddress,\n bytes signature\n )\n internal\n view\n returns (bool isValid);\n}\n", + "2.0.0/protocol/Exchange/mixins/MTransactions.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\npragma solidity 0.4.24;\n\nimport \"../interfaces/ITransactions.sol\";\n\n\ncontract MTransactions is\n ITransactions\n{\n // Hash for the EIP712 ZeroEx Transaction Schema\n bytes32 constant internal EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH = keccak256(abi.encodePacked(\n \"ZeroExTransaction(\",\n \"uint256 salt,\",\n \"address signerAddress,\",\n \"bytes data\",\n \")\"\n ));\n\n /// @dev Calculates EIP712 hash of the Transaction.\n /// @param salt Arbitrary number to ensure uniqueness of transaction hash.\n /// @param signerAddress Address of transaction signer.\n /// @param data AbiV2 encoded calldata.\n /// @return EIP712 hash of the Transaction.\n function hashZeroExTransaction(\n uint256 salt,\n address signerAddress,\n bytes memory data\n )\n internal\n pure\n returns (bytes32 result);\n\n /// @dev The current function will be called in the context of this address (either 0x transaction signer or `msg.sender`).\n /// If calling a fill function, this address will represent the taker.\n /// If calling a cancel function, this address will represent the maker.\n /// @return Signer of 0x transaction if entry point is `executeTransaction`.\n /// `msg.sender` if entry point is any other function.\n function getCurrentContextAddress()\n internal\n view\n returns (address);\n}\n", + "2.0.0/protocol/Exchange/mixins/MWrapperFunctions.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../libs/LibOrder.sol\";\nimport \"../libs/LibFillResults.sol\";\nimport \"../interfaces/IWrapperFunctions.sol\";\n\n\ncontract MWrapperFunctions is \n IWrapperFunctions\n{\n /// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.\n /// @param order LibOrder.Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n function fillOrKillOrderInternal(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n internal\n returns (LibFillResults.FillResults memory fillResults);\n}\n", + "2.0.0/tokens/ERC20Token/IERC20Token.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\ncontract IERC20Token {\n\n // solhint-disable no-simple-event-func-name\n event Transfer(\n address indexed _from,\n address indexed _to,\n uint256 _value\n );\n\n event Approval(\n address indexed _owner,\n address indexed _spender,\n uint256 _value\n );\n\n /// @dev send `value` token to `to` from `msg.sender`\n /// @param _to The address of the recipient\n /// @param _value The amount of token to be transferred\n /// @return True if transfer was successful\n function transfer(address _to, uint256 _value)\n external\n returns (bool);\n\n /// @dev send `value` token to `to` from `from` on the condition it is approved by `from`\n /// @param _from The address of the sender\n /// @param _to The address of the recipient\n /// @param _value The amount of token to be transferred\n /// @return True if transfer was successful\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n )\n external\n returns (bool);\n \n /// @dev `msg.sender` approves `_spender` to spend `_value` tokens\n /// @param _spender The address of the account able to transfer the tokens\n /// @param _value The amount of wei to be approved for transfer\n /// @return Always true if the call has enough gas to complete execution\n function approve(address _spender, uint256 _value)\n external\n returns (bool);\n\n /// @dev Query total supply of token\n /// @return Total supply of token\n function totalSupply()\n external\n view\n returns (uint256);\n \n /// @param _owner The address from which the balance will be retrieved\n /// @return Balance of owner\n function balanceOf(address _owner)\n external\n view\n returns (uint256);\n\n /// @param _owner The address of the account owning tokens\n /// @param _spender The address of the account able to transfer the tokens\n /// @return Amount of remaining tokens allowed to spent\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256);\n}\n", + "2.0.0/tokens/ERC721Token/IERC721Token.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\ncontract IERC721Token {\n\n /// @dev This emits when ownership of any NFT changes by any mechanism.\n /// This event emits when NFTs are created (`from` == 0) and destroyed\n /// (`to` == 0). Exception: during contract creation, any number of NFTs\n /// may be created and assigned without emitting Transfer. At the time of\n /// any transfer, the approved address for that NFT (if any) is reset to none.\n event Transfer(\n address indexed _from,\n address indexed _to,\n uint256 indexed _tokenId\n );\n\n /// @dev This emits when the approved address for an NFT is changed or\n /// reaffirmed. The zero address indicates there is no approved address.\n /// When a Transfer event emits, this also indicates that the approved\n /// address for that NFT (if any) is reset to none.\n event Approval(\n address indexed _owner,\n address indexed _approved,\n uint256 indexed _tokenId\n );\n\n /// @dev This emits when an operator is enabled or disabled for an owner.\n /// The operator can manage all NFTs of the owner.\n event ApprovalForAll(\n address indexed _owner,\n address indexed _operator,\n bool _approved\n );\n\n /// @notice Transfers the ownership of an NFT from one address to another address\n /// @dev Throws unless `msg.sender` is the current owner, an authorized\n /// perator, or the approved address for this NFT. Throws if `_from` is\n /// not the current owner. Throws if `_to` is the zero address. Throws if\n /// `_tokenId` is not a valid NFT. When transfer is complete, this function\n /// checks if `_to` is a smart contract (code size > 0). If so, it calls\n /// `onERC721Received` on `_to` and throws if the return value is not\n /// `bytes4(keccak256(\"onERC721Received(address,address,uint256,bytes)\"))`.\n /// @param _from The current owner of the NFT\n /// @param _to The new owner\n /// @param _tokenId The NFT to transfer\n /// @param _data Additional data with no specified format, sent in call to `_to`\n function safeTransferFrom(\n address _from,\n address _to,\n uint256 _tokenId,\n bytes _data\n )\n external;\n\n /// @notice Transfers the ownership of an NFT from one address to another address\n /// @dev This works identically to the other function with an extra data parameter,\n /// except this function just sets data to \"\".\n /// @param _from The current owner of the NFT\n /// @param _to The new owner\n /// @param _tokenId The NFT to transfer\n function safeTransferFrom(\n address _from,\n address _to,\n uint256 _tokenId\n )\n external;\n\n /// @notice Change or reaffirm the approved address for an NFT\n /// @dev The zero address indicates there is no approved address.\n /// Throws unless `msg.sender` is the current NFT owner, or an authorized\n /// operator of the current owner.\n /// @param _approved The new approved NFT controller\n /// @param _tokenId The NFT to approve\n function approve(address _approved, uint256 _tokenId)\n external;\n\n /// @notice Enable or disable approval for a third party (\"operator\") to manage\n /// all of `msg.sender`'s assets\n /// @dev Emits the ApprovalForAll event. The contract MUST allow\n /// multiple operators per owner.\n /// @param _operator Address to add to the set of authorized operators\n /// @param _approved True if the operator is approved, false to revoke approval\n function setApprovalForAll(address _operator, bool _approved)\n external;\n\n /// @notice Count all NFTs assigned to an owner\n /// @dev NFTs assigned to the zero address are considered invalid, and this\n /// function throws for queries about the zero address.\n /// @param _owner An address for whom to query the balance\n /// @return The number of NFTs owned by `_owner`, possibly zero\n function balanceOf(address _owner)\n external\n view\n returns (uint256);\n\n /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE\n /// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE\n /// THEY MAY BE PERMANENTLY LOST\n /// @dev Throws unless `msg.sender` is the current owner, an authorized\n /// operator, or the approved address for this NFT. Throws if `_from` is\n /// not the current owner. Throws if `_to` is the zero address. Throws if\n /// `_tokenId` is not a valid NFT.\n /// @param _from The current owner of the NFT\n /// @param _to The new owner\n /// @param _tokenId The NFT to transfer\n function transferFrom(\n address _from,\n address _to,\n uint256 _tokenId\n )\n public;\n\n /// @notice Find the owner of an NFT\n /// @dev NFTs assigned to zero address are considered invalid, and queries\n /// about them do throw.\n /// @param _tokenId The identifier for an NFT\n /// @return The address of the owner of the NFT\n function ownerOf(uint256 _tokenId)\n public\n view\n returns (address);\n\n /// @notice Get the approved address for a single NFT\n /// @dev Throws if `_tokenId` is not a valid NFT.\n /// @param _tokenId The NFT to find the approved address for\n /// @return The approved address for this NFT, or the zero address if there is none\n function getApproved(uint256 _tokenId) \n public\n view\n returns (address);\n \n /// @notice Query if an address is an authorized operator for another address\n /// @param _owner The address that owns the NFTs\n /// @param _operator The address that acts on behalf of the owner\n /// @return True if `_operator` is an approved operator for `_owner`, false otherwise\n function isApprovedForAll(address _owner, address _operator)\n public\n view\n returns (bool);\n}\n", + "2.0.0/tokens/EtherToken/IEtherToken.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../ERC20Token/IERC20Token.sol\";\n\n\ncontract IEtherToken is\n IERC20Token\n{\n function deposit()\n public\n payable;\n \n function withdraw(uint256 amount)\n public;\n}\n", + "2.0.0/utils/LibBytes/LibBytes.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\nlibrary LibBytes {\n\n using LibBytes for bytes;\n\n /// @dev Gets the memory address for a byte array.\n /// @param input Byte array to lookup.\n /// @return memoryAddress Memory address of byte array. This\n /// points to the header of the byte array which contains\n /// the length.\n function rawAddress(bytes memory input)\n internal\n pure\n returns (uint256 memoryAddress)\n {\n assembly {\n memoryAddress := input\n }\n return memoryAddress;\n }\n \n /// @dev Gets the memory address for the contents of a byte array.\n /// @param input Byte array to lookup.\n /// @return memoryAddress Memory address of the contents of the byte array.\n function contentAddress(bytes memory input)\n internal\n pure\n returns (uint256 memoryAddress)\n {\n assembly {\n memoryAddress := add(input, 32)\n }\n return memoryAddress;\n }\n\n /// @dev Copies `length` bytes from memory location `source` to `dest`.\n /// @param dest memory address to copy bytes to.\n /// @param source memory address to copy bytes from.\n /// @param length number of bytes to copy.\n function memCopy(\n uint256 dest,\n uint256 source,\n uint256 length\n )\n internal\n pure\n {\n if (length < 32) {\n // Handle a partial word by reading destination and masking\n // off the bits we are interested in.\n // This correctly handles overlap, zero lengths and source == dest\n assembly {\n let mask := sub(exp(256, sub(32, length)), 1)\n let s := and(mload(source), not(mask))\n let d := and(mload(dest), mask)\n mstore(dest, or(s, d))\n }\n } else {\n // Skip the O(length) loop when source == dest.\n if (source == dest) {\n return;\n }\n\n // For large copies we copy whole words at a time. The final\n // word is aligned to the end of the range (instead of after the\n // previous) to handle partial words. So a copy will look like this:\n //\n // ####\n // ####\n // ####\n // ####\n //\n // We handle overlap in the source and destination range by\n // changing the copying direction. This prevents us from\n // overwriting parts of source that we still need to copy.\n //\n // This correctly handles source == dest\n //\n if (source > dest) {\n assembly {\n // We subtract 32 from `sEnd` and `dEnd` because it\n // is easier to compare with in the loop, and these\n // are also the addresses we need for copying the\n // last bytes.\n length := sub(length, 32)\n let sEnd := add(source, length)\n let dEnd := add(dest, length)\n\n // Remember the last 32 bytes of source\n // This needs to be done here and not after the loop\n // because we may have overwritten the last bytes in\n // source already due to overlap.\n let last := mload(sEnd)\n\n // Copy whole words front to back\n // Note: the first check is always true,\n // this could have been a do-while loop.\n // solhint-disable-next-line no-empty-blocks\n for {} lt(source, sEnd) {} {\n mstore(dest, mload(source))\n source := add(source, 32)\n dest := add(dest, 32)\n }\n \n // Write the last 32 bytes\n mstore(dEnd, last)\n }\n } else {\n assembly {\n // We subtract 32 from `sEnd` and `dEnd` because those\n // are the starting points when copying a word at the end.\n length := sub(length, 32)\n let sEnd := add(source, length)\n let dEnd := add(dest, length)\n\n // Remember the first 32 bytes of source\n // This needs to be done here and not after the loop\n // because we may have overwritten the first bytes in\n // source already due to overlap.\n let first := mload(source)\n\n // Copy whole words back to front\n // We use a signed comparisson here to allow dEnd to become\n // negative (happens when source and dest < 32). Valid\n // addresses in local memory will never be larger than\n // 2**255, so they can be safely re-interpreted as signed.\n // Note: the first check is always true,\n // this could have been a do-while loop.\n // solhint-disable-next-line no-empty-blocks\n for {} slt(dest, dEnd) {} {\n mstore(dEnd, mload(sEnd))\n sEnd := sub(sEnd, 32)\n dEnd := sub(dEnd, 32)\n }\n \n // Write the first 32 bytes\n mstore(dest, first)\n }\n }\n }\n }\n\n /// @dev Returns a slices from a byte array.\n /// @param b The byte array to take a slice from.\n /// @param from The starting index for the slice (inclusive).\n /// @param to The final index for the slice (exclusive).\n /// @return result The slice containing bytes at indices [from, to)\n function slice(\n bytes memory b,\n uint256 from,\n uint256 to\n )\n internal\n pure\n returns (bytes memory result)\n {\n require(\n from <= to,\n \"FROM_LESS_THAN_TO_REQUIRED\"\n );\n require(\n to < b.length,\n \"TO_LESS_THAN_LENGTH_REQUIRED\"\n );\n \n // Create a new bytes structure and copy contents\n result = new bytes(to - from);\n memCopy(\n result.contentAddress(),\n b.contentAddress() + from,\n result.length\n );\n return result;\n }\n \n /// @dev Returns a slice from a byte array without preserving the input.\n /// @param b The byte array to take a slice from. Will be destroyed in the process.\n /// @param from The starting index for the slice (inclusive).\n /// @param to The final index for the slice (exclusive).\n /// @return result The slice containing bytes at indices [from, to)\n /// @dev When `from == 0`, the original array will match the slice. In other cases its state will be corrupted.\n function sliceDestructive(\n bytes memory b,\n uint256 from,\n uint256 to\n )\n internal\n pure\n returns (bytes memory result)\n {\n require(\n from <= to,\n \"FROM_LESS_THAN_TO_REQUIRED\"\n );\n require(\n to < b.length,\n \"TO_LESS_THAN_LENGTH_REQUIRED\"\n );\n \n // Create a new bytes structure around [from, to) in-place.\n assembly {\n result := add(b, from)\n mstore(result, sub(to, from))\n }\n return result;\n }\n\n /// @dev Pops the last byte off of a byte array by modifying its length.\n /// @param b Byte array that will be modified.\n /// @return The byte that was popped off.\n function popLastByte(bytes memory b)\n internal\n pure\n returns (bytes1 result)\n {\n require(\n b.length > 0,\n \"GREATER_THAN_ZERO_LENGTH_REQUIRED\"\n );\n\n // Store last byte.\n result = b[b.length - 1];\n\n assembly {\n // Decrement length of byte array.\n let newLen := sub(mload(b), 1)\n mstore(b, newLen)\n }\n return result;\n }\n\n /// @dev Pops the last 20 bytes off of a byte array by modifying its length.\n /// @param b Byte array that will be modified.\n /// @return The 20 byte address that was popped off.\n function popLast20Bytes(bytes memory b)\n internal\n pure\n returns (address result)\n {\n require(\n b.length >= 20,\n \"GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED\"\n );\n\n // Store last 20 bytes.\n result = readAddress(b, b.length - 20);\n\n assembly {\n // Subtract 20 from byte array length.\n let newLen := sub(mload(b), 20)\n mstore(b, newLen)\n }\n return result;\n }\n\n /// @dev Tests equality of two byte arrays.\n /// @param lhs First byte array to compare.\n /// @param rhs Second byte array to compare.\n /// @return True if arrays are the same. False otherwise.\n function equals(\n bytes memory lhs,\n bytes memory rhs\n )\n internal\n pure\n returns (bool equal)\n {\n // Keccak gas cost is 30 + numWords * 6. This is a cheap way to compare.\n // We early exit on unequal lengths, but keccak would also correctly\n // handle this.\n return lhs.length == rhs.length && keccak256(lhs) == keccak256(rhs);\n }\n\n /// @dev Reads an address from a position in a byte array.\n /// @param b Byte array containing an address.\n /// @param index Index in byte array of address.\n /// @return address from byte array.\n function readAddress(\n bytes memory b,\n uint256 index\n )\n internal\n pure\n returns (address result)\n {\n require(\n b.length >= index + 20, // 20 is length of address\n \"GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED\"\n );\n\n // Add offset to index:\n // 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)\n // 2. Account for size difference between address length and 32-byte storage word (subtract 12 from index)\n index += 20;\n\n // Read address from array memory\n assembly {\n // 1. Add index to address of bytes array\n // 2. Load 32-byte word from memory\n // 3. Apply 20-byte mask to obtain address\n result := and(mload(add(b, index)), 0xffffffffffffffffffffffffffffffffffffffff)\n }\n return result;\n }\n\n /// @dev Writes an address into a specific position in a byte array.\n /// @param b Byte array to insert address into.\n /// @param index Index in byte array of address.\n /// @param input Address to put into byte array.\n function writeAddress(\n bytes memory b,\n uint256 index,\n address input\n )\n internal\n pure\n {\n require(\n b.length >= index + 20, // 20 is length of address\n \"GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED\"\n );\n\n // Add offset to index:\n // 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)\n // 2. Account for size difference between address length and 32-byte storage word (subtract 12 from index)\n index += 20;\n\n // Store address into array memory\n assembly {\n // The address occupies 20 bytes and mstore stores 32 bytes.\n // First fetch the 32-byte word where we'll be storing the address, then\n // apply a mask so we have only the bytes in the word that the address will not occupy.\n // Then combine these bytes with the address and store the 32 bytes back to memory with mstore.\n\n // 1. Add index to address of bytes array\n // 2. Load 32-byte word from memory\n // 3. Apply 12-byte mask to obtain extra bytes occupying word of memory where we'll store the address\n let neighbors := and(\n mload(add(b, index)),\n 0xffffffffffffffffffffffff0000000000000000000000000000000000000000\n )\n \n // Make sure input address is clean.\n // (Solidity does not guarantee this)\n input := and(input, 0xffffffffffffffffffffffffffffffffffffffff)\n\n // Store the neighbors and address into memory\n mstore(add(b, index), xor(input, neighbors))\n }\n }\n\n /// @dev Reads a bytes32 value from a position in a byte array.\n /// @param b Byte array containing a bytes32 value.\n /// @param index Index in byte array of bytes32 value.\n /// @return bytes32 value from byte array.\n function readBytes32(\n bytes memory b,\n uint256 index\n )\n internal\n pure\n returns (bytes32 result)\n {\n require(\n b.length >= index + 32,\n \"GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED\"\n );\n\n // Arrays are prefixed by a 256 bit length parameter\n index += 32;\n\n // Read the bytes32 from array memory\n assembly {\n result := mload(add(b, index))\n }\n return result;\n }\n\n /// @dev Writes a bytes32 into a specific position in a byte array.\n /// @param b Byte array to insert into.\n /// @param index Index in byte array of .\n /// @param input bytes32 to put into byte array.\n function writeBytes32(\n bytes memory b,\n uint256 index,\n bytes32 input\n )\n internal\n pure\n {\n require(\n b.length >= index + 32,\n \"GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED\"\n );\n\n // Arrays are prefixed by a 256 bit length parameter\n index += 32;\n\n // Read the bytes32 from array memory\n assembly {\n mstore(add(b, index), input)\n }\n }\n\n /// @dev Reads a uint256 value from a position in a byte array.\n /// @param b Byte array containing a uint256 value.\n /// @param index Index in byte array of uint256 value.\n /// @return uint256 value from byte array.\n function readUint256(\n bytes memory b,\n uint256 index\n )\n internal\n pure\n returns (uint256 result)\n {\n result = uint256(readBytes32(b, index));\n return result;\n }\n\n /// @dev Writes a uint256 into a specific position in a byte array.\n /// @param b Byte array to insert into.\n /// @param index Index in byte array of .\n /// @param input uint256 to put into byte array.\n function writeUint256(\n bytes memory b,\n uint256 index,\n uint256 input\n )\n internal\n pure\n {\n writeBytes32(b, index, bytes32(input));\n }\n\n /// @dev Reads an unpadded bytes4 value from a position in a byte array.\n /// @param b Byte array containing a bytes4 value.\n /// @param index Index in byte array of bytes4 value.\n /// @return bytes4 value from byte array.\n function readBytes4(\n bytes memory b,\n uint256 index\n )\n internal\n pure\n returns (bytes4 result)\n {\n require(\n b.length >= index + 4,\n \"GREATER_OR_EQUAL_TO_4_LENGTH_REQUIRED\"\n );\n\n // Arrays are prefixed by a 32 byte length field\n index += 32;\n\n // Read the bytes4 from array memory\n assembly {\n result := mload(add(b, index))\n // Solidity does not require us to clean the trailing bytes.\n // We do it anyway\n result := and(result, 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000)\n }\n return result;\n }\n\n /// @dev Reads nested bytes from a specific position.\n /// @dev NOTE: the returned value overlaps with the input value.\n /// Both should be treated as immutable.\n /// @param b Byte array containing nested bytes.\n /// @param index Index of nested bytes.\n /// @return result Nested bytes.\n function readBytesWithLength(\n bytes memory b,\n uint256 index\n )\n internal\n pure\n returns (bytes memory result)\n {\n // Read length of nested bytes\n uint256 nestedBytesLength = readUint256(b, index);\n index += 32;\n\n // Assert length of is valid, given\n // length of nested bytes\n require(\n b.length >= index + nestedBytesLength,\n \"GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED\"\n );\n \n // Return a pointer to the byte array as it exists inside `b`\n assembly {\n result := add(b, index)\n }\n return result;\n }\n\n /// @dev Inserts bytes at a specific position in a byte array.\n /// @param b Byte array to insert into.\n /// @param index Index in byte array of .\n /// @param input bytes to insert.\n function writeBytesWithLength(\n bytes memory b,\n uint256 index,\n bytes memory input\n )\n internal\n pure\n {\n // Assert length of is valid, given\n // length of input\n require(\n b.length >= index + 32 + input.length, // 32 bytes to store length\n \"GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED\"\n );\n\n // Copy into \n memCopy(\n b.contentAddress() + index,\n input.rawAddress(), // includes length of \n input.length + 32 // +32 bytes to store length\n );\n }\n\n /// @dev Performs a deep copy of a byte array onto another byte array of greater than or equal length.\n /// @param dest Byte array that will be overwritten with source bytes.\n /// @param source Byte array to copy onto dest bytes.\n function deepCopyBytes(\n bytes memory dest,\n bytes memory source\n )\n internal\n pure\n {\n uint256 sourceLen = source.length;\n // Dest length must be >= source length, or some bytes would not be copied.\n require(\n dest.length >= sourceLen,\n \"GREATER_OR_EQUAL_TO_SOURCE_BYTES_LENGTH_REQUIRED\"\n );\n memCopy(\n dest.contentAddress(),\n source.contentAddress(),\n sourceLen\n );\n }\n}\n", + "2.0.0/utils/Ownable/IOwnable.sol": "pragma solidity 0.4.24;\n\n\ncontract IOwnable {\n\n function transferOwnership(address newOwner)\n public;\n}\n", + "2.0.0/utils/Ownable/Ownable.sol": "pragma solidity 0.4.24;\n\nimport \"./IOwnable.sol\";\n\n\ncontract Ownable is\n IOwnable\n{\n address public owner;\n\n constructor ()\n public\n {\n owner = msg.sender;\n }\n\n modifier onlyOwner() {\n require(\n msg.sender == owner,\n \"ONLY_CONTRACT_OWNER\"\n );\n _;\n }\n\n function transferOwnership(address newOwner)\n public\n onlyOwner\n {\n if (newOwner != address(0)) {\n owner = newOwner;\n }\n }\n}\n", + "2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\ncontract ReentrancyGuard {\n\n // Locked state of mutex\n bool private locked = false;\n\n /// @dev Functions with this modifer cannot be reentered. The mutex will be locked\n /// before function execution and unlocked after.\n modifier nonReentrant() {\n // Ensure mutex is unlocked\n require(\n !locked,\n \"REENTRANCY_ILLEGAL\"\n );\n\n // Lock mutex before function call\n locked = true;\n\n // Perform function call\n _;\n\n // Unlock mutex after function call\n locked = false;\n }\n}\n", + "2.0.0/utils/SafeMath/SafeMath.sol": "pragma solidity 0.4.24;\n\n\ncontract SafeMath {\n\n function safeMul(uint256 a, uint256 b)\n internal\n pure\n returns (uint256)\n {\n if (a == 0) {\n return 0;\n }\n uint256 c = a * b;\n require(\n c / a == b,\n \"UINT256_OVERFLOW\"\n );\n return c;\n }\n\n function safeDiv(uint256 a, uint256 b)\n internal\n pure\n returns (uint256)\n {\n uint256 c = a / b;\n return c;\n }\n\n function safeSub(uint256 a, uint256 b)\n internal\n pure\n returns (uint256)\n {\n require(\n b <= a,\n \"UINT256_UNDERFLOW\"\n );\n return a - b;\n }\n\n function safeAdd(uint256 a, uint256 b)\n internal\n pure\n returns (uint256)\n {\n uint256 c = a + b;\n require(\n c >= a,\n \"UINT256_OVERFLOW\"\n );\n return c;\n }\n\n function max64(uint64 a, uint64 b)\n internal\n pure\n returns (uint256)\n {\n return a >= b ? a : b;\n }\n\n function min64(uint64 a, uint64 b)\n internal\n pure\n returns (uint256)\n {\n return a < b ? a : b;\n }\n\n function max256(uint256 a, uint256 b)\n internal\n pure\n returns (uint256)\n {\n return a >= b ? a : b;\n }\n\n function min256(uint256 a, uint256 b)\n internal\n pure\n returns (uint256)\n {\n return a < b ? a : b;\n }\n}\n" + }, + "sourceTreeHashHex": "0xd4b411ae8c566bfc879b9880fcf5306a0b0b7051368f0c84ad27da6b5a6f0637", + "compiler": { + "name": "solc", + "version": "soljson-v0.4.24+commit.e67f0147.js", + "settings": { + "optimizer": { + "enabled": true, + "runs": 1000000 + }, + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode.object", + "evm.bytecode.sourceMap", + "evm.deployedBytecode.object", + "evm.deployedBytecode.sourceMap" + ] + } + } + } + }, + "networks": { + "1": { + "address": "0x4f833a24e1f95d70f028921e27040ca56e09ab0b", + "links": {}, + "constructorArgs": "[]" + }, + "3": { + "address": "0x4530c0483a1633c7a1c97d2c53721caff2caaaaf", + "links": {}, + "constructorArgs": "[\"0xf47261b0000000000000000000000000ff67881f8d12f372d91baae9752eb3631ff0ed00\"]" + }, + "42": { + "address": "0x35dd2932454449b14cee11a94d3674a936d5d7b2", + "links": {}, + "constructorArgs": "[\"0xf47261b00000000000000000000000002002d3812f58e35f0ea1ffbf80a75a38c32175fa\"]" + }, + "50": { + "address": "0x48bacb9266a570d521063ef5dd96e61686dbe788", + "links": {}, + "constructorArgs": "[\"0xf47261b0000000000000000000000000871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c\"]" + } + } +} diff --git a/packages/pipeline/src/events.ts b/packages/pipeline/src/events.ts new file mode 100644 index 000000000..a6e1e4ad3 --- /dev/null +++ b/packages/pipeline/src/events.ts @@ -0,0 +1,75 @@ +import { AbiDecoder } from '@0xproject/utils'; +import { default as axios } from 'axios'; +import { BlockParam, BlockParamLiteral, DecodedLogArgs, LogEntry, LogWithDecodedArgs } from 'ethereum-types'; +import * as R from 'ramda'; + +import { artifacts } from './artifacts'; + +// const EXCHANGE_ADDRESS = '0x4f833a24e1f95d70f028921e27040ca56e09ab0b'; +const ETHERSCAN_URL = 'https://api.etherscan.io/api'; +// TOOD(albrow): Pass this in as a constructor argument instead of an env var. +const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY; + +// Raw response from etherescan.io +interface EventsResponse { + status: string; + message: string; + result: EventsResponseResult[]; +} + +// Events as represented in the response from etherscan.io +interface EventsResponseResult { + address: string; + topics: string[]; + data: string; + blockNumber: string; + timeStamp: string; + gasPrice: string; + gasUsed: string; + logIndex: string; + transactionHash: string; + transactionIndex: string; +} + +// Parses and abi-decodes the fill events response from etherscan.io. +const parseFillEventsResponse = R.pipe(R.map(convertResponseToLogEntry), R.map(tryToDecodeLogOrNoop)); + +function convertResponseToLogEntry(result: EventsResponseResult): LogEntry { + const radix = 10; + return { + logIndex: parseInt(result.logIndex, radix), + transactionIndex: parseInt(result.logIndex, radix), + transactionHash: result.transactionHash, + blockHash: '', + blockNumber: parseInt(result.blockNumber, radix), + address: result.address, + data: result.data, + topics: result.topics, + }; +} + +function tryToDecodeLogOrNoop(log: LogEntry): LogWithDecodedArgs { + const abiDecoder = new AbiDecoder([artifacts.Exchange.compilerOutput.abi]); + const logWithDecodedArgs = abiDecoder.tryToDecodeLogOrNoop(log); + // tslint:disable-next-line:no-unnecessary-type-assertion + return logWithDecodedArgs as LogWithDecodedArgs; +} + +/** + * Gets the decoded events for a specific contract and block range. + * @param contractAddress The address of the contract to get the events for. + * @param fromBlock The start of the block range to get events for (inclusive). + * @param toBlock The end of the block range to get events for (inclusive). + * @returns A list of decoded events. + */ +export async function getContractEventsAsync( + contractAddress: string, + fromBlock: BlockParam = BlockParamLiteral.Earliest, + toBlock: BlockParam = BlockParamLiteral.Latest, +): Promise>> { + const fullURL = `${ETHERSCAN_URL}?module=logs&action=getLogs&address=${contractAddress}&fromBlock=${fromBlock}&toBlock=${toBlock}&apikey=${ETHERSCAN_API_KEY}`; + const resp = await axios.get(fullURL); + // TODO(albrow): Check response code. + const decodedEvents = parseFillEventsResponse(resp.data.result); + return decodedEvents; +} diff --git a/packages/pipeline/src/global.d.ts b/packages/pipeline/src/global.d.ts index 0af4ddb30..94e63a32d 100644 --- a/packages/pipeline/src/global.d.ts +++ b/packages/pipeline/src/global.d.ts @@ -1,6 +1,6 @@ -declare module 'queue'; -declare module 'airtable'; declare module '*.json' { - const value: any; - export default value; + const json: any; + /* tslint:disable */ + export default json; + /* tslint:enable */ } diff --git a/packages/pipeline/src/models/block.ts b/packages/pipeline/src/models/block.ts deleted file mode 100644 index a81cdb293..000000000 --- a/packages/pipeline/src/models/block.ts +++ /dev/null @@ -1,22 +0,0 @@ -const block = { - tableName: 'blocks', - tableProperties: { - id: { - type: 'key', - }, - timestamp: { - type: 'timestamp', - required: true, - }, - block_number: { - type: 'bigint', - required: true, - }, - }, -}; -const logToBlockSchemaMapping: any = { - number: 'block_number', - hash: 'block_hash', - timestamp: 'timestamp', -}; -export { block, logToBlockSchemaMapping }; diff --git a/packages/pipeline/src/models/event.ts b/packages/pipeline/src/models/event.ts deleted file mode 100644 index cb1c939e5..000000000 --- a/packages/pipeline/src/models/event.ts +++ /dev/null @@ -1,84 +0,0 @@ -const event = { - tableName: 'events', - tableProperties: { - id: { - type: 'key', - }, - timestamp: { - type: 'timestamp', - required: true, - }, - event_type: { - type: 'varchar', - required: true, - }, - error_id: { - type: 'varchar', - }, - order_hash: { - type: 'char(66)', - }, - maker: { - type: 'char(42)', - }, - maker_amount: { - type: 'varchar', - }, - maker_fee: { - type: 'varchar', - }, - maker_token: { - type: 'char(42)', - }, - taker_amount: { - type: 'varchar', - }, - taker_fee: { - type: 'varchar', - }, - taker_token: { - type: 'char(42)', - }, - txn_hash: { - type: 'char(66)', - }, - gas_used: { - type: 'varchar', - }, - gas_price: { - type: 'varchar', - }, - fee_recipient: { - type: 'char(42)', - }, - method_id: { - type: 'char(10)', - }, - salt: { - type: 'varchar', - }, - block_number: { - type: 'bigint', - }, - }, -}; -const logToEventSchemaMapping: any = { - blockNumber: 'block_number', - transactionHash: 'txn_hash', - event: 'event_type', - logIndex: 'log_index', - 'args.maker': 'maker', - 'args.taker': 'taker', - 'args.feeRecipient': 'fee_recipient', - 'args.makerToken': 'maker_token', - 'args.takerToken': 'taker_token', - 'args.filledMakerTokenAmount': 'maker_amount', - 'args.filledTakerTokenAmount': 'taker_amount', - 'args.paidMakerFee': 'maker_fee', - 'args.paidTakerFee': 'taker_fee', - 'args.orderHash': 'order_hash', - 'args.cancelledMakerTokenAmount': 'maker_amount', - 'args.cancelledTakerTokenAmount': 'taker_amount', - 'args.errorId': 'error_id', -}; -export { event, logToEventSchemaMapping }; diff --git a/packages/pipeline/src/models/historical_prices.ts b/packages/pipeline/src/models/historical_prices.ts deleted file mode 100644 index cf49b579e..000000000 --- a/packages/pipeline/src/models/historical_prices.ts +++ /dev/null @@ -1,43 +0,0 @@ -const historicalPrices = { - tableName: 'historical_prices', - tableProperties: { - token: { - type: 'varchar', - }, - base: { - type: 'varchar', - }, - timestamp: { - type: 'timestamp', - }, - close: { - type: 'numeric(50)', - }, - high: { - type: 'numeric(50)', - }, - low: { - type: 'numeric(50)', - }, - open: { - type: 'numeric(50)', - }, - volume_from: { - type: 'numeric(50)', - }, - volume_to: { - type: 'numeric(50)', - }, - }, -}; -const logToHistoricalPricesSchema: { [log: string]: string } = { - token: 'token', - time: 'timestamp', - close: 'close', - high: 'high', - low: 'low', - open: 'open', - volumefrom: 'volume_from', - volumeto: 'volume_to', -}; -export { historicalPrices, logToHistoricalPricesSchema }; diff --git a/packages/pipeline/src/models/order.ts b/packages/pipeline/src/models/order.ts deleted file mode 100644 index dadae2883..000000000 --- a/packages/pipeline/src/models/order.ts +++ /dev/null @@ -1,30 +0,0 @@ -const order = { - tableName: 'orders', - tableProperties: { - id: { - type: 'key', - }, - timestamp: { - type: 'timestamp', - required: true, - }, - block_number: { - type: 'bigint', - required: true, - }, - }, -}; -const logToOrderSchemaMapping: any = { - exchangeContractAddress: 'exchange_contract_address', - maker: 'maker', - makerTokenAddress: 'maker_token', - makerTokenAmount: 'maker_amount', - makerFee: 'maker_fee', - taker: 'taker', - takerTokenAddress: 'taker_token', - takerTokenAmount: 'taker_amount', - takerFee: 'taker_fee', - expirationUnixTimestampSec: 'expiration_unix_timestamp_sec', - salt: 'salt', -}; -export { order, logToOrderSchemaMapping }; diff --git a/packages/pipeline/src/models/price.ts b/packages/pipeline/src/models/price.ts deleted file mode 100644 index 9e45cc2a9..000000000 --- a/packages/pipeline/src/models/price.ts +++ /dev/null @@ -1,15 +0,0 @@ -const price = { - tableName: 'prices', - tableProperties: { - address: { - type: 'char(42)', - }, - timestamp: { - type: 'timestamp', - }, - price: { - type: 'numeric(50)', - }, - }, -}; -export { price }; diff --git a/packages/pipeline/src/models/relayer.ts b/packages/pipeline/src/models/relayer.ts deleted file mode 100644 index 77a123e8b..000000000 --- a/packages/pipeline/src/models/relayer.ts +++ /dev/null @@ -1,75 +0,0 @@ -// const relayer = { -// tableName: 'relayers', -// tableProperties: { -// id: { -// type: 'integer', -// }, -// name: { -// type: 'varchar', -// }, -// url : { -// type: 'varchar', -// }, -// model: { -// type: 'varchar[]', -// }, -// status: { -// type: 'varchar', -// }, -// sra_status: { -// type: 'varchar', -// }, -// sra_http_url: { -// type: 'varchar', -// }, -// known_fee_addresses: { -// type: 'char(42)[]', -// }, -// known_taker_addresses: { -// type: 'char(42)[]', -// }, -// relayer_type: { -// type: 'varchar', -// }, -// }, -// }; -const relayer = { - tableName: 'relayers', - tableProperties: { - name: { - type: 'varchar', - }, - url: { - type: 'varchar', - }, - sra_http_endpoint: { - type: 'varchar', - }, - sra_ws_endpoint: { - type: 'varchar', - }, - fee_recipient_addresses: { - type: 'char(42)[]', - }, - taker_addresses: { - type: 'char(42)[]', - }, - }, -}; -// const logToRelayerSchemaMapping: any = { -// 'id' : 'id', -// 'fields[\'Name\']': 'name', -// 'fields[\'URL\']': 'url', -// 'fields[\'Model\']': 'model', -// 'fields[\'Status\']': 'status', -// 'fields[\'SRA Status\']': 'sra_status', -// 'fields[\'SRA HTTP URL\']': 'sra_http_url', -// 'fields[\'Known Fee Addresses\']': 'known_fee_addresses', -// 'fields[\'Known Taker Addresses\']': 'known_taker_addresses', -// 'fields[\'Relayer Type\']': 'relayer_type', -// }; -const logToRelayerSchemaMapping: any = { - name: 'name', - homepage_url: 'url', -}; -export { relayer, logToRelayerSchemaMapping }; diff --git a/packages/pipeline/src/models/tokens.ts b/packages/pipeline/src/models/tokens.ts deleted file mode 100644 index 96e8a31af..000000000 --- a/packages/pipeline/src/models/tokens.ts +++ /dev/null @@ -1,24 +0,0 @@ -const token = { - tableName: 'tokens', - tableProperties: { - address: { - type: 'char(66)', - }, - decimals: { - type: 'bigint', - }, - name: { - type: 'varchar', - }, - symbol: { - type: 'varchar', - }, - }, -}; -const logToTokenSchemaMapping: any = { - address: 'address', - decimals: 'decimals', - name: 'name', - symbol: 'symbol', -}; -export { token, logToTokenSchemaMapping }; diff --git a/packages/pipeline/src/models/transaction.ts b/packages/pipeline/src/models/transaction.ts deleted file mode 100644 index 715cc9480..000000000 --- a/packages/pipeline/src/models/transaction.ts +++ /dev/null @@ -1,36 +0,0 @@ -const transaction = { - tableName: 'transactions', - tableProperties: { - txn_hash: { - type: 'char(66)', - }, - block_hash: { - type: 'char(66)', - }, - block_number: { - type: 'bigint', - }, - gas_used: { - type: 'varchar', - }, - gas_price: { - type: 'varchar', - }, - method_id: { - type: 'char(10)', - }, - salt: { - type: 'varchar', - }, - }, -}; -const logToTransactionSchemaMapping: any = { - hash: 'txn_hash', - gas: 'gas_used', - gasPrice: 'gas_price', - blockHash: 'block_hash', - blockNumber: 'block_number', - method_id: 'method_id', - salt: 'salt', -}; -export { transaction, logToTransactionSchemaMapping }; diff --git a/packages/pipeline/src/postgres.ts b/packages/pipeline/src/postgres.ts deleted file mode 100644 index d095e5c9e..000000000 --- a/packages/pipeline/src/postgres.ts +++ /dev/null @@ -1,12 +0,0 @@ -import * as dotenv from 'dotenv'; -import { Pool, PoolConfig } from 'pg'; -dotenv.config(); -const client: PoolConfig = { - user: process.env.AURORA_USER, - database: process.env.AURORA_DB, - password: process.env.AURORA_PASSWORD, - port: parseInt(process.env.AURORA_PORT || '5432', 10), - host: process.env.AURORA_HOST, -}; -const postgresClient = new Pool(client); -export { postgresClient }; diff --git a/packages/pipeline/src/run_jobs.ts b/packages/pipeline/src/run_jobs.ts deleted file mode 100644 index 4d82d4e2d..000000000 --- a/packages/pipeline/src/run_jobs.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { exec } from 'child_process'; - -import { postgresClient } from './postgres.js'; -import { dataFetchingQueries } from './scripts/query_data.js'; -import { web3, zrx } from './zrx.js'; -const CUR_BLOCK_OFFSET = 20; -postgresClient.query(dataFetchingQueries.get_max_block, []).then((data: any) => { - const maxBlockNumber = data.rows[0].max; - const safeCurBlockNumber = web3.eth.blockNumber - CUR_BLOCK_OFFSET; - console.log('Scraping ' + maxBlockNumber + ' to ' + safeCurBlockNumber); - exec( - 'node ./lib/scripts/scrape_data --type events --from ' + maxBlockNumber + ' --to ' + safeCurBlockNumber, - (error, stdout, stderr) => { - if (error) { - console.log(error); - return; - } - console.log('Scraped events'); - console.log('Scraping blocks'); - exec( - 'node ./lib/scripts/scrape_data --type blocks --from ' + maxBlockNumber + ' --to ' + safeCurBlockNumber, - (error, stdout, stderr) => { - if (error) { - console.log(error); - return; - } - console.log('Scraped blocks'); - console.log('Scraping transactions'); - exec( - 'node ./lib/scripts/scrape_data --type transactions --from ' + - maxBlockNumber + - ' --to ' + - safeCurBlockNumber, - (error, stdout, stderr) => { - if (error) { - console.log(error); - return; - } - console.log('Scraped transactions'); - console.log('Joining events_staging'); - exec( - 'node ./lib/scripts/join_tables --name events_staging --from ' + - maxBlockNumber + - ' --to ' + - safeCurBlockNumber, - (error, stdout, stderr) => { - if (error) { - console.log(error); - return; - } - console.log('Joined events_staging'); - console.log('Joining events'); - exec( - 'node ./lib/scripts/join_tables --name events --from ' + - maxBlockNumber + - ' --to ' + - safeCurBlockNumber, - (error, stdout, stderr) => { - if (error) { - console.log(error); - return; - } - console.log('Joined events'); - console.log('Joining events_full'); - exec( - 'node ./lib/scripts/join_tables --name events_full --from ' + - maxBlockNumber + - ' --to ' + - safeCurBlockNumber, - (error, stdout, stderr) => { - if (error) { - console.log(error); - return; - } - }, - ); - }, - ); - }, - ); - }, - ); - }, - ); - }, - ); -}); diff --git a/packages/pipeline/src/scrape_prices.ts b/packages/pipeline/src/scrape_prices.ts deleted file mode 100644 index c26062fac..000000000 --- a/packages/pipeline/src/scrape_prices.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { postgresClient } from './postgres.js'; -import { dataFetchingQueries } from './scripts/query_data.js'; -import { scrapeDataScripts } from './scripts/scrape_data.js'; -import { web3, zrx } from './zrx.js'; -const CUR_BLOCK_OFFSET = 20; -postgresClient.query(dataFetchingQueries.get_most_recent_pricing_date, []).then((data: any) => { - const curMaxScrapedDate = new Date(data.rows[0].max); - const curDate = new Date(); - scrapeDataScripts.scrapeAllPricesToDB(curMaxScrapedDate.getTime(), curDate.getTime()); -}); diff --git a/packages/pipeline/src/scripts/create_tables.ts b/packages/pipeline/src/scripts/create_tables.ts deleted file mode 100644 index fd0d2b78b..000000000 --- a/packages/pipeline/src/scripts/create_tables.ts +++ /dev/null @@ -1,258 +0,0 @@ -import * as commandLineArgs from 'command-line-args'; - -import { postgresClient } from '../postgres'; -import { formatters } from '../utils'; -const tableQueries: any = { - events_full: `CREATE TABLE IF NOT EXISTS events_full ( - timestamp TIMESTAMP WITH TIME ZONE, - event_type VARCHAR, - error_id VARCHAR, - order_hash CHAR(66), - maker CHAR(42), - maker_amount NUMERIC(78), - maker_fee NUMERIC(78), - maker_token CHAR(42), - taker CHAR(42), - taker_amount NUMERIC(78), - taker_fee NUMERIC(78), - taker_token CHAR(42), - txn_hash CHAR(66), - gas_used NUMERIC(78), - gas_price NUMERIC(78), - fee_recipient CHAR(42), - method_id CHAR(10), - salt VARCHAR, - block_number BIGINT, - log_index BIGINT, - taker_symbol VARCHAR, - taker_name VARCHAR, - taker_decimals BIGINT, - taker_usd_price NUMERIC(78), - taker_txn_usd_value NUMERIC(78), - maker_symbol VARCHAR, - maker_name VARCHAR, - maker_decimals BIGINT, - maker_usd_price NUMERIC(78), - maker_txn_usd_value NUMERIC(78), - PRIMARY KEY (txn_hash, order_hash, log_index) - )`, - events: `CREATE TABLE IF NOT EXISTS events ( - timestamp TIMESTAMP WITH TIME ZONE, - event_type VARCHAR, - error_id VARCHAR, - order_hash CHAR(66), - maker CHAR(42), - maker_amount NUMERIC(78), - maker_fee NUMERIC(78), - maker_token CHAR(42), - taker CHAR(42), - taker_amount NUMERIC(78), - taker_fee NUMERIC(78), - taker_token CHAR(42), - txn_hash CHAR(66), - gas_used NUMERIC(78), - gas_price NUMERIC(78), - fee_recipient CHAR(42), - method_id CHAR(10), - salt VARCHAR, - block_number BIGINT, - log_index BIGINT, - PRIMARY KEY (txn_hash, order_hash, log_index) - )`, - events_staging: `CREATE TABLE IF NOT EXISTS events_staging ( - timestamp TIMESTAMP WITH TIME ZONE, - event_type VARCHAR, - error_id VARCHAR, - order_hash CHAR(66), - maker CHAR(42), - maker_amount NUMERIC(78), - maker_fee NUMERIC(78), - maker_token CHAR(42), - taker CHAR(42), - taker_amount NUMERIC(78), - taker_fee NUMERIC(78), - taker_token CHAR(42), - txn_hash CHAR(66), - fee_recipient CHAR(42), - block_number BIGINT, - log_index BIGINT, - PRIMARY KEY (txn_hash, order_hash, log_index) - )`, - events_raw: `CREATE TABLE IF NOT EXISTS events_raw ( - event_type VARCHAR, - error_id VARCHAR, - order_hash CHAR(66), - maker CHAR(42), - maker_amount NUMERIC(78), - maker_fee NUMERIC(78), - maker_token CHAR(42), - taker CHAR(42), - taker_amount NUMERIC(78), - taker_fee NUMERIC(78), - taker_token CHAR(42), - txn_hash CHAR(66), - fee_recipient CHAR(42), - block_number BIGINT, - log_index BIGINT, - PRIMARY KEY (txn_hash, order_hash, log_index) - )`, - blocks: `CREATE TABLE IF NOT EXISTS blocks ( - timestamp TIMESTAMP WITH TIME ZONE, - block_hash CHAR(66) UNIQUE, - block_number BIGINT, - PRIMARY KEY (block_hash) - )`, - transactions: `CREATE TABLE IF NOT EXISTS transactions ( - txn_hash CHAR(66) UNIQUE, - block_hash CHAR(66), - block_number BIGINT, - gas_used NUMERIC(78), - gas_price NUMERIC(78), - method_id CHAR(10), - salt VARCHAR, - PRIMARY KEY (txn_hash) - )`, - tokens: `CREATE TABLE IF NOT EXISTS tokens ( - address CHAR(42) UNIQUE, - name VARCHAR, - symbol VARCHAR, - decimals INT, - PRIMARY KEY (address) - )`, - prices: `CREATE TABLE IF NOT EXISTS prices ( - address CHAR(42) UNIQUE, - timestamp TIMESTAMP WITH TIME ZONE, - price NUMERIC(78, 18), - PRIMARY KEY (address, timestamp) - )`, - relayers: `CREATE TABLE IF NOT EXISTS relayers ( - name VARCHAR UNIQUE, - url VARCHAR DEFAULT '', - sra_http_endpoint VARCHAR DEFAULT '', - sra_ws_endpoint VARCHAR DEFAULT '', - fee_recipient_addresses CHAR(42)[] DEFAULT '{}', - taker_addresses CHAR(42)[] DEFAULT '{}', - PRIMARY KEY(name)`, - historical_prices: `CREATE TABLE IF NOT EXISTS historical_prices ( - token VARCHAR, - base VARCHAR, - timestamp TIMESTAMP WITH TIME ZONE, - close NUMERIC(78, 18), - high NUMERIC(78, 18), - low NUMERIC(78, 18), - open NUMERIC(78, 18), - volume_from NUMERIC(78, 18), - volume_to NUMERIC(78, 18), - PRIMARY KEY (token, base, timestamp) - )`, - orders: `CREATE TABLE IF NOT EXISTS orders ( - relayer_id VARCHAR, - exchange_contract_address CHAR(42), - maker CHAR(42), - maker_amount NUMERIC(78), - maker_fee NUMERIC(78), - maker_token CHAR(42), - taker CHAR(42), - taker_amount NUMERIC(78), - taker_fee NUMERIC(78), - taker_token CHAR(42), - fee_recipient CHAR(42), - expiration_unix_timestamp_sec NUMERIC(78), - salt VARCHAR, - order_hash CHAR(66), - PRIMARY KEY (relayer_id, order_hash) - )`, -}; -function _safeQuery(query: string): any { - return new Promise((resolve, reject) => { - postgresClient - .query(query) - .then((data: any) => { - resolve(data); - }) - .catch((err: any) => { - reject(err); - }); - }); -} -export const tableScripts = { - createTable(query: string): any { - return _safeQuery(query); - }, - createAllTables(): any { - for (const tableName of tableQueries) { - _safeQuery(tableQueries[tableName]); - } - }, -}; -export const insertDataScripts = { - insertSingleRow(table: string, object: any): any { - return new Promise((resolve, reject) => { - const columns = Object.keys(object); - const safeArray: any = []; - for (const key of columns) { - if (key in object) { - if (key === 'timestamp') { - safeArray.push('to_timestamp(' + object[key] + ')'); - } else if (typeof object[key] === 'string' || object[key] instanceof String) { - safeArray.push(formatters.escapeSQLParam(object[key])); - } else { - safeArray.push(object[key]); - } - } else { - safeArray.push('default'); - } - } - const queryString = `INSERT INTO ${table} (${columns}) VALUES (${safeArray}) ON CONFLICT DO NOTHING`; - console.log(queryString); - postgresClient - .query(queryString) - .then((data: any) => { - resolve(data); - }) - .catch((err: any) => { - reject(err); - }); - }); - }, - insertMultipleRows(table: string, rows: any[], columns: any[]): any { - return new Promise((resolve, reject) => { - if (rows.length > 0) { - const rowsSplit = rows.map((value, index) => { - const safeArray: any = []; - for (const key of columns) { - if (key in value) { - if (key === 'timestamp') { - safeArray.push('to_timestamp(' + value[key] + ')'); - } else if (typeof value[key] === 'string' || value[key] instanceof String) { - safeArray.push(formatters.escapeSQLParam(value[key])); - } else if (value[key] instanceof Array) { - const escapedArray = value[key].map((subValue: string, subIndex: number) => { - return formatters.escapeSQLParam(subValue); - }); - safeArray.push('ARRAY[' + escapedArray.toString() + ']'); - } else { - safeArray.push(value[key]); - } - } else { - safeArray.push('default'); - } - } - return '(' + safeArray + ')'; - }); - const queryString = `INSERT INTO ${table} (${columns}) VALUES ${rowsSplit} ON CONFLICT DO NOTHING`; - postgresClient - .query(queryString) - .then((data: any) => { - resolve(data); - }) - .catch((err: any) => { - // console.log(err); - reject(err); - }); - } else { - resolve({}); - } - }); - }, -}; diff --git a/packages/pipeline/src/scripts/join_tables.ts b/packages/pipeline/src/scripts/join_tables.ts deleted file mode 100644 index e7c05b39a..000000000 --- a/packages/pipeline/src/scripts/join_tables.ts +++ /dev/null @@ -1,234 +0,0 @@ -import * as commandLineArgs from 'command-line-args'; - -import { postgresClient } from '../postgres'; -import { formatters } from '../utils'; -const optionDefinitions = [ - { name: 'name', alias: 'n', type: String }, - { name: 'from', alias: 'f', type: Number }, - { name: 'to', alias: 't', type: Number }, -]; -const cli = commandLineArgs(optionDefinitions); -const dataInsertionQueries: any = { - events_staging: `INSERT INTO events_staging ( - timestamp, - event_type, - error_id, - order_hash, - maker, - maker_amount, - maker_fee, - maker_token, - taker, - taker_amount, - taker_fee, - taker_token, - txn_hash, - fee_recipient, - block_number, - log_index - ) - (SELECT - b.timestamp, - a.event_type, - a.error_id, - a.order_hash, - a.maker, - a.maker_amount, - a.maker_fee, - a.maker_token, - a.taker, - a.taker_amount, - a.taker_fee, - a.taker_token, - a.txn_hash, - a.fee_recipient, - a.block_number, - a.log_index - FROM - events_raw a - JOIN - blocks b - ON - a.block_number = b.block_number - AND - b.block_number >= $1 - AND - b.block_number <= $2 - ) ON CONFLICT (order_hash, txn_hash, log_index) DO NOTHING`, - events: `INSERT INTO events ( - timestamp, - event_type, - error_id, - order_hash, - maker, - maker_amount, - maker_fee, - maker_token, - taker, - taker_amount, - taker_fee, - taker_token, - txn_hash, - fee_recipient, - block_number, - log_index, - gas_used, - gas_price, - method_id, - salt - ) - (SELECT - a.timestamp, - a.event_type, - a.error_id, - a.order_hash, - a.maker, - a.maker_amount, - a.maker_fee, - a.maker_token, - a.taker, - a.taker_amount, - a.taker_fee, - a.taker_token, - a.txn_hash, - a.fee_recipient, - a.block_number, - a.log_index, - b.gas_used, - b.gas_price, - b.method_id, - b.salt - FROM - events_staging a - JOIN - transactions b - ON - a.txn_hash = b.txn_hash - AND - a.block_number >= $1 - AND - a.block_number <= $2 - ) ON CONFLICT (order_hash, txn_hash, log_index) DO NOTHING`, - events_full: ` - INSERT INTO events_full ( - timestamp, - event_type, - error_id, - order_hash, - maker, - maker_amount, - maker_fee, - maker_token, - taker, - taker_amount, - taker_fee, - taker_token, - txn_hash, - fee_recipient, - block_number, - log_index, - gas_used, - gas_price, - method_id, - salt, - taker_symbol, - taker_name, - taker_decimals, - taker_usd_price, - taker_txn_usd_value, - maker_symbol, - maker_name, - maker_decimals, - maker_usd_price, - maker_txn_usd_value - ) - (SELECT - events.timestamp, - events.event_type, - events.error_id, - events.order_hash, - events.maker, - events.maker_amount, - events.maker_fee, - events.maker_token, - events.taker, - events.taker_amount, - events.taker_fee, - events.taker_token, - events.txn_hash, - events.fee_recipient, - events.block_number, - events.log_index, - events.gas_used, - events.gas_price, - events.method_id, - events.salt, - taker_token_prices.symbol, - taker_token_prices.name, - taker_token_prices.decimals, - taker_token_prices.price, - (events.taker_amount / (10 ^ taker_token_prices.decimals)) * taker_token_prices.price, - maker_token_prices.symbol, - maker_token_prices.name, - maker_token_prices.decimals, - maker_token_prices.price, - (events.maker_amount / (10 ^ maker_token_prices.decimals)) * maker_token_prices.price - FROM - events - LEFT JOIN - (SELECT - tokens.address, - tokens.name, - tokens.symbol, - tokens.decimals, - prices.timestamp, - prices.price - FROM - tokens - LEFT JOIN - prices - ON - tokens.symbol = prices.symbol) taker_token_prices - ON - (events.taker_token = taker_token_prices.address - AND - (DATE(events.timestamp) = DATE(taker_token_prices.timestamp) OR taker_token_prices.timestamp IS NULL)) - LEFT JOIN - (SELECT - tokens.address, - tokens.name, - tokens.symbol, - tokens.decimals, - prices.timestamp, - prices.price - FROM - tokens - LEFT JOIN - prices - ON - tokens.symbol = prices.symbol) maker_token_prices - ON - (events.maker_token = maker_token_prices.address - AND - (DATE(events.timestamp) = DATE(maker_token_prices.timestamp) OR maker_token_prices.timestamp IS NULL)) - WHERE - events.block_number >= $1 - AND - events.block_number <= $2 - ) ON CONFLICT (order_hash, txn_hash, log_index) DO NOTHING`, -}; -if (cli.name) { - const query = dataInsertionQueries[cli.name]; - if (query && cli.from) { - const fromBlock = cli.from; - const toBlock = cli.to ? cli.to : cli.from + 1; - postgresClient - .query(query, [fromBlock, toBlock]) - .then((data: any) => { - console.log(data); - }) - .catch((err: any) => { - console.error(err); - }); - } -} diff --git a/packages/pipeline/src/scripts/query_data.ts b/packages/pipeline/src/scripts/query_data.ts deleted file mode 100644 index 97e3749ea..000000000 --- a/packages/pipeline/src/scripts/query_data.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { formatters } from '../utils'; -export const dataFetchingQueries: any = { - get_missing_txn_hashes: ` - SELECT - a.txn_hash - FROM - events_raw a - WHERE NOT EXISTS - ( - SELECT - * - FROM - transactions b - WHERE - b.txn_hash = a.txn_hash - ) - AND - a.block_number >= $1 - AND - a.block_number < $2`, - get_used_block_numbers: ` - SELECT DISTINCT - a.block_number - FROM - events_raw a - WHERE NOT EXISTS - ( - SELECT - * - FROM - blocks b - WHERE - b.block_number = a.block_number - ) - AND - a.block_number >= $1 - AND - a.block_number < $2`, - get_token_registry: ` - SELECT - * - FROM - tokens`, - get_max_block: ` - SELECT - MAX(block_number) - FROM - events_raw`, - get_relayers: ` - SELECT - * - FROM - relayers`, - get_most_recent_pricing_date: ` - SELECT - MAX(DATE(timestamp)) - FROM - prices - `, - get_top_unknown_token_addresses: ` - SELECT a.token_address as address, a.txn_value / 2 as total_txn_value -FROM -(SELECT token_address, SUM(txn_value) as txn_value -FROM -(select a.timestamp, a.maker_token as token_address, (CASE WHEN a.taker_txn_usd_value > a.maker_txn_usd_value OR a.maker_txn_usd_value IS NULL - THEN a.taker_txn_usd_value - ELSE a.maker_txn_usd_value END) as txn_value - from events_full a - where a.event_type = 'LogFill' - and a.timestamp > (NOW() + INTERVAL '-24 hours') - union - select a.timestamp, a.taker_token as token_address, (CASE WHEN a.taker_txn_usd_value > a.maker_txn_usd_value OR a.maker_txn_usd_value IS NULL - THEN a.taker_txn_usd_value - ELSE a.maker_txn_usd_value END) as txn_value - from events_full a - where a.event_type = 'LogFill' - and a.timestamp > (NOW() + INTERVAL '-24 hours')) token_txn_values -WHERE token_address IS NOT NULL -AND txn_value > 0 -GROUP BY 1 -ORDER BY 2 DESC) a -LEFT JOIN tokens b -ON a.token_address = b.address -WHERE symbol is NULL -ORDER BY 2 DESC -`, -}; diff --git a/packages/pipeline/src/scripts/scrape_data.ts b/packages/pipeline/src/scripts/scrape_data.ts deleted file mode 100644 index 963670b47..000000000 --- a/packages/pipeline/src/scripts/scrape_data.ts +++ /dev/null @@ -1,649 +0,0 @@ -import { ExchangeEvents, ZeroEx } from '0x.js'; -import { HttpClient, Order, OrderbookRequest, OrderbookResponse, TokenPairsItem } from '@0xproject/connect'; -import * as Airtable from 'airtable'; -import * as commandLineArgs from 'command-line-args'; -import * as _ from 'lodash'; -import * as querystring from 'querystring'; -import * as queue from 'queue'; -import * as request from 'request'; -import * as rpn from 'request-promise-native'; - -import { HttpRequestOptions } from '../../../connect/lib/src/types.js'; -import { relayer } from '../models/relayer.js'; -import { token } from '../models/tokens.js'; -import { postgresClient } from '../postgres.js'; -import { typeConverters } from '../utils.js'; -import { web3, zrx } from '../zrx.js'; - -import { insertDataScripts } from './create_tables.js'; -import { dataFetchingQueries } from './query_data.js'; -const optionDefinitions = [ - { name: 'from', alias: 'f', type: Number }, - { name: 'to', alias: 't', type: Number }, - { name: 'type', type: String }, - { name: 'id', type: String }, - { name: 'force', type: Boolean }, - { name: 'token', type: String }, -]; -const cli = commandLineArgs(optionDefinitions); -const q = queue({ concurrency: 6, autostart: true }); -const airtableBase = new Airtable({ apiKey: process.env.AIRTABLE_API_KEY }).base(process.env.AIRTABLE_0X_BASE); -const BLOCK_INCREMENTS = 1000; -const BASE_SYMBOL = 'USD'; // use USD as base currency against -const API_HIST_LIMIT = 2000; // cryptocompare API limits histoday price query to 2000 days -const SECONDS_PER_DAY = 86400; -const PRICE_API_ENDPOINT = 'https://min-api.cryptocompare.com/data/pricehistorical'; -const RELAYER_REGISTRY_JSON = 'https://raw.githubusercontent.com/0xProject/0x-relayer-registry/master/relayers.json'; -const METAMASK_ETH_CONTRACT_METADATA_JSON = - 'https://raw.githubusercontent.com/MetaMask/eth-contract-metadata/master/contract-map.json'; -const ETHPLORER_BASE_URL = 'http://api.ethplorer.io'; -const ETHPLORER_TOP_TOKENS_JSON = `${ETHPLORER_BASE_URL}/getTopTokens?apiKey=dyijm5418TjOJe34`; -// const HIST_PRICE_API_ENDPOINT = 'https://min-api.cryptocompare.com/data/histoday'; -const AIRTABLE_RELAYER_INFO = 'Relayer Info'; -export const pullDataScripts = { - getAllEvents(fromBlockNumber: number, toBlockNumber: number): any { - return new Promise((resolve, reject) => { - const getLogsPromises: any[] = []; - getLogsPromises.push( - zrx.exchange.getLogsAsync( - ExchangeEvents.LogFill, - { fromBlock: fromBlockNumber, toBlock: toBlockNumber }, - {}, - ), - zrx.exchange.getLogsAsync( - ExchangeEvents.LogCancel, - { fromBlock: fromBlockNumber, toBlock: toBlockNumber }, - {}, - ), - zrx.exchange.getLogsAsync( - ExchangeEvents.LogError, - { fromBlock: fromBlockNumber, toBlock: toBlockNumber }, - {}, - ), - ); - Promise.all(getLogsPromises) - .then((data: any[]) => { - resolve(data); - }) - .catch((err: any) => { - reject(err); - }); - }); - }, - getBlockInfo(blockNumber: number): any { - return new Promise((resolve, reject) => { - web3.eth.getBlock(blockNumber, (err, result) => { - if (err) { - reject(err); - } else { - resolve(result); - } - }); - }); - }, - getTransactionInfo(transactionHash: string): any { - return new Promise((resolve, reject) => { - web3.eth.getTransaction(transactionHash, (err, result) => { - if (err) { - reject(err); - } else { - resolve(result); - } - }); - }); - }, - getTokenRegistry(): any { - return new Promise((resolve, reject) => { - zrx.tokenRegistry - .getTokensAsync() - .then((data: any) => { - resolve(data); - }) - .catch((err: any) => { - reject(err); - }); - }); - }, - getMetaMaskTokens(): any { - return new Promise((resolve, reject) => { - request(METAMASK_ETH_CONTRACT_METADATA_JSON, (error, response, body) => { - if (error) { - reject(error); - } else { - resolve(JSON.parse(body)); - } - }); - }); - }, - getEthplorerTopTokens(): any { - return new Promise((resolve, reject) => { - request(ETHPLORER_TOP_TOKENS_JSON, (error, response, body) => { - if (error) { - reject(error); - } else { - resolve(JSON.parse(body)); - } - }); - }); - }, - getEthplorerToken(tokenAddress: string): any { - return new Promise((resolve, reject) => { - const url = `${ETHPLORER_BASE_URL}/getTokenInfo/${tokenAddress}?apiKey=dyijm5418TjOJe34`; - request(url, (error, response, body) => { - if (error) { - reject(error); - } else { - try { - const json = JSON.parse(body); - resolve(json); - } catch (err) { - resolve({ error: 'error' }); - } - } - }); - }); - }, - getPriceData(symbol: string, timestamp: number, timeDelay?: number): any { - return new Promise((resolve, reject) => { - if (symbol === 'WETH') { - symbol = 'ETH'; - } - let parsedParams = querystring.stringify({ - fsym: symbol, - tsyms: 'USD', - ts: timestamp / 1000, - }); - console.debug(parsedParams); - setTimeout(() => { - request(PRICE_API_ENDPOINT + '?' + parsedParams, (error, response, body) => { - if (error) { - reject(error); - } else { - resolve(JSON.parse(body)); - } - }); - }, timeDelay); - }); - }, - getRelayers(): any { - return new Promise((resolve, reject) => { - request(RELAYER_REGISTRY_JSON, (error, response, body) => { - if (error) { - reject(error); - } else { - resolve(JSON.parse(body)); - } - }); - }); - }, - async getOrderBook(sraEndpoint: string): Promise { - const relayerClient = new HttpClient(sraEndpoint); - const tokenResponse: TokenPairsItem[] = await relayerClient.getTokenPairsAsync(); - const fullOrderBook: OrderbookResponse[] = []; - for (const tokenPair of tokenResponse) { - const orderBookRequest: OrderbookRequest = { - baseTokenAddress: tokenPair.tokenA.address, - quoteTokenAddress: tokenPair.tokenB.address, - }; - const orderBook: OrderbookResponse = await relayerClient.getOrderbookAsync(orderBookRequest); - fullOrderBook.push(orderBook); - } - return fullOrderBook; - }, - // async getHistoricalPrices( - // fromSymbol: string, - // toSymbol: string, - // fromTimestamp: number, - // toTimestamp: number, - // ): Promise { - // const daysInQueryPeriod = Math.round((toTimestamp - fromTimestamp) / (SECONDS_PER_DAY)); - // if(fromSymbol === 'WETH') { - // fromSymbol = 'ETH'; - // } - // var parsedParams = { - // fsym: fromSymbol, - // tsym: toSymbol, - // limit: Math.min(daysInQueryPeriod, API_HIST_LIMIT), - // toTs: toTimestamp, - // }; - // var options = { - // uri: HIST_PRICE_API_ENDPOINT, - // qs: parsedParams, - // json: false, - // }; - // try { - // const response = await rpn(options); - // return Promise.resolve(JSON.parse(response)); - // } catch (error) { - // console.debug(error); - // return Promise.reject(error); - // } - // }, -}; -export const scrapeDataScripts = { - scrapeAllPricesToDB(fromTime: number, toTime: number) { - const fromDate = new Date(fromTime); - fromDate.setUTCHours(0); - fromDate.setUTCMinutes(0); - fromDate.setUTCSeconds(0); - fromDate.setUTCMilliseconds(0); - const toDate = new Date(toTime); - postgresClient - .query(dataFetchingQueries.get_token_registry, []) - .then((result: any) => { - for (const curDate = fromDate; curDate < toDate; curDate.setDate(curDate.getDate() + 1)) { - for (const token of Object.values(result.rows)) { - console.debug('Scraping ' + curDate + ' ' + token); - q.push(_scrapePriceToDB(curDate.getTime(), token, 500)); - } - } - }) - .catch((err: any) => { - console.debug(err); - }); - }, -}; -function _scrapeEventsToDB(fromBlock: number, toBlock: number): any { - return (cb: () => void) => { - pullDataScripts - .getAllEvents(fromBlock, toBlock) - .then((data: any) => { - const parsedEvents: any = {}; - parsedEvents[ExchangeEvents.LogFill] = []; - parsedEvents[ExchangeEvents.LogCancel] = []; - parsedEvents[ExchangeEvents.LogError] = []; - for (const index in data) { - for (const datum of data[index]) { - const event = typeConverters.convertLogEventToEventObject(datum); - parsedEvents[event.event_type].push(event); - } - } - console.log(fromBlock + ' : ' + toBlock + ' ' + parsedEvents[ExchangeEvents.LogFill].length); - for (const event_type in parsedEvents) { - if (parsedEvents[event_type].length > 0) { - insertDataScripts - .insertMultipleRows( - 'events_raw', - parsedEvents[event_type], - Object.keys(parsedEvents[event_type][0]), - ) - .then(() => {}) - .catch((error: any) => {}); - } - } - cb(); - }) - .catch((err: any) => { - cb(); - }); - }; -} -function _scrapeBlockToDB(block: number): any { - return (cb: () => void) => { - pullDataScripts - .getBlockInfo(block) - .then((data: any) => { - const parsedBlock = typeConverters.convertLogBlockToBlockObject(data); - insertDataScripts - .insertSingleRow('blocks', parsedBlock) - .then((result: any) => { - cb(); - }) - .catch((err: any) => { - cb(); - }); - }) - .catch((err: any) => { - cb(); - }); - }; -} -// function _scrapeAllRelayersToDB(): any { -// return (cb: () => void) => { -// airtableBase(AIRTABLE_RELAYER_INFO) -// .select() -// .eachPage((records: any, fetchNextPage: () => void) => { -// const parsedRelayers: any[] = []; -// for(const record of records) { -// parsedRelayers.push(typeConverters.convertRelayerToRelayerObject(record)); -// } -// insertDataScripts.insertMultipleRows('relayers', parsedRelayers, Object.keys(parsedRelayers[0])) -// .then((result: any) => { -// cb(); -// }) -// .catch((err: any) => { -// cb(); -// }); -// }) -// .catch((err: any) => { -// cb(); -// }); -// }; -// } -function _scrapeAllRelayersToDB(): any { - return (cb: () => void) => { - pullDataScripts - .getRelayers() - .then((relayers: any[]) => { - console.log(relayers); - const parsedRelayers: any[] = []; - for (const relayer of relayers) { - parsedRelayers.push(typeConverters.convertRelayerToRelayerObject(relayer)); - } - console.log(parsedRelayers); - insertDataScripts - .insertMultipleRows('relayers', parsedRelayers, Object.keys(relayer.tableProperties)) - .then((result: any) => { - console.log(result); - cb(); - }) - .catch((err: any) => { - console.log(err); - cb(); - }); - }) - .catch((err: any) => { - cb(); - }); - }; -} -function _scrapeTransactionToDB(transactionHash: string): any { - return (cb: () => void) => { - pullDataScripts - .getTransactionInfo(transactionHash) - .then((data: any) => { - const parsedTransaction = typeConverters.convertLogTransactionToTransactionObject(data); - insertDataScripts - .insertSingleRow('transactions', parsedTransaction) - .then((result: any) => { - cb(); - }) - .catch((err: any) => { - cb(); - }); - }) - .catch((err: any) => { - cb(); - }); - }; -} -function _scrapeTokenRegistryToDB(): any { - return (cb: () => void) => { - pullDataScripts - .getTokenRegistry() - .then((data: any) => { - const parsedTokens: any = []; - for (const token of data) { - parsedTokens.push(typeConverters.convertLogTokenToTokenObject(token)); - } - insertDataScripts.insertMultipleRows('tokens', parsedTokens, Object.keys(parsedTokens[0])); - cb(); - }) - .catch((err: any) => { - cb(); - }); - }; -} -function _scrapeMetaMaskEthContractMetadataToDB(): any { - return (cb: () => void) => { - pullDataScripts - .getMetaMaskTokens() - .then((data: any) => { - const parsedTokens: any = []; - const dataArray = _.map(_.keys(data), (tokenAddress: string) => { - const value = _.get(data, tokenAddress); - return { - address: tokenAddress, - ...value, - }; - }); - const erc20TokensOnly = _.filter(dataArray, entry => { - const isErc20 = _.get(entry, 'erc20'); - return isErc20; - }); - for (const token of erc20TokensOnly) { - parsedTokens.push(typeConverters.convertMetaMaskTokenToTokenObject(token)); - } - insertDataScripts.insertMultipleRows('tokens', parsedTokens, Object.keys(parsedTokens[0])); - cb(); - }) - .catch((err: any) => { - cb(); - }); - }; -} -function _scrapeEthplorerTopTokensToDB(): any { - return (cb: () => void) => { - pullDataScripts - .getEthplorerTopTokens() - .then((data: any) => { - const parsedTokens: any = []; - const tokens = _.get(data, 'tokens'); - for (const token of tokens) { - parsedTokens.push(typeConverters.convertMetaMaskTokenToTokenObject(token)); - } - insertDataScripts.insertMultipleRows('tokens', parsedTokens, Object.keys(parsedTokens[0])); - cb(); - }) - .catch((err: any) => { - cb(); - }); - }; -} -function _scrapeUnknownTokenInformationToDB(): any { - return (cb: () => void) => { - postgresClient - .query(dataFetchingQueries.get_top_unknown_token_addresses) - .then(async (result: any) => { - const addresses = _.map(result.rows, row => _.get(row, 'address')); - const responses = await Promise.all( - _.map(addresses, address => pullDataScripts.getEthplorerToken(address)), - ); - const tokens = _.filter(responses, response => _.isUndefined(_.get(response, 'error'))); - const parsedTokens = _.map(tokens, tokenInfo => - typeConverters.convertEthplorerTokenToTokenObject(tokenInfo), - ); - insertDataScripts.insertMultipleRows('tokens', parsedTokens, Object.keys(parsedTokens[0])); - cb(); - }) - .catch((err: any) => { - cb(); - }); - }; -} -function _scrapePriceToDB(timestamp: number, token: any, timeDelay?: number): any { - return (cb: () => void) => { - pullDataScripts - .getPriceData(token.symbol, timestamp, timeDelay) - .then((data: any) => { - const safeSymbol = token.symbol === 'WETH' ? 'ETH' : token.symbol; - const parsedPrice = { - timestamp: timestamp / 1000, - symbol: token.symbol, - base: 'USD', - price: _.has(data[safeSymbol], 'USD') ? data[safeSymbol].USD : 0, - }; - console.debug('Inserting ' + timestamp); - console.debug(parsedPrice); - insertDataScripts.insertSingleRow('prices', parsedPrice); - cb(); - }) - .catch((err: any) => { - console.debug(err); - cb(); - }); - }; -} -// function _scrapeHistoricalPricesToDB(token: any, fromTimestamp: number, toTimestamp: number): any { -// return (cb: () => void) => { -// pullDataScripts -// .getHistoricalPrices(token, BASE_SYMBOL, fromTimestamp, toTimestamp) -// .then((data: any) => { -// const parsedHistoricalPrices: any = []; -// for (const historicalPrice of data['Data']) { -// const parsedHistoricalPrice = typeConverters.convertLogHistoricalPricesToHistoricalPricesObject(historicalPrice); -// parsedHistoricalPrice['token'] = token; -// parsedHistoricalPrice['base'] = BASE_SYMBOL; -// parsedHistoricalPrices.push(parsedHistoricalPrice); -// } -// if (parsedHistoricalPrices.length > 0) { -// insertDataScripts -// .insertMultipleRows( -// 'historical_prices', -// parsedHistoricalPrices, -// Object.keys(parsedHistoricalPrices[0]), -// ) -// .catch((error: any) => { -// console.error(error); -// }); -// } -// cb(); -// }) -// .catch((error: any) => { -// console.error(error); -// cb(); -// }); -// }; -// } -function _scrapeOrderBookToDB(id: string, sraEndpoint: string): any { - return (cb: () => void) => { - pullDataScripts - .getOrderBook(sraEndpoint) - .then((data: any) => { - for (const book of data) { - for (const order of book.bids) { - console.debug(order); - const parsedOrder = typeConverters.convertLogOrderToOrderObject(order); - parsedOrder.relayer_id = id; - parsedOrder.order_hash = ZeroEx.getOrderHashHex(order); - insertDataScripts.insertSingleRow('orders', parsedOrder).catch((error: any) => { - console.error(error); - }); - } - for (const order of book.asks) { - console.debug(order); - const parsedOrder = typeConverters.convertLogOrderToOrderObject(order); - parsedOrder.relayer_id = id; - parsedOrder.order_hash = ZeroEx.getOrderHashHex(order); - insertDataScripts.insertSingleRow('orders', parsedOrder).catch((error: any) => { - console.error(error); - }); - } - } - cb(); - }) - .catch((error: any) => { - console.error(error); - cb(); - }); - }; -} -if (cli.type === 'events') { - if (cli.from && cli.to) { - const destToBlock = cli.to ? cli.to : cli.from; - let curFromBlock = cli.from; - let curToBlock = curFromBlock; - do { - curToBlock += destToBlock - curToBlock < BLOCK_INCREMENTS ? destToBlock - curToBlock : BLOCK_INCREMENTS; - q.push(_scrapeEventsToDB(curFromBlock, curToBlock)); - curFromBlock = curToBlock + 1; - } while (curToBlock < destToBlock); - } -} else if (cli.type === 'blocks') { - if (cli.from && cli.to) { - if (cli.force) { - const destToBlock = cli.to ? cli.to : cli.from; - let curFromBlock = cli.from; - const curToBlock = curFromBlock; - for (; curFromBlock < destToBlock; curFromBlock++) { - q.push(_scrapeBlockToDB(curFromBlock)); - } - } else { - const fetchFrom = cli.from; - const fetchTo = cli.to ? cli.to : cli.from + 1; - postgresClient - .query(dataFetchingQueries.get_used_block_numbers, [fetchFrom, fetchTo]) - .then((data: any) => { - for (const row of data.rows) { - q.push(_scrapeBlockToDB(row.block_number)); - } - }) - .catch((err: any) => { - // console.debug(err); - }); - } - } -} else if (cli.type === 'transactions') { - if (cli.id) { - q.push(_scrapeTransactionToDB(cli.id)); - } else if (cli.from) { - const fetchFrom = cli.from; - const fetchTo = cli.to ? cli.to : cli.from + 1; - postgresClient - .query(dataFetchingQueries.get_missing_txn_hashes, [fetchFrom, fetchTo]) - .then((data: any) => { - for (const row of data.rows) { - q.push(_scrapeTransactionToDB(row.txn_hash)); - } - }) - .catch((err: any) => { - // console.debug(err); - }); - } -} else if (cli.type === 'tokens') { - q.push(_scrapeMetaMaskEthContractMetadataToDB()); - q.push(_scrapeEthplorerTopTokensToDB()); -} else if (cli.type === 'unknown_tokens') { - q.push(_scrapeUnknownTokenInformationToDB()); -} else if (cli.type === 'prices' && cli.from && cli.to) { - const fromDate = new Date(cli.from); - console.debug(fromDate); - fromDate.setUTCHours(0); - fromDate.setUTCMinutes(0); - fromDate.setUTCSeconds(0); - fromDate.setUTCMilliseconds(0); - console.debug(fromDate); - const toDate = new Date(cli.to); - postgresClient - .query(dataFetchingQueries.get_token_registry, []) - .then((result: any) => { - for (const curDate = fromDate; curDate < toDate; curDate.setDate(curDate.getDate() + 1)) { - for (const token of Object.values(result.rows)) { - console.debug('Scraping ' + curDate + ' ' + token); - q.push(_scrapePriceToDB(curDate.getTime(), token)); - } - } - }) - .catch((err: any) => { - console.debug(err); - }); - // } else if (cli.type === 'historical_prices') { - // if (cli.token && cli.from && cli.to) { - // q.push(_scrapeHistoricalPricesToDB(cli.token, cli.from, cli.to)); - // } - // } else if (cli.type === 'all_historical_prices') { - // if (cli.from && cli.to) { - // postgresClient - // .query(dataFetchingQueries.get_token_registry, []) - // .then((result: any) => { - // const curTokens: any = result.rows.map((a: any): any => a.symbol); - // for (const curToken of curTokens) { - // console.debug('Historical data backfill: Pushing coin ' + curToken); - // q.push(_scrapeHistoricalPricesToDB(curToken, cli.from, cli.to)); - // } - // }) - // .catch((err: any) => { - // console.debug(err); - // }); - // } -} else if (cli.type === 'relayers') { - q.push(_scrapeAllRelayersToDB()); -} else if (cli.type === 'orders') { - postgresClient.query(dataFetchingQueries.get_relayers, []).then((result: any) => { - for (const relayer of result.rows) { - if (relayer.sra_http_url) { - q.push(_scrapeOrderBookToDB(relayer.id, relayer.sra_http_url)); - } - } - }); -} diff --git a/packages/pipeline/src/types.ts b/packages/pipeline/src/types.ts deleted file mode 100644 index 1c76f12a7..000000000 --- a/packages/pipeline/src/types.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface HistoricalPriceResponse { - Response: string; - Type: number; - Aggregated: boolean; - Data: JSON[]; -} diff --git a/packages/pipeline/src/utils.ts b/packages/pipeline/src/utils.ts deleted file mode 100644 index 3474cd16b..000000000 --- a/packages/pipeline/src/utils.ts +++ /dev/null @@ -1,181 +0,0 @@ -import * as _ from 'lodash'; - -import { block, logToBlockSchemaMapping } from './models/block'; -import { event, logToEventSchemaMapping } from './models/event'; -import { historicalPrices, logToHistoricalPricesSchema } from './models/historical_prices'; -import { logToOrderSchemaMapping, order } from './models/order'; -import { logToRelayerSchemaMapping } from './models/relayer'; -import { logToTokenSchemaMapping, token } from './models/tokens'; -import { logToTransactionSchemaMapping, transaction } from './models/transaction'; -export const typeConverters = { - convertLogEventToEventObject(log: any): any { - const newEvent: any = {}; - for (const key in logToEventSchemaMapping) { - if (_.has(log, key)) { - newEvent[logToEventSchemaMapping[key]] = _.get(log, key); - if (newEvent[logToEventSchemaMapping[key]].constructor.name === 'BigNumber') { - newEvent[logToEventSchemaMapping[key]] = newEvent[logToEventSchemaMapping[key]].toString(); - } - } - } - return newEvent; - }, - convertLogBlockToBlockObject(logBlock: any): any { - const newBlock: any = {}; - for (const key in logToBlockSchemaMapping) { - if (_.has(logBlock, key)) { - newBlock[logToBlockSchemaMapping[key]] = _.get(logBlock, key); - if (newBlock[logToBlockSchemaMapping[key]].constructor.name === 'BigNumber') { - newBlock[logToBlockSchemaMapping[key]] = newBlock[logToBlockSchemaMapping[key]].toString(); - } - } - } - return newBlock; - }, - convertLogTokenToTokenObject(logToken: any): any { - const newToken: any = {}; - for (const key in logToTokenSchemaMapping) { - if (_.has(logToken, key)) { - newToken[logToTokenSchemaMapping[key]] = _.get(logToken, key); - if (newToken[logToTokenSchemaMapping[key]].constructor.name === 'BigNumber') { - newToken[logToTokenSchemaMapping[key]] = newToken[logToTokenSchemaMapping[key]].toString(); - } - } - } - newToken[logToTokenSchemaMapping.address] = newToken[logToTokenSchemaMapping.address].toLowerCase(); - return newToken; - }, - convertMetaMaskTokenToTokenObject(metaMaskToken: any): any { - const newToken: any = {}; - for (const key in logToTokenSchemaMapping) { - if (_.has(metaMaskToken, key)) { - newToken[logToTokenSchemaMapping[key]] = _.get(metaMaskToken, key); - } - } - newToken[logToTokenSchemaMapping.address] = newToken[logToTokenSchemaMapping.address].toLowerCase(); - console.log(newToken); - return newToken; - }, - convertEthplorerTokenToTokenObject(ethplorerToken: any): any { - const newToken: any = {}; - for (const key in logToTokenSchemaMapping) { - if (_.has(ethplorerToken, key)) { - newToken[logToTokenSchemaMapping[key]] = _.get(ethplorerToken, key); - } - } - newToken[logToTokenSchemaMapping.address] = newToken[logToTokenSchemaMapping.address].toLowerCase(); - return newToken; - }, - convertLogTransactionToTransactionObject(logTransaction: any): any { - const newTransaction: any = {}; - for (const key in logToTransactionSchemaMapping) { - if (_.has(logTransaction, key)) { - newTransaction[logToTransactionSchemaMapping[key]] = _.get(logTransaction, key); - if (newTransaction[logToTransactionSchemaMapping[key]].constructor.name === 'BigNumber') { - newTransaction[logToTransactionSchemaMapping[key]] = newTransaction[ - logToTransactionSchemaMapping[key] - ].toString(); - } - } else { - if (key === 'method_id') { - newTransaction[logToTransactionSchemaMapping[key]] = logTransaction.input.substring(0, 10); - } else if (key === 'salt') { - newTransaction[logToTransactionSchemaMapping[key]] = - '0x' + logTransaction.input.substring(714, 778); // Only God can judge me - } - } - } - return newTransaction; - }, - // convertRelayerToRelayerObject(logRelayer: any): any { - // const newRelayer: any = {}; - // for (const key in logToRelayerSchemaMapping) { - // if (_.has(logRelayer, key)) { - // newRelayer[logToRelayerSchemaMapping[key]] = _.get(logRelayer, key); - // if (newRelayer[logToRelayerSchemaMapping[key]].constructor.name === 'BigNumber') { - // newRelayer[logToRelayerSchemaMapping[key]] = newRelayer[logToRelayerSchemaMapping[key]].toString(); - // } - // } else if((logToRelayerSchemaMapping[key] === 'known_fee_addresses' || logToRelayerSchemaMapping[key] === 'known_taker_addresses')) { - // newRelayer[logToRelayerSchemaMapping[key]] = '{}'; - // } else { - // newRelayer[logToRelayerSchemaMapping[key]] = ''; - // } - // } - // return newRelayer; - // }, - convertRelayerToRelayerObject(logRelayer: any): any { - const newRelayer: any = {}; - for (const key in logToRelayerSchemaMapping) { - if (_.has(logRelayer, key)) { - newRelayer[logToRelayerSchemaMapping[key]] = _.get(logRelayer, key); - if (newRelayer[logToRelayerSchemaMapping[key]].constructor.name === 'BigNumber') { - newRelayer[logToRelayerSchemaMapping[key]] = newRelayer[logToRelayerSchemaMapping[key]].toString(); - } - } else if ( - logToRelayerSchemaMapping[key] === 'known_fee_addresses' || - logToRelayerSchemaMapping[key] === 'known_taker_addresses' - ) { - newRelayer[logToRelayerSchemaMapping[key]] = '{}'; - } else { - newRelayer[logToRelayerSchemaMapping[key]] = ''; - } - } - if (_.has(logRelayer, 'networks')) { - for (const network of logRelayer.networks) { - if (network.networkId === 1) { - if (_.has(network, 'sra_http_endpoint')) { - newRelayer.sra_http_endpoint = network.sra_http_endpoint; - } - if (_.has(network, 'sra_ws_endpoint')) { - newRelayer.sra_ws_endpoint = network.sra_ws_endpoint; - } - if (_.has(network, 'static_order_fields')) { - if (_.has(network, 'static_order_fields.fee_recipient_addresses')) { - newRelayer.fee_recipient_addresses = network.static_order_fields.fee_recipient_addresses; - } - if (_.has(network, 'static_order_fields.taker_addresses')) { - newRelayer.taker_addresses = network.static_order_fields.taker_addresses; - } - } - } - } - } - return newRelayer; - }, - convertLogHistoricalPricesToHistoricalPricesObject(logHistoricalPrice: any): any { - const newHistoricalPrices: any = {}; - for (const key in logToHistoricalPricesSchema) { - if (_.has(logHistoricalPrice, key)) { - newHistoricalPrices[logToHistoricalPricesSchema[key]] = _.get(logHistoricalPrice, key); - } - } - return newHistoricalPrices; - }, - convertLogOrderToOrderObject(logOrder: any): any { - const newOrder: any = {}; - for (const key in logToOrderSchemaMapping) { - if (_.has(logOrder, key)) { - console.log(key); - console.log(logOrder[key]); - newOrder[logToOrderSchemaMapping[key]] = _.get(logOrder, key); - if (newOrder[logToOrderSchemaMapping[key]].constructor.name === 'BigNumber') { - newOrder[logToOrderSchemaMapping[key]] = newOrder[logToOrderSchemaMapping[key]].toString(); - } - } - } - console.log(newOrder); - return newOrder; - }, -}; -export const formatters = { - escapeSQLParams(params: any[]): string { - let escapedString = ''; - for (const i in params) { - escapedString += "'" + params[i] + "',"; - } - return escapedString.slice(0, -1); - }, - escapeSQLParam(param: string): string { - return "'" + param + "'"; - }, -}; diff --git a/packages/pipeline/src/zrx.ts b/packages/pipeline/src/zrx.ts deleted file mode 100644 index cbe10a55a..000000000 --- a/packages/pipeline/src/zrx.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ExchangeEvents, ZeroEx } from '0x.js'; -import * as dotenv from 'dotenv'; -import * as Web3 from 'web3'; -dotenv.config(); -const provider = new Web3.providers.HttpProvider(process.env.WEB3_PROVIDER_URL); -const web3 = new Web3(provider); -const MAINNET = 1; -const zrx = new ZeroEx(provider, { - networkId: MAINNET, -}); -export { web3, zrx }; diff --git a/packages/pipeline/tsconfig.json b/packages/pipeline/tsconfig.json index 718e623c7..2ee711adc 100644 --- a/packages/pipeline/tsconfig.json +++ b/packages/pipeline/tsconfig.json @@ -4,5 +4,5 @@ "outDir": "lib", "rootDir": "." }, - "include": ["src/**/*", "test/**/*"] + "include": ["./src/**/*", "./test/**/*"] } diff --git a/tsconfig.json b/tsconfig.json index 3d2dc6da7..b8b795aab 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -40,6 +40,7 @@ { "path": "./packages/monorepo-scripts" }, { "path": "./packages/order-utils" }, { "path": "./packages/order-watcher" }, + { "path": "./packages/pipeline" }, { "path": "./packages/react-docs" }, { "path": "./packages/react-shared" }, { "path": "./packages/sol-compiler" }, diff --git a/yarn.lock b/yarn.lock index cf9052f7c..215389b52 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,23 +2,6 @@ # yarn lockfile v1 -"0x.js@^0.38.3": - version "0.38.6" - resolved "https://registry.yarnpkg.com/0x.js/-/0x.js-0.38.6.tgz#f6cb2b6ef9aea2ff7696c923095e0b794c1d84fc" - dependencies: - "@0xproject/assert" "^0.2.14" - "@0xproject/base-contract" "^0.3.6" - "@0xproject/contract-wrappers" "^0.1.1" - "@0xproject/order-utils" "^0.0.9" - "@0xproject/order-watcher" "^0.0.8" - "@0xproject/sol-compiler" "^0.5.4" - "@0xproject/types" "^0.8.2" - "@0xproject/typescript-typings" "^0.4.3" - "@0xproject/utils" "^0.7.3" - "@0xproject/web3-wrapper" "^0.7.3" - ethers "3.0.22" - lodash "4.17.10" - "@0x-lerna-fork/add@^3.0.0-beta.22": version "3.0.0-beta.22" resolved "https://registry.yarnpkg.com/@0x-lerna-fork/add/-/add-3.0.0-beta.22.tgz#b69d7cbf6a28af6abb3aabb47fb617e7c6c7ac2c" @@ -489,83 +472,81 @@ npmlog "^4.1.2" write-file-atomic "^2.3.0" -"@0xproject/assert@^0.2.14": - version "0.2.14" - resolved "https://registry.yarnpkg.com/@0xproject/assert/-/assert-0.2.14.tgz#7d5a373fedc8eb482716b730f4dddf3ef33bfa29" +"@0xproject/assert@^1.0.10", "@0xproject/assert@^1.0.13": + version "1.0.13" + resolved "https://registry.yarnpkg.com/@0xproject/assert/-/assert-1.0.13.tgz#e370ccce08933dd2a970bdd02b92e59c65dd75d4" dependencies: - "@0xproject/json-schemas" "^0.8.3" - "@0xproject/typescript-typings" "^0.4.3" - "@0xproject/utils" "^0.7.3" - lodash "4.17.10" - valid-url "1.0.9" + "@0xproject/json-schemas" "^1.0.7" + "@0xproject/typescript-typings" "^3.0.2" + "@0xproject/utils" "^2.0.2" + lodash "^4.17.5" + valid-url "^1.0.9" -"@0xproject/base-contract@^0.3.6": - version "0.3.6" - resolved "https://registry.yarnpkg.com/@0xproject/base-contract/-/base-contract-0.3.6.tgz#c6d742ce2904bdd9e77524d99ba2438d3aae8a40" +"@0xproject/base-contract@^2.0.4": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@0xproject/base-contract/-/base-contract-2.0.5.tgz#caca54b33efeb82c53142a96becc6871c96974d6" dependencies: - "@0xproject/types" "^0.8.2" - "@0xproject/typescript-typings" "^0.4.3" - "@0xproject/utils" "^0.7.3" - "@0xproject/web3-wrapper" "^0.7.3" + "@0xproject/typescript-typings" "^2.0.2" + "@0xproject/utils" "^1.0.11" + "@0xproject/web3-wrapper" "^3.0.1" + ethereum-types "^1.0.8" ethers "3.0.22" - lodash "4.17.10" - -"@0xproject/connect@^0.6.14": - version "0.6.17" - resolved "https://registry.yarnpkg.com/@0xproject/connect/-/connect-0.6.17.tgz#c281b1468d53b09adccbc87a9f1ee462dd6c0977" - dependencies: - "@0xproject/assert" "^0.2.14" - "@0xproject/json-schemas" "^0.8.3" - "@0xproject/types" "^0.8.2" - "@0xproject/typescript-typings" "^0.4.3" - "@0xproject/utils" "^0.7.3" - isomorphic-fetch "2.2.1" - lodash "4.17.10" - query-string "4.3.4" - websocket "1.0.25" - -"@0xproject/contract-wrappers@^0.1.1": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@0xproject/contract-wrappers/-/contract-wrappers-0.1.1.tgz#8108d7ec051f202ef0cfa77c91c4ef994bf89881" - dependencies: - "@0xproject/assert" "^0.2.14" - "@0xproject/base-contract" "^0.3.6" - "@0xproject/fill-scenarios" "^0.0.6" - "@0xproject/json-schemas" "^0.8.3" - "@0xproject/order-utils" "^0.0.9" - "@0xproject/sol-compiler" "^0.5.4" - "@0xproject/types" "^0.8.2" - "@0xproject/typescript-typings" "^0.4.3" - "@0xproject/utils" "^0.7.3" - "@0xproject/web3-wrapper" "^0.7.3" + lodash "^4.17.5" + +"@0xproject/base-contract@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@0xproject/base-contract/-/base-contract-3.0.1.tgz#ae6ff8c4492affcb015324d6a1a8de72b22e1f8b" + dependencies: + "@0xproject/typescript-typings" "^3.0.2" + "@0xproject/utils" "^2.0.2" + "@0xproject/web3-wrapper" "^3.0.3" + ethereum-types "^1.0.11" + ethers "4.0.0-beta.14" + lodash "^4.17.5" + +"@0xproject/contract-wrappers@^1.0.1": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@0xproject/contract-wrappers/-/contract-wrappers-1.0.5.tgz#5a522b3fd8404b8c42169d5be7e3b6903a7b1356" + dependencies: + "@0xproject/assert" "^1.0.10" + "@0xproject/base-contract" "^2.0.4" + "@0xproject/fill-scenarios" "^1.0.4" + "@0xproject/json-schemas" "^1.0.3" + "@0xproject/order-utils" "^1.0.4" + "@0xproject/types" "^1.1.0" + "@0xproject/typescript-typings" "^2.0.1" + "@0xproject/utils" "^1.0.10" + "@0xproject/web3-wrapper" "^3.0.0" + ethereum-types "^1.0.7" ethereumjs-blockstream "5.0.0" - ethereumjs-util "5.1.5" + ethereumjs-util "^5.1.1" ethers "3.0.22" - js-sha3 "0.7.0" - lodash "4.17.10" - uuid "3.2.1" + js-sha3 "^0.7.0" + lodash "^4.17.5" + uuid "^3.1.0" -"@0xproject/fill-scenarios@^0.0.6": - version "0.0.6" - resolved "https://registry.yarnpkg.com/@0xproject/fill-scenarios/-/fill-scenarios-0.0.6.tgz#969a2c49122650af5a1bf7fe7c6d286a0e6f2de2" - dependencies: - "@0xproject/base-contract" "^0.3.6" - "@0xproject/order-utils" "^0.0.9" - "@0xproject/types" "^0.8.2" - "@0xproject/typescript-typings" "^0.4.3" - "@0xproject/utils" "^0.7.3" - "@0xproject/web3-wrapper" "^0.7.3" - ethers "3.0.22" - lodash "4.17.10" +"@0xproject/fill-scenarios@^1.0.4": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@0xproject/fill-scenarios/-/fill-scenarios-1.0.7.tgz#3a5e7acec46065ccbd7f50d4052d653242aa2da2" + dependencies: + "@0xproject/base-contract" "^3.0.1" + "@0xproject/order-utils" "^1.0.7" + "@0xproject/types" "^1.1.4" + "@0xproject/typescript-typings" "^3.0.2" + "@0xproject/utils" "^2.0.2" + "@0xproject/web3-wrapper" "^3.0.3" + ethereum-types "^1.0.11" + ethers "4.0.0-beta.14" + lodash "^4.17.5" -"@0xproject/json-schemas@^0.8.3": - version "0.8.3" - resolved "https://registry.yarnpkg.com/@0xproject/json-schemas/-/json-schemas-0.8.3.tgz#455e6219a6bd05e990392165192a983a9ab89f26" +"@0xproject/json-schemas@^1.0.3", "@0xproject/json-schemas@^1.0.7": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@0xproject/json-schemas/-/json-schemas-1.0.7.tgz#64b5692a1bcc5938ce2da01fc2f8aecd72ec35be" dependencies: - "@0xproject/typescript-typings" "^0.4.3" - "@types/node" "9.6.0" - jsonschema "1.2.2" - lodash.values "4.3.0" + "@0xproject/typescript-typings" "^3.0.2" + "@types/node" "*" + jsonschema "^1.2.0" + lodash.values "^4.3.0" "@0xproject/npm-cli-login@^0.0.11": version "0.0.11" @@ -573,72 +554,53 @@ dependencies: npm-registry-client "7.0.9" -"@0xproject/order-utils@^0.0.9": - version "0.0.9" - resolved "https://registry.yarnpkg.com/@0xproject/order-utils/-/order-utils-0.0.9.tgz#75225dfbd87335d18810abf995d8e077b9a84868" - dependencies: - "@0xproject/assert" "^0.2.14" - "@0xproject/json-schemas" "^0.8.3" - "@0xproject/types" "^0.8.2" - "@0xproject/typescript-typings" "^0.4.3" - "@0xproject/utils" "^0.7.3" - "@0xproject/web3-wrapper" "^0.7.3" - "@types/node" "9.6.0" - bn.js "4.11.7" +"@0xproject/order-utils@^1.0.4", "@0xproject/order-utils@^1.0.7": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@0xproject/order-utils/-/order-utils-1.0.7.tgz#475cd5f1a11dc7816847abb4d3fd17bbaf32bf4f" + dependencies: + "@0xproject/assert" "^1.0.13" + "@0xproject/base-contract" "^3.0.1" + "@0xproject/json-schemas" "^1.0.7" + "@0xproject/types" "^1.1.4" + "@0xproject/typescript-typings" "^3.0.2" + "@0xproject/utils" "^2.0.2" + "@0xproject/web3-wrapper" "^3.0.3" + "@types/node" "*" + bn.js "^4.11.8" + ethereum-types "^1.0.11" ethereumjs-abi "0.6.5" - ethereumjs-util "5.1.5" - lodash "4.17.10" - -"@0xproject/order-watcher@^0.0.8": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@0xproject/order-watcher/-/order-watcher-0.0.8.tgz#cfba4c39ca8c5d39e076276479383d0aae022c6d" - dependencies: - "@0xproject/assert" "^0.2.14" - "@0xproject/base-contract" "^0.3.6" - "@0xproject/contract-wrappers" "^0.1.1" - "@0xproject/fill-scenarios" "^0.0.6" - "@0xproject/json-schemas" "^0.8.3" - "@0xproject/order-utils" "^0.0.9" - "@0xproject/types" "^0.8.2" - "@0xproject/typescript-typings" "^0.4.3" - "@0xproject/utils" "^0.7.3" - "@0xproject/web3-wrapper" "^0.7.3" - bintrees "1.0.2" - ethereum-types "^0.0.2" - ethereumjs-blockstream "5.0.0" - ethers "3.0.22" - lodash "4.17.10" - -"@0xproject/sol-compiler@^0.5.4": - version "0.5.4" - resolved "https://registry.yarnpkg.com/@0xproject/sol-compiler/-/sol-compiler-0.5.4.tgz#3e0b04b0c02c5ec046ebb962b5ed20978c6b4cdd" - dependencies: - "@0xproject/json-schemas" "^0.8.3" - "@0xproject/sol-resolver" "^0.0.8" - "@0xproject/types" "^0.8.2" - "@0xproject/typescript-typings" "^0.4.3" - "@0xproject/utils" "^0.7.3" - "@0xproject/web3-wrapper" "^0.7.3" - "@types/yargs" "10.0.2" - chalk "2.3.2" - ethereumjs-util "5.1.5" - isomorphic-fetch "2.2.1" - lodash "4.17.10" - mkdirp "0.5.1" - require-from-string "2.0.1" - semver "5.5.0" - solc "0.4.23" - web3 "0.20.6" - web3-eth-abi "1.0.0-beta.33" - yargs "11.1.0" + ethereumjs-util "^5.1.1" + ethers "4.0.0-beta.14" + lodash "^4.17.5" -"@0xproject/sol-resolver@^0.0.8": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@0xproject/sol-resolver/-/sol-resolver-0.0.8.tgz#5496ccebc9afa9eedfb5b62104f6478aa852bf66" - dependencies: - "@0xproject/types" "^0.8.2" - "@0xproject/typescript-typings" "^0.0.3" - lodash "4.17.10" +"@0xproject/subproviders@^2.0.2": + version "2.0.7" + resolved "https://registry.yarnpkg.com/@0xproject/subproviders/-/subproviders-2.0.7.tgz#0b126f6d6df0ad7bd65689fae5a2d109991f019e" + dependencies: + "@0xproject/assert" "^1.0.13" + "@0xproject/types" "^1.1.4" + "@0xproject/typescript-typings" "^3.0.2" + "@0xproject/utils" "^2.0.2" + "@0xproject/web3-wrapper" "^3.0.3" + "@ledgerhq/hw-app-eth" "^4.3.0" + "@ledgerhq/hw-transport-u2f" "^4.3.0" + "@types/eth-lightwallet" "^3.0.0" + "@types/ganache-core" "^2.1.0" + "@types/hdkey" "^0.7.0" + bip39 "^2.5.0" + bn.js "^4.11.8" + eth-lightwallet "^3.0.1" + ethereum-types "^1.0.11" + ethereumjs-tx "^1.3.5" + ethereumjs-util "^5.1.1" + ganache-core "0xProject/ganache-core#monorepo-dep" + hdkey "^0.7.1" + json-rpc-error "2.0.0" + lodash "^4.17.5" + semaphore-async-await "^1.5.1" + web3-provider-engine "14.0.6" + optionalDependencies: + "@ledgerhq/hw-transport-node-hid" "^4.3.0" "@0xproject/tslint-config@^1.0.7": version "1.0.8" @@ -650,58 +612,80 @@ tslint-react "^3.2.0" tsutils "3.0.0" -"@0xproject/types@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@0xproject/types/-/types-0.5.0.tgz#ba3cfbc11a8c6344b57c9680aa7df2ea84b9bf05" +"@0xproject/types@^1.1.0", "@0xproject/types@^1.1.1", "@0xproject/types@^1.1.4": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@0xproject/types/-/types-1.1.4.tgz#3ffd65e670d6a21dab19ee0ffd5fad0056291b8e" dependencies: + "@types/node" "*" bignumber.js "~4.1.0" + ethereum-types "^1.0.11" -"@0xproject/types@^0.8.2": - version "0.8.2" - resolved "https://registry.yarnpkg.com/@0xproject/types/-/types-0.8.2.tgz#6f936b73bfb6f017b5102002d97da0881da92d1b" +"@0xproject/typescript-typings@^2.0.1", "@0xproject/typescript-typings@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@0xproject/typescript-typings/-/typescript-typings-2.0.2.tgz#1812f64e341f1d24c09b8b5a951cbde0e5fff9c2" dependencies: - "@types/node" "9.6.0" + "@types/bn.js" "^4.11.0" + "@types/react" "*" bignumber.js "~4.1.0" + ethereum-types "^1.0.8" + popper.js "1.14.3" -"@0xproject/typescript-typings@^0.0.3": - version "0.0.3" - resolved "https://registry.yarnpkg.com/@0xproject/typescript-typings/-/typescript-typings-0.0.3.tgz#3272080bde00ade0a970b0d236686b483b08a1d0" +"@0xproject/typescript-typings@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@0xproject/typescript-typings/-/typescript-typings-3.0.2.tgz#797416af57618304cffb7a0053cf1822725b8dcd" dependencies: - "@0xproject/types" "^0.5.0" + "@types/bn.js" "^4.11.0" + "@types/react" "*" bignumber.js "~4.1.0" + ethereum-types "^1.0.11" + popper.js "1.14.3" -"@0xproject/typescript-typings@^0.4.3": - version "0.4.3" - resolved "https://registry.yarnpkg.com/@0xproject/typescript-typings/-/typescript-typings-0.4.3.tgz#f99f939a43f2764ad7182fcd78a71212a1d76d96" +"@0xproject/utils@^1.0.10", "@0xproject/utils@^1.0.11", "@0xproject/utils@^1.0.8": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@0xproject/utils/-/utils-1.0.11.tgz#5b53e7d9d4dbe68e219049218c9db04e97c37429" dependencies: - "@0xproject/types" "^0.8.2" + "@0xproject/types" "^1.1.1" + "@0xproject/typescript-typings" "^2.0.2" + "@types/node" "*" + abortcontroller-polyfill "^1.1.9" bignumber.js "~4.1.0" - ethereum-types "^0.0.2" + detect-node "2.0.3" + ethereum-types "^1.0.8" + ethereumjs-util "^5.1.1" + ethers "3.0.22" + isomorphic-fetch "^2.2.1" + js-sha3 "^0.7.0" + lodash "^4.17.5" -"@0xproject/utils@^0.7.3": - version "0.7.3" - resolved "https://registry.yarnpkg.com/@0xproject/utils/-/utils-0.7.3.tgz#ffa7c6da9bf0dd3e13694f185dcfc48a8981ff05" +"@0xproject/utils@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@0xproject/utils/-/utils-2.0.2.tgz#5a0cba11ea8378378a600ed7b25c82834038c932" dependencies: - "@0xproject/typescript-typings" "^0.4.3" - "@types/node" "9.6.0" + "@0xproject/types" "^1.1.4" + "@0xproject/typescript-typings" "^3.0.2" + "@types/node" "*" + abortcontroller-polyfill "^1.1.9" bignumber.js "~4.1.0" - ethereum-types "^0.0.2" + detect-node "2.0.3" + ethereum-types "^1.0.11" ethereumjs-util "^5.1.1" - ethers "3.0.22" - js-sha3 "0.7.0" - lodash "4.17.10" - web3 "0.20.6" + ethers "4.0.0-beta.14" + isomorphic-fetch "^2.2.1" + js-sha3 "^0.7.0" + lodash "^4.17.5" -"@0xproject/web3-wrapper@^0.7.3": - version "0.7.3" - resolved "https://registry.yarnpkg.com/@0xproject/web3-wrapper/-/web3-wrapper-0.7.3.tgz#9bd50b034b92fd505b6766b6e225f014b6d08b08" +"@0xproject/web3-wrapper@^3.0.0", "@0xproject/web3-wrapper@^3.0.1", "@0xproject/web3-wrapper@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@0xproject/web3-wrapper/-/web3-wrapper-3.0.3.tgz#2faaf3fa75308480efbbbb75416a1e416596d989" dependencies: - "@0xproject/types" "^0.8.2" - "@0xproject/typescript-typings" "^0.4.3" - "@0xproject/utils" "^0.7.3" - ethers "3.0.22" - lodash "4.17.10" - web3 "0.20.6" + "@0xproject/assert" "^1.0.13" + "@0xproject/json-schemas" "^1.0.7" + "@0xproject/typescript-typings" "^3.0.2" + "@0xproject/utils" "^2.0.2" + ethereum-types "^1.0.11" + ethereumjs-util "^5.1.1" + ethers "4.0.0-beta.14" + lodash "^4.17.5" "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.0.0-beta.35": version "7.0.0" @@ -773,6 +757,13 @@ "@ledgerhq/hw-transport" "^4.24.0" u2f-api "0.2.7" +"@ledgerhq/hw-transport-u2f@^4.3.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-u2f/-/hw-transport-u2f-4.31.0.tgz#b3c97e7f8e42a3b43372853f33a713cb5c49efdc" + dependencies: + "@ledgerhq/hw-transport" "^4.24.0" + u2f-api "0.2.7" + "@ledgerhq/hw-transport@^4.24.0": version "4.24.0" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-4.24.0.tgz#8def925d8c2e1f73d15128d9e27ead729870be58" @@ -1427,18 +1418,10 @@ "@types/express" "*" "@types/node" "*" -"@types/caseless@*": - version "0.12.1" - resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.1.tgz#9794c69c8385d0192acc471a540d1f8e0d16218a" - "@types/cheerio@*": version "0.22.9" resolved "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.9.tgz#b5990152604c2ada749b7f88cab3476f21f39d7b" -"@types/command-line-args@^4.0.2": - version "4.0.3" - resolved "https://registry.yarnpkg.com/@types/command-line-args/-/command-line-args-4.0.3.tgz#6f09c9873beb00013496941f4d73b98410bc1802" - "@types/compare-versions@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/compare-versions/-/compare-versions-3.0.0.tgz#4a45dffe0ebbc00d0f2daef8a0e96ffc66cf5955" @@ -1455,12 +1438,6 @@ version "2.0.0" resolved "https://registry.npmjs.org/@types/detect-node/-/detect-node-2.0.0.tgz#696e024ddd105c72bbc6a2e3f97902a2886f2c3f" -"@types/dotenv@^4.0.2": - version "4.0.3" - resolved "https://registry.yarnpkg.com/@types/dotenv/-/dotenv-4.0.3.tgz#ebcfc40da7bc0728b705945b7db48485ec5b4b67" - dependencies: - "@types/node" "*" - "@types/enzyme-adapter-react-16@^1.0.3": version "1.0.3" resolved "https://registry.npmjs.org/@types/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.0.3.tgz#0cf7025b036694ca8d596fe38f24162e7117acf1" @@ -1525,12 +1502,6 @@ version "2.0.0" resolved "https://registry.yarnpkg.com/@types/find-versions/-/find-versions-2.0.0.tgz#a976ca032ab8dd1161116604eded0620d2b85df2" -"@types/form-data@*": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@types/form-data/-/form-data-2.2.1.tgz#ee2b3b8eaa11c0938289953606b745b738c54b1e" - dependencies: - "@types/node" "*" - "@types/fs-extra@^5.0.3": version "5.0.4" resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-5.0.4.tgz#b971134d162cc0497d221adde3dbb67502225599" @@ -1674,10 +1645,6 @@ version "9.6.5" resolved "https://registry.yarnpkg.com/@types/node/-/node-9.6.5.tgz#ee700810fdf49ac1c399fc5980b7559b3e5a381d" -"@types/node@9.6.0": - version "9.6.0" - resolved "http://registry.npmjs.org/@types/node/-/node-9.6.0.tgz#d3480ee666df9784b1001a1872a2f6ccefb6c2d7" - "@types/node@^10.3.2": version "10.9.4" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.9.4.tgz#0f4cb2dc7c1de6096055357f70179043c33e9897" @@ -1686,10 +1653,6 @@ version "10.5.7" resolved "https://registry.npmjs.org/@types/node/-/node-10.5.7.tgz#960d9feb3ade2233bcc9843c918d740b4f78a7cf" -"@types/node@^8.0.53": - version "8.10.36" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.36.tgz#eac05d576fbcd0b4ea3c912dc58c20475c08d9e4" - "@types/numeral@^0.0.22": version "0.0.22" resolved "https://registry.yarnpkg.com/@types/numeral/-/numeral-0.0.22.tgz#86bef1f0a2d743afdc2ef3168d45f2905e1a0b93" @@ -1700,20 +1663,6 @@ dependencies: "@types/node" "*" -"@types/pg-types@*": - version "1.11.4" - resolved "https://registry.yarnpkg.com/@types/pg-types/-/pg-types-1.11.4.tgz#8d7c59fb509ce3dca3f8bae589252051c639a9a8" - dependencies: - moment ">=2.14.0" - -"@types/pg@^7.4.1": - version "7.4.10" - resolved "https://registry.yarnpkg.com/@types/pg/-/pg-7.4.10.tgz#1cf4d6c5d32e621566eca82981161b371fb90ace" - dependencies: - "@types/events" "*" - "@types/node" "*" - "@types/pg-types" "*" - "@types/prop-types@*": version "15.5.5" resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.5.5.tgz#17038dd322c2325f5da650a94d5f9974943625e3" @@ -1724,6 +1673,10 @@ version "5.1.0" resolved "https://registry.yarnpkg.com/@types/query-string/-/query-string-5.1.0.tgz#7f40cdea49ddafa0ea4f3db35fb6c24d3bfd4dcc" +"@types/ramda@^0.25.38": + version "0.25.38" + resolved "https://registry.yarnpkg.com/@types/ramda/-/ramda-0.25.38.tgz#6c945fcc91a5fa470cc3c5a4cd4a62d7f8d03576" + "@types/react-addons-linked-state-mixin@*": version "0.14.19" resolved "https://registry.yarnpkg.com/@types/react-addons-linked-state-mixin/-/react-addons-linked-state-mixin-0.14.19.tgz#7ef00a5618a089da4a99e1f58c9aa4c1781d46d5" @@ -1828,15 +1781,6 @@ dependencies: redux "*" -"@types/request@^2.0.13": - version "2.47.1" - resolved "https://registry.yarnpkg.com/@types/request/-/request-2.47.1.tgz#25410d3afbdac04c91a94ad9efc9824100735824" - dependencies: - "@types/caseless" "*" - "@types/form-data" "*" - "@types/node" "*" - "@types/tough-cookie" "*" - "@types/require-from-string@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@types/require-from-string/-/require-from-string-1.2.0.tgz#c18cfc8a2c1a0259e5841d1fef2b5e9d01c64242" @@ -1896,10 +1840,6 @@ version "0.0.33" resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.0.33.tgz#1073c4bc824754ae3d10cfab88ab0237ba964e4d" -"@types/tough-cookie@*": - version "2.3.3" - resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-2.3.3.tgz#7f226d67d654ec9070e755f46daebf014628e9d9" - "@types/uuid@^3.4.2", "@types/uuid@^3.4.3": version "3.4.3" resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-3.4.3.tgz#121ace265f5569ce40f4f6d0ff78a338c732a754" @@ -1923,7 +1863,7 @@ "@types/events" "*" "@types/node" "*" -"@types/yargs@10.0.2", "@types/yargs@^10.0.0": +"@types/yargs@^10.0.0": version "10.0.2" resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-10.0.2.tgz#cc4ea921877874d1261e2c44b89807bc836e1b12" @@ -2180,10 +2120,6 @@ aes-js@^0.2.3: version "0.2.4" resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-0.2.4.tgz#94b881ab717286d015fa219e08fb66709dda5a3d" -aes-js@^3.1.1: - version "3.1.2" - resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.1.2.tgz#db9aabde85d5caabbfc0d4f2a4446960f627146a" - agent-base@4, agent-base@^4.1.0, agent-base@~4.2.0: version "4.2.1" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" @@ -2196,15 +2132,6 @@ agentkeepalive@^3.4.1: dependencies: humanize-ms "^1.2.1" -airtable@^0.5.2: - version "0.5.6" - resolved "https://registry.yarnpkg.com/airtable/-/airtable-0.5.6.tgz#7f1a3cc038f77c9734c89deacdad500e25ad6fb1" - dependencies: - async "1.5.2" - lodash "4.17.10" - request "2.85.0" - xhr "2.3.3" - ajv-errors@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.0.tgz#ecf021fa108fd17dfb5e6b383f2dd233e31ffc59" @@ -2406,18 +2333,6 @@ arr-union@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" -array-back@^1.0.3, array-back@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/array-back/-/array-back-1.0.4.tgz#644ba7f095f7ffcf7c43b5f0dc39d3c1f03c063b" - dependencies: - typical "^2.6.0" - -array-back@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/array-back/-/array-back-2.0.0.tgz#6877471d51ecc9c9bfa6136fb6c7d5fe69748022" - dependencies: - typical "^2.6.1" - array-differ@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" @@ -2560,7 +2475,7 @@ async-limiter@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" -async@1.5.2, async@1.x, async@^1.4.0, async@^1.4.2, async@^1.5.2: +async@1.x, async@^1.4.0, async@^1.4.2, async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" @@ -3352,7 +3267,7 @@ bindings@^1.2.1, bindings@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.3.0.tgz#b346f6ecf6a95f5a815c5839fc7cdb22502f1ed7" -bintrees@1.0.2, bintrees@^1.0.2: +bintrees@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/bintrees/-/bintrees-1.0.2.tgz#49f896d6e858a4a499df85c38fb399b9aff840f8" @@ -3655,7 +3570,7 @@ bs-logger@0.x: dependencies: fast-json-stable-stringify "^2.0.0" -bs58@=4.0.1, bs58@^4.0.0: +bs58@=4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" dependencies: @@ -3678,14 +3593,6 @@ bs58check@^1.0.8: bs58 "^3.1.0" create-hash "^1.1.0" -bs58check@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" - dependencies: - bs58 "^4.0.0" - create-hash "^1.1.0" - safe-buffer "^5.1.2" - bser@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719" @@ -3724,10 +3631,6 @@ buffer-to-arraybuffer@^0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz#6064a40fa76eb43c723aba9ef8f6e1216d10511a" -buffer-writer@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-1.0.1.tgz#22a936901e3029afcd7547eb4487ceb697a3bf08" - buffer-xor@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" @@ -4006,14 +3909,6 @@ chain-function@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/chain-function/-/chain-function-1.0.0.tgz#0d4ab37e7e18ead0bdc47b920764118ce58733dc" -chalk@2.3.2: - version "2.3.2" - resolved "http://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65" - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -4403,14 +4298,6 @@ combined-stream@1.0.6, combined-stream@^1.0.5, combined-stream@~1.0.5, combined- dependencies: delayed-stream "~1.0.0" -command-line-args@^4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-4.0.7.tgz#f8d1916ecb90e9e121eda6428e41300bfb64cc46" - dependencies: - array-back "^2.0.0" - find-replace "^1.0.3" - typical "^2.6.1" - commander@2.11.0: version "2.11.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" @@ -4419,7 +4306,7 @@ commander@2.15.1, commander@^2.12.1, commander@^2.8.1: version "2.15.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" -commander@2.18.0, commander@^2.9.0: +commander@2.18.0: version "2.18.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970" @@ -4724,10 +4611,6 @@ cookie@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" -cookiejar@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c" - copy-concurrently@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" @@ -5118,13 +5001,6 @@ date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" -dateformat@^1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" - dependencies: - get-stdin "^4.0.1" - meow "^3.3.0" - dateformat@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-2.2.0.tgz#4065e2013cf9fb916ddfd82efb506ad4c6769062" @@ -6182,13 +6058,6 @@ ethereum-common@^0.0.18: version "0.0.18" resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.0.18.tgz#2fdc3576f232903358976eb39da783213ff9523f" -ethereum-types@^0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/ethereum-types/-/ethereum-types-0.0.2.tgz#6ef6faf46a24697cbf66b6c8a0ecf2095ce58c38" - dependencies: - "@types/node" "^8.0.53" - bignumber.js "~4.1.0" - ethereumjs-abi@0.6.5, ethereumjs-abi@^0.6.5: version "0.6.5" resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.5.tgz#5a637ef16ab43473fa72a29ad90871405b3f5241" @@ -6260,7 +6129,17 @@ ethereumjs-tx@^1.0.0, ethereumjs-tx@^1.1.1, ethereumjs-tx@^1.2.0, ethereumjs-tx@ ethereum-common "^0.0.18" ethereumjs-util "^5.0.0" -ethereumjs-util@5.1.5, ethereumjs-util@^5.0.0, ethereumjs-util@^5.0.1, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.2, ethereumjs-util@^5.1.3, ethereumjs-util@^5.1.5: +ethereumjs-util@^4.0.1, ethereumjs-util@^4.3.0, ethereumjs-util@^4.4.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-4.5.0.tgz#3e9428b317eebda3d7260d854fddda954b1f1bc6" + dependencies: + bn.js "^4.8.0" + create-hash "^1.1.2" + keccakjs "^0.2.0" + rlp "^2.0.0" + secp256k1 "^3.0.1" + +ethereumjs-util@^5.0.0, ethereumjs-util@^5.0.1, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.2, ethereumjs-util@^5.1.3, ethereumjs-util@^5.1.5: version "5.1.5" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-5.1.5.tgz#2f02575852627d45622426f25ee4a0b5f377f27a" dependencies: @@ -6272,16 +6151,6 @@ ethereumjs-util@5.1.5, ethereumjs-util@^5.0.0, ethereumjs-util@^5.0.1, ethereumj safe-buffer "^5.1.1" secp256k1 "^3.0.1" -ethereumjs-util@^4.0.1, ethereumjs-util@^4.3.0, ethereumjs-util@^4.4.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-4.5.0.tgz#3e9428b317eebda3d7260d854fddda954b1f1bc6" - dependencies: - bn.js "^4.8.0" - create-hash "^1.1.2" - keccakjs "^0.2.0" - rlp "^2.0.0" - secp256k1 "^3.0.1" - ethereumjs-util@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz#3e0c0d1741471acf1036052d048623dee54ad642" @@ -6338,19 +6207,6 @@ ethereumjs-wallet@0.6.0: utf8 "^2.1.1" uuid "^2.0.1" -ethereumjs-wallet@~0.6.0: - version "0.6.2" - resolved "https://registry.yarnpkg.com/ethereumjs-wallet/-/ethereumjs-wallet-0.6.2.tgz#67244b6af3e8113b53d709124b25477b64aeccda" - dependencies: - aes-js "^3.1.1" - bs58check "^2.1.2" - ethereumjs-util "^5.2.0" - hdkey "^1.0.0" - safe-buffer "^5.1.2" - scrypt.js "^0.2.0" - utf8 "^3.0.0" - uuid "^3.3.2" - ethers@3.0.22: version "3.0.22" resolved "https://registry.yarnpkg.com/ethers/-/ethers-3.0.22.tgz#7fab1ea16521705837aa43c15831877b2716b436" @@ -6366,6 +6222,21 @@ ethers@3.0.22: uuid "2.0.1" xmlhttprequest "1.8.0" +ethers@4.0.0-beta.14: + version "4.0.0-beta.14" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.0-beta.14.tgz#76aa9257b9c93a7604ff4dc11f2a445d07f6459d" + dependencies: + "@types/node" "^10.3.2" + aes-js "3.0.0" + bn.js "^4.4.0" + elliptic "6.3.3" + hash.js "1.1.3" + js-sha3 "0.5.7" + scrypt-js "2.0.3" + setimmediate "1.0.4" + uuid "2.0.1" + xmlhttprequest "1.8.0" + ethers@~4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.4.tgz#d3f85e8b27f4b59537e06526439b0fb15b44dc65" @@ -6885,13 +6756,6 @@ find-index@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/find-index/-/find-index-0.1.1.tgz#675d358b2ca3892d795a1ab47232f8b6e2e0dde4" -find-replace@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/find-replace/-/find-replace-1.0.3.tgz#b88e7364d2d9c959559f388c66670d6130441fa0" - dependencies: - array-back "^1.0.4" - test-value "^2.1.0" - find-up@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" @@ -7286,10 +7150,6 @@ gaze@^0.5.1: dependencies: globule "~0.1.0" -generic-pool@2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-2.4.3.tgz#780c36f69dfad05a5a045dd37be7adca11a4f6ff" - generic-pool@~2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-2.0.4.tgz#f9718deda82fa125ed5c43e341c9a215a766d9a3" @@ -7914,14 +7774,6 @@ hdkey@^0.7.0, hdkey@^0.7.1: coinstring "^2.0.0" secp256k1 "^3.0.1" -hdkey@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/hdkey/-/hdkey-1.1.0.tgz#e74e7b01d2c47f797fa65d1d839adb7a44639f29" - dependencies: - coinstring "^2.0.0" - safe-buffer "^5.1.1" - secp256k1 "^3.0.1" - he@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" @@ -9306,17 +9158,13 @@ js-sha3@0.5.7: version "0.5.7" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" -js-sha3@0.7.0, js-sha3@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.7.0.tgz#0a5c57b36f79882573b2d84051f8bb85dd1bd63a" - js-sha3@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.3.1.tgz#86122802142f0828502a0d1dee1d95e253bb0243" -js-string-escape@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" +js-sha3@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.7.0.tgz#0a5c57b36f79882573b2d84051f8bb85dd1bd63a" js-tokens@^3.0.0, js-tokens@^3.0.2: version "3.0.2" @@ -9501,10 +9349,6 @@ jsonschema@*, jsonschema@1.2.4, jsonschema@^1.2.0: version "1.2.4" resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.2.4.tgz#a46bac5d3506a254465bc548876e267c6d0d6464" -jsonschema@1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.2.2.tgz#83ab9c63d65bf4d596f91d81195e78772f6452bc" - jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -10195,7 +10039,7 @@ lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" -lodash.values@4.3.0, lodash.values@^4.3.0: +lodash.values@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.values/-/lodash.values-4.3.0.tgz#a3a6c2b0ebecc5c2cba1c17e6e620fe81b53d347" @@ -10205,10 +10049,6 @@ lodash.words@^3.0.0: dependencies: lodash._root "^3.0.0" -lodash@4.17.10, lodash@^4.17.10: - version "4.17.10" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" - lodash@=4.17.4: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -10225,6 +10065,10 @@ lodash@^4.14.0, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1, lodash@^4.3.0, lo version "4.17.5" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" +lodash@^4.17.10: + version "4.17.10" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" + lodash@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/lodash/-/lodash-1.0.2.tgz#8f57560c83b59fc270bd3d561b690043430e2551" @@ -10641,12 +10485,6 @@ micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8, mic snapdragon "^0.8.1" to-regex "^3.0.2" -migrate@^0.2.2: - version "0.2.4" - resolved "https://registry.yarnpkg.com/migrate/-/migrate-0.2.4.tgz#30a54dac5c269b7321cec56b2aff4cccaea6418d" - dependencies: - dateformat "^1.0.12" - miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" @@ -10866,7 +10704,7 @@ moment@2.21.0: version "2.21.0" resolved "https://registry.yarnpkg.com/moment/-/moment-2.21.0.tgz#2a114b51d2a6ec9e6d83cf803f838a878d8a023a" -moment@>=2.14.0, moment@^2.6.0: +moment@^2.6.0: version "2.22.2" resolved "http://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66" @@ -11156,16 +10994,6 @@ node-pre-gyp@^0.6.39: tar "^2.2.1" tar-pack "^3.4.0" -node-redshift@^0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/node-redshift/-/node-redshift-0.1.5.tgz#c6c7cff16d230a148094225f20072939b66582f0" - dependencies: - bluebird "^3.5.0" - commander "^2.9.0" - migrate "^0.2.2" - pg "^6.1.2" - sql-bricks "^1.2.3" - nodemon@^1.11.0: version "1.17.3" resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.17.3.tgz#3b0bbc2ee05ccb43b1aef15ba05c63c7bc9b8530" @@ -11446,10 +11274,6 @@ oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" -object-assign@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" - object-assign@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" @@ -11823,10 +11647,6 @@ package-json@^4.0.0, package-json@^4.0.1: registry-url "^3.0.3" semver "^5.1.0" -packet-reader@0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/packet-reader/-/packet-reader-0.3.1.tgz#cd62e60af8d7fea8a705ec4ff990871c46871f27" - pacote@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/pacote/-/pacote-9.1.0.tgz#59810859bbd72984dcb267269259375d32f391e5" @@ -12088,75 +11908,6 @@ performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" -pg-connection-string@0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-0.1.3.tgz#da1847b20940e42ee1492beaf65d49d91b245df7" - -pg-int8@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" - -pg-pool@1.*: - version "1.8.0" - resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-1.8.0.tgz#f7ec73824c37a03f076f51bfdf70e340147c4f37" - dependencies: - generic-pool "2.4.3" - object-assign "4.1.0" - -pg-pool@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-2.0.3.tgz#c022032c8949f312a4f91fb6409ce04076be3257" - -pg-types@1.*: - version "1.13.0" - resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-1.13.0.tgz#75f490b8a8abf75f1386ef5ec4455ecf6b345c63" - dependencies: - pg-int8 "1.0.1" - postgres-array "~1.0.0" - postgres-bytea "~1.0.0" - postgres-date "~1.0.0" - postgres-interval "^1.1.0" - -pg-types@~1.12.1: - version "1.12.1" - resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-1.12.1.tgz#d64087e3903b58ffaad279e7595c52208a14c3d2" - dependencies: - postgres-array "~1.0.0" - postgres-bytea "~1.0.0" - postgres-date "~1.0.0" - postgres-interval "^1.1.0" - -pg@^6.1.2: - version "6.4.2" - resolved "https://registry.yarnpkg.com/pg/-/pg-6.4.2.tgz#c364011060eac7a507a2ae063eb857ece910e27f" - dependencies: - buffer-writer "1.0.1" - js-string-escape "1.0.1" - packet-reader "0.3.1" - pg-connection-string "0.1.3" - pg-pool "1.*" - pg-types "1.*" - pgpass "1.*" - semver "4.3.2" - -pg@^7.4.1: - version "7.4.3" - resolved "https://registry.yarnpkg.com/pg/-/pg-7.4.3.tgz#f7b6f93f5340ecc2596afbb94a13e3d6b609834b" - dependencies: - buffer-writer "1.0.1" - packet-reader "0.3.1" - pg-connection-string "0.1.3" - pg-pool "~2.0.3" - pg-types "~1.12.1" - pgpass "1.x" - semver "4.3.2" - -pgpass@1.*, pgpass@1.x: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pgpass/-/pgpass-1.0.2.tgz#2a7bb41b6065b67907e91da1b07c1847c877b306" - dependencies: - split "^1.0.0" - pify@^2.0.0, pify@^2.2.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -12485,24 +12236,6 @@ postcss@^6.0.1: source-map "^0.6.1" supports-color "^5.3.0" -postgres-array@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-1.0.2.tgz#8e0b32eb03bf77a5c0a7851e0441c169a256a238" - -postgres-bytea@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35" - -postgres-date@~1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.3.tgz#e2d89702efdb258ff9d9cee0fe91bd06975257a8" - -postgres-interval@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-1.1.2.tgz#bf71ff902635f21cb241a013fc421d81d1db15a9" - dependencies: - xtend "^4.0.0" - prebuild-install@^2.2.2: version "2.5.3" resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-2.5.3.tgz#9f65f242782d370296353710e9bc843490c19f69" @@ -12860,7 +12593,7 @@ qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" -query-string@4.3.4, query-string@^4.1.0: +query-string@^4.1.0: version "4.3.4" resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" dependencies: @@ -12898,12 +12631,6 @@ querystringify@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-1.0.0.tgz#6286242112c5b712fa654e526652bf6a13ff05cb" -queue@^4.4.2: - version "4.5.0" - resolved "https://registry.yarnpkg.com/queue/-/queue-4.5.0.tgz#0f125191a983e3c38fcc0c0c75087d358d0857f4" - dependencies: - inherits "~2.0.0" - quick-lru@^1.0.0: version "1.1.0" resolved "http://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" @@ -12922,6 +12649,10 @@ ramda@0.21.0: version "0.21.0" resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.21.0.tgz#a001abedb3ff61077d4ff1d577d44de77e8d0a35" +ramda@^0.25.0: + version "0.25.0" + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.25.0.tgz#8fdf68231cffa90bc2f9460390a0cb74a29b29a9" + randexp@0.4.6: version "0.4.6" resolved "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3" @@ -13667,7 +13398,32 @@ request@2.81.0, "request@>=2.9.0 <2.82.0": tunnel-agent "^0.6.0" uuid "^3.0.0" -request@2.85.0, request@^2.54.0, request@^2.67.0, request@^2.79.0: +request@^2.47.0, request@^2.87.0: + version "2.88.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.0" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.4.3" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +request@^2.54.0, request@^2.67.0, request@^2.79.0: version "2.85.0" resolved "https://registry.yarnpkg.com/request/-/request-2.85.0.tgz#5a03615a47c61420b3eb99b7dba204f83603e1fa" dependencies: @@ -13694,31 +13450,6 @@ request@2.85.0, request@^2.54.0, request@^2.67.0, request@^2.79.0: tunnel-agent "^0.6.0" uuid "^3.1.0" -request@^2.47.0, request@^2.83.0, request@^2.87.0: - version "2.88.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.0" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.4.3" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - request@^2.85.0: version "2.87.0" resolved "https://registry.npmjs.org/request/-/request-2.87.0.tgz#32f00235cd08d482b4d0d68db93a829c0ed5756e" @@ -13748,10 +13479,6 @@ require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" -require-from-string@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.1.tgz#c545233e9d7da6616e9d59adfb39fc9f588676ff" - require-from-string@^1.1.0: version "1.2.1" resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418" @@ -14201,10 +13928,6 @@ semver-sort@0.0.4: version "5.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" -semver@4.3.2: - version "4.3.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.2.tgz#c7a07158a80bedd052355b770d82d6640f803be7" - semver@^4.1.0: version "4.3.6" resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" @@ -14552,9 +14275,9 @@ socks@~2.2.0: ip "^1.1.5" smart-buffer "^4.0.1" -solc@0.4.23, solc@^0.4.23: - version "0.4.23" - resolved "https://registry.yarnpkg.com/solc/-/solc-0.4.23.tgz#54a0ff4015827b32fddb62c0a418b5247310a58e" +solc@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.4.24.tgz#354f14b269b38cbaa82a47d1ff151723502b954e" dependencies: fs-extra "^0.30.0" memorystream "^0.3.1" @@ -14562,9 +14285,9 @@ solc@0.4.23, solc@^0.4.23: semver "^5.3.0" yargs "^4.7.1" -solc@0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/solc/-/solc-0.4.24.tgz#354f14b269b38cbaa82a47d1ff151723502b954e" +solc@^0.4.2: + version "0.4.21" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.4.21.tgz#6a7ecd505bfa0fc268330d5de6b9ae65c8c68264" dependencies: fs-extra "^0.30.0" memorystream "^0.3.1" @@ -14572,9 +14295,9 @@ solc@0.4.24: semver "^5.3.0" yargs "^4.7.1" -solc@^0.4.2: - version "0.4.21" - resolved "https://registry.yarnpkg.com/solc/-/solc-0.4.21.tgz#6a7ecd505bfa0fc268330d5de6b9ae65c8c68264" +solc@^0.4.23: + version "0.4.23" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.4.23.tgz#54a0ff4015827b32fddb62c0a418b5247310a58e" dependencies: fs-extra "^0.30.0" memorystream "^0.3.1" @@ -14805,12 +14528,6 @@ sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" -sql-bricks@^1.2.3: - version "1.5.1" - resolved "https://registry.yarnpkg.com/sql-bricks/-/sql-bricks-1.5.1.tgz#68dc6d77b21a696f4885184a031b861ea5575981" - dependencies: - underscore "1.4.x" - sshpk@^1.7.0: version "1.14.1" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.1.tgz#130f5975eddad963f1d56f92b9ac6c51fa9f83eb" @@ -15431,13 +15148,6 @@ test-exclude@^4.2.1: read-pkg-up "^1.0.1" require-main-filename "^1.0.1" -test-value@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/test-value/-/test-value-2.1.0.tgz#11da6ff670f3471a73b625ca4f3fdcf7bb748291" - dependencies: - array-back "^1.0.3" - typical "^2.6.0" - text-encoding@^0.6.4: version "0.6.4" resolved "https://registry.yarnpkg.com/text-encoding/-/text-encoding-0.6.4.tgz#e399a982257a276dae428bb92845cb71bdc26d19" @@ -15942,10 +15652,6 @@ typewiselite@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/typewiselite/-/typewiselite-1.0.0.tgz#c8882fa1bb1092c06005a97f34ef5c8508e3664e" -typical@^2.6.0, typical@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/typical/-/typical-2.6.1.tgz#5c080e5d661cbbe38259d2e70a3c7253e873881d" - u2f-api@0.2.7: version "0.2.7" resolved "https://registry.yarnpkg.com/u2f-api/-/u2f-api-0.2.7.tgz#17bf196b242f6bf72353d9858e6a7566cc192720" @@ -16036,14 +15742,14 @@ undefsafe@^2.0.2: dependencies: debug "^2.2.0" -underscore@1.4.x, underscore@~1.4.4: - version "1.4.4" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.4.4.tgz#61a6a32010622afa07963bf325203cf12239d604" - underscore@1.8.3: version "1.8.3" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" +underscore@~1.4.4: + version "1.4.4" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.4.4.tgz#61a6a32010622afa07963bf325203cf12239d604" + unherit@^1.0.4: version "1.1.0" resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.0.tgz#6b9aaedfbf73df1756ad9e316dd981885840cd7d" @@ -16263,10 +15969,6 @@ utf8@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/utf8/-/utf8-2.1.2.tgz#1fa0d9270e9be850d9b05027f63519bf46457d96" -utf8@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" - util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -16333,7 +16035,7 @@ v8flags@^2.0.2: dependencies: user-home "^1.1.1" -valid-url@1.0.9, valid-url@^1.0.9: +valid-url@^1.0.9: version "1.0.9" resolved "https://registry.yarnpkg.com/valid-url/-/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200" @@ -16539,14 +16241,6 @@ web3-bzz@1.0.0-beta.34: swarm-js "0.1.37" underscore "1.8.3" -web3-core-helpers@1.0.0-beta.33: - version "1.0.0-beta.33" - resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.0.0-beta.33.tgz#2af733e504db05e7c3648c1dacf577b0ec15dc43" - dependencies: - underscore "1.8.3" - web3-eth-iban "1.0.0-beta.33" - web3-utils "1.0.0-beta.33" - web3-core-helpers@1.0.0-beta.34: version "1.0.0-beta.34" resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.0.0-beta.34.tgz#b168da00d3e19e156bc15ae203203dd4dfee2d03" @@ -16599,15 +16293,6 @@ web3-core@1.0.0-beta.34: web3-core-requestmanager "1.0.0-beta.34" web3-utils "1.0.0-beta.34" -web3-eth-abi@1.0.0-beta.33: - version "1.0.0-beta.33" - resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.0.0-beta.33.tgz#2221f7151643660032a4df340f612349168c824a" - dependencies: - bn.js "4.11.6" - underscore "1.8.3" - web3-core-helpers "1.0.0-beta.33" - web3-utils "1.0.0-beta.33" - web3-eth-abi@1.0.0-beta.34, web3-eth-abi@^1.0.0-beta.24: version "1.0.0-beta.34" resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.0.0-beta.34.tgz#034533e3aa2f7e59ff31793eaea685c0ed5af67a" @@ -16645,13 +16330,6 @@ web3-eth-contract@1.0.0-beta.34: web3-eth-abi "1.0.0-beta.34" web3-utils "1.0.0-beta.34" -web3-eth-iban@1.0.0-beta.33: - version "1.0.0-beta.33" - resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.0.0-beta.33.tgz#1d73d0c5288a4565b1754a75b5fb3ea0b77a532f" - dependencies: - bn.js "4.11.6" - web3-utils "1.0.0-beta.33" - web3-eth-iban@1.0.0-beta.34: version "1.0.0-beta.34" resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.0.0-beta.34.tgz#9af458605867ccf74ea979aaf326b38ba6a5ba0c" @@ -16782,18 +16460,6 @@ web3-typescript-typings@^0.10.2: dependencies: bignumber.js "~4.1.0" -web3-utils@1.0.0-beta.33: - version "1.0.0-beta.33" - resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.0.0-beta.33.tgz#e091b7994f09b714b0198a4057d3ad2eb8cbe238" - dependencies: - bn.js "4.11.6" - eth-lib "0.1.27" - ethjs-unit "0.1.6" - number-to-bn "1.7.0" - randomhex "0.1.5" - underscore "1.8.3" - utf8 "2.1.1" - web3-utils@1.0.0-beta.34: version "1.0.0-beta.34" resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.0.0-beta.34.tgz#9411fc39aaef39ca4e06169f762297d9ff020970" @@ -16816,16 +16482,6 @@ web3@0.20.2: xhr2 "*" xmlhttprequest "*" -web3@0.20.6: - version "0.20.6" - resolved "http://registry.npmjs.org/web3/-/web3-0.20.6.tgz#3e97306ae024fb24e10a3d75c884302562215120" - dependencies: - bignumber.js "git+https://github.com/frozeman/bignumber.js-nolookahead.git" - crypto-js "^3.1.4" - utf8 "^2.1.1" - xhr2 "*" - xmlhttprequest "*" - web3@^0.18.0: version "0.18.4" resolved "https://registry.yarnpkg.com/web3/-/web3-0.18.4.tgz#81ec1784145491f2eaa8955b31c06049e07c5e7d" @@ -16836,16 +16492,6 @@ web3@^0.18.0: xhr2 "*" xmlhttprequest "*" -web3@^0.20.0: - version "0.20.7" - resolved "https://registry.yarnpkg.com/web3/-/web3-0.20.7.tgz#1605e6d81399ed6f85a471a4f3da0c8be57df2f7" - dependencies: - bignumber.js "git+https://github.com/frozeman/bignumber.js-nolookahead.git" - crypto-js "^3.1.4" - utf8 "^2.1.1" - xhr2-cookies "^1.1.0" - xmlhttprequest "*" - web3@^1.0.0-beta.34: version "1.0.0-beta.34" resolved "https://registry.yarnpkg.com/web3/-/web3-1.0.0-beta.34.tgz#347e561b784098cb5563315f490479a1d91f2ab1" @@ -16993,7 +16639,7 @@ websocket-extensions@>=0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" -websocket@1.0.25, websocket@^1.0.24, websocket@^1.0.25: +websocket@^1.0.24, websocket@^1.0.25: version "1.0.25" resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.25.tgz#998ec790f0a3eacb8b08b50a4350026692a11958" dependencies: @@ -17236,25 +16882,10 @@ xhr-request@^1.0.1: url-set-query "^1.0.0" xhr "^2.0.4" -xhr2-cookies@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz#7d77449d0999197f155cb73b23df72505ed89d48" - dependencies: - cookiejar "^2.1.1" - xhr2@*, xhr2@0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/xhr2/-/xhr2-0.1.4.tgz#7f87658847716db5026323812f818cadab387a5f" -xhr@2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.3.3.tgz#ad6b810e0917ce72b5ec704f5d41f1503b8e7524" - dependencies: - global "~4.3.0" - is-function "^1.0.1" - parse-headers "^2.0.0" - xtend "^4.0.0" - xhr@^2.0.4, xhr@^2.2.0, xhr@^2.3.3: version "2.4.1" resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.4.1.tgz#ba982cced205ae5eec387169ac9dc77ca4853d38" -- cgit v1.2.3 From 2b7f94c00f7fd38cfaa50540c6bef8237306c064 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Wed, 19 Sep 2018 11:25:46 -0700 Subject: Restructure pipeline package. Create data-sources dir --- .../pipeline/src/data-sources/etherscan/events.ts | 54 ++++++++++++++++ .../pipeline/src/data-sources/etherscan/index.ts | 34 ++++++++++ packages/pipeline/src/events.ts | 75 ---------------------- packages/pipeline/src/index.ts | 8 +++ 4 files changed, 96 insertions(+), 75 deletions(-) create mode 100644 packages/pipeline/src/data-sources/etherscan/events.ts create mode 100644 packages/pipeline/src/data-sources/etherscan/index.ts delete mode 100644 packages/pipeline/src/events.ts create mode 100644 packages/pipeline/src/index.ts diff --git a/packages/pipeline/src/data-sources/etherscan/events.ts b/packages/pipeline/src/data-sources/etherscan/events.ts new file mode 100644 index 000000000..50962a266 --- /dev/null +++ b/packages/pipeline/src/data-sources/etherscan/events.ts @@ -0,0 +1,54 @@ +import { AbiDecoder } from '@0xproject/utils'; +import { DecodedLogArgs, LogEntry, LogWithDecodedArgs } from 'ethereum-types'; +import * as R from 'ramda'; + +import { artifacts } from '../../artifacts'; + +// Raw events response from etherescan.io +export interface EventsResponse { + status: string; + message: string; + result: EventsResponseResult[]; +} + +// Events as represented in the response from etherscan.io +export interface EventsResponseResult { + address: string; + topics: string[]; + data: string; + blockNumber: string; + timeStamp: string; + gasPrice: string; + gasUsed: string; + logIndex: string; + transactionHash: string; + transactionIndex: string; +} + +function convertResponseToLogEntry(result: EventsResponseResult): LogEntry { + const radix = 10; + return { + logIndex: parseInt(result.logIndex, radix), + transactionIndex: parseInt(result.logIndex, radix), + transactionHash: result.transactionHash, + blockHash: '', + blockNumber: parseInt(result.blockNumber, radix), + address: result.address, + data: result.data, + topics: result.topics, + }; +} + +function tryToDecodeLogOrNoop(log: LogEntry): LogWithDecodedArgs { + const abiDecoder = new AbiDecoder([artifacts.Exchange.compilerOutput.abi]); + const logWithDecodedArgs = abiDecoder.tryToDecodeLogOrNoop(log); + // tslint:disable-next-line:no-unnecessary-type-assertion + return logWithDecodedArgs as LogWithDecodedArgs; +} + +/** + * Parses and abi-decodes the raw events response from etherscan.io. + * @param rawEventsResponse The raw events response from etherescan.io. + * @returns Parsed and decoded events. + */ +export const parseRawEventsResponse = R.pipe(R.map(convertResponseToLogEntry), R.map(tryToDecodeLogOrNoop)); diff --git a/packages/pipeline/src/data-sources/etherscan/index.ts b/packages/pipeline/src/data-sources/etherscan/index.ts new file mode 100644 index 000000000..0891d351a --- /dev/null +++ b/packages/pipeline/src/data-sources/etherscan/index.ts @@ -0,0 +1,34 @@ +import { default as axios } from 'axios'; +import { BlockParam, BlockParamLiteral, DecodedLogArgs, LogWithDecodedArgs } from 'ethereum-types'; + +import { EventsResponse, parseRawEventsResponse } from './events'; + +const ETHERSCAN_URL = 'https://api.etherscan.io/api'; + +export class Etherscan { + private readonly _apiKey: string; + constructor(apiKey: string) { + this._apiKey = apiKey; + } + + /** + * Gets the decoded events for a specific contract and block range. + * @param contractAddress The address of the contract to get the events for. + * @param fromBlock The start of the block range to get events for (inclusive). + * @param toBlock The end of the block range to get events for (inclusive). + * @returns A list of decoded events. + */ + public async getContractEventsAsync( + contractAddress: string, + fromBlock: BlockParam = BlockParamLiteral.Earliest, + toBlock: BlockParam = BlockParamLiteral.Latest, + ): Promise>> { + const fullURL = `${ETHERSCAN_URL}?module=logs&action=getLogs&address=${contractAddress}&fromBlock=${fromBlock}&toBlock=${toBlock}&apikey=${ + this._apiKey + }`; + const resp = await axios.get(fullURL); + // TODO(albrow): Check response code. + const decodedEvents = parseRawEventsResponse(resp.data.result); + return decodedEvents; + } +} diff --git a/packages/pipeline/src/events.ts b/packages/pipeline/src/events.ts deleted file mode 100644 index a6e1e4ad3..000000000 --- a/packages/pipeline/src/events.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { AbiDecoder } from '@0xproject/utils'; -import { default as axios } from 'axios'; -import { BlockParam, BlockParamLiteral, DecodedLogArgs, LogEntry, LogWithDecodedArgs } from 'ethereum-types'; -import * as R from 'ramda'; - -import { artifacts } from './artifacts'; - -// const EXCHANGE_ADDRESS = '0x4f833a24e1f95d70f028921e27040ca56e09ab0b'; -const ETHERSCAN_URL = 'https://api.etherscan.io/api'; -// TOOD(albrow): Pass this in as a constructor argument instead of an env var. -const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY; - -// Raw response from etherescan.io -interface EventsResponse { - status: string; - message: string; - result: EventsResponseResult[]; -} - -// Events as represented in the response from etherscan.io -interface EventsResponseResult { - address: string; - topics: string[]; - data: string; - blockNumber: string; - timeStamp: string; - gasPrice: string; - gasUsed: string; - logIndex: string; - transactionHash: string; - transactionIndex: string; -} - -// Parses and abi-decodes the fill events response from etherscan.io. -const parseFillEventsResponse = R.pipe(R.map(convertResponseToLogEntry), R.map(tryToDecodeLogOrNoop)); - -function convertResponseToLogEntry(result: EventsResponseResult): LogEntry { - const radix = 10; - return { - logIndex: parseInt(result.logIndex, radix), - transactionIndex: parseInt(result.logIndex, radix), - transactionHash: result.transactionHash, - blockHash: '', - blockNumber: parseInt(result.blockNumber, radix), - address: result.address, - data: result.data, - topics: result.topics, - }; -} - -function tryToDecodeLogOrNoop(log: LogEntry): LogWithDecodedArgs { - const abiDecoder = new AbiDecoder([artifacts.Exchange.compilerOutput.abi]); - const logWithDecodedArgs = abiDecoder.tryToDecodeLogOrNoop(log); - // tslint:disable-next-line:no-unnecessary-type-assertion - return logWithDecodedArgs as LogWithDecodedArgs; -} - -/** - * Gets the decoded events for a specific contract and block range. - * @param contractAddress The address of the contract to get the events for. - * @param fromBlock The start of the block range to get events for (inclusive). - * @param toBlock The end of the block range to get events for (inclusive). - * @returns A list of decoded events. - */ -export async function getContractEventsAsync( - contractAddress: string, - fromBlock: BlockParam = BlockParamLiteral.Earliest, - toBlock: BlockParam = BlockParamLiteral.Latest, -): Promise>> { - const fullURL = `${ETHERSCAN_URL}?module=logs&action=getLogs&address=${contractAddress}&fromBlock=${fromBlock}&toBlock=${toBlock}&apikey=${ETHERSCAN_API_KEY}`; - const resp = await axios.get(fullURL); - // TODO(albrow): Check response code. - const decodedEvents = parseFillEventsResponse(resp.data.result); - return decodedEvents; -} diff --git a/packages/pipeline/src/index.ts b/packages/pipeline/src/index.ts new file mode 100644 index 000000000..c9254cc2a --- /dev/null +++ b/packages/pipeline/src/index.ts @@ -0,0 +1,8 @@ +import { Etherscan } from './data-sources/etherscan'; + +const etherscan = new Etherscan(process.env.ETHERSCAN_API_KEY as string); + +(async () => { + const events = await etherscan.getContractEventsAsync('0x4f833a24e1f95d70f028921e27040ca56e09ab0b'); + console.log(events); +})(); -- cgit v1.2.3 From 75d3f24835fc68a758cfb44c6bc05095c3e87ad3 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Wed, 19 Sep 2018 11:56:41 -0700 Subject: Add tests for etherscan events --- packages/pipeline/package.json | 7 +- .../pipeline/src/data-sources/etherscan/events.ts | 23 ++++-- packages/pipeline/src/index.ts | 3 +- .../test/data-sources/etherscan/events_test.ts | 88 ++++++++++++++++++++++ packages/pipeline/test/utils/chai_setup.ts | 13 ++++ yarn.lock | 2 +- 6 files changed, 125 insertions(+), 11 deletions(-) create mode 100644 packages/pipeline/test/data-sources/etherscan/events_test.ts create mode 100644 packages/pipeline/test/utils/chai_setup.ts diff --git a/packages/pipeline/package.json b/packages/pipeline/package.json index b4ed33d8d..cdcd5ed18 100644 --- a/packages/pipeline/package.json +++ b/packages/pipeline/package.json @@ -31,7 +31,12 @@ "devDependencies": { "@0xproject/tslint-config": "^1.0.7", "tslint": "5.11.0", - "typescript": "3.0.1" + "typescript": "3.0.1", + "mocha": "^5.2.0", + "chai": "^4.1.2", + "chai-as-promised": "^7.1.1", + "chai-bignumber": "^2.0.2", + "dirty-chai": "^2.0.1" }, "dependencies": { "@0xproject/contract-wrappers": "^1.0.1", diff --git a/packages/pipeline/src/data-sources/etherscan/events.ts b/packages/pipeline/src/data-sources/etherscan/events.ts index 50962a266..3083af267 100644 --- a/packages/pipeline/src/data-sources/etherscan/events.ts +++ b/packages/pipeline/src/data-sources/etherscan/events.ts @@ -25,21 +25,30 @@ export interface EventsResponseResult { transactionIndex: string; } -function convertResponseToLogEntry(result: EventsResponseResult): LogEntry { - const radix = 10; +const hexRadix = 16; + +function hexToInt(hex: string): number { + return parseInt(hex.replace('0x', ''), hexRadix); +} + +// Converts a raw event response to a LogEntry +// tslint:disable-next-line:completed-docs +export function _convertResponseToLogEntry(result: EventsResponseResult): LogEntry { return { - logIndex: parseInt(result.logIndex, radix), - transactionIndex: parseInt(result.logIndex, radix), + logIndex: hexToInt(result.logIndex), + transactionIndex: hexToInt(result.transactionIndex), transactionHash: result.transactionHash, blockHash: '', - blockNumber: parseInt(result.blockNumber, radix), + blockNumber: hexToInt(result.blockNumber), address: result.address, data: result.data, topics: result.topics, }; } -function tryToDecodeLogOrNoop(log: LogEntry): LogWithDecodedArgs { +// Decodes a LogEntry into a LogWithDecodedArgs +// tslint:disable-next-line:completed-docs +export function _decodeLogEntry(log: LogEntry): LogWithDecodedArgs { const abiDecoder = new AbiDecoder([artifacts.Exchange.compilerOutput.abi]); const logWithDecodedArgs = abiDecoder.tryToDecodeLogOrNoop(log); // tslint:disable-next-line:no-unnecessary-type-assertion @@ -51,4 +60,4 @@ function tryToDecodeLogOrNoop(log: LogEntry): LogWithDecodedArgs * @param rawEventsResponse The raw events response from etherescan.io. * @returns Parsed and decoded events. */ -export const parseRawEventsResponse = R.pipe(R.map(convertResponseToLogEntry), R.map(tryToDecodeLogOrNoop)); +export const parseRawEventsResponse = R.pipe(R.map(_convertResponseToLogEntry), R.map(_decodeLogEntry)); diff --git a/packages/pipeline/src/index.ts b/packages/pipeline/src/index.ts index c9254cc2a..baed6933e 100644 --- a/packages/pipeline/src/index.ts +++ b/packages/pipeline/src/index.ts @@ -3,6 +3,5 @@ import { Etherscan } from './data-sources/etherscan'; const etherscan = new Etherscan(process.env.ETHERSCAN_API_KEY as string); (async () => { - const events = await etherscan.getContractEventsAsync('0x4f833a24e1f95d70f028921e27040ca56e09ab0b'); - console.log(events); + await etherscan.getContractEventsAsync('0x4f833a24e1f95d70f028921e27040ca56e09ab0b'); })(); diff --git a/packages/pipeline/test/data-sources/etherscan/events_test.ts b/packages/pipeline/test/data-sources/etherscan/events_test.ts new file mode 100644 index 000000000..1151757f2 --- /dev/null +++ b/packages/pipeline/test/data-sources/etherscan/events_test.ts @@ -0,0 +1,88 @@ +import { BigNumber } from '@0xproject/utils'; +import * as chai from 'chai'; +import { DecodedLogArgs, LogEntry, LogWithDecodedArgs } from 'ethereum-types'; +import 'mocha'; + +import { + _convertResponseToLogEntry, + _decodeLogEntry, + EventsResponseResult, +} from '../../../src/data-sources/etherscan/events'; +import { chaiSetup } from '../../utils/chai_setup'; + +chaiSetup.configure(); +const expect = chai.expect; + +describe('etherscan#events', () => { + describe('_convertResponseToLogEntry', () => { + it('converts EventsResponseResult to LogEntry', () => { + const input: EventsResponseResult = { + address: '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', + topics: [ + '0x82af639571738f4ebd4268fb0363d8957ebe1bbb9e78dba5ebd69eed39b154f0', + '0x00000000000000000000000067032ef7be8fa07c4335d0134099db0f3875e930', + '0x0000000000000000000000000000000000000000000000000000000000000000', + ], + data: '0x00000000000000000000000000000000000000000000000000000165f2d3f94d', + blockNumber: '0x61127b', + timeStamp: '0x5ba2878e', + gasPrice: '0x1a13b8600', + gasUsed: '0xd9dc', + logIndex: '0x63', + transactionHash: '0xa3f71931ddab6e758b9d1755b2715b376759f49f23fff60755f7e073367d61b5', + transactionIndex: '0x35', + }; + const expected: LogEntry = { + logIndex: 99, + transactionIndex: 53, + transactionHash: input.transactionHash, + blockHash: '', + blockNumber: 6361723, + address: input.address, + data: input.data, + topics: input.topics, + }; + const actual = _convertResponseToLogEntry(input); + expect(actual).deep.equal(expected); + }); + }); + describe('_decodeLogEntry', () => { + it('decodes LogEntry into LogWithDecodedArgs', () => { + const input: LogEntry = { + logIndex: 96, + transactionIndex: 52, + transactionHash: '0x02b59043e9b38b430c8c66abe67ab4a9e5509def8f8552b54231e88db1839831', + blockHash: '', + blockNumber: 6361723, + address: '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', + data: + '0x00000000000000000000000067032ef7be8fa07c4335d0134099db0f3875e93000000000000000000000000067032ef7be8fa07c4335d0134099db0f3875e930000000000000000000000000000000000000000000000000000000174876e8000000000000000000000000000000000000000000000000000000000013ab668000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000', + topics: [ + '0x0bcc4c97732e47d9946f229edb95f5b6323f601300e4690de719993f3c371129', + '0x0000000000000000000000003f7f832abb3be28442c0e48b7222e02b322c78f3', + '0x000000000000000000000000a258b39954cef5cb142fd567a46cddb31a670124', + '0x523404b4e6f847d9aefcf5be024be396449b4635590291fd7a28a8c940843858', + ], + }; + const expected: LogWithDecodedArgs = { + ...input, + event: 'Fill', + args: { + makerAddress: '0x3f7f832abb3be28442c0e48b7222e02b322c78f3', + feeRecipientAddress: '0xa258b39954cef5cb142fd567a46cddb31a670124', + takerAddress: '0x67032ef7be8fa07c4335d0134099db0f3875e930', + senderAddress: '0x67032ef7be8fa07c4335d0134099db0f3875e930', + makerAssetFilledAmount: new BigNumber('100000000000'), + takerAssetFilledAmount: new BigNumber('330000000'), + makerFeePaid: new BigNumber('0'), + takerFeePaid: new BigNumber('0'), + orderHash: '0x523404b4e6f847d9aefcf5be024be396449b4635590291fd7a28a8c940843858', + makerAssetData: '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498', + takerAssetData: '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + }, + }; + const actual = _decodeLogEntry(input); + expect(actual).deep.equal(expected); + }); + }); +}); diff --git a/packages/pipeline/test/utils/chai_setup.ts b/packages/pipeline/test/utils/chai_setup.ts new file mode 100644 index 000000000..1a8733093 --- /dev/null +++ b/packages/pipeline/test/utils/chai_setup.ts @@ -0,0 +1,13 @@ +import * as chai from 'chai'; +import chaiAsPromised = require('chai-as-promised'); +import ChaiBigNumber = require('chai-bignumber'); +import * as dirtyChai from 'dirty-chai'; + +export const chaiSetup = { + configure(): void { + chai.config.includeStack = true; + chai.use(ChaiBigNumber()); + chai.use(dirtyChai); + chai.use(chaiAsPromised); + }, +}; diff --git a/yarn.lock b/yarn.lock index 215389b52..2871c1f4f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3876,7 +3876,7 @@ center-align@^0.1.1: align-text "^0.1.3" lazy-cache "^1.0.3" -chai-as-promised@^7.1.0: +chai-as-promised@^7.1.0, chai-as-promised@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/chai-as-promised/-/chai-as-promised-7.1.1.tgz#08645d825deb8696ee61725dbf590c012eb00ca0" dependencies: -- cgit v1.2.3 From d71fa6535987a0f13900f8e31dbb51772c12fc4f Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Wed, 19 Sep 2018 12:09:36 -0700 Subject: Make contractAbi a parameter of getContractEventsAsync --- .../pipeline/src/data-sources/etherscan/events.ts | 22 +++++++++++++++------- .../pipeline/src/data-sources/etherscan/index.ts | 6 ++++-- packages/pipeline/src/index.ts | 8 +++++++- .../test/data-sources/etherscan/events_test.ts | 4 +++- 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/packages/pipeline/src/data-sources/etherscan/events.ts b/packages/pipeline/src/data-sources/etherscan/events.ts index 3083af267..89b3ffac1 100644 --- a/packages/pipeline/src/data-sources/etherscan/events.ts +++ b/packages/pipeline/src/data-sources/etherscan/events.ts @@ -1,9 +1,7 @@ import { AbiDecoder } from '@0xproject/utils'; -import { DecodedLogArgs, LogEntry, LogWithDecodedArgs } from 'ethereum-types'; +import { AbiDefinition, DecodedLogArgs, LogEntry, LogWithDecodedArgs } from 'ethereum-types'; import * as R from 'ramda'; -import { artifacts } from '../../artifacts'; - // Raw events response from etherescan.io export interface EventsResponse { status: string; @@ -48,16 +46,26 @@ export function _convertResponseToLogEntry(result: EventsResponseResult): LogEnt // Decodes a LogEntry into a LogWithDecodedArgs // tslint:disable-next-line:completed-docs -export function _decodeLogEntry(log: LogEntry): LogWithDecodedArgs { - const abiDecoder = new AbiDecoder([artifacts.Exchange.compilerOutput.abi]); +export const _decodeLogEntry = R.curry((contractAbi: AbiDefinition[], log: LogEntry): LogWithDecodedArgs< + DecodedLogArgs +> => { + const abiDecoder = new AbiDecoder([contractAbi]); const logWithDecodedArgs = abiDecoder.tryToDecodeLogOrNoop(log); // tslint:disable-next-line:no-unnecessary-type-assertion return logWithDecodedArgs as LogWithDecodedArgs; -} +}); /** * Parses and abi-decodes the raw events response from etherscan.io. + * @param contractAbi The ABI for the contract that the events where emited from. * @param rawEventsResponse The raw events response from etherescan.io. * @returns Parsed and decoded events. */ -export const parseRawEventsResponse = R.pipe(R.map(_convertResponseToLogEntry), R.map(_decodeLogEntry)); +export function parseRawEventsResponse( + contractAbi: AbiDefinition[], + rawEventsResponse: EventsResponse, +): Array> { + return R.pipe(R.map(_convertResponseToLogEntry), R.map(_decodeLogEntry(contractAbi)))(rawEventsResponse.result); +} + +// export const parseRawEventsResponse = R.pipe(R.map(_convertResponseToLogEntry), R.map(_decodeLogEntry)); diff --git a/packages/pipeline/src/data-sources/etherscan/index.ts b/packages/pipeline/src/data-sources/etherscan/index.ts index 0891d351a..66b6b1a8d 100644 --- a/packages/pipeline/src/data-sources/etherscan/index.ts +++ b/packages/pipeline/src/data-sources/etherscan/index.ts @@ -1,5 +1,5 @@ import { default as axios } from 'axios'; -import { BlockParam, BlockParamLiteral, DecodedLogArgs, LogWithDecodedArgs } from 'ethereum-types'; +import { AbiDefinition, BlockParam, BlockParamLiteral, DecodedLogArgs, LogWithDecodedArgs } from 'ethereum-types'; import { EventsResponse, parseRawEventsResponse } from './events'; @@ -14,12 +14,14 @@ export class Etherscan { /** * Gets the decoded events for a specific contract and block range. * @param contractAddress The address of the contract to get the events for. + * @param constractAbi The ABI of the contract. * @param fromBlock The start of the block range to get events for (inclusive). * @param toBlock The end of the block range to get events for (inclusive). * @returns A list of decoded events. */ public async getContractEventsAsync( contractAddress: string, + contractAbi: AbiDefinition[], fromBlock: BlockParam = BlockParamLiteral.Earliest, toBlock: BlockParam = BlockParamLiteral.Latest, ): Promise>> { @@ -28,7 +30,7 @@ export class Etherscan { }`; const resp = await axios.get(fullURL); // TODO(albrow): Check response code. - const decodedEvents = parseRawEventsResponse(resp.data.result); + const decodedEvents = parseRawEventsResponse(contractAbi, resp.data); return decodedEvents; } } diff --git a/packages/pipeline/src/index.ts b/packages/pipeline/src/index.ts index baed6933e..eccb24d18 100644 --- a/packages/pipeline/src/index.ts +++ b/packages/pipeline/src/index.ts @@ -1,7 +1,13 @@ import { Etherscan } from './data-sources/etherscan'; +import { artifacts } from './artifacts'; + const etherscan = new Etherscan(process.env.ETHERSCAN_API_KEY as string); (async () => { - await etherscan.getContractEventsAsync('0x4f833a24e1f95d70f028921e27040ca56e09ab0b'); + const events = await etherscan.getContractEventsAsync( + '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', + artifacts.Exchange.compilerOutput.abi, + ); + console.log(events); })(); diff --git a/packages/pipeline/test/data-sources/etherscan/events_test.ts b/packages/pipeline/test/data-sources/etherscan/events_test.ts index 1151757f2..cb8e9d8e8 100644 --- a/packages/pipeline/test/data-sources/etherscan/events_test.ts +++ b/packages/pipeline/test/data-sources/etherscan/events_test.ts @@ -3,6 +3,8 @@ import * as chai from 'chai'; import { DecodedLogArgs, LogEntry, LogWithDecodedArgs } from 'ethereum-types'; import 'mocha'; +import { artifacts } from '../../../src/artifacts'; + import { _convertResponseToLogEntry, _decodeLogEntry, @@ -81,7 +83,7 @@ describe('etherscan#events', () => { takerAssetData: '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', }, }; - const actual = _decodeLogEntry(input); + const actual = _decodeLogEntry(artifacts.Exchange.compilerOutput.abi, input); expect(actual).deep.equal(expected); }); }); -- cgit v1.2.3 From 4e6919a6e58d76766098262664c78f92d888337d Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Wed, 19 Sep 2018 17:30:34 -0700 Subject: Add typeorm and begin inserting database records --- .gitignore | 4 ++ packages/pipeline/package.json | 13 ++-- .../pipeline/src/data-sources/etherscan/events.ts | 9 +-- .../pipeline/src/entities/ExchangeFillEvent.ts | 24 +++++++ packages/pipeline/src/index.ts | 38 +++++++++- packages/pipeline/src/ormconfig.ts | 14 ++++ packages/pipeline/tsconfig.json | 4 +- yarn.lock | 82 +++++++++++++++++++--- 8 files changed, 166 insertions(+), 22 deletions(-) create mode 100644 packages/pipeline/src/entities/ExchangeFillEvent.ts create mode 100644 packages/pipeline/src/ormconfig.ts diff --git a/.gitignore b/.gitignore index 6a82f3e26..7db118507 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,10 @@ pids *.seed *.pid.lock +# SQLite database files +*.db +*.sqlite + # Directory for instrumented libs generated by jscoverage/JSCover lib-cov diff --git a/packages/pipeline/package.json b/packages/pipeline/package.json index cdcd5ed18..d81ea2668 100644 --- a/packages/pipeline/package.json +++ b/packages/pipeline/package.json @@ -30,13 +30,13 @@ "license": "Apache-2.0", "devDependencies": { "@0xproject/tslint-config": "^1.0.7", - "tslint": "5.11.0", - "typescript": "3.0.1", - "mocha": "^5.2.0", "chai": "^4.1.2", "chai-as-promised": "^7.1.1", "chai-bignumber": "^2.0.2", - "dirty-chai": "^2.0.1" + "dirty-chai": "^2.0.1", + "mocha": "^5.2.0", + "tslint": "5.11.0", + "typescript": "3.0.1" }, "dependencies": { "@0xproject/contract-wrappers": "^1.0.1", @@ -45,6 +45,9 @@ "@types/ramda": "^0.25.38", "axios": "^0.18.0", "ethereum-types": "^1.0.6", - "ramda": "^0.25.0" + "ramda": "^0.25.0", + "reflect-metadata": "^0.1.12", + "sqlite3": "^4.0.2", + "typeorm": "^0.2.7" } } diff --git a/packages/pipeline/src/data-sources/etherscan/events.ts b/packages/pipeline/src/data-sources/etherscan/events.ts index 89b3ffac1..edc8cde7b 100644 --- a/packages/pipeline/src/data-sources/etherscan/events.ts +++ b/packages/pipeline/src/data-sources/etherscan/events.ts @@ -1,5 +1,6 @@ +import { ExchangeEventArgs } from '@0xproject/contract-wrappers'; import { AbiDecoder } from '@0xproject/utils'; -import { AbiDefinition, DecodedLogArgs, LogEntry, LogWithDecodedArgs } from 'ethereum-types'; +import { AbiDefinition, LogEntry, LogWithDecodedArgs } from 'ethereum-types'; import * as R from 'ramda'; // Raw events response from etherescan.io @@ -47,12 +48,12 @@ export function _convertResponseToLogEntry(result: EventsResponseResult): LogEnt // Decodes a LogEntry into a LogWithDecodedArgs // tslint:disable-next-line:completed-docs export const _decodeLogEntry = R.curry((contractAbi: AbiDefinition[], log: LogEntry): LogWithDecodedArgs< - DecodedLogArgs + ExchangeEventArgs > => { const abiDecoder = new AbiDecoder([contractAbi]); const logWithDecodedArgs = abiDecoder.tryToDecodeLogOrNoop(log); // tslint:disable-next-line:no-unnecessary-type-assertion - return logWithDecodedArgs as LogWithDecodedArgs; + return logWithDecodedArgs as LogWithDecodedArgs; }); /** @@ -64,7 +65,7 @@ export const _decodeLogEntry = R.curry((contractAbi: AbiDefinition[], log: LogEn export function parseRawEventsResponse( contractAbi: AbiDefinition[], rawEventsResponse: EventsResponse, -): Array> { +): Array> { return R.pipe(R.map(_convertResponseToLogEntry), R.map(_decodeLogEntry(contractAbi)))(rawEventsResponse.result); } diff --git a/packages/pipeline/src/entities/ExchangeFillEvent.ts b/packages/pipeline/src/entities/ExchangeFillEvent.ts new file mode 100644 index 000000000..1e9e8d986 --- /dev/null +++ b/packages/pipeline/src/entities/ExchangeFillEvent.ts @@ -0,0 +1,24 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +@Entity() +export class ExchangeFillEvent { + @PrimaryColumn() public logIndex!: number; + + @Column() public address!: string; + @Column() public rawData!: string; + @Column() public blockNumber!: number; + + @Column() public makerAddress!: string; + @Column() public takerAddress!: string; + @Column() public feeRecepientAddress!: string; + @Column() public senderAddress!: string; + @Column() public makerAssetFilledAmount!: string; + @Column() public takerAssetFilledAmount!: string; + @Column() public makerFeePaid!: string; + @Column() public takerFeePaid!: string; + @Column() public orderHash!: string; + // TODO(albrow): Decode asset data. + @Column() public makerAssetData!: string; + @Column() public takerAssetData!: string; + // TODO(albrow): Include topics? +} diff --git a/packages/pipeline/src/index.ts b/packages/pipeline/src/index.ts index eccb24d18..d70ec3e3e 100644 --- a/packages/pipeline/src/index.ts +++ b/packages/pipeline/src/index.ts @@ -1,13 +1,45 @@ -import { Etherscan } from './data-sources/etherscan'; +import { ExchangeFillEventArgs } from '@0xproject/contract-wrappers'; +import { LogWithDecodedArgs } from 'ethereum-types'; +import 'reflect-metadata'; +import { createConnection } from 'typeorm'; import { artifacts } from './artifacts'; +import { Etherscan } from './data-sources/etherscan'; +import { ExchangeFillEvent } from './entities/ExchangeFillEvent'; +import { config } from './ormconfig'; const etherscan = new Etherscan(process.env.ETHERSCAN_API_KEY as string); (async () => { - const events = await etherscan.getContractEventsAsync( + const connection = await createConnection(config); + const repository = connection.getRepository(ExchangeFillEvent); + console.log(`found ${await repository.count()} existing fill events`); + const eventLogs = await etherscan.getContractEventsAsync( '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', artifacts.Exchange.compilerOutput.abi, ); - console.log(events); + for (const eventLog of eventLogs) { + if (eventLog.event !== 'Fill') { + continue; + } + const fillEventLog = eventLog as LogWithDecodedArgs; + const exchangeFillEvent = new ExchangeFillEvent(); + exchangeFillEvent.logIndex = fillEventLog.logIndex as number; + exchangeFillEvent.address = fillEventLog.address as string; + exchangeFillEvent.rawData = fillEventLog.data as string; + exchangeFillEvent.blockNumber = fillEventLog.blockNumber as number; + exchangeFillEvent.makerAddress = fillEventLog.args.makerAddress.toString(); + exchangeFillEvent.takerAddress = fillEventLog.args.takerAddress.toString(); + exchangeFillEvent.feeRecepientAddress = fillEventLog.args.feeRecipientAddress; + exchangeFillEvent.senderAddress = fillEventLog.args.senderAddress; + exchangeFillEvent.makerAssetFilledAmount = fillEventLog.args.makerAssetFilledAmount.toString(); + exchangeFillEvent.takerAssetFilledAmount = fillEventLog.args.takerAssetFilledAmount.toString(); + exchangeFillEvent.makerFeePaid = fillEventLog.args.makerFeePaid.toString(); + exchangeFillEvent.takerFeePaid = fillEventLog.args.takerFeePaid.toString(); + exchangeFillEvent.orderHash = fillEventLog.args.orderHash; + exchangeFillEvent.makerAssetData = fillEventLog.args.makerAssetData; + exchangeFillEvent.takerAssetData = fillEventLog.args.takerAssetData; + await repository.save(exchangeFillEvent); + } + console.log(`now ${await repository.count()} total fill events`); })(); diff --git a/packages/pipeline/src/ormconfig.ts b/packages/pipeline/src/ormconfig.ts new file mode 100644 index 000000000..48e316078 --- /dev/null +++ b/packages/pipeline/src/ormconfig.ts @@ -0,0 +1,14 @@ +import { ConnectionOptions } from 'typeorm'; + +export const config: ConnectionOptions = { + type: 'sqlite', + database: 'database.sqlite', + synchronize: true, + logging: false, + entities: ['./lib/src/entities/**/*.js'], + migrations: ['./lib/src/migrations/**/*.js'], + cli: { + entitiesDir: 'lib/src/entities', + migrationsDir: 'lib/src/migrations', + }, +}; diff --git a/packages/pipeline/tsconfig.json b/packages/pipeline/tsconfig.json index 2ee711adc..918d469ed 100644 --- a/packages/pipeline/tsconfig.json +++ b/packages/pipeline/tsconfig.json @@ -2,7 +2,9 @@ "extends": "../../tsconfig", "compilerOptions": { "outDir": "lib", - "rootDir": "." + "rootDir": ".", + "emitDecoratorMetadata": true, + "experimentalDecorators": true }, "include": ["./src/**/*", "./test/**/*"] } diff --git a/yarn.lock b/yarn.lock index 2871c1f4f..00165b935 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2274,6 +2274,10 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" +app-root-path@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.1.0.tgz#98bf6599327ecea199309866e8140368fd2e646a" + append-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/append-buffer/-/append-buffer-1.0.2.tgz#d8220cf466081525efea50614f3de6514dfa58f1" @@ -3658,7 +3662,7 @@ buffer@^5.0.3, buffer@^5.0.5: base64-js "^1.0.2" ieee754 "^1.1.4" -buffer@^5.2.1: +buffer@^5.1.0, buffer@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.1.tgz#dd57fa0f109ac59c602479044dca7b8b3d0b71d6" dependencies: @@ -4111,6 +4115,16 @@ cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" +cli-highlight@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/cli-highlight/-/cli-highlight-1.2.3.tgz#b200f97ed0e43d24633e89de0f489a48bb87d2bf" + dependencies: + chalk "^2.3.0" + highlight.js "^9.6.0" + mz "^2.4.0" + parse5 "^3.0.3" + yargs "^10.0.3" + cli-spinners@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-0.1.2.tgz#bb764d88e185fb9e1e6a2a1f19772318f605e31c" @@ -5499,6 +5513,10 @@ dotenv@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-4.0.0.tgz#864ef1379aced55ce6f95debecdce179f7a0cd1d" +dotenv@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-5.0.1.tgz#a5317459bd3d79ab88cff6e44057a6a3fbb1fcef" + drbg.js@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/drbg.js/-/drbg.js-1.0.1.tgz#3e36b6c42b37043823cdbc332d58f31e2445480b" @@ -6654,6 +6672,10 @@ figgy-pudding@^3.1.0, figgy-pudding@^3.2.1, figgy-pudding@^3.4.1, figgy-pudding@ version "3.5.1" resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" +figlet@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/figlet/-/figlet-1.2.0.tgz#6c46537378fab649146b5a6143dda019b430b410" + figures@^1.3.5, figures@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" @@ -7789,7 +7811,7 @@ heap@~0.2.6: version "0.2.6" resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.6.tgz#087e1f10b046932fc8594dd9e6d378afc9d1e5ac" -highlight.js@^9.0.0, highlight.js@^9.11.0: +highlight.js@^9.0.0, highlight.js@^9.11.0, highlight.js@^9.6.0: version "9.12.0" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.12.0.tgz#e6d9dbe57cbefe60751f02af336195870c90c01e" @@ -9181,7 +9203,7 @@ js-yaml@3.x, js-yaml@^3.4.2, js-yaml@^3.6.1, js-yaml@^3.7.0: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@^3.12.0, js-yaml@^3.9.0: +js-yaml@^3.11.0, js-yaml@^3.12.0, js-yaml@^3.9.0: version "3.12.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" dependencies: @@ -10773,7 +10795,7 @@ mute-stream@0.0.7, mute-stream@~0.0.4: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" -mz@^2.6.0: +mz@^2.4.0, mz@^2.6.0: version "2.7.0" resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" dependencies: @@ -10781,7 +10803,7 @@ mz@^2.6.0: object-assign "^4.0.1" thenify-all "^1.0.0" -nan@2.10.0, nan@>=2.5.1, nan@^2.0.8, nan@^2.2.1, nan@^2.3.0, nan@^2.3.3, nan@^2.6.2, nan@^2.9.2: +nan@2.10.0, nan@>=2.5.1, nan@^2.0.8, nan@^2.2.1, nan@^2.3.0, nan@^2.3.3, nan@^2.6.2, nan@^2.9.2, nan@~2.10.0: version "2.10.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" @@ -10963,7 +10985,7 @@ node-notifier@^5.2.1: shellwords "^0.1.1" which "^1.3.0" -node-pre-gyp@^0.10.0: +node-pre-gyp@^0.10.0, node-pre-gyp@^0.10.3: version "0.10.3" resolved "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz#3070040716afdc778747b61b6887bf78880b80fc" dependencies: @@ -11697,6 +11719,10 @@ param-case@^2.1.0: dependencies: no-case "^2.2.0" +parent-require@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/parent-require/-/parent-require-1.0.0.tgz#746a167638083a860b0eef6732cb27ed46c32977" + parse-asn1@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.1.tgz#f6bf293818332bd0dab54efb16087724745e6ca8" @@ -11771,7 +11797,7 @@ parse5@4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" -parse5@^3.0.1: +parse5@^3.0.1, parse5@^3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c" dependencies: @@ -13210,6 +13236,10 @@ redux@^3.6.0: loose-envify "^1.1.0" symbol-observable "^1.0.3" +reflect-metadata@^0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.12.tgz#311bf0c6b63cd782f228a81abe146a2bfa9c56f2" + regenerate@^1.2.1: version "1.3.3" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.3.tgz#0c336d3980553d755c39b586ae3b20aa49c82b7f" @@ -14528,6 +14558,14 @@ sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" +sqlite3@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/sqlite3/-/sqlite3-4.0.2.tgz#1bbeb68b03ead5d499e42a3a1b140064791c5a64" + dependencies: + nan "~2.10.0" + node-pre-gyp "^0.10.3" + request "^2.87.0" + sshpk@^1.7.0: version "1.14.1" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.1.tgz#130f5975eddad963f1d56f92b9ac6c51fa9f83eb" @@ -15624,6 +15662,24 @@ typedoc@0.13.0: typedoc-default-themes "^0.5.0" typescript "3.1.x" +typeorm@^0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.7.tgz#4bbbace80dc91b1303be13f42d44ebf01d1b2558" + dependencies: + app-root-path "^2.0.1" + buffer "^5.1.0" + chalk "^2.3.2" + cli-highlight "^1.2.3" + debug "^3.1.0" + dotenv "^5.0.1" + glob "^7.1.2" + js-yaml "^3.11.0" + mkdirp "^0.5.1" + reflect-metadata "^0.1.12" + xml2js "^0.4.17" + yargonaut "^1.1.2" + yargs "^11.1.0" + types-bn@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/types-bn/-/types-bn-0.0.1.tgz#4253c7c7251b14e1d77cdca6f58800e5e2b82c4b" @@ -16912,7 +16968,7 @@ xml2js@0.4.17: sax ">=0.6.0" xmlbuilder "^4.1.0" -xml2js@0.4.19: +xml2js@0.4.19, xml2js@^0.4.17: version "0.4.19" resolved "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7" dependencies: @@ -16971,6 +17027,14 @@ yallist@^3.0.0, yallist@^3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9" +yargonaut@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/yargonaut/-/yargonaut-1.1.4.tgz#c64f56432c7465271221f53f5cc517890c3d6e0c" + dependencies: + chalk "^1.1.1" + figlet "^1.1.1" + parent-require "^1.0.0" + yargs-parser@10.x, yargs-parser@^10.0.0, yargs-parser@^10.1.0: version "10.1.0" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" @@ -17008,7 +17072,7 @@ yargs-parser@^9.0.2: dependencies: camelcase "^4.1.0" -yargs@11.1.0: +yargs@11.1.0, yargs@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.0.tgz#90b869934ed6e871115ea2ff58b03f4724ed2d77" dependencies: -- cgit v1.2.3 From 9e9104578c8526ff48ecdda8b87d61ccb3d66a2d Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Thu, 20 Sep 2018 17:25:48 -0700 Subject: Add support for decoding asset data --- packages/pipeline/package.json | 2 + .../pipeline/src/data-sources/etherscan/events.ts | 78 ++++++++++++++++++++-- .../pipeline/src/data-sources/etherscan/index.ts | 6 +- .../pipeline/src/entities/ExchangeFillEvent.ts | 17 ++++- packages/pipeline/src/index.ts | 27 ++------ yarn.lock | 6 +- 6 files changed, 97 insertions(+), 39 deletions(-) diff --git a/packages/pipeline/package.json b/packages/pipeline/package.json index d81ea2668..6670938f2 100644 --- a/packages/pipeline/package.json +++ b/packages/pipeline/package.json @@ -40,7 +40,9 @@ }, "dependencies": { "@0xproject/contract-wrappers": "^1.0.1", + "@0xproject/order-utils": "^1.0.2", "@0xproject/subproviders": "^2.0.2", + "@0xproject/types": "^1.0.1", "@0xproject/utils": "^1.0.8", "@types/ramda": "^0.25.38", "axios": "^0.18.0", diff --git a/packages/pipeline/src/data-sources/etherscan/events.ts b/packages/pipeline/src/data-sources/etherscan/events.ts index edc8cde7b..a828af527 100644 --- a/packages/pipeline/src/data-sources/etherscan/events.ts +++ b/packages/pipeline/src/data-sources/etherscan/events.ts @@ -1,8 +1,15 @@ -import { ExchangeEventArgs } from '@0xproject/contract-wrappers'; -import { AbiDecoder } from '@0xproject/utils'; +import { ExchangeEventArgs, ExchangeFillEventArgs } from '@0xproject/contract-wrappers'; +import { assetDataUtils } from '@0xproject/order-utils'; +import { AssetProxyId, ERC721AssetData } from '@0xproject/types'; +import { AbiDecoder, BigNumber } from '@0xproject/utils'; import { AbiDefinition, LogEntry, LogWithDecodedArgs } from 'ethereum-types'; import * as R from 'ramda'; +import { ExchangeFillEvent } from '../../entities/ExchangeFillEvent'; + +// TODO(albrow): Union with other exchange event entity types +export type ExchangeEventEntity = ExchangeFillEvent; + // Raw events response from etherescan.io export interface EventsResponse { status: string; @@ -56,17 +63,74 @@ export const _decodeLogEntry = R.curry((contractAbi: AbiDefinition[], log: LogEn return logWithDecodedArgs as LogWithDecodedArgs; }); +export function _convertToEntity(eventLog: LogWithDecodedArgs): ExchangeEventEntity { + switch (eventLog.event) { + case 'Fill': + return _convertToExchangeFillEvent(eventLog as LogWithDecodedArgs); + default: + throw new Error('unexpected eventLog.event type: ' + eventLog.event); + } +} + +export function _convertToExchangeFillEvent(eventLog: LogWithDecodedArgs): ExchangeFillEvent { + const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.makerAssetData); + const makerAssetType = makerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; + const takerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.takerAssetData); + const takerAssetType = takerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; + const exchangeFillEvent = new ExchangeFillEvent(); + exchangeFillEvent.logIndex = eventLog.logIndex as number; + exchangeFillEvent.address = eventLog.address as string; + exchangeFillEvent.rawData = eventLog.data as string; + exchangeFillEvent.blockNumber = eventLog.blockNumber as number; + exchangeFillEvent.makerAddress = eventLog.args.makerAddress.toString(); + exchangeFillEvent.takerAddress = eventLog.args.takerAddress.toString(); + exchangeFillEvent.feeRecepientAddress = eventLog.args.feeRecipientAddress; + exchangeFillEvent.senderAddress = eventLog.args.senderAddress; + exchangeFillEvent.makerAssetFilledAmount = eventLog.args.makerAssetFilledAmount.toString(); + exchangeFillEvent.takerAssetFilledAmount = eventLog.args.takerAssetFilledAmount.toString(); + exchangeFillEvent.makerFeePaid = eventLog.args.makerFeePaid.toString(); + exchangeFillEvent.takerFeePaid = eventLog.args.takerFeePaid.toString(); + exchangeFillEvent.orderHash = eventLog.args.orderHash; + exchangeFillEvent.rawMakerAssetData = eventLog.args.makerAssetData; + exchangeFillEvent.makerAssetType = makerAssetType; + exchangeFillEvent.makerAssetProxyId = makerAssetData.assetProxyId; + exchangeFillEvent.makerTokenAddress = makerAssetData.tokenAddress; + exchangeFillEvent.makerTokenId = bigNumbertoStringOrNull((makerAssetData as ERC721AssetData).tokenId); + exchangeFillEvent.rawTakerAssetData = eventLog.args.takerAssetData; + exchangeFillEvent.takerAssetType = takerAssetType; + exchangeFillEvent.takerAssetProxyId = takerAssetData.assetProxyId; + exchangeFillEvent.takerTokenAddress = takerAssetData.tokenAddress; + exchangeFillEvent.takerTokenId = bigNumbertoStringOrNull((takerAssetData as ERC721AssetData).tokenId); + return exchangeFillEvent; +} + +function bigNumbertoStringOrNull(n: BigNumber): string | null { + if (n == null) { + return null; + } + return n.toString(); +} + +function filterEventLogs( + eventLogs: Array>, +): Array> { + return R.filter(eventLog => eventLog.event === 'Fill', eventLogs); +} + /** * Parses and abi-decodes the raw events response from etherscan.io. * @param contractAbi The ABI for the contract that the events where emited from. * @param rawEventsResponse The raw events response from etherescan.io. - * @returns Parsed and decoded events. + * @returns Parsed and decoded event entities, ready to be saved to database. */ export function parseRawEventsResponse( contractAbi: AbiDefinition[], rawEventsResponse: EventsResponse, -): Array> { - return R.pipe(R.map(_convertResponseToLogEntry), R.map(_decodeLogEntry(contractAbi)))(rawEventsResponse.result); +): ExchangeEventEntity[] { + return R.pipe( + R.map(_convertResponseToLogEntry), + R.map(_decodeLogEntry(contractAbi)), + filterEventLogs, + R.map(_convertToEntity), + )(rawEventsResponse.result); } - -// export const parseRawEventsResponse = R.pipe(R.map(_convertResponseToLogEntry), R.map(_decodeLogEntry)); diff --git a/packages/pipeline/src/data-sources/etherscan/index.ts b/packages/pipeline/src/data-sources/etherscan/index.ts index 66b6b1a8d..c5eb2b9c6 100644 --- a/packages/pipeline/src/data-sources/etherscan/index.ts +++ b/packages/pipeline/src/data-sources/etherscan/index.ts @@ -1,7 +1,7 @@ import { default as axios } from 'axios'; -import { AbiDefinition, BlockParam, BlockParamLiteral, DecodedLogArgs, LogWithDecodedArgs } from 'ethereum-types'; +import { AbiDefinition, BlockParam, BlockParamLiteral } from 'ethereum-types'; -import { EventsResponse, parseRawEventsResponse } from './events'; +import { EventsResponse, ExchangeEventEntity, parseRawEventsResponse } from './events'; const ETHERSCAN_URL = 'https://api.etherscan.io/api'; @@ -24,7 +24,7 @@ export class Etherscan { contractAbi: AbiDefinition[], fromBlock: BlockParam = BlockParamLiteral.Earliest, toBlock: BlockParam = BlockParamLiteral.Latest, - ): Promise>> { + ): Promise { const fullURL = `${ETHERSCAN_URL}?module=logs&action=getLogs&address=${contractAddress}&fromBlock=${fromBlock}&toBlock=${toBlock}&apikey=${ this._apiKey }`; diff --git a/packages/pipeline/src/entities/ExchangeFillEvent.ts b/packages/pipeline/src/entities/ExchangeFillEvent.ts index 1e9e8d986..1716c60d1 100644 --- a/packages/pipeline/src/entities/ExchangeFillEvent.ts +++ b/packages/pipeline/src/entities/ExchangeFillEvent.ts @@ -1,5 +1,7 @@ import { Column, Entity, PrimaryColumn } from 'typeorm'; +export type ExchangeFillEventAssetType = 'erc20' | 'erc721'; + @Entity() export class ExchangeFillEvent { @PrimaryColumn() public logIndex!: number; @@ -17,8 +19,17 @@ export class ExchangeFillEvent { @Column() public makerFeePaid!: string; @Column() public takerFeePaid!: string; @Column() public orderHash!: string; - // TODO(albrow): Decode asset data. - @Column() public makerAssetData!: string; - @Column() public takerAssetData!: string; + @Column() public rawMakerAssetData!: string; + @Column() public makerAssetType!: ExchangeFillEventAssetType; + @Column() public makerAssetProxyId!: string; + @Column() public makerTokenAddress!: string; + @Column({ nullable: true, type: String }) + public makerTokenId!: string | null; + @Column() public rawTakerAssetData!: string; + @Column() public takerAssetType!: ExchangeFillEventAssetType; + @Column() public takerAssetProxyId!: string; + @Column() public takerTokenAddress!: string; + @Column({ nullable: true, type: String }) + public takerTokenId!: string | null; // TODO(albrow): Include topics? } diff --git a/packages/pipeline/src/index.ts b/packages/pipeline/src/index.ts index d70ec3e3e..db26343e0 100644 --- a/packages/pipeline/src/index.ts +++ b/packages/pipeline/src/index.ts @@ -1,4 +1,5 @@ import { ExchangeFillEventArgs } from '@0xproject/contract-wrappers'; +import { assetDataUtils } from '@0xproject/order-utils'; import { LogWithDecodedArgs } from 'ethereum-types'; import 'reflect-metadata'; import { createConnection } from 'typeorm'; @@ -14,32 +15,12 @@ const etherscan = new Etherscan(process.env.ETHERSCAN_API_KEY as string); const connection = await createConnection(config); const repository = connection.getRepository(ExchangeFillEvent); console.log(`found ${await repository.count()} existing fill events`); - const eventLogs = await etherscan.getContractEventsAsync( + const events = await etherscan.getContractEventsAsync( '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', artifacts.Exchange.compilerOutput.abi, ); - for (const eventLog of eventLogs) { - if (eventLog.event !== 'Fill') { - continue; - } - const fillEventLog = eventLog as LogWithDecodedArgs; - const exchangeFillEvent = new ExchangeFillEvent(); - exchangeFillEvent.logIndex = fillEventLog.logIndex as number; - exchangeFillEvent.address = fillEventLog.address as string; - exchangeFillEvent.rawData = fillEventLog.data as string; - exchangeFillEvent.blockNumber = fillEventLog.blockNumber as number; - exchangeFillEvent.makerAddress = fillEventLog.args.makerAddress.toString(); - exchangeFillEvent.takerAddress = fillEventLog.args.takerAddress.toString(); - exchangeFillEvent.feeRecepientAddress = fillEventLog.args.feeRecipientAddress; - exchangeFillEvent.senderAddress = fillEventLog.args.senderAddress; - exchangeFillEvent.makerAssetFilledAmount = fillEventLog.args.makerAssetFilledAmount.toString(); - exchangeFillEvent.takerAssetFilledAmount = fillEventLog.args.takerAssetFilledAmount.toString(); - exchangeFillEvent.makerFeePaid = fillEventLog.args.makerFeePaid.toString(); - exchangeFillEvent.takerFeePaid = fillEventLog.args.takerFeePaid.toString(); - exchangeFillEvent.orderHash = fillEventLog.args.orderHash; - exchangeFillEvent.makerAssetData = fillEventLog.args.makerAssetData; - exchangeFillEvent.takerAssetData = fillEventLog.args.takerAssetData; - await repository.save(exchangeFillEvent); + for (const event of events) { + await repository.save(event); } console.log(`now ${await repository.count()} total fill events`); })(); diff --git a/yarn.lock b/yarn.lock index 00165b935..9e293e586 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6225,9 +6225,9 @@ ethereumjs-wallet@0.6.0: utf8 "^2.1.1" uuid "^2.0.1" -ethers@3.0.22: - version "3.0.22" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-3.0.22.tgz#7fab1ea16521705837aa43c15831877b2716b436" +ethers@0xproject/ethers.js#eip-838-reasons, ethers@3.0.22: + version "3.0.18" + resolved "https://codeload.github.com/0xproject/ethers.js/tar.gz/b91342bd200d142af0165d6befddf783c8ae8447" dependencies: aes-js "3.0.0" bn.js "^4.4.0" -- cgit v1.2.3 From fe523e1f3f765077bdaf4dfc345c9dca67693668 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Tue, 25 Sep 2018 12:54:10 -0700 Subject: Re-organize event parsing and decoding --- .../pipeline/src/data-sources/etherscan/events.ts | 136 ---------------- .../pipeline/src/data-sources/etherscan/index.ts | 36 ----- .../pipeline/src/data_sources/etherscan/index.ts | 52 +++++++ .../events/event_handlers/base_event_handler.ts | 34 ++++ .../event_handlers/exchange_event_handler.ts | 76 +++++++++ .../pipeline/src/data_types/events/event_utils.ts | 35 +++++ .../pipeline/src/entities/ExchangeFillEvent.ts | 4 +- packages/pipeline/src/index.ts | 20 ++- .../test/data-sources/etherscan/events_test.ts | 172 ++++++++++----------- 9 files changed, 298 insertions(+), 267 deletions(-) delete mode 100644 packages/pipeline/src/data-sources/etherscan/events.ts delete mode 100644 packages/pipeline/src/data-sources/etherscan/index.ts create mode 100644 packages/pipeline/src/data_sources/etherscan/index.ts create mode 100644 packages/pipeline/src/data_types/events/event_handlers/base_event_handler.ts create mode 100644 packages/pipeline/src/data_types/events/event_handlers/exchange_event_handler.ts create mode 100644 packages/pipeline/src/data_types/events/event_utils.ts diff --git a/packages/pipeline/src/data-sources/etherscan/events.ts b/packages/pipeline/src/data-sources/etherscan/events.ts deleted file mode 100644 index a828af527..000000000 --- a/packages/pipeline/src/data-sources/etherscan/events.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { ExchangeEventArgs, ExchangeFillEventArgs } from '@0xproject/contract-wrappers'; -import { assetDataUtils } from '@0xproject/order-utils'; -import { AssetProxyId, ERC721AssetData } from '@0xproject/types'; -import { AbiDecoder, BigNumber } from '@0xproject/utils'; -import { AbiDefinition, LogEntry, LogWithDecodedArgs } from 'ethereum-types'; -import * as R from 'ramda'; - -import { ExchangeFillEvent } from '../../entities/ExchangeFillEvent'; - -// TODO(albrow): Union with other exchange event entity types -export type ExchangeEventEntity = ExchangeFillEvent; - -// Raw events response from etherescan.io -export interface EventsResponse { - status: string; - message: string; - result: EventsResponseResult[]; -} - -// Events as represented in the response from etherscan.io -export interface EventsResponseResult { - address: string; - topics: string[]; - data: string; - blockNumber: string; - timeStamp: string; - gasPrice: string; - gasUsed: string; - logIndex: string; - transactionHash: string; - transactionIndex: string; -} - -const hexRadix = 16; - -function hexToInt(hex: string): number { - return parseInt(hex.replace('0x', ''), hexRadix); -} - -// Converts a raw event response to a LogEntry -// tslint:disable-next-line:completed-docs -export function _convertResponseToLogEntry(result: EventsResponseResult): LogEntry { - return { - logIndex: hexToInt(result.logIndex), - transactionIndex: hexToInt(result.transactionIndex), - transactionHash: result.transactionHash, - blockHash: '', - blockNumber: hexToInt(result.blockNumber), - address: result.address, - data: result.data, - topics: result.topics, - }; -} - -// Decodes a LogEntry into a LogWithDecodedArgs -// tslint:disable-next-line:completed-docs -export const _decodeLogEntry = R.curry((contractAbi: AbiDefinition[], log: LogEntry): LogWithDecodedArgs< - ExchangeEventArgs -> => { - const abiDecoder = new AbiDecoder([contractAbi]); - const logWithDecodedArgs = abiDecoder.tryToDecodeLogOrNoop(log); - // tslint:disable-next-line:no-unnecessary-type-assertion - return logWithDecodedArgs as LogWithDecodedArgs; -}); - -export function _convertToEntity(eventLog: LogWithDecodedArgs): ExchangeEventEntity { - switch (eventLog.event) { - case 'Fill': - return _convertToExchangeFillEvent(eventLog as LogWithDecodedArgs); - default: - throw new Error('unexpected eventLog.event type: ' + eventLog.event); - } -} - -export function _convertToExchangeFillEvent(eventLog: LogWithDecodedArgs): ExchangeFillEvent { - const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.makerAssetData); - const makerAssetType = makerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; - const takerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.takerAssetData); - const takerAssetType = takerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; - const exchangeFillEvent = new ExchangeFillEvent(); - exchangeFillEvent.logIndex = eventLog.logIndex as number; - exchangeFillEvent.address = eventLog.address as string; - exchangeFillEvent.rawData = eventLog.data as string; - exchangeFillEvent.blockNumber = eventLog.blockNumber as number; - exchangeFillEvent.makerAddress = eventLog.args.makerAddress.toString(); - exchangeFillEvent.takerAddress = eventLog.args.takerAddress.toString(); - exchangeFillEvent.feeRecepientAddress = eventLog.args.feeRecipientAddress; - exchangeFillEvent.senderAddress = eventLog.args.senderAddress; - exchangeFillEvent.makerAssetFilledAmount = eventLog.args.makerAssetFilledAmount.toString(); - exchangeFillEvent.takerAssetFilledAmount = eventLog.args.takerAssetFilledAmount.toString(); - exchangeFillEvent.makerFeePaid = eventLog.args.makerFeePaid.toString(); - exchangeFillEvent.takerFeePaid = eventLog.args.takerFeePaid.toString(); - exchangeFillEvent.orderHash = eventLog.args.orderHash; - exchangeFillEvent.rawMakerAssetData = eventLog.args.makerAssetData; - exchangeFillEvent.makerAssetType = makerAssetType; - exchangeFillEvent.makerAssetProxyId = makerAssetData.assetProxyId; - exchangeFillEvent.makerTokenAddress = makerAssetData.tokenAddress; - exchangeFillEvent.makerTokenId = bigNumbertoStringOrNull((makerAssetData as ERC721AssetData).tokenId); - exchangeFillEvent.rawTakerAssetData = eventLog.args.takerAssetData; - exchangeFillEvent.takerAssetType = takerAssetType; - exchangeFillEvent.takerAssetProxyId = takerAssetData.assetProxyId; - exchangeFillEvent.takerTokenAddress = takerAssetData.tokenAddress; - exchangeFillEvent.takerTokenId = bigNumbertoStringOrNull((takerAssetData as ERC721AssetData).tokenId); - return exchangeFillEvent; -} - -function bigNumbertoStringOrNull(n: BigNumber): string | null { - if (n == null) { - return null; - } - return n.toString(); -} - -function filterEventLogs( - eventLogs: Array>, -): Array> { - return R.filter(eventLog => eventLog.event === 'Fill', eventLogs); -} - -/** - * Parses and abi-decodes the raw events response from etherscan.io. - * @param contractAbi The ABI for the contract that the events where emited from. - * @param rawEventsResponse The raw events response from etherescan.io. - * @returns Parsed and decoded event entities, ready to be saved to database. - */ -export function parseRawEventsResponse( - contractAbi: AbiDefinition[], - rawEventsResponse: EventsResponse, -): ExchangeEventEntity[] { - return R.pipe( - R.map(_convertResponseToLogEntry), - R.map(_decodeLogEntry(contractAbi)), - filterEventLogs, - R.map(_convertToEntity), - )(rawEventsResponse.result); -} diff --git a/packages/pipeline/src/data-sources/etherscan/index.ts b/packages/pipeline/src/data-sources/etherscan/index.ts deleted file mode 100644 index c5eb2b9c6..000000000 --- a/packages/pipeline/src/data-sources/etherscan/index.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { default as axios } from 'axios'; -import { AbiDefinition, BlockParam, BlockParamLiteral } from 'ethereum-types'; - -import { EventsResponse, ExchangeEventEntity, parseRawEventsResponse } from './events'; - -const ETHERSCAN_URL = 'https://api.etherscan.io/api'; - -export class Etherscan { - private readonly _apiKey: string; - constructor(apiKey: string) { - this._apiKey = apiKey; - } - - /** - * Gets the decoded events for a specific contract and block range. - * @param contractAddress The address of the contract to get the events for. - * @param constractAbi The ABI of the contract. - * @param fromBlock The start of the block range to get events for (inclusive). - * @param toBlock The end of the block range to get events for (inclusive). - * @returns A list of decoded events. - */ - public async getContractEventsAsync( - contractAddress: string, - contractAbi: AbiDefinition[], - fromBlock: BlockParam = BlockParamLiteral.Earliest, - toBlock: BlockParam = BlockParamLiteral.Latest, - ): Promise { - const fullURL = `${ETHERSCAN_URL}?module=logs&action=getLogs&address=${contractAddress}&fromBlock=${fromBlock}&toBlock=${toBlock}&apikey=${ - this._apiKey - }`; - const resp = await axios.get(fullURL); - // TODO(albrow): Check response code. - const decodedEvents = parseRawEventsResponse(contractAbi, resp.data); - return decodedEvents; - } -} diff --git a/packages/pipeline/src/data_sources/etherscan/index.ts b/packages/pipeline/src/data_sources/etherscan/index.ts new file mode 100644 index 000000000..044fff02e --- /dev/null +++ b/packages/pipeline/src/data_sources/etherscan/index.ts @@ -0,0 +1,52 @@ +import { default as axios } from 'axios'; +import { BlockParam, BlockParamLiteral } from 'ethereum-types'; + +const ETHERSCAN_URL = 'https://api.etherscan.io/api'; + +export class Etherscan { + private readonly _apiKey: string; + constructor(apiKey: string) { + this._apiKey = apiKey; + } + + /** + * Gets the raw events for a specific contract and block range. + * @param contractAddress The address of the contract to get the events for. + * @param fromBlock The start of the block range to get events for (inclusive). + * @param toBlock The end of the block range to get events for (inclusive). + * @returns A list of decoded events. + */ + public async getContractEventsAsync( + contractAddress: string, + fromBlock: BlockParam = BlockParamLiteral.Earliest, + toBlock: BlockParam = BlockParamLiteral.Latest, + ): Promise { + const fullURL = `${ETHERSCAN_URL}?module=logs&action=getLogs&address=${contractAddress}&fromBlock=${fromBlock}&toBlock=${toBlock}&apikey=${ + this._apiKey + }`; + const resp = await axios.get(fullURL); + // TODO(albrow): Check response code. + return resp.data; + } +} + +// Raw events response from etherescan.io +export interface EventsResponse { + status: string; + message: string; + result: EventsResponseResult[]; +} + +// Events as represented in the response from etherscan.io +export interface EventsResponseResult { + address: string; + topics: string[]; + data: string; + blockNumber: string; + timeStamp: string; + gasPrice: string; + gasUsed: string; + logIndex: string; + transactionHash: string; + transactionIndex: string; +} diff --git a/packages/pipeline/src/data_types/events/event_handlers/base_event_handler.ts b/packages/pipeline/src/data_types/events/event_handlers/base_event_handler.ts new file mode 100644 index 000000000..59331fc4f --- /dev/null +++ b/packages/pipeline/src/data_types/events/event_handlers/base_event_handler.ts @@ -0,0 +1,34 @@ +import { AbiDefinition, BlockParam, BlockParamLiteral, LogEntry } from 'ethereum-types'; +import * as R from 'ramda'; +import { BaseEntity } from 'typeorm'; + +import { Etherscan } from '../../../data_sources/etherscan'; +import { convertResponseToLogEntry } from '../event_utils'; + +export abstract class BaseEventHandler { + protected _abi: AbiDefinition[]; + protected _address: string; + protected _etherscan: Etherscan; + constructor(abi: AbiDefinition[], address: string, etherscan: Etherscan) { + this._abi = abi; + this._address = address; + this._etherscan = etherscan; + } + public abstract convertLogEntryToEventEntity(logEntry: LogEntry): EntityType; + + public async getEventsAsync( + fromBlock: BlockParam = BlockParamLiteral.Earliest, + toBlock: BlockParam = BlockParamLiteral.Latest, + ): Promise { + const rawEventsResponse = await this._etherscan.getContractEventsAsync(this._address, fromBlock, toBlock); + const logEntries = R.map(convertResponseToLogEntry, rawEventsResponse.result); + // Note(albrow): Imperative for loop is required here because we can't + // bind convertLogEntryToEventEntity without having a specific instance + // of a sub-class. + const result = []; + for (const logEntry of logEntries) { + result.push(this.convertLogEntryToEventEntity(logEntry)); + } + return result; + } +} diff --git a/packages/pipeline/src/data_types/events/event_handlers/exchange_event_handler.ts b/packages/pipeline/src/data_types/events/event_handlers/exchange_event_handler.ts new file mode 100644 index 000000000..38ff20595 --- /dev/null +++ b/packages/pipeline/src/data_types/events/event_handlers/exchange_event_handler.ts @@ -0,0 +1,76 @@ +import { ExchangeEventArgs, ExchangeFillEventArgs } from '@0xproject/contract-wrappers'; +import { assetDataUtils } from '@0xproject/order-utils'; +import { AssetProxyId, ERC721AssetData } from '@0xproject/types'; +import { AbiDecoder, BigNumber } from '@0xproject/utils'; +import { AbiDefinition, LogEntry, LogWithDecodedArgs } from 'ethereum-types'; +import * as R from 'ramda'; + +import { ExchangeFillEvent } from '../../../entities/ExchangeFillEvent'; +import { decodeLogEntry } from '../event_utils'; + +import { BaseEventHandler } from './base_event_handler'; + +// TODO(albrow): Union with other exchange event entity types +export type ExchangeEventEntity = ExchangeFillEvent; + +export class ExchangeEventHandler extends BaseEventHandler { + public convertLogEntryToEventEntity(logEntry: LogEntry): ExchangeEventEntity { + const decodedLogEntry = decodeLogEntry(this._abi, logEntry); + return _convertToEntity(decodedLogEntry); + } +} + +export function _convertToEntity(eventLog: LogWithDecodedArgs): ExchangeEventEntity { + switch (eventLog.event) { + case 'Fill': + return _convertToExchangeFillEvent(eventLog as LogWithDecodedArgs); + default: + return new ExchangeFillEvent(); + // throw new Error('unexpected eventLog.event type: ' + eventLog.event); + } +} + +export function _convertToExchangeFillEvent(eventLog: LogWithDecodedArgs): ExchangeFillEvent { + const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.makerAssetData); + const makerAssetType = makerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; + const takerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.takerAssetData); + const takerAssetType = takerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; + const exchangeFillEvent = new ExchangeFillEvent(); + exchangeFillEvent.logIndex = eventLog.logIndex as number; + exchangeFillEvent.address = eventLog.address as string; + exchangeFillEvent.rawData = eventLog.data as string; + exchangeFillEvent.blockNumber = eventLog.blockNumber as number; + exchangeFillEvent.makerAddress = eventLog.args.makerAddress.toString(); + exchangeFillEvent.takerAddress = eventLog.args.takerAddress.toString(); + exchangeFillEvent.feeRecepientAddress = eventLog.args.feeRecipientAddress; + exchangeFillEvent.senderAddress = eventLog.args.senderAddress; + exchangeFillEvent.makerAssetFilledAmount = eventLog.args.makerAssetFilledAmount.toString(); + exchangeFillEvent.takerAssetFilledAmount = eventLog.args.takerAssetFilledAmount.toString(); + exchangeFillEvent.makerFeePaid = eventLog.args.makerFeePaid.toString(); + exchangeFillEvent.takerFeePaid = eventLog.args.takerFeePaid.toString(); + exchangeFillEvent.orderHash = eventLog.args.orderHash; + exchangeFillEvent.rawMakerAssetData = eventLog.args.makerAssetData; + exchangeFillEvent.makerAssetType = makerAssetType; + exchangeFillEvent.makerAssetProxyId = makerAssetData.assetProxyId; + exchangeFillEvent.makerTokenAddress = makerAssetData.tokenAddress; + exchangeFillEvent.makerTokenId = bigNumbertoStringOrNull((makerAssetData as ERC721AssetData).tokenId); + exchangeFillEvent.rawTakerAssetData = eventLog.args.takerAssetData; + exchangeFillEvent.takerAssetType = takerAssetType; + exchangeFillEvent.takerAssetProxyId = takerAssetData.assetProxyId; + exchangeFillEvent.takerTokenAddress = takerAssetData.tokenAddress; + exchangeFillEvent.takerTokenId = bigNumbertoStringOrNull((takerAssetData as ERC721AssetData).tokenId); + return exchangeFillEvent; +} + +function bigNumbertoStringOrNull(n: BigNumber): string | null { + if (n == null) { + return null; + } + return n.toString(); +} + +function filterEventLogs( + eventLogs: Array>, +): Array> { + return R.filter(eventLog => eventLog.event === 'Fill', eventLogs); +} diff --git a/packages/pipeline/src/data_types/events/event_utils.ts b/packages/pipeline/src/data_types/events/event_utils.ts new file mode 100644 index 000000000..6be964807 --- /dev/null +++ b/packages/pipeline/src/data_types/events/event_utils.ts @@ -0,0 +1,35 @@ +import { AbiDecoder } from '@0xproject/utils'; +import { AbiDefinition, LogEntry, LogWithDecodedArgs } from 'ethereum-types'; + +import { EventsResponseResult } from '../../data_sources/etherscan'; + +const hexRadix = 16; + +function hexToInt(hex: string): number { + return parseInt(hex.replace('0x', ''), hexRadix); +} + +// Converts a raw event response to a LogEntry +export function convertResponseToLogEntry(result: EventsResponseResult): LogEntry { + return { + logIndex: hexToInt(result.logIndex), + transactionIndex: hexToInt(result.transactionIndex), + transactionHash: result.transactionHash, + blockHash: '', + blockNumber: hexToInt(result.blockNumber), + address: result.address, + data: result.data, + topics: result.topics, + }; +} + +// Decodes a LogEntry into a LogWithDecodedArgs +export function decodeLogEntry( + contractAbi: AbiDefinition[], + log: LogEntry, +): LogWithDecodedArgs { + const abiDecoder = new AbiDecoder([contractAbi]); + const logWithDecodedArgs = abiDecoder.tryToDecodeLogOrNoop(log); + // tslint:disable-next-line:no-unnecessary-type-assertion + return logWithDecodedArgs as LogWithDecodedArgs; +} diff --git a/packages/pipeline/src/entities/ExchangeFillEvent.ts b/packages/pipeline/src/entities/ExchangeFillEvent.ts index 1716c60d1..a7e817240 100644 --- a/packages/pipeline/src/entities/ExchangeFillEvent.ts +++ b/packages/pipeline/src/entities/ExchangeFillEvent.ts @@ -1,9 +1,9 @@ -import { Column, Entity, PrimaryColumn } from 'typeorm'; +import { BaseEntity, Column, Entity, PrimaryColumn } from 'typeorm'; export type ExchangeFillEventAssetType = 'erc20' | 'erc721'; @Entity() -export class ExchangeFillEvent { +export class ExchangeFillEvent extends BaseEntity { @PrimaryColumn() public logIndex!: number; @Column() public address!: string; diff --git a/packages/pipeline/src/index.ts b/packages/pipeline/src/index.ts index db26343e0..e71a6ae4c 100644 --- a/packages/pipeline/src/index.ts +++ b/packages/pipeline/src/index.ts @@ -1,26 +1,32 @@ -import { ExchangeFillEventArgs } from '@0xproject/contract-wrappers'; -import { assetDataUtils } from '@0xproject/order-utils'; -import { LogWithDecodedArgs } from 'ethereum-types'; import 'reflect-metadata'; import { createConnection } from 'typeorm'; import { artifacts } from './artifacts'; -import { Etherscan } from './data-sources/etherscan'; +import { Etherscan } from './data_sources/etherscan'; import { ExchangeFillEvent } from './entities/ExchangeFillEvent'; import { config } from './ormconfig'; +import { ExchangeEventHandler } from './data_types/events/event_handlers/exchange_event_handler'; + const etherscan = new Etherscan(process.env.ETHERSCAN_API_KEY as string); +const EXCHANGE_ADDRESS = '0x4f833a24e1f95d70f028921e27040ca56e09ab0b'; (async () => { const connection = await createConnection(config); const repository = connection.getRepository(ExchangeFillEvent); console.log(`found ${await repository.count()} existing fill events`); - const events = await etherscan.getContractEventsAsync( - '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', + const exchangeEventHandler = new ExchangeEventHandler( artifacts.Exchange.compilerOutput.abi, + EXCHANGE_ADDRESS, + etherscan, ); + const events = await exchangeEventHandler.getEventsAsync(); + console.log(JSON.stringify(events, null, 2)); for (const event of events) { - await repository.save(event); + // TODO(albrow): remove this check once we can parse all Exchange events + if (event.address != null) { + await event.save(); + } } console.log(`now ${await repository.count()} total fill events`); })(); diff --git a/packages/pipeline/test/data-sources/etherscan/events_test.ts b/packages/pipeline/test/data-sources/etherscan/events_test.ts index cb8e9d8e8..b0f8c7d1c 100644 --- a/packages/pipeline/test/data-sources/etherscan/events_test.ts +++ b/packages/pipeline/test/data-sources/etherscan/events_test.ts @@ -1,90 +1,90 @@ -import { BigNumber } from '@0xproject/utils'; -import * as chai from 'chai'; -import { DecodedLogArgs, LogEntry, LogWithDecodedArgs } from 'ethereum-types'; -import 'mocha'; +// import { BigNumber } from '@0xproject/utils'; +// import * as chai from 'chai'; +// import { DecodedLogArgs, LogEntry, LogWithDecodedArgs } from 'ethereum-types'; +// import 'mocha'; -import { artifacts } from '../../../src/artifacts'; +// import { artifacts } from '../../../src/artifacts'; -import { - _convertResponseToLogEntry, - _decodeLogEntry, - EventsResponseResult, -} from '../../../src/data-sources/etherscan/events'; -import { chaiSetup } from '../../utils/chai_setup'; +// import { +// _convertResponseToLogEntry, +// _decodeLogEntry, +// EventsResponseResult, +// } from '../../../src/data-sources/etherscan/events'; +// import { chaiSetup } from '../../utils/chai_setup'; -chaiSetup.configure(); -const expect = chai.expect; +// chaiSetup.configure(); +// const expect = chai.expect; -describe('etherscan#events', () => { - describe('_convertResponseToLogEntry', () => { - it('converts EventsResponseResult to LogEntry', () => { - const input: EventsResponseResult = { - address: '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', - topics: [ - '0x82af639571738f4ebd4268fb0363d8957ebe1bbb9e78dba5ebd69eed39b154f0', - '0x00000000000000000000000067032ef7be8fa07c4335d0134099db0f3875e930', - '0x0000000000000000000000000000000000000000000000000000000000000000', - ], - data: '0x00000000000000000000000000000000000000000000000000000165f2d3f94d', - blockNumber: '0x61127b', - timeStamp: '0x5ba2878e', - gasPrice: '0x1a13b8600', - gasUsed: '0xd9dc', - logIndex: '0x63', - transactionHash: '0xa3f71931ddab6e758b9d1755b2715b376759f49f23fff60755f7e073367d61b5', - transactionIndex: '0x35', - }; - const expected: LogEntry = { - logIndex: 99, - transactionIndex: 53, - transactionHash: input.transactionHash, - blockHash: '', - blockNumber: 6361723, - address: input.address, - data: input.data, - topics: input.topics, - }; - const actual = _convertResponseToLogEntry(input); - expect(actual).deep.equal(expected); - }); - }); - describe('_decodeLogEntry', () => { - it('decodes LogEntry into LogWithDecodedArgs', () => { - const input: LogEntry = { - logIndex: 96, - transactionIndex: 52, - transactionHash: '0x02b59043e9b38b430c8c66abe67ab4a9e5509def8f8552b54231e88db1839831', - blockHash: '', - blockNumber: 6361723, - address: '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', - data: - '0x00000000000000000000000067032ef7be8fa07c4335d0134099db0f3875e93000000000000000000000000067032ef7be8fa07c4335d0134099db0f3875e930000000000000000000000000000000000000000000000000000000174876e8000000000000000000000000000000000000000000000000000000000013ab668000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000', - topics: [ - '0x0bcc4c97732e47d9946f229edb95f5b6323f601300e4690de719993f3c371129', - '0x0000000000000000000000003f7f832abb3be28442c0e48b7222e02b322c78f3', - '0x000000000000000000000000a258b39954cef5cb142fd567a46cddb31a670124', - '0x523404b4e6f847d9aefcf5be024be396449b4635590291fd7a28a8c940843858', - ], - }; - const expected: LogWithDecodedArgs = { - ...input, - event: 'Fill', - args: { - makerAddress: '0x3f7f832abb3be28442c0e48b7222e02b322c78f3', - feeRecipientAddress: '0xa258b39954cef5cb142fd567a46cddb31a670124', - takerAddress: '0x67032ef7be8fa07c4335d0134099db0f3875e930', - senderAddress: '0x67032ef7be8fa07c4335d0134099db0f3875e930', - makerAssetFilledAmount: new BigNumber('100000000000'), - takerAssetFilledAmount: new BigNumber('330000000'), - makerFeePaid: new BigNumber('0'), - takerFeePaid: new BigNumber('0'), - orderHash: '0x523404b4e6f847d9aefcf5be024be396449b4635590291fd7a28a8c940843858', - makerAssetData: '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498', - takerAssetData: '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - }, - }; - const actual = _decodeLogEntry(artifacts.Exchange.compilerOutput.abi, input); - expect(actual).deep.equal(expected); - }); - }); -}); +// describe('etherscan#events', () => { +// describe('_convertResponseToLogEntry', () => { +// it('converts EventsResponseResult to LogEntry', () => { +// const input: EventsResponseResult = { +// address: '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', +// topics: [ +// '0x82af639571738f4ebd4268fb0363d8957ebe1bbb9e78dba5ebd69eed39b154f0', +// '0x00000000000000000000000067032ef7be8fa07c4335d0134099db0f3875e930', +// '0x0000000000000000000000000000000000000000000000000000000000000000', +// ], +// data: '0x00000000000000000000000000000000000000000000000000000165f2d3f94d', +// blockNumber: '0x61127b', +// timeStamp: '0x5ba2878e', +// gasPrice: '0x1a13b8600', +// gasUsed: '0xd9dc', +// logIndex: '0x63', +// transactionHash: '0xa3f71931ddab6e758b9d1755b2715b376759f49f23fff60755f7e073367d61b5', +// transactionIndex: '0x35', +// }; +// const expected: LogEntry = { +// logIndex: 99, +// transactionIndex: 53, +// transactionHash: input.transactionHash, +// blockHash: '', +// blockNumber: 6361723, +// address: input.address, +// data: input.data, +// topics: input.topics, +// }; +// const actual = _convertResponseToLogEntry(input); +// expect(actual).deep.equal(expected); +// }); +// }); +// describe('_decodeLogEntry', () => { +// it('decodes LogEntry into LogWithDecodedArgs', () => { +// const input: LogEntry = { +// logIndex: 96, +// transactionIndex: 52, +// transactionHash: '0x02b59043e9b38b430c8c66abe67ab4a9e5509def8f8552b54231e88db1839831', +// blockHash: '', +// blockNumber: 6361723, +// address: '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', +// data: +// '0x00000000000000000000000067032ef7be8fa07c4335d0134099db0f3875e93000000000000000000000000067032ef7be8fa07c4335d0134099db0f3875e930000000000000000000000000000000000000000000000000000000174876e8000000000000000000000000000000000000000000000000000000000013ab668000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000', +// topics: [ +// '0x0bcc4c97732e47d9946f229edb95f5b6323f601300e4690de719993f3c371129', +// '0x0000000000000000000000003f7f832abb3be28442c0e48b7222e02b322c78f3', +// '0x000000000000000000000000a258b39954cef5cb142fd567a46cddb31a670124', +// '0x523404b4e6f847d9aefcf5be024be396449b4635590291fd7a28a8c940843858', +// ], +// }; +// const expected: LogWithDecodedArgs = { +// ...input, +// event: 'Fill', +// args: { +// makerAddress: '0x3f7f832abb3be28442c0e48b7222e02b322c78f3', +// feeRecipientAddress: '0xa258b39954cef5cb142fd567a46cddb31a670124', +// takerAddress: '0x67032ef7be8fa07c4335d0134099db0f3875e930', +// senderAddress: '0x67032ef7be8fa07c4335d0134099db0f3875e930', +// makerAssetFilledAmount: new BigNumber('100000000000'), +// takerAssetFilledAmount: new BigNumber('330000000'), +// makerFeePaid: new BigNumber('0'), +// takerFeePaid: new BigNumber('0'), +// orderHash: '0x523404b4e6f847d9aefcf5be024be396449b4635590291fd7a28a8c940843858', +// makerAssetData: '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498', +// takerAssetData: '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', +// }, +// }; +// const actual = _decodeLogEntry(artifacts.Exchange.compilerOutput.abi, input); +// expect(actual).deep.equal(expected); +// }); +// }); +// }); -- cgit v1.2.3 From 2207f09ce2c8b595f75fce6387256a64c64b23c3 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Tue, 25 Sep 2018 14:41:45 -0700 Subject: Make event parsing more functional and less class-based --- .../events/event_handlers/base_event_handler.ts | 34 ---------- .../event_handlers/exchange_event_handler.ts | 76 ---------------------- .../src/data_types/events/exchange_events.ts | 75 +++++++++++++++++++++ packages/pipeline/src/index.ts | 17 ++--- 4 files changed, 79 insertions(+), 123 deletions(-) delete mode 100644 packages/pipeline/src/data_types/events/event_handlers/base_event_handler.ts delete mode 100644 packages/pipeline/src/data_types/events/event_handlers/exchange_event_handler.ts create mode 100644 packages/pipeline/src/data_types/events/exchange_events.ts diff --git a/packages/pipeline/src/data_types/events/event_handlers/base_event_handler.ts b/packages/pipeline/src/data_types/events/event_handlers/base_event_handler.ts deleted file mode 100644 index 59331fc4f..000000000 --- a/packages/pipeline/src/data_types/events/event_handlers/base_event_handler.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { AbiDefinition, BlockParam, BlockParamLiteral, LogEntry } from 'ethereum-types'; -import * as R from 'ramda'; -import { BaseEntity } from 'typeorm'; - -import { Etherscan } from '../../../data_sources/etherscan'; -import { convertResponseToLogEntry } from '../event_utils'; - -export abstract class BaseEventHandler { - protected _abi: AbiDefinition[]; - protected _address: string; - protected _etherscan: Etherscan; - constructor(abi: AbiDefinition[], address: string, etherscan: Etherscan) { - this._abi = abi; - this._address = address; - this._etherscan = etherscan; - } - public abstract convertLogEntryToEventEntity(logEntry: LogEntry): EntityType; - - public async getEventsAsync( - fromBlock: BlockParam = BlockParamLiteral.Earliest, - toBlock: BlockParam = BlockParamLiteral.Latest, - ): Promise { - const rawEventsResponse = await this._etherscan.getContractEventsAsync(this._address, fromBlock, toBlock); - const logEntries = R.map(convertResponseToLogEntry, rawEventsResponse.result); - // Note(albrow): Imperative for loop is required here because we can't - // bind convertLogEntryToEventEntity without having a specific instance - // of a sub-class. - const result = []; - for (const logEntry of logEntries) { - result.push(this.convertLogEntryToEventEntity(logEntry)); - } - return result; - } -} diff --git a/packages/pipeline/src/data_types/events/event_handlers/exchange_event_handler.ts b/packages/pipeline/src/data_types/events/event_handlers/exchange_event_handler.ts deleted file mode 100644 index 38ff20595..000000000 --- a/packages/pipeline/src/data_types/events/event_handlers/exchange_event_handler.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { ExchangeEventArgs, ExchangeFillEventArgs } from '@0xproject/contract-wrappers'; -import { assetDataUtils } from '@0xproject/order-utils'; -import { AssetProxyId, ERC721AssetData } from '@0xproject/types'; -import { AbiDecoder, BigNumber } from '@0xproject/utils'; -import { AbiDefinition, LogEntry, LogWithDecodedArgs } from 'ethereum-types'; -import * as R from 'ramda'; - -import { ExchangeFillEvent } from '../../../entities/ExchangeFillEvent'; -import { decodeLogEntry } from '../event_utils'; - -import { BaseEventHandler } from './base_event_handler'; - -// TODO(albrow): Union with other exchange event entity types -export type ExchangeEventEntity = ExchangeFillEvent; - -export class ExchangeEventHandler extends BaseEventHandler { - public convertLogEntryToEventEntity(logEntry: LogEntry): ExchangeEventEntity { - const decodedLogEntry = decodeLogEntry(this._abi, logEntry); - return _convertToEntity(decodedLogEntry); - } -} - -export function _convertToEntity(eventLog: LogWithDecodedArgs): ExchangeEventEntity { - switch (eventLog.event) { - case 'Fill': - return _convertToExchangeFillEvent(eventLog as LogWithDecodedArgs); - default: - return new ExchangeFillEvent(); - // throw new Error('unexpected eventLog.event type: ' + eventLog.event); - } -} - -export function _convertToExchangeFillEvent(eventLog: LogWithDecodedArgs): ExchangeFillEvent { - const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.makerAssetData); - const makerAssetType = makerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; - const takerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.takerAssetData); - const takerAssetType = takerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; - const exchangeFillEvent = new ExchangeFillEvent(); - exchangeFillEvent.logIndex = eventLog.logIndex as number; - exchangeFillEvent.address = eventLog.address as string; - exchangeFillEvent.rawData = eventLog.data as string; - exchangeFillEvent.blockNumber = eventLog.blockNumber as number; - exchangeFillEvent.makerAddress = eventLog.args.makerAddress.toString(); - exchangeFillEvent.takerAddress = eventLog.args.takerAddress.toString(); - exchangeFillEvent.feeRecepientAddress = eventLog.args.feeRecipientAddress; - exchangeFillEvent.senderAddress = eventLog.args.senderAddress; - exchangeFillEvent.makerAssetFilledAmount = eventLog.args.makerAssetFilledAmount.toString(); - exchangeFillEvent.takerAssetFilledAmount = eventLog.args.takerAssetFilledAmount.toString(); - exchangeFillEvent.makerFeePaid = eventLog.args.makerFeePaid.toString(); - exchangeFillEvent.takerFeePaid = eventLog.args.takerFeePaid.toString(); - exchangeFillEvent.orderHash = eventLog.args.orderHash; - exchangeFillEvent.rawMakerAssetData = eventLog.args.makerAssetData; - exchangeFillEvent.makerAssetType = makerAssetType; - exchangeFillEvent.makerAssetProxyId = makerAssetData.assetProxyId; - exchangeFillEvent.makerTokenAddress = makerAssetData.tokenAddress; - exchangeFillEvent.makerTokenId = bigNumbertoStringOrNull((makerAssetData as ERC721AssetData).tokenId); - exchangeFillEvent.rawTakerAssetData = eventLog.args.takerAssetData; - exchangeFillEvent.takerAssetType = takerAssetType; - exchangeFillEvent.takerAssetProxyId = takerAssetData.assetProxyId; - exchangeFillEvent.takerTokenAddress = takerAssetData.tokenAddress; - exchangeFillEvent.takerTokenId = bigNumbertoStringOrNull((takerAssetData as ERC721AssetData).tokenId); - return exchangeFillEvent; -} - -function bigNumbertoStringOrNull(n: BigNumber): string | null { - if (n == null) { - return null; - } - return n.toString(); -} - -function filterEventLogs( - eventLogs: Array>, -): Array> { - return R.filter(eventLog => eventLog.event === 'Fill', eventLogs); -} diff --git a/packages/pipeline/src/data_types/events/exchange_events.ts b/packages/pipeline/src/data_types/events/exchange_events.ts new file mode 100644 index 000000000..fb5341b58 --- /dev/null +++ b/packages/pipeline/src/data_types/events/exchange_events.ts @@ -0,0 +1,75 @@ +import { ExchangeEventArgs, ExchangeFillEventArgs } from '@0xproject/contract-wrappers'; +import { assetDataUtils } from '@0xproject/order-utils'; +import { AssetProxyId, ERC721AssetData } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; +import { LogWithDecodedArgs } from 'ethereum-types'; +import * as R from 'ramda'; + +import { artifacts } from '../../artifacts'; +import { EventsResponse } from '../../data_sources/etherscan'; +import { ExchangeFillEvent } from '../../entities/ExchangeFillEvent'; + +import { convertResponseToLogEntry, decodeLogEntry } from './event_utils'; + +// TODO(albrow): Union with other exchange event entity types +export type ExchangeEventEntity = ExchangeFillEvent; + +const exchangeContractAbi = artifacts.Exchange.compilerOutput.abi; + +export function parseExchangeEvents(rawEventsResponse: EventsResponse): ExchangeEventEntity[] { + const logEntries = R.map(convertResponseToLogEntry, rawEventsResponse.result); + const decodedLogEntries = R.map( + eventResponse => decodeLogEntry(exchangeContractAbi, eventResponse), + logEntries, + ); + const filteredLogEntries = R.filter(logEntry => R.contains(logEntry.event, ['Fill']), decodedLogEntries); + return R.map(_convertToEntity, filteredLogEntries); +} + +export function _convertToEntity(eventLog: LogWithDecodedArgs): ExchangeEventEntity { + switch (eventLog.event) { + case 'Fill': + return _convertToExchangeFillEvent(eventLog as LogWithDecodedArgs); + default: + throw new Error('unexpected eventLog.event type: ' + eventLog.event); + } +} + +export function _convertToExchangeFillEvent(eventLog: LogWithDecodedArgs): ExchangeFillEvent { + const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.makerAssetData); + const makerAssetType = makerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; + const takerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.takerAssetData); + const takerAssetType = takerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; + const exchangeFillEvent = new ExchangeFillEvent(); + exchangeFillEvent.logIndex = eventLog.logIndex as number; + exchangeFillEvent.address = eventLog.address as string; + exchangeFillEvent.rawData = eventLog.data as string; + exchangeFillEvent.blockNumber = eventLog.blockNumber as number; + exchangeFillEvent.makerAddress = eventLog.args.makerAddress.toString(); + exchangeFillEvent.takerAddress = eventLog.args.takerAddress.toString(); + exchangeFillEvent.feeRecepientAddress = eventLog.args.feeRecipientAddress; + exchangeFillEvent.senderAddress = eventLog.args.senderAddress; + exchangeFillEvent.makerAssetFilledAmount = eventLog.args.makerAssetFilledAmount.toString(); + exchangeFillEvent.takerAssetFilledAmount = eventLog.args.takerAssetFilledAmount.toString(); + exchangeFillEvent.makerFeePaid = eventLog.args.makerFeePaid.toString(); + exchangeFillEvent.takerFeePaid = eventLog.args.takerFeePaid.toString(); + exchangeFillEvent.orderHash = eventLog.args.orderHash; + exchangeFillEvent.rawMakerAssetData = eventLog.args.makerAssetData; + exchangeFillEvent.makerAssetType = makerAssetType; + exchangeFillEvent.makerAssetProxyId = makerAssetData.assetProxyId; + exchangeFillEvent.makerTokenAddress = makerAssetData.tokenAddress; + exchangeFillEvent.makerTokenId = bigNumbertoStringOrNull((makerAssetData as ERC721AssetData).tokenId); + exchangeFillEvent.rawTakerAssetData = eventLog.args.takerAssetData; + exchangeFillEvent.takerAssetType = takerAssetType; + exchangeFillEvent.takerAssetProxyId = takerAssetData.assetProxyId; + exchangeFillEvent.takerTokenAddress = takerAssetData.tokenAddress; + exchangeFillEvent.takerTokenId = bigNumbertoStringOrNull((takerAssetData as ERC721AssetData).tokenId); + return exchangeFillEvent; +} + +function bigNumbertoStringOrNull(n: BigNumber): string | null { + if (n == null) { + return null; + } + return n.toString(); +} diff --git a/packages/pipeline/src/index.ts b/packages/pipeline/src/index.ts index e71a6ae4c..c01ff57b4 100644 --- a/packages/pipeline/src/index.ts +++ b/packages/pipeline/src/index.ts @@ -1,12 +1,11 @@ import 'reflect-metadata'; import { createConnection } from 'typeorm'; -import { artifacts } from './artifacts'; import { Etherscan } from './data_sources/etherscan'; import { ExchangeFillEvent } from './entities/ExchangeFillEvent'; import { config } from './ormconfig'; -import { ExchangeEventHandler } from './data_types/events/event_handlers/exchange_event_handler'; +import { parseExchangeEvents } from './data_types/events/exchange_events'; const etherscan = new Etherscan(process.env.ETHERSCAN_API_KEY as string); const EXCHANGE_ADDRESS = '0x4f833a24e1f95d70f028921e27040ca56e09ab0b'; @@ -15,18 +14,10 @@ const EXCHANGE_ADDRESS = '0x4f833a24e1f95d70f028921e27040ca56e09ab0b'; const connection = await createConnection(config); const repository = connection.getRepository(ExchangeFillEvent); console.log(`found ${await repository.count()} existing fill events`); - const exchangeEventHandler = new ExchangeEventHandler( - artifacts.Exchange.compilerOutput.abi, - EXCHANGE_ADDRESS, - etherscan, - ); - const events = await exchangeEventHandler.getEventsAsync(); - console.log(JSON.stringify(events, null, 2)); + const rawEvents = await etherscan.getContractEventsAsync(EXCHANGE_ADDRESS); + const events = parseExchangeEvents(rawEvents); for (const event of events) { - // TODO(albrow): remove this check once we can parse all Exchange events - if (event.address != null) { - await event.save(); - } + await event.save(); } console.log(`now ${await repository.count()} total fill events`); })(); -- cgit v1.2.3 From 60bc27c616c411cf67bf89e669fb293135359685 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Tue, 25 Sep 2018 15:03:49 -0700 Subject: Implement scraping and parsing exchange cancel events --- .../src/data_types/events/exchange_events.ts | 41 +++++++++++++++++++--- .../pipeline/src/entities/ExchangeCancelEvent.ts | 32 +++++++++++++++++ .../pipeline/src/entities/ExchangeFillEvent.ts | 6 ++-- packages/pipeline/src/index.ts | 11 ++++-- packages/pipeline/src/types.ts | 1 + 5 files changed, 81 insertions(+), 10 deletions(-) create mode 100644 packages/pipeline/src/entities/ExchangeCancelEvent.ts create mode 100644 packages/pipeline/src/types.ts diff --git a/packages/pipeline/src/data_types/events/exchange_events.ts b/packages/pipeline/src/data_types/events/exchange_events.ts index fb5341b58..3938f3a69 100644 --- a/packages/pipeline/src/data_types/events/exchange_events.ts +++ b/packages/pipeline/src/data_types/events/exchange_events.ts @@ -1,4 +1,4 @@ -import { ExchangeEventArgs, ExchangeFillEventArgs } from '@0xproject/contract-wrappers'; +import { ExchangeCancelEventArgs, ExchangeEventArgs, ExchangeFillEventArgs } from '@0xproject/contract-wrappers'; import { assetDataUtils } from '@0xproject/order-utils'; import { AssetProxyId, ERC721AssetData } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; @@ -7,12 +7,12 @@ import * as R from 'ramda'; import { artifacts } from '../../artifacts'; import { EventsResponse } from '../../data_sources/etherscan'; +import { ExchangeCancelEvent } from '../../entities/ExchangeCancelEvent'; import { ExchangeFillEvent } from '../../entities/ExchangeFillEvent'; import { convertResponseToLogEntry, decodeLogEntry } from './event_utils'; -// TODO(albrow): Union with other exchange event entity types -export type ExchangeEventEntity = ExchangeFillEvent; +export type ExchangeEventEntity = ExchangeFillEvent | ExchangeCancelEvent; const exchangeContractAbi = artifacts.Exchange.compilerOutput.abi; @@ -22,7 +22,7 @@ export function parseExchangeEvents(rawEventsResponse: EventsResponse): Exchange eventResponse => decodeLogEntry(exchangeContractAbi, eventResponse), logEntries, ); - const filteredLogEntries = R.filter(logEntry => R.contains(logEntry.event, ['Fill']), decodedLogEntries); + const filteredLogEntries = R.filter(logEntry => R.contains(logEntry.event, ['Fill', 'Cancel']), decodedLogEntries); return R.map(_convertToEntity, filteredLogEntries); } @@ -30,6 +30,8 @@ export function _convertToEntity(eventLog: LogWithDecodedArgs switch (eventLog.event) { case 'Fill': return _convertToExchangeFillEvent(eventLog as LogWithDecodedArgs); + case 'Cancel': + return _convertToExchangeCancelEvent(eventLog as LogWithDecodedArgs); default: throw new Error('unexpected eventLog.event type: ' + eventLog.event); } @@ -67,6 +69,37 @@ export function _convertToExchangeFillEvent(eventLog: LogWithDecodedArgs, +): ExchangeCancelEvent { + const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.makerAssetData); + const makerAssetType = makerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; + const takerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.takerAssetData); + const takerAssetType = takerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; + const exchangeCancelEvent = new ExchangeCancelEvent(); + exchangeCancelEvent.logIndex = eventLog.logIndex as number; + exchangeCancelEvent.address = eventLog.address as string; + exchangeCancelEvent.rawData = eventLog.data as string; + exchangeCancelEvent.blockNumber = eventLog.blockNumber as number; + exchangeCancelEvent.makerAddress = eventLog.args.makerAddress.toString(); + exchangeCancelEvent.takerAddress = + eventLog.args.takerAddress == null ? null : eventLog.args.takerAddress.toString(); + exchangeCancelEvent.feeRecepientAddress = eventLog.args.feeRecipientAddress; + exchangeCancelEvent.senderAddress = eventLog.args.senderAddress; + exchangeCancelEvent.orderHash = eventLog.args.orderHash; + exchangeCancelEvent.rawMakerAssetData = eventLog.args.makerAssetData; + exchangeCancelEvent.makerAssetType = makerAssetType; + exchangeCancelEvent.makerAssetProxyId = makerAssetData.assetProxyId; + exchangeCancelEvent.makerTokenAddress = makerAssetData.tokenAddress; + exchangeCancelEvent.makerTokenId = bigNumbertoStringOrNull((makerAssetData as ERC721AssetData).tokenId); + exchangeCancelEvent.rawTakerAssetData = eventLog.args.takerAssetData; + exchangeCancelEvent.takerAssetType = takerAssetType; + exchangeCancelEvent.takerAssetProxyId = takerAssetData.assetProxyId; + exchangeCancelEvent.takerTokenAddress = takerAssetData.tokenAddress; + exchangeCancelEvent.takerTokenId = bigNumbertoStringOrNull((takerAssetData as ERC721AssetData).tokenId); + return exchangeCancelEvent; +} + function bigNumbertoStringOrNull(n: BigNumber): string | null { if (n == null) { return null; diff --git a/packages/pipeline/src/entities/ExchangeCancelEvent.ts b/packages/pipeline/src/entities/ExchangeCancelEvent.ts new file mode 100644 index 000000000..8e21518d3 --- /dev/null +++ b/packages/pipeline/src/entities/ExchangeCancelEvent.ts @@ -0,0 +1,32 @@ +import { BaseEntity, Column, Entity, PrimaryColumn } from 'typeorm'; + +import { AssetType } from '../types'; + +@Entity() +export class ExchangeCancelEvent extends BaseEntity { + @PrimaryColumn() public logIndex!: number; + + @Column() public address!: string; + @Column() public rawData!: string; + @Column() public blockNumber!: number; + + @Column() public makerAddress!: string; + @Column({ nullable: true, type: String }) + public takerAddress!: string; + @Column() public feeRecepientAddress!: string; + @Column() public senderAddress!: string; + @Column() public orderHash!: string; + @Column() public rawMakerAssetData!: string; + @Column() public makerAssetType!: AssetType; + @Column() public makerAssetProxyId!: string; + @Column() public makerTokenAddress!: string; + @Column({ nullable: true, type: String }) + public makerTokenId!: string | null; + @Column() public rawTakerAssetData!: string; + @Column() public takerAssetType!: AssetType; + @Column() public takerAssetProxyId!: string; + @Column() public takerTokenAddress!: string; + @Column({ nullable: true, type: String }) + public takerTokenId!: string | null; + // TODO(albrow): Include topics? +} diff --git a/packages/pipeline/src/entities/ExchangeFillEvent.ts b/packages/pipeline/src/entities/ExchangeFillEvent.ts index a7e817240..e66bd64e3 100644 --- a/packages/pipeline/src/entities/ExchangeFillEvent.ts +++ b/packages/pipeline/src/entities/ExchangeFillEvent.ts @@ -1,6 +1,6 @@ import { BaseEntity, Column, Entity, PrimaryColumn } from 'typeorm'; -export type ExchangeFillEventAssetType = 'erc20' | 'erc721'; +import { AssetType } from '../types'; @Entity() export class ExchangeFillEvent extends BaseEntity { @@ -20,13 +20,13 @@ export class ExchangeFillEvent extends BaseEntity { @Column() public takerFeePaid!: string; @Column() public orderHash!: string; @Column() public rawMakerAssetData!: string; - @Column() public makerAssetType!: ExchangeFillEventAssetType; + @Column() public makerAssetType!: AssetType; @Column() public makerAssetProxyId!: string; @Column() public makerTokenAddress!: string; @Column({ nullable: true, type: String }) public makerTokenId!: string | null; @Column() public rawTakerAssetData!: string; - @Column() public takerAssetType!: ExchangeFillEventAssetType; + @Column() public takerAssetType!: AssetType; @Column() public takerAssetProxyId!: string; @Column() public takerTokenAddress!: string; @Column({ nullable: true, type: String }) diff --git a/packages/pipeline/src/index.ts b/packages/pipeline/src/index.ts index c01ff57b4..c68df95bf 100644 --- a/packages/pipeline/src/index.ts +++ b/packages/pipeline/src/index.ts @@ -2,6 +2,7 @@ import 'reflect-metadata'; import { createConnection } from 'typeorm'; import { Etherscan } from './data_sources/etherscan'; +import { ExchangeCancelEvent } from './entities/ExchangeCancelEvent'; import { ExchangeFillEvent } from './entities/ExchangeFillEvent'; import { config } from './ormconfig'; @@ -12,12 +13,16 @@ const EXCHANGE_ADDRESS = '0x4f833a24e1f95d70f028921e27040ca56e09ab0b'; (async () => { const connection = await createConnection(config); - const repository = connection.getRepository(ExchangeFillEvent); - console.log(`found ${await repository.count()} existing fill events`); + const fillRepository = connection.getRepository(ExchangeFillEvent); + const cancelRepository = connection.getRepository(ExchangeCancelEvent); + console.log(`found ${await fillRepository.count()} existing fill events`); + console.log(`found ${await cancelRepository.count()} existing cancel events`); const rawEvents = await etherscan.getContractEventsAsync(EXCHANGE_ADDRESS); const events = parseExchangeEvents(rawEvents); + console.log(`got ${events.length} parsed events`); for (const event of events) { await event.save(); } - console.log(`now ${await repository.count()} total fill events`); + console.log(`now ${await fillRepository.count()} total fill events`); + console.log(`now ${await cancelRepository.count()} total cancel events`); })(); diff --git a/packages/pipeline/src/types.ts b/packages/pipeline/src/types.ts new file mode 100644 index 000000000..a12d37895 --- /dev/null +++ b/packages/pipeline/src/types.ts @@ -0,0 +1 @@ +export type AssetType = 'erc20' | 'erc721'; -- cgit v1.2.3 From 40610830da70ae406227e80c700efea11042ba68 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Tue, 25 Sep 2018 15:31:41 -0700 Subject: Use multi-primary keys for event and filter null logIndexes --- packages/pipeline/src/data_types/events/exchange_events.ts | 13 +++++++++++-- packages/pipeline/src/entities/ExchangeCancelEvent.ts | 2 +- packages/pipeline/src/entities/ExchangeFillEvent.ts | 2 +- packages/pipeline/src/index.ts | 4 ++-- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/packages/pipeline/src/data_types/events/exchange_events.ts b/packages/pipeline/src/data_types/events/exchange_events.ts index 3938f3a69..cc4332f6f 100644 --- a/packages/pipeline/src/data_types/events/exchange_events.ts +++ b/packages/pipeline/src/data_types/events/exchange_events.ts @@ -2,7 +2,7 @@ import { ExchangeCancelEventArgs, ExchangeEventArgs, ExchangeFillEventArgs } fro import { assetDataUtils } from '@0xproject/order-utils'; import { AssetProxyId, ERC721AssetData } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; -import { LogWithDecodedArgs } from 'ethereum-types'; +import { LogEntry, LogWithDecodedArgs } from 'ethereum-types'; import * as R from 'ramda'; import { artifacts } from '../../artifacts'; @@ -22,10 +22,19 @@ export function parseExchangeEvents(rawEventsResponse: EventsResponse): Exchange eventResponse => decodeLogEntry(exchangeContractAbi, eventResponse), logEntries, ); - const filteredLogEntries = R.filter(logEntry => R.contains(logEntry.event, ['Fill', 'Cancel']), decodedLogEntries); + const filteredLogEntries = R.filter(shouldIncludeLogEntry, decodedLogEntries); return R.map(_convertToEntity, filteredLogEntries); } +export function shouldIncludeLogEntry(logEntry: LogWithDecodedArgs): boolean { + if (!R.contains(logEntry.event, ['Fill', 'Cancel'])) { + return false; + } else if (logEntry.logIndex == null || isNaN(logEntry.logIndex)) { + return false; + } + return true; +} + export function _convertToEntity(eventLog: LogWithDecodedArgs): ExchangeEventEntity { switch (eventLog.event) { case 'Fill': diff --git a/packages/pipeline/src/entities/ExchangeCancelEvent.ts b/packages/pipeline/src/entities/ExchangeCancelEvent.ts index 8e21518d3..c925a4b01 100644 --- a/packages/pipeline/src/entities/ExchangeCancelEvent.ts +++ b/packages/pipeline/src/entities/ExchangeCancelEvent.ts @@ -5,10 +5,10 @@ import { AssetType } from '../types'; @Entity() export class ExchangeCancelEvent extends BaseEntity { @PrimaryColumn() public logIndex!: number; + @PrimaryColumn() public blockNumber!: number; @Column() public address!: string; @Column() public rawData!: string; - @Column() public blockNumber!: number; @Column() public makerAddress!: string; @Column({ nullable: true, type: String }) diff --git a/packages/pipeline/src/entities/ExchangeFillEvent.ts b/packages/pipeline/src/entities/ExchangeFillEvent.ts index e66bd64e3..9ac8eb349 100644 --- a/packages/pipeline/src/entities/ExchangeFillEvent.ts +++ b/packages/pipeline/src/entities/ExchangeFillEvent.ts @@ -5,10 +5,10 @@ import { AssetType } from '../types'; @Entity() export class ExchangeFillEvent extends BaseEntity { @PrimaryColumn() public logIndex!: number; + @PrimaryColumn() public blockNumber!: number; @Column() public address!: string; @Column() public rawData!: string; - @Column() public blockNumber!: number; @Column() public makerAddress!: string; @Column() public takerAddress!: string; diff --git a/packages/pipeline/src/index.ts b/packages/pipeline/src/index.ts index c68df95bf..67b1f6fb2 100644 --- a/packages/pipeline/src/index.ts +++ b/packages/pipeline/src/index.ts @@ -1,13 +1,13 @@ +import * as R from 'ramda'; import 'reflect-metadata'; import { createConnection } from 'typeorm'; import { Etherscan } from './data_sources/etherscan'; +import { parseExchangeEvents } from './data_types/events/exchange_events'; import { ExchangeCancelEvent } from './entities/ExchangeCancelEvent'; import { ExchangeFillEvent } from './entities/ExchangeFillEvent'; import { config } from './ormconfig'; -import { parseExchangeEvents } from './data_types/events/exchange_events'; - const etherscan = new Etherscan(process.env.ETHERSCAN_API_KEY as string); const EXCHANGE_ADDRESS = '0x4f833a24e1f95d70f028921e27040ca56e09ab0b'; -- cgit v1.2.3 From b0de2a388fc7079bc45c7886b002420783021416 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Tue, 25 Sep 2018 16:03:54 -0700 Subject: Implement scraping and parsing exchange CancelUpTo events --- .../src/data_types/events/exchange_events.ts | 30 +++++++++++++++++++--- .../src/entities/ExchangeCancelUpToEvent.ts | 15 +++++++++++ packages/pipeline/src/index.ts | 9 +++---- 3 files changed, 45 insertions(+), 9 deletions(-) create mode 100644 packages/pipeline/src/entities/ExchangeCancelUpToEvent.ts diff --git a/packages/pipeline/src/data_types/events/exchange_events.ts b/packages/pipeline/src/data_types/events/exchange_events.ts index cc4332f6f..763a141ea 100644 --- a/packages/pipeline/src/data_types/events/exchange_events.ts +++ b/packages/pipeline/src/data_types/events/exchange_events.ts @@ -1,18 +1,24 @@ -import { ExchangeCancelEventArgs, ExchangeEventArgs, ExchangeFillEventArgs } from '@0xproject/contract-wrappers'; +import { + ExchangeCancelEventArgs, + ExchangeCancelUpToEventArgs, + ExchangeEventArgs, + ExchangeFillEventArgs, +} from '@0xproject/contract-wrappers'; import { assetDataUtils } from '@0xproject/order-utils'; import { AssetProxyId, ERC721AssetData } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; -import { LogEntry, LogWithDecodedArgs } from 'ethereum-types'; +import { LogWithDecodedArgs } from 'ethereum-types'; import * as R from 'ramda'; import { artifacts } from '../../artifacts'; import { EventsResponse } from '../../data_sources/etherscan'; import { ExchangeCancelEvent } from '../../entities/ExchangeCancelEvent'; +import { ExchangeCancelUpToEvent } from '../../entities/ExchangeCancelUpToEvent'; import { ExchangeFillEvent } from '../../entities/ExchangeFillEvent'; import { convertResponseToLogEntry, decodeLogEntry } from './event_utils'; -export type ExchangeEventEntity = ExchangeFillEvent | ExchangeCancelEvent; +export type ExchangeEventEntity = ExchangeFillEvent | ExchangeCancelEvent | ExchangeCancelUpToEvent; const exchangeContractAbi = artifacts.Exchange.compilerOutput.abi; @@ -27,7 +33,7 @@ export function parseExchangeEvents(rawEventsResponse: EventsResponse): Exchange } export function shouldIncludeLogEntry(logEntry: LogWithDecodedArgs): boolean { - if (!R.contains(logEntry.event, ['Fill', 'Cancel'])) { + if (!R.contains(logEntry.event, ['Fill', 'Cancel', 'CancelUpTo'])) { return false; } else if (logEntry.logIndex == null || isNaN(logEntry.logIndex)) { return false; @@ -41,6 +47,8 @@ export function _convertToEntity(eventLog: LogWithDecodedArgs return _convertToExchangeFillEvent(eventLog as LogWithDecodedArgs); case 'Cancel': return _convertToExchangeCancelEvent(eventLog as LogWithDecodedArgs); + case 'CancelUpTo': + return _convertToExchangeCancelUpToEvent(eventLog as LogWithDecodedArgs); default: throw new Error('unexpected eventLog.event type: ' + eventLog.event); } @@ -109,6 +117,20 @@ export function _convertToExchangeCancelEvent( return exchangeCancelEvent; } +export function _convertToExchangeCancelUpToEvent( + eventLog: LogWithDecodedArgs, +): ExchangeCancelUpToEvent { + const exchangeCancelUpToEvent = new ExchangeCancelUpToEvent(); + exchangeCancelUpToEvent.logIndex = eventLog.logIndex as number; + exchangeCancelUpToEvent.address = eventLog.address as string; + exchangeCancelUpToEvent.rawData = eventLog.data as string; + exchangeCancelUpToEvent.blockNumber = eventLog.blockNumber as number; + exchangeCancelUpToEvent.makerAddress = eventLog.args.makerAddress.toString(); + exchangeCancelUpToEvent.senderAddress = eventLog.args.senderAddress.toString(); + exchangeCancelUpToEvent.orderEpoch = eventLog.args.orderEpoch.toString(); + return exchangeCancelUpToEvent; +} + function bigNumbertoStringOrNull(n: BigNumber): string | null { if (n == null) { return null; diff --git a/packages/pipeline/src/entities/ExchangeCancelUpToEvent.ts b/packages/pipeline/src/entities/ExchangeCancelUpToEvent.ts new file mode 100644 index 000000000..3021f7394 --- /dev/null +++ b/packages/pipeline/src/entities/ExchangeCancelUpToEvent.ts @@ -0,0 +1,15 @@ +import { BaseEntity, Column, Entity, PrimaryColumn } from 'typeorm'; + +@Entity() +export class ExchangeCancelUpToEvent extends BaseEntity { + @PrimaryColumn() public logIndex!: number; + @PrimaryColumn() public blockNumber!: number; + + @Column() public address!: string; + @Column() public rawData!: string; + + @Column() public makerAddress!: string; + @Column() public senderAddress!: string; + @Column() public orderEpoch!: string; + // TODO(albrow): Include topics? +} diff --git a/packages/pipeline/src/index.ts b/packages/pipeline/src/index.ts index 67b1f6fb2..1a010b6e5 100644 --- a/packages/pipeline/src/index.ts +++ b/packages/pipeline/src/index.ts @@ -5,6 +5,7 @@ import { createConnection } from 'typeorm'; import { Etherscan } from './data_sources/etherscan'; import { parseExchangeEvents } from './data_types/events/exchange_events'; import { ExchangeCancelEvent } from './entities/ExchangeCancelEvent'; +import { ExchangeCancelUpToEvent } from './entities/ExchangeCancelUpToEvent'; import { ExchangeFillEvent } from './entities/ExchangeFillEvent'; import { config } from './ormconfig'; @@ -15,14 +16,12 @@ const EXCHANGE_ADDRESS = '0x4f833a24e1f95d70f028921e27040ca56e09ab0b'; const connection = await createConnection(config); const fillRepository = connection.getRepository(ExchangeFillEvent); const cancelRepository = connection.getRepository(ExchangeCancelEvent); - console.log(`found ${await fillRepository.count()} existing fill events`); - console.log(`found ${await cancelRepository.count()} existing cancel events`); + const cancelUpToRepository = connection.getRepository(ExchangeCancelUpToEvent); + console.log(`found ${(await fillRepository.count()) + (await cancelRepository.count())} existing events`); const rawEvents = await etherscan.getContractEventsAsync(EXCHANGE_ADDRESS); const events = parseExchangeEvents(rawEvents); - console.log(`got ${events.length} parsed events`); for (const event of events) { await event.save(); } - console.log(`now ${await fillRepository.count()} total fill events`); - console.log(`now ${await cancelRepository.count()} total cancel events`); + console.log(`now there are ${(await fillRepository.count()) + (await cancelRepository.count())} total events`); })(); -- cgit v1.2.3 From 71a015f2aa32fedd3d3c5e9d5a3fa4eb272b2852 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Tue, 25 Sep 2018 16:06:52 -0700 Subject: For event entities, rename address -> contractAddress and make it a primary key --- packages/pipeline/src/data_types/events/exchange_events.ts | 12 ++++++------ packages/pipeline/src/entities/ExchangeCancelEvent.ts | 2 +- packages/pipeline/src/entities/ExchangeCancelUpToEvent.ts | 2 +- packages/pipeline/src/entities/ExchangeFillEvent.ts | 2 +- packages/pipeline/src/index.ts | 12 ++++++++++-- 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/packages/pipeline/src/data_types/events/exchange_events.ts b/packages/pipeline/src/data_types/events/exchange_events.ts index 763a141ea..2d4059a0e 100644 --- a/packages/pipeline/src/data_types/events/exchange_events.ts +++ b/packages/pipeline/src/data_types/events/exchange_events.ts @@ -60,10 +60,10 @@ export function _convertToExchangeFillEvent(eventLog: LogWithDecodedArgs, ): ExchangeCancelUpToEvent { const exchangeCancelUpToEvent = new ExchangeCancelUpToEvent(); + exchangeCancelUpToEvent.contractAddress = eventLog.address as string; + exchangeCancelUpToEvent.blockNumber = eventLog.blockNumber as number; exchangeCancelUpToEvent.logIndex = eventLog.logIndex as number; - exchangeCancelUpToEvent.address = eventLog.address as string; exchangeCancelUpToEvent.rawData = eventLog.data as string; - exchangeCancelUpToEvent.blockNumber = eventLog.blockNumber as number; exchangeCancelUpToEvent.makerAddress = eventLog.args.makerAddress.toString(); exchangeCancelUpToEvent.senderAddress = eventLog.args.senderAddress.toString(); exchangeCancelUpToEvent.orderEpoch = eventLog.args.orderEpoch.toString(); diff --git a/packages/pipeline/src/entities/ExchangeCancelEvent.ts b/packages/pipeline/src/entities/ExchangeCancelEvent.ts index c925a4b01..d0188c2f5 100644 --- a/packages/pipeline/src/entities/ExchangeCancelEvent.ts +++ b/packages/pipeline/src/entities/ExchangeCancelEvent.ts @@ -4,10 +4,10 @@ import { AssetType } from '../types'; @Entity() export class ExchangeCancelEvent extends BaseEntity { + @PrimaryColumn() public contractAddress!: string; @PrimaryColumn() public logIndex!: number; @PrimaryColumn() public blockNumber!: number; - @Column() public address!: string; @Column() public rawData!: string; @Column() public makerAddress!: string; diff --git a/packages/pipeline/src/entities/ExchangeCancelUpToEvent.ts b/packages/pipeline/src/entities/ExchangeCancelUpToEvent.ts index 3021f7394..03a02b069 100644 --- a/packages/pipeline/src/entities/ExchangeCancelUpToEvent.ts +++ b/packages/pipeline/src/entities/ExchangeCancelUpToEvent.ts @@ -2,10 +2,10 @@ import { BaseEntity, Column, Entity, PrimaryColumn } from 'typeorm'; @Entity() export class ExchangeCancelUpToEvent extends BaseEntity { + @PrimaryColumn() public contractAddress!: string; @PrimaryColumn() public logIndex!: number; @PrimaryColumn() public blockNumber!: number; - @Column() public address!: string; @Column() public rawData!: string; @Column() public makerAddress!: string; diff --git a/packages/pipeline/src/entities/ExchangeFillEvent.ts b/packages/pipeline/src/entities/ExchangeFillEvent.ts index 9ac8eb349..abd73191a 100644 --- a/packages/pipeline/src/entities/ExchangeFillEvent.ts +++ b/packages/pipeline/src/entities/ExchangeFillEvent.ts @@ -4,10 +4,10 @@ import { AssetType } from '../types'; @Entity() export class ExchangeFillEvent extends BaseEntity { + @PrimaryColumn() public contractAddress!: string; @PrimaryColumn() public logIndex!: number; @PrimaryColumn() public blockNumber!: number; - @Column() public address!: string; @Column() public rawData!: string; @Column() public makerAddress!: string; diff --git a/packages/pipeline/src/index.ts b/packages/pipeline/src/index.ts index 1a010b6e5..07ab1d991 100644 --- a/packages/pipeline/src/index.ts +++ b/packages/pipeline/src/index.ts @@ -17,11 +17,19 @@ const EXCHANGE_ADDRESS = '0x4f833a24e1f95d70f028921e27040ca56e09ab0b'; const fillRepository = connection.getRepository(ExchangeFillEvent); const cancelRepository = connection.getRepository(ExchangeCancelEvent); const cancelUpToRepository = connection.getRepository(ExchangeCancelUpToEvent); - console.log(`found ${(await fillRepository.count()) + (await cancelRepository.count())} existing events`); + console.log( + `found ${(await fillRepository.count()) + + (await cancelRepository.count()) + + (await cancelUpToRepository.count())} existing events`, + ); const rawEvents = await etherscan.getContractEventsAsync(EXCHANGE_ADDRESS); const events = parseExchangeEvents(rawEvents); for (const event of events) { await event.save(); } - console.log(`now there are ${(await fillRepository.count()) + (await cancelRepository.count())} total events`); + console.log( + `now there are ${(await fillRepository.count()) + + (await cancelRepository.count()) + + (await cancelUpToRepository.count())} total events`, + ); })(); -- cgit v1.2.3 From 9ab55ccec0478e65f9d605aa2da7ed1fa13e01ac Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Wed, 26 Sep 2018 16:42:48 -0700 Subject: Add preliminary support for scraping orders from SRA endpoints (no pagination, only RR support for now) --- packages/pipeline/package.json | 1 + .../src/data_types/events/exchange_events.ts | 9 +- .../pipeline/src/data_types/sra_order/index.ts | 54 ++++++++ .../pipeline/src/entities/ExchangeCancelEvent.ts | 2 + .../pipeline/src/entities/ExchangeFillEvent.ts | 2 + packages/pipeline/src/entities/SraOrder.ts | 40 ++++++ packages/pipeline/src/index.ts | 30 ++++- packages/pipeline/src/utils/index.ts | 8 ++ yarn.lock | 139 ++++++++++++++++++--- 9 files changed, 255 insertions(+), 30 deletions(-) create mode 100644 packages/pipeline/src/data_types/sra_order/index.ts create mode 100644 packages/pipeline/src/entities/SraOrder.ts create mode 100644 packages/pipeline/src/utils/index.ts diff --git a/packages/pipeline/package.json b/packages/pipeline/package.json index 6670938f2..c01b7f448 100644 --- a/packages/pipeline/package.json +++ b/packages/pipeline/package.json @@ -39,6 +39,7 @@ "typescript": "3.0.1" }, "dependencies": { + "@0xproject/connect": "^2.0.4", "@0xproject/contract-wrappers": "^1.0.1", "@0xproject/order-utils": "^1.0.2", "@0xproject/subproviders": "^2.0.2", diff --git a/packages/pipeline/src/data_types/events/exchange_events.ts b/packages/pipeline/src/data_types/events/exchange_events.ts index 2d4059a0e..a406d27fb 100644 --- a/packages/pipeline/src/data_types/events/exchange_events.ts +++ b/packages/pipeline/src/data_types/events/exchange_events.ts @@ -6,7 +6,6 @@ import { } from '@0xproject/contract-wrappers'; import { assetDataUtils } from '@0xproject/order-utils'; import { AssetProxyId, ERC721AssetData } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; import { LogWithDecodedArgs } from 'ethereum-types'; import * as R from 'ramda'; @@ -15,6 +14,7 @@ import { EventsResponse } from '../../data_sources/etherscan'; import { ExchangeCancelEvent } from '../../entities/ExchangeCancelEvent'; import { ExchangeCancelUpToEvent } from '../../entities/ExchangeCancelUpToEvent'; import { ExchangeFillEvent } from '../../entities/ExchangeFillEvent'; +import { bigNumbertoStringOrNull } from '../../utils'; import { convertResponseToLogEntry, decodeLogEntry } from './event_utils'; @@ -130,10 +130,3 @@ export function _convertToExchangeCancelUpToEvent( exchangeCancelUpToEvent.orderEpoch = eventLog.args.orderEpoch.toString(); return exchangeCancelUpToEvent; } - -function bigNumbertoStringOrNull(n: BigNumber): string | null { - if (n == null) { - return null; - } - return n.toString(); -} diff --git a/packages/pipeline/src/data_types/sra_order/index.ts b/packages/pipeline/src/data_types/sra_order/index.ts new file mode 100644 index 000000000..b6364415c --- /dev/null +++ b/packages/pipeline/src/data_types/sra_order/index.ts @@ -0,0 +1,54 @@ +import { APIOrder, OrdersResponse } from '@0xproject/connect'; +import { assetDataUtils, orderHashUtils } from '@0xproject/order-utils'; +import { AssetProxyId, ERC721AssetData } from '@0xproject/types'; +import * as R from 'ramda'; + +import { SraOrder } from '../../entities/SraOrder'; +import { bigNumbertoStringOrNull } from '../../utils'; + +export function parseSraOrders(rawOrdersResponse: OrdersResponse): SraOrder[] { + return R.map(_convertToEntity, rawOrdersResponse.records); +} + +export function _convertToEntity(apiOrder: APIOrder): SraOrder { + // TODO(albrow): refactor out common asset data decoding code. + const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(apiOrder.order.makerAssetData); + const makerAssetType = makerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; + const takerAssetData = assetDataUtils.decodeAssetDataOrThrow(apiOrder.order.takerAssetData); + const takerAssetType = takerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; + + const sraOrder = new SraOrder(); + sraOrder.exchangeAddress = apiOrder.order.exchangeAddress; + sraOrder.orderHashHex = orderHashUtils.getOrderHashHex(apiOrder.order); + + // TODO(albrow): Set these fields to the correct values upstack. + sraOrder.lastUpdatedTimestamp = Date.now(); + sraOrder.firstSeenTimestamp = Date.now(); + + sraOrder.makerAddress = apiOrder.order.makerAddress; + sraOrder.takerAddress = apiOrder.order.takerAddress; + sraOrder.feeRecipientAddress = apiOrder.order.feeRecipientAddress; + sraOrder.senderAddress = apiOrder.order.senderAddress; + sraOrder.makerAssetAmount = apiOrder.order.makerAssetAmount.toString(); + sraOrder.takerAssetAmount = apiOrder.order.takerAssetAmount.toString(); + sraOrder.makerFee = apiOrder.order.makerFee.toString(); + sraOrder.takerFee = apiOrder.order.takerFee.toString(); + sraOrder.expirationTimeSeconds = apiOrder.order.expirationTimeSeconds.toString(); + sraOrder.salt = apiOrder.order.salt.toString(); + sraOrder.signature = apiOrder.order.signature; + + sraOrder.rawMakerAssetData = apiOrder.order.makerAssetData; + sraOrder.makerAssetType = makerAssetType; + sraOrder.makerAssetProxyId = makerAssetData.assetProxyId; + sraOrder.makerTokenAddress = makerAssetData.tokenAddress; + sraOrder.makerTokenId = bigNumbertoStringOrNull((makerAssetData as ERC721AssetData).tokenId); + sraOrder.rawTakerAssetData = apiOrder.order.takerAssetData; + sraOrder.takerAssetType = takerAssetType; + sraOrder.takerAssetProxyId = takerAssetData.assetProxyId; + sraOrder.takerTokenAddress = takerAssetData.tokenAddress; + sraOrder.takerTokenId = bigNumbertoStringOrNull((takerAssetData as ERC721AssetData).tokenId); + + sraOrder.metaDataJson = JSON.stringify(apiOrder.metaData); + + return sraOrder; +} diff --git a/packages/pipeline/src/entities/ExchangeCancelEvent.ts b/packages/pipeline/src/entities/ExchangeCancelEvent.ts index d0188c2f5..7010ab9f2 100644 --- a/packages/pipeline/src/entities/ExchangeCancelEvent.ts +++ b/packages/pipeline/src/entities/ExchangeCancelEvent.ts @@ -16,6 +16,7 @@ export class ExchangeCancelEvent extends BaseEntity { @Column() public feeRecepientAddress!: string; @Column() public senderAddress!: string; @Column() public orderHash!: string; + @Column() public rawMakerAssetData!: string; @Column() public makerAssetType!: AssetType; @Column() public makerAssetProxyId!: string; @@ -28,5 +29,6 @@ export class ExchangeCancelEvent extends BaseEntity { @Column() public takerTokenAddress!: string; @Column({ nullable: true, type: String }) public takerTokenId!: string | null; + // TODO(albrow): Include topics? } diff --git a/packages/pipeline/src/entities/ExchangeFillEvent.ts b/packages/pipeline/src/entities/ExchangeFillEvent.ts index abd73191a..5eafa7449 100644 --- a/packages/pipeline/src/entities/ExchangeFillEvent.ts +++ b/packages/pipeline/src/entities/ExchangeFillEvent.ts @@ -19,6 +19,7 @@ export class ExchangeFillEvent extends BaseEntity { @Column() public makerFeePaid!: string; @Column() public takerFeePaid!: string; @Column() public orderHash!: string; + @Column() public rawMakerAssetData!: string; @Column() public makerAssetType!: AssetType; @Column() public makerAssetProxyId!: string; @@ -31,5 +32,6 @@ export class ExchangeFillEvent extends BaseEntity { @Column() public takerTokenAddress!: string; @Column({ nullable: true, type: String }) public takerTokenId!: string | null; + // TODO(albrow): Include topics? } diff --git a/packages/pipeline/src/entities/SraOrder.ts b/packages/pipeline/src/entities/SraOrder.ts new file mode 100644 index 000000000..c9a1f926d --- /dev/null +++ b/packages/pipeline/src/entities/SraOrder.ts @@ -0,0 +1,40 @@ +import { BaseEntity, Column, Entity, PrimaryColumn } from 'typeorm'; + +import { AssetType } from '../types'; + +@Entity() +export class SraOrder extends BaseEntity { + @PrimaryColumn() public exchangeAddress!: string; + @PrimaryColumn() public orderHashHex!: string; + + @Column() public lastUpdatedTimestamp!: number; + @Column() public firstSeenTimestamp!: number; + + @Column() public makerAddress!: string; + @Column() public takerAddress!: string; + @Column() public feeRecipientAddress!: string; + @Column() public senderAddress!: string; + @Column() public makerAssetAmount!: string; + @Column() public takerAssetAmount!: string; + @Column() public makerFee!: string; + @Column() public takerFee!: string; + @Column() public expirationTimeSeconds!: string; + @Column() public salt!: string; + @Column() public signature!: string; + + @Column() public rawMakerAssetData!: string; + @Column() public makerAssetType!: AssetType; + @Column() public makerAssetProxyId!: string; + @Column() public makerTokenAddress!: string; + @Column({ nullable: true, type: String }) + public makerTokenId!: string | null; + @Column() public rawTakerAssetData!: string; + @Column() public takerAssetType!: AssetType; + @Column() public takerAssetProxyId!: string; + @Column() public takerTokenAddress!: string; + @Column({ nullable: true, type: String }) + public takerTokenId!: string | null; + + // TODO(albrow): Make this optional? + @Column() public metaDataJson!: string; +} diff --git a/packages/pipeline/src/index.ts b/packages/pipeline/src/index.ts index 07ab1d991..3e8434e3d 100644 --- a/packages/pipeline/src/index.ts +++ b/packages/pipeline/src/index.ts @@ -1,19 +1,28 @@ -import * as R from 'ramda'; +import { HttpClient } from '@0xproject/connect'; import 'reflect-metadata'; -import { createConnection } from 'typeorm'; +import { Connection, createConnection } from 'typeorm'; import { Etherscan } from './data_sources/etherscan'; import { parseExchangeEvents } from './data_types/events/exchange_events'; +import { parseSraOrders } from './data_types/sra_order'; import { ExchangeCancelEvent } from './entities/ExchangeCancelEvent'; import { ExchangeCancelUpToEvent } from './entities/ExchangeCancelUpToEvent'; import { ExchangeFillEvent } from './entities/ExchangeFillEvent'; +import { SraOrder } from './entities/SraOrder'; import { config } from './ormconfig'; const etherscan = new Etherscan(process.env.ETHERSCAN_API_KEY as string); const EXCHANGE_ADDRESS = '0x4f833a24e1f95d70f028921e27040ca56e09ab0b'; +let connection: Connection; + (async () => { - const connection = await createConnection(config); + connection = await createConnection(config); + await getExchangeEventsAsync(); + await getSraOrdersAsync(); +})(); + +async function getExchangeEventsAsync(): Promise { const fillRepository = connection.getRepository(ExchangeFillEvent); const cancelRepository = connection.getRepository(ExchangeCancelEvent); const cancelUpToRepository = connection.getRepository(ExchangeCancelUpToEvent); @@ -32,4 +41,17 @@ const EXCHANGE_ADDRESS = '0x4f833a24e1f95d70f028921e27040ca56e09ab0b'; (await cancelRepository.count()) + (await cancelUpToRepository.count())} total events`, ); -})(); +} + +async function getSraOrdersAsync(): Promise { + const orderRepository = connection.getRepository(SraOrder); + console.log(`found ${await orderRepository.count()} existing orders`); + + const connect = new HttpClient('https://api.radarrelay.com/0x/v2'); + const rawOrders = await connect.getOrdersAsync(); + const orders = parseSraOrders(rawOrders); + for (const order of orders) { + order.save(); + } + console.log(`now there are ${await orderRepository.count()} total orders`); +} diff --git a/packages/pipeline/src/utils/index.ts b/packages/pipeline/src/utils/index.ts new file mode 100644 index 000000000..8fe7f9685 --- /dev/null +++ b/packages/pipeline/src/utils/index.ts @@ -0,0 +1,8 @@ +import { BigNumber } from '@0xproject/utils'; + +export function bigNumbertoStringOrNull(n: BigNumber): string | null { + if (n == null) { + return null; + } + return n.toString(); +} diff --git a/yarn.lock b/yarn.lock index 9e293e586..38318d0a7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -472,7 +472,17 @@ npmlog "^4.1.2" write-file-atomic "^2.3.0" -"@0xproject/assert@^1.0.10", "@0xproject/assert@^1.0.13": +"@0xproject/assert@^0.2.14": + version "0.2.14" + resolved "https://registry.yarnpkg.com/@0xproject/assert/-/assert-0.2.14.tgz#7d5a373fedc8eb482716b730f4dddf3ef33bfa29" + dependencies: + "@0xproject/json-schemas" "^0.8.3" + "@0xproject/typescript-typings" "^0.4.3" + "@0xproject/utils" "^0.7.3" + lodash "4.17.10" + valid-url "1.0.9" + +"@0xproject/assert@^1.0.10", "@0xproject/assert@^1.0.11", "@0xproject/assert@^1.0.13": version "1.0.13" resolved "https://registry.yarnpkg.com/@0xproject/assert/-/assert-1.0.13.tgz#e370ccce08933dd2a970bdd02b92e59c65dd75d4" dependencies: @@ -504,6 +514,22 @@ ethers "4.0.0-beta.14" lodash "^4.17.5" +"@0xproject/connect@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@0xproject/connect/-/connect-2.0.4.tgz#d61cd382edbb80120c8efce91dc85d2c668a5c5e" + dependencies: + "@0xproject/assert" "^1.0.11" + "@0xproject/json-schemas" "^1.0.4" + "@0xproject/order-utils" "^1.0.5" + "@0xproject/types" "^1.1.1" + "@0xproject/typescript-typings" "^2.0.2" + "@0xproject/utils" "^1.0.11" + lodash "^4.17.5" + query-string "^5.0.1" + sinon "^4.0.0" + uuid "^3.3.2" + websocket "^1.0.25" + "@0xproject/contract-wrappers@^1.0.1": version "1.0.5" resolved "https://registry.yarnpkg.com/@0xproject/contract-wrappers/-/contract-wrappers-1.0.5.tgz#5a522b3fd8404b8c42169d5be7e3b6903a7b1356" @@ -539,7 +565,16 @@ ethers "4.0.0-beta.14" lodash "^4.17.5" -"@0xproject/json-schemas@^1.0.3", "@0xproject/json-schemas@^1.0.7": +"@0xproject/json-schemas@^0.8.3": + version "0.8.3" + resolved "https://registry.yarnpkg.com/@0xproject/json-schemas/-/json-schemas-0.8.3.tgz#455e6219a6bd05e990392165192a983a9ab89f26" + dependencies: + "@0xproject/typescript-typings" "^0.4.3" + "@types/node" "9.6.0" + jsonschema "1.2.2" + lodash.values "4.3.0" + +"@0xproject/json-schemas@^1.0.3", "@0xproject/json-schemas@^1.0.4", "@0xproject/json-schemas@^1.0.7": version "1.0.7" resolved "https://registry.yarnpkg.com/@0xproject/json-schemas/-/json-schemas-1.0.7.tgz#64b5692a1bcc5938ce2da01fc2f8aecd72ec35be" dependencies: @@ -554,7 +589,7 @@ dependencies: npm-registry-client "7.0.9" -"@0xproject/order-utils@^1.0.4", "@0xproject/order-utils@^1.0.7": +"@0xproject/order-utils@^1.0.2", "@0xproject/order-utils@^1.0.4", "@0xproject/order-utils@^1.0.5", "@0xproject/order-utils@^1.0.7": version "1.0.7" resolved "https://registry.yarnpkg.com/@0xproject/order-utils/-/order-utils-1.0.7.tgz#475cd5f1a11dc7816847abb4d3fd17bbaf32bf4f" dependencies: @@ -612,7 +647,14 @@ tslint-react "^3.2.0" tsutils "3.0.0" -"@0xproject/types@^1.1.0", "@0xproject/types@^1.1.1", "@0xproject/types@^1.1.4": +"@0xproject/types@^0.8.2": + version "0.8.2" + resolved "https://registry.yarnpkg.com/@0xproject/types/-/types-0.8.2.tgz#6f936b73bfb6f017b5102002d97da0881da92d1b" + dependencies: + "@types/node" "9.6.0" + bignumber.js "~4.1.0" + +"@0xproject/types@^1.0.1", "@0xproject/types@^1.1.0", "@0xproject/types@^1.1.1", "@0xproject/types@^1.1.4": version "1.1.4" resolved "https://registry.yarnpkg.com/@0xproject/types/-/types-1.1.4.tgz#3ffd65e670d6a21dab19ee0ffd5fad0056291b8e" dependencies: @@ -620,6 +662,24 @@ bignumber.js "~4.1.0" ethereum-types "^1.0.11" +"@0xproject/typescript-typings@^0.4.3": + version "0.4.3" + resolved "https://registry.yarnpkg.com/@0xproject/typescript-typings/-/typescript-typings-0.4.3.tgz#f99f939a43f2764ad7182fcd78a71212a1d76d96" + dependencies: + "@0xproject/types" "^0.8.2" + bignumber.js "~4.1.0" + ethereum-types "^0.0.2" + +"@0xproject/typescript-typings@^1.0.3": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@0xproject/typescript-typings/-/typescript-typings-1.0.5.tgz#a808443419f26a7b90d63d1afd3efbfb48644184" + dependencies: + "@types/bn.js" "^4.11.0" + "@types/react" "*" + bignumber.js "~4.1.0" + ethereum-types "^1.0.5" + popper.js "1.14.3" + "@0xproject/typescript-typings@^2.0.1", "@0xproject/typescript-typings@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@0xproject/typescript-typings/-/typescript-typings-2.0.2.tgz#1812f64e341f1d24c09b8b5a951cbde0e5fff9c2" @@ -640,7 +700,21 @@ ethereum-types "^1.0.11" popper.js "1.14.3" -"@0xproject/utils@^1.0.10", "@0xproject/utils@^1.0.11", "@0xproject/utils@^1.0.8": +"@0xproject/utils@^0.7.3": + version "0.7.3" + resolved "https://registry.yarnpkg.com/@0xproject/utils/-/utils-0.7.3.tgz#ffa7c6da9bf0dd3e13694f185dcfc48a8981ff05" + dependencies: + "@0xproject/typescript-typings" "^0.4.3" + "@types/node" "9.6.0" + bignumber.js "~4.1.0" + ethereum-types "^0.0.2" + ethereumjs-util "^5.1.1" + ethers "3.0.22" + js-sha3 "0.7.0" + lodash "4.17.10" + web3 "0.20.6" + +"@0xproject/utils@^1.0.10", "@0xproject/utils@^1.0.11", "@0xproject/utils@^1.0.4", "@0xproject/utils@^1.0.8": version "1.0.11" resolved "https://registry.yarnpkg.com/@0xproject/utils/-/utils-1.0.11.tgz#5b53e7d9d4dbe68e219049218c9db04e97c37429" dependencies: @@ -1645,6 +1719,10 @@ version "9.6.5" resolved "https://registry.yarnpkg.com/@types/node/-/node-9.6.5.tgz#ee700810fdf49ac1c399fc5980b7559b3e5a381d" +"@types/node@9.6.0": + version "9.6.0" + resolved "http://registry.npmjs.org/@types/node/-/node-9.6.0.tgz#d3480ee666df9784b1001a1872a2f6ccefb6c2d7" + "@types/node@^10.3.2": version "10.9.4" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.9.4.tgz#0f4cb2dc7c1de6096055357f70179043c33e9897" @@ -1653,6 +1731,10 @@ version "10.5.7" resolved "https://registry.npmjs.org/@types/node/-/node-10.5.7.tgz#960d9feb3ade2233bcc9843c918d740b4f78a7cf" +"@types/node@^8.0.53": + version "8.10.36" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.36.tgz#eac05d576fbcd0b4ea3c912dc58c20475c08d9e4" + "@types/numeral@^0.0.22": version "0.0.22" resolved "https://registry.yarnpkg.com/@types/numeral/-/numeral-0.0.22.tgz#86bef1f0a2d743afdc2ef3168d45f2905e1a0b93" @@ -6076,6 +6158,13 @@ ethereum-common@^0.0.18: version "0.0.18" resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.0.18.tgz#2fdc3576f232903358976eb39da783213ff9523f" +ethereum-types@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/ethereum-types/-/ethereum-types-0.0.2.tgz#6ef6faf46a24697cbf66b6c8a0ecf2095ce58c38" + dependencies: + "@types/node" "^8.0.53" + bignumber.js "~4.1.0" + ethereumjs-abi@0.6.5, ethereumjs-abi@^0.6.5: version "0.6.5" resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.5.tgz#5a637ef16ab43473fa72a29ad90871405b3f5241" @@ -6225,9 +6314,9 @@ ethereumjs-wallet@0.6.0: utf8 "^2.1.1" uuid "^2.0.1" -ethers@0xproject/ethers.js#eip-838-reasons, ethers@3.0.22: - version "3.0.18" - resolved "https://codeload.github.com/0xproject/ethers.js/tar.gz/b91342bd200d142af0165d6befddf783c8ae8447" +ethers@3.0.22: + version "3.0.22" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-3.0.22.tgz#7fab1ea16521705837aa43c15831877b2716b436" dependencies: aes-js "3.0.0" bn.js "^4.4.0" @@ -9180,14 +9269,14 @@ js-sha3@0.5.7: version "0.5.7" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" +js-sha3@0.7.0, js-sha3@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.7.0.tgz#0a5c57b36f79882573b2d84051f8bb85dd1bd63a" + js-sha3@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.3.1.tgz#86122802142f0828502a0d1dee1d95e253bb0243" -js-sha3@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.7.0.tgz#0a5c57b36f79882573b2d84051f8bb85dd1bd63a" - js-tokens@^3.0.0, js-tokens@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" @@ -9371,6 +9460,10 @@ jsonschema@*, jsonschema@1.2.4, jsonschema@^1.2.0: version "1.2.4" resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.2.4.tgz#a46bac5d3506a254465bc548876e267c6d0d6464" +jsonschema@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.2.2.tgz#83ab9c63d65bf4d596f91d81195e78772f6452bc" + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -10061,7 +10154,7 @@ lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" -lodash.values@^4.3.0: +lodash.values@4.3.0, lodash.values@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.values/-/lodash.values-4.3.0.tgz#a3a6c2b0ebecc5c2cba1c17e6e620fe81b53d347" @@ -10071,6 +10164,10 @@ lodash.words@^3.0.0: dependencies: lodash._root "^3.0.0" +lodash@4.17.10, lodash@^4.17.10: + version "4.17.10" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" + lodash@=4.17.4: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -10087,10 +10184,6 @@ lodash@^4.14.0, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1, lodash@^4.3.0, lo version "4.17.5" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" -lodash@^4.17.10: - version "4.17.10" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" - lodash@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/lodash/-/lodash-1.0.2.tgz#8f57560c83b59fc270bd3d561b690043430e2551" @@ -16091,7 +16184,7 @@ v8flags@^2.0.2: dependencies: user-home "^1.1.1" -valid-url@^1.0.9: +valid-url@1.0.9, valid-url@^1.0.9: version "1.0.9" resolved "https://registry.yarnpkg.com/valid-url/-/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200" @@ -16538,6 +16631,16 @@ web3@0.20.2: xhr2 "*" xmlhttprequest "*" +web3@0.20.6: + version "0.20.6" + resolved "http://registry.npmjs.org/web3/-/web3-0.20.6.tgz#3e97306ae024fb24e10a3d75c884302562215120" + dependencies: + bignumber.js "git+https://github.com/frozeman/bignumber.js-nolookahead.git" + crypto-js "^3.1.4" + utf8 "^2.1.1" + xhr2 "*" + xmlhttprequest "*" + web3@^0.18.0: version "0.18.4" resolved "https://registry.yarnpkg.com/web3/-/web3-0.18.4.tgz#81ec1784145491f2eaa8955b31c06049e07c5e7d" -- cgit v1.2.3 From dd093daf916ee3d6ce0021be0a245990e0cd2c56 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Thu, 27 Sep 2018 13:53:37 -0700 Subject: Add soruceUrl to SraOrder entity --- packages/pipeline/src/entities/SraOrder.ts | 1 + packages/pipeline/src/index.ts | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/pipeline/src/entities/SraOrder.ts b/packages/pipeline/src/entities/SraOrder.ts index c9a1f926d..e4987df57 100644 --- a/packages/pipeline/src/entities/SraOrder.ts +++ b/packages/pipeline/src/entities/SraOrder.ts @@ -7,6 +7,7 @@ export class SraOrder extends BaseEntity { @PrimaryColumn() public exchangeAddress!: string; @PrimaryColumn() public orderHashHex!: string; + @Column() public sourceUrl!: string; @Column() public lastUpdatedTimestamp!: number; @Column() public firstSeenTimestamp!: number; diff --git a/packages/pipeline/src/index.ts b/packages/pipeline/src/index.ts index 3e8434e3d..c442c7970 100644 --- a/packages/pipeline/src/index.ts +++ b/packages/pipeline/src/index.ts @@ -47,10 +47,12 @@ async function getSraOrdersAsync(): Promise { const orderRepository = connection.getRepository(SraOrder); console.log(`found ${await orderRepository.count()} existing orders`); - const connect = new HttpClient('https://api.radarrelay.com/0x/v2'); + const sraUrl = 'https://api.radarrelay.com/0x/v2'; + const connect = new HttpClient(sraUrl); const rawOrders = await connect.getOrdersAsync(); const orders = parseSraOrders(rawOrders); for (const order of orders) { + order.sourceUrl = sraUrl; order.save(); } console.log(`now there are ${await orderRepository.count()} total orders`); -- cgit v1.2.3 From 6aca4e8272a9b4e1d098db7ba1b8d4081153d6d9 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Thu, 27 Sep 2018 17:04:55 -0700 Subject: Re-add missing tests --- .../test/data-sources/etherscan/events_test.ts | 90 ---------------------- .../test/data_types/events/event_utils_test.ts | 87 +++++++++++++++++++++ .../test/data_types/events/exchange_events_test.ts | 77 ++++++++++++++++++ 3 files changed, 164 insertions(+), 90 deletions(-) delete mode 100644 packages/pipeline/test/data-sources/etherscan/events_test.ts create mode 100644 packages/pipeline/test/data_types/events/event_utils_test.ts create mode 100644 packages/pipeline/test/data_types/events/exchange_events_test.ts diff --git a/packages/pipeline/test/data-sources/etherscan/events_test.ts b/packages/pipeline/test/data-sources/etherscan/events_test.ts deleted file mode 100644 index b0f8c7d1c..000000000 --- a/packages/pipeline/test/data-sources/etherscan/events_test.ts +++ /dev/null @@ -1,90 +0,0 @@ -// import { BigNumber } from '@0xproject/utils'; -// import * as chai from 'chai'; -// import { DecodedLogArgs, LogEntry, LogWithDecodedArgs } from 'ethereum-types'; -// import 'mocha'; - -// import { artifacts } from '../../../src/artifacts'; - -// import { -// _convertResponseToLogEntry, -// _decodeLogEntry, -// EventsResponseResult, -// } from '../../../src/data-sources/etherscan/events'; -// import { chaiSetup } from '../../utils/chai_setup'; - -// chaiSetup.configure(); -// const expect = chai.expect; - -// describe('etherscan#events', () => { -// describe('_convertResponseToLogEntry', () => { -// it('converts EventsResponseResult to LogEntry', () => { -// const input: EventsResponseResult = { -// address: '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', -// topics: [ -// '0x82af639571738f4ebd4268fb0363d8957ebe1bbb9e78dba5ebd69eed39b154f0', -// '0x00000000000000000000000067032ef7be8fa07c4335d0134099db0f3875e930', -// '0x0000000000000000000000000000000000000000000000000000000000000000', -// ], -// data: '0x00000000000000000000000000000000000000000000000000000165f2d3f94d', -// blockNumber: '0x61127b', -// timeStamp: '0x5ba2878e', -// gasPrice: '0x1a13b8600', -// gasUsed: '0xd9dc', -// logIndex: '0x63', -// transactionHash: '0xa3f71931ddab6e758b9d1755b2715b376759f49f23fff60755f7e073367d61b5', -// transactionIndex: '0x35', -// }; -// const expected: LogEntry = { -// logIndex: 99, -// transactionIndex: 53, -// transactionHash: input.transactionHash, -// blockHash: '', -// blockNumber: 6361723, -// address: input.address, -// data: input.data, -// topics: input.topics, -// }; -// const actual = _convertResponseToLogEntry(input); -// expect(actual).deep.equal(expected); -// }); -// }); -// describe('_decodeLogEntry', () => { -// it('decodes LogEntry into LogWithDecodedArgs', () => { -// const input: LogEntry = { -// logIndex: 96, -// transactionIndex: 52, -// transactionHash: '0x02b59043e9b38b430c8c66abe67ab4a9e5509def8f8552b54231e88db1839831', -// blockHash: '', -// blockNumber: 6361723, -// address: '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', -// data: -// '0x00000000000000000000000067032ef7be8fa07c4335d0134099db0f3875e93000000000000000000000000067032ef7be8fa07c4335d0134099db0f3875e930000000000000000000000000000000000000000000000000000000174876e8000000000000000000000000000000000000000000000000000000000013ab668000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000', -// topics: [ -// '0x0bcc4c97732e47d9946f229edb95f5b6323f601300e4690de719993f3c371129', -// '0x0000000000000000000000003f7f832abb3be28442c0e48b7222e02b322c78f3', -// '0x000000000000000000000000a258b39954cef5cb142fd567a46cddb31a670124', -// '0x523404b4e6f847d9aefcf5be024be396449b4635590291fd7a28a8c940843858', -// ], -// }; -// const expected: LogWithDecodedArgs = { -// ...input, -// event: 'Fill', -// args: { -// makerAddress: '0x3f7f832abb3be28442c0e48b7222e02b322c78f3', -// feeRecipientAddress: '0xa258b39954cef5cb142fd567a46cddb31a670124', -// takerAddress: '0x67032ef7be8fa07c4335d0134099db0f3875e930', -// senderAddress: '0x67032ef7be8fa07c4335d0134099db0f3875e930', -// makerAssetFilledAmount: new BigNumber('100000000000'), -// takerAssetFilledAmount: new BigNumber('330000000'), -// makerFeePaid: new BigNumber('0'), -// takerFeePaid: new BigNumber('0'), -// orderHash: '0x523404b4e6f847d9aefcf5be024be396449b4635590291fd7a28a8c940843858', -// makerAssetData: '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498', -// takerAssetData: '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', -// }, -// }; -// const actual = _decodeLogEntry(artifacts.Exchange.compilerOutput.abi, input); -// expect(actual).deep.equal(expected); -// }); -// }); -// }); diff --git a/packages/pipeline/test/data_types/events/event_utils_test.ts b/packages/pipeline/test/data_types/events/event_utils_test.ts new file mode 100644 index 000000000..91a73c47d --- /dev/null +++ b/packages/pipeline/test/data_types/events/event_utils_test.ts @@ -0,0 +1,87 @@ +import { BigNumber } from '@0xproject/utils'; +import * as chai from 'chai'; +import { DecodedLogArgs, LogEntry, LogWithDecodedArgs } from 'ethereum-types'; +import 'mocha'; + +import { artifacts } from '../../../src/artifacts'; + +import { EventsResponseResult } from '../../../src/data_sources/etherscan'; +import { convertResponseToLogEntry, decodeLogEntry } from '../../../src/data_types/events/event_utils'; +import { chaiSetup } from '../../utils/chai_setup'; + +chaiSetup.configure(); +const expect = chai.expect; + +describe('event_utils', () => { + describe('convertResponseToLogEntry', () => { + it('converts EventsResponseResult to LogEntry', () => { + const input: EventsResponseResult = { + address: '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', + topics: [ + '0x82af639571738f4ebd4268fb0363d8957ebe1bbb9e78dba5ebd69eed39b154f0', + '0x00000000000000000000000067032ef7be8fa07c4335d0134099db0f3875e930', + '0x0000000000000000000000000000000000000000000000000000000000000000', + ], + data: '0x00000000000000000000000000000000000000000000000000000165f2d3f94d', + blockNumber: '0x61127b', + timeStamp: '0x5ba2878e', + gasPrice: '0x1a13b8600', + gasUsed: '0xd9dc', + logIndex: '0x63', + transactionHash: '0xa3f71931ddab6e758b9d1755b2715b376759f49f23fff60755f7e073367d61b5', + transactionIndex: '0x35', + }; + const expected: LogEntry = { + logIndex: 99, + transactionIndex: 53, + transactionHash: input.transactionHash, + blockHash: '', + blockNumber: 6361723, + address: input.address, + data: input.data, + topics: input.topics, + }; + const actual = convertResponseToLogEntry(input); + expect(actual).deep.equal(expected); + }); + }); + describe('decodeLogEntry', () => { + it('decodes LogEntry into LogWithDecodedArgs', () => { + const input: LogEntry = { + logIndex: 96, + transactionIndex: 52, + transactionHash: '0x02b59043e9b38b430c8c66abe67ab4a9e5509def8f8552b54231e88db1839831', + blockHash: '', + blockNumber: 6361723, + address: '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', + data: + '0x00000000000000000000000067032ef7be8fa07c4335d0134099db0f3875e93000000000000000000000000067032ef7be8fa07c4335d0134099db0f3875e930000000000000000000000000000000000000000000000000000000174876e8000000000000000000000000000000000000000000000000000000000013ab668000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000', + topics: [ + '0x0bcc4c97732e47d9946f229edb95f5b6323f601300e4690de719993f3c371129', + '0x0000000000000000000000003f7f832abb3be28442c0e48b7222e02b322c78f3', + '0x000000000000000000000000a258b39954cef5cb142fd567a46cddb31a670124', + '0x523404b4e6f847d9aefcf5be024be396449b4635590291fd7a28a8c940843858', + ], + }; + const expected: LogWithDecodedArgs = { + ...input, + event: 'Fill', + args: { + makerAddress: '0x3f7f832abb3be28442c0e48b7222e02b322c78f3', + feeRecipientAddress: '0xa258b39954cef5cb142fd567a46cddb31a670124', + takerAddress: '0x67032ef7be8fa07c4335d0134099db0f3875e930', + senderAddress: '0x67032ef7be8fa07c4335d0134099db0f3875e930', + makerAssetFilledAmount: new BigNumber('100000000000'), + takerAssetFilledAmount: new BigNumber('330000000'), + makerFeePaid: new BigNumber('0'), + takerFeePaid: new BigNumber('0'), + orderHash: '0x523404b4e6f847d9aefcf5be024be396449b4635590291fd7a28a8c940843858', + makerAssetData: '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498', + takerAssetData: '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + }, + }; + const actual = decodeLogEntry(artifacts.Exchange.compilerOutput.abi, input); + expect(actual).deep.equal(expected); + }); + }); +}); diff --git a/packages/pipeline/test/data_types/events/exchange_events_test.ts b/packages/pipeline/test/data_types/events/exchange_events_test.ts new file mode 100644 index 000000000..f1432892d --- /dev/null +++ b/packages/pipeline/test/data_types/events/exchange_events_test.ts @@ -0,0 +1,77 @@ +import { ExchangeFillEventArgs } from '@0xproject/contract-wrappers'; +import { BigNumber } from '@0xproject/utils'; +import * as chai from 'chai'; +import { LogWithDecodedArgs } from 'ethereum-types'; +import 'mocha'; + +import { _convertToEntity } from '../../../src/data_types/events/exchange_events'; +import { ExchangeFillEvent } from '../../../src/entities/ExchangeFillEvent'; +import { chaiSetup } from '../../utils/chai_setup'; + +chaiSetup.configure(); +const expect = chai.expect; + +// tslint:disable:custom-no-magic-numbers +describe('exchange_events', () => { + describe('_convertToEntity', () => { + it('converts LogWithDecodedArgs to ExchangeFillEvent entity', () => { + const input: LogWithDecodedArgs = { + logIndex: 102, + transactionIndex: 38, + transactionHash: '0x6dd106d002873746072fc5e496dd0fb2541b68c77bcf9184ae19a42fd33657fe', + blockHash: '', + blockNumber: 6276262, + address: '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', + data: + '0x000000000000000000000000f6da68519f78b0d0bc93c701e86affcb75c92428000000000000000000000000f6da68519f78b0d0bc93c701e86affcb75c92428000000000000000000000000000000000000000000000000002386f26fc10000000000000000000000000000000000000000000000000000016345785d8a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f49800000000000000000000000000000000000000000000000000000000', + topics: [ + '0x0bcc4c97732e47d9946f229edb95f5b6323f601300e4690de719993f3c371129', + '0x000000000000000000000000f6da68519f78b0d0bc93c701e86affcb75c92428', + '0x000000000000000000000000c370d2a5920344aa6b7d8d11250e3e861434cbdd', + '0xab12ed2cbaa5615ab690b9da75a46e53ddfcf3f1a68655b5fe0d94c75a1aac4a', + ], + event: 'Fill', + args: { + makerAddress: '0xf6da68519f78b0d0bc93c701e86affcb75c92428', + feeRecipientAddress: '0xc370d2a5920344aa6b7d8d11250e3e861434cbdd', + takerAddress: '0xf6da68519f78b0d0bc93c701e86affcb75c92428', + senderAddress: '0xf6da68519f78b0d0bc93c701e86affcb75c92428', + makerAssetFilledAmount: new BigNumber('10000000000000000'), + takerAssetFilledAmount: new BigNumber('100000000000000000'), + makerFeePaid: new BigNumber('0'), + takerFeePaid: new BigNumber('0'), + orderHash: '0xab12ed2cbaa5615ab690b9da75a46e53ddfcf3f1a68655b5fe0d94c75a1aac4a', + makerAssetData: '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + takerAssetData: '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498', + }, + }; + const expected = new ExchangeFillEvent(); + expected.contractAddress = '0x4f833a24e1f95d70f028921e27040ca56e09ab0b'; + expected.blockNumber = 6276262; + expected.logIndex = 102; + expected.rawData = + '0x000000000000000000000000f6da68519f78b0d0bc93c701e86affcb75c92428000000000000000000000000f6da68519f78b0d0bc93c701e86affcb75c92428000000000000000000000000000000000000000000000000002386f26fc10000000000000000000000000000000000000000000000000000016345785d8a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f49800000000000000000000000000000000000000000000000000000000'; + expected.makerAddress = '0xf6da68519f78b0d0bc93c701e86affcb75c92428'; + expected.takerAddress = '0xf6da68519f78b0d0bc93c701e86affcb75c92428'; + expected.feeRecepientAddress = '0xc370d2a5920344aa6b7d8d11250e3e861434cbdd'; + expected.senderAddress = '0xf6da68519f78b0d0bc93c701e86affcb75c92428'; + expected.makerAssetFilledAmount = '10000000000000000'; + expected.takerAssetFilledAmount = '100000000000000000'; + expected.makerFeePaid = '0'; + expected.takerFeePaid = '0'; + expected.orderHash = '0xab12ed2cbaa5615ab690b9da75a46e53ddfcf3f1a68655b5fe0d94c75a1aac4a'; + expected.rawMakerAssetData = '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; + expected.makerAssetType = 'erc20'; + expected.makerAssetProxyId = '0xf47261b0'; + expected.makerTokenAddress = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; + expected.makerTokenId = null; + expected.rawTakerAssetData = '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498'; + expected.takerAssetType = 'erc20'; + expected.takerAssetProxyId = '0xf47261b0'; + expected.takerTokenAddress = '0xe41d2489571d322189246dafa5ebde1f4699f498'; + expected.takerTokenId = null; + const actual = _convertToEntity(input); + expect(actual).deep.equal(expected); + }); + }); +}); -- cgit v1.2.3 From ada78d140b3d60997461e3786564baec291b7220 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Thu, 27 Sep 2018 17:32:56 -0700 Subject: Add tests for SRA order conversion --- .../pipeline/src/data_types/sra_order/index.ts | 54 --------------- .../pipeline/src/data_types/sra_orders/index.ts | 54 +++++++++++++++ packages/pipeline/src/index.ts | 4 +- .../test/data_types/sra_orders/index_test.ts | 79 ++++++++++++++++++++++ 4 files changed, 135 insertions(+), 56 deletions(-) delete mode 100644 packages/pipeline/src/data_types/sra_order/index.ts create mode 100644 packages/pipeline/src/data_types/sra_orders/index.ts create mode 100644 packages/pipeline/test/data_types/sra_orders/index_test.ts diff --git a/packages/pipeline/src/data_types/sra_order/index.ts b/packages/pipeline/src/data_types/sra_order/index.ts deleted file mode 100644 index b6364415c..000000000 --- a/packages/pipeline/src/data_types/sra_order/index.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { APIOrder, OrdersResponse } from '@0xproject/connect'; -import { assetDataUtils, orderHashUtils } from '@0xproject/order-utils'; -import { AssetProxyId, ERC721AssetData } from '@0xproject/types'; -import * as R from 'ramda'; - -import { SraOrder } from '../../entities/SraOrder'; -import { bigNumbertoStringOrNull } from '../../utils'; - -export function parseSraOrders(rawOrdersResponse: OrdersResponse): SraOrder[] { - return R.map(_convertToEntity, rawOrdersResponse.records); -} - -export function _convertToEntity(apiOrder: APIOrder): SraOrder { - // TODO(albrow): refactor out common asset data decoding code. - const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(apiOrder.order.makerAssetData); - const makerAssetType = makerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; - const takerAssetData = assetDataUtils.decodeAssetDataOrThrow(apiOrder.order.takerAssetData); - const takerAssetType = takerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; - - const sraOrder = new SraOrder(); - sraOrder.exchangeAddress = apiOrder.order.exchangeAddress; - sraOrder.orderHashHex = orderHashUtils.getOrderHashHex(apiOrder.order); - - // TODO(albrow): Set these fields to the correct values upstack. - sraOrder.lastUpdatedTimestamp = Date.now(); - sraOrder.firstSeenTimestamp = Date.now(); - - sraOrder.makerAddress = apiOrder.order.makerAddress; - sraOrder.takerAddress = apiOrder.order.takerAddress; - sraOrder.feeRecipientAddress = apiOrder.order.feeRecipientAddress; - sraOrder.senderAddress = apiOrder.order.senderAddress; - sraOrder.makerAssetAmount = apiOrder.order.makerAssetAmount.toString(); - sraOrder.takerAssetAmount = apiOrder.order.takerAssetAmount.toString(); - sraOrder.makerFee = apiOrder.order.makerFee.toString(); - sraOrder.takerFee = apiOrder.order.takerFee.toString(); - sraOrder.expirationTimeSeconds = apiOrder.order.expirationTimeSeconds.toString(); - sraOrder.salt = apiOrder.order.salt.toString(); - sraOrder.signature = apiOrder.order.signature; - - sraOrder.rawMakerAssetData = apiOrder.order.makerAssetData; - sraOrder.makerAssetType = makerAssetType; - sraOrder.makerAssetProxyId = makerAssetData.assetProxyId; - sraOrder.makerTokenAddress = makerAssetData.tokenAddress; - sraOrder.makerTokenId = bigNumbertoStringOrNull((makerAssetData as ERC721AssetData).tokenId); - sraOrder.rawTakerAssetData = apiOrder.order.takerAssetData; - sraOrder.takerAssetType = takerAssetType; - sraOrder.takerAssetProxyId = takerAssetData.assetProxyId; - sraOrder.takerTokenAddress = takerAssetData.tokenAddress; - sraOrder.takerTokenId = bigNumbertoStringOrNull((takerAssetData as ERC721AssetData).tokenId); - - sraOrder.metaDataJson = JSON.stringify(apiOrder.metaData); - - return sraOrder; -} diff --git a/packages/pipeline/src/data_types/sra_orders/index.ts b/packages/pipeline/src/data_types/sra_orders/index.ts new file mode 100644 index 000000000..fb2b74dfe --- /dev/null +++ b/packages/pipeline/src/data_types/sra_orders/index.ts @@ -0,0 +1,54 @@ +import { APIOrder, OrdersResponse } from '@0xproject/connect'; +import { assetDataUtils, orderHashUtils } from '@0xproject/order-utils'; +import { AssetProxyId, ERC721AssetData } from '@0xproject/types'; +import * as R from 'ramda'; + +import { SraOrder } from '../../entities/SraOrder'; +import { bigNumbertoStringOrNull } from '../../utils'; + +export function parseSraOrders(rawOrdersResponse: OrdersResponse): SraOrder[] { + return R.map(_convertToEntity, rawOrdersResponse.records); +} + +export function _convertToEntity(apiOrder: APIOrder): SraOrder { + // TODO(albrow): refactor out common asset data decoding code. + const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(apiOrder.order.makerAssetData); + const makerAssetType = makerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; + const takerAssetData = assetDataUtils.decodeAssetDataOrThrow(apiOrder.order.takerAssetData); + const takerAssetType = takerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; + + const sraOrder = new SraOrder(); + sraOrder.exchangeAddress = apiOrder.order.exchangeAddress; + sraOrder.orderHashHex = orderHashUtils.getOrderHashHex(apiOrder.order); + + // TODO(albrow): Set these fields to the correct values upstack. + sraOrder.lastUpdatedTimestamp = 0; + sraOrder.firstSeenTimestamp = 0; + + sraOrder.makerAddress = apiOrder.order.makerAddress; + sraOrder.takerAddress = apiOrder.order.takerAddress; + sraOrder.feeRecipientAddress = apiOrder.order.feeRecipientAddress; + sraOrder.senderAddress = apiOrder.order.senderAddress; + sraOrder.makerAssetAmount = apiOrder.order.makerAssetAmount.toString(); + sraOrder.takerAssetAmount = apiOrder.order.takerAssetAmount.toString(); + sraOrder.makerFee = apiOrder.order.makerFee.toString(); + sraOrder.takerFee = apiOrder.order.takerFee.toString(); + sraOrder.expirationTimeSeconds = apiOrder.order.expirationTimeSeconds.toString(); + sraOrder.salt = apiOrder.order.salt.toString(); + sraOrder.signature = apiOrder.order.signature; + + sraOrder.rawMakerAssetData = apiOrder.order.makerAssetData; + sraOrder.makerAssetType = makerAssetType; + sraOrder.makerAssetProxyId = makerAssetData.assetProxyId; + sraOrder.makerTokenAddress = makerAssetData.tokenAddress; + sraOrder.makerTokenId = bigNumbertoStringOrNull((makerAssetData as ERC721AssetData).tokenId); + sraOrder.rawTakerAssetData = apiOrder.order.takerAssetData; + sraOrder.takerAssetType = takerAssetType; + sraOrder.takerAssetProxyId = takerAssetData.assetProxyId; + sraOrder.takerTokenAddress = takerAssetData.tokenAddress; + sraOrder.takerTokenId = bigNumbertoStringOrNull((takerAssetData as ERC721AssetData).tokenId); + + sraOrder.metaDataJson = JSON.stringify(apiOrder.metaData); + + return sraOrder; +} diff --git a/packages/pipeline/src/index.ts b/packages/pipeline/src/index.ts index c442c7970..a1dbb35ff 100644 --- a/packages/pipeline/src/index.ts +++ b/packages/pipeline/src/index.ts @@ -4,7 +4,7 @@ import { Connection, createConnection } from 'typeorm'; import { Etherscan } from './data_sources/etherscan'; import { parseExchangeEvents } from './data_types/events/exchange_events'; -import { parseSraOrders } from './data_types/sra_order'; +import { parseSraOrders } from './data_types/sra_orders'; import { ExchangeCancelEvent } from './entities/ExchangeCancelEvent'; import { ExchangeCancelUpToEvent } from './entities/ExchangeCancelUpToEvent'; import { ExchangeFillEvent } from './entities/ExchangeFillEvent'; @@ -53,7 +53,7 @@ async function getSraOrdersAsync(): Promise { const orders = parseSraOrders(rawOrders); for (const order of orders) { order.sourceUrl = sraUrl; - order.save(); + await order.save(); } console.log(`now there are ${await orderRepository.count()} total orders`); } diff --git a/packages/pipeline/test/data_types/sra_orders/index_test.ts b/packages/pipeline/test/data_types/sra_orders/index_test.ts new file mode 100644 index 000000000..174f89b4f --- /dev/null +++ b/packages/pipeline/test/data_types/sra_orders/index_test.ts @@ -0,0 +1,79 @@ +import { APIOrder } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; +import * as chai from 'chai'; +import 'mocha'; +import { Connection, createConnection } from 'typeorm'; + +import { _convertToEntity } from '../../../src/data_types/sra_orders'; +import { SraOrder } from '../../../src/entities/SraOrder'; +import { chaiSetup } from '../../utils/chai_setup'; + +import { config } from '../../../src/ormconfig'; + +chaiSetup.configure(); +const expect = chai.expect; + +// tslint:disable:custom-no-magic-numbers +describe('sra_orders', () => { + describe('_convertToEntity', () => { + before(async () => { + // HACK(albrow): We don't actually use this connection but it seems + // to be required because chai calls the inspect method of the + // entity and that method requires a "default" connection. + await createConnection(config); + }); + it('converts ApiOrder to SraOrder entity', () => { + const input: APIOrder = { + order: { + makerAddress: '0xb45df06e38540a675fdb5b598abf2c0dbe9d6b81', + takerAddress: '0x0000000000000000000000000000000000000000', + feeRecipientAddress: '0xa258b39954cef5cb142fd567a46cddb31a670124', + senderAddress: '0x0000000000000000000000000000000000000000', + makerAssetAmount: new BigNumber('1619310371000000000'), + takerAssetAmount: new BigNumber('8178335207070707070707'), + makerFee: new BigNumber('0'), + takerFee: new BigNumber('0'), + exchangeAddress: '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', + expirationTimeSeconds: new BigNumber('1538529488'), + signature: + '0x1b5a5d672b0d647b5797387ccbb89d822d5d2e873346b014f4ff816ff0783f2a7a0d2824d2d7042ec8ea375bc7f870963e1cb8248f1db03ddf125e27b5963aa11f03', + salt: new BigNumber('1537924688891'), + makerAssetData: '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + takerAssetData: '0xf47261b000000000000000000000000042d6622dece394b54999fbd73d108123806f6a18', + }, + metaData: { isThisArbitraryData: true, powerLevel: 9001 }, + }; + const expected = new SraOrder(); + expected.exchangeAddress = '0x4f833a24e1f95d70f028921e27040ca56e09ab0b'; + expected.orderHashHex = '0x1bdbeb0d088a33da28b9ee6d94e8771452f90f4a69107da2fa75195d61b9a1c9'; + expected.lastUpdatedTimestamp = 0; + expected.firstSeenTimestamp = 0; + expected.makerAddress = '0xb45df06e38540a675fdb5b598abf2c0dbe9d6b81'; + expected.takerAddress = '0x0000000000000000000000000000000000000000'; + expected.feeRecipientAddress = '0xa258b39954cef5cb142fd567a46cddb31a670124'; + expected.senderAddress = '0x0000000000000000000000000000000000000000'; + expected.makerAssetAmount = '1619310371000000000'; + expected.takerAssetAmount = '8178335207070707070707'; + expected.makerFee = '0'; + expected.takerFee = '0'; + expected.expirationTimeSeconds = '1538529488'; + expected.salt = '1537924688891'; + expected.signature = + '0x1b5a5d672b0d647b5797387ccbb89d822d5d2e873346b014f4ff816ff0783f2a7a0d2824d2d7042ec8ea375bc7f870963e1cb8248f1db03ddf125e27b5963aa11f03'; + expected.rawMakerAssetData = '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; + expected.makerAssetType = 'erc20'; + expected.makerAssetProxyId = '0xf47261b0'; + expected.makerTokenAddress = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; + expected.makerTokenId = null; + expected.rawTakerAssetData = '0xf47261b000000000000000000000000042d6622dece394b54999fbd73d108123806f6a18'; + expected.takerAssetType = 'erc20'; + expected.takerAssetProxyId = '0xf47261b0'; + expected.takerTokenAddress = '0x42d6622dece394b54999fbd73d108123806f6a18'; + expected.takerTokenId = null; + expected.metaDataJson = '{"isThisArbitraryData":true,"powerLevel":9001}'; + + const actual = _convertToEntity(input); + expect(actual).deep.equal(expected); + }); + }); +}); -- cgit v1.2.3 From 6c00dd6f3900e322e5b6d5fe09b35fdafc1bfb2f Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Tue, 16 Oct 2018 14:59:59 -0700 Subject: Rebase and update to use new modularized artifacts --- packages/pipeline/package.json | 11 +- packages/pipeline/src/artifacts.ts | 7 - packages/pipeline/src/artifacts/Exchange.json | 2262 -------------------- .../src/data_types/events/exchange_events.ts | 6 +- packages/pipeline/src/global.d.ts | 6 - .../test/data_types/events/event_utils_test.ts | 5 +- 6 files changed, 8 insertions(+), 2289 deletions(-) delete mode 100644 packages/pipeline/src/artifacts.ts delete mode 100644 packages/pipeline/src/artifacts/Exchange.json delete mode 100644 packages/pipeline/src/global.d.ts diff --git a/packages/pipeline/package.json b/packages/pipeline/package.json index c01b7f448..be23bfe2c 100644 --- a/packages/pipeline/package.json +++ b/packages/pipeline/package.json @@ -4,21 +4,17 @@ "private": true, "description": "Data pipeline for offline analysis", "scripts": { - "build": "yarn pre_build && tsc -b", - "pre_build": "run-s update_artifacts copy_artifacts", - "copy_artifacts": "copyfiles -u 2 './src/artifacts/**/*.json' ./lib/src/artifacts", - "update_artifacts": "for i in ${npm_package_config_contracts_v2}; do copyfiles -u 4 ../migrations/artifacts/2.0.0/$i.json src/artifacts; done;", + "build": "yarn tsc -b", "test": "yarn run_mocha", "rebuild_and_test": "run-s build test", "test:circleci": "yarn test:coverage", "run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js --bail --exit", "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", "coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info", - "clean": "shx rm -rf lib src/artifacts", - "lint": "tslint --project . --exclude **/src/generated_contract_wrappers/**/*" + "clean": "shx rm -rf lib", + "lint": "tslint --project ." }, "config": { - "contracts_v2": "Exchange", "postpublish": { "assets": [] } @@ -39,6 +35,7 @@ "typescript": "3.0.1" }, "dependencies": { + "@0xproject/contract-artifacts": "^1.0.0", "@0xproject/connect": "^2.0.4", "@0xproject/contract-wrappers": "^1.0.1", "@0xproject/order-utils": "^1.0.2", diff --git a/packages/pipeline/src/artifacts.ts b/packages/pipeline/src/artifacts.ts deleted file mode 100644 index c0d7e11f6..000000000 --- a/packages/pipeline/src/artifacts.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { ContractArtifact } from 'ethereum-types'; - -import * as Exchange from './artifacts/Exchange.json'; - -export const artifacts = { - Exchange: (Exchange as any) as ContractArtifact, -}; diff --git a/packages/pipeline/src/artifacts/Exchange.json b/packages/pipeline/src/artifacts/Exchange.json deleted file mode 100644 index 631bba974..000000000 --- a/packages/pipeline/src/artifacts/Exchange.json +++ /dev/null @@ -1,2262 +0,0 @@ -{ - "schemaVersion": "2.0.0", - "contractName": "Exchange", - "compilerOutput": { - "abi": [ - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "bytes32" - } - ], - "name": "filled", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "components": [ - { - "name": "makerAddress", - "type": "address" - }, - { - "name": "takerAddress", - "type": "address" - }, - { - "name": "feeRecipientAddress", - "type": "address" - }, - { - "name": "senderAddress", - "type": "address" - }, - { - "name": "makerAssetAmount", - "type": "uint256" - }, - { - "name": "takerAssetAmount", - "type": "uint256" - }, - { - "name": "makerFee", - "type": "uint256" - }, - { - "name": "takerFee", - "type": "uint256" - }, - { - "name": "expirationTimeSeconds", - "type": "uint256" - }, - { - "name": "salt", - "type": "uint256" - }, - { - "name": "makerAssetData", - "type": "bytes" - }, - { - "name": "takerAssetData", - "type": "bytes" - } - ], - "name": "orders", - "type": "tuple[]" - }, - { - "name": "takerAssetFillAmounts", - "type": "uint256[]" - }, - { - "name": "signatures", - "type": "bytes[]" - } - ], - "name": "batchFillOrders", - "outputs": [ - { - "components": [ - { - "name": "makerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "takerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "makerFeePaid", - "type": "uint256" - }, - { - "name": "takerFeePaid", - "type": "uint256" - } - ], - "name": "totalFillResults", - "type": "tuple" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "bytes32" - } - ], - "name": "cancelled", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "hash", - "type": "bytes32" - }, - { - "name": "signerAddress", - "type": "address" - }, - { - "name": "signature", - "type": "bytes" - } - ], - "name": "preSign", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "components": [ - { - "name": "makerAddress", - "type": "address" - }, - { - "name": "takerAddress", - "type": "address" - }, - { - "name": "feeRecipientAddress", - "type": "address" - }, - { - "name": "senderAddress", - "type": "address" - }, - { - "name": "makerAssetAmount", - "type": "uint256" - }, - { - "name": "takerAssetAmount", - "type": "uint256" - }, - { - "name": "makerFee", - "type": "uint256" - }, - { - "name": "takerFee", - "type": "uint256" - }, - { - "name": "expirationTimeSeconds", - "type": "uint256" - }, - { - "name": "salt", - "type": "uint256" - }, - { - "name": "makerAssetData", - "type": "bytes" - }, - { - "name": "takerAssetData", - "type": "bytes" - } - ], - "name": "leftOrder", - "type": "tuple" - }, - { - "components": [ - { - "name": "makerAddress", - "type": "address" - }, - { - "name": "takerAddress", - "type": "address" - }, - { - "name": "feeRecipientAddress", - "type": "address" - }, - { - "name": "senderAddress", - "type": "address" - }, - { - "name": "makerAssetAmount", - "type": "uint256" - }, - { - "name": "takerAssetAmount", - "type": "uint256" - }, - { - "name": "makerFee", - "type": "uint256" - }, - { - "name": "takerFee", - "type": "uint256" - }, - { - "name": "expirationTimeSeconds", - "type": "uint256" - }, - { - "name": "salt", - "type": "uint256" - }, - { - "name": "makerAssetData", - "type": "bytes" - }, - { - "name": "takerAssetData", - "type": "bytes" - } - ], - "name": "rightOrder", - "type": "tuple" - }, - { - "name": "leftSignature", - "type": "bytes" - }, - { - "name": "rightSignature", - "type": "bytes" - } - ], - "name": "matchOrders", - "outputs": [ - { - "components": [ - { - "components": [ - { - "name": "makerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "takerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "makerFeePaid", - "type": "uint256" - }, - { - "name": "takerFeePaid", - "type": "uint256" - } - ], - "name": "left", - "type": "tuple" - }, - { - "components": [ - { - "name": "makerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "takerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "makerFeePaid", - "type": "uint256" - }, - { - "name": "takerFeePaid", - "type": "uint256" - } - ], - "name": "right", - "type": "tuple" - }, - { - "name": "leftMakerAssetSpreadAmount", - "type": "uint256" - } - ], - "name": "matchedFillResults", - "type": "tuple" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "components": [ - { - "name": "makerAddress", - "type": "address" - }, - { - "name": "takerAddress", - "type": "address" - }, - { - "name": "feeRecipientAddress", - "type": "address" - }, - { - "name": "senderAddress", - "type": "address" - }, - { - "name": "makerAssetAmount", - "type": "uint256" - }, - { - "name": "takerAssetAmount", - "type": "uint256" - }, - { - "name": "makerFee", - "type": "uint256" - }, - { - "name": "takerFee", - "type": "uint256" - }, - { - "name": "expirationTimeSeconds", - "type": "uint256" - }, - { - "name": "salt", - "type": "uint256" - }, - { - "name": "makerAssetData", - "type": "bytes" - }, - { - "name": "takerAssetData", - "type": "bytes" - } - ], - "name": "order", - "type": "tuple" - }, - { - "name": "takerAssetFillAmount", - "type": "uint256" - }, - { - "name": "signature", - "type": "bytes" - } - ], - "name": "fillOrderNoThrow", - "outputs": [ - { - "components": [ - { - "name": "makerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "takerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "makerFeePaid", - "type": "uint256" - }, - { - "name": "takerFeePaid", - "type": "uint256" - } - ], - "name": "fillResults", - "type": "tuple" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "bytes4" - } - ], - "name": "assetProxies", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "components": [ - { - "name": "makerAddress", - "type": "address" - }, - { - "name": "takerAddress", - "type": "address" - }, - { - "name": "feeRecipientAddress", - "type": "address" - }, - { - "name": "senderAddress", - "type": "address" - }, - { - "name": "makerAssetAmount", - "type": "uint256" - }, - { - "name": "takerAssetAmount", - "type": "uint256" - }, - { - "name": "makerFee", - "type": "uint256" - }, - { - "name": "takerFee", - "type": "uint256" - }, - { - "name": "expirationTimeSeconds", - "type": "uint256" - }, - { - "name": "salt", - "type": "uint256" - }, - { - "name": "makerAssetData", - "type": "bytes" - }, - { - "name": "takerAssetData", - "type": "bytes" - } - ], - "name": "orders", - "type": "tuple[]" - } - ], - "name": "batchCancelOrders", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "components": [ - { - "name": "makerAddress", - "type": "address" - }, - { - "name": "takerAddress", - "type": "address" - }, - { - "name": "feeRecipientAddress", - "type": "address" - }, - { - "name": "senderAddress", - "type": "address" - }, - { - "name": "makerAssetAmount", - "type": "uint256" - }, - { - "name": "takerAssetAmount", - "type": "uint256" - }, - { - "name": "makerFee", - "type": "uint256" - }, - { - "name": "takerFee", - "type": "uint256" - }, - { - "name": "expirationTimeSeconds", - "type": "uint256" - }, - { - "name": "salt", - "type": "uint256" - }, - { - "name": "makerAssetData", - "type": "bytes" - }, - { - "name": "takerAssetData", - "type": "bytes" - } - ], - "name": "orders", - "type": "tuple[]" - }, - { - "name": "takerAssetFillAmounts", - "type": "uint256[]" - }, - { - "name": "signatures", - "type": "bytes[]" - } - ], - "name": "batchFillOrKillOrders", - "outputs": [ - { - "components": [ - { - "name": "makerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "takerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "makerFeePaid", - "type": "uint256" - }, - { - "name": "takerFeePaid", - "type": "uint256" - } - ], - "name": "totalFillResults", - "type": "tuple" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "targetOrderEpoch", - "type": "uint256" - } - ], - "name": "cancelOrdersUpTo", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "components": [ - { - "name": "makerAddress", - "type": "address" - }, - { - "name": "takerAddress", - "type": "address" - }, - { - "name": "feeRecipientAddress", - "type": "address" - }, - { - "name": "senderAddress", - "type": "address" - }, - { - "name": "makerAssetAmount", - "type": "uint256" - }, - { - "name": "takerAssetAmount", - "type": "uint256" - }, - { - "name": "makerFee", - "type": "uint256" - }, - { - "name": "takerFee", - "type": "uint256" - }, - { - "name": "expirationTimeSeconds", - "type": "uint256" - }, - { - "name": "salt", - "type": "uint256" - }, - { - "name": "makerAssetData", - "type": "bytes" - }, - { - "name": "takerAssetData", - "type": "bytes" - } - ], - "name": "orders", - "type": "tuple[]" - }, - { - "name": "takerAssetFillAmounts", - "type": "uint256[]" - }, - { - "name": "signatures", - "type": "bytes[]" - } - ], - "name": "batchFillOrdersNoThrow", - "outputs": [ - { - "components": [ - { - "name": "makerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "takerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "makerFeePaid", - "type": "uint256" - }, - { - "name": "takerFeePaid", - "type": "uint256" - } - ], - "name": "totalFillResults", - "type": "tuple" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "assetProxyId", - "type": "bytes4" - } - ], - "name": "getAssetProxy", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "bytes32" - } - ], - "name": "transactions", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "components": [ - { - "name": "makerAddress", - "type": "address" - }, - { - "name": "takerAddress", - "type": "address" - }, - { - "name": "feeRecipientAddress", - "type": "address" - }, - { - "name": "senderAddress", - "type": "address" - }, - { - "name": "makerAssetAmount", - "type": "uint256" - }, - { - "name": "takerAssetAmount", - "type": "uint256" - }, - { - "name": "makerFee", - "type": "uint256" - }, - { - "name": "takerFee", - "type": "uint256" - }, - { - "name": "expirationTimeSeconds", - "type": "uint256" - }, - { - "name": "salt", - "type": "uint256" - }, - { - "name": "makerAssetData", - "type": "bytes" - }, - { - "name": "takerAssetData", - "type": "bytes" - } - ], - "name": "order", - "type": "tuple" - }, - { - "name": "takerAssetFillAmount", - "type": "uint256" - }, - { - "name": "signature", - "type": "bytes" - } - ], - "name": "fillOrKillOrder", - "outputs": [ - { - "components": [ - { - "name": "makerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "takerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "makerFeePaid", - "type": "uint256" - }, - { - "name": "takerFeePaid", - "type": "uint256" - } - ], - "name": "fillResults", - "type": "tuple" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "validatorAddress", - "type": "address" - }, - { - "name": "approval", - "type": "bool" - } - ], - "name": "setSignatureValidatorApproval", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "address" - }, - { - "name": "", - "type": "address" - } - ], - "name": "allowedValidators", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "components": [ - { - "name": "makerAddress", - "type": "address" - }, - { - "name": "takerAddress", - "type": "address" - }, - { - "name": "feeRecipientAddress", - "type": "address" - }, - { - "name": "senderAddress", - "type": "address" - }, - { - "name": "makerAssetAmount", - "type": "uint256" - }, - { - "name": "takerAssetAmount", - "type": "uint256" - }, - { - "name": "makerFee", - "type": "uint256" - }, - { - "name": "takerFee", - "type": "uint256" - }, - { - "name": "expirationTimeSeconds", - "type": "uint256" - }, - { - "name": "salt", - "type": "uint256" - }, - { - "name": "makerAssetData", - "type": "bytes" - }, - { - "name": "takerAssetData", - "type": "bytes" - } - ], - "name": "orders", - "type": "tuple[]" - }, - { - "name": "takerAssetFillAmount", - "type": "uint256" - }, - { - "name": "signatures", - "type": "bytes[]" - } - ], - "name": "marketSellOrders", - "outputs": [ - { - "components": [ - { - "name": "makerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "takerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "makerFeePaid", - "type": "uint256" - }, - { - "name": "takerFeePaid", - "type": "uint256" - } - ], - "name": "totalFillResults", - "type": "tuple" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "components": [ - { - "name": "makerAddress", - "type": "address" - }, - { - "name": "takerAddress", - "type": "address" - }, - { - "name": "feeRecipientAddress", - "type": "address" - }, - { - "name": "senderAddress", - "type": "address" - }, - { - "name": "makerAssetAmount", - "type": "uint256" - }, - { - "name": "takerAssetAmount", - "type": "uint256" - }, - { - "name": "makerFee", - "type": "uint256" - }, - { - "name": "takerFee", - "type": "uint256" - }, - { - "name": "expirationTimeSeconds", - "type": "uint256" - }, - { - "name": "salt", - "type": "uint256" - }, - { - "name": "makerAssetData", - "type": "bytes" - }, - { - "name": "takerAssetData", - "type": "bytes" - } - ], - "name": "orders", - "type": "tuple[]" - } - ], - "name": "getOrdersInfo", - "outputs": [ - { - "components": [ - { - "name": "orderStatus", - "type": "uint8" - }, - { - "name": "orderHash", - "type": "bytes32" - }, - { - "name": "orderTakerAssetFilledAmount", - "type": "uint256" - } - ], - "name": "", - "type": "tuple[]" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "bytes32" - }, - { - "name": "", - "type": "address" - } - ], - "name": "preSigned", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "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": "hash", - "type": "bytes32" - }, - { - "name": "signerAddress", - "type": "address" - }, - { - "name": "signature", - "type": "bytes" - } - ], - "name": "isValidSignature", - "outputs": [ - { - "name": "isValid", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "components": [ - { - "name": "makerAddress", - "type": "address" - }, - { - "name": "takerAddress", - "type": "address" - }, - { - "name": "feeRecipientAddress", - "type": "address" - }, - { - "name": "senderAddress", - "type": "address" - }, - { - "name": "makerAssetAmount", - "type": "uint256" - }, - { - "name": "takerAssetAmount", - "type": "uint256" - }, - { - "name": "makerFee", - "type": "uint256" - }, - { - "name": "takerFee", - "type": "uint256" - }, - { - "name": "expirationTimeSeconds", - "type": "uint256" - }, - { - "name": "salt", - "type": "uint256" - }, - { - "name": "makerAssetData", - "type": "bytes" - }, - { - "name": "takerAssetData", - "type": "bytes" - } - ], - "name": "orders", - "type": "tuple[]" - }, - { - "name": "makerAssetFillAmount", - "type": "uint256" - }, - { - "name": "signatures", - "type": "bytes[]" - } - ], - "name": "marketBuyOrdersNoThrow", - "outputs": [ - { - "components": [ - { - "name": "makerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "takerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "makerFeePaid", - "type": "uint256" - }, - { - "name": "takerFeePaid", - "type": "uint256" - } - ], - "name": "totalFillResults", - "type": "tuple" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "components": [ - { - "name": "makerAddress", - "type": "address" - }, - { - "name": "takerAddress", - "type": "address" - }, - { - "name": "feeRecipientAddress", - "type": "address" - }, - { - "name": "senderAddress", - "type": "address" - }, - { - "name": "makerAssetAmount", - "type": "uint256" - }, - { - "name": "takerAssetAmount", - "type": "uint256" - }, - { - "name": "makerFee", - "type": "uint256" - }, - { - "name": "takerFee", - "type": "uint256" - }, - { - "name": "expirationTimeSeconds", - "type": "uint256" - }, - { - "name": "salt", - "type": "uint256" - }, - { - "name": "makerAssetData", - "type": "bytes" - }, - { - "name": "takerAssetData", - "type": "bytes" - } - ], - "name": "order", - "type": "tuple" - }, - { - "name": "takerAssetFillAmount", - "type": "uint256" - }, - { - "name": "signature", - "type": "bytes" - } - ], - "name": "fillOrder", - "outputs": [ - { - "components": [ - { - "name": "makerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "takerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "makerFeePaid", - "type": "uint256" - }, - { - "name": "takerFeePaid", - "type": "uint256" - } - ], - "name": "fillResults", - "type": "tuple" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "salt", - "type": "uint256" - }, - { - "name": "signerAddress", - "type": "address" - }, - { - "name": "data", - "type": "bytes" - }, - { - "name": "signature", - "type": "bytes" - } - ], - "name": "executeTransaction", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "assetProxy", - "type": "address" - } - ], - "name": "registerAssetProxy", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "components": [ - { - "name": "makerAddress", - "type": "address" - }, - { - "name": "takerAddress", - "type": "address" - }, - { - "name": "feeRecipientAddress", - "type": "address" - }, - { - "name": "senderAddress", - "type": "address" - }, - { - "name": "makerAssetAmount", - "type": "uint256" - }, - { - "name": "takerAssetAmount", - "type": "uint256" - }, - { - "name": "makerFee", - "type": "uint256" - }, - { - "name": "takerFee", - "type": "uint256" - }, - { - "name": "expirationTimeSeconds", - "type": "uint256" - }, - { - "name": "salt", - "type": "uint256" - }, - { - "name": "makerAssetData", - "type": "bytes" - }, - { - "name": "takerAssetData", - "type": "bytes" - } - ], - "name": "order", - "type": "tuple" - } - ], - "name": "getOrderInfo", - "outputs": [ - { - "components": [ - { - "name": "orderStatus", - "type": "uint8" - }, - { - "name": "orderHash", - "type": "bytes32" - }, - { - "name": "orderTakerAssetFilledAmount", - "type": "uint256" - } - ], - "name": "orderInfo", - "type": "tuple" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "components": [ - { - "name": "makerAddress", - "type": "address" - }, - { - "name": "takerAddress", - "type": "address" - }, - { - "name": "feeRecipientAddress", - "type": "address" - }, - { - "name": "senderAddress", - "type": "address" - }, - { - "name": "makerAssetAmount", - "type": "uint256" - }, - { - "name": "takerAssetAmount", - "type": "uint256" - }, - { - "name": "makerFee", - "type": "uint256" - }, - { - "name": "takerFee", - "type": "uint256" - }, - { - "name": "expirationTimeSeconds", - "type": "uint256" - }, - { - "name": "salt", - "type": "uint256" - }, - { - "name": "makerAssetData", - "type": "bytes" - }, - { - "name": "takerAssetData", - "type": "bytes" - } - ], - "name": "order", - "type": "tuple" - } - ], - "name": "cancelOrder", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "address" - }, - { - "name": "", - "type": "address" - } - ], - "name": "orderEpoch", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "ZRX_ASSET_DATA", - "outputs": [ - { - "name": "", - "type": "bytes" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "components": [ - { - "name": "makerAddress", - "type": "address" - }, - { - "name": "takerAddress", - "type": "address" - }, - { - "name": "feeRecipientAddress", - "type": "address" - }, - { - "name": "senderAddress", - "type": "address" - }, - { - "name": "makerAssetAmount", - "type": "uint256" - }, - { - "name": "takerAssetAmount", - "type": "uint256" - }, - { - "name": "makerFee", - "type": "uint256" - }, - { - "name": "takerFee", - "type": "uint256" - }, - { - "name": "expirationTimeSeconds", - "type": "uint256" - }, - { - "name": "salt", - "type": "uint256" - }, - { - "name": "makerAssetData", - "type": "bytes" - }, - { - "name": "takerAssetData", - "type": "bytes" - } - ], - "name": "orders", - "type": "tuple[]" - }, - { - "name": "takerAssetFillAmount", - "type": "uint256" - }, - { - "name": "signatures", - "type": "bytes[]" - } - ], - "name": "marketSellOrdersNoThrow", - "outputs": [ - { - "components": [ - { - "name": "makerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "takerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "makerFeePaid", - "type": "uint256" - }, - { - "name": "takerFeePaid", - "type": "uint256" - } - ], - "name": "totalFillResults", - "type": "tuple" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "EIP712_DOMAIN_HASH", - "outputs": [ - { - "name": "", - "type": "bytes32" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "components": [ - { - "name": "makerAddress", - "type": "address" - }, - { - "name": "takerAddress", - "type": "address" - }, - { - "name": "feeRecipientAddress", - "type": "address" - }, - { - "name": "senderAddress", - "type": "address" - }, - { - "name": "makerAssetAmount", - "type": "uint256" - }, - { - "name": "takerAssetAmount", - "type": "uint256" - }, - { - "name": "makerFee", - "type": "uint256" - }, - { - "name": "takerFee", - "type": "uint256" - }, - { - "name": "expirationTimeSeconds", - "type": "uint256" - }, - { - "name": "salt", - "type": "uint256" - }, - { - "name": "makerAssetData", - "type": "bytes" - }, - { - "name": "takerAssetData", - "type": "bytes" - } - ], - "name": "orders", - "type": "tuple[]" - }, - { - "name": "makerAssetFillAmount", - "type": "uint256" - }, - { - "name": "signatures", - "type": "bytes[]" - } - ], - "name": "marketBuyOrders", - "outputs": [ - { - "components": [ - { - "name": "makerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "takerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "makerFeePaid", - "type": "uint256" - }, - { - "name": "takerFeePaid", - "type": "uint256" - } - ], - "name": "totalFillResults", - "type": "tuple" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "currentContextAddress", - "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": "VERSION", - "outputs": [ - { - "name": "", - "type": "string" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "signerAddress", - "type": "address" - }, - { - "indexed": true, - "name": "validatorAddress", - "type": "address" - }, - { - "indexed": false, - "name": "approved", - "type": "bool" - } - ], - "name": "SignatureValidatorApproval", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "makerAddress", - "type": "address" - }, - { - "indexed": true, - "name": "feeRecipientAddress", - "type": "address" - }, - { - "indexed": false, - "name": "takerAddress", - "type": "address" - }, - { - "indexed": false, - "name": "senderAddress", - "type": "address" - }, - { - "indexed": false, - "name": "makerAssetFilledAmount", - "type": "uint256" - }, - { - "indexed": false, - "name": "takerAssetFilledAmount", - "type": "uint256" - }, - { - "indexed": false, - "name": "makerFeePaid", - "type": "uint256" - }, - { - "indexed": false, - "name": "takerFeePaid", - "type": "uint256" - }, - { - "indexed": true, - "name": "orderHash", - "type": "bytes32" - }, - { - "indexed": false, - "name": "makerAssetData", - "type": "bytes" - }, - { - "indexed": false, - "name": "takerAssetData", - "type": "bytes" - } - ], - "name": "Fill", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "makerAddress", - "type": "address" - }, - { - "indexed": true, - "name": "feeRecipientAddress", - "type": "address" - }, - { - "indexed": false, - "name": "senderAddress", - "type": "address" - }, - { - "indexed": true, - "name": "orderHash", - "type": "bytes32" - }, - { - "indexed": false, - "name": "makerAssetData", - "type": "bytes" - }, - { - "indexed": false, - "name": "takerAssetData", - "type": "bytes" - } - ], - "name": "Cancel", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "makerAddress", - "type": "address" - }, - { - "indexed": true, - "name": "senderAddress", - "type": "address" - }, - { - "indexed": false, - "name": "orderEpoch", - "type": "uint256" - } - ], - "name": "CancelUpTo", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "name": "id", - "type": "bytes4" - }, - { - "indexed": false, - "name": "assetProxy", - "type": "address" - } - ], - "name": "AssetProxyRegistered", - "type": "event" - } - ], - "evm": { - "bytecode": { - "linkReferences": {}, - "object": "0x60806040526000805460ff191690553480156200001b57600080fd5b50604080517f454950373132446f6d61696e28000000000000000000000000000000000000006020808301919091527f737472696e67206e616d652c0000000000000000000000000000000000000000602d8301527f737472696e672076657273696f6e2c000000000000000000000000000000000060398301527f6164647265737320766572696679696e67436f6e74726163740000000000000060488301527f2900000000000000000000000000000000000000000000000000000000000000606183015282516042818403018152606290920192839052815191929182918401908083835b60208310620001245780518252601f19909201916020918201910162000103565b51815160209384036101000a6000190180199092169116179052604080519290940182900382208285018552600b8084527f30782050726f746f636f6c000000000000000000000000000000000000000000928401928352945190965091945090928392508083835b60208310620001ae5780518252601f1990920191602091820191016200018d565b51815160209384036101000a600019018019909216911617905260408051929094018290038220828501855260018084527f3200000000000000000000000000000000000000000000000000000000000000928401928352945190965091945090928392508083835b60208310620002385780518252601f19909201916020918201910162000217565b51815160209384036101000a6000190180199092169116179052604080519290940182900382208282019890985281840196909652606081019690965250306080808701919091528151808703909101815260a09095019081905284519093849350850191508083835b60208310620002c35780518252601f199092019160209182019101620002a2565b5181516000196020949094036101000a939093019283169219169190911790526040519201829003909120600155505060028054600160a060020a03191633179055506158b380620003166000396000f3006080604052600436106101b65763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663288cdc9181146101bb578063297bb70b146101f15780632ac126221461021e5780633683ef8e1461024b5780633c28d8611461026d5780633e228bae1461029a5780633fd3c997146102ba5780634ac14782146102e75780634d0ae546146103075780634f9559b11461032757806350dde190146103475780636070410814610367578063642f2eaf1461039457806364a3bc15146103b457806377fcce68146103d45780637b8e3514146103f45780637e1d9808146104145780637e9d74dc1461043457806382c174d0146104615780638da5cb5b146104815780639363470214610496578063a3e20380146104b6578063b4be83d5146104d6578063bfc8bfce146104f6578063c585bb9314610516578063c75e0a8114610536578063d46b02c314610563578063d9bfa73e14610583578063db123b1a146105a3578063dd1c7d18146105c5578063e306f779146105e5578063e5fa431b146105fa578063eea086ba1461061a578063f2fde38b1461062f578063ffa1ad741461064f575b600080fd5b3480156101c757600080fd5b506101db6101d6366004614800565b610664565b6040516101e89190615425565b60405180910390f35b3480156101fd57600080fd5b5061021161020c366004614723565b610676565b6040516101e891906156ff565b34801561022a57600080fd5b5061023e610239366004614800565b6107a1565b6040516101e89190615417565b34801561025757600080fd5b5061026b61026636600461483d565b6107b6565b005b34801561027957600080fd5b5061028d610288366004614971565b6108a3565b6040516101e8919061570d565b3480156102a657600080fd5b506102116102b5366004614a31565b610a3a565b3480156102c657600080fd5b506102da6102d5366004614900565b610a90565b6040516101e891906154e1565b3480156102f357600080fd5b5061026b6103023660046146ee565b610ab8565b34801561031357600080fd5b50610211610322366004614723565b610b85565b34801561033357600080fd5b5061026b610342366004614800565b610c75565b34801561035357600080fd5b50610211610362366004614723565b610e2a565b34801561037357600080fd5b50610387610382366004614900565b610ebe565b6040516101e89190615337565b3480156103a057600080fd5b5061023e6103af366004614800565b610f0c565b3480156103c057600080fd5b506102116103cf366004614a31565b610f21565b3480156103e057600080fd5b5061026b6103ef3660046146be565b610fcc565b34801561040057600080fd5b5061023e61040f366004614684565b611106565b34801561042057600080fd5b5061021161042f3660046147b7565b611126565b34801561044057600080fd5b5061045461044f3660046146ee565b61128a565b6040516101e89190615406565b34801561046d57600080fd5b5061023e61047c36600461481e565b61131f565b34801561048d57600080fd5b5061038761133f565b3480156104a257600080fd5b5061023e6104b13660046148a5565b61135b565b3480156104c257600080fd5b506102116104d13660046147b7565b6118de565b3480156104e257600080fd5b506102116104f1366004614a31565b6119f1565b34801561050257600080fd5b5061026b610511366004614a7a565b611a6c565b34801561052257600080fd5b5061026b610531366004614666565b611d05565b34801561054257600080fd5b5061055661055136600461493c565b611f30565b6040516101e8919061571c565b34801561056f57600080fd5b5061026b61057e36600461493c565b61202a565b34801561058f57600080fd5b506101db61059e366004614684565b6120c6565b3480156105af57600080fd5b506105b86120e3565b6040516101e891906154d0565b3480156105d157600080fd5b506102116105e03660046147b7565b612143565b3480156105f157600080fd5b506101db612218565b34801561060657600080fd5b506102116106153660046147b7565b61221e565b34801561062657600080fd5b50610387612390565b34801561063b57600080fd5b5061026b61064a366004614666565b6123ac565b34801561065b57600080fd5b506105b861245d565b60036020526000908152604090205481565b61067e614298565b600080610689614298565b60005460ff16156106cf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b60405180910390fd5b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011781558751935091505b81831461076f57610758878381518110151561071957fe5b90602001906020020151878481518110151561073157fe5b90602001906020020151878581518110151561074957fe5b90602001906020020151612494565b90506107648482612532565b600190910190610701565b5050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055509392505050565b60046020526000908152604090205460ff1681565b73ffffffffffffffffffffffffffffffffffffffff831633146108465761080e848484848080601f0160208091040260200160405190810160405280939291908181526020018383808284375061135b945050505050565b1515610846576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906155af565b5050600091825260066020908152604080842073ffffffffffffffffffffffffffffffffffffffff9093168452919052902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b6108ab6142c1565b6108b36142f0565b6108bb6142f0565b6000805460ff16156108f9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905561016080890151610140808a01919091528901519088015261094588611f30565b925061095087611f30565b915061095a612594565b9050610968888483896125c6565b610974878383886125c6565b61097e888861275e565b6109928888856040015185604001516127be565b8051602081015190519195506109ad918a9186918190612945565b6020808501519081015190516109c99189918591908190612945565b6109e28882856020015186604001518860000151612a5e565b6109fb8782846020015185604001518860200151612a5e565b610a0788888387612b0a565b5050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905550949350505050565b610a42614298565b6060610a4f858585612c92565b9050608081825160208401305af48015610a8657815183526020820151602084015260408201516040840152606082015160608401525b505b509392505050565b600a6020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b60008054819060ff1615610af8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b5050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011781558151905b808214610b5857610b508382815181101515610b4157fe5b90602001906020020151612e64565b600101610b29565b5050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905550565b610b8d614298565b600080610b98614298565b60005460ff1615610bd5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011781558751935091505b81831461076f57610c5e8783815181101515610c1f57fe5b906020019060200201518784815181101515610c3757fe5b906020019060200201518785815181101515610c4f57fe5b90602001906020020151612e8f565b9050610c6a8482612532565b600190910190610c07565b6000805481908190819060ff1615610cb9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055610cec612594565b935073ffffffffffffffffffffffffffffffffffffffff84163314610d115733610d14565b60005b73ffffffffffffffffffffffffffffffffffffffff8086166000908152600560209081526040808320938516835292905220549093506001860192509050808211610d8b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061563f565b73ffffffffffffffffffffffffffffffffffffffff80851660008181526005602090815260408083209488168084529490915290819020859055517f82af639571738f4ebd4268fb0363d8957ebe1bbb9e78dba5ebd69eed39b154f090610df3908690615425565b60405180910390a35050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055505050565b610e32614298565b600080610e3d614298565b86519250600091505b818314610eb457610e9d8783815181101515610e5e57fe5b906020019060200201518784815181101515610e7657fe5b906020019060200201518785815181101515610e8e57fe5b90602001906020020151610a3a565b9050610ea98482612532565b600190910190610e46565b5050509392505050565b7fffffffff0000000000000000000000000000000000000000000000000000000081166000908152600a602052604090205473ffffffffffffffffffffffffffffffffffffffff165b919050565b60086020526000908152604090205460ff1681565b610f29614298565b60005460ff1615610f66576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055610f9c848484612e8f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055949350505050565b6000805460ff161561100a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905561103d612594565b73ffffffffffffffffffffffffffffffffffffffff8181166000818152600760209081526040808320948916808452949091529081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168715151790555192935090917fa8656e308026eeabce8f0bc18048433252318ab80ac79da0b3d3d8697dfba891906110d1908690615417565b60405180910390a35050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905550565b600760209081526000928352604080842090915290825290205460ff1681565b61112e614298565b6060600080600061113d614298565b60005460ff161561117a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117815589518a919081106111b257fe5b906020019060200201516101600151945088519350600092505b828414611255578489848151811015156111e257fe5b906020019060200201516101600181905250611202888760200151612ee2565b915061122e898481518110151561121557fe5b9060200190602002015183898681518110151561074957fe5b905061123a8682612532565b6020860151881161124a57611255565b6001909201916111cc565b5050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055509195945050505050565b606060006060600084519250826040519080825280602002602001820160405280156112d057816020015b6112bd6142f0565b8152602001906001900390816112b55790505b509150600090505b808314610a88576112ff85828151811015156112f057fe5b90602001906020020151611f30565b828281518110151561130d57fe5b602090810290910101526001016112d8565b600660209081526000928352604080842090915290825290205460ff1681565b60025473ffffffffffffffffffffffffffffffffffffffff1681565b600080600080600080600080600089511115156113a4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061562f565b6113ad89612f29565b7f010000000000000000000000000000000000000000000000000000000000000090049650600760ff88161061140f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061554f565b8660ff16600781111561141e57fe5b9550600086600781111561142e57fe5b1415611466576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061561f565b600186600781111561147457fe5b14156114bc578851156114b3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906156ef565b600097506118d0565b60028660078111156114ca57fe5b141561160557885160411461150b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906154ef565b88600081518110151561151a57fe5b01602001517f010000000000000000000000000000000000000000000000000000000000000090819004810204945061155a89600163ffffffff612fed16565b935061156d89602163ffffffff612fed16565b925060018b868686604051600081526020016040526040516115929493929190615480565b60206040516020810390808403906000865af11580156115b6573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015173ffffffffffffffffffffffffffffffffffffffff8c811690821614995092506118d09050565b600386600781111561161357fe5b14156117b9578851604114611654576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906154ef565b88600081518110151561166357fe5b01602001517f01000000000000000000000000000000000000000000000000000000000000009081900481020494506116a389600163ffffffff612fed16565b93506116b689602163ffffffff612fed16565b925060018b60405160200180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c0182600019166000191681526020019150506040516020818303038152906040526040518082805190602001908083835b6020831061175757805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161171a565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018019909216911617905260408051929094018290038220600083529101928390526115929450925089918991508890615480565b60048660078111156117c757fe5b14156117df576117d88b8b8b613038565b97506118d0565b60058660078111156117ed57fe5b1415611850576117fc8961318d565b73ffffffffffffffffffffffffffffffffffffffff808c1660009081526007602090815260408083209385168352929052205490915060ff16151561184457600097506118d0565b6117d8818c8c8c613206565b600686600781111561185e57fe5b141561189e5760008b815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff8e16845290915290205460ff1697506118d0565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061554f565b505050505050509392505050565b6118e6614298565b60606000806000806118f6614298565b89600081518110151561190557fe5b906020019060200201516101400151955089519450600093505b8385146119e457858a8581518110151561193557fe5b6020908102909101015161014001528651611951908a90612ee2565b92506119948a8581518110151561196457fe5b9060200190602002015160a001518b8681518110151561198057fe5b906020019060200201516080015185613362565b91506119c08a858151811015156119a757fe5b90602001906020020151838a87815181101515610e8e57fe5b90506119cc8782612532565b865189116119d9576119e4565b60019093019261191f565b5050505050509392505050565b6119f9614298565b60005460ff1615611a36576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055610f9c848484612494565b60095460009073ffffffffffffffffffffffffffffffffffffffff1615611abf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b611b02611afd888888888080601f016020809104026020016040519081016040528093929190818152602001838380828437506133b8945050505050565b6135f9565b60008181526008602052604090205490915060ff1615611b4e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061559f565b73ffffffffffffffffffffffffffffffffffffffff86163314611c1f57611ba6818785858080601f0160208091040260200160405190810160405280939291908181526020018383808284375061135b945050505050565b1515611bde576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906156df565b600980547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff88161790555b6000818152600860205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555130908690869080838380828437820191505092505050600060405180830381855af49150501515611cb6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906155cf565b73ffffffffffffffffffffffffffffffffffffffff86163314611cfc57600980547fffffffffffffffffffffffff00000000000000000000000000000000000000001690555b50505050505050565b6002546000908190819073ffffffffffffffffffffffffffffffffffffffff163314611d5d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061568f565b8392508273ffffffffffffffffffffffffffffffffffffffff1663ae25532e6040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b158015611dc457600080fd5b505af1158015611dd8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611dfc919081019061491e565b7fffffffff0000000000000000000000000000000000000000000000000000000081166000908152600a602052604090205490925073ffffffffffffffffffffffffffffffffffffffff1690508015611e81576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061552f565b7fffffffff0000000000000000000000000000000000000000000000000000000082166000908152600a60205260409081902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8616179055517fd2c6b762299c609bdb96520b58a49bfb80186934d4f71a86a367571a15c0319490611f2290849087906154b5565b60405180910390a150505050565b611f386142f0565b611f4182613639565b6020808301829052600091825260039052604090819020549082015260808201511515611f755760015b60ff168152610f07565b60a08201511515611f87576002611f6b565b60a0820151604082015110611f9d576005611f6b565b6101008201514210611fb0576004611f6b565b60208082015160009081526004909152604090205460ff1615611fd4576006611f6b565b610120820151825173ffffffffffffffffffffffffffffffffffffffff90811660009081526005602090815260408083206060880151909416835292905220541115612021576006611f6b565b60038152919050565b60005460ff1615612067576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905561209b81612e64565b50600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055565b600560209081526000928352604080842090915290825290205481565b606060405190810160405280602481526020017ff47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f81526020017f4699f4980000000000000000000000000000000000000000000000000000000081525081565b61214b614298565b6060600080600061215a614298565b88600081518110151561216957fe5b906020019060200201516101600151945088519350600092505b82841461220c5784898481518110151561219957fe5b9060200190602002015161016001819052506121b9888760200151612ee2565b91506121e589848151811015156121cc57fe5b90602001906020020151838986815181101515610e8e57fe5b90506121f18682612532565b602086015188116122015761220c565b600190920191612183565b50505050509392505050565b60015481565b612226614298565b6060600080600080612236614298565b60005460ff1615612273576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011781558a518b919081106122ab57fe5b906020019060200201516101400151955089519450600093505b83851461235a57858a858151811015156122db57fe5b60209081029091010151610140015286516122f7908a90612ee2565b925061230a8a8581518110151561196457fe5b91506123368a8581518110151561231d57fe5b90602001906020020151838a8781518110151561074957fe5b90506123428782612532565b8651891161234f5761235a565b6001909301926122c5565b5050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905550929695505050505050565b60095473ffffffffffffffffffffffffffffffffffffffff1681565b60025473ffffffffffffffffffffffffffffffffffffffff1633146123fd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061568f565b73ffffffffffffffffffffffffffffffffffffffff81161561245a57600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83161790555b50565b60408051808201909152600581527f322e302e30000000000000000000000000000000000000000000000000000000602082015281565b61249c614298565b6124a46142f0565b60008060006124b288611f30565b93506124bc612594565b92506124ca888585896125c6565b6124dc8860a001518560400151612ee2565b91506124e88783613647565b90506124fb888589848960000151612945565b612505888261365d565b945061251c88848660200151876040015189612a5e565b6125278884876136be565b505050509392505050565b815181516125409190613776565b8252602080830151908201516125569190613776565b60208301526040808301519082015161256f9190613776565b6040830152606080830151908201516125889190613776565b60609092019190915250565b60095460009073ffffffffffffffffffffffffffffffffffffffff168181156125bd57816125bf565b335b9392505050565b825160ff16600314612604576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906156af565b606084015173ffffffffffffffffffffffffffffffffffffffff161561267757606084015173ffffffffffffffffffffffffffffffffffffffff163314612677576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906156bf565b602084015173ffffffffffffffffffffffffffffffffffffffff1615612702578173ffffffffffffffffffffffffffffffffffffffff16846020015173ffffffffffffffffffffffffffffffffffffffff16141515612702576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906154ff565b6040830151151561275857612720836020015185600001518361135b565b1515612758576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061556f565b50505050565b6127708260a001518260a001516137c0565b612782836080015183608001516137c0565b10156127ba576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906156cf565b5050565b6127c66142c1565b6000806000806127da8960a0015188612ee2565b93506127ef89608001518a60a001518661381b565b92506127ff8860a0015187612ee2565b915061281488608001518960a001518461381b565b90508084106128575760208086018051839052805182018490525151865182015260808a015160a08b015187519092015161284f929061381b565b855152612894565b845183905284516020908101859052855181015190860180519190915260a089015160808a015191515161288b9290613898565b60208087015101525b84515160208087015101516128a99190612ee2565b604086015284515160808a015160c08b01516128c692919061381b565b85516040015284516020015160a08a015160e08b01516128e792919061381b565b855160600152602085015151608089015160c08a015161290892919061381b565b856020015160400181815250506129308560200151602001518960a001518a60e0015161381b565b60208601516060015250505050949350505050565b82151561297e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906155ef565b828211156129b8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906155df565b8460a001516129cb856040015184613776565b1115612a03576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061550f565b612a118560800151836137c0565b612a1f828760a001516137c0565b1115612a57576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061566f565b5050505050565b612a6c828260200151613776565b600084815260036020908152604091829020929092558681015187518451938501518584015160608701516101408c01516101608d015196518b9873ffffffffffffffffffffffffffffffffffffffff9788169897909616967f0bcc4c97732e47d9946f229edb95f5b6323f601300e4690de719993f3c37112996612afb968f96339692959194909390615345565b60405180910390a45050505050565b60608060405190810160405280602481526020017ff47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f81526020017f4699f498000000000000000000000000000000000000000000000000000000008152509050612b8b85610140015186600001518660000151856020015160200151613935565b61014084015184518651845160200151612ba793929190613935565b612bc08561014001518660000151858560400151613935565b612bdc8186600001518760400151856000015160400151613935565b612bf88185600001518660400151856020015160400151613935565b836040015173ffffffffffffffffffffffffffffffffffffffff16856040015173ffffffffffffffffffffffffffffffffffffffff161415612c6257612c5d81848760400151612c58866000015160600151876020015160600151613776565b613935565b612a57565b612c7a81848760400151856000015160600151613935565b612a5781848660400151856020015160600151613935565b604080517fb4be83d5000000000000000000000000000000000000000000000000000000006020808301919091526060602483018181528751608485019081528884015160a48601529488015160c48501529087015160e4840152608087015161010484015260a087015161012484015260c087015161014484015260e08701516101648401526101008701516101848401526101208701516101a4840152610140870180516101c485019081526101608901516101e4860152610180905251805161020485018190529394919384936044870192849261022489019291820191601f82010460005b81811015612d99578351855260209485019490930192600101612d7b565b50505050818103610160808401919091528a0151805180835260209283019291820191601f82010460005b81811015612de2578351855260209485019490930192600101612dc4565b50505089845250848103602093840190815288518083529093918201918981019190601f82010460005b81811015612e2a578351855260209485019490930192600101612e0c565b5050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08883030188525060405250505050509392505050565b612e6c6142f0565b612e7582611f30565b9050612e818282613aff565b6127ba828260200151613c16565b612e97614298565b612ea2848484612494565b602081015190915083146125bf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061565f565b600082821115612f1e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061551f565b508082035b92915050565b6000808251111515612f67576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061560f565b815182907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101908110612f9757fe5b016020015182517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01909252507f0100000000000000000000000000000000000000000000000000000000000000908190040290565b60008160200183511015151561302f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061553f565b50016020015190565b6040516000906060907f1626ba7e00000000000000000000000000000000000000000000000000000000906130739087908690602401615460565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093178352815191935090829081885afa808015613110576001811461318157612527565b7f08c379a0000000000000000000000000000000000000000000000000000000006000527c20000000000000000000000000000000000000000000000000000000006020527c0c57414c4c45545f4552524f5200000000000000000000000000000000604052600060605260646000fd5b50505195945050505050565b600060148251101515156131cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061569f565b6131db826014845103613cbd565b82517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec019092525090565b6040516000906060907f93634702000000000000000000000000000000000000000000000000000000009061324390879087908790602401615433565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931783528151919350908290818a5afa8080156132e0576001811461335157613356565b7f08c379a0000000000000000000000000000000000000000000000000000000006000527c20000000000000000000000000000000000000000000000000000000006020527c0f56414c494441544f525f4552524f5200000000000000000000000000604052600060605260646000fd5b825194505b50505050949350505050565b600080831161339d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061555f565b6133b06133aa85846137c0565b84613d1e565b949350505050565b604080517f5a65726f45785472616e73616374696f6e2800000000000000000000000000006020808301919091527f75696e743235362073616c742c0000000000000000000000000000000000000060328301527f61646472657373207369676e6572416464726573732c00000000000000000000603f8301527f627974657320646174610000000000000000000000000000000000000000000060558301527f2900000000000000000000000000000000000000000000000000000000000000605f830152825180830384018152606090920192839052815160009384938493909282918401908083835b602083106134e157805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016134a4565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018019909216911617905260405191909301819003812089519097508995509093508392850191508083835b6020831061357757805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161353a565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040805192909401829003822097825281019a909a525073ffffffffffffffffffffffffffffffffffffffff97909716968801969096525050606085015250506080909120919050565b6001546040517f19010000000000000000000000000000000000000000000000000000000000008152600281019190915260228101919091526042902090565b6000612f23611afd83613d35565b600081831061365657816125bf565b5090919050565b613665614298565b6020810182905260a0830151608084015161368191849161381b565b808252608084015160c085015161369992919061381b565b604082015260a083015160e08401516136b391849161381b565b606082015292915050565b60408051606081018252602481527ff47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f60208201527f4699f4980000000000000000000000000000000000000000000000000000000091810191909152610140840151845183516137319291908690613935565b61374a8461016001518486600001518560200151613935565b61376281856000015186604001518560400151613935565b612758818486604001518560600151613935565b6000828201838110156137b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061558f565b8091505b5092915050565b6000808315156137d357600091506137b9565b508282028284828115156137e357fe5b04146137b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061558f565b6000808311613856576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061555f565b61386184848461418e565b1561339d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906155bf565b60008083116138d3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061555f565b6138de848484614213565b15613915576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906155bf565b6133b06133aa61392586856137c0565b613930866001612ee2565b613776565b60008060008311801561397457508373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614155b15613af75785516003106139b4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061564f565b50506020848101517fffffffff00000000000000000000000000000000000000000000000000000000166000818152600a90925260409091205473ffffffffffffffffffffffffffffffffffffffff16801515613a3d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906155ff565b604051660fffffffffffe0603f885101168060840182017fa85e59e40000000000000000000000000000000000000000000000000000000083526080600484015273ffffffffffffffffffffffffffffffffffffffff8816602484015273ffffffffffffffffffffffffffffffffffffffff87166044840152856064840152608483015b81811015613ad957895181526020998a019901613ac1565b61020084858403866000895af1801515613af1573d85fd5b50505050505b505050505050565b805160009060ff16600314613b40576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906156af565b606083015173ffffffffffffffffffffffffffffffffffffffff1615613bb357606083015173ffffffffffffffffffffffffffffffffffffffff163314613bb3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906156bf565b613bbb612594565b835190915073ffffffffffffffffffffffffffffffffffffffff808316911614613c11576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061557f565b505050565b6000818152600460205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558281015183516101408501516101608601519351859473ffffffffffffffffffffffffffffffffffffffff9485169493909316927fdc47b3613d9fe400085f6dbdc99453462279057e6207385042827ed6b1a62cf792613cb1923392906153c9565b60405180910390a45050565b600081601401835110151515613cff576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061569f565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b6000808284811515613d2c57fe5b04949350505050565b604080517f4f726465722800000000000000000000000000000000000000000000000000006020808301919091527f61646472657373206d616b6572416464726573732c000000000000000000000060268301527f616464726573732074616b6572416464726573732c0000000000000000000000603b8301527f6164647265737320666565526563697069656e74416464726573732c0000000060508301527f616464726573732073656e646572416464726573732c00000000000000000000606c8301527f75696e74323536206d616b65724173736574416d6f756e742c0000000000000060828301527f75696e743235362074616b65724173736574416d6f756e742c00000000000000609b8301527f75696e74323536206d616b65724665652c00000000000000000000000000000060b48301527f75696e743235362074616b65724665652c00000000000000000000000000000060c58301527f75696e743235362065787069726174696f6e54696d655365636f6e64732c000060d68301527f75696e743235362073616c742c0000000000000000000000000000000000000060f48301527f6279746573206d616b65724173736574446174612c00000000000000000000006101018301527f62797465732074616b65724173736574446174610000000000000000000000006101168301527f290000000000000000000000000000000000000000000000000000000000000061012a830152825161010b81840301815261012b90920192839052815160009384938493849391929182918401908083835b60208310613fbd57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101613f80565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930181900381206101408b0151805191995095509093508392850191508083835b6020831061405857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161401b565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930181900381206101608b0151805191985095509093508392850191508083835b602083106140f357805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016140b6565b5181516020939093036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018019909116921691909117905260405192018290039091207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0890180516101408b018051610160909c0180519a84529881529288526101a0822091529890525050509190525090919050565b6000808084116141ca576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061555f565b8215806141d5575084155b156141e35760009150610a88565b838015156141ed57fe5b85840990506141fc85846137c0565b6142086103e8836137c0565b101595945050505050565b60008080841161424f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061555f565b82158061425a575084155b156142685760009150610a88565b8380151561427257fe5b8584099050836142828583612ee2565b81151561428b57fe5b0690506141fc85846137c0565b608060405190810160405280600081526020016000815260200160008152602001600081525090565b610120604051908101604052806142d6614298565b81526020016142e3614298565b8152602001600081525090565b604080516060810182526000808252602082018190529181019190915290565b60006125bf82356157c2565b6000601f8201831361432d57600080fd5b813561434061433b82615751565b61572a565b81815260209384019390925082018360005b8381101561437e578135860161436888826144ce565b8452506020928301929190910190600101614352565b5050505092915050565b6000601f8201831361439957600080fd5b81356143a761433b82615751565b81815260209384019390925082018360005b8381101561437e57813586016143cf888261451d565b84525060209283019291909101906001016143b9565b6000601f820183136143f657600080fd5b813561440461433b82615751565b9150818183526020840193506020810190508385602084028201111561442957600080fd5b60005b8381101561437e578161443f8882614461565b845250602092830192919091019060010161442c565b60006125bf82356157db565b60006125bf82356157e0565b60006125bf82356157e3565b60006125bf82516157e3565b600080601f8301841361449757600080fd5b50813567ffffffffffffffff8111156144af57600080fd5b6020830191508360018202830111156144c757600080fd5b9250929050565b6000601f820183136144df57600080fd5b81356144ed61433b82615772565b9150808252602083016020830185838301111561450957600080fd5b614514838284615819565b50505092915050565b6000610180828403121561453057600080fd5b61453b61018061572a565b905060006145498484614310565b825250602061455a84848301614310565b602083015250604061456e84828501614310565b604083015250606061458284828501614310565b606083015250608061459684828501614461565b60808301525060a06145aa84828501614461565b60a08301525060c06145be84828501614461565b60c08301525060e06145d284828501614461565b60e0830152506101006145e784828501614461565b610100830152506101206145fd84828501614461565b6101208301525061014082013567ffffffffffffffff81111561461f57600080fd5b61462b848285016144ce565b6101408301525061016082013567ffffffffffffffff81111561464d57600080fd5b614659848285016144ce565b6101608301525092915050565b60006020828403121561467857600080fd5b60006133b08484614310565b6000806040838503121561469757600080fd5b60006146a38585614310565b92505060206146b485828601614310565b9150509250929050565b600080604083850312156146d157600080fd5b60006146dd8585614310565b92505060206146b485828601614455565b60006020828403121561470057600080fd5b813567ffffffffffffffff81111561471757600080fd5b6133b084828501614388565b60008060006060848603121561473857600080fd5b833567ffffffffffffffff81111561474f57600080fd5b61475b86828701614388565b935050602084013567ffffffffffffffff81111561477857600080fd5b614784868287016143e5565b925050604084013567ffffffffffffffff8111156147a157600080fd5b6147ad8682870161431c565b9150509250925092565b6000806000606084860312156147cc57600080fd5b833567ffffffffffffffff8111156147e357600080fd5b6147ef86828701614388565b935050602061478486828701614461565b60006020828403121561481257600080fd5b60006133b08484614461565b6000806040838503121561483157600080fd5b60006146a38585614461565b6000806000806060858703121561485357600080fd5b600061485f8787614461565b945050602061487087828801614310565b935050604085013567ffffffffffffffff81111561488d57600080fd5b61489987828801614485565b95989497509550505050565b6000806000606084860312156148ba57600080fd5b60006148c68686614461565b93505060206148d786828701614310565b925050604084013567ffffffffffffffff8111156148f457600080fd5b6147ad868287016144ce565b60006020828403121561491257600080fd5b60006133b0848461446d565b60006020828403121561493057600080fd5b60006133b08484614479565b60006020828403121561494e57600080fd5b813567ffffffffffffffff81111561496557600080fd5b6133b08482850161451d565b6000806000806080858703121561498757600080fd5b843567ffffffffffffffff81111561499e57600080fd5b6149aa8782880161451d565b945050602085013567ffffffffffffffff8111156149c757600080fd5b6149d38782880161451d565b935050604085013567ffffffffffffffff8111156149f057600080fd5b6149fc878288016144ce565b925050606085013567ffffffffffffffff811115614a1957600080fd5b614a25878288016144ce565b91505092959194509250565b600080600060608486031215614a4657600080fd5b833567ffffffffffffffff811115614a5d57600080fd5b614a698682870161451d565b93505060206148d786828701614461565b60008060008060008060808789031215614a9357600080fd5b6000614a9f8989614461565b9650506020614ab089828a01614310565b955050604087013567ffffffffffffffff811115614acd57600080fd5b614ad989828a01614485565b9450945050606087013567ffffffffffffffff811115614af857600080fd5b614b0489828a01614485565b92509250509295509295509295565b614b1c816157c2565b82525050565b6000614b2d826157be565b808452602084019350614b3f836157b8565b60005b82811015614b6f57614b558683516152f7565b614b5e826157b8565b606096909601959150600101614b42565b5093949350505050565b614b1c816157db565b614b1c816157e0565b614b1c816157e3565b6000614b9f826157be565b808452614bb3816020860160208601615825565b614bbc81615851565b9093016020019392505050565b614b1c8161580e565b601281527f4c454e4754485f36355f52455155495245440000000000000000000000000000602082015260400190565b600d81527f494e56414c49445f54414b455200000000000000000000000000000000000000602082015260400190565b600e81527f4f524445525f4f56455246494c4c000000000000000000000000000000000000602082015260400190565b601181527f55494e543235365f554e444552464c4f57000000000000000000000000000000602082015260400190565b601a81527f41535345545f50524f58595f414c52454144595f455849535453000000000000602082015260400190565b602681527f475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b601581527f5349474e41545552455f554e535550504f525445440000000000000000000000602082015260400190565b601081527f4449564953494f4e5f42595f5a45524f00000000000000000000000000000000602082015260400190565b601781527f494e56414c49445f4f524445525f5349474e4154555245000000000000000000602082015260400190565b600d81527f494e56414c49445f4d414b455200000000000000000000000000000000000000602082015260400190565b601081527f55494e543235365f4f564552464c4f5700000000000000000000000000000000602082015260400190565b600f81527f494e56414c49445f54585f484153480000000000000000000000000000000000602082015260400190565b601181527f494e56414c49445f5349474e4154555245000000000000000000000000000000602082015260400190565b600e81527f524f554e44494e475f4552524f52000000000000000000000000000000000000602082015260400190565b601081527f4641494c45445f455845435554494f4e00000000000000000000000000000000602082015260400190565b600d81527f54414b45525f4f56455250415900000000000000000000000000000000000000602082015260400190565b601481527f494e56414c49445f54414b45525f414d4f554e54000000000000000000000000602082015260400190565b601a81527f41535345545f50524f58595f444f45535f4e4f545f4558495354000000000000602082015260400190565b602181527f475245415445525f5448414e5f5a45524f5f4c454e4754485f5245515549524560208201527f4400000000000000000000000000000000000000000000000000000000000000604082015260600190565b601181527f5349474e41545552455f494c4c4547414c000000000000000000000000000000602082015260400190565b601e81527f4c454e4754485f475245415445525f5448414e5f305f52455155495245440000602082015260400190565b601781527f494e56414c49445f4e45575f4f524445525f45504f4348000000000000000000602082015260400190565b601e81527f4c454e4754485f475245415445525f5448414e5f335f52455155495245440000602082015260400190565b601481527f434f4d504c4554455f46494c4c5f4641494c4544000000000000000000000000602082015260400190565b601281527f494e56414c49445f46494c4c5f50524943450000000000000000000000000000602082015260400190565b601281527f5245454e5452414e43595f494c4c4547414c0000000000000000000000000000602082015260400190565b601381527f4f4e4c595f434f4e54524143545f4f574e455200000000000000000000000000602082015260400190565b602681527f475245415445525f4f525f455155414c5f544f5f32305f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b601081527f4f524445525f554e46494c4c41424c4500000000000000000000000000000000602082015260400190565b600e81527f494e56414c49445f53454e444552000000000000000000000000000000000000602082015260400190565b601881527f4e454741544956455f5350524541445f52455155495245440000000000000000602082015260400190565b601481527f494e56414c49445f54585f5349474e4154555245000000000000000000000000602082015260400190565b601181527f4c454e4754485f305f5245515549524544000000000000000000000000000000602082015260400190565b805160808301906152858482614b82565b5060208201516152986020850182614b82565b5060408201516152ab6040850182614b82565b5060608201516127586060850182614b82565b80516101208301906152d08482615274565b5060208201516152e36080850182615274565b506040820151612758610100850182614b82565b80516060830190615308848261532e565b50602082015161531b6020850182614b82565b5060408201516127586040850182614b82565b614b1c81615808565b60208101612f238284614b13565b6101008101615354828b614b13565b615361602083018a614b13565b61536e6040830189614b82565b61537b6060830188614b82565b6153886080830187614b82565b61539560a0830186614b82565b81810360c08301526153a78185614b94565b905081810360e08301526153bb8184614b94565b9a9950505050505050505050565b606081016153d78286614b13565b81810360208301526153e98185614b94565b905081810360408301526153fd8184614b94565b95945050505050565b602080825281016125bf8184614b22565b60208101612f238284614b79565b60208101612f238284614b82565b606081016154418286614b82565b61544e6020830185614b13565b81810360408301526153fd8184614b94565b6040810161546e8285614b82565b81810360208301526133b08184614b94565b6080810161548e8287614b82565b61549b602083018661532e565b6154a86040830185614b82565b6153fd6060830184614b82565b604081016154c38285614b8b565b6125bf6020830184614b13565b602080825281016125bf8184614b94565b60208101612f238284614bc9565b60208082528101612f2381614bd2565b60208082528101612f2381614c02565b60208082528101612f2381614c32565b60208082528101612f2381614c62565b60208082528101612f2381614c92565b60208082528101612f2381614cc2565b60208082528101612f2381614d18565b60208082528101612f2381614d48565b60208082528101612f2381614d78565b60208082528101612f2381614da8565b60208082528101612f2381614dd8565b60208082528101612f2381614e08565b60208082528101612f2381614e38565b60208082528101612f2381614e68565b60208082528101612f2381614e98565b60208082528101612f2381614ec8565b60208082528101612f2381614ef8565b60208082528101612f2381614f28565b60208082528101612f2381614f58565b60208082528101612f2381614fae565b60208082528101612f2381614fde565b60208082528101612f238161500e565b60208082528101612f238161503e565b60208082528101612f238161506e565b60208082528101612f238161509e565b60208082528101612f23816150ce565b60208082528101612f23816150fe565b60208082528101612f238161512e565b60208082528101612f2381615184565b60208082528101612f23816151b4565b60208082528101612f23816151e4565b60208082528101612f2381615214565b60208082528101612f2381615244565b60808101612f238284615274565b6101208101612f2382846152be565b60608101612f2382846152f7565b60405181810167ffffffffffffffff8111828210171561574957600080fd5b604052919050565b600067ffffffffffffffff82111561576857600080fd5b5060209081020190565b600067ffffffffffffffff82111561578957600080fd5b506020601f919091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160190565b60200190565b5190565b73ffffffffffffffffffffffffffffffffffffffff1690565b151590565b90565b7fffffffff000000000000000000000000000000000000000000000000000000001690565b60ff1690565b6000612f23826157c2565b82818337506000910152565b60005b83811015615840578181015183820152602001615828565b838111156127585750506000910152565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016905600a265627a7a72305820b19c92201032603398c007dccf80930c626ef89fa6c848c8b7ef5267d07cd4ed6c6578706572696d656e74616cf50037", - "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x0 DUP1 SLOAD PUSH1 0xFF NOT AND SWAP1 SSTORE CALLVALUE DUP1 ISZERO PUSH3 0x1B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x40 DUP1 MLOAD PUSH32 0x454950373132446F6D61696E2800000000000000000000000000000000000000 PUSH1 0x20 DUP1 DUP4 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH32 0x737472696E67206E616D652C0000000000000000000000000000000000000000 PUSH1 0x2D DUP4 ADD MSTORE PUSH32 0x737472696E672076657273696F6E2C0000000000000000000000000000000000 PUSH1 0x39 DUP4 ADD MSTORE PUSH32 0x6164647265737320766572696679696E67436F6E747261637400000000000000 PUSH1 0x48 DUP4 ADD MSTORE PUSH32 0x2900000000000000000000000000000000000000000000000000000000000000 PUSH1 0x61 DUP4 ADD MSTORE DUP3 MLOAD PUSH1 0x42 DUP2 DUP5 SUB ADD DUP2 MSTORE PUSH1 0x62 SWAP1 SWAP3 ADD SWAP3 DUP4 SWAP1 MSTORE DUP2 MLOAD SWAP2 SWAP3 SWAP2 DUP3 SWAP2 DUP5 ADD SWAP1 DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH3 0x124 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH1 0x1F NOT SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH3 0x103 JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 DUP5 SUB PUSH2 0x100 EXP PUSH1 0x0 NOT ADD DUP1 NOT SWAP1 SWAP3 AND SWAP2 AND OR SWAP1 MSTORE PUSH1 0x40 DUP1 MLOAD SWAP3 SWAP1 SWAP5 ADD DUP3 SWAP1 SUB DUP3 KECCAK256 DUP3 DUP6 ADD DUP6 MSTORE PUSH1 0xB DUP1 DUP5 MSTORE PUSH32 0x30782050726F746F636F6C000000000000000000000000000000000000000000 SWAP3 DUP5 ADD SWAP3 DUP4 MSTORE SWAP5 MLOAD SWAP1 SWAP7 POP SWAP2 SWAP5 POP SWAP1 SWAP3 DUP4 SWAP3 POP DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH3 0x1AE JUMPI DUP1 MLOAD DUP3 MSTORE PUSH1 0x1F NOT SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH3 0x18D JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 DUP5 SUB PUSH2 0x100 EXP PUSH1 0x0 NOT ADD DUP1 NOT SWAP1 SWAP3 AND SWAP2 AND OR SWAP1 MSTORE PUSH1 0x40 DUP1 MLOAD SWAP3 SWAP1 SWAP5 ADD DUP3 SWAP1 SUB DUP3 KECCAK256 DUP3 DUP6 ADD DUP6 MSTORE PUSH1 0x1 DUP1 DUP5 MSTORE PUSH32 0x3200000000000000000000000000000000000000000000000000000000000000 SWAP3 DUP5 ADD SWAP3 DUP4 MSTORE SWAP5 MLOAD SWAP1 SWAP7 POP SWAP2 SWAP5 POP SWAP1 SWAP3 DUP4 SWAP3 POP DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH3 0x238 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH1 0x1F NOT SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH3 0x217 JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 DUP5 SUB PUSH2 0x100 EXP PUSH1 0x0 NOT ADD DUP1 NOT SWAP1 SWAP3 AND SWAP2 AND OR SWAP1 MSTORE PUSH1 0x40 DUP1 MLOAD SWAP3 SWAP1 SWAP5 ADD DUP3 SWAP1 SUB DUP3 KECCAK256 DUP3 DUP3 ADD SWAP9 SWAP1 SWAP9 MSTORE DUP2 DUP5 ADD SWAP7 SWAP1 SWAP7 MSTORE PUSH1 0x60 DUP2 ADD SWAP7 SWAP1 SWAP7 MSTORE POP ADDRESS PUSH1 0x80 DUP1 DUP8 ADD SWAP2 SWAP1 SWAP2 MSTORE DUP2 MLOAD DUP1 DUP8 SUB SWAP1 SWAP2 ADD DUP2 MSTORE PUSH1 0xA0 SWAP1 SWAP6 ADD SWAP1 DUP2 SWAP1 MSTORE DUP5 MLOAD SWAP1 SWAP4 DUP5 SWAP4 POP DUP6 ADD SWAP2 POP DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH3 0x2C3 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH1 0x1F NOT SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH3 0x2A2 JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x0 NOT PUSH1 0x20 SWAP5 SWAP1 SWAP5 SUB PUSH2 0x100 EXP SWAP4 SWAP1 SWAP4 ADD SWAP3 DUP4 AND SWAP3 NOT AND SWAP2 SWAP1 SWAP2 OR SWAP1 MSTORE PUSH1 0x40 MLOAD SWAP3 ADD DUP3 SWAP1 SUB SWAP1 SWAP2 KECCAK256 PUSH1 0x1 SSTORE POP POP PUSH1 0x2 DUP1 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB NOT AND CALLER OR SWAP1 SSTORE POP PUSH2 0x58B3 DUP1 PUSH3 0x316 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0x1B6 JUMPI PUSH4 0xFFFFFFFF PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0x288CDC91 DUP2 EQ PUSH2 0x1BB JUMPI DUP1 PUSH4 0x297BB70B EQ PUSH2 0x1F1 JUMPI DUP1 PUSH4 0x2AC12622 EQ PUSH2 0x21E JUMPI DUP1 PUSH4 0x3683EF8E EQ PUSH2 0x24B JUMPI DUP1 PUSH4 0x3C28D861 EQ PUSH2 0x26D JUMPI DUP1 PUSH4 0x3E228BAE EQ PUSH2 0x29A JUMPI DUP1 PUSH4 0x3FD3C997 EQ PUSH2 0x2BA JUMPI DUP1 PUSH4 0x4AC14782 EQ PUSH2 0x2E7 JUMPI DUP1 PUSH4 0x4D0AE546 EQ PUSH2 0x307 JUMPI DUP1 PUSH4 0x4F9559B1 EQ PUSH2 0x327 JUMPI DUP1 PUSH4 0x50DDE190 EQ PUSH2 0x347 JUMPI DUP1 PUSH4 0x60704108 EQ PUSH2 0x367 JUMPI DUP1 PUSH4 0x642F2EAF EQ PUSH2 0x394 JUMPI DUP1 PUSH4 0x64A3BC15 EQ PUSH2 0x3B4 JUMPI DUP1 PUSH4 0x77FCCE68 EQ PUSH2 0x3D4 JUMPI DUP1 PUSH4 0x7B8E3514 EQ PUSH2 0x3F4 JUMPI DUP1 PUSH4 0x7E1D9808 EQ PUSH2 0x414 JUMPI DUP1 PUSH4 0x7E9D74DC EQ PUSH2 0x434 JUMPI DUP1 PUSH4 0x82C174D0 EQ PUSH2 0x461 JUMPI DUP1 PUSH4 0x8DA5CB5B EQ PUSH2 0x481 JUMPI DUP1 PUSH4 0x93634702 EQ PUSH2 0x496 JUMPI DUP1 PUSH4 0xA3E20380 EQ PUSH2 0x4B6 JUMPI DUP1 PUSH4 0xB4BE83D5 EQ PUSH2 0x4D6 JUMPI DUP1 PUSH4 0xBFC8BFCE EQ PUSH2 0x4F6 JUMPI DUP1 PUSH4 0xC585BB93 EQ PUSH2 0x516 JUMPI DUP1 PUSH4 0xC75E0A81 EQ PUSH2 0x536 JUMPI DUP1 PUSH4 0xD46B02C3 EQ PUSH2 0x563 JUMPI DUP1 PUSH4 0xD9BFA73E EQ PUSH2 0x583 JUMPI DUP1 PUSH4 0xDB123B1A EQ PUSH2 0x5A3 JUMPI DUP1 PUSH4 0xDD1C7D18 EQ PUSH2 0x5C5 JUMPI DUP1 PUSH4 0xE306F779 EQ PUSH2 0x5E5 JUMPI DUP1 PUSH4 0xE5FA431B EQ PUSH2 0x5FA JUMPI DUP1 PUSH4 0xEEA086BA EQ PUSH2 0x61A JUMPI DUP1 PUSH4 0xF2FDE38B EQ PUSH2 0x62F JUMPI DUP1 PUSH4 0xFFA1AD74 EQ PUSH2 0x64F JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1C7 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1DB PUSH2 0x1D6 CALLDATASIZE PUSH1 0x4 PUSH2 0x4800 JUMP JUMPDEST PUSH2 0x664 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x5425 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1FD JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x20C CALLDATASIZE PUSH1 0x4 PUSH2 0x4723 JUMP JUMPDEST PUSH2 0x676 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x56FF JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x22A JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x23E PUSH2 0x239 CALLDATASIZE PUSH1 0x4 PUSH2 0x4800 JUMP JUMPDEST PUSH2 0x7A1 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x5417 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x257 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x266 CALLDATASIZE PUSH1 0x4 PUSH2 0x483D JUMP JUMPDEST PUSH2 0x7B6 JUMP JUMPDEST STOP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x279 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x28D PUSH2 0x288 CALLDATASIZE PUSH1 0x4 PUSH2 0x4971 JUMP JUMPDEST PUSH2 0x8A3 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x570D JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x2A6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x2B5 CALLDATASIZE PUSH1 0x4 PUSH2 0x4A31 JUMP JUMPDEST PUSH2 0xA3A JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x2C6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x2DA PUSH2 0x2D5 CALLDATASIZE PUSH1 0x4 PUSH2 0x4900 JUMP JUMPDEST PUSH2 0xA90 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x54E1 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x2F3 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x302 CALLDATASIZE PUSH1 0x4 PUSH2 0x46EE JUMP JUMPDEST PUSH2 0xAB8 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x313 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x322 CALLDATASIZE PUSH1 0x4 PUSH2 0x4723 JUMP JUMPDEST PUSH2 0xB85 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x333 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x342 CALLDATASIZE PUSH1 0x4 PUSH2 0x4800 JUMP JUMPDEST PUSH2 0xC75 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x353 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x362 CALLDATASIZE PUSH1 0x4 PUSH2 0x4723 JUMP JUMPDEST PUSH2 0xE2A JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x373 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x387 PUSH2 0x382 CALLDATASIZE PUSH1 0x4 PUSH2 0x4900 JUMP JUMPDEST PUSH2 0xEBE JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x5337 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x3A0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x23E PUSH2 0x3AF CALLDATASIZE PUSH1 0x4 PUSH2 0x4800 JUMP JUMPDEST PUSH2 0xF0C JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x3C0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x3CF CALLDATASIZE PUSH1 0x4 PUSH2 0x4A31 JUMP JUMPDEST PUSH2 0xF21 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x3E0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x3EF CALLDATASIZE PUSH1 0x4 PUSH2 0x46BE JUMP JUMPDEST PUSH2 0xFCC JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x400 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x23E PUSH2 0x40F CALLDATASIZE PUSH1 0x4 PUSH2 0x4684 JUMP JUMPDEST PUSH2 0x1106 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x420 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x42F CALLDATASIZE PUSH1 0x4 PUSH2 0x47B7 JUMP JUMPDEST PUSH2 0x1126 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x440 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x454 PUSH2 0x44F CALLDATASIZE PUSH1 0x4 PUSH2 0x46EE JUMP JUMPDEST PUSH2 0x128A JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x5406 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x46D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x23E PUSH2 0x47C CALLDATASIZE PUSH1 0x4 PUSH2 0x481E JUMP JUMPDEST PUSH2 0x131F JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x48D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x387 PUSH2 0x133F JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x4A2 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x23E PUSH2 0x4B1 CALLDATASIZE PUSH1 0x4 PUSH2 0x48A5 JUMP JUMPDEST PUSH2 0x135B JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x4C2 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x4D1 CALLDATASIZE PUSH1 0x4 PUSH2 0x47B7 JUMP JUMPDEST PUSH2 0x18DE JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x4E2 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x4F1 CALLDATASIZE PUSH1 0x4 PUSH2 0x4A31 JUMP JUMPDEST PUSH2 0x19F1 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x502 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x511 CALLDATASIZE PUSH1 0x4 PUSH2 0x4A7A JUMP JUMPDEST PUSH2 0x1A6C JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x522 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x531 CALLDATASIZE PUSH1 0x4 PUSH2 0x4666 JUMP JUMPDEST PUSH2 0x1D05 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x542 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x556 PUSH2 0x551 CALLDATASIZE PUSH1 0x4 PUSH2 0x493C JUMP JUMPDEST PUSH2 0x1F30 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x571C JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x56F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x57E CALLDATASIZE PUSH1 0x4 PUSH2 0x493C JUMP JUMPDEST PUSH2 0x202A JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x58F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1DB PUSH2 0x59E CALLDATASIZE PUSH1 0x4 PUSH2 0x4684 JUMP JUMPDEST PUSH2 0x20C6 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x5AF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x5B8 PUSH2 0x20E3 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x54D0 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x5D1 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x5E0 CALLDATASIZE PUSH1 0x4 PUSH2 0x47B7 JUMP JUMPDEST PUSH2 0x2143 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x5F1 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1DB PUSH2 0x2218 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x606 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x615 CALLDATASIZE PUSH1 0x4 PUSH2 0x47B7 JUMP JUMPDEST PUSH2 0x221E JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x626 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x387 PUSH2 0x2390 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x63B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x64A CALLDATASIZE PUSH1 0x4 PUSH2 0x4666 JUMP JUMPDEST PUSH2 0x23AC JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x65B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x5B8 PUSH2 0x245D JUMP JUMPDEST PUSH1 0x3 PUSH1 0x20 MSTORE PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP2 JUMP JUMPDEST PUSH2 0x67E PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH2 0x689 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x6CF JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR DUP2 SSTORE DUP8 MLOAD SWAP4 POP SWAP2 POP JUMPDEST DUP2 DUP4 EQ PUSH2 0x76F JUMPI PUSH2 0x758 DUP8 DUP4 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x719 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP8 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x731 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP8 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x749 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x2494 JUMP JUMPDEST SWAP1 POP PUSH2 0x764 DUP5 DUP3 PUSH2 0x2532 JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP2 ADD SWAP1 PUSH2 0x701 JUMP JUMPDEST POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x4 PUSH1 0x20 MSTORE PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND CALLER EQ PUSH2 0x846 JUMPI PUSH2 0x80E DUP5 DUP5 DUP5 DUP5 DUP1 DUP1 PUSH1 0x1F ADD PUSH1 0x20 DUP1 SWAP2 DIV MUL PUSH1 0x20 ADD PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 SWAP4 SWAP3 SWAP2 SWAP1 DUP2 DUP2 MSTORE PUSH1 0x20 ADD DUP4 DUP4 DUP1 DUP3 DUP5 CALLDATACOPY POP PUSH2 0x135B SWAP5 POP POP POP POP POP JUMP JUMPDEST ISZERO ISZERO PUSH2 0x846 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x55AF JUMP JUMPDEST POP POP PUSH1 0x0 SWAP2 DUP3 MSTORE PUSH1 0x6 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP4 AND DUP5 MSTORE SWAP2 SWAP1 MSTORE SWAP1 KECCAK256 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE JUMP JUMPDEST PUSH2 0x8AB PUSH2 0x42C1 JUMP JUMPDEST PUSH2 0x8B3 PUSH2 0x42F0 JUMP JUMPDEST PUSH2 0x8BB PUSH2 0x42F0 JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x8F9 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE PUSH2 0x160 DUP1 DUP10 ADD MLOAD PUSH2 0x140 DUP1 DUP11 ADD SWAP2 SWAP1 SWAP2 MSTORE DUP10 ADD MLOAD SWAP1 DUP9 ADD MSTORE PUSH2 0x945 DUP9 PUSH2 0x1F30 JUMP JUMPDEST SWAP3 POP PUSH2 0x950 DUP8 PUSH2 0x1F30 JUMP JUMPDEST SWAP2 POP PUSH2 0x95A PUSH2 0x2594 JUMP JUMPDEST SWAP1 POP PUSH2 0x968 DUP9 DUP5 DUP4 DUP10 PUSH2 0x25C6 JUMP JUMPDEST PUSH2 0x974 DUP8 DUP4 DUP4 DUP9 PUSH2 0x25C6 JUMP JUMPDEST PUSH2 0x97E DUP9 DUP9 PUSH2 0x275E JUMP JUMPDEST PUSH2 0x992 DUP9 DUP9 DUP6 PUSH1 0x40 ADD MLOAD DUP6 PUSH1 0x40 ADD MLOAD PUSH2 0x27BE JUMP JUMPDEST DUP1 MLOAD PUSH1 0x20 DUP2 ADD MLOAD SWAP1 MLOAD SWAP2 SWAP6 POP PUSH2 0x9AD SWAP2 DUP11 SWAP2 DUP7 SWAP2 DUP2 SWAP1 PUSH2 0x2945 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP6 ADD MLOAD SWAP1 DUP2 ADD MLOAD SWAP1 MLOAD PUSH2 0x9C9 SWAP2 DUP10 SWAP2 DUP6 SWAP2 SWAP1 DUP2 SWAP1 PUSH2 0x2945 JUMP JUMPDEST PUSH2 0x9E2 DUP9 DUP3 DUP6 PUSH1 0x20 ADD MLOAD DUP7 PUSH1 0x40 ADD MLOAD DUP9 PUSH1 0x0 ADD MLOAD PUSH2 0x2A5E JUMP JUMPDEST PUSH2 0x9FB DUP8 DUP3 DUP5 PUSH1 0x20 ADD MLOAD DUP6 PUSH1 0x40 ADD MLOAD DUP9 PUSH1 0x20 ADD MLOAD PUSH2 0x2A5E JUMP JUMPDEST PUSH2 0xA07 DUP9 DUP9 DUP4 DUP8 PUSH2 0x2B0A JUMP JUMPDEST POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE POP SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH2 0xA42 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x60 PUSH2 0xA4F DUP6 DUP6 DUP6 PUSH2 0x2C92 JUMP JUMPDEST SWAP1 POP PUSH1 0x80 DUP2 DUP3 MLOAD PUSH1 0x20 DUP5 ADD ADDRESS GAS DELEGATECALL DUP1 ISZERO PUSH2 0xA86 JUMPI DUP2 MLOAD DUP4 MSTORE PUSH1 0x20 DUP3 ADD MLOAD PUSH1 0x20 DUP5 ADD MSTORE PUSH1 0x40 DUP3 ADD MLOAD PUSH1 0x40 DUP5 ADD MSTORE PUSH1 0x60 DUP3 ADD MLOAD PUSH1 0x60 DUP5 ADD MSTORE JUMPDEST POP JUMPDEST POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0xA PUSH1 0x20 MSTORE PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD DUP2 SWAP1 PUSH1 0xFF AND ISZERO PUSH2 0xAF8 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR DUP2 SSTORE DUP2 MLOAD SWAP1 JUMPDEST DUP1 DUP3 EQ PUSH2 0xB58 JUMPI PUSH2 0xB50 DUP4 DUP3 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xB41 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x2E64 JUMP JUMPDEST PUSH1 0x1 ADD PUSH2 0xB29 JUMP JUMPDEST POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE POP JUMP JUMPDEST PUSH2 0xB8D PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH2 0xB98 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0xBD5 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR DUP2 SSTORE DUP8 MLOAD SWAP4 POP SWAP2 POP JUMPDEST DUP2 DUP4 EQ PUSH2 0x76F JUMPI PUSH2 0xC5E DUP8 DUP4 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xC1F JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP8 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xC37 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP8 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xC4F JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x2E8F JUMP JUMPDEST SWAP1 POP PUSH2 0xC6A DUP5 DUP3 PUSH2 0x2532 JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP2 ADD SWAP1 PUSH2 0xC07 JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD DUP2 SWAP1 DUP2 SWAP1 DUP2 SWAP1 PUSH1 0xFF AND ISZERO PUSH2 0xCB9 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE PUSH2 0xCEC PUSH2 0x2594 JUMP JUMPDEST SWAP4 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 AND CALLER EQ PUSH2 0xD11 JUMPI CALLER PUSH2 0xD14 JUMP JUMPDEST PUSH1 0x0 JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP7 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x5 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP4 DUP6 AND DUP4 MSTORE SWAP3 SWAP1 MSTORE KECCAK256 SLOAD SWAP1 SWAP4 POP PUSH1 0x1 DUP7 ADD SWAP3 POP SWAP1 POP DUP1 DUP3 GT PUSH2 0xD8B JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x563F JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP6 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x5 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP5 DUP9 AND DUP1 DUP5 MSTORE SWAP5 SWAP1 SWAP2 MSTORE SWAP1 DUP2 SWAP1 KECCAK256 DUP6 SWAP1 SSTORE MLOAD PUSH32 0x82AF639571738F4EBD4268FB0363D8957EBE1BBB9E78DBA5EBD69EED39B154F0 SWAP1 PUSH2 0xDF3 SWAP1 DUP7 SWAP1 PUSH2 0x5425 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE POP POP POP JUMP JUMPDEST PUSH2 0xE32 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH2 0xE3D PUSH2 0x4298 JUMP JUMPDEST DUP7 MLOAD SWAP3 POP PUSH1 0x0 SWAP2 POP JUMPDEST DUP2 DUP4 EQ PUSH2 0xEB4 JUMPI PUSH2 0xE9D DUP8 DUP4 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xE5E JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP8 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xE76 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP8 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xE8E JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0xA3A JUMP JUMPDEST SWAP1 POP PUSH2 0xEA9 DUP5 DUP3 PUSH2 0x2532 JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP2 ADD SWAP1 PUSH2 0xE46 JUMP JUMPDEST POP POP POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 DUP2 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0xA PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND JUMPDEST SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x8 PUSH1 0x20 MSTORE PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND DUP2 JUMP JUMPDEST PUSH2 0xF29 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0xF66 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE PUSH2 0xF9C DUP5 DUP5 DUP5 PUSH2 0x2E8F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x100A JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE PUSH2 0x103D PUSH2 0x2594 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 DUP2 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x7 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP5 DUP10 AND DUP1 DUP5 MSTORE SWAP5 SWAP1 SWAP2 MSTORE SWAP1 DUP2 SWAP1 KECCAK256 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND DUP8 ISZERO ISZERO OR SWAP1 SSTORE MLOAD SWAP3 SWAP4 POP SWAP1 SWAP2 PUSH32 0xA8656E308026EEABCE8F0BC18048433252318AB80AC79DA0B3D3D8697DFBA891 SWAP1 PUSH2 0x10D1 SWAP1 DUP7 SWAP1 PUSH2 0x5417 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE POP JUMP JUMPDEST PUSH1 0x7 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x0 SWAP3 DUP4 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 SWAP1 SWAP2 MSTORE SWAP1 DUP3 MSTORE SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND DUP2 JUMP JUMPDEST PUSH2 0x112E PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x60 PUSH1 0x0 DUP1 PUSH1 0x0 PUSH2 0x113D PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x117A JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR DUP2 SSTORE DUP10 MLOAD DUP11 SWAP2 SWAP1 DUP2 LT PUSH2 0x11B2 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x160 ADD MLOAD SWAP5 POP DUP9 MLOAD SWAP4 POP PUSH1 0x0 SWAP3 POP JUMPDEST DUP3 DUP5 EQ PUSH2 0x1255 JUMPI DUP5 DUP10 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x11E2 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x160 ADD DUP2 SWAP1 MSTORE POP PUSH2 0x1202 DUP9 DUP8 PUSH1 0x20 ADD MLOAD PUSH2 0x2EE2 JUMP JUMPDEST SWAP2 POP PUSH2 0x122E DUP10 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x1215 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP4 DUP10 DUP7 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x749 JUMPI INVALID JUMPDEST SWAP1 POP PUSH2 0x123A DUP7 DUP3 PUSH2 0x2532 JUMP JUMPDEST PUSH1 0x20 DUP7 ADD MLOAD DUP9 GT PUSH2 0x124A JUMPI PUSH2 0x1255 JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP3 ADD SWAP2 PUSH2 0x11CC JUMP JUMPDEST POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE POP SWAP2 SWAP6 SWAP5 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x60 PUSH1 0x0 PUSH1 0x60 PUSH1 0x0 DUP5 MLOAD SWAP3 POP DUP3 PUSH1 0x40 MLOAD SWAP1 DUP1 DUP3 MSTORE DUP1 PUSH1 0x20 MUL PUSH1 0x20 ADD DUP3 ADD PUSH1 0x40 MSTORE DUP1 ISZERO PUSH2 0x12D0 JUMPI DUP2 PUSH1 0x20 ADD JUMPDEST PUSH2 0x12BD PUSH2 0x42F0 JUMP JUMPDEST DUP2 MSTORE PUSH1 0x20 ADD SWAP1 PUSH1 0x1 SWAP1 SUB SWAP1 DUP2 PUSH2 0x12B5 JUMPI SWAP1 POP JUMPDEST POP SWAP2 POP PUSH1 0x0 SWAP1 POP JUMPDEST DUP1 DUP4 EQ PUSH2 0xA88 JUMPI PUSH2 0x12FF DUP6 DUP3 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x12F0 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x1F30 JUMP JUMPDEST DUP3 DUP3 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x130D JUMPI INVALID JUMPDEST PUSH1 0x20 SWAP1 DUP2 MUL SWAP1 SWAP2 ADD ADD MSTORE PUSH1 0x1 ADD PUSH2 0x12D8 JUMP JUMPDEST PUSH1 0x6 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x0 SWAP3 DUP4 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 SWAP1 SWAP2 MSTORE SWAP1 DUP3 MSTORE SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND DUP2 JUMP JUMPDEST PUSH1 0x2 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x0 DUP10 MLOAD GT ISZERO ISZERO PUSH2 0x13A4 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x562F JUMP JUMPDEST PUSH2 0x13AD DUP10 PUSH2 0x2F29 JUMP JUMPDEST PUSH32 0x100000000000000000000000000000000000000000000000000000000000000 SWAP1 DIV SWAP7 POP PUSH1 0x7 PUSH1 0xFF DUP9 AND LT PUSH2 0x140F JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x554F JUMP JUMPDEST DUP7 PUSH1 0xFF AND PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x141E JUMPI INVALID JUMPDEST SWAP6 POP PUSH1 0x0 DUP7 PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x142E JUMPI INVALID JUMPDEST EQ ISZERO PUSH2 0x1466 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x561F JUMP JUMPDEST PUSH1 0x1 DUP7 PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x1474 JUMPI INVALID JUMPDEST EQ ISZERO PUSH2 0x14BC JUMPI DUP9 MLOAD ISZERO PUSH2 0x14B3 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x56EF JUMP JUMPDEST PUSH1 0x0 SWAP8 POP PUSH2 0x18D0 JUMP JUMPDEST PUSH1 0x2 DUP7 PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x14CA JUMPI INVALID JUMPDEST EQ ISZERO PUSH2 0x1605 JUMPI DUP9 MLOAD PUSH1 0x41 EQ PUSH2 0x150B JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x54EF JUMP JUMPDEST DUP9 PUSH1 0x0 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x151A JUMPI INVALID JUMPDEST ADD PUSH1 0x20 ADD MLOAD PUSH32 0x100000000000000000000000000000000000000000000000000000000000000 SWAP1 DUP2 SWAP1 DIV DUP2 MUL DIV SWAP5 POP PUSH2 0x155A DUP10 PUSH1 0x1 PUSH4 0xFFFFFFFF PUSH2 0x2FED AND JUMP JUMPDEST SWAP4 POP PUSH2 0x156D DUP10 PUSH1 0x21 PUSH4 0xFFFFFFFF PUSH2 0x2FED AND JUMP JUMPDEST SWAP3 POP PUSH1 0x1 DUP12 DUP7 DUP7 DUP7 PUSH1 0x40 MLOAD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x40 MSTORE PUSH1 0x40 MLOAD PUSH2 0x1592 SWAP5 SWAP4 SWAP3 SWAP2 SWAP1 PUSH2 0x5480 JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD PUSH1 0x20 DUP2 SUB SWAP1 DUP1 DUP5 SUB SWAP1 PUSH1 0x0 DUP7 GAS CALL ISZERO DUP1 ISZERO PUSH2 0x15B6 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP PUSH1 0x40 MLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP13 DUP2 AND SWAP1 DUP3 AND EQ SWAP10 POP SWAP3 POP PUSH2 0x18D0 SWAP1 POP JUMP JUMPDEST PUSH1 0x3 DUP7 PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x1613 JUMPI INVALID JUMPDEST EQ ISZERO PUSH2 0x17B9 JUMPI DUP9 MLOAD PUSH1 0x41 EQ PUSH2 0x1654 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x54EF JUMP JUMPDEST DUP9 PUSH1 0x0 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x1663 JUMPI INVALID JUMPDEST ADD PUSH1 0x20 ADD MLOAD PUSH32 0x100000000000000000000000000000000000000000000000000000000000000 SWAP1 DUP2 SWAP1 DIV DUP2 MUL DIV SWAP5 POP PUSH2 0x16A3 DUP10 PUSH1 0x1 PUSH4 0xFFFFFFFF PUSH2 0x2FED AND JUMP JUMPDEST SWAP4 POP PUSH2 0x16B6 DUP10 PUSH1 0x21 PUSH4 0xFFFFFFFF PUSH2 0x2FED AND JUMP JUMPDEST SWAP3 POP PUSH1 0x1 DUP12 PUSH1 0x40 MLOAD PUSH1 0x20 ADD DUP1 DUP1 PUSH32 0x19457468657265756D205369676E6564204D6573736167653A0A333200000000 DUP2 MSTORE POP PUSH1 0x1C ADD DUP3 PUSH1 0x0 NOT AND PUSH1 0x0 NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP POP PUSH1 0x40 MLOAD PUSH1 0x20 DUP2 DUP4 SUB SUB DUP2 MSTORE SWAP1 PUSH1 0x40 MSTORE PUSH1 0x40 MLOAD DUP1 DUP3 DUP1 MLOAD SWAP1 PUSH1 0x20 ADD SWAP1 DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH2 0x1757 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0x171A JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 DUP5 SUB PUSH2 0x100 EXP PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ADD DUP1 NOT SWAP1 SWAP3 AND SWAP2 AND OR SWAP1 MSTORE PUSH1 0x40 DUP1 MLOAD SWAP3 SWAP1 SWAP5 ADD DUP3 SWAP1 SUB DUP3 KECCAK256 PUSH1 0x0 DUP4 MSTORE SWAP2 ADD SWAP3 DUP4 SWAP1 MSTORE PUSH2 0x1592 SWAP5 POP SWAP3 POP DUP10 SWAP2 DUP10 SWAP2 POP DUP9 SWAP1 PUSH2 0x5480 JUMP JUMPDEST PUSH1 0x4 DUP7 PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x17C7 JUMPI INVALID JUMPDEST EQ ISZERO PUSH2 0x17DF JUMPI PUSH2 0x17D8 DUP12 DUP12 DUP12 PUSH2 0x3038 JUMP JUMPDEST SWAP8 POP PUSH2 0x18D0 JUMP JUMPDEST PUSH1 0x5 DUP7 PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x17ED JUMPI INVALID JUMPDEST EQ ISZERO PUSH2 0x1850 JUMPI PUSH2 0x17FC DUP10 PUSH2 0x318D JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP13 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x7 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP4 DUP6 AND DUP4 MSTORE SWAP3 SWAP1 MSTORE KECCAK256 SLOAD SWAP1 SWAP2 POP PUSH1 0xFF AND ISZERO ISZERO PUSH2 0x1844 JUMPI PUSH1 0x0 SWAP8 POP PUSH2 0x18D0 JUMP JUMPDEST PUSH2 0x17D8 DUP2 DUP13 DUP13 DUP13 PUSH2 0x3206 JUMP JUMPDEST PUSH1 0x6 DUP7 PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x185E JUMPI INVALID JUMPDEST EQ ISZERO PUSH2 0x189E JUMPI PUSH1 0x0 DUP12 DUP2 MSTORE PUSH1 0x6 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP15 AND DUP5 MSTORE SWAP1 SWAP2 MSTORE SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND SWAP8 POP PUSH2 0x18D0 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x554F JUMP JUMPDEST POP POP POP POP POP POP POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH2 0x18E6 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x60 PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH2 0x18F6 PUSH2 0x4298 JUMP JUMPDEST DUP10 PUSH1 0x0 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x1905 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x140 ADD MLOAD SWAP6 POP DUP10 MLOAD SWAP5 POP PUSH1 0x0 SWAP4 POP JUMPDEST DUP4 DUP6 EQ PUSH2 0x19E4 JUMPI DUP6 DUP11 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x1935 JUMPI INVALID JUMPDEST PUSH1 0x20 SWAP1 DUP2 MUL SWAP1 SWAP2 ADD ADD MLOAD PUSH2 0x140 ADD MSTORE DUP7 MLOAD PUSH2 0x1951 SWAP1 DUP11 SWAP1 PUSH2 0x2EE2 JUMP JUMPDEST SWAP3 POP PUSH2 0x1994 DUP11 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x1964 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH1 0xA0 ADD MLOAD DUP12 DUP7 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x1980 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH1 0x80 ADD MLOAD DUP6 PUSH2 0x3362 JUMP JUMPDEST SWAP2 POP PUSH2 0x19C0 DUP11 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x19A7 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP4 DUP11 DUP8 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xE8E JUMPI INVALID JUMPDEST SWAP1 POP PUSH2 0x19CC DUP8 DUP3 PUSH2 0x2532 JUMP JUMPDEST DUP7 MLOAD DUP10 GT PUSH2 0x19D9 JUMPI PUSH2 0x19E4 JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP4 ADD SWAP3 PUSH2 0x191F JUMP JUMPDEST POP POP POP POP POP POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH2 0x19F9 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x1A36 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE PUSH2 0xF9C DUP5 DUP5 DUP5 PUSH2 0x2494 JUMP JUMPDEST PUSH1 0x9 SLOAD PUSH1 0x0 SWAP1 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND ISZERO PUSH2 0x1ABF JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH2 0x1B02 PUSH2 0x1AFD DUP9 DUP9 DUP9 DUP9 DUP1 DUP1 PUSH1 0x1F ADD PUSH1 0x20 DUP1 SWAP2 DIV MUL PUSH1 0x20 ADD PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 SWAP4 SWAP3 SWAP2 SWAP1 DUP2 DUP2 MSTORE PUSH1 0x20 ADD DUP4 DUP4 DUP1 DUP3 DUP5 CALLDATACOPY POP PUSH2 0x33B8 SWAP5 POP POP POP POP POP JUMP JUMPDEST PUSH2 0x35F9 JUMP JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x8 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD SWAP1 SWAP2 POP PUSH1 0xFF AND ISZERO PUSH2 0x1B4E JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x559F JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP7 AND CALLER EQ PUSH2 0x1C1F JUMPI PUSH2 0x1BA6 DUP2 DUP8 DUP6 DUP6 DUP1 DUP1 PUSH1 0x1F ADD PUSH1 0x20 DUP1 SWAP2 DIV MUL PUSH1 0x20 ADD PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 SWAP4 SWAP3 SWAP2 SWAP1 DUP2 DUP2 MSTORE PUSH1 0x20 ADD DUP4 DUP4 DUP1 DUP3 DUP5 CALLDATACOPY POP PUSH2 0x135B SWAP5 POP POP POP POP POP JUMP JUMPDEST ISZERO ISZERO PUSH2 0x1BDE JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x56DF JUMP JUMPDEST PUSH1 0x9 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP9 AND OR SWAP1 SSTORE JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x8 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 DUP2 SWAP1 KECCAK256 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE MLOAD ADDRESS SWAP1 DUP7 SWAP1 DUP7 SWAP1 DUP1 DUP4 DUP4 DUP1 DUP3 DUP5 CALLDATACOPY DUP3 ADD SWAP2 POP POP SWAP3 POP POP POP PUSH1 0x0 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 DUP6 GAS DELEGATECALL SWAP2 POP POP ISZERO ISZERO PUSH2 0x1CB6 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x55CF JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP7 AND CALLER EQ PUSH2 0x1CFC JUMPI PUSH1 0x9 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 AND SWAP1 SSTORE JUMPDEST POP POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x2 SLOAD PUSH1 0x0 SWAP1 DUP2 SWAP1 DUP2 SWAP1 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER EQ PUSH2 0x1D5D JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x568F JUMP JUMPDEST DUP4 SWAP3 POP DUP3 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH4 0xAE25532E PUSH1 0x40 MLOAD DUP2 PUSH4 0xFFFFFFFF AND PUSH29 0x100000000000000000000000000000000000000000000000000000000 MUL DUP2 MSTORE PUSH1 0x4 ADD PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x1DC4 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x1DD8 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x1DFC SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x491E JUMP JUMPDEST PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 DUP2 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0xA PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD SWAP1 SWAP3 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 POP DUP1 ISZERO PUSH2 0x1E81 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x552F JUMP JUMPDEST PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 DUP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0xA PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 DUP2 SWAP1 KECCAK256 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP7 AND OR SWAP1 SSTORE MLOAD PUSH32 0xD2C6B762299C609BDB96520B58A49BFB80186934D4F71A86A367571A15C03194 SWAP1 PUSH2 0x1F22 SWAP1 DUP5 SWAP1 DUP8 SWAP1 PUSH2 0x54B5 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG1 POP POP POP POP JUMP JUMPDEST PUSH2 0x1F38 PUSH2 0x42F0 JUMP JUMPDEST PUSH2 0x1F41 DUP3 PUSH2 0x3639 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP4 ADD DUP3 SWAP1 MSTORE PUSH1 0x0 SWAP2 DUP3 MSTORE PUSH1 0x3 SWAP1 MSTORE PUSH1 0x40 SWAP1 DUP2 SWAP1 KECCAK256 SLOAD SWAP1 DUP3 ADD MSTORE PUSH1 0x80 DUP3 ADD MLOAD ISZERO ISZERO PUSH2 0x1F75 JUMPI PUSH1 0x1 JUMPDEST PUSH1 0xFF AND DUP2 MSTORE PUSH2 0xF07 JUMP JUMPDEST PUSH1 0xA0 DUP3 ADD MLOAD ISZERO ISZERO PUSH2 0x1F87 JUMPI PUSH1 0x2 PUSH2 0x1F6B JUMP JUMPDEST PUSH1 0xA0 DUP3 ADD MLOAD PUSH1 0x40 DUP3 ADD MLOAD LT PUSH2 0x1F9D JUMPI PUSH1 0x5 PUSH2 0x1F6B JUMP JUMPDEST PUSH2 0x100 DUP3 ADD MLOAD TIMESTAMP LT PUSH2 0x1FB0 JUMPI PUSH1 0x4 PUSH2 0x1F6B JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 ADD MLOAD PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x4 SWAP1 SWAP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x1FD4 JUMPI PUSH1 0x6 PUSH2 0x1F6B JUMP JUMPDEST PUSH2 0x120 DUP3 ADD MLOAD DUP3 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 DUP2 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x5 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 PUSH1 0x60 DUP9 ADD MLOAD SWAP1 SWAP5 AND DUP4 MSTORE SWAP3 SWAP1 MSTORE KECCAK256 SLOAD GT ISZERO PUSH2 0x2021 JUMPI PUSH1 0x6 PUSH2 0x1F6B JUMP JUMPDEST PUSH1 0x3 DUP2 MSTORE SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x2067 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE PUSH2 0x209B DUP2 PUSH2 0x2E64 JUMP JUMPDEST POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE JUMP JUMPDEST PUSH1 0x5 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x0 SWAP3 DUP4 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 SWAP1 SWAP2 MSTORE SWAP1 DUP3 MSTORE SWAP1 KECCAK256 SLOAD DUP2 JUMP JUMPDEST PUSH1 0x60 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x24 DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0xF47261B0000000000000000000000000E41D2489571D322189246DAFA5EBDE1F DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0x4699F49800000000000000000000000000000000000000000000000000000000 DUP2 MSTORE POP DUP2 JUMP JUMPDEST PUSH2 0x214B PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x60 PUSH1 0x0 DUP1 PUSH1 0x0 PUSH2 0x215A PUSH2 0x4298 JUMP JUMPDEST DUP9 PUSH1 0x0 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x2169 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x160 ADD MLOAD SWAP5 POP DUP9 MLOAD SWAP4 POP PUSH1 0x0 SWAP3 POP JUMPDEST DUP3 DUP5 EQ PUSH2 0x220C JUMPI DUP5 DUP10 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x2199 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x160 ADD DUP2 SWAP1 MSTORE POP PUSH2 0x21B9 DUP9 DUP8 PUSH1 0x20 ADD MLOAD PUSH2 0x2EE2 JUMP JUMPDEST SWAP2 POP PUSH2 0x21E5 DUP10 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x21CC JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP4 DUP10 DUP7 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xE8E JUMPI INVALID JUMPDEST SWAP1 POP PUSH2 0x21F1 DUP7 DUP3 PUSH2 0x2532 JUMP JUMPDEST PUSH1 0x20 DUP7 ADD MLOAD DUP9 GT PUSH2 0x2201 JUMPI PUSH2 0x220C JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP3 ADD SWAP2 PUSH2 0x2183 JUMP JUMPDEST POP POP POP POP POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x1 SLOAD DUP2 JUMP JUMPDEST PUSH2 0x2226 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x60 PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH2 0x2236 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x2273 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR DUP2 SSTORE DUP11 MLOAD DUP12 SWAP2 SWAP1 DUP2 LT PUSH2 0x22AB JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x140 ADD MLOAD SWAP6 POP DUP10 MLOAD SWAP5 POP PUSH1 0x0 SWAP4 POP JUMPDEST DUP4 DUP6 EQ PUSH2 0x235A JUMPI DUP6 DUP11 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x22DB JUMPI INVALID JUMPDEST PUSH1 0x20 SWAP1 DUP2 MUL SWAP1 SWAP2 ADD ADD MLOAD PUSH2 0x140 ADD MSTORE DUP7 MLOAD PUSH2 0x22F7 SWAP1 DUP11 SWAP1 PUSH2 0x2EE2 JUMP JUMPDEST SWAP3 POP PUSH2 0x230A DUP11 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x1964 JUMPI INVALID JUMPDEST SWAP2 POP PUSH2 0x2336 DUP11 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x231D JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP4 DUP11 DUP8 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x749 JUMPI INVALID JUMPDEST SWAP1 POP PUSH2 0x2342 DUP8 DUP3 PUSH2 0x2532 JUMP JUMPDEST DUP7 MLOAD DUP10 GT PUSH2 0x234F JUMPI PUSH2 0x235A JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP4 ADD SWAP3 PUSH2 0x22C5 JUMP JUMPDEST POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE POP SWAP3 SWAP7 SWAP6 POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x9 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x2 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER EQ PUSH2 0x23FD JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x568F JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 AND ISZERO PUSH2 0x245A JUMPI PUSH1 0x2 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND OR SWAP1 SSTORE JUMPDEST POP JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP1 DUP3 ADD SWAP1 SWAP2 MSTORE PUSH1 0x5 DUP2 MSTORE PUSH32 0x322E302E30000000000000000000000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE DUP2 JUMP JUMPDEST PUSH2 0x249C PUSH2 0x4298 JUMP JUMPDEST PUSH2 0x24A4 PUSH2 0x42F0 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 PUSH2 0x24B2 DUP9 PUSH2 0x1F30 JUMP JUMPDEST SWAP4 POP PUSH2 0x24BC PUSH2 0x2594 JUMP JUMPDEST SWAP3 POP PUSH2 0x24CA DUP9 DUP6 DUP6 DUP10 PUSH2 0x25C6 JUMP JUMPDEST PUSH2 0x24DC DUP9 PUSH1 0xA0 ADD MLOAD DUP6 PUSH1 0x40 ADD MLOAD PUSH2 0x2EE2 JUMP JUMPDEST SWAP2 POP PUSH2 0x24E8 DUP8 DUP4 PUSH2 0x3647 JUMP JUMPDEST SWAP1 POP PUSH2 0x24FB DUP9 DUP6 DUP10 DUP5 DUP10 PUSH1 0x0 ADD MLOAD PUSH2 0x2945 JUMP JUMPDEST PUSH2 0x2505 DUP9 DUP3 PUSH2 0x365D JUMP JUMPDEST SWAP5 POP PUSH2 0x251C DUP9 DUP5 DUP7 PUSH1 0x20 ADD MLOAD DUP8 PUSH1 0x40 ADD MLOAD DUP10 PUSH2 0x2A5E JUMP JUMPDEST PUSH2 0x2527 DUP9 DUP5 DUP8 PUSH2 0x36BE JUMP JUMPDEST POP POP POP POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST DUP2 MLOAD DUP2 MLOAD PUSH2 0x2540 SWAP2 SWAP1 PUSH2 0x3776 JUMP JUMPDEST DUP3 MSTORE PUSH1 0x20 DUP1 DUP4 ADD MLOAD SWAP1 DUP3 ADD MLOAD PUSH2 0x2556 SWAP2 SWAP1 PUSH2 0x3776 JUMP JUMPDEST PUSH1 0x20 DUP4 ADD MSTORE PUSH1 0x40 DUP1 DUP4 ADD MLOAD SWAP1 DUP3 ADD MLOAD PUSH2 0x256F SWAP2 SWAP1 PUSH2 0x3776 JUMP JUMPDEST PUSH1 0x40 DUP4 ADD MSTORE PUSH1 0x60 DUP1 DUP4 ADD MLOAD SWAP1 DUP3 ADD MLOAD PUSH2 0x2588 SWAP2 SWAP1 PUSH2 0x3776 JUMP JUMPDEST PUSH1 0x60 SWAP1 SWAP3 ADD SWAP2 SWAP1 SWAP2 MSTORE POP JUMP JUMPDEST PUSH1 0x9 SLOAD PUSH1 0x0 SWAP1 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 DUP2 ISZERO PUSH2 0x25BD JUMPI DUP2 PUSH2 0x25BF JUMP JUMPDEST CALLER JUMPDEST SWAP4 SWAP3 POP POP POP JUMP JUMPDEST DUP3 MLOAD PUSH1 0xFF AND PUSH1 0x3 EQ PUSH2 0x2604 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x56AF JUMP JUMPDEST PUSH1 0x60 DUP5 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND ISZERO PUSH2 0x2677 JUMPI PUSH1 0x60 DUP5 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER EQ PUSH2 0x2677 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x56BF JUMP JUMPDEST PUSH1 0x20 DUP5 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND ISZERO PUSH2 0x2702 JUMPI DUP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP5 PUSH1 0x20 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ ISZERO ISZERO PUSH2 0x2702 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x54FF JUMP JUMPDEST PUSH1 0x40 DUP4 ADD MLOAD ISZERO ISZERO PUSH2 0x2758 JUMPI PUSH2 0x2720 DUP4 PUSH1 0x20 ADD MLOAD DUP6 PUSH1 0x0 ADD MLOAD DUP4 PUSH2 0x135B JUMP JUMPDEST ISZERO ISZERO PUSH2 0x2758 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x556F JUMP JUMPDEST POP POP POP POP JUMP JUMPDEST PUSH2 0x2770 DUP3 PUSH1 0xA0 ADD MLOAD DUP3 PUSH1 0xA0 ADD MLOAD PUSH2 0x37C0 JUMP JUMPDEST PUSH2 0x2782 DUP4 PUSH1 0x80 ADD MLOAD DUP4 PUSH1 0x80 ADD MLOAD PUSH2 0x37C0 JUMP JUMPDEST LT ISZERO PUSH2 0x27BA JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x56CF JUMP JUMPDEST POP POP JUMP JUMPDEST PUSH2 0x27C6 PUSH2 0x42C1 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH2 0x27DA DUP10 PUSH1 0xA0 ADD MLOAD DUP9 PUSH2 0x2EE2 JUMP JUMPDEST SWAP4 POP PUSH2 0x27EF DUP10 PUSH1 0x80 ADD MLOAD DUP11 PUSH1 0xA0 ADD MLOAD DUP7 PUSH2 0x381B JUMP JUMPDEST SWAP3 POP PUSH2 0x27FF DUP9 PUSH1 0xA0 ADD MLOAD DUP8 PUSH2 0x2EE2 JUMP JUMPDEST SWAP2 POP PUSH2 0x2814 DUP9 PUSH1 0x80 ADD MLOAD DUP10 PUSH1 0xA0 ADD MLOAD DUP5 PUSH2 0x381B JUMP JUMPDEST SWAP1 POP DUP1 DUP5 LT PUSH2 0x2857 JUMPI PUSH1 0x20 DUP1 DUP7 ADD DUP1 MLOAD DUP4 SWAP1 MSTORE DUP1 MLOAD DUP3 ADD DUP5 SWAP1 MSTORE MLOAD MLOAD DUP7 MLOAD DUP3 ADD MSTORE PUSH1 0x80 DUP11 ADD MLOAD PUSH1 0xA0 DUP12 ADD MLOAD DUP8 MLOAD SWAP1 SWAP3 ADD MLOAD PUSH2 0x284F SWAP3 SWAP1 PUSH2 0x381B JUMP JUMPDEST DUP6 MLOAD MSTORE PUSH2 0x2894 JUMP JUMPDEST DUP5 MLOAD DUP4 SWAP1 MSTORE DUP5 MLOAD PUSH1 0x20 SWAP1 DUP2 ADD DUP6 SWAP1 MSTORE DUP6 MLOAD DUP2 ADD MLOAD SWAP1 DUP7 ADD DUP1 MLOAD SWAP2 SWAP1 SWAP2 MSTORE PUSH1 0xA0 DUP10 ADD MLOAD PUSH1 0x80 DUP11 ADD MLOAD SWAP2 MLOAD MLOAD PUSH2 0x288B SWAP3 SWAP1 PUSH2 0x3898 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP8 ADD MLOAD ADD MSTORE JUMPDEST DUP5 MLOAD MLOAD PUSH1 0x20 DUP1 DUP8 ADD MLOAD ADD MLOAD PUSH2 0x28A9 SWAP2 SWAP1 PUSH2 0x2EE2 JUMP JUMPDEST PUSH1 0x40 DUP7 ADD MSTORE DUP5 MLOAD MLOAD PUSH1 0x80 DUP11 ADD MLOAD PUSH1 0xC0 DUP12 ADD MLOAD PUSH2 0x28C6 SWAP3 SWAP2 SWAP1 PUSH2 0x381B JUMP JUMPDEST DUP6 MLOAD PUSH1 0x40 ADD MSTORE DUP5 MLOAD PUSH1 0x20 ADD MLOAD PUSH1 0xA0 DUP11 ADD MLOAD PUSH1 0xE0 DUP12 ADD MLOAD PUSH2 0x28E7 SWAP3 SWAP2 SWAP1 PUSH2 0x381B JUMP JUMPDEST DUP6 MLOAD PUSH1 0x60 ADD MSTORE PUSH1 0x20 DUP6 ADD MLOAD MLOAD PUSH1 0x80 DUP10 ADD MLOAD PUSH1 0xC0 DUP11 ADD MLOAD PUSH2 0x2908 SWAP3 SWAP2 SWAP1 PUSH2 0x381B JUMP JUMPDEST DUP6 PUSH1 0x20 ADD MLOAD PUSH1 0x40 ADD DUP2 DUP2 MSTORE POP POP PUSH2 0x2930 DUP6 PUSH1 0x20 ADD MLOAD PUSH1 0x20 ADD MLOAD DUP10 PUSH1 0xA0 ADD MLOAD DUP11 PUSH1 0xE0 ADD MLOAD PUSH2 0x381B JUMP JUMPDEST PUSH1 0x20 DUP7 ADD MLOAD PUSH1 0x60 ADD MSTORE POP POP POP POP SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST DUP3 ISZERO ISZERO PUSH2 0x297E JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x55EF JUMP JUMPDEST DUP3 DUP3 GT ISZERO PUSH2 0x29B8 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x55DF JUMP JUMPDEST DUP5 PUSH1 0xA0 ADD MLOAD PUSH2 0x29CB DUP6 PUSH1 0x40 ADD MLOAD DUP5 PUSH2 0x3776 JUMP JUMPDEST GT ISZERO PUSH2 0x2A03 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x550F JUMP JUMPDEST PUSH2 0x2A11 DUP6 PUSH1 0x80 ADD MLOAD DUP4 PUSH2 0x37C0 JUMP JUMPDEST PUSH2 0x2A1F DUP3 DUP8 PUSH1 0xA0 ADD MLOAD PUSH2 0x37C0 JUMP JUMPDEST GT ISZERO PUSH2 0x2A57 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x566F JUMP JUMPDEST POP POP POP POP POP JUMP JUMPDEST PUSH2 0x2A6C DUP3 DUP3 PUSH1 0x20 ADD MLOAD PUSH2 0x3776 JUMP JUMPDEST PUSH1 0x0 DUP5 DUP2 MSTORE PUSH1 0x3 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP2 DUP3 SWAP1 KECCAK256 SWAP3 SWAP1 SWAP3 SSTORE DUP7 DUP2 ADD MLOAD DUP8 MLOAD DUP5 MLOAD SWAP4 DUP6 ADD MLOAD DUP6 DUP5 ADD MLOAD PUSH1 0x60 DUP8 ADD MLOAD PUSH2 0x140 DUP13 ADD MLOAD PUSH2 0x160 DUP14 ADD MLOAD SWAP7 MLOAD DUP12 SWAP9 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP8 DUP9 AND SWAP9 SWAP8 SWAP1 SWAP7 AND SWAP7 PUSH32 0xBCC4C97732E47D9946F229EDB95F5B6323F601300E4690DE719993F3C371129 SWAP7 PUSH2 0x2AFB SWAP7 DUP16 SWAP7 CALLER SWAP7 SWAP3 SWAP6 SWAP2 SWAP5 SWAP1 SWAP4 SWAP1 PUSH2 0x5345 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG4 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x60 DUP1 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x24 DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0xF47261B0000000000000000000000000E41D2489571D322189246DAFA5EBDE1F DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0x4699F49800000000000000000000000000000000000000000000000000000000 DUP2 MSTORE POP SWAP1 POP PUSH2 0x2B8B DUP6 PUSH2 0x140 ADD MLOAD DUP7 PUSH1 0x0 ADD MLOAD DUP7 PUSH1 0x0 ADD MLOAD DUP6 PUSH1 0x20 ADD MLOAD PUSH1 0x20 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x140 DUP5 ADD MLOAD DUP5 MLOAD DUP7 MLOAD DUP5 MLOAD PUSH1 0x20 ADD MLOAD PUSH2 0x2BA7 SWAP4 SWAP3 SWAP2 SWAP1 PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x2BC0 DUP6 PUSH2 0x140 ADD MLOAD DUP7 PUSH1 0x0 ADD MLOAD DUP6 DUP6 PUSH1 0x40 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x2BDC DUP2 DUP7 PUSH1 0x0 ADD MLOAD DUP8 PUSH1 0x40 ADD MLOAD DUP6 PUSH1 0x0 ADD MLOAD PUSH1 0x40 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x2BF8 DUP2 DUP6 PUSH1 0x0 ADD MLOAD DUP7 PUSH1 0x40 ADD MLOAD DUP6 PUSH1 0x20 ADD MLOAD PUSH1 0x40 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST DUP4 PUSH1 0x40 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP6 PUSH1 0x40 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ ISZERO PUSH2 0x2C62 JUMPI PUSH2 0x2C5D DUP2 DUP5 DUP8 PUSH1 0x40 ADD MLOAD PUSH2 0x2C58 DUP7 PUSH1 0x0 ADD MLOAD PUSH1 0x60 ADD MLOAD DUP8 PUSH1 0x20 ADD MLOAD PUSH1 0x60 ADD MLOAD PUSH2 0x3776 JUMP JUMPDEST PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x2A57 JUMP JUMPDEST PUSH2 0x2C7A DUP2 DUP5 DUP8 PUSH1 0x40 ADD MLOAD DUP6 PUSH1 0x0 ADD MLOAD PUSH1 0x60 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x2A57 DUP2 DUP5 DUP7 PUSH1 0x40 ADD MLOAD DUP6 PUSH1 0x20 ADD MLOAD PUSH1 0x60 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0xB4BE83D500000000000000000000000000000000000000000000000000000000 PUSH1 0x20 DUP1 DUP4 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH1 0x60 PUSH1 0x24 DUP4 ADD DUP2 DUP2 MSTORE DUP8 MLOAD PUSH1 0x84 DUP6 ADD SWAP1 DUP2 MSTORE DUP9 DUP5 ADD MLOAD PUSH1 0xA4 DUP7 ADD MSTORE SWAP5 DUP9 ADD MLOAD PUSH1 0xC4 DUP6 ADD MSTORE SWAP1 DUP8 ADD MLOAD PUSH1 0xE4 DUP5 ADD MSTORE PUSH1 0x80 DUP8 ADD MLOAD PUSH2 0x104 DUP5 ADD MSTORE PUSH1 0xA0 DUP8 ADD MLOAD PUSH2 0x124 DUP5 ADD MSTORE PUSH1 0xC0 DUP8 ADD MLOAD PUSH2 0x144 DUP5 ADD MSTORE PUSH1 0xE0 DUP8 ADD MLOAD PUSH2 0x164 DUP5 ADD MSTORE PUSH2 0x100 DUP8 ADD MLOAD PUSH2 0x184 DUP5 ADD MSTORE PUSH2 0x120 DUP8 ADD MLOAD PUSH2 0x1A4 DUP5 ADD MSTORE PUSH2 0x140 DUP8 ADD DUP1 MLOAD PUSH2 0x1C4 DUP6 ADD SWAP1 DUP2 MSTORE PUSH2 0x160 DUP10 ADD MLOAD PUSH2 0x1E4 DUP7 ADD MSTORE PUSH2 0x180 SWAP1 MSTORE MLOAD DUP1 MLOAD PUSH2 0x204 DUP6 ADD DUP2 SWAP1 MSTORE SWAP4 SWAP5 SWAP2 SWAP4 DUP5 SWAP4 PUSH1 0x44 DUP8 ADD SWAP3 DUP5 SWAP3 PUSH2 0x224 DUP10 ADD SWAP3 SWAP2 DUP3 ADD SWAP2 PUSH1 0x1F DUP3 ADD DIV PUSH1 0x0 JUMPDEST DUP2 DUP2 LT ISZERO PUSH2 0x2D99 JUMPI DUP4 MLOAD DUP6 MSTORE PUSH1 0x20 SWAP5 DUP6 ADD SWAP5 SWAP1 SWAP4 ADD SWAP3 PUSH1 0x1 ADD PUSH2 0x2D7B JUMP JUMPDEST POP POP POP POP DUP2 DUP2 SUB PUSH2 0x160 DUP1 DUP5 ADD SWAP2 SWAP1 SWAP2 MSTORE DUP11 ADD MLOAD DUP1 MLOAD DUP1 DUP4 MSTORE PUSH1 0x20 SWAP3 DUP4 ADD SWAP3 SWAP2 DUP3 ADD SWAP2 PUSH1 0x1F DUP3 ADD DIV PUSH1 0x0 JUMPDEST DUP2 DUP2 LT ISZERO PUSH2 0x2DE2 JUMPI DUP4 MLOAD DUP6 MSTORE PUSH1 0x20 SWAP5 DUP6 ADD SWAP5 SWAP1 SWAP4 ADD SWAP3 PUSH1 0x1 ADD PUSH2 0x2DC4 JUMP JUMPDEST POP POP POP DUP10 DUP5 MSTORE POP DUP5 DUP2 SUB PUSH1 0x20 SWAP4 DUP5 ADD SWAP1 DUP2 MSTORE DUP9 MLOAD DUP1 DUP4 MSTORE SWAP1 SWAP4 SWAP2 DUP3 ADD SWAP2 DUP10 DUP2 ADD SWAP2 SWAP1 PUSH1 0x1F DUP3 ADD DIV PUSH1 0x0 JUMPDEST DUP2 DUP2 LT ISZERO PUSH2 0x2E2A JUMPI DUP4 MLOAD DUP6 MSTORE PUSH1 0x20 SWAP5 DUP6 ADD SWAP5 SWAP1 SWAP4 ADD SWAP3 PUSH1 0x1 ADD PUSH2 0x2E0C JUMP JUMPDEST POP POP POP PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 DUP9 DUP4 SUB ADD DUP9 MSTORE POP PUSH1 0x40 MSTORE POP POP POP POP POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH2 0x2E6C PUSH2 0x42F0 JUMP JUMPDEST PUSH2 0x2E75 DUP3 PUSH2 0x1F30 JUMP JUMPDEST SWAP1 POP PUSH2 0x2E81 DUP3 DUP3 PUSH2 0x3AFF JUMP JUMPDEST PUSH2 0x27BA DUP3 DUP3 PUSH1 0x20 ADD MLOAD PUSH2 0x3C16 JUMP JUMPDEST PUSH2 0x2E97 PUSH2 0x4298 JUMP JUMPDEST PUSH2 0x2EA2 DUP5 DUP5 DUP5 PUSH2 0x2494 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD MLOAD SWAP1 SWAP2 POP DUP4 EQ PUSH2 0x25BF JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x565F JUMP JUMPDEST PUSH1 0x0 DUP3 DUP3 GT ISZERO PUSH2 0x2F1E JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x551F JUMP JUMPDEST POP DUP1 DUP3 SUB JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP3 MLOAD GT ISZERO ISZERO PUSH2 0x2F67 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x560F JUMP JUMPDEST DUP2 MLOAD DUP3 SWAP1 PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 ADD SWAP1 DUP2 LT PUSH2 0x2F97 JUMPI INVALID JUMPDEST ADD PUSH1 0x20 ADD MLOAD DUP3 MLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ADD SWAP1 SWAP3 MSTORE POP PUSH32 0x100000000000000000000000000000000000000000000000000000000000000 SWAP1 DUP2 SWAP1 DIV MUL SWAP1 JUMP JUMPDEST PUSH1 0x0 DUP2 PUSH1 0x20 ADD DUP4 MLOAD LT ISZERO ISZERO ISZERO PUSH2 0x302F JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x553F JUMP JUMPDEST POP ADD PUSH1 0x20 ADD MLOAD SWAP1 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH1 0x0 SWAP1 PUSH1 0x60 SWAP1 PUSH32 0x1626BA7E00000000000000000000000000000000000000000000000000000000 SWAP1 PUSH2 0x3073 SWAP1 DUP8 SWAP1 DUP7 SWAP1 PUSH1 0x24 ADD PUSH2 0x5460 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 DUP2 DUP5 SUB ADD DUP2 MSTORE SWAP2 SWAP1 MSTORE PUSH1 0x20 DUP1 DUP3 ADD DUP1 MLOAD PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 SWAP1 SWAP5 AND SWAP4 SWAP1 SWAP4 OR DUP4 MSTORE DUP2 MLOAD SWAP2 SWAP4 POP SWAP1 DUP3 SWAP1 DUP2 DUP9 GAS STATICCALL DUP1 DUP1 ISZERO PUSH2 0x3110 JUMPI PUSH1 0x1 DUP2 EQ PUSH2 0x3181 JUMPI PUSH2 0x2527 JUMP JUMPDEST PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 PUSH1 0x0 MSTORE PUSH29 0x2000000000000000000000000000000000000000000000000000000000 PUSH1 0x20 MSTORE PUSH29 0xC57414C4C45545F4552524F5200000000000000000000000000000000 PUSH1 0x40 MSTORE PUSH1 0x0 PUSH1 0x60 MSTORE PUSH1 0x64 PUSH1 0x0 REVERT JUMPDEST POP POP MLOAD SWAP6 SWAP5 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x14 DUP3 MLOAD LT ISZERO ISZERO ISZERO PUSH2 0x31CD JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x569F JUMP JUMPDEST PUSH2 0x31DB DUP3 PUSH1 0x14 DUP5 MLOAD SUB PUSH2 0x3CBD JUMP JUMPDEST DUP3 MLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC ADD SWAP1 SWAP3 MSTORE POP SWAP1 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH1 0x0 SWAP1 PUSH1 0x60 SWAP1 PUSH32 0x9363470200000000000000000000000000000000000000000000000000000000 SWAP1 PUSH2 0x3243 SWAP1 DUP8 SWAP1 DUP8 SWAP1 DUP8 SWAP1 PUSH1 0x24 ADD PUSH2 0x5433 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 DUP2 DUP5 SUB ADD DUP2 MSTORE SWAP2 SWAP1 MSTORE PUSH1 0x20 DUP1 DUP3 ADD DUP1 MLOAD PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 SWAP1 SWAP5 AND SWAP4 SWAP1 SWAP4 OR DUP4 MSTORE DUP2 MLOAD SWAP2 SWAP4 POP SWAP1 DUP3 SWAP1 DUP2 DUP11 GAS STATICCALL DUP1 DUP1 ISZERO PUSH2 0x32E0 JUMPI PUSH1 0x1 DUP2 EQ PUSH2 0x3351 JUMPI PUSH2 0x3356 JUMP JUMPDEST PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 PUSH1 0x0 MSTORE PUSH29 0x2000000000000000000000000000000000000000000000000000000000 PUSH1 0x20 MSTORE PUSH29 0xF56414C494441544F525F4552524F5200000000000000000000000000 PUSH1 0x40 MSTORE PUSH1 0x0 PUSH1 0x60 MSTORE PUSH1 0x64 PUSH1 0x0 REVERT JUMPDEST DUP3 MLOAD SWAP5 POP JUMPDEST POP POP POP POP SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP4 GT PUSH2 0x339D JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x555F JUMP JUMPDEST PUSH2 0x33B0 PUSH2 0x33AA DUP6 DUP5 PUSH2 0x37C0 JUMP JUMPDEST DUP5 PUSH2 0x3D1E JUMP JUMPDEST SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0x5A65726F45785472616E73616374696F6E280000000000000000000000000000 PUSH1 0x20 DUP1 DUP4 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH32 0x75696E743235362073616C742C00000000000000000000000000000000000000 PUSH1 0x32 DUP4 ADD MSTORE PUSH32 0x61646472657373207369676E6572416464726573732C00000000000000000000 PUSH1 0x3F DUP4 ADD MSTORE PUSH32 0x6279746573206461746100000000000000000000000000000000000000000000 PUSH1 0x55 DUP4 ADD MSTORE PUSH32 0x2900000000000000000000000000000000000000000000000000000000000000 PUSH1 0x5F DUP4 ADD MSTORE DUP3 MLOAD DUP1 DUP4 SUB DUP5 ADD DUP2 MSTORE PUSH1 0x60 SWAP1 SWAP3 ADD SWAP3 DUP4 SWAP1 MSTORE DUP2 MLOAD PUSH1 0x0 SWAP4 DUP5 SWAP4 DUP5 SWAP4 SWAP1 SWAP3 DUP3 SWAP2 DUP5 ADD SWAP1 DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH2 0x34E1 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0x34A4 JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 DUP5 SUB PUSH2 0x100 EXP PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ADD DUP1 NOT SWAP1 SWAP3 AND SWAP2 AND OR SWAP1 MSTORE PUSH1 0x40 MLOAD SWAP2 SWAP1 SWAP4 ADD DUP2 SWAP1 SUB DUP2 KECCAK256 DUP10 MLOAD SWAP1 SWAP8 POP DUP10 SWAP6 POP SWAP1 SWAP4 POP DUP4 SWAP3 DUP6 ADD SWAP2 POP DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH2 0x3577 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0x353A JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 DUP5 SUB PUSH2 0x100 EXP PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ADD DUP1 NOT SWAP1 SWAP3 AND SWAP2 AND OR SWAP1 MSTORE PUSH1 0x40 DUP1 MLOAD SWAP3 SWAP1 SWAP5 ADD DUP3 SWAP1 SUB DUP3 KECCAK256 SWAP8 DUP3 MSTORE DUP2 ADD SWAP11 SWAP1 SWAP11 MSTORE POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP8 SWAP1 SWAP8 AND SWAP7 DUP9 ADD SWAP7 SWAP1 SWAP7 MSTORE POP POP PUSH1 0x60 DUP6 ADD MSTORE POP POP PUSH1 0x80 SWAP1 SWAP2 KECCAK256 SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x1 SLOAD PUSH1 0x40 MLOAD PUSH32 0x1901000000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x2 DUP2 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH1 0x22 DUP2 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH1 0x42 SWAP1 KECCAK256 SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x2F23 PUSH2 0x1AFD DUP4 PUSH2 0x3D35 JUMP JUMPDEST PUSH1 0x0 DUP2 DUP4 LT PUSH2 0x3656 JUMPI DUP2 PUSH2 0x25BF JUMP JUMPDEST POP SWAP1 SWAP2 SWAP1 POP JUMP JUMPDEST PUSH2 0x3665 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD DUP3 SWAP1 MSTORE PUSH1 0xA0 DUP4 ADD MLOAD PUSH1 0x80 DUP5 ADD MLOAD PUSH2 0x3681 SWAP2 DUP5 SWAP2 PUSH2 0x381B JUMP JUMPDEST DUP1 DUP3 MSTORE PUSH1 0x80 DUP5 ADD MLOAD PUSH1 0xC0 DUP6 ADD MLOAD PUSH2 0x3699 SWAP3 SWAP2 SWAP1 PUSH2 0x381B JUMP JUMPDEST PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0xA0 DUP4 ADD MLOAD PUSH1 0xE0 DUP5 ADD MLOAD PUSH2 0x36B3 SWAP2 DUP5 SWAP2 PUSH2 0x381B JUMP JUMPDEST PUSH1 0x60 DUP3 ADD MSTORE SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x60 DUP2 ADD DUP3 MSTORE PUSH1 0x24 DUP2 MSTORE PUSH32 0xF47261B0000000000000000000000000E41D2489571D322189246DAFA5EBDE1F PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x4699F49800000000000000000000000000000000000000000000000000000000 SWAP2 DUP2 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH2 0x140 DUP5 ADD MLOAD DUP5 MLOAD DUP4 MLOAD PUSH2 0x3731 SWAP3 SWAP2 SWAP1 DUP7 SWAP1 PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x374A DUP5 PUSH2 0x160 ADD MLOAD DUP5 DUP7 PUSH1 0x0 ADD MLOAD DUP6 PUSH1 0x20 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x3762 DUP2 DUP6 PUSH1 0x0 ADD MLOAD DUP7 PUSH1 0x40 ADD MLOAD DUP6 PUSH1 0x40 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x2758 DUP2 DUP5 DUP7 PUSH1 0x40 ADD MLOAD DUP6 PUSH1 0x60 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH1 0x0 DUP3 DUP3 ADD DUP4 DUP2 LT ISZERO PUSH2 0x37B5 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x558F JUMP JUMPDEST DUP1 SWAP2 POP JUMPDEST POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP4 ISZERO ISZERO PUSH2 0x37D3 JUMPI PUSH1 0x0 SWAP2 POP PUSH2 0x37B9 JUMP JUMPDEST POP DUP3 DUP3 MUL DUP3 DUP5 DUP3 DUP2 ISZERO ISZERO PUSH2 0x37E3 JUMPI INVALID JUMPDEST DIV EQ PUSH2 0x37B5 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x558F JUMP JUMPDEST PUSH1 0x0 DUP1 DUP4 GT PUSH2 0x3856 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x555F JUMP JUMPDEST PUSH2 0x3861 DUP5 DUP5 DUP5 PUSH2 0x418E JUMP JUMPDEST ISZERO PUSH2 0x339D JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x55BF JUMP JUMPDEST PUSH1 0x0 DUP1 DUP4 GT PUSH2 0x38D3 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x555F JUMP JUMPDEST PUSH2 0x38DE DUP5 DUP5 DUP5 PUSH2 0x4213 JUMP JUMPDEST ISZERO PUSH2 0x3915 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x55BF JUMP JUMPDEST PUSH2 0x33B0 PUSH2 0x33AA PUSH2 0x3925 DUP7 DUP6 PUSH2 0x37C0 JUMP JUMPDEST PUSH2 0x3930 DUP7 PUSH1 0x1 PUSH2 0x2EE2 JUMP JUMPDEST PUSH2 0x3776 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP4 GT DUP1 ISZERO PUSH2 0x3974 JUMPI POP DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP6 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ ISZERO JUMPDEST ISZERO PUSH2 0x3AF7 JUMPI DUP6 MLOAD PUSH1 0x3 LT PUSH2 0x39B4 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x564F JUMP JUMPDEST POP POP PUSH1 0x20 DUP5 DUP2 ADD MLOAD PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0xA SWAP1 SWAP3 MSTORE PUSH1 0x40 SWAP1 SWAP2 KECCAK256 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP1 ISZERO ISZERO PUSH2 0x3A3D JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x55FF JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH7 0xFFFFFFFFFFFE0 PUSH1 0x3F DUP9 MLOAD ADD AND DUP1 PUSH1 0x84 ADD DUP3 ADD PUSH32 0xA85E59E400000000000000000000000000000000000000000000000000000000 DUP4 MSTORE PUSH1 0x80 PUSH1 0x4 DUP5 ADD MSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP9 AND PUSH1 0x24 DUP5 ADD MSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP8 AND PUSH1 0x44 DUP5 ADD MSTORE DUP6 PUSH1 0x64 DUP5 ADD MSTORE PUSH1 0x84 DUP4 ADD JUMPDEST DUP2 DUP2 LT ISZERO PUSH2 0x3AD9 JUMPI DUP10 MLOAD DUP2 MSTORE PUSH1 0x20 SWAP10 DUP11 ADD SWAP10 ADD PUSH2 0x3AC1 JUMP JUMPDEST PUSH2 0x200 DUP5 DUP6 DUP5 SUB DUP7 PUSH1 0x0 DUP10 GAS CALL DUP1 ISZERO ISZERO PUSH2 0x3AF1 JUMPI RETURNDATASIZE DUP6 REVERT JUMPDEST POP POP POP POP POP JUMPDEST POP POP POP POP POP POP JUMP JUMPDEST DUP1 MLOAD PUSH1 0x0 SWAP1 PUSH1 0xFF AND PUSH1 0x3 EQ PUSH2 0x3B40 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x56AF JUMP JUMPDEST PUSH1 0x60 DUP4 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND ISZERO PUSH2 0x3BB3 JUMPI PUSH1 0x60 DUP4 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER EQ PUSH2 0x3BB3 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x56BF JUMP JUMPDEST PUSH2 0x3BBB PUSH2 0x2594 JUMP JUMPDEST DUP4 MLOAD SWAP1 SWAP2 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP4 AND SWAP2 AND EQ PUSH2 0x3C11 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x557F JUMP JUMPDEST POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x4 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 DUP2 SWAP1 KECCAK256 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE DUP3 DUP2 ADD MLOAD DUP4 MLOAD PUSH2 0x140 DUP6 ADD MLOAD PUSH2 0x160 DUP7 ADD MLOAD SWAP4 MLOAD DUP6 SWAP5 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP5 DUP6 AND SWAP5 SWAP4 SWAP1 SWAP4 AND SWAP3 PUSH32 0xDC47B3613D9FE400085F6DBDC99453462279057E6207385042827ED6B1A62CF7 SWAP3 PUSH2 0x3CB1 SWAP3 CALLER SWAP3 SWAP1 PUSH2 0x53C9 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG4 POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 PUSH1 0x14 ADD DUP4 MLOAD LT ISZERO ISZERO ISZERO PUSH2 0x3CFF JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x569F JUMP JUMPDEST POP ADD PUSH1 0x14 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 JUMP JUMPDEST PUSH1 0x0 DUP1 DUP3 DUP5 DUP2 ISZERO ISZERO PUSH2 0x3D2C JUMPI INVALID JUMPDEST DIV SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0x4F72646572280000000000000000000000000000000000000000000000000000 PUSH1 0x20 DUP1 DUP4 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH32 0x61646472657373206D616B6572416464726573732C0000000000000000000000 PUSH1 0x26 DUP4 ADD MSTORE PUSH32 0x616464726573732074616B6572416464726573732C0000000000000000000000 PUSH1 0x3B DUP4 ADD MSTORE PUSH32 0x6164647265737320666565526563697069656E74416464726573732C00000000 PUSH1 0x50 DUP4 ADD MSTORE PUSH32 0x616464726573732073656E646572416464726573732C00000000000000000000 PUSH1 0x6C DUP4 ADD MSTORE PUSH32 0x75696E74323536206D616B65724173736574416D6F756E742C00000000000000 PUSH1 0x82 DUP4 ADD MSTORE PUSH32 0x75696E743235362074616B65724173736574416D6F756E742C00000000000000 PUSH1 0x9B DUP4 ADD MSTORE PUSH32 0x75696E74323536206D616B65724665652C000000000000000000000000000000 PUSH1 0xB4 DUP4 ADD MSTORE PUSH32 0x75696E743235362074616B65724665652C000000000000000000000000000000 PUSH1 0xC5 DUP4 ADD MSTORE PUSH32 0x75696E743235362065787069726174696F6E54696D655365636F6E64732C0000 PUSH1 0xD6 DUP4 ADD MSTORE PUSH32 0x75696E743235362073616C742C00000000000000000000000000000000000000 PUSH1 0xF4 DUP4 ADD MSTORE PUSH32 0x6279746573206D616B65724173736574446174612C0000000000000000000000 PUSH2 0x101 DUP4 ADD MSTORE PUSH32 0x62797465732074616B6572417373657444617461000000000000000000000000 PUSH2 0x116 DUP4 ADD MSTORE PUSH32 0x2900000000000000000000000000000000000000000000000000000000000000 PUSH2 0x12A DUP4 ADD MSTORE DUP3 MLOAD PUSH2 0x10B DUP2 DUP5 SUB ADD DUP2 MSTORE PUSH2 0x12B SWAP1 SWAP3 ADD SWAP3 DUP4 SWAP1 MSTORE DUP2 MLOAD PUSH1 0x0 SWAP4 DUP5 SWAP4 DUP5 SWAP4 DUP5 SWAP4 SWAP2 SWAP3 SWAP2 DUP3 SWAP2 DUP5 ADD SWAP1 DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH2 0x3FBD JUMPI DUP1 MLOAD DUP3 MSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0x3F80 JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 DUP5 SUB PUSH2 0x100 EXP PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ADD DUP1 NOT SWAP1 SWAP3 AND SWAP2 AND OR SWAP1 MSTORE PUSH1 0x40 MLOAD SWAP2 SWAP1 SWAP4 ADD DUP2 SWAP1 SUB DUP2 KECCAK256 PUSH2 0x140 DUP12 ADD MLOAD DUP1 MLOAD SWAP2 SWAP10 POP SWAP6 POP SWAP1 SWAP4 POP DUP4 SWAP3 DUP6 ADD SWAP2 POP DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH2 0x4058 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0x401B JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 DUP5 SUB PUSH2 0x100 EXP PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ADD DUP1 NOT SWAP1 SWAP3 AND SWAP2 AND OR SWAP1 MSTORE PUSH1 0x40 MLOAD SWAP2 SWAP1 SWAP4 ADD DUP2 SWAP1 SUB DUP2 KECCAK256 PUSH2 0x160 DUP12 ADD MLOAD DUP1 MLOAD SWAP2 SWAP9 POP SWAP6 POP SWAP1 SWAP4 POP DUP4 SWAP3 DUP6 ADD SWAP2 POP DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH2 0x40F3 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0x40B6 JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 SWAP1 SWAP4 SUB PUSH2 0x100 EXP PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ADD DUP1 NOT SWAP1 SWAP2 AND SWAP3 AND SWAP2 SWAP1 SWAP2 OR SWAP1 MSTORE PUSH1 0x40 MLOAD SWAP3 ADD DUP3 SWAP1 SUB SWAP1 SWAP2 KECCAK256 PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 DUP10 ADD DUP1 MLOAD PUSH2 0x140 DUP12 ADD DUP1 MLOAD PUSH2 0x160 SWAP1 SWAP13 ADD DUP1 MLOAD SWAP11 DUP5 MSTORE SWAP9 DUP2 MSTORE SWAP3 DUP9 MSTORE PUSH2 0x1A0 DUP3 KECCAK256 SWAP2 MSTORE SWAP9 SWAP1 MSTORE POP POP POP SWAP2 SWAP1 MSTORE POP SWAP1 SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP1 DUP5 GT PUSH2 0x41CA JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x555F JUMP JUMPDEST DUP3 ISZERO DUP1 PUSH2 0x41D5 JUMPI POP DUP5 ISZERO JUMPDEST ISZERO PUSH2 0x41E3 JUMPI PUSH1 0x0 SWAP2 POP PUSH2 0xA88 JUMP JUMPDEST DUP4 DUP1 ISZERO ISZERO PUSH2 0x41ED JUMPI INVALID JUMPDEST DUP6 DUP5 MULMOD SWAP1 POP PUSH2 0x41FC DUP6 DUP5 PUSH2 0x37C0 JUMP JUMPDEST PUSH2 0x4208 PUSH2 0x3E8 DUP4 PUSH2 0x37C0 JUMP JUMPDEST LT ISZERO SWAP6 SWAP5 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP1 DUP5 GT PUSH2 0x424F JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x555F JUMP JUMPDEST DUP3 ISZERO DUP1 PUSH2 0x425A JUMPI POP DUP5 ISZERO JUMPDEST ISZERO PUSH2 0x4268 JUMPI PUSH1 0x0 SWAP2 POP PUSH2 0xA88 JUMP JUMPDEST DUP4 DUP1 ISZERO ISZERO PUSH2 0x4272 JUMPI INVALID JUMPDEST DUP6 DUP5 MULMOD SWAP1 POP DUP4 PUSH2 0x4282 DUP6 DUP4 PUSH2 0x2EE2 JUMP JUMPDEST DUP2 ISZERO ISZERO PUSH2 0x428B JUMPI INVALID JUMPDEST MOD SWAP1 POP PUSH2 0x41FC DUP6 DUP5 PUSH2 0x37C0 JUMP JUMPDEST PUSH1 0x80 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE POP SWAP1 JUMP JUMPDEST PUSH2 0x120 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH2 0x42D6 PUSH2 0x4298 JUMP JUMPDEST DUP2 MSTORE PUSH1 0x20 ADD PUSH2 0x42E3 PUSH2 0x4298 JUMP JUMPDEST DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE POP SWAP1 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x60 DUP2 ADD DUP3 MSTORE PUSH1 0x0 DUP1 DUP3 MSTORE PUSH1 0x20 DUP3 ADD DUP2 SWAP1 MSTORE SWAP2 DUP2 ADD SWAP2 SWAP1 SWAP2 MSTORE SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x25BF DUP3 CALLDATALOAD PUSH2 0x57C2 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0x432D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0x4340 PUSH2 0x433B DUP3 PUSH2 0x5751 JUMP JUMPDEST PUSH2 0x572A JUMP JUMPDEST DUP2 DUP2 MSTORE PUSH1 0x20 SWAP4 DUP5 ADD SWAP4 SWAP1 SWAP3 POP DUP3 ADD DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x437E JUMPI DUP2 CALLDATALOAD DUP7 ADD PUSH2 0x4368 DUP9 DUP3 PUSH2 0x44CE JUMP JUMPDEST DUP5 MSTORE POP PUSH1 0x20 SWAP3 DUP4 ADD SWAP3 SWAP2 SWAP1 SWAP2 ADD SWAP1 PUSH1 0x1 ADD PUSH2 0x4352 JUMP JUMPDEST POP POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0x4399 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0x43A7 PUSH2 0x433B DUP3 PUSH2 0x5751 JUMP JUMPDEST DUP2 DUP2 MSTORE PUSH1 0x20 SWAP4 DUP5 ADD SWAP4 SWAP1 SWAP3 POP DUP3 ADD DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x437E JUMPI DUP2 CALLDATALOAD DUP7 ADD PUSH2 0x43CF DUP9 DUP3 PUSH2 0x451D JUMP JUMPDEST DUP5 MSTORE POP PUSH1 0x20 SWAP3 DUP4 ADD SWAP3 SWAP2 SWAP1 SWAP2 ADD SWAP1 PUSH1 0x1 ADD PUSH2 0x43B9 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0x43F6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0x4404 PUSH2 0x433B DUP3 PUSH2 0x5751 JUMP JUMPDEST SWAP2 POP DUP2 DUP2 DUP4 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP PUSH1 0x20 DUP2 ADD SWAP1 POP DUP4 DUP6 PUSH1 0x20 DUP5 MUL DUP3 ADD GT ISZERO PUSH2 0x4429 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x437E JUMPI DUP2 PUSH2 0x443F DUP9 DUP3 PUSH2 0x4461 JUMP JUMPDEST DUP5 MSTORE POP PUSH1 0x20 SWAP3 DUP4 ADD SWAP3 SWAP2 SWAP1 SWAP2 ADD SWAP1 PUSH1 0x1 ADD PUSH2 0x442C JUMP JUMPDEST PUSH1 0x0 PUSH2 0x25BF DUP3 CALLDATALOAD PUSH2 0x57DB JUMP JUMPDEST PUSH1 0x0 PUSH2 0x25BF DUP3 CALLDATALOAD PUSH2 0x57E0 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x25BF DUP3 CALLDATALOAD PUSH2 0x57E3 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x25BF DUP3 MLOAD PUSH2 0x57E3 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x1F DUP4 ADD DUP5 SGT PUSH2 0x4497 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP DUP2 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x44AF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x20 DUP4 ADD SWAP2 POP DUP4 PUSH1 0x1 DUP3 MUL DUP4 ADD GT ISZERO PUSH2 0x44C7 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST SWAP3 POP SWAP3 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0x44DF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0x44ED PUSH2 0x433B DUP3 PUSH2 0x5772 JUMP JUMPDEST SWAP2 POP DUP1 DUP3 MSTORE PUSH1 0x20 DUP4 ADD PUSH1 0x20 DUP4 ADD DUP6 DUP4 DUP4 ADD GT ISZERO PUSH2 0x4509 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4514 DUP4 DUP3 DUP5 PUSH2 0x5819 JUMP JUMPDEST POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x180 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x4530 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x453B PUSH2 0x180 PUSH2 0x572A JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0x4549 DUP5 DUP5 PUSH2 0x4310 JUMP JUMPDEST DUP3 MSTORE POP PUSH1 0x20 PUSH2 0x455A DUP5 DUP5 DUP4 ADD PUSH2 0x4310 JUMP JUMPDEST PUSH1 0x20 DUP4 ADD MSTORE POP PUSH1 0x40 PUSH2 0x456E DUP5 DUP3 DUP6 ADD PUSH2 0x4310 JUMP JUMPDEST PUSH1 0x40 DUP4 ADD MSTORE POP PUSH1 0x60 PUSH2 0x4582 DUP5 DUP3 DUP6 ADD PUSH2 0x4310 JUMP JUMPDEST PUSH1 0x60 DUP4 ADD MSTORE POP PUSH1 0x80 PUSH2 0x4596 DUP5 DUP3 DUP6 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH1 0x80 DUP4 ADD MSTORE POP PUSH1 0xA0 PUSH2 0x45AA DUP5 DUP3 DUP6 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH1 0xA0 DUP4 ADD MSTORE POP PUSH1 0xC0 PUSH2 0x45BE DUP5 DUP3 DUP6 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH1 0xC0 DUP4 ADD MSTORE POP PUSH1 0xE0 PUSH2 0x45D2 DUP5 DUP3 DUP6 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH1 0xE0 DUP4 ADD MSTORE POP PUSH2 0x100 PUSH2 0x45E7 DUP5 DUP3 DUP6 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH2 0x100 DUP4 ADD MSTORE POP PUSH2 0x120 PUSH2 0x45FD DUP5 DUP3 DUP6 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH2 0x120 DUP4 ADD MSTORE POP PUSH2 0x140 DUP3 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x461F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x462B DUP5 DUP3 DUP6 ADD PUSH2 0x44CE JUMP JUMPDEST PUSH2 0x140 DUP4 ADD MSTORE POP PUSH2 0x160 DUP3 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x464D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4659 DUP5 DUP3 DUP6 ADD PUSH2 0x44CE JUMP JUMPDEST PUSH2 0x160 DUP4 ADD MSTORE POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x4678 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x33B0 DUP5 DUP5 PUSH2 0x4310 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x4697 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x46A3 DUP6 DUP6 PUSH2 0x4310 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 PUSH2 0x46B4 DUP6 DUP3 DUP7 ADD PUSH2 0x4310 JUMP JUMPDEST SWAP2 POP POP SWAP3 POP SWAP3 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x46D1 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x46DD DUP6 DUP6 PUSH2 0x4310 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 PUSH2 0x46B4 DUP6 DUP3 DUP7 ADD PUSH2 0x4455 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x4700 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x4717 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x33B0 DUP5 DUP3 DUP6 ADD PUSH2 0x4388 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 PUSH1 0x60 DUP5 DUP7 SUB SLT ISZERO PUSH2 0x4738 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP4 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x474F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x475B DUP7 DUP3 DUP8 ADD PUSH2 0x4388 JUMP JUMPDEST SWAP4 POP POP PUSH1 0x20 DUP5 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x4778 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4784 DUP7 DUP3 DUP8 ADD PUSH2 0x43E5 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x40 DUP5 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x47A1 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x47AD DUP7 DUP3 DUP8 ADD PUSH2 0x431C JUMP JUMPDEST SWAP2 POP POP SWAP3 POP SWAP3 POP SWAP3 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 PUSH1 0x60 DUP5 DUP7 SUB SLT ISZERO PUSH2 0x47CC JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP4 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x47E3 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x47EF DUP7 DUP3 DUP8 ADD PUSH2 0x4388 JUMP JUMPDEST SWAP4 POP POP PUSH1 0x20 PUSH2 0x4784 DUP7 DUP3 DUP8 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x4812 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x33B0 DUP5 DUP5 PUSH2 0x4461 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x4831 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x46A3 DUP6 DUP6 PUSH2 0x4461 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x60 DUP6 DUP8 SUB SLT ISZERO PUSH2 0x4853 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x485F DUP8 DUP8 PUSH2 0x4461 JUMP JUMPDEST SWAP5 POP POP PUSH1 0x20 PUSH2 0x4870 DUP8 DUP3 DUP9 ADD PUSH2 0x4310 JUMP JUMPDEST SWAP4 POP POP PUSH1 0x40 DUP6 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x488D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4899 DUP8 DUP3 DUP9 ADD PUSH2 0x4485 JUMP JUMPDEST SWAP6 SWAP9 SWAP5 SWAP8 POP SWAP6 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 PUSH1 0x60 DUP5 DUP7 SUB SLT ISZERO PUSH2 0x48BA JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x48C6 DUP7 DUP7 PUSH2 0x4461 JUMP JUMPDEST SWAP4 POP POP PUSH1 0x20 PUSH2 0x48D7 DUP7 DUP3 DUP8 ADD PUSH2 0x4310 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x40 DUP5 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x48F4 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x47AD DUP7 DUP3 DUP8 ADD PUSH2 0x44CE JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x4912 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x33B0 DUP5 DUP5 PUSH2 0x446D JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x4930 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x33B0 DUP5 DUP5 PUSH2 0x4479 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x494E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x4965 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x33B0 DUP5 DUP3 DUP6 ADD PUSH2 0x451D JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x80 DUP6 DUP8 SUB SLT ISZERO PUSH2 0x4987 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP5 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x499E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x49AA DUP8 DUP3 DUP9 ADD PUSH2 0x451D JUMP JUMPDEST SWAP5 POP POP PUSH1 0x20 DUP6 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x49C7 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x49D3 DUP8 DUP3 DUP9 ADD PUSH2 0x451D JUMP JUMPDEST SWAP4 POP POP PUSH1 0x40 DUP6 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x49F0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x49FC DUP8 DUP3 DUP9 ADD PUSH2 0x44CE JUMP JUMPDEST SWAP3 POP POP PUSH1 0x60 DUP6 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x4A19 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4A25 DUP8 DUP3 DUP9 ADD PUSH2 0x44CE JUMP JUMPDEST SWAP2 POP POP SWAP3 SWAP6 SWAP2 SWAP5 POP SWAP3 POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 PUSH1 0x60 DUP5 DUP7 SUB SLT ISZERO PUSH2 0x4A46 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP4 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x4A5D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4A69 DUP7 DUP3 DUP8 ADD PUSH2 0x451D JUMP JUMPDEST SWAP4 POP POP PUSH1 0x20 PUSH2 0x48D7 DUP7 DUP3 DUP8 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x80 DUP8 DUP10 SUB SLT ISZERO PUSH2 0x4A93 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x4A9F DUP10 DUP10 PUSH2 0x4461 JUMP JUMPDEST SWAP7 POP POP PUSH1 0x20 PUSH2 0x4AB0 DUP10 DUP3 DUP11 ADD PUSH2 0x4310 JUMP JUMPDEST SWAP6 POP POP PUSH1 0x40 DUP8 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x4ACD JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4AD9 DUP10 DUP3 DUP11 ADD PUSH2 0x4485 JUMP JUMPDEST SWAP5 POP SWAP5 POP POP PUSH1 0x60 DUP8 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x4AF8 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4B04 DUP10 DUP3 DUP11 ADD PUSH2 0x4485 JUMP JUMPDEST SWAP3 POP SWAP3 POP POP SWAP3 SWAP6 POP SWAP3 SWAP6 POP SWAP3 SWAP6 JUMP JUMPDEST PUSH2 0x4B1C DUP2 PUSH2 0x57C2 JUMP JUMPDEST DUP3 MSTORE POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x4B2D DUP3 PUSH2 0x57BE JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP PUSH2 0x4B3F DUP4 PUSH2 0x57B8 JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP3 DUP2 LT ISZERO PUSH2 0x4B6F JUMPI PUSH2 0x4B55 DUP7 DUP4 MLOAD PUSH2 0x52F7 JUMP JUMPDEST PUSH2 0x4B5E DUP3 PUSH2 0x57B8 JUMP JUMPDEST PUSH1 0x60 SWAP7 SWAP1 SWAP7 ADD SWAP6 SWAP2 POP PUSH1 0x1 ADD PUSH2 0x4B42 JUMP JUMPDEST POP SWAP4 SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH2 0x4B1C DUP2 PUSH2 0x57DB JUMP JUMPDEST PUSH2 0x4B1C DUP2 PUSH2 0x57E0 JUMP JUMPDEST PUSH2 0x4B1C DUP2 PUSH2 0x57E3 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x4B9F DUP3 PUSH2 0x57BE JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH2 0x4BB3 DUP2 PUSH1 0x20 DUP7 ADD PUSH1 0x20 DUP7 ADD PUSH2 0x5825 JUMP JUMPDEST PUSH2 0x4BBC DUP2 PUSH2 0x5851 JUMP JUMPDEST SWAP1 SWAP4 ADD PUSH1 0x20 ADD SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH2 0x4B1C DUP2 PUSH2 0x580E JUMP JUMPDEST PUSH1 0x12 DUP2 MSTORE PUSH32 0x4C454E4754485F36355F52455155495245440000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xD DUP2 MSTORE PUSH32 0x494E56414C49445F54414B455200000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xE DUP2 MSTORE PUSH32 0x4F524445525F4F56455246494C4C000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x11 DUP2 MSTORE PUSH32 0x55494E543235365F554E444552464C4F57000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x1A DUP2 MSTORE PUSH32 0x41535345545F50524F58595F414C52454144595F455849535453000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x26 DUP2 MSTORE PUSH32 0x475245415445525F4F525F455155414C5F544F5F33325F4C454E4754485F5245 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x5155495245440000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x15 DUP2 MSTORE PUSH32 0x5349474E41545552455F554E535550504F525445440000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x10 DUP2 MSTORE PUSH32 0x4449564953494F4E5F42595F5A45524F00000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x17 DUP2 MSTORE PUSH32 0x494E56414C49445F4F524445525F5349474E4154555245000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xD DUP2 MSTORE PUSH32 0x494E56414C49445F4D414B455200000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x10 DUP2 MSTORE PUSH32 0x55494E543235365F4F564552464C4F5700000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xF DUP2 MSTORE PUSH32 0x494E56414C49445F54585F484153480000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x11 DUP2 MSTORE PUSH32 0x494E56414C49445F5349474E4154555245000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xE DUP2 MSTORE PUSH32 0x524F554E44494E475F4552524F52000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x10 DUP2 MSTORE PUSH32 0x4641494C45445F455845435554494F4E00000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xD DUP2 MSTORE PUSH32 0x54414B45525F4F56455250415900000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x14 DUP2 MSTORE PUSH32 0x494E56414C49445F54414B45525F414D4F554E54000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x1A DUP2 MSTORE PUSH32 0x41535345545F50524F58595F444F45535F4E4F545F4558495354000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x21 DUP2 MSTORE PUSH32 0x475245415445525F5448414E5F5A45524F5F4C454E4754485F52455155495245 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x4400000000000000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x11 DUP2 MSTORE PUSH32 0x5349474E41545552455F494C4C4547414C000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x1E DUP2 MSTORE PUSH32 0x4C454E4754485F475245415445525F5448414E5F305F52455155495245440000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x17 DUP2 MSTORE PUSH32 0x494E56414C49445F4E45575F4F524445525F45504F4348000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x1E DUP2 MSTORE PUSH32 0x4C454E4754485F475245415445525F5448414E5F335F52455155495245440000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x14 DUP2 MSTORE PUSH32 0x434F4D504C4554455F46494C4C5F4641494C4544000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x12 DUP2 MSTORE PUSH32 0x494E56414C49445F46494C4C5F50524943450000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x12 DUP2 MSTORE PUSH32 0x5245454E5452414E43595F494C4C4547414C0000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x13 DUP2 MSTORE PUSH32 0x4F4E4C595F434F4E54524143545F4F574E455200000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x26 DUP2 MSTORE PUSH32 0x475245415445525F4F525F455155414C5F544F5F32305F4C454E4754485F5245 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x5155495245440000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x10 DUP2 MSTORE PUSH32 0x4F524445525F554E46494C4C41424C4500000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xE DUP2 MSTORE PUSH32 0x494E56414C49445F53454E444552000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x18 DUP2 MSTORE PUSH32 0x4E454741544956455F5350524541445F52455155495245440000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x14 DUP2 MSTORE PUSH32 0x494E56414C49445F54585F5349474E4154555245000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x11 DUP2 MSTORE PUSH32 0x4C454E4754485F305F5245515549524544000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST DUP1 MLOAD PUSH1 0x80 DUP4 ADD SWAP1 PUSH2 0x5285 DUP5 DUP3 PUSH2 0x4B82 JUMP JUMPDEST POP PUSH1 0x20 DUP3 ADD MLOAD PUSH2 0x5298 PUSH1 0x20 DUP6 ADD DUP3 PUSH2 0x4B82 JUMP JUMPDEST POP PUSH1 0x40 DUP3 ADD MLOAD PUSH2 0x52AB PUSH1 0x40 DUP6 ADD DUP3 PUSH2 0x4B82 JUMP JUMPDEST POP PUSH1 0x60 DUP3 ADD MLOAD PUSH2 0x2758 PUSH1 0x60 DUP6 ADD DUP3 PUSH2 0x4B82 JUMP JUMPDEST DUP1 MLOAD PUSH2 0x120 DUP4 ADD SWAP1 PUSH2 0x52D0 DUP5 DUP3 PUSH2 0x5274 JUMP JUMPDEST POP PUSH1 0x20 DUP3 ADD MLOAD PUSH2 0x52E3 PUSH1 0x80 DUP6 ADD DUP3 PUSH2 0x5274 JUMP JUMPDEST POP PUSH1 0x40 DUP3 ADD MLOAD PUSH2 0x2758 PUSH2 0x100 DUP6 ADD DUP3 PUSH2 0x4B82 JUMP JUMPDEST DUP1 MLOAD PUSH1 0x60 DUP4 ADD SWAP1 PUSH2 0x5308 DUP5 DUP3 PUSH2 0x532E JUMP JUMPDEST POP PUSH1 0x20 DUP3 ADD MLOAD PUSH2 0x531B PUSH1 0x20 DUP6 ADD DUP3 PUSH2 0x4B82 JUMP JUMPDEST POP PUSH1 0x40 DUP3 ADD MLOAD PUSH2 0x2758 PUSH1 0x40 DUP6 ADD DUP3 PUSH2 0x4B82 JUMP JUMPDEST PUSH2 0x4B1C DUP2 PUSH2 0x5808 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x2F23 DUP3 DUP5 PUSH2 0x4B13 JUMP JUMPDEST PUSH2 0x100 DUP2 ADD PUSH2 0x5354 DUP3 DUP12 PUSH2 0x4B13 JUMP JUMPDEST PUSH2 0x5361 PUSH1 0x20 DUP4 ADD DUP11 PUSH2 0x4B13 JUMP JUMPDEST PUSH2 0x536E PUSH1 0x40 DUP4 ADD DUP10 PUSH2 0x4B82 JUMP JUMPDEST PUSH2 0x537B PUSH1 0x60 DUP4 ADD DUP9 PUSH2 0x4B82 JUMP JUMPDEST PUSH2 0x5388 PUSH1 0x80 DUP4 ADD DUP8 PUSH2 0x4B82 JUMP JUMPDEST PUSH2 0x5395 PUSH1 0xA0 DUP4 ADD DUP7 PUSH2 0x4B82 JUMP JUMPDEST DUP2 DUP2 SUB PUSH1 0xC0 DUP4 ADD MSTORE PUSH2 0x53A7 DUP2 DUP6 PUSH2 0x4B94 JUMP JUMPDEST SWAP1 POP DUP2 DUP2 SUB PUSH1 0xE0 DUP4 ADD MSTORE PUSH2 0x53BB DUP2 DUP5 PUSH2 0x4B94 JUMP JUMPDEST SWAP11 SWAP10 POP POP POP POP POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x60 DUP2 ADD PUSH2 0x53D7 DUP3 DUP7 PUSH2 0x4B13 JUMP JUMPDEST DUP2 DUP2 SUB PUSH1 0x20 DUP4 ADD MSTORE PUSH2 0x53E9 DUP2 DUP6 PUSH2 0x4B94 JUMP JUMPDEST SWAP1 POP DUP2 DUP2 SUB PUSH1 0x40 DUP4 ADD MSTORE PUSH2 0x53FD DUP2 DUP5 PUSH2 0x4B94 JUMP JUMPDEST SWAP6 SWAP5 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x25BF DUP2 DUP5 PUSH2 0x4B22 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x2F23 DUP3 DUP5 PUSH2 0x4B79 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x2F23 DUP3 DUP5 PUSH2 0x4B82 JUMP JUMPDEST PUSH1 0x60 DUP2 ADD PUSH2 0x5441 DUP3 DUP7 PUSH2 0x4B82 JUMP JUMPDEST PUSH2 0x544E PUSH1 0x20 DUP4 ADD DUP6 PUSH2 0x4B13 JUMP JUMPDEST DUP2 DUP2 SUB PUSH1 0x40 DUP4 ADD MSTORE PUSH2 0x53FD DUP2 DUP5 PUSH2 0x4B94 JUMP JUMPDEST PUSH1 0x40 DUP2 ADD PUSH2 0x546E DUP3 DUP6 PUSH2 0x4B82 JUMP JUMPDEST DUP2 DUP2 SUB PUSH1 0x20 DUP4 ADD MSTORE PUSH2 0x33B0 DUP2 DUP5 PUSH2 0x4B94 JUMP JUMPDEST PUSH1 0x80 DUP2 ADD PUSH2 0x548E DUP3 DUP8 PUSH2 0x4B82 JUMP JUMPDEST PUSH2 0x549B PUSH1 0x20 DUP4 ADD DUP7 PUSH2 0x532E JUMP JUMPDEST PUSH2 0x54A8 PUSH1 0x40 DUP4 ADD DUP6 PUSH2 0x4B82 JUMP JUMPDEST PUSH2 0x53FD PUSH1 0x60 DUP4 ADD DUP5 PUSH2 0x4B82 JUMP JUMPDEST PUSH1 0x40 DUP2 ADD PUSH2 0x54C3 DUP3 DUP6 PUSH2 0x4B8B JUMP JUMPDEST PUSH2 0x25BF PUSH1 0x20 DUP4 ADD DUP5 PUSH2 0x4B13 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x25BF DUP2 DUP5 PUSH2 0x4B94 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x2F23 DUP3 DUP5 PUSH2 0x4BC9 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4BD2 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4C02 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4C32 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4C62 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4C92 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4CC2 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4D18 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4D48 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4D78 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4DA8 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4DD8 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4E08 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4E38 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4E68 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4E98 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4EC8 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4EF8 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4F28 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4F58 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4FAE JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4FDE JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x500E JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x503E JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x506E JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x509E JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x50CE JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x50FE JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x512E JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x5184 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x51B4 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x51E4 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x5214 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x5244 JUMP JUMPDEST PUSH1 0x80 DUP2 ADD PUSH2 0x2F23 DUP3 DUP5 PUSH2 0x5274 JUMP JUMPDEST PUSH2 0x120 DUP2 ADD PUSH2 0x2F23 DUP3 DUP5 PUSH2 0x52BE JUMP JUMPDEST PUSH1 0x60 DUP2 ADD PUSH2 0x2F23 DUP3 DUP5 PUSH2 0x52F7 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP2 DUP2 ADD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT DUP3 DUP3 LT OR ISZERO PUSH2 0x5749 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x40 MSTORE SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 PUSH8 0xFFFFFFFFFFFFFFFF DUP3 GT ISZERO PUSH2 0x5768 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x20 SWAP1 DUP2 MUL ADD SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH8 0xFFFFFFFFFFFFFFFF DUP3 GT ISZERO PUSH2 0x5789 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x20 PUSH1 0x1F SWAP2 SWAP1 SWAP2 ADD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 AND ADD SWAP1 JUMP JUMPDEST PUSH1 0x20 ADD SWAP1 JUMP JUMPDEST MLOAD SWAP1 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 JUMP JUMPDEST ISZERO ISZERO SWAP1 JUMP JUMPDEST SWAP1 JUMP JUMPDEST PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 AND SWAP1 JUMP JUMPDEST PUSH1 0xFF AND SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x2F23 DUP3 PUSH2 0x57C2 JUMP JUMPDEST DUP3 DUP2 DUP4 CALLDATACOPY POP PUSH1 0x0 SWAP2 ADD MSTORE JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x5840 JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0x5828 JUMP JUMPDEST DUP4 DUP2 GT ISZERO PUSH2 0x2758 JUMPI POP POP PUSH1 0x0 SWAP2 ADD MSTORE JUMP JUMPDEST PUSH1 0x1F ADD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 AND SWAP1 JUMP STOP LOG2 PUSH6 0x627A7A723058 KECCAK256 0xb1 SWAP13 SWAP3 KECCAK256 LT ORIGIN PUSH1 0x33 SWAP9 0xc0 SMOD 0xdc 0xcf DUP1 SWAP4 0xc PUSH3 0x6EF89F 0xa6 0xc8 0x48 0xc8 0xb7 0xef MSTORE PUSH8 0xD07CD4ED6C657870 PUSH6 0x72696D656E74 PUSH2 0x6CF5 STOP CALLDATACOPY ", - "sourceMap": "897:514:21:-;;;689:5:56;667:27;;-1:-1:-1;;667:27:56;;;1188:221:21;5:2:-1;;;;30:1;27;20:12;5:2;-1:-1;1045:148:39;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;22:32:-1;26:21;;;22:32;6:49;;1045:148:39;;;;;;;;1035:159;;1045:148;;;;;1035:159;;;;1045:148;1035:159;36:153:-1;66:2;58:11;;36:153;;176:10;;164:23;;-1:-1;;139:12;;;;98:2;89:12;;;;114;36:153;;;299:10;344;;263:2;259:12;;;254:3;250:22;-1:-1;;246:30;311:9;;295:26;;;340:21;;377:20;365:33;;1035:159:39;;;;;;;;;;;;1511:18;;;;;;;;;;;;;;;;1495:36;;1035:159;;-1:-1:-1;1035:159:39;;-1:-1:-1;1495:36:39;;;;-1:-1:-1;1511:18:39;1495:36;1511:18;36:153:-1;66:2;58:11;;36:153;;176:10;;164:23;;-1:-1;;139:12;;;;98:2;89:12;;;;114;36:153;;;299:10;344;;263:2;259:12;;;254:3;250:22;-1:-1;;246:30;311:9;;295:26;;;340:21;;377:20;365:33;;1495:36:39;;;;;;;;;;;;1561:21;;;;;274:1:-1;1561:21:39;;;;;;;;;;1545:39;;1495:36;;-1:-1:-1;1495:36:39;;-1:-1:-1;1545:39:39;;;;-1:-1:-1;274:1;1545:39:39;1561:21;36:153:-1;66:2;58:11;;36:153;;176:10;;164:23;;-1:-1;;139:12;;;;98:2;89:12;;;;114;36:153;;;299:10;344;;263:2;259:12;;;254:3;250:22;-1:-1;;246:30;311:9;;295:26;;;340:21;;377:20;365:33;;1545:39:39;;;;;;;;;;;;1416:214;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1614:4:39;1416:214;;;;;;;;;;26:21:-1;;;22:32;;;6:49;;1416:214:39;;;;;;;;1406:225;;1416:214;;;;-1:-1:-1;1406:225:39;;;-1:-1:-1;1406:225:39;1416:214;1406:225;36:153:-1;66:2;58:11;;36:153;;176:10;;164:23;;-1:-1;;139:12;;;;98:2;89:12;;;;114;36:153;;;299:10;344;;-1:-1;;263:2;259:12;;;;254:3;250:22;246:30;;;;340:21;;;311:9;;295:26;377:20;;;;365:33;;1406:225:39;;;;;;;;;;274:1:-1;1385:246:39;-1:-1:-1;;162:5:55;:18;;-1:-1:-1;;;;;;162:18:55;170:10;162:18;;;-1:-1:-1;897:514:21;;;;;;" - }, - "deployedBytecode": { - "linkReferences": {}, - "object": "0x6080604052600436106101b65763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663288cdc9181146101bb578063297bb70b146101f15780632ac126221461021e5780633683ef8e1461024b5780633c28d8611461026d5780633e228bae1461029a5780633fd3c997146102ba5780634ac14782146102e75780634d0ae546146103075780634f9559b11461032757806350dde190146103475780636070410814610367578063642f2eaf1461039457806364a3bc15146103b457806377fcce68146103d45780637b8e3514146103f45780637e1d9808146104145780637e9d74dc1461043457806382c174d0146104615780638da5cb5b146104815780639363470214610496578063a3e20380146104b6578063b4be83d5146104d6578063bfc8bfce146104f6578063c585bb9314610516578063c75e0a8114610536578063d46b02c314610563578063d9bfa73e14610583578063db123b1a146105a3578063dd1c7d18146105c5578063e306f779146105e5578063e5fa431b146105fa578063eea086ba1461061a578063f2fde38b1461062f578063ffa1ad741461064f575b600080fd5b3480156101c757600080fd5b506101db6101d6366004614800565b610664565b6040516101e89190615425565b60405180910390f35b3480156101fd57600080fd5b5061021161020c366004614723565b610676565b6040516101e891906156ff565b34801561022a57600080fd5b5061023e610239366004614800565b6107a1565b6040516101e89190615417565b34801561025757600080fd5b5061026b61026636600461483d565b6107b6565b005b34801561027957600080fd5b5061028d610288366004614971565b6108a3565b6040516101e8919061570d565b3480156102a657600080fd5b506102116102b5366004614a31565b610a3a565b3480156102c657600080fd5b506102da6102d5366004614900565b610a90565b6040516101e891906154e1565b3480156102f357600080fd5b5061026b6103023660046146ee565b610ab8565b34801561031357600080fd5b50610211610322366004614723565b610b85565b34801561033357600080fd5b5061026b610342366004614800565b610c75565b34801561035357600080fd5b50610211610362366004614723565b610e2a565b34801561037357600080fd5b50610387610382366004614900565b610ebe565b6040516101e89190615337565b3480156103a057600080fd5b5061023e6103af366004614800565b610f0c565b3480156103c057600080fd5b506102116103cf366004614a31565b610f21565b3480156103e057600080fd5b5061026b6103ef3660046146be565b610fcc565b34801561040057600080fd5b5061023e61040f366004614684565b611106565b34801561042057600080fd5b5061021161042f3660046147b7565b611126565b34801561044057600080fd5b5061045461044f3660046146ee565b61128a565b6040516101e89190615406565b34801561046d57600080fd5b5061023e61047c36600461481e565b61131f565b34801561048d57600080fd5b5061038761133f565b3480156104a257600080fd5b5061023e6104b13660046148a5565b61135b565b3480156104c257600080fd5b506102116104d13660046147b7565b6118de565b3480156104e257600080fd5b506102116104f1366004614a31565b6119f1565b34801561050257600080fd5b5061026b610511366004614a7a565b611a6c565b34801561052257600080fd5b5061026b610531366004614666565b611d05565b34801561054257600080fd5b5061055661055136600461493c565b611f30565b6040516101e8919061571c565b34801561056f57600080fd5b5061026b61057e36600461493c565b61202a565b34801561058f57600080fd5b506101db61059e366004614684565b6120c6565b3480156105af57600080fd5b506105b86120e3565b6040516101e891906154d0565b3480156105d157600080fd5b506102116105e03660046147b7565b612143565b3480156105f157600080fd5b506101db612218565b34801561060657600080fd5b506102116106153660046147b7565b61221e565b34801561062657600080fd5b50610387612390565b34801561063b57600080fd5b5061026b61064a366004614666565b6123ac565b34801561065b57600080fd5b506105b861245d565b60036020526000908152604090205481565b61067e614298565b600080610689614298565b60005460ff16156106cf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b60405180910390fd5b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011781558751935091505b81831461076f57610758878381518110151561071957fe5b90602001906020020151878481518110151561073157fe5b90602001906020020151878581518110151561074957fe5b90602001906020020151612494565b90506107648482612532565b600190910190610701565b5050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055509392505050565b60046020526000908152604090205460ff1681565b73ffffffffffffffffffffffffffffffffffffffff831633146108465761080e848484848080601f0160208091040260200160405190810160405280939291908181526020018383808284375061135b945050505050565b1515610846576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906155af565b5050600091825260066020908152604080842073ffffffffffffffffffffffffffffffffffffffff9093168452919052902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b6108ab6142c1565b6108b36142f0565b6108bb6142f0565b6000805460ff16156108f9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905561016080890151610140808a01919091528901519088015261094588611f30565b925061095087611f30565b915061095a612594565b9050610968888483896125c6565b610974878383886125c6565b61097e888861275e565b6109928888856040015185604001516127be565b8051602081015190519195506109ad918a9186918190612945565b6020808501519081015190516109c99189918591908190612945565b6109e28882856020015186604001518860000151612a5e565b6109fb8782846020015185604001518860200151612a5e565b610a0788888387612b0a565b5050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905550949350505050565b610a42614298565b6060610a4f858585612c92565b9050608081825160208401305af48015610a8657815183526020820151602084015260408201516040840152606082015160608401525b505b509392505050565b600a6020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b60008054819060ff1615610af8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b5050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011781558151905b808214610b5857610b508382815181101515610b4157fe5b90602001906020020151612e64565b600101610b29565b5050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905550565b610b8d614298565b600080610b98614298565b60005460ff1615610bd5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011781558751935091505b81831461076f57610c5e8783815181101515610c1f57fe5b906020019060200201518784815181101515610c3757fe5b906020019060200201518785815181101515610c4f57fe5b90602001906020020151612e8f565b9050610c6a8482612532565b600190910190610c07565b6000805481908190819060ff1615610cb9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055610cec612594565b935073ffffffffffffffffffffffffffffffffffffffff84163314610d115733610d14565b60005b73ffffffffffffffffffffffffffffffffffffffff8086166000908152600560209081526040808320938516835292905220549093506001860192509050808211610d8b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061563f565b73ffffffffffffffffffffffffffffffffffffffff80851660008181526005602090815260408083209488168084529490915290819020859055517f82af639571738f4ebd4268fb0363d8957ebe1bbb9e78dba5ebd69eed39b154f090610df3908690615425565b60405180910390a35050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055505050565b610e32614298565b600080610e3d614298565b86519250600091505b818314610eb457610e9d8783815181101515610e5e57fe5b906020019060200201518784815181101515610e7657fe5b906020019060200201518785815181101515610e8e57fe5b90602001906020020151610a3a565b9050610ea98482612532565b600190910190610e46565b5050509392505050565b7fffffffff0000000000000000000000000000000000000000000000000000000081166000908152600a602052604090205473ffffffffffffffffffffffffffffffffffffffff165b919050565b60086020526000908152604090205460ff1681565b610f29614298565b60005460ff1615610f66576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055610f9c848484612e8f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055949350505050565b6000805460ff161561100a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905561103d612594565b73ffffffffffffffffffffffffffffffffffffffff8181166000818152600760209081526040808320948916808452949091529081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168715151790555192935090917fa8656e308026eeabce8f0bc18048433252318ab80ac79da0b3d3d8697dfba891906110d1908690615417565b60405180910390a35050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905550565b600760209081526000928352604080842090915290825290205460ff1681565b61112e614298565b6060600080600061113d614298565b60005460ff161561117a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117815589518a919081106111b257fe5b906020019060200201516101600151945088519350600092505b828414611255578489848151811015156111e257fe5b906020019060200201516101600181905250611202888760200151612ee2565b915061122e898481518110151561121557fe5b9060200190602002015183898681518110151561074957fe5b905061123a8682612532565b6020860151881161124a57611255565b6001909201916111cc565b5050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055509195945050505050565b606060006060600084519250826040519080825280602002602001820160405280156112d057816020015b6112bd6142f0565b8152602001906001900390816112b55790505b509150600090505b808314610a88576112ff85828151811015156112f057fe5b90602001906020020151611f30565b828281518110151561130d57fe5b602090810290910101526001016112d8565b600660209081526000928352604080842090915290825290205460ff1681565b60025473ffffffffffffffffffffffffffffffffffffffff1681565b600080600080600080600080600089511115156113a4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061562f565b6113ad89612f29565b7f010000000000000000000000000000000000000000000000000000000000000090049650600760ff88161061140f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061554f565b8660ff16600781111561141e57fe5b9550600086600781111561142e57fe5b1415611466576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061561f565b600186600781111561147457fe5b14156114bc578851156114b3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906156ef565b600097506118d0565b60028660078111156114ca57fe5b141561160557885160411461150b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906154ef565b88600081518110151561151a57fe5b01602001517f010000000000000000000000000000000000000000000000000000000000000090819004810204945061155a89600163ffffffff612fed16565b935061156d89602163ffffffff612fed16565b925060018b868686604051600081526020016040526040516115929493929190615480565b60206040516020810390808403906000865af11580156115b6573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015173ffffffffffffffffffffffffffffffffffffffff8c811690821614995092506118d09050565b600386600781111561161357fe5b14156117b9578851604114611654576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906154ef565b88600081518110151561166357fe5b01602001517f01000000000000000000000000000000000000000000000000000000000000009081900481020494506116a389600163ffffffff612fed16565b93506116b689602163ffffffff612fed16565b925060018b60405160200180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c0182600019166000191681526020019150506040516020818303038152906040526040518082805190602001908083835b6020831061175757805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161171a565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018019909216911617905260408051929094018290038220600083529101928390526115929450925089918991508890615480565b60048660078111156117c757fe5b14156117df576117d88b8b8b613038565b97506118d0565b60058660078111156117ed57fe5b1415611850576117fc8961318d565b73ffffffffffffffffffffffffffffffffffffffff808c1660009081526007602090815260408083209385168352929052205490915060ff16151561184457600097506118d0565b6117d8818c8c8c613206565b600686600781111561185e57fe5b141561189e5760008b815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff8e16845290915290205460ff1697506118d0565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061554f565b505050505050509392505050565b6118e6614298565b60606000806000806118f6614298565b89600081518110151561190557fe5b906020019060200201516101400151955089519450600093505b8385146119e457858a8581518110151561193557fe5b6020908102909101015161014001528651611951908a90612ee2565b92506119948a8581518110151561196457fe5b9060200190602002015160a001518b8681518110151561198057fe5b906020019060200201516080015185613362565b91506119c08a858151811015156119a757fe5b90602001906020020151838a87815181101515610e8e57fe5b90506119cc8782612532565b865189116119d9576119e4565b60019093019261191f565b5050505050509392505050565b6119f9614298565b60005460ff1615611a36576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055610f9c848484612494565b60095460009073ffffffffffffffffffffffffffffffffffffffff1615611abf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b611b02611afd888888888080601f016020809104026020016040519081016040528093929190818152602001838380828437506133b8945050505050565b6135f9565b60008181526008602052604090205490915060ff1615611b4e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061559f565b73ffffffffffffffffffffffffffffffffffffffff86163314611c1f57611ba6818785858080601f0160208091040260200160405190810160405280939291908181526020018383808284375061135b945050505050565b1515611bde576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906156df565b600980547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff88161790555b6000818152600860205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555130908690869080838380828437820191505092505050600060405180830381855af49150501515611cb6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906155cf565b73ffffffffffffffffffffffffffffffffffffffff86163314611cfc57600980547fffffffffffffffffffffffff00000000000000000000000000000000000000001690555b50505050505050565b6002546000908190819073ffffffffffffffffffffffffffffffffffffffff163314611d5d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061568f565b8392508273ffffffffffffffffffffffffffffffffffffffff1663ae25532e6040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b158015611dc457600080fd5b505af1158015611dd8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611dfc919081019061491e565b7fffffffff0000000000000000000000000000000000000000000000000000000081166000908152600a602052604090205490925073ffffffffffffffffffffffffffffffffffffffff1690508015611e81576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061552f565b7fffffffff0000000000000000000000000000000000000000000000000000000082166000908152600a60205260409081902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8616179055517fd2c6b762299c609bdb96520b58a49bfb80186934d4f71a86a367571a15c0319490611f2290849087906154b5565b60405180910390a150505050565b611f386142f0565b611f4182613639565b6020808301829052600091825260039052604090819020549082015260808201511515611f755760015b60ff168152610f07565b60a08201511515611f87576002611f6b565b60a0820151604082015110611f9d576005611f6b565b6101008201514210611fb0576004611f6b565b60208082015160009081526004909152604090205460ff1615611fd4576006611f6b565b610120820151825173ffffffffffffffffffffffffffffffffffffffff90811660009081526005602090815260408083206060880151909416835292905220541115612021576006611f6b565b60038152919050565b60005460ff1615612067576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905561209b81612e64565b50600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055565b600560209081526000928352604080842090915290825290205481565b606060405190810160405280602481526020017ff47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f81526020017f4699f4980000000000000000000000000000000000000000000000000000000081525081565b61214b614298565b6060600080600061215a614298565b88600081518110151561216957fe5b906020019060200201516101600151945088519350600092505b82841461220c5784898481518110151561219957fe5b9060200190602002015161016001819052506121b9888760200151612ee2565b91506121e589848151811015156121cc57fe5b90602001906020020151838986815181101515610e8e57fe5b90506121f18682612532565b602086015188116122015761220c565b600190920191612183565b50505050509392505050565b60015481565b612226614298565b6060600080600080612236614298565b60005460ff1615612273576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061567f565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011781558a518b919081106122ab57fe5b906020019060200201516101400151955089519450600093505b83851461235a57858a858151811015156122db57fe5b60209081029091010151610140015286516122f7908a90612ee2565b925061230a8a8581518110151561196457fe5b91506123368a8581518110151561231d57fe5b90602001906020020151838a8781518110151561074957fe5b90506123428782612532565b8651891161234f5761235a565b6001909301926122c5565b5050600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905550929695505050505050565b60095473ffffffffffffffffffffffffffffffffffffffff1681565b60025473ffffffffffffffffffffffffffffffffffffffff1633146123fd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061568f565b73ffffffffffffffffffffffffffffffffffffffff81161561245a57600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83161790555b50565b60408051808201909152600581527f322e302e30000000000000000000000000000000000000000000000000000000602082015281565b61249c614298565b6124a46142f0565b60008060006124b288611f30565b93506124bc612594565b92506124ca888585896125c6565b6124dc8860a001518560400151612ee2565b91506124e88783613647565b90506124fb888589848960000151612945565b612505888261365d565b945061251c88848660200151876040015189612a5e565b6125278884876136be565b505050509392505050565b815181516125409190613776565b8252602080830151908201516125569190613776565b60208301526040808301519082015161256f9190613776565b6040830152606080830151908201516125889190613776565b60609092019190915250565b60095460009073ffffffffffffffffffffffffffffffffffffffff168181156125bd57816125bf565b335b9392505050565b825160ff16600314612604576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906156af565b606084015173ffffffffffffffffffffffffffffffffffffffff161561267757606084015173ffffffffffffffffffffffffffffffffffffffff163314612677576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906156bf565b602084015173ffffffffffffffffffffffffffffffffffffffff1615612702578173ffffffffffffffffffffffffffffffffffffffff16846020015173ffffffffffffffffffffffffffffffffffffffff16141515612702576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906154ff565b6040830151151561275857612720836020015185600001518361135b565b1515612758576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061556f565b50505050565b6127708260a001518260a001516137c0565b612782836080015183608001516137c0565b10156127ba576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906156cf565b5050565b6127c66142c1565b6000806000806127da8960a0015188612ee2565b93506127ef89608001518a60a001518661381b565b92506127ff8860a0015187612ee2565b915061281488608001518960a001518461381b565b90508084106128575760208086018051839052805182018490525151865182015260808a015160a08b015187519092015161284f929061381b565b855152612894565b845183905284516020908101859052855181015190860180519190915260a089015160808a015191515161288b9290613898565b60208087015101525b84515160208087015101516128a99190612ee2565b604086015284515160808a015160c08b01516128c692919061381b565b85516040015284516020015160a08a015160e08b01516128e792919061381b565b855160600152602085015151608089015160c08a015161290892919061381b565b856020015160400181815250506129308560200151602001518960a001518a60e0015161381b565b60208601516060015250505050949350505050565b82151561297e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906155ef565b828211156129b8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906155df565b8460a001516129cb856040015184613776565b1115612a03576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061550f565b612a118560800151836137c0565b612a1f828760a001516137c0565b1115612a57576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061566f565b5050505050565b612a6c828260200151613776565b600084815260036020908152604091829020929092558681015187518451938501518584015160608701516101408c01516101608d015196518b9873ffffffffffffffffffffffffffffffffffffffff9788169897909616967f0bcc4c97732e47d9946f229edb95f5b6323f601300e4690de719993f3c37112996612afb968f96339692959194909390615345565b60405180910390a45050505050565b60608060405190810160405280602481526020017ff47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f81526020017f4699f498000000000000000000000000000000000000000000000000000000008152509050612b8b85610140015186600001518660000151856020015160200151613935565b61014084015184518651845160200151612ba793929190613935565b612bc08561014001518660000151858560400151613935565b612bdc8186600001518760400151856000015160400151613935565b612bf88185600001518660400151856020015160400151613935565b836040015173ffffffffffffffffffffffffffffffffffffffff16856040015173ffffffffffffffffffffffffffffffffffffffff161415612c6257612c5d81848760400151612c58866000015160600151876020015160600151613776565b613935565b612a57565b612c7a81848760400151856000015160600151613935565b612a5781848660400151856020015160600151613935565b604080517fb4be83d5000000000000000000000000000000000000000000000000000000006020808301919091526060602483018181528751608485019081528884015160a48601529488015160c48501529087015160e4840152608087015161010484015260a087015161012484015260c087015161014484015260e08701516101648401526101008701516101848401526101208701516101a4840152610140870180516101c485019081526101608901516101e4860152610180905251805161020485018190529394919384936044870192849261022489019291820191601f82010460005b81811015612d99578351855260209485019490930192600101612d7b565b50505050818103610160808401919091528a0151805180835260209283019291820191601f82010460005b81811015612de2578351855260209485019490930192600101612dc4565b50505089845250848103602093840190815288518083529093918201918981019190601f82010460005b81811015612e2a578351855260209485019490930192600101612e0c565b5050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08883030188525060405250505050509392505050565b612e6c6142f0565b612e7582611f30565b9050612e818282613aff565b6127ba828260200151613c16565b612e97614298565b612ea2848484612494565b602081015190915083146125bf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061565f565b600082821115612f1e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061551f565b508082035b92915050565b6000808251111515612f67576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061560f565b815182907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101908110612f9757fe5b016020015182517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01909252507f0100000000000000000000000000000000000000000000000000000000000000908190040290565b60008160200183511015151561302f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061553f565b50016020015190565b6040516000906060907f1626ba7e00000000000000000000000000000000000000000000000000000000906130739087908690602401615460565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093178352815191935090829081885afa808015613110576001811461318157612527565b7f08c379a0000000000000000000000000000000000000000000000000000000006000527c20000000000000000000000000000000000000000000000000000000006020527c0c57414c4c45545f4552524f5200000000000000000000000000000000604052600060605260646000fd5b50505195945050505050565b600060148251101515156131cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061569f565b6131db826014845103613cbd565b82517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec019092525090565b6040516000906060907f93634702000000000000000000000000000000000000000000000000000000009061324390879087908790602401615433565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931783528151919350908290818a5afa8080156132e0576001811461335157613356565b7f08c379a0000000000000000000000000000000000000000000000000000000006000527c20000000000000000000000000000000000000000000000000000000006020527c0f56414c494441544f525f4552524f5200000000000000000000000000604052600060605260646000fd5b825194505b50505050949350505050565b600080831161339d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061555f565b6133b06133aa85846137c0565b84613d1e565b949350505050565b604080517f5a65726f45785472616e73616374696f6e2800000000000000000000000000006020808301919091527f75696e743235362073616c742c0000000000000000000000000000000000000060328301527f61646472657373207369676e6572416464726573732c00000000000000000000603f8301527f627974657320646174610000000000000000000000000000000000000000000060558301527f2900000000000000000000000000000000000000000000000000000000000000605f830152825180830384018152606090920192839052815160009384938493909282918401908083835b602083106134e157805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016134a4565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018019909216911617905260405191909301819003812089519097508995509093508392850191508083835b6020831061357757805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161353a565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040805192909401829003822097825281019a909a525073ffffffffffffffffffffffffffffffffffffffff97909716968801969096525050606085015250506080909120919050565b6001546040517f19010000000000000000000000000000000000000000000000000000000000008152600281019190915260228101919091526042902090565b6000612f23611afd83613d35565b600081831061365657816125bf565b5090919050565b613665614298565b6020810182905260a0830151608084015161368191849161381b565b808252608084015160c085015161369992919061381b565b604082015260a083015160e08401516136b391849161381b565b606082015292915050565b60408051606081018252602481527ff47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f60208201527f4699f4980000000000000000000000000000000000000000000000000000000091810191909152610140840151845183516137319291908690613935565b61374a8461016001518486600001518560200151613935565b61376281856000015186604001518560400151613935565b612758818486604001518560600151613935565b6000828201838110156137b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061558f565b8091505b5092915050565b6000808315156137d357600091506137b9565b508282028284828115156137e357fe5b04146137b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061558f565b6000808311613856576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061555f565b61386184848461418e565b1561339d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906155bf565b60008083116138d3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061555f565b6138de848484614213565b15613915576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906155bf565b6133b06133aa61392586856137c0565b613930866001612ee2565b613776565b60008060008311801561397457508373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614155b15613af75785516003106139b4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061564f565b50506020848101517fffffffff00000000000000000000000000000000000000000000000000000000166000818152600a90925260409091205473ffffffffffffffffffffffffffffffffffffffff16801515613a3d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906155ff565b604051660fffffffffffe0603f885101168060840182017fa85e59e40000000000000000000000000000000000000000000000000000000083526080600484015273ffffffffffffffffffffffffffffffffffffffff8816602484015273ffffffffffffffffffffffffffffffffffffffff87166044840152856064840152608483015b81811015613ad957895181526020998a019901613ac1565b61020084858403866000895af1801515613af1573d85fd5b50505050505b505050505050565b805160009060ff16600314613b40576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906156af565b606083015173ffffffffffffffffffffffffffffffffffffffff1615613bb357606083015173ffffffffffffffffffffffffffffffffffffffff163314613bb3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c6906156bf565b613bbb612594565b835190915073ffffffffffffffffffffffffffffffffffffffff808316911614613c11576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061557f565b505050565b6000818152600460205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558281015183516101408501516101608601519351859473ffffffffffffffffffffffffffffffffffffffff9485169493909316927fdc47b3613d9fe400085f6dbdc99453462279057e6207385042827ed6b1a62cf792613cb1923392906153c9565b60405180910390a45050565b600081601401835110151515613cff576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061569f565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b6000808284811515613d2c57fe5b04949350505050565b604080517f4f726465722800000000000000000000000000000000000000000000000000006020808301919091527f61646472657373206d616b6572416464726573732c000000000000000000000060268301527f616464726573732074616b6572416464726573732c0000000000000000000000603b8301527f6164647265737320666565526563697069656e74416464726573732c0000000060508301527f616464726573732073656e646572416464726573732c00000000000000000000606c8301527f75696e74323536206d616b65724173736574416d6f756e742c0000000000000060828301527f75696e743235362074616b65724173736574416d6f756e742c00000000000000609b8301527f75696e74323536206d616b65724665652c00000000000000000000000000000060b48301527f75696e743235362074616b65724665652c00000000000000000000000000000060c58301527f75696e743235362065787069726174696f6e54696d655365636f6e64732c000060d68301527f75696e743235362073616c742c0000000000000000000000000000000000000060f48301527f6279746573206d616b65724173736574446174612c00000000000000000000006101018301527f62797465732074616b65724173736574446174610000000000000000000000006101168301527f290000000000000000000000000000000000000000000000000000000000000061012a830152825161010b81840301815261012b90920192839052815160009384938493849391929182918401908083835b60208310613fbd57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101613f80565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930181900381206101408b0151805191995095509093508392850191508083835b6020831061405857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161401b565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930181900381206101608b0151805191985095509093508392850191508083835b602083106140f357805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016140b6565b5181516020939093036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018019909116921691909117905260405192018290039091207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0890180516101408b018051610160909c0180519a84529881529288526101a0822091529890525050509190525090919050565b6000808084116141ca576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061555f565b8215806141d5575084155b156141e35760009150610a88565b838015156141ed57fe5b85840990506141fc85846137c0565b6142086103e8836137c0565b101595945050505050565b60008080841161424f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106c69061555f565b82158061425a575084155b156142685760009150610a88565b8380151561427257fe5b8584099050836142828583612ee2565b81151561428b57fe5b0690506141fc85846137c0565b608060405190810160405280600081526020016000815260200160008152602001600081525090565b610120604051908101604052806142d6614298565b81526020016142e3614298565b8152602001600081525090565b604080516060810182526000808252602082018190529181019190915290565b60006125bf82356157c2565b6000601f8201831361432d57600080fd5b813561434061433b82615751565b61572a565b81815260209384019390925082018360005b8381101561437e578135860161436888826144ce565b8452506020928301929190910190600101614352565b5050505092915050565b6000601f8201831361439957600080fd5b81356143a761433b82615751565b81815260209384019390925082018360005b8381101561437e57813586016143cf888261451d565b84525060209283019291909101906001016143b9565b6000601f820183136143f657600080fd5b813561440461433b82615751565b9150818183526020840193506020810190508385602084028201111561442957600080fd5b60005b8381101561437e578161443f8882614461565b845250602092830192919091019060010161442c565b60006125bf82356157db565b60006125bf82356157e0565b60006125bf82356157e3565b60006125bf82516157e3565b600080601f8301841361449757600080fd5b50813567ffffffffffffffff8111156144af57600080fd5b6020830191508360018202830111156144c757600080fd5b9250929050565b6000601f820183136144df57600080fd5b81356144ed61433b82615772565b9150808252602083016020830185838301111561450957600080fd5b614514838284615819565b50505092915050565b6000610180828403121561453057600080fd5b61453b61018061572a565b905060006145498484614310565b825250602061455a84848301614310565b602083015250604061456e84828501614310565b604083015250606061458284828501614310565b606083015250608061459684828501614461565b60808301525060a06145aa84828501614461565b60a08301525060c06145be84828501614461565b60c08301525060e06145d284828501614461565b60e0830152506101006145e784828501614461565b610100830152506101206145fd84828501614461565b6101208301525061014082013567ffffffffffffffff81111561461f57600080fd5b61462b848285016144ce565b6101408301525061016082013567ffffffffffffffff81111561464d57600080fd5b614659848285016144ce565b6101608301525092915050565b60006020828403121561467857600080fd5b60006133b08484614310565b6000806040838503121561469757600080fd5b60006146a38585614310565b92505060206146b485828601614310565b9150509250929050565b600080604083850312156146d157600080fd5b60006146dd8585614310565b92505060206146b485828601614455565b60006020828403121561470057600080fd5b813567ffffffffffffffff81111561471757600080fd5b6133b084828501614388565b60008060006060848603121561473857600080fd5b833567ffffffffffffffff81111561474f57600080fd5b61475b86828701614388565b935050602084013567ffffffffffffffff81111561477857600080fd5b614784868287016143e5565b925050604084013567ffffffffffffffff8111156147a157600080fd5b6147ad8682870161431c565b9150509250925092565b6000806000606084860312156147cc57600080fd5b833567ffffffffffffffff8111156147e357600080fd5b6147ef86828701614388565b935050602061478486828701614461565b60006020828403121561481257600080fd5b60006133b08484614461565b6000806040838503121561483157600080fd5b60006146a38585614461565b6000806000806060858703121561485357600080fd5b600061485f8787614461565b945050602061487087828801614310565b935050604085013567ffffffffffffffff81111561488d57600080fd5b61489987828801614485565b95989497509550505050565b6000806000606084860312156148ba57600080fd5b60006148c68686614461565b93505060206148d786828701614310565b925050604084013567ffffffffffffffff8111156148f457600080fd5b6147ad868287016144ce565b60006020828403121561491257600080fd5b60006133b0848461446d565b60006020828403121561493057600080fd5b60006133b08484614479565b60006020828403121561494e57600080fd5b813567ffffffffffffffff81111561496557600080fd5b6133b08482850161451d565b6000806000806080858703121561498757600080fd5b843567ffffffffffffffff81111561499e57600080fd5b6149aa8782880161451d565b945050602085013567ffffffffffffffff8111156149c757600080fd5b6149d38782880161451d565b935050604085013567ffffffffffffffff8111156149f057600080fd5b6149fc878288016144ce565b925050606085013567ffffffffffffffff811115614a1957600080fd5b614a25878288016144ce565b91505092959194509250565b600080600060608486031215614a4657600080fd5b833567ffffffffffffffff811115614a5d57600080fd5b614a698682870161451d565b93505060206148d786828701614461565b60008060008060008060808789031215614a9357600080fd5b6000614a9f8989614461565b9650506020614ab089828a01614310565b955050604087013567ffffffffffffffff811115614acd57600080fd5b614ad989828a01614485565b9450945050606087013567ffffffffffffffff811115614af857600080fd5b614b0489828a01614485565b92509250509295509295509295565b614b1c816157c2565b82525050565b6000614b2d826157be565b808452602084019350614b3f836157b8565b60005b82811015614b6f57614b558683516152f7565b614b5e826157b8565b606096909601959150600101614b42565b5093949350505050565b614b1c816157db565b614b1c816157e0565b614b1c816157e3565b6000614b9f826157be565b808452614bb3816020860160208601615825565b614bbc81615851565b9093016020019392505050565b614b1c8161580e565b601281527f4c454e4754485f36355f52455155495245440000000000000000000000000000602082015260400190565b600d81527f494e56414c49445f54414b455200000000000000000000000000000000000000602082015260400190565b600e81527f4f524445525f4f56455246494c4c000000000000000000000000000000000000602082015260400190565b601181527f55494e543235365f554e444552464c4f57000000000000000000000000000000602082015260400190565b601a81527f41535345545f50524f58595f414c52454144595f455849535453000000000000602082015260400190565b602681527f475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b601581527f5349474e41545552455f554e535550504f525445440000000000000000000000602082015260400190565b601081527f4449564953494f4e5f42595f5a45524f00000000000000000000000000000000602082015260400190565b601781527f494e56414c49445f4f524445525f5349474e4154555245000000000000000000602082015260400190565b600d81527f494e56414c49445f4d414b455200000000000000000000000000000000000000602082015260400190565b601081527f55494e543235365f4f564552464c4f5700000000000000000000000000000000602082015260400190565b600f81527f494e56414c49445f54585f484153480000000000000000000000000000000000602082015260400190565b601181527f494e56414c49445f5349474e4154555245000000000000000000000000000000602082015260400190565b600e81527f524f554e44494e475f4552524f52000000000000000000000000000000000000602082015260400190565b601081527f4641494c45445f455845435554494f4e00000000000000000000000000000000602082015260400190565b600d81527f54414b45525f4f56455250415900000000000000000000000000000000000000602082015260400190565b601481527f494e56414c49445f54414b45525f414d4f554e54000000000000000000000000602082015260400190565b601a81527f41535345545f50524f58595f444f45535f4e4f545f4558495354000000000000602082015260400190565b602181527f475245415445525f5448414e5f5a45524f5f4c454e4754485f5245515549524560208201527f4400000000000000000000000000000000000000000000000000000000000000604082015260600190565b601181527f5349474e41545552455f494c4c4547414c000000000000000000000000000000602082015260400190565b601e81527f4c454e4754485f475245415445525f5448414e5f305f52455155495245440000602082015260400190565b601781527f494e56414c49445f4e45575f4f524445525f45504f4348000000000000000000602082015260400190565b601e81527f4c454e4754485f475245415445525f5448414e5f335f52455155495245440000602082015260400190565b601481527f434f4d504c4554455f46494c4c5f4641494c4544000000000000000000000000602082015260400190565b601281527f494e56414c49445f46494c4c5f50524943450000000000000000000000000000602082015260400190565b601281527f5245454e5452414e43595f494c4c4547414c0000000000000000000000000000602082015260400190565b601381527f4f4e4c595f434f4e54524143545f4f574e455200000000000000000000000000602082015260400190565b602681527f475245415445525f4f525f455155414c5f544f5f32305f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b601081527f4f524445525f554e46494c4c41424c4500000000000000000000000000000000602082015260400190565b600e81527f494e56414c49445f53454e444552000000000000000000000000000000000000602082015260400190565b601881527f4e454741544956455f5350524541445f52455155495245440000000000000000602082015260400190565b601481527f494e56414c49445f54585f5349474e4154555245000000000000000000000000602082015260400190565b601181527f4c454e4754485f305f5245515549524544000000000000000000000000000000602082015260400190565b805160808301906152858482614b82565b5060208201516152986020850182614b82565b5060408201516152ab6040850182614b82565b5060608201516127586060850182614b82565b80516101208301906152d08482615274565b5060208201516152e36080850182615274565b506040820151612758610100850182614b82565b80516060830190615308848261532e565b50602082015161531b6020850182614b82565b5060408201516127586040850182614b82565b614b1c81615808565b60208101612f238284614b13565b6101008101615354828b614b13565b615361602083018a614b13565b61536e6040830189614b82565b61537b6060830188614b82565b6153886080830187614b82565b61539560a0830186614b82565b81810360c08301526153a78185614b94565b905081810360e08301526153bb8184614b94565b9a9950505050505050505050565b606081016153d78286614b13565b81810360208301526153e98185614b94565b905081810360408301526153fd8184614b94565b95945050505050565b602080825281016125bf8184614b22565b60208101612f238284614b79565b60208101612f238284614b82565b606081016154418286614b82565b61544e6020830185614b13565b81810360408301526153fd8184614b94565b6040810161546e8285614b82565b81810360208301526133b08184614b94565b6080810161548e8287614b82565b61549b602083018661532e565b6154a86040830185614b82565b6153fd6060830184614b82565b604081016154c38285614b8b565b6125bf6020830184614b13565b602080825281016125bf8184614b94565b60208101612f238284614bc9565b60208082528101612f2381614bd2565b60208082528101612f2381614c02565b60208082528101612f2381614c32565b60208082528101612f2381614c62565b60208082528101612f2381614c92565b60208082528101612f2381614cc2565b60208082528101612f2381614d18565b60208082528101612f2381614d48565b60208082528101612f2381614d78565b60208082528101612f2381614da8565b60208082528101612f2381614dd8565b60208082528101612f2381614e08565b60208082528101612f2381614e38565b60208082528101612f2381614e68565b60208082528101612f2381614e98565b60208082528101612f2381614ec8565b60208082528101612f2381614ef8565b60208082528101612f2381614f28565b60208082528101612f2381614f58565b60208082528101612f2381614fae565b60208082528101612f2381614fde565b60208082528101612f238161500e565b60208082528101612f238161503e565b60208082528101612f238161506e565b60208082528101612f238161509e565b60208082528101612f23816150ce565b60208082528101612f23816150fe565b60208082528101612f238161512e565b60208082528101612f2381615184565b60208082528101612f23816151b4565b60208082528101612f23816151e4565b60208082528101612f2381615214565b60208082528101612f2381615244565b60808101612f238284615274565b6101208101612f2382846152be565b60608101612f2382846152f7565b60405181810167ffffffffffffffff8111828210171561574957600080fd5b604052919050565b600067ffffffffffffffff82111561576857600080fd5b5060209081020190565b600067ffffffffffffffff82111561578957600080fd5b506020601f919091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160190565b60200190565b5190565b73ffffffffffffffffffffffffffffffffffffffff1690565b151590565b90565b7fffffffff000000000000000000000000000000000000000000000000000000001690565b60ff1690565b6000612f23826157c2565b82818337506000910152565b60005b83811015615840578181015183820152602001615828565b838111156127585750506000910152565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016905600a265627a7a72305820b19c92201032603398c007dccf80930c626ef89fa6c848c8b7ef5267d07cd4ed6c6578706572696d656e74616cf50037", - "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0x1B6 JUMPI PUSH4 0xFFFFFFFF PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0x288CDC91 DUP2 EQ PUSH2 0x1BB JUMPI DUP1 PUSH4 0x297BB70B EQ PUSH2 0x1F1 JUMPI DUP1 PUSH4 0x2AC12622 EQ PUSH2 0x21E JUMPI DUP1 PUSH4 0x3683EF8E EQ PUSH2 0x24B JUMPI DUP1 PUSH4 0x3C28D861 EQ PUSH2 0x26D JUMPI DUP1 PUSH4 0x3E228BAE EQ PUSH2 0x29A JUMPI DUP1 PUSH4 0x3FD3C997 EQ PUSH2 0x2BA JUMPI DUP1 PUSH4 0x4AC14782 EQ PUSH2 0x2E7 JUMPI DUP1 PUSH4 0x4D0AE546 EQ PUSH2 0x307 JUMPI DUP1 PUSH4 0x4F9559B1 EQ PUSH2 0x327 JUMPI DUP1 PUSH4 0x50DDE190 EQ PUSH2 0x347 JUMPI DUP1 PUSH4 0x60704108 EQ PUSH2 0x367 JUMPI DUP1 PUSH4 0x642F2EAF EQ PUSH2 0x394 JUMPI DUP1 PUSH4 0x64A3BC15 EQ PUSH2 0x3B4 JUMPI DUP1 PUSH4 0x77FCCE68 EQ PUSH2 0x3D4 JUMPI DUP1 PUSH4 0x7B8E3514 EQ PUSH2 0x3F4 JUMPI DUP1 PUSH4 0x7E1D9808 EQ PUSH2 0x414 JUMPI DUP1 PUSH4 0x7E9D74DC EQ PUSH2 0x434 JUMPI DUP1 PUSH4 0x82C174D0 EQ PUSH2 0x461 JUMPI DUP1 PUSH4 0x8DA5CB5B EQ PUSH2 0x481 JUMPI DUP1 PUSH4 0x93634702 EQ PUSH2 0x496 JUMPI DUP1 PUSH4 0xA3E20380 EQ PUSH2 0x4B6 JUMPI DUP1 PUSH4 0xB4BE83D5 EQ PUSH2 0x4D6 JUMPI DUP1 PUSH4 0xBFC8BFCE EQ PUSH2 0x4F6 JUMPI DUP1 PUSH4 0xC585BB93 EQ PUSH2 0x516 JUMPI DUP1 PUSH4 0xC75E0A81 EQ PUSH2 0x536 JUMPI DUP1 PUSH4 0xD46B02C3 EQ PUSH2 0x563 JUMPI DUP1 PUSH4 0xD9BFA73E EQ PUSH2 0x583 JUMPI DUP1 PUSH4 0xDB123B1A EQ PUSH2 0x5A3 JUMPI DUP1 PUSH4 0xDD1C7D18 EQ PUSH2 0x5C5 JUMPI DUP1 PUSH4 0xE306F779 EQ PUSH2 0x5E5 JUMPI DUP1 PUSH4 0xE5FA431B EQ PUSH2 0x5FA JUMPI DUP1 PUSH4 0xEEA086BA EQ PUSH2 0x61A JUMPI DUP1 PUSH4 0xF2FDE38B EQ PUSH2 0x62F JUMPI DUP1 PUSH4 0xFFA1AD74 EQ PUSH2 0x64F JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1C7 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1DB PUSH2 0x1D6 CALLDATASIZE PUSH1 0x4 PUSH2 0x4800 JUMP JUMPDEST PUSH2 0x664 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x5425 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1FD JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x20C CALLDATASIZE PUSH1 0x4 PUSH2 0x4723 JUMP JUMPDEST PUSH2 0x676 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x56FF JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x22A JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x23E PUSH2 0x239 CALLDATASIZE PUSH1 0x4 PUSH2 0x4800 JUMP JUMPDEST PUSH2 0x7A1 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x5417 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x257 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x266 CALLDATASIZE PUSH1 0x4 PUSH2 0x483D JUMP JUMPDEST PUSH2 0x7B6 JUMP JUMPDEST STOP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x279 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x28D PUSH2 0x288 CALLDATASIZE PUSH1 0x4 PUSH2 0x4971 JUMP JUMPDEST PUSH2 0x8A3 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x570D JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x2A6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x2B5 CALLDATASIZE PUSH1 0x4 PUSH2 0x4A31 JUMP JUMPDEST PUSH2 0xA3A JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x2C6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x2DA PUSH2 0x2D5 CALLDATASIZE PUSH1 0x4 PUSH2 0x4900 JUMP JUMPDEST PUSH2 0xA90 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x54E1 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x2F3 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x302 CALLDATASIZE PUSH1 0x4 PUSH2 0x46EE JUMP JUMPDEST PUSH2 0xAB8 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x313 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x322 CALLDATASIZE PUSH1 0x4 PUSH2 0x4723 JUMP JUMPDEST PUSH2 0xB85 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x333 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x342 CALLDATASIZE PUSH1 0x4 PUSH2 0x4800 JUMP JUMPDEST PUSH2 0xC75 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x353 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x362 CALLDATASIZE PUSH1 0x4 PUSH2 0x4723 JUMP JUMPDEST PUSH2 0xE2A JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x373 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x387 PUSH2 0x382 CALLDATASIZE PUSH1 0x4 PUSH2 0x4900 JUMP JUMPDEST PUSH2 0xEBE JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x5337 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x3A0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x23E PUSH2 0x3AF CALLDATASIZE PUSH1 0x4 PUSH2 0x4800 JUMP JUMPDEST PUSH2 0xF0C JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x3C0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x3CF CALLDATASIZE PUSH1 0x4 PUSH2 0x4A31 JUMP JUMPDEST PUSH2 0xF21 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x3E0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x3EF CALLDATASIZE PUSH1 0x4 PUSH2 0x46BE JUMP JUMPDEST PUSH2 0xFCC JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x400 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x23E PUSH2 0x40F CALLDATASIZE PUSH1 0x4 PUSH2 0x4684 JUMP JUMPDEST PUSH2 0x1106 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x420 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x42F CALLDATASIZE PUSH1 0x4 PUSH2 0x47B7 JUMP JUMPDEST PUSH2 0x1126 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x440 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x454 PUSH2 0x44F CALLDATASIZE PUSH1 0x4 PUSH2 0x46EE JUMP JUMPDEST PUSH2 0x128A JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x5406 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x46D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x23E PUSH2 0x47C CALLDATASIZE PUSH1 0x4 PUSH2 0x481E JUMP JUMPDEST PUSH2 0x131F JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x48D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x387 PUSH2 0x133F JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x4A2 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x23E PUSH2 0x4B1 CALLDATASIZE PUSH1 0x4 PUSH2 0x48A5 JUMP JUMPDEST PUSH2 0x135B JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x4C2 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x4D1 CALLDATASIZE PUSH1 0x4 PUSH2 0x47B7 JUMP JUMPDEST PUSH2 0x18DE JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x4E2 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x4F1 CALLDATASIZE PUSH1 0x4 PUSH2 0x4A31 JUMP JUMPDEST PUSH2 0x19F1 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x502 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x511 CALLDATASIZE PUSH1 0x4 PUSH2 0x4A7A JUMP JUMPDEST PUSH2 0x1A6C JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x522 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x531 CALLDATASIZE PUSH1 0x4 PUSH2 0x4666 JUMP JUMPDEST PUSH2 0x1D05 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x542 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x556 PUSH2 0x551 CALLDATASIZE PUSH1 0x4 PUSH2 0x493C JUMP JUMPDEST PUSH2 0x1F30 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x571C JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x56F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x57E CALLDATASIZE PUSH1 0x4 PUSH2 0x493C JUMP JUMPDEST PUSH2 0x202A JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x58F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1DB PUSH2 0x59E CALLDATASIZE PUSH1 0x4 PUSH2 0x4684 JUMP JUMPDEST PUSH2 0x20C6 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x5AF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x5B8 PUSH2 0x20E3 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x1E8 SWAP2 SWAP1 PUSH2 0x54D0 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x5D1 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x5E0 CALLDATASIZE PUSH1 0x4 PUSH2 0x47B7 JUMP JUMPDEST PUSH2 0x2143 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x5F1 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1DB PUSH2 0x2218 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x606 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x211 PUSH2 0x615 CALLDATASIZE PUSH1 0x4 PUSH2 0x47B7 JUMP JUMPDEST PUSH2 0x221E JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x626 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x387 PUSH2 0x2390 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x63B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x26B PUSH2 0x64A CALLDATASIZE PUSH1 0x4 PUSH2 0x4666 JUMP JUMPDEST PUSH2 0x23AC JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x65B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x5B8 PUSH2 0x245D JUMP JUMPDEST PUSH1 0x3 PUSH1 0x20 MSTORE PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP2 JUMP JUMPDEST PUSH2 0x67E PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH2 0x689 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x6CF JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR DUP2 SSTORE DUP8 MLOAD SWAP4 POP SWAP2 POP JUMPDEST DUP2 DUP4 EQ PUSH2 0x76F JUMPI PUSH2 0x758 DUP8 DUP4 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x719 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP8 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x731 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP8 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x749 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x2494 JUMP JUMPDEST SWAP1 POP PUSH2 0x764 DUP5 DUP3 PUSH2 0x2532 JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP2 ADD SWAP1 PUSH2 0x701 JUMP JUMPDEST POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x4 PUSH1 0x20 MSTORE PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND CALLER EQ PUSH2 0x846 JUMPI PUSH2 0x80E DUP5 DUP5 DUP5 DUP5 DUP1 DUP1 PUSH1 0x1F ADD PUSH1 0x20 DUP1 SWAP2 DIV MUL PUSH1 0x20 ADD PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 SWAP4 SWAP3 SWAP2 SWAP1 DUP2 DUP2 MSTORE PUSH1 0x20 ADD DUP4 DUP4 DUP1 DUP3 DUP5 CALLDATACOPY POP PUSH2 0x135B SWAP5 POP POP POP POP POP JUMP JUMPDEST ISZERO ISZERO PUSH2 0x846 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x55AF JUMP JUMPDEST POP POP PUSH1 0x0 SWAP2 DUP3 MSTORE PUSH1 0x6 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP4 AND DUP5 MSTORE SWAP2 SWAP1 MSTORE SWAP1 KECCAK256 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE JUMP JUMPDEST PUSH2 0x8AB PUSH2 0x42C1 JUMP JUMPDEST PUSH2 0x8B3 PUSH2 0x42F0 JUMP JUMPDEST PUSH2 0x8BB PUSH2 0x42F0 JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x8F9 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE PUSH2 0x160 DUP1 DUP10 ADD MLOAD PUSH2 0x140 DUP1 DUP11 ADD SWAP2 SWAP1 SWAP2 MSTORE DUP10 ADD MLOAD SWAP1 DUP9 ADD MSTORE PUSH2 0x945 DUP9 PUSH2 0x1F30 JUMP JUMPDEST SWAP3 POP PUSH2 0x950 DUP8 PUSH2 0x1F30 JUMP JUMPDEST SWAP2 POP PUSH2 0x95A PUSH2 0x2594 JUMP JUMPDEST SWAP1 POP PUSH2 0x968 DUP9 DUP5 DUP4 DUP10 PUSH2 0x25C6 JUMP JUMPDEST PUSH2 0x974 DUP8 DUP4 DUP4 DUP9 PUSH2 0x25C6 JUMP JUMPDEST PUSH2 0x97E DUP9 DUP9 PUSH2 0x275E JUMP JUMPDEST PUSH2 0x992 DUP9 DUP9 DUP6 PUSH1 0x40 ADD MLOAD DUP6 PUSH1 0x40 ADD MLOAD PUSH2 0x27BE JUMP JUMPDEST DUP1 MLOAD PUSH1 0x20 DUP2 ADD MLOAD SWAP1 MLOAD SWAP2 SWAP6 POP PUSH2 0x9AD SWAP2 DUP11 SWAP2 DUP7 SWAP2 DUP2 SWAP1 PUSH2 0x2945 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP6 ADD MLOAD SWAP1 DUP2 ADD MLOAD SWAP1 MLOAD PUSH2 0x9C9 SWAP2 DUP10 SWAP2 DUP6 SWAP2 SWAP1 DUP2 SWAP1 PUSH2 0x2945 JUMP JUMPDEST PUSH2 0x9E2 DUP9 DUP3 DUP6 PUSH1 0x20 ADD MLOAD DUP7 PUSH1 0x40 ADD MLOAD DUP9 PUSH1 0x0 ADD MLOAD PUSH2 0x2A5E JUMP JUMPDEST PUSH2 0x9FB DUP8 DUP3 DUP5 PUSH1 0x20 ADD MLOAD DUP6 PUSH1 0x40 ADD MLOAD DUP9 PUSH1 0x20 ADD MLOAD PUSH2 0x2A5E JUMP JUMPDEST PUSH2 0xA07 DUP9 DUP9 DUP4 DUP8 PUSH2 0x2B0A JUMP JUMPDEST POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE POP SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH2 0xA42 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x60 PUSH2 0xA4F DUP6 DUP6 DUP6 PUSH2 0x2C92 JUMP JUMPDEST SWAP1 POP PUSH1 0x80 DUP2 DUP3 MLOAD PUSH1 0x20 DUP5 ADD ADDRESS GAS DELEGATECALL DUP1 ISZERO PUSH2 0xA86 JUMPI DUP2 MLOAD DUP4 MSTORE PUSH1 0x20 DUP3 ADD MLOAD PUSH1 0x20 DUP5 ADD MSTORE PUSH1 0x40 DUP3 ADD MLOAD PUSH1 0x40 DUP5 ADD MSTORE PUSH1 0x60 DUP3 ADD MLOAD PUSH1 0x60 DUP5 ADD MSTORE JUMPDEST POP JUMPDEST POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0xA PUSH1 0x20 MSTORE PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD DUP2 SWAP1 PUSH1 0xFF AND ISZERO PUSH2 0xAF8 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR DUP2 SSTORE DUP2 MLOAD SWAP1 JUMPDEST DUP1 DUP3 EQ PUSH2 0xB58 JUMPI PUSH2 0xB50 DUP4 DUP3 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xB41 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x2E64 JUMP JUMPDEST PUSH1 0x1 ADD PUSH2 0xB29 JUMP JUMPDEST POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE POP JUMP JUMPDEST PUSH2 0xB8D PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH2 0xB98 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0xBD5 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR DUP2 SSTORE DUP8 MLOAD SWAP4 POP SWAP2 POP JUMPDEST DUP2 DUP4 EQ PUSH2 0x76F JUMPI PUSH2 0xC5E DUP8 DUP4 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xC1F JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP8 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xC37 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP8 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xC4F JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x2E8F JUMP JUMPDEST SWAP1 POP PUSH2 0xC6A DUP5 DUP3 PUSH2 0x2532 JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP2 ADD SWAP1 PUSH2 0xC07 JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD DUP2 SWAP1 DUP2 SWAP1 DUP2 SWAP1 PUSH1 0xFF AND ISZERO PUSH2 0xCB9 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE PUSH2 0xCEC PUSH2 0x2594 JUMP JUMPDEST SWAP4 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 AND CALLER EQ PUSH2 0xD11 JUMPI CALLER PUSH2 0xD14 JUMP JUMPDEST PUSH1 0x0 JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP7 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x5 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP4 DUP6 AND DUP4 MSTORE SWAP3 SWAP1 MSTORE KECCAK256 SLOAD SWAP1 SWAP4 POP PUSH1 0x1 DUP7 ADD SWAP3 POP SWAP1 POP DUP1 DUP3 GT PUSH2 0xD8B JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x563F JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP6 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x5 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP5 DUP9 AND DUP1 DUP5 MSTORE SWAP5 SWAP1 SWAP2 MSTORE SWAP1 DUP2 SWAP1 KECCAK256 DUP6 SWAP1 SSTORE MLOAD PUSH32 0x82AF639571738F4EBD4268FB0363D8957EBE1BBB9E78DBA5EBD69EED39B154F0 SWAP1 PUSH2 0xDF3 SWAP1 DUP7 SWAP1 PUSH2 0x5425 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE POP POP POP JUMP JUMPDEST PUSH2 0xE32 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH2 0xE3D PUSH2 0x4298 JUMP JUMPDEST DUP7 MLOAD SWAP3 POP PUSH1 0x0 SWAP2 POP JUMPDEST DUP2 DUP4 EQ PUSH2 0xEB4 JUMPI PUSH2 0xE9D DUP8 DUP4 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xE5E JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP8 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xE76 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP8 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xE8E JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0xA3A JUMP JUMPDEST SWAP1 POP PUSH2 0xEA9 DUP5 DUP3 PUSH2 0x2532 JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP2 ADD SWAP1 PUSH2 0xE46 JUMP JUMPDEST POP POP POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 DUP2 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0xA PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND JUMPDEST SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x8 PUSH1 0x20 MSTORE PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND DUP2 JUMP JUMPDEST PUSH2 0xF29 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0xF66 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE PUSH2 0xF9C DUP5 DUP5 DUP5 PUSH2 0x2E8F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x100A JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE PUSH2 0x103D PUSH2 0x2594 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 DUP2 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x7 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP5 DUP10 AND DUP1 DUP5 MSTORE SWAP5 SWAP1 SWAP2 MSTORE SWAP1 DUP2 SWAP1 KECCAK256 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND DUP8 ISZERO ISZERO OR SWAP1 SSTORE MLOAD SWAP3 SWAP4 POP SWAP1 SWAP2 PUSH32 0xA8656E308026EEABCE8F0BC18048433252318AB80AC79DA0B3D3D8697DFBA891 SWAP1 PUSH2 0x10D1 SWAP1 DUP7 SWAP1 PUSH2 0x5417 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE POP JUMP JUMPDEST PUSH1 0x7 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x0 SWAP3 DUP4 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 SWAP1 SWAP2 MSTORE SWAP1 DUP3 MSTORE SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND DUP2 JUMP JUMPDEST PUSH2 0x112E PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x60 PUSH1 0x0 DUP1 PUSH1 0x0 PUSH2 0x113D PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x117A JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR DUP2 SSTORE DUP10 MLOAD DUP11 SWAP2 SWAP1 DUP2 LT PUSH2 0x11B2 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x160 ADD MLOAD SWAP5 POP DUP9 MLOAD SWAP4 POP PUSH1 0x0 SWAP3 POP JUMPDEST DUP3 DUP5 EQ PUSH2 0x1255 JUMPI DUP5 DUP10 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x11E2 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x160 ADD DUP2 SWAP1 MSTORE POP PUSH2 0x1202 DUP9 DUP8 PUSH1 0x20 ADD MLOAD PUSH2 0x2EE2 JUMP JUMPDEST SWAP2 POP PUSH2 0x122E DUP10 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x1215 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP4 DUP10 DUP7 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x749 JUMPI INVALID JUMPDEST SWAP1 POP PUSH2 0x123A DUP7 DUP3 PUSH2 0x2532 JUMP JUMPDEST PUSH1 0x20 DUP7 ADD MLOAD DUP9 GT PUSH2 0x124A JUMPI PUSH2 0x1255 JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP3 ADD SWAP2 PUSH2 0x11CC JUMP JUMPDEST POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE POP SWAP2 SWAP6 SWAP5 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x60 PUSH1 0x0 PUSH1 0x60 PUSH1 0x0 DUP5 MLOAD SWAP3 POP DUP3 PUSH1 0x40 MLOAD SWAP1 DUP1 DUP3 MSTORE DUP1 PUSH1 0x20 MUL PUSH1 0x20 ADD DUP3 ADD PUSH1 0x40 MSTORE DUP1 ISZERO PUSH2 0x12D0 JUMPI DUP2 PUSH1 0x20 ADD JUMPDEST PUSH2 0x12BD PUSH2 0x42F0 JUMP JUMPDEST DUP2 MSTORE PUSH1 0x20 ADD SWAP1 PUSH1 0x1 SWAP1 SUB SWAP1 DUP2 PUSH2 0x12B5 JUMPI SWAP1 POP JUMPDEST POP SWAP2 POP PUSH1 0x0 SWAP1 POP JUMPDEST DUP1 DUP4 EQ PUSH2 0xA88 JUMPI PUSH2 0x12FF DUP6 DUP3 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x12F0 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x1F30 JUMP JUMPDEST DUP3 DUP3 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x130D JUMPI INVALID JUMPDEST PUSH1 0x20 SWAP1 DUP2 MUL SWAP1 SWAP2 ADD ADD MSTORE PUSH1 0x1 ADD PUSH2 0x12D8 JUMP JUMPDEST PUSH1 0x6 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x0 SWAP3 DUP4 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 SWAP1 SWAP2 MSTORE SWAP1 DUP3 MSTORE SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND DUP2 JUMP JUMPDEST PUSH1 0x2 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x0 DUP10 MLOAD GT ISZERO ISZERO PUSH2 0x13A4 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x562F JUMP JUMPDEST PUSH2 0x13AD DUP10 PUSH2 0x2F29 JUMP JUMPDEST PUSH32 0x100000000000000000000000000000000000000000000000000000000000000 SWAP1 DIV SWAP7 POP PUSH1 0x7 PUSH1 0xFF DUP9 AND LT PUSH2 0x140F JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x554F JUMP JUMPDEST DUP7 PUSH1 0xFF AND PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x141E JUMPI INVALID JUMPDEST SWAP6 POP PUSH1 0x0 DUP7 PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x142E JUMPI INVALID JUMPDEST EQ ISZERO PUSH2 0x1466 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x561F JUMP JUMPDEST PUSH1 0x1 DUP7 PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x1474 JUMPI INVALID JUMPDEST EQ ISZERO PUSH2 0x14BC JUMPI DUP9 MLOAD ISZERO PUSH2 0x14B3 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x56EF JUMP JUMPDEST PUSH1 0x0 SWAP8 POP PUSH2 0x18D0 JUMP JUMPDEST PUSH1 0x2 DUP7 PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x14CA JUMPI INVALID JUMPDEST EQ ISZERO PUSH2 0x1605 JUMPI DUP9 MLOAD PUSH1 0x41 EQ PUSH2 0x150B JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x54EF JUMP JUMPDEST DUP9 PUSH1 0x0 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x151A JUMPI INVALID JUMPDEST ADD PUSH1 0x20 ADD MLOAD PUSH32 0x100000000000000000000000000000000000000000000000000000000000000 SWAP1 DUP2 SWAP1 DIV DUP2 MUL DIV SWAP5 POP PUSH2 0x155A DUP10 PUSH1 0x1 PUSH4 0xFFFFFFFF PUSH2 0x2FED AND JUMP JUMPDEST SWAP4 POP PUSH2 0x156D DUP10 PUSH1 0x21 PUSH4 0xFFFFFFFF PUSH2 0x2FED AND JUMP JUMPDEST SWAP3 POP PUSH1 0x1 DUP12 DUP7 DUP7 DUP7 PUSH1 0x40 MLOAD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x40 MSTORE PUSH1 0x40 MLOAD PUSH2 0x1592 SWAP5 SWAP4 SWAP3 SWAP2 SWAP1 PUSH2 0x5480 JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD PUSH1 0x20 DUP2 SUB SWAP1 DUP1 DUP5 SUB SWAP1 PUSH1 0x0 DUP7 GAS CALL ISZERO DUP1 ISZERO PUSH2 0x15B6 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP PUSH1 0x40 MLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP13 DUP2 AND SWAP1 DUP3 AND EQ SWAP10 POP SWAP3 POP PUSH2 0x18D0 SWAP1 POP JUMP JUMPDEST PUSH1 0x3 DUP7 PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x1613 JUMPI INVALID JUMPDEST EQ ISZERO PUSH2 0x17B9 JUMPI DUP9 MLOAD PUSH1 0x41 EQ PUSH2 0x1654 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x54EF JUMP JUMPDEST DUP9 PUSH1 0x0 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x1663 JUMPI INVALID JUMPDEST ADD PUSH1 0x20 ADD MLOAD PUSH32 0x100000000000000000000000000000000000000000000000000000000000000 SWAP1 DUP2 SWAP1 DIV DUP2 MUL DIV SWAP5 POP PUSH2 0x16A3 DUP10 PUSH1 0x1 PUSH4 0xFFFFFFFF PUSH2 0x2FED AND JUMP JUMPDEST SWAP4 POP PUSH2 0x16B6 DUP10 PUSH1 0x21 PUSH4 0xFFFFFFFF PUSH2 0x2FED AND JUMP JUMPDEST SWAP3 POP PUSH1 0x1 DUP12 PUSH1 0x40 MLOAD PUSH1 0x20 ADD DUP1 DUP1 PUSH32 0x19457468657265756D205369676E6564204D6573736167653A0A333200000000 DUP2 MSTORE POP PUSH1 0x1C ADD DUP3 PUSH1 0x0 NOT AND PUSH1 0x0 NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP POP PUSH1 0x40 MLOAD PUSH1 0x20 DUP2 DUP4 SUB SUB DUP2 MSTORE SWAP1 PUSH1 0x40 MSTORE PUSH1 0x40 MLOAD DUP1 DUP3 DUP1 MLOAD SWAP1 PUSH1 0x20 ADD SWAP1 DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH2 0x1757 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0x171A JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 DUP5 SUB PUSH2 0x100 EXP PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ADD DUP1 NOT SWAP1 SWAP3 AND SWAP2 AND OR SWAP1 MSTORE PUSH1 0x40 DUP1 MLOAD SWAP3 SWAP1 SWAP5 ADD DUP3 SWAP1 SUB DUP3 KECCAK256 PUSH1 0x0 DUP4 MSTORE SWAP2 ADD SWAP3 DUP4 SWAP1 MSTORE PUSH2 0x1592 SWAP5 POP SWAP3 POP DUP10 SWAP2 DUP10 SWAP2 POP DUP9 SWAP1 PUSH2 0x5480 JUMP JUMPDEST PUSH1 0x4 DUP7 PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x17C7 JUMPI INVALID JUMPDEST EQ ISZERO PUSH2 0x17DF JUMPI PUSH2 0x17D8 DUP12 DUP12 DUP12 PUSH2 0x3038 JUMP JUMPDEST SWAP8 POP PUSH2 0x18D0 JUMP JUMPDEST PUSH1 0x5 DUP7 PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x17ED JUMPI INVALID JUMPDEST EQ ISZERO PUSH2 0x1850 JUMPI PUSH2 0x17FC DUP10 PUSH2 0x318D JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP13 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x7 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP4 DUP6 AND DUP4 MSTORE SWAP3 SWAP1 MSTORE KECCAK256 SLOAD SWAP1 SWAP2 POP PUSH1 0xFF AND ISZERO ISZERO PUSH2 0x1844 JUMPI PUSH1 0x0 SWAP8 POP PUSH2 0x18D0 JUMP JUMPDEST PUSH2 0x17D8 DUP2 DUP13 DUP13 DUP13 PUSH2 0x3206 JUMP JUMPDEST PUSH1 0x6 DUP7 PUSH1 0x7 DUP2 GT ISZERO PUSH2 0x185E JUMPI INVALID JUMPDEST EQ ISZERO PUSH2 0x189E JUMPI PUSH1 0x0 DUP12 DUP2 MSTORE PUSH1 0x6 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP15 AND DUP5 MSTORE SWAP1 SWAP2 MSTORE SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND SWAP8 POP PUSH2 0x18D0 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x554F JUMP JUMPDEST POP POP POP POP POP POP POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH2 0x18E6 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x60 PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH2 0x18F6 PUSH2 0x4298 JUMP JUMPDEST DUP10 PUSH1 0x0 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x1905 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x140 ADD MLOAD SWAP6 POP DUP10 MLOAD SWAP5 POP PUSH1 0x0 SWAP4 POP JUMPDEST DUP4 DUP6 EQ PUSH2 0x19E4 JUMPI DUP6 DUP11 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x1935 JUMPI INVALID JUMPDEST PUSH1 0x20 SWAP1 DUP2 MUL SWAP1 SWAP2 ADD ADD MLOAD PUSH2 0x140 ADD MSTORE DUP7 MLOAD PUSH2 0x1951 SWAP1 DUP11 SWAP1 PUSH2 0x2EE2 JUMP JUMPDEST SWAP3 POP PUSH2 0x1994 DUP11 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x1964 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH1 0xA0 ADD MLOAD DUP12 DUP7 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x1980 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH1 0x80 ADD MLOAD DUP6 PUSH2 0x3362 JUMP JUMPDEST SWAP2 POP PUSH2 0x19C0 DUP11 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x19A7 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP4 DUP11 DUP8 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xE8E JUMPI INVALID JUMPDEST SWAP1 POP PUSH2 0x19CC DUP8 DUP3 PUSH2 0x2532 JUMP JUMPDEST DUP7 MLOAD DUP10 GT PUSH2 0x19D9 JUMPI PUSH2 0x19E4 JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP4 ADD SWAP3 PUSH2 0x191F JUMP JUMPDEST POP POP POP POP POP POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH2 0x19F9 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x1A36 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE PUSH2 0xF9C DUP5 DUP5 DUP5 PUSH2 0x2494 JUMP JUMPDEST PUSH1 0x9 SLOAD PUSH1 0x0 SWAP1 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND ISZERO PUSH2 0x1ABF JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH2 0x1B02 PUSH2 0x1AFD DUP9 DUP9 DUP9 DUP9 DUP1 DUP1 PUSH1 0x1F ADD PUSH1 0x20 DUP1 SWAP2 DIV MUL PUSH1 0x20 ADD PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 SWAP4 SWAP3 SWAP2 SWAP1 DUP2 DUP2 MSTORE PUSH1 0x20 ADD DUP4 DUP4 DUP1 DUP3 DUP5 CALLDATACOPY POP PUSH2 0x33B8 SWAP5 POP POP POP POP POP JUMP JUMPDEST PUSH2 0x35F9 JUMP JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x8 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD SWAP1 SWAP2 POP PUSH1 0xFF AND ISZERO PUSH2 0x1B4E JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x559F JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP7 AND CALLER EQ PUSH2 0x1C1F JUMPI PUSH2 0x1BA6 DUP2 DUP8 DUP6 DUP6 DUP1 DUP1 PUSH1 0x1F ADD PUSH1 0x20 DUP1 SWAP2 DIV MUL PUSH1 0x20 ADD PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 SWAP4 SWAP3 SWAP2 SWAP1 DUP2 DUP2 MSTORE PUSH1 0x20 ADD DUP4 DUP4 DUP1 DUP3 DUP5 CALLDATACOPY POP PUSH2 0x135B SWAP5 POP POP POP POP POP JUMP JUMPDEST ISZERO ISZERO PUSH2 0x1BDE JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x56DF JUMP JUMPDEST PUSH1 0x9 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP9 AND OR SWAP1 SSTORE JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x8 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 DUP2 SWAP1 KECCAK256 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE MLOAD ADDRESS SWAP1 DUP7 SWAP1 DUP7 SWAP1 DUP1 DUP4 DUP4 DUP1 DUP3 DUP5 CALLDATACOPY DUP3 ADD SWAP2 POP POP SWAP3 POP POP POP PUSH1 0x0 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 DUP6 GAS DELEGATECALL SWAP2 POP POP ISZERO ISZERO PUSH2 0x1CB6 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x55CF JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP7 AND CALLER EQ PUSH2 0x1CFC JUMPI PUSH1 0x9 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 AND SWAP1 SSTORE JUMPDEST POP POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x2 SLOAD PUSH1 0x0 SWAP1 DUP2 SWAP1 DUP2 SWAP1 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER EQ PUSH2 0x1D5D JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x568F JUMP JUMPDEST DUP4 SWAP3 POP DUP3 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH4 0xAE25532E PUSH1 0x40 MLOAD DUP2 PUSH4 0xFFFFFFFF AND PUSH29 0x100000000000000000000000000000000000000000000000000000000 MUL DUP2 MSTORE PUSH1 0x4 ADD PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x1DC4 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x1DD8 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x1DFC SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x491E JUMP JUMPDEST PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 DUP2 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0xA PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD SWAP1 SWAP3 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 POP DUP1 ISZERO PUSH2 0x1E81 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x552F JUMP JUMPDEST PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 DUP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0xA PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 DUP2 SWAP1 KECCAK256 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP7 AND OR SWAP1 SSTORE MLOAD PUSH32 0xD2C6B762299C609BDB96520B58A49BFB80186934D4F71A86A367571A15C03194 SWAP1 PUSH2 0x1F22 SWAP1 DUP5 SWAP1 DUP8 SWAP1 PUSH2 0x54B5 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG1 POP POP POP POP JUMP JUMPDEST PUSH2 0x1F38 PUSH2 0x42F0 JUMP JUMPDEST PUSH2 0x1F41 DUP3 PUSH2 0x3639 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP4 ADD DUP3 SWAP1 MSTORE PUSH1 0x0 SWAP2 DUP3 MSTORE PUSH1 0x3 SWAP1 MSTORE PUSH1 0x40 SWAP1 DUP2 SWAP1 KECCAK256 SLOAD SWAP1 DUP3 ADD MSTORE PUSH1 0x80 DUP3 ADD MLOAD ISZERO ISZERO PUSH2 0x1F75 JUMPI PUSH1 0x1 JUMPDEST PUSH1 0xFF AND DUP2 MSTORE PUSH2 0xF07 JUMP JUMPDEST PUSH1 0xA0 DUP3 ADD MLOAD ISZERO ISZERO PUSH2 0x1F87 JUMPI PUSH1 0x2 PUSH2 0x1F6B JUMP JUMPDEST PUSH1 0xA0 DUP3 ADD MLOAD PUSH1 0x40 DUP3 ADD MLOAD LT PUSH2 0x1F9D JUMPI PUSH1 0x5 PUSH2 0x1F6B JUMP JUMPDEST PUSH2 0x100 DUP3 ADD MLOAD TIMESTAMP LT PUSH2 0x1FB0 JUMPI PUSH1 0x4 PUSH2 0x1F6B JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 ADD MLOAD PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x4 SWAP1 SWAP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x1FD4 JUMPI PUSH1 0x6 PUSH2 0x1F6B JUMP JUMPDEST PUSH2 0x120 DUP3 ADD MLOAD DUP3 MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 DUP2 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x5 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 PUSH1 0x60 DUP9 ADD MLOAD SWAP1 SWAP5 AND DUP4 MSTORE SWAP3 SWAP1 MSTORE KECCAK256 SLOAD GT ISZERO PUSH2 0x2021 JUMPI PUSH1 0x6 PUSH2 0x1F6B JUMP JUMPDEST PUSH1 0x3 DUP2 MSTORE SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x2067 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE PUSH2 0x209B DUP2 PUSH2 0x2E64 JUMP JUMPDEST POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE JUMP JUMPDEST PUSH1 0x5 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x0 SWAP3 DUP4 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 SWAP1 SWAP2 MSTORE SWAP1 DUP3 MSTORE SWAP1 KECCAK256 SLOAD DUP2 JUMP JUMPDEST PUSH1 0x60 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x24 DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0xF47261B0000000000000000000000000E41D2489571D322189246DAFA5EBDE1F DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0x4699F49800000000000000000000000000000000000000000000000000000000 DUP2 MSTORE POP DUP2 JUMP JUMPDEST PUSH2 0x214B PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x60 PUSH1 0x0 DUP1 PUSH1 0x0 PUSH2 0x215A PUSH2 0x4298 JUMP JUMPDEST DUP9 PUSH1 0x0 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x2169 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x160 ADD MLOAD SWAP5 POP DUP9 MLOAD SWAP4 POP PUSH1 0x0 SWAP3 POP JUMPDEST DUP3 DUP5 EQ PUSH2 0x220C JUMPI DUP5 DUP10 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x2199 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x160 ADD DUP2 SWAP1 MSTORE POP PUSH2 0x21B9 DUP9 DUP8 PUSH1 0x20 ADD MLOAD PUSH2 0x2EE2 JUMP JUMPDEST SWAP2 POP PUSH2 0x21E5 DUP10 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x21CC JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP4 DUP10 DUP7 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xE8E JUMPI INVALID JUMPDEST SWAP1 POP PUSH2 0x21F1 DUP7 DUP3 PUSH2 0x2532 JUMP JUMPDEST PUSH1 0x20 DUP7 ADD MLOAD DUP9 GT PUSH2 0x2201 JUMPI PUSH2 0x220C JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP3 ADD SWAP2 PUSH2 0x2183 JUMP JUMPDEST POP POP POP POP POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x1 SLOAD DUP2 JUMP JUMPDEST PUSH2 0x2226 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x60 PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH2 0x2236 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x2273 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x567F JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR DUP2 SSTORE DUP11 MLOAD DUP12 SWAP2 SWAP1 DUP2 LT PUSH2 0x22AB JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x140 ADD MLOAD SWAP6 POP DUP10 MLOAD SWAP5 POP PUSH1 0x0 SWAP4 POP JUMPDEST DUP4 DUP6 EQ PUSH2 0x235A JUMPI DUP6 DUP11 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x22DB JUMPI INVALID JUMPDEST PUSH1 0x20 SWAP1 DUP2 MUL SWAP1 SWAP2 ADD ADD MLOAD PUSH2 0x140 ADD MSTORE DUP7 MLOAD PUSH2 0x22F7 SWAP1 DUP11 SWAP1 PUSH2 0x2EE2 JUMP JUMPDEST SWAP3 POP PUSH2 0x230A DUP11 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x1964 JUMPI INVALID JUMPDEST SWAP2 POP PUSH2 0x2336 DUP11 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x231D JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP4 DUP11 DUP8 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x749 JUMPI INVALID JUMPDEST SWAP1 POP PUSH2 0x2342 DUP8 DUP3 PUSH2 0x2532 JUMP JUMPDEST DUP7 MLOAD DUP10 GT PUSH2 0x234F JUMPI PUSH2 0x235A JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP4 ADD SWAP3 PUSH2 0x22C5 JUMP JUMPDEST POP POP PUSH1 0x0 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND SWAP1 SSTORE POP SWAP3 SWAP7 SWAP6 POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x9 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 JUMP JUMPDEST PUSH1 0x2 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER EQ PUSH2 0x23FD JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x568F JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 AND ISZERO PUSH2 0x245A JUMPI PUSH1 0x2 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000 AND PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND OR SWAP1 SSTORE JUMPDEST POP JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP1 DUP3 ADD SWAP1 SWAP2 MSTORE PUSH1 0x5 DUP2 MSTORE PUSH32 0x322E302E30000000000000000000000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE DUP2 JUMP JUMPDEST PUSH2 0x249C PUSH2 0x4298 JUMP JUMPDEST PUSH2 0x24A4 PUSH2 0x42F0 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 PUSH2 0x24B2 DUP9 PUSH2 0x1F30 JUMP JUMPDEST SWAP4 POP PUSH2 0x24BC PUSH2 0x2594 JUMP JUMPDEST SWAP3 POP PUSH2 0x24CA DUP9 DUP6 DUP6 DUP10 PUSH2 0x25C6 JUMP JUMPDEST PUSH2 0x24DC DUP9 PUSH1 0xA0 ADD MLOAD DUP6 PUSH1 0x40 ADD MLOAD PUSH2 0x2EE2 JUMP JUMPDEST SWAP2 POP PUSH2 0x24E8 DUP8 DUP4 PUSH2 0x3647 JUMP JUMPDEST SWAP1 POP PUSH2 0x24FB DUP9 DUP6 DUP10 DUP5 DUP10 PUSH1 0x0 ADD MLOAD PUSH2 0x2945 JUMP JUMPDEST PUSH2 0x2505 DUP9 DUP3 PUSH2 0x365D JUMP JUMPDEST SWAP5 POP PUSH2 0x251C DUP9 DUP5 DUP7 PUSH1 0x20 ADD MLOAD DUP8 PUSH1 0x40 ADD MLOAD DUP10 PUSH2 0x2A5E JUMP JUMPDEST PUSH2 0x2527 DUP9 DUP5 DUP8 PUSH2 0x36BE JUMP JUMPDEST POP POP POP POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST DUP2 MLOAD DUP2 MLOAD PUSH2 0x2540 SWAP2 SWAP1 PUSH2 0x3776 JUMP JUMPDEST DUP3 MSTORE PUSH1 0x20 DUP1 DUP4 ADD MLOAD SWAP1 DUP3 ADD MLOAD PUSH2 0x2556 SWAP2 SWAP1 PUSH2 0x3776 JUMP JUMPDEST PUSH1 0x20 DUP4 ADD MSTORE PUSH1 0x40 DUP1 DUP4 ADD MLOAD SWAP1 DUP3 ADD MLOAD PUSH2 0x256F SWAP2 SWAP1 PUSH2 0x3776 JUMP JUMPDEST PUSH1 0x40 DUP4 ADD MSTORE PUSH1 0x60 DUP1 DUP4 ADD MLOAD SWAP1 DUP3 ADD MLOAD PUSH2 0x2588 SWAP2 SWAP1 PUSH2 0x3776 JUMP JUMPDEST PUSH1 0x60 SWAP1 SWAP3 ADD SWAP2 SWAP1 SWAP2 MSTORE POP JUMP JUMPDEST PUSH1 0x9 SLOAD PUSH1 0x0 SWAP1 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP2 DUP2 ISZERO PUSH2 0x25BD JUMPI DUP2 PUSH2 0x25BF JUMP JUMPDEST CALLER JUMPDEST SWAP4 SWAP3 POP POP POP JUMP JUMPDEST DUP3 MLOAD PUSH1 0xFF AND PUSH1 0x3 EQ PUSH2 0x2604 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x56AF JUMP JUMPDEST PUSH1 0x60 DUP5 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND ISZERO PUSH2 0x2677 JUMPI PUSH1 0x60 DUP5 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER EQ PUSH2 0x2677 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x56BF JUMP JUMPDEST PUSH1 0x20 DUP5 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND ISZERO PUSH2 0x2702 JUMPI DUP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP5 PUSH1 0x20 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ ISZERO ISZERO PUSH2 0x2702 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x54FF JUMP JUMPDEST PUSH1 0x40 DUP4 ADD MLOAD ISZERO ISZERO PUSH2 0x2758 JUMPI PUSH2 0x2720 DUP4 PUSH1 0x20 ADD MLOAD DUP6 PUSH1 0x0 ADD MLOAD DUP4 PUSH2 0x135B JUMP JUMPDEST ISZERO ISZERO PUSH2 0x2758 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x556F JUMP JUMPDEST POP POP POP POP JUMP JUMPDEST PUSH2 0x2770 DUP3 PUSH1 0xA0 ADD MLOAD DUP3 PUSH1 0xA0 ADD MLOAD PUSH2 0x37C0 JUMP JUMPDEST PUSH2 0x2782 DUP4 PUSH1 0x80 ADD MLOAD DUP4 PUSH1 0x80 ADD MLOAD PUSH2 0x37C0 JUMP JUMPDEST LT ISZERO PUSH2 0x27BA JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x56CF JUMP JUMPDEST POP POP JUMP JUMPDEST PUSH2 0x27C6 PUSH2 0x42C1 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH2 0x27DA DUP10 PUSH1 0xA0 ADD MLOAD DUP9 PUSH2 0x2EE2 JUMP JUMPDEST SWAP4 POP PUSH2 0x27EF DUP10 PUSH1 0x80 ADD MLOAD DUP11 PUSH1 0xA0 ADD MLOAD DUP7 PUSH2 0x381B JUMP JUMPDEST SWAP3 POP PUSH2 0x27FF DUP9 PUSH1 0xA0 ADD MLOAD DUP8 PUSH2 0x2EE2 JUMP JUMPDEST SWAP2 POP PUSH2 0x2814 DUP9 PUSH1 0x80 ADD MLOAD DUP10 PUSH1 0xA0 ADD MLOAD DUP5 PUSH2 0x381B JUMP JUMPDEST SWAP1 POP DUP1 DUP5 LT PUSH2 0x2857 JUMPI PUSH1 0x20 DUP1 DUP7 ADD DUP1 MLOAD DUP4 SWAP1 MSTORE DUP1 MLOAD DUP3 ADD DUP5 SWAP1 MSTORE MLOAD MLOAD DUP7 MLOAD DUP3 ADD MSTORE PUSH1 0x80 DUP11 ADD MLOAD PUSH1 0xA0 DUP12 ADD MLOAD DUP8 MLOAD SWAP1 SWAP3 ADD MLOAD PUSH2 0x284F SWAP3 SWAP1 PUSH2 0x381B JUMP JUMPDEST DUP6 MLOAD MSTORE PUSH2 0x2894 JUMP JUMPDEST DUP5 MLOAD DUP4 SWAP1 MSTORE DUP5 MLOAD PUSH1 0x20 SWAP1 DUP2 ADD DUP6 SWAP1 MSTORE DUP6 MLOAD DUP2 ADD MLOAD SWAP1 DUP7 ADD DUP1 MLOAD SWAP2 SWAP1 SWAP2 MSTORE PUSH1 0xA0 DUP10 ADD MLOAD PUSH1 0x80 DUP11 ADD MLOAD SWAP2 MLOAD MLOAD PUSH2 0x288B SWAP3 SWAP1 PUSH2 0x3898 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP8 ADD MLOAD ADD MSTORE JUMPDEST DUP5 MLOAD MLOAD PUSH1 0x20 DUP1 DUP8 ADD MLOAD ADD MLOAD PUSH2 0x28A9 SWAP2 SWAP1 PUSH2 0x2EE2 JUMP JUMPDEST PUSH1 0x40 DUP7 ADD MSTORE DUP5 MLOAD MLOAD PUSH1 0x80 DUP11 ADD MLOAD PUSH1 0xC0 DUP12 ADD MLOAD PUSH2 0x28C6 SWAP3 SWAP2 SWAP1 PUSH2 0x381B JUMP JUMPDEST DUP6 MLOAD PUSH1 0x40 ADD MSTORE DUP5 MLOAD PUSH1 0x20 ADD MLOAD PUSH1 0xA0 DUP11 ADD MLOAD PUSH1 0xE0 DUP12 ADD MLOAD PUSH2 0x28E7 SWAP3 SWAP2 SWAP1 PUSH2 0x381B JUMP JUMPDEST DUP6 MLOAD PUSH1 0x60 ADD MSTORE PUSH1 0x20 DUP6 ADD MLOAD MLOAD PUSH1 0x80 DUP10 ADD MLOAD PUSH1 0xC0 DUP11 ADD MLOAD PUSH2 0x2908 SWAP3 SWAP2 SWAP1 PUSH2 0x381B JUMP JUMPDEST DUP6 PUSH1 0x20 ADD MLOAD PUSH1 0x40 ADD DUP2 DUP2 MSTORE POP POP PUSH2 0x2930 DUP6 PUSH1 0x20 ADD MLOAD PUSH1 0x20 ADD MLOAD DUP10 PUSH1 0xA0 ADD MLOAD DUP11 PUSH1 0xE0 ADD MLOAD PUSH2 0x381B JUMP JUMPDEST PUSH1 0x20 DUP7 ADD MLOAD PUSH1 0x60 ADD MSTORE POP POP POP POP SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST DUP3 ISZERO ISZERO PUSH2 0x297E JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x55EF JUMP JUMPDEST DUP3 DUP3 GT ISZERO PUSH2 0x29B8 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x55DF JUMP JUMPDEST DUP5 PUSH1 0xA0 ADD MLOAD PUSH2 0x29CB DUP6 PUSH1 0x40 ADD MLOAD DUP5 PUSH2 0x3776 JUMP JUMPDEST GT ISZERO PUSH2 0x2A03 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x550F JUMP JUMPDEST PUSH2 0x2A11 DUP6 PUSH1 0x80 ADD MLOAD DUP4 PUSH2 0x37C0 JUMP JUMPDEST PUSH2 0x2A1F DUP3 DUP8 PUSH1 0xA0 ADD MLOAD PUSH2 0x37C0 JUMP JUMPDEST GT ISZERO PUSH2 0x2A57 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x566F JUMP JUMPDEST POP POP POP POP POP JUMP JUMPDEST PUSH2 0x2A6C DUP3 DUP3 PUSH1 0x20 ADD MLOAD PUSH2 0x3776 JUMP JUMPDEST PUSH1 0x0 DUP5 DUP2 MSTORE PUSH1 0x3 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP2 DUP3 SWAP1 KECCAK256 SWAP3 SWAP1 SWAP3 SSTORE DUP7 DUP2 ADD MLOAD DUP8 MLOAD DUP5 MLOAD SWAP4 DUP6 ADD MLOAD DUP6 DUP5 ADD MLOAD PUSH1 0x60 DUP8 ADD MLOAD PUSH2 0x140 DUP13 ADD MLOAD PUSH2 0x160 DUP14 ADD MLOAD SWAP7 MLOAD DUP12 SWAP9 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP8 DUP9 AND SWAP9 SWAP8 SWAP1 SWAP7 AND SWAP7 PUSH32 0xBCC4C97732E47D9946F229EDB95F5B6323F601300E4690DE719993F3C371129 SWAP7 PUSH2 0x2AFB SWAP7 DUP16 SWAP7 CALLER SWAP7 SWAP3 SWAP6 SWAP2 SWAP5 SWAP1 SWAP4 SWAP1 PUSH2 0x5345 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG4 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x60 DUP1 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x24 DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0xF47261B0000000000000000000000000E41D2489571D322189246DAFA5EBDE1F DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0x4699F49800000000000000000000000000000000000000000000000000000000 DUP2 MSTORE POP SWAP1 POP PUSH2 0x2B8B DUP6 PUSH2 0x140 ADD MLOAD DUP7 PUSH1 0x0 ADD MLOAD DUP7 PUSH1 0x0 ADD MLOAD DUP6 PUSH1 0x20 ADD MLOAD PUSH1 0x20 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x140 DUP5 ADD MLOAD DUP5 MLOAD DUP7 MLOAD DUP5 MLOAD PUSH1 0x20 ADD MLOAD PUSH2 0x2BA7 SWAP4 SWAP3 SWAP2 SWAP1 PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x2BC0 DUP6 PUSH2 0x140 ADD MLOAD DUP7 PUSH1 0x0 ADD MLOAD DUP6 DUP6 PUSH1 0x40 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x2BDC DUP2 DUP7 PUSH1 0x0 ADD MLOAD DUP8 PUSH1 0x40 ADD MLOAD DUP6 PUSH1 0x0 ADD MLOAD PUSH1 0x40 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x2BF8 DUP2 DUP6 PUSH1 0x0 ADD MLOAD DUP7 PUSH1 0x40 ADD MLOAD DUP6 PUSH1 0x20 ADD MLOAD PUSH1 0x40 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST DUP4 PUSH1 0x40 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP6 PUSH1 0x40 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ ISZERO PUSH2 0x2C62 JUMPI PUSH2 0x2C5D DUP2 DUP5 DUP8 PUSH1 0x40 ADD MLOAD PUSH2 0x2C58 DUP7 PUSH1 0x0 ADD MLOAD PUSH1 0x60 ADD MLOAD DUP8 PUSH1 0x20 ADD MLOAD PUSH1 0x60 ADD MLOAD PUSH2 0x3776 JUMP JUMPDEST PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x2A57 JUMP JUMPDEST PUSH2 0x2C7A DUP2 DUP5 DUP8 PUSH1 0x40 ADD MLOAD DUP6 PUSH1 0x0 ADD MLOAD PUSH1 0x60 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x2A57 DUP2 DUP5 DUP7 PUSH1 0x40 ADD MLOAD DUP6 PUSH1 0x20 ADD MLOAD PUSH1 0x60 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0xB4BE83D500000000000000000000000000000000000000000000000000000000 PUSH1 0x20 DUP1 DUP4 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH1 0x60 PUSH1 0x24 DUP4 ADD DUP2 DUP2 MSTORE DUP8 MLOAD PUSH1 0x84 DUP6 ADD SWAP1 DUP2 MSTORE DUP9 DUP5 ADD MLOAD PUSH1 0xA4 DUP7 ADD MSTORE SWAP5 DUP9 ADD MLOAD PUSH1 0xC4 DUP6 ADD MSTORE SWAP1 DUP8 ADD MLOAD PUSH1 0xE4 DUP5 ADD MSTORE PUSH1 0x80 DUP8 ADD MLOAD PUSH2 0x104 DUP5 ADD MSTORE PUSH1 0xA0 DUP8 ADD MLOAD PUSH2 0x124 DUP5 ADD MSTORE PUSH1 0xC0 DUP8 ADD MLOAD PUSH2 0x144 DUP5 ADD MSTORE PUSH1 0xE0 DUP8 ADD MLOAD PUSH2 0x164 DUP5 ADD MSTORE PUSH2 0x100 DUP8 ADD MLOAD PUSH2 0x184 DUP5 ADD MSTORE PUSH2 0x120 DUP8 ADD MLOAD PUSH2 0x1A4 DUP5 ADD MSTORE PUSH2 0x140 DUP8 ADD DUP1 MLOAD PUSH2 0x1C4 DUP6 ADD SWAP1 DUP2 MSTORE PUSH2 0x160 DUP10 ADD MLOAD PUSH2 0x1E4 DUP7 ADD MSTORE PUSH2 0x180 SWAP1 MSTORE MLOAD DUP1 MLOAD PUSH2 0x204 DUP6 ADD DUP2 SWAP1 MSTORE SWAP4 SWAP5 SWAP2 SWAP4 DUP5 SWAP4 PUSH1 0x44 DUP8 ADD SWAP3 DUP5 SWAP3 PUSH2 0x224 DUP10 ADD SWAP3 SWAP2 DUP3 ADD SWAP2 PUSH1 0x1F DUP3 ADD DIV PUSH1 0x0 JUMPDEST DUP2 DUP2 LT ISZERO PUSH2 0x2D99 JUMPI DUP4 MLOAD DUP6 MSTORE PUSH1 0x20 SWAP5 DUP6 ADD SWAP5 SWAP1 SWAP4 ADD SWAP3 PUSH1 0x1 ADD PUSH2 0x2D7B JUMP JUMPDEST POP POP POP POP DUP2 DUP2 SUB PUSH2 0x160 DUP1 DUP5 ADD SWAP2 SWAP1 SWAP2 MSTORE DUP11 ADD MLOAD DUP1 MLOAD DUP1 DUP4 MSTORE PUSH1 0x20 SWAP3 DUP4 ADD SWAP3 SWAP2 DUP3 ADD SWAP2 PUSH1 0x1F DUP3 ADD DIV PUSH1 0x0 JUMPDEST DUP2 DUP2 LT ISZERO PUSH2 0x2DE2 JUMPI DUP4 MLOAD DUP6 MSTORE PUSH1 0x20 SWAP5 DUP6 ADD SWAP5 SWAP1 SWAP4 ADD SWAP3 PUSH1 0x1 ADD PUSH2 0x2DC4 JUMP JUMPDEST POP POP POP DUP10 DUP5 MSTORE POP DUP5 DUP2 SUB PUSH1 0x20 SWAP4 DUP5 ADD SWAP1 DUP2 MSTORE DUP9 MLOAD DUP1 DUP4 MSTORE SWAP1 SWAP4 SWAP2 DUP3 ADD SWAP2 DUP10 DUP2 ADD SWAP2 SWAP1 PUSH1 0x1F DUP3 ADD DIV PUSH1 0x0 JUMPDEST DUP2 DUP2 LT ISZERO PUSH2 0x2E2A JUMPI DUP4 MLOAD DUP6 MSTORE PUSH1 0x20 SWAP5 DUP6 ADD SWAP5 SWAP1 SWAP4 ADD SWAP3 PUSH1 0x1 ADD PUSH2 0x2E0C JUMP JUMPDEST POP POP POP PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 DUP9 DUP4 SUB ADD DUP9 MSTORE POP PUSH1 0x40 MSTORE POP POP POP POP POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH2 0x2E6C PUSH2 0x42F0 JUMP JUMPDEST PUSH2 0x2E75 DUP3 PUSH2 0x1F30 JUMP JUMPDEST SWAP1 POP PUSH2 0x2E81 DUP3 DUP3 PUSH2 0x3AFF JUMP JUMPDEST PUSH2 0x27BA DUP3 DUP3 PUSH1 0x20 ADD MLOAD PUSH2 0x3C16 JUMP JUMPDEST PUSH2 0x2E97 PUSH2 0x4298 JUMP JUMPDEST PUSH2 0x2EA2 DUP5 DUP5 DUP5 PUSH2 0x2494 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD MLOAD SWAP1 SWAP2 POP DUP4 EQ PUSH2 0x25BF JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x565F JUMP JUMPDEST PUSH1 0x0 DUP3 DUP3 GT ISZERO PUSH2 0x2F1E JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x551F JUMP JUMPDEST POP DUP1 DUP3 SUB JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP3 MLOAD GT ISZERO ISZERO PUSH2 0x2F67 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x560F JUMP JUMPDEST DUP2 MLOAD DUP3 SWAP1 PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 ADD SWAP1 DUP2 LT PUSH2 0x2F97 JUMPI INVALID JUMPDEST ADD PUSH1 0x20 ADD MLOAD DUP3 MLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ADD SWAP1 SWAP3 MSTORE POP PUSH32 0x100000000000000000000000000000000000000000000000000000000000000 SWAP1 DUP2 SWAP1 DIV MUL SWAP1 JUMP JUMPDEST PUSH1 0x0 DUP2 PUSH1 0x20 ADD DUP4 MLOAD LT ISZERO ISZERO ISZERO PUSH2 0x302F JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x553F JUMP JUMPDEST POP ADD PUSH1 0x20 ADD MLOAD SWAP1 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH1 0x0 SWAP1 PUSH1 0x60 SWAP1 PUSH32 0x1626BA7E00000000000000000000000000000000000000000000000000000000 SWAP1 PUSH2 0x3073 SWAP1 DUP8 SWAP1 DUP7 SWAP1 PUSH1 0x24 ADD PUSH2 0x5460 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 DUP2 DUP5 SUB ADD DUP2 MSTORE SWAP2 SWAP1 MSTORE PUSH1 0x20 DUP1 DUP3 ADD DUP1 MLOAD PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 SWAP1 SWAP5 AND SWAP4 SWAP1 SWAP4 OR DUP4 MSTORE DUP2 MLOAD SWAP2 SWAP4 POP SWAP1 DUP3 SWAP1 DUP2 DUP9 GAS STATICCALL DUP1 DUP1 ISZERO PUSH2 0x3110 JUMPI PUSH1 0x1 DUP2 EQ PUSH2 0x3181 JUMPI PUSH2 0x2527 JUMP JUMPDEST PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 PUSH1 0x0 MSTORE PUSH29 0x2000000000000000000000000000000000000000000000000000000000 PUSH1 0x20 MSTORE PUSH29 0xC57414C4C45545F4552524F5200000000000000000000000000000000 PUSH1 0x40 MSTORE PUSH1 0x0 PUSH1 0x60 MSTORE PUSH1 0x64 PUSH1 0x0 REVERT JUMPDEST POP POP MLOAD SWAP6 SWAP5 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x14 DUP3 MLOAD LT ISZERO ISZERO ISZERO PUSH2 0x31CD JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x569F JUMP JUMPDEST PUSH2 0x31DB DUP3 PUSH1 0x14 DUP5 MLOAD SUB PUSH2 0x3CBD JUMP JUMPDEST DUP3 MLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC ADD SWAP1 SWAP3 MSTORE POP SWAP1 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH1 0x0 SWAP1 PUSH1 0x60 SWAP1 PUSH32 0x9363470200000000000000000000000000000000000000000000000000000000 SWAP1 PUSH2 0x3243 SWAP1 DUP8 SWAP1 DUP8 SWAP1 DUP8 SWAP1 PUSH1 0x24 ADD PUSH2 0x5433 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 DUP2 DUP5 SUB ADD DUP2 MSTORE SWAP2 SWAP1 MSTORE PUSH1 0x20 DUP1 DUP3 ADD DUP1 MLOAD PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 SWAP1 SWAP5 AND SWAP4 SWAP1 SWAP4 OR DUP4 MSTORE DUP2 MLOAD SWAP2 SWAP4 POP SWAP1 DUP3 SWAP1 DUP2 DUP11 GAS STATICCALL DUP1 DUP1 ISZERO PUSH2 0x32E0 JUMPI PUSH1 0x1 DUP2 EQ PUSH2 0x3351 JUMPI PUSH2 0x3356 JUMP JUMPDEST PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 PUSH1 0x0 MSTORE PUSH29 0x2000000000000000000000000000000000000000000000000000000000 PUSH1 0x20 MSTORE PUSH29 0xF56414C494441544F525F4552524F5200000000000000000000000000 PUSH1 0x40 MSTORE PUSH1 0x0 PUSH1 0x60 MSTORE PUSH1 0x64 PUSH1 0x0 REVERT JUMPDEST DUP3 MLOAD SWAP5 POP JUMPDEST POP POP POP POP SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP4 GT PUSH2 0x339D JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x555F JUMP JUMPDEST PUSH2 0x33B0 PUSH2 0x33AA DUP6 DUP5 PUSH2 0x37C0 JUMP JUMPDEST DUP5 PUSH2 0x3D1E JUMP JUMPDEST SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0x5A65726F45785472616E73616374696F6E280000000000000000000000000000 PUSH1 0x20 DUP1 DUP4 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH32 0x75696E743235362073616C742C00000000000000000000000000000000000000 PUSH1 0x32 DUP4 ADD MSTORE PUSH32 0x61646472657373207369676E6572416464726573732C00000000000000000000 PUSH1 0x3F DUP4 ADD MSTORE PUSH32 0x6279746573206461746100000000000000000000000000000000000000000000 PUSH1 0x55 DUP4 ADD MSTORE PUSH32 0x2900000000000000000000000000000000000000000000000000000000000000 PUSH1 0x5F DUP4 ADD MSTORE DUP3 MLOAD DUP1 DUP4 SUB DUP5 ADD DUP2 MSTORE PUSH1 0x60 SWAP1 SWAP3 ADD SWAP3 DUP4 SWAP1 MSTORE DUP2 MLOAD PUSH1 0x0 SWAP4 DUP5 SWAP4 DUP5 SWAP4 SWAP1 SWAP3 DUP3 SWAP2 DUP5 ADD SWAP1 DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH2 0x34E1 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0x34A4 JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 DUP5 SUB PUSH2 0x100 EXP PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ADD DUP1 NOT SWAP1 SWAP3 AND SWAP2 AND OR SWAP1 MSTORE PUSH1 0x40 MLOAD SWAP2 SWAP1 SWAP4 ADD DUP2 SWAP1 SUB DUP2 KECCAK256 DUP10 MLOAD SWAP1 SWAP8 POP DUP10 SWAP6 POP SWAP1 SWAP4 POP DUP4 SWAP3 DUP6 ADD SWAP2 POP DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH2 0x3577 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0x353A JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 DUP5 SUB PUSH2 0x100 EXP PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ADD DUP1 NOT SWAP1 SWAP3 AND SWAP2 AND OR SWAP1 MSTORE PUSH1 0x40 DUP1 MLOAD SWAP3 SWAP1 SWAP5 ADD DUP3 SWAP1 SUB DUP3 KECCAK256 SWAP8 DUP3 MSTORE DUP2 ADD SWAP11 SWAP1 SWAP11 MSTORE POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP8 SWAP1 SWAP8 AND SWAP7 DUP9 ADD SWAP7 SWAP1 SWAP7 MSTORE POP POP PUSH1 0x60 DUP6 ADD MSTORE POP POP PUSH1 0x80 SWAP1 SWAP2 KECCAK256 SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x1 SLOAD PUSH1 0x40 MLOAD PUSH32 0x1901000000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x2 DUP2 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH1 0x22 DUP2 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH1 0x42 SWAP1 KECCAK256 SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x2F23 PUSH2 0x1AFD DUP4 PUSH2 0x3D35 JUMP JUMPDEST PUSH1 0x0 DUP2 DUP4 LT PUSH2 0x3656 JUMPI DUP2 PUSH2 0x25BF JUMP JUMPDEST POP SWAP1 SWAP2 SWAP1 POP JUMP JUMPDEST PUSH2 0x3665 PUSH2 0x4298 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD DUP3 SWAP1 MSTORE PUSH1 0xA0 DUP4 ADD MLOAD PUSH1 0x80 DUP5 ADD MLOAD PUSH2 0x3681 SWAP2 DUP5 SWAP2 PUSH2 0x381B JUMP JUMPDEST DUP1 DUP3 MSTORE PUSH1 0x80 DUP5 ADD MLOAD PUSH1 0xC0 DUP6 ADD MLOAD PUSH2 0x3699 SWAP3 SWAP2 SWAP1 PUSH2 0x381B JUMP JUMPDEST PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0xA0 DUP4 ADD MLOAD PUSH1 0xE0 DUP5 ADD MLOAD PUSH2 0x36B3 SWAP2 DUP5 SWAP2 PUSH2 0x381B JUMP JUMPDEST PUSH1 0x60 DUP3 ADD MSTORE SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x60 DUP2 ADD DUP3 MSTORE PUSH1 0x24 DUP2 MSTORE PUSH32 0xF47261B0000000000000000000000000E41D2489571D322189246DAFA5EBDE1F PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x4699F49800000000000000000000000000000000000000000000000000000000 SWAP2 DUP2 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH2 0x140 DUP5 ADD MLOAD DUP5 MLOAD DUP4 MLOAD PUSH2 0x3731 SWAP3 SWAP2 SWAP1 DUP7 SWAP1 PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x374A DUP5 PUSH2 0x160 ADD MLOAD DUP5 DUP7 PUSH1 0x0 ADD MLOAD DUP6 PUSH1 0x20 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x3762 DUP2 DUP6 PUSH1 0x0 ADD MLOAD DUP7 PUSH1 0x40 ADD MLOAD DUP6 PUSH1 0x40 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH2 0x2758 DUP2 DUP5 DUP7 PUSH1 0x40 ADD MLOAD DUP6 PUSH1 0x60 ADD MLOAD PUSH2 0x3935 JUMP JUMPDEST PUSH1 0x0 DUP3 DUP3 ADD DUP4 DUP2 LT ISZERO PUSH2 0x37B5 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x558F JUMP JUMPDEST DUP1 SWAP2 POP JUMPDEST POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP4 ISZERO ISZERO PUSH2 0x37D3 JUMPI PUSH1 0x0 SWAP2 POP PUSH2 0x37B9 JUMP JUMPDEST POP DUP3 DUP3 MUL DUP3 DUP5 DUP3 DUP2 ISZERO ISZERO PUSH2 0x37E3 JUMPI INVALID JUMPDEST DIV EQ PUSH2 0x37B5 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x558F JUMP JUMPDEST PUSH1 0x0 DUP1 DUP4 GT PUSH2 0x3856 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x555F JUMP JUMPDEST PUSH2 0x3861 DUP5 DUP5 DUP5 PUSH2 0x418E JUMP JUMPDEST ISZERO PUSH2 0x339D JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x55BF JUMP JUMPDEST PUSH1 0x0 DUP1 DUP4 GT PUSH2 0x38D3 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x555F JUMP JUMPDEST PUSH2 0x38DE DUP5 DUP5 DUP5 PUSH2 0x4213 JUMP JUMPDEST ISZERO PUSH2 0x3915 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x55BF JUMP JUMPDEST PUSH2 0x33B0 PUSH2 0x33AA PUSH2 0x3925 DUP7 DUP6 PUSH2 0x37C0 JUMP JUMPDEST PUSH2 0x3930 DUP7 PUSH1 0x1 PUSH2 0x2EE2 JUMP JUMPDEST PUSH2 0x3776 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP4 GT DUP1 ISZERO PUSH2 0x3974 JUMPI POP DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP6 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ ISZERO JUMPDEST ISZERO PUSH2 0x3AF7 JUMPI DUP6 MLOAD PUSH1 0x3 LT PUSH2 0x39B4 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x564F JUMP JUMPDEST POP POP PUSH1 0x20 DUP5 DUP2 ADD MLOAD PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0xA SWAP1 SWAP3 MSTORE PUSH1 0x40 SWAP1 SWAP2 KECCAK256 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP1 ISZERO ISZERO PUSH2 0x3A3D JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x55FF JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH7 0xFFFFFFFFFFFE0 PUSH1 0x3F DUP9 MLOAD ADD AND DUP1 PUSH1 0x84 ADD DUP3 ADD PUSH32 0xA85E59E400000000000000000000000000000000000000000000000000000000 DUP4 MSTORE PUSH1 0x80 PUSH1 0x4 DUP5 ADD MSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP9 AND PUSH1 0x24 DUP5 ADD MSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP8 AND PUSH1 0x44 DUP5 ADD MSTORE DUP6 PUSH1 0x64 DUP5 ADD MSTORE PUSH1 0x84 DUP4 ADD JUMPDEST DUP2 DUP2 LT ISZERO PUSH2 0x3AD9 JUMPI DUP10 MLOAD DUP2 MSTORE PUSH1 0x20 SWAP10 DUP11 ADD SWAP10 ADD PUSH2 0x3AC1 JUMP JUMPDEST PUSH2 0x200 DUP5 DUP6 DUP5 SUB DUP7 PUSH1 0x0 DUP10 GAS CALL DUP1 ISZERO ISZERO PUSH2 0x3AF1 JUMPI RETURNDATASIZE DUP6 REVERT JUMPDEST POP POP POP POP POP JUMPDEST POP POP POP POP POP POP JUMP JUMPDEST DUP1 MLOAD PUSH1 0x0 SWAP1 PUSH1 0xFF AND PUSH1 0x3 EQ PUSH2 0x3B40 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x56AF JUMP JUMPDEST PUSH1 0x60 DUP4 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND ISZERO PUSH2 0x3BB3 JUMPI PUSH1 0x60 DUP4 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND CALLER EQ PUSH2 0x3BB3 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x56BF JUMP JUMPDEST PUSH2 0x3BBB PUSH2 0x2594 JUMP JUMPDEST DUP4 MLOAD SWAP1 SWAP2 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP4 AND SWAP2 AND EQ PUSH2 0x3C11 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x557F JUMP JUMPDEST POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x4 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 DUP2 SWAP1 KECCAK256 DUP1 SLOAD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND PUSH1 0x1 OR SWAP1 SSTORE DUP3 DUP2 ADD MLOAD DUP4 MLOAD PUSH2 0x140 DUP6 ADD MLOAD PUSH2 0x160 DUP7 ADD MLOAD SWAP4 MLOAD DUP6 SWAP5 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP5 DUP6 AND SWAP5 SWAP4 SWAP1 SWAP4 AND SWAP3 PUSH32 0xDC47B3613D9FE400085F6DBDC99453462279057E6207385042827ED6B1A62CF7 SWAP3 PUSH2 0x3CB1 SWAP3 CALLER SWAP3 SWAP1 PUSH2 0x53C9 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG4 POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 PUSH1 0x14 ADD DUP4 MLOAD LT ISZERO ISZERO ISZERO PUSH2 0x3CFF JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x569F JUMP JUMPDEST POP ADD PUSH1 0x14 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 JUMP JUMPDEST PUSH1 0x0 DUP1 DUP3 DUP5 DUP2 ISZERO ISZERO PUSH2 0x3D2C JUMPI INVALID JUMPDEST DIV SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0x4F72646572280000000000000000000000000000000000000000000000000000 PUSH1 0x20 DUP1 DUP4 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH32 0x61646472657373206D616B6572416464726573732C0000000000000000000000 PUSH1 0x26 DUP4 ADD MSTORE PUSH32 0x616464726573732074616B6572416464726573732C0000000000000000000000 PUSH1 0x3B DUP4 ADD MSTORE PUSH32 0x6164647265737320666565526563697069656E74416464726573732C00000000 PUSH1 0x50 DUP4 ADD MSTORE PUSH32 0x616464726573732073656E646572416464726573732C00000000000000000000 PUSH1 0x6C DUP4 ADD MSTORE PUSH32 0x75696E74323536206D616B65724173736574416D6F756E742C00000000000000 PUSH1 0x82 DUP4 ADD MSTORE PUSH32 0x75696E743235362074616B65724173736574416D6F756E742C00000000000000 PUSH1 0x9B DUP4 ADD MSTORE PUSH32 0x75696E74323536206D616B65724665652C000000000000000000000000000000 PUSH1 0xB4 DUP4 ADD MSTORE PUSH32 0x75696E743235362074616B65724665652C000000000000000000000000000000 PUSH1 0xC5 DUP4 ADD MSTORE PUSH32 0x75696E743235362065787069726174696F6E54696D655365636F6E64732C0000 PUSH1 0xD6 DUP4 ADD MSTORE PUSH32 0x75696E743235362073616C742C00000000000000000000000000000000000000 PUSH1 0xF4 DUP4 ADD MSTORE PUSH32 0x6279746573206D616B65724173736574446174612C0000000000000000000000 PUSH2 0x101 DUP4 ADD MSTORE PUSH32 0x62797465732074616B6572417373657444617461000000000000000000000000 PUSH2 0x116 DUP4 ADD MSTORE PUSH32 0x2900000000000000000000000000000000000000000000000000000000000000 PUSH2 0x12A DUP4 ADD MSTORE DUP3 MLOAD PUSH2 0x10B DUP2 DUP5 SUB ADD DUP2 MSTORE PUSH2 0x12B SWAP1 SWAP3 ADD SWAP3 DUP4 SWAP1 MSTORE DUP2 MLOAD PUSH1 0x0 SWAP4 DUP5 SWAP4 DUP5 SWAP4 DUP5 SWAP4 SWAP2 SWAP3 SWAP2 DUP3 SWAP2 DUP5 ADD SWAP1 DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH2 0x3FBD JUMPI DUP1 MLOAD DUP3 MSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0x3F80 JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 DUP5 SUB PUSH2 0x100 EXP PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ADD DUP1 NOT SWAP1 SWAP3 AND SWAP2 AND OR SWAP1 MSTORE PUSH1 0x40 MLOAD SWAP2 SWAP1 SWAP4 ADD DUP2 SWAP1 SUB DUP2 KECCAK256 PUSH2 0x140 DUP12 ADD MLOAD DUP1 MLOAD SWAP2 SWAP10 POP SWAP6 POP SWAP1 SWAP4 POP DUP4 SWAP3 DUP6 ADD SWAP2 POP DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH2 0x4058 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0x401B JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 DUP5 SUB PUSH2 0x100 EXP PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ADD DUP1 NOT SWAP1 SWAP3 AND SWAP2 AND OR SWAP1 MSTORE PUSH1 0x40 MLOAD SWAP2 SWAP1 SWAP4 ADD DUP2 SWAP1 SUB DUP2 KECCAK256 PUSH2 0x160 DUP12 ADD MLOAD DUP1 MLOAD SWAP2 SWAP9 POP SWAP6 POP SWAP1 SWAP4 POP DUP4 SWAP3 DUP6 ADD SWAP2 POP DUP1 DUP4 DUP4 JUMPDEST PUSH1 0x20 DUP4 LT PUSH2 0x40F3 JUMPI DUP1 MLOAD DUP3 MSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0x40B6 JUMP JUMPDEST MLOAD DUP2 MLOAD PUSH1 0x20 SWAP4 SWAP1 SWAP4 SUB PUSH2 0x100 EXP PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ADD DUP1 NOT SWAP1 SWAP2 AND SWAP3 AND SWAP2 SWAP1 SWAP2 OR SWAP1 MSTORE PUSH1 0x40 MLOAD SWAP3 ADD DUP3 SWAP1 SUB SWAP1 SWAP2 KECCAK256 PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 DUP10 ADD DUP1 MLOAD PUSH2 0x140 DUP12 ADD DUP1 MLOAD PUSH2 0x160 SWAP1 SWAP13 ADD DUP1 MLOAD SWAP11 DUP5 MSTORE SWAP9 DUP2 MSTORE SWAP3 DUP9 MSTORE PUSH2 0x1A0 DUP3 KECCAK256 SWAP2 MSTORE SWAP9 SWAP1 MSTORE POP POP POP SWAP2 SWAP1 MSTORE POP SWAP1 SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP1 DUP5 GT PUSH2 0x41CA JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x555F JUMP JUMPDEST DUP3 ISZERO DUP1 PUSH2 0x41D5 JUMPI POP DUP5 ISZERO JUMPDEST ISZERO PUSH2 0x41E3 JUMPI PUSH1 0x0 SWAP2 POP PUSH2 0xA88 JUMP JUMPDEST DUP4 DUP1 ISZERO ISZERO PUSH2 0x41ED JUMPI INVALID JUMPDEST DUP6 DUP5 MULMOD SWAP1 POP PUSH2 0x41FC DUP6 DUP5 PUSH2 0x37C0 JUMP JUMPDEST PUSH2 0x4208 PUSH2 0x3E8 DUP4 PUSH2 0x37C0 JUMP JUMPDEST LT ISZERO SWAP6 SWAP5 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP1 DUP5 GT PUSH2 0x424F JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C6 SWAP1 PUSH2 0x555F JUMP JUMPDEST DUP3 ISZERO DUP1 PUSH2 0x425A JUMPI POP DUP5 ISZERO JUMPDEST ISZERO PUSH2 0x4268 JUMPI PUSH1 0x0 SWAP2 POP PUSH2 0xA88 JUMP JUMPDEST DUP4 DUP1 ISZERO ISZERO PUSH2 0x4272 JUMPI INVALID JUMPDEST DUP6 DUP5 MULMOD SWAP1 POP DUP4 PUSH2 0x4282 DUP6 DUP4 PUSH2 0x2EE2 JUMP JUMPDEST DUP2 ISZERO ISZERO PUSH2 0x428B JUMPI INVALID JUMPDEST MOD SWAP1 POP PUSH2 0x41FC DUP6 DUP5 PUSH2 0x37C0 JUMP JUMPDEST PUSH1 0x80 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE POP SWAP1 JUMP JUMPDEST PUSH2 0x120 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH2 0x42D6 PUSH2 0x4298 JUMP JUMPDEST DUP2 MSTORE PUSH1 0x20 ADD PUSH2 0x42E3 PUSH2 0x4298 JUMP JUMPDEST DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE POP SWAP1 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x60 DUP2 ADD DUP3 MSTORE PUSH1 0x0 DUP1 DUP3 MSTORE PUSH1 0x20 DUP3 ADD DUP2 SWAP1 MSTORE SWAP2 DUP2 ADD SWAP2 SWAP1 SWAP2 MSTORE SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x25BF DUP3 CALLDATALOAD PUSH2 0x57C2 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0x432D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0x4340 PUSH2 0x433B DUP3 PUSH2 0x5751 JUMP JUMPDEST PUSH2 0x572A JUMP JUMPDEST DUP2 DUP2 MSTORE PUSH1 0x20 SWAP4 DUP5 ADD SWAP4 SWAP1 SWAP3 POP DUP3 ADD DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x437E JUMPI DUP2 CALLDATALOAD DUP7 ADD PUSH2 0x4368 DUP9 DUP3 PUSH2 0x44CE JUMP JUMPDEST DUP5 MSTORE POP PUSH1 0x20 SWAP3 DUP4 ADD SWAP3 SWAP2 SWAP1 SWAP2 ADD SWAP1 PUSH1 0x1 ADD PUSH2 0x4352 JUMP JUMPDEST POP POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0x4399 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0x43A7 PUSH2 0x433B DUP3 PUSH2 0x5751 JUMP JUMPDEST DUP2 DUP2 MSTORE PUSH1 0x20 SWAP4 DUP5 ADD SWAP4 SWAP1 SWAP3 POP DUP3 ADD DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x437E JUMPI DUP2 CALLDATALOAD DUP7 ADD PUSH2 0x43CF DUP9 DUP3 PUSH2 0x451D JUMP JUMPDEST DUP5 MSTORE POP PUSH1 0x20 SWAP3 DUP4 ADD SWAP3 SWAP2 SWAP1 SWAP2 ADD SWAP1 PUSH1 0x1 ADD PUSH2 0x43B9 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0x43F6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0x4404 PUSH2 0x433B DUP3 PUSH2 0x5751 JUMP JUMPDEST SWAP2 POP DUP2 DUP2 DUP4 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP PUSH1 0x20 DUP2 ADD SWAP1 POP DUP4 DUP6 PUSH1 0x20 DUP5 MUL DUP3 ADD GT ISZERO PUSH2 0x4429 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x437E JUMPI DUP2 PUSH2 0x443F DUP9 DUP3 PUSH2 0x4461 JUMP JUMPDEST DUP5 MSTORE POP PUSH1 0x20 SWAP3 DUP4 ADD SWAP3 SWAP2 SWAP1 SWAP2 ADD SWAP1 PUSH1 0x1 ADD PUSH2 0x442C JUMP JUMPDEST PUSH1 0x0 PUSH2 0x25BF DUP3 CALLDATALOAD PUSH2 0x57DB JUMP JUMPDEST PUSH1 0x0 PUSH2 0x25BF DUP3 CALLDATALOAD PUSH2 0x57E0 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x25BF DUP3 CALLDATALOAD PUSH2 0x57E3 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x25BF DUP3 MLOAD PUSH2 0x57E3 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x1F DUP4 ADD DUP5 SGT PUSH2 0x4497 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP DUP2 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x44AF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x20 DUP4 ADD SWAP2 POP DUP4 PUSH1 0x1 DUP3 MUL DUP4 ADD GT ISZERO PUSH2 0x44C7 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST SWAP3 POP SWAP3 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0x44DF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0x44ED PUSH2 0x433B DUP3 PUSH2 0x5772 JUMP JUMPDEST SWAP2 POP DUP1 DUP3 MSTORE PUSH1 0x20 DUP4 ADD PUSH1 0x20 DUP4 ADD DUP6 DUP4 DUP4 ADD GT ISZERO PUSH2 0x4509 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4514 DUP4 DUP3 DUP5 PUSH2 0x5819 JUMP JUMPDEST POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x180 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x4530 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x453B PUSH2 0x180 PUSH2 0x572A JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0x4549 DUP5 DUP5 PUSH2 0x4310 JUMP JUMPDEST DUP3 MSTORE POP PUSH1 0x20 PUSH2 0x455A DUP5 DUP5 DUP4 ADD PUSH2 0x4310 JUMP JUMPDEST PUSH1 0x20 DUP4 ADD MSTORE POP PUSH1 0x40 PUSH2 0x456E DUP5 DUP3 DUP6 ADD PUSH2 0x4310 JUMP JUMPDEST PUSH1 0x40 DUP4 ADD MSTORE POP PUSH1 0x60 PUSH2 0x4582 DUP5 DUP3 DUP6 ADD PUSH2 0x4310 JUMP JUMPDEST PUSH1 0x60 DUP4 ADD MSTORE POP PUSH1 0x80 PUSH2 0x4596 DUP5 DUP3 DUP6 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH1 0x80 DUP4 ADD MSTORE POP PUSH1 0xA0 PUSH2 0x45AA DUP5 DUP3 DUP6 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH1 0xA0 DUP4 ADD MSTORE POP PUSH1 0xC0 PUSH2 0x45BE DUP5 DUP3 DUP6 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH1 0xC0 DUP4 ADD MSTORE POP PUSH1 0xE0 PUSH2 0x45D2 DUP5 DUP3 DUP6 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH1 0xE0 DUP4 ADD MSTORE POP PUSH2 0x100 PUSH2 0x45E7 DUP5 DUP3 DUP6 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH2 0x100 DUP4 ADD MSTORE POP PUSH2 0x120 PUSH2 0x45FD DUP5 DUP3 DUP6 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH2 0x120 DUP4 ADD MSTORE POP PUSH2 0x140 DUP3 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x461F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x462B DUP5 DUP3 DUP6 ADD PUSH2 0x44CE JUMP JUMPDEST PUSH2 0x140 DUP4 ADD MSTORE POP PUSH2 0x160 DUP3 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x464D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4659 DUP5 DUP3 DUP6 ADD PUSH2 0x44CE JUMP JUMPDEST PUSH2 0x160 DUP4 ADD MSTORE POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x4678 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x33B0 DUP5 DUP5 PUSH2 0x4310 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x4697 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x46A3 DUP6 DUP6 PUSH2 0x4310 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 PUSH2 0x46B4 DUP6 DUP3 DUP7 ADD PUSH2 0x4310 JUMP JUMPDEST SWAP2 POP POP SWAP3 POP SWAP3 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x46D1 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x46DD DUP6 DUP6 PUSH2 0x4310 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 PUSH2 0x46B4 DUP6 DUP3 DUP7 ADD PUSH2 0x4455 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x4700 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x4717 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x33B0 DUP5 DUP3 DUP6 ADD PUSH2 0x4388 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 PUSH1 0x60 DUP5 DUP7 SUB SLT ISZERO PUSH2 0x4738 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP4 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x474F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x475B DUP7 DUP3 DUP8 ADD PUSH2 0x4388 JUMP JUMPDEST SWAP4 POP POP PUSH1 0x20 DUP5 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x4778 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4784 DUP7 DUP3 DUP8 ADD PUSH2 0x43E5 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x40 DUP5 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x47A1 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x47AD DUP7 DUP3 DUP8 ADD PUSH2 0x431C JUMP JUMPDEST SWAP2 POP POP SWAP3 POP SWAP3 POP SWAP3 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 PUSH1 0x60 DUP5 DUP7 SUB SLT ISZERO PUSH2 0x47CC JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP4 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x47E3 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x47EF DUP7 DUP3 DUP8 ADD PUSH2 0x4388 JUMP JUMPDEST SWAP4 POP POP PUSH1 0x20 PUSH2 0x4784 DUP7 DUP3 DUP8 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x4812 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x33B0 DUP5 DUP5 PUSH2 0x4461 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x4831 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x46A3 DUP6 DUP6 PUSH2 0x4461 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x60 DUP6 DUP8 SUB SLT ISZERO PUSH2 0x4853 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x485F DUP8 DUP8 PUSH2 0x4461 JUMP JUMPDEST SWAP5 POP POP PUSH1 0x20 PUSH2 0x4870 DUP8 DUP3 DUP9 ADD PUSH2 0x4310 JUMP JUMPDEST SWAP4 POP POP PUSH1 0x40 DUP6 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x488D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4899 DUP8 DUP3 DUP9 ADD PUSH2 0x4485 JUMP JUMPDEST SWAP6 SWAP9 SWAP5 SWAP8 POP SWAP6 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 PUSH1 0x60 DUP5 DUP7 SUB SLT ISZERO PUSH2 0x48BA JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x48C6 DUP7 DUP7 PUSH2 0x4461 JUMP JUMPDEST SWAP4 POP POP PUSH1 0x20 PUSH2 0x48D7 DUP7 DUP3 DUP8 ADD PUSH2 0x4310 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x40 DUP5 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x48F4 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x47AD DUP7 DUP3 DUP8 ADD PUSH2 0x44CE JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x4912 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x33B0 DUP5 DUP5 PUSH2 0x446D JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x4930 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x33B0 DUP5 DUP5 PUSH2 0x4479 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x494E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x4965 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x33B0 DUP5 DUP3 DUP6 ADD PUSH2 0x451D JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x80 DUP6 DUP8 SUB SLT ISZERO PUSH2 0x4987 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP5 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x499E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x49AA DUP8 DUP3 DUP9 ADD PUSH2 0x451D JUMP JUMPDEST SWAP5 POP POP PUSH1 0x20 DUP6 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x49C7 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x49D3 DUP8 DUP3 DUP9 ADD PUSH2 0x451D JUMP JUMPDEST SWAP4 POP POP PUSH1 0x40 DUP6 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x49F0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x49FC DUP8 DUP3 DUP9 ADD PUSH2 0x44CE JUMP JUMPDEST SWAP3 POP POP PUSH1 0x60 DUP6 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x4A19 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4A25 DUP8 DUP3 DUP9 ADD PUSH2 0x44CE JUMP JUMPDEST SWAP2 POP POP SWAP3 SWAP6 SWAP2 SWAP5 POP SWAP3 POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 PUSH1 0x60 DUP5 DUP7 SUB SLT ISZERO PUSH2 0x4A46 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP4 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x4A5D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4A69 DUP7 DUP3 DUP8 ADD PUSH2 0x451D JUMP JUMPDEST SWAP4 POP POP PUSH1 0x20 PUSH2 0x48D7 DUP7 DUP3 DUP8 ADD PUSH2 0x4461 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x80 DUP8 DUP10 SUB SLT ISZERO PUSH2 0x4A93 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x4A9F DUP10 DUP10 PUSH2 0x4461 JUMP JUMPDEST SWAP7 POP POP PUSH1 0x20 PUSH2 0x4AB0 DUP10 DUP3 DUP11 ADD PUSH2 0x4310 JUMP JUMPDEST SWAP6 POP POP PUSH1 0x40 DUP8 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x4ACD JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4AD9 DUP10 DUP3 DUP11 ADD PUSH2 0x4485 JUMP JUMPDEST SWAP5 POP SWAP5 POP POP PUSH1 0x60 DUP8 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x4AF8 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4B04 DUP10 DUP3 DUP11 ADD PUSH2 0x4485 JUMP JUMPDEST SWAP3 POP SWAP3 POP POP SWAP3 SWAP6 POP SWAP3 SWAP6 POP SWAP3 SWAP6 JUMP JUMPDEST PUSH2 0x4B1C DUP2 PUSH2 0x57C2 JUMP JUMPDEST DUP3 MSTORE POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x4B2D DUP3 PUSH2 0x57BE JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP PUSH2 0x4B3F DUP4 PUSH2 0x57B8 JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP3 DUP2 LT ISZERO PUSH2 0x4B6F JUMPI PUSH2 0x4B55 DUP7 DUP4 MLOAD PUSH2 0x52F7 JUMP JUMPDEST PUSH2 0x4B5E DUP3 PUSH2 0x57B8 JUMP JUMPDEST PUSH1 0x60 SWAP7 SWAP1 SWAP7 ADD SWAP6 SWAP2 POP PUSH1 0x1 ADD PUSH2 0x4B42 JUMP JUMPDEST POP SWAP4 SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH2 0x4B1C DUP2 PUSH2 0x57DB JUMP JUMPDEST PUSH2 0x4B1C DUP2 PUSH2 0x57E0 JUMP JUMPDEST PUSH2 0x4B1C DUP2 PUSH2 0x57E3 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x4B9F DUP3 PUSH2 0x57BE JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH2 0x4BB3 DUP2 PUSH1 0x20 DUP7 ADD PUSH1 0x20 DUP7 ADD PUSH2 0x5825 JUMP JUMPDEST PUSH2 0x4BBC DUP2 PUSH2 0x5851 JUMP JUMPDEST SWAP1 SWAP4 ADD PUSH1 0x20 ADD SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH2 0x4B1C DUP2 PUSH2 0x580E JUMP JUMPDEST PUSH1 0x12 DUP2 MSTORE PUSH32 0x4C454E4754485F36355F52455155495245440000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xD DUP2 MSTORE PUSH32 0x494E56414C49445F54414B455200000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xE DUP2 MSTORE PUSH32 0x4F524445525F4F56455246494C4C000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x11 DUP2 MSTORE PUSH32 0x55494E543235365F554E444552464C4F57000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x1A DUP2 MSTORE PUSH32 0x41535345545F50524F58595F414C52454144595F455849535453000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x26 DUP2 MSTORE PUSH32 0x475245415445525F4F525F455155414C5F544F5F33325F4C454E4754485F5245 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x5155495245440000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x15 DUP2 MSTORE PUSH32 0x5349474E41545552455F554E535550504F525445440000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x10 DUP2 MSTORE PUSH32 0x4449564953494F4E5F42595F5A45524F00000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x17 DUP2 MSTORE PUSH32 0x494E56414C49445F4F524445525F5349474E4154555245000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xD DUP2 MSTORE PUSH32 0x494E56414C49445F4D414B455200000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x10 DUP2 MSTORE PUSH32 0x55494E543235365F4F564552464C4F5700000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xF DUP2 MSTORE PUSH32 0x494E56414C49445F54585F484153480000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x11 DUP2 MSTORE PUSH32 0x494E56414C49445F5349474E4154555245000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xE DUP2 MSTORE PUSH32 0x524F554E44494E475F4552524F52000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x10 DUP2 MSTORE PUSH32 0x4641494C45445F455845435554494F4E00000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xD DUP2 MSTORE PUSH32 0x54414B45525F4F56455250415900000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x14 DUP2 MSTORE PUSH32 0x494E56414C49445F54414B45525F414D4F554E54000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x1A DUP2 MSTORE PUSH32 0x41535345545F50524F58595F444F45535F4E4F545F4558495354000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x21 DUP2 MSTORE PUSH32 0x475245415445525F5448414E5F5A45524F5F4C454E4754485F52455155495245 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x4400000000000000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x11 DUP2 MSTORE PUSH32 0x5349474E41545552455F494C4C4547414C000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x1E DUP2 MSTORE PUSH32 0x4C454E4754485F475245415445525F5448414E5F305F52455155495245440000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x17 DUP2 MSTORE PUSH32 0x494E56414C49445F4E45575F4F524445525F45504F4348000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x1E DUP2 MSTORE PUSH32 0x4C454E4754485F475245415445525F5448414E5F335F52455155495245440000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x14 DUP2 MSTORE PUSH32 0x434F4D504C4554455F46494C4C5F4641494C4544000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x12 DUP2 MSTORE PUSH32 0x494E56414C49445F46494C4C5F50524943450000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x12 DUP2 MSTORE PUSH32 0x5245454E5452414E43595F494C4C4547414C0000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x13 DUP2 MSTORE PUSH32 0x4F4E4C595F434F4E54524143545F4F574E455200000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x26 DUP2 MSTORE PUSH32 0x475245415445525F4F525F455155414C5F544F5F32305F4C454E4754485F5245 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x5155495245440000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x10 DUP2 MSTORE PUSH32 0x4F524445525F554E46494C4C41424C4500000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xE DUP2 MSTORE PUSH32 0x494E56414C49445F53454E444552000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x18 DUP2 MSTORE PUSH32 0x4E454741544956455F5350524541445F52455155495245440000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x14 DUP2 MSTORE PUSH32 0x494E56414C49445F54585F5349474E4154555245000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x11 DUP2 MSTORE PUSH32 0x4C454E4754485F305F5245515549524544000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST DUP1 MLOAD PUSH1 0x80 DUP4 ADD SWAP1 PUSH2 0x5285 DUP5 DUP3 PUSH2 0x4B82 JUMP JUMPDEST POP PUSH1 0x20 DUP3 ADD MLOAD PUSH2 0x5298 PUSH1 0x20 DUP6 ADD DUP3 PUSH2 0x4B82 JUMP JUMPDEST POP PUSH1 0x40 DUP3 ADD MLOAD PUSH2 0x52AB PUSH1 0x40 DUP6 ADD DUP3 PUSH2 0x4B82 JUMP JUMPDEST POP PUSH1 0x60 DUP3 ADD MLOAD PUSH2 0x2758 PUSH1 0x60 DUP6 ADD DUP3 PUSH2 0x4B82 JUMP JUMPDEST DUP1 MLOAD PUSH2 0x120 DUP4 ADD SWAP1 PUSH2 0x52D0 DUP5 DUP3 PUSH2 0x5274 JUMP JUMPDEST POP PUSH1 0x20 DUP3 ADD MLOAD PUSH2 0x52E3 PUSH1 0x80 DUP6 ADD DUP3 PUSH2 0x5274 JUMP JUMPDEST POP PUSH1 0x40 DUP3 ADD MLOAD PUSH2 0x2758 PUSH2 0x100 DUP6 ADD DUP3 PUSH2 0x4B82 JUMP JUMPDEST DUP1 MLOAD PUSH1 0x60 DUP4 ADD SWAP1 PUSH2 0x5308 DUP5 DUP3 PUSH2 0x532E JUMP JUMPDEST POP PUSH1 0x20 DUP3 ADD MLOAD PUSH2 0x531B PUSH1 0x20 DUP6 ADD DUP3 PUSH2 0x4B82 JUMP JUMPDEST POP PUSH1 0x40 DUP3 ADD MLOAD PUSH2 0x2758 PUSH1 0x40 DUP6 ADD DUP3 PUSH2 0x4B82 JUMP JUMPDEST PUSH2 0x4B1C DUP2 PUSH2 0x5808 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x2F23 DUP3 DUP5 PUSH2 0x4B13 JUMP JUMPDEST PUSH2 0x100 DUP2 ADD PUSH2 0x5354 DUP3 DUP12 PUSH2 0x4B13 JUMP JUMPDEST PUSH2 0x5361 PUSH1 0x20 DUP4 ADD DUP11 PUSH2 0x4B13 JUMP JUMPDEST PUSH2 0x536E PUSH1 0x40 DUP4 ADD DUP10 PUSH2 0x4B82 JUMP JUMPDEST PUSH2 0x537B PUSH1 0x60 DUP4 ADD DUP9 PUSH2 0x4B82 JUMP JUMPDEST PUSH2 0x5388 PUSH1 0x80 DUP4 ADD DUP8 PUSH2 0x4B82 JUMP JUMPDEST PUSH2 0x5395 PUSH1 0xA0 DUP4 ADD DUP7 PUSH2 0x4B82 JUMP JUMPDEST DUP2 DUP2 SUB PUSH1 0xC0 DUP4 ADD MSTORE PUSH2 0x53A7 DUP2 DUP6 PUSH2 0x4B94 JUMP JUMPDEST SWAP1 POP DUP2 DUP2 SUB PUSH1 0xE0 DUP4 ADD MSTORE PUSH2 0x53BB DUP2 DUP5 PUSH2 0x4B94 JUMP JUMPDEST SWAP11 SWAP10 POP POP POP POP POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x60 DUP2 ADD PUSH2 0x53D7 DUP3 DUP7 PUSH2 0x4B13 JUMP JUMPDEST DUP2 DUP2 SUB PUSH1 0x20 DUP4 ADD MSTORE PUSH2 0x53E9 DUP2 DUP6 PUSH2 0x4B94 JUMP JUMPDEST SWAP1 POP DUP2 DUP2 SUB PUSH1 0x40 DUP4 ADD MSTORE PUSH2 0x53FD DUP2 DUP5 PUSH2 0x4B94 JUMP JUMPDEST SWAP6 SWAP5 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x25BF DUP2 DUP5 PUSH2 0x4B22 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x2F23 DUP3 DUP5 PUSH2 0x4B79 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x2F23 DUP3 DUP5 PUSH2 0x4B82 JUMP JUMPDEST PUSH1 0x60 DUP2 ADD PUSH2 0x5441 DUP3 DUP7 PUSH2 0x4B82 JUMP JUMPDEST PUSH2 0x544E PUSH1 0x20 DUP4 ADD DUP6 PUSH2 0x4B13 JUMP JUMPDEST DUP2 DUP2 SUB PUSH1 0x40 DUP4 ADD MSTORE PUSH2 0x53FD DUP2 DUP5 PUSH2 0x4B94 JUMP JUMPDEST PUSH1 0x40 DUP2 ADD PUSH2 0x546E DUP3 DUP6 PUSH2 0x4B82 JUMP JUMPDEST DUP2 DUP2 SUB PUSH1 0x20 DUP4 ADD MSTORE PUSH2 0x33B0 DUP2 DUP5 PUSH2 0x4B94 JUMP JUMPDEST PUSH1 0x80 DUP2 ADD PUSH2 0x548E DUP3 DUP8 PUSH2 0x4B82 JUMP JUMPDEST PUSH2 0x549B PUSH1 0x20 DUP4 ADD DUP7 PUSH2 0x532E JUMP JUMPDEST PUSH2 0x54A8 PUSH1 0x40 DUP4 ADD DUP6 PUSH2 0x4B82 JUMP JUMPDEST PUSH2 0x53FD PUSH1 0x60 DUP4 ADD DUP5 PUSH2 0x4B82 JUMP JUMPDEST PUSH1 0x40 DUP2 ADD PUSH2 0x54C3 DUP3 DUP6 PUSH2 0x4B8B JUMP JUMPDEST PUSH2 0x25BF PUSH1 0x20 DUP4 ADD DUP5 PUSH2 0x4B13 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x25BF DUP2 DUP5 PUSH2 0x4B94 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x2F23 DUP3 DUP5 PUSH2 0x4BC9 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4BD2 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4C02 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4C32 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4C62 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4C92 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4CC2 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4D18 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4D48 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4D78 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4DA8 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4DD8 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4E08 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4E38 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4E68 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4E98 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4EC8 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4EF8 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4F28 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4F58 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4FAE JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x4FDE JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x500E JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x503E JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x506E JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x509E JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x50CE JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x50FE JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x512E JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x5184 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x51B4 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x51E4 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x5214 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x2F23 DUP2 PUSH2 0x5244 JUMP JUMPDEST PUSH1 0x80 DUP2 ADD PUSH2 0x2F23 DUP3 DUP5 PUSH2 0x5274 JUMP JUMPDEST PUSH2 0x120 DUP2 ADD PUSH2 0x2F23 DUP3 DUP5 PUSH2 0x52BE JUMP JUMPDEST PUSH1 0x60 DUP2 ADD PUSH2 0x2F23 DUP3 DUP5 PUSH2 0x52F7 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP2 DUP2 ADD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT DUP3 DUP3 LT OR ISZERO PUSH2 0x5749 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x40 MSTORE SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 PUSH8 0xFFFFFFFFFFFFFFFF DUP3 GT ISZERO PUSH2 0x5768 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x20 SWAP1 DUP2 MUL ADD SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH8 0xFFFFFFFFFFFFFFFF DUP3 GT ISZERO PUSH2 0x5789 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x20 PUSH1 0x1F SWAP2 SWAP1 SWAP2 ADD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 AND ADD SWAP1 JUMP JUMPDEST PUSH1 0x20 ADD SWAP1 JUMP JUMPDEST MLOAD SWAP1 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 JUMP JUMPDEST ISZERO ISZERO SWAP1 JUMP JUMPDEST SWAP1 JUMP JUMPDEST PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 AND SWAP1 JUMP JUMPDEST PUSH1 0xFF AND SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x2F23 DUP3 PUSH2 0x57C2 JUMP JUMPDEST DUP3 DUP2 DUP4 CALLDATACOPY POP PUSH1 0x0 SWAP2 ADD MSTORE JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x5840 JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0x5828 JUMP JUMPDEST DUP4 DUP2 GT ISZERO PUSH2 0x2758 JUMPI POP POP PUSH1 0x0 SWAP2 ADD MSTORE JUMP JUMPDEST PUSH1 0x1F ADD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 AND SWAP1 JUMP STOP LOG2 PUSH6 0x627A7A723058 KECCAK256 0xb1 SWAP13 SWAP3 KECCAK256 LT ORIGIN PUSH1 0x33 SWAP9 0xc0 SMOD 0xdc 0xcf DUP1 SWAP4 0xc PUSH3 0x6EF89F 0xa6 0xc8 0x48 0xc8 0xb7 0xef MSTORE PUSH8 0xD07CD4ED6C657870 PUSH6 0x72696D656E74 PUSH2 0x6CF5 STOP CALLDATACOPY ", - "sourceMap": "897:514:21:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1277:42:23;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1277:42:23;;;;;;;;;;;;;;;;;;;;;;;;;4133:647:27;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;4133:647:27;;;;;;;;;;;;;;;;;1367:42:23;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1367:42:23;;;;;;;;;;;;;;;;;1572:445:25;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1572:445:25;;;;;;;;;;;1870:2891:24;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1870:2891:24;;;;;;;;;;;;;;;;;2139:1515:27;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;2139:1515:27;;;;;;;;;897:51:22;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;897:51:22;;;;;;;;;;;;;;;;;15747:260:27;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;15747:260:27;;;;;;;;;5260:659;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;5260:659:27;;;;;;;;;2018:1079:23;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;2018:1079:23;;;;;;;;;6478:632:27;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;6478:632:27;;;;;;;;;2013:154:22;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;2013:154:22;;;;;;;;;;;;;;;;;967:45:26;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;967:45:26;;;;;;;;;1357:395:27;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1357:395:27;;;;;;;;;2254:412:25;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;2254:412:25;;;;;;;;;1166:71;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1166:71:25;;;;;;;;;7498:1507:27;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;7498:1507:27;;;;;;;;;16201:419;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;16201:419:27;;;;;;;;;;;;;;;;;1046:63:25;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1046:63:25;;;;;;;;;91:20:55;;8:9:-1;5:2;;;30:1;27;20:12;5:2;91:20:55;;;;3038:4848:25;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;3038:4848:25;;;;;;;;;13713:1899:27;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;13713:1899:27;;;;;;;;;3414:374:23;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;3414:374:23;;;;;;;;;1426:1456:26;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1426:1456:26;;;;;;;;;1157:666:22;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1157:666:22;;;;;;;;;4417:2261:23;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;4417:2261:23;;;;;;;;;;;;;;;;;4019:124;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;4019:124:23;;;;;;;;;1627:67;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1627:67:23;;;;;;;;;1528:185:38;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1528:185:38;;;;;;;;;;;;9462:1488:27;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;9462:1488:27;;;;;;;;;1301:33:39;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1301:33:39;;;;11338:1914:27;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;11338:1914:27;;;;;;;;;1064:36:26;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1064:36:26;;;;333:167:55;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;333:167:55;;;;;;;;;1078:40:21;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1078:40:21;;;;1277:42:23;;;;;;;;;;;;;:::o;4133:647:27:-;4340:35;;:::i;:::-;4391:20;4442:9;4495:36;;:::i;:::-;939:6:56;;;;938:7;917:72;;;;;;;;;;;;;;;;;;;;;;1043:6;:13;;;;1052:4;1043:13;;;4414::27;;;-1:-1:-1;1043:6:56;-1:-1:-1;4437:304:27;4457:17;;;4437:304;;4534:131;4569:6;4576:1;4569:9;;;;;;;;;;;;;;;;;;4596:21;4618:1;4596:24;;;;;;;;;;;;;;;;;;4638:10;4649:1;4638:13;;;;;;;;;;;;;;;;;;4534:17;:131::i;:::-;4495:170;;4679:51;4694:16;4712:17;4679:14;:51::i;:::-;4476:3;;;;;4437:304;;;-1:-1:-1;;1165:5:56;1156:14;;;;;;-1:-1:-1;4133:647:27;;-1:-1:-1;;;4133:647:27:o;1367:42:23:-;;;;;;;;;;;;;;;:::o;1572:445:25:-;1708:27;;;1725:10;1708:27;1704:260;;1776:126;1814:4;1840:13;1875:9;;1776:126;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1776:16:25;;-1:-1:-1;;;;;1776:126:25:i;:::-;1751:202;;;;;;;;;;;;;;;;-1:-1:-1;;1973:15:25;;;;:9;:15;;;;;;;;:30;;;;;;;;;;;:37;;;;2006:4;1973:37;;;1572:445::o;1870:2891:24:-;2106:59;;:::i;:::-;2571:39;;:::i;:::-;2646:40;;:::i;:::-;2755:20;939:6:56;;;;938:7;917:72;;;;;;;;;;;;;;1043:6;:13;;;;1052:4;1043:13;;;2435:24:24;;;;;2407:25;;;;:52;;;;2497:24;;;2469:25;;;:52;2613:23;2435:9;2613:12;:23::i;:::-;2571:65;;2689:24;2702:10;2689:12;:24::i;:::-;2646:67;;2778:26;:24;:26::i;:::-;2755:49;;2875:132;2908:9;2931:13;2958:12;2984:13;2875:19;:132::i;:::-;3017:135;3050:10;3074:14;3102:12;3128:14;3017:19;:135::i;:::-;3162:39;3179:9;3190:10;3162:16;:39::i;:::-;3278:195;3319:9;3342:10;3366:13;:41;;;3421:14;:42;;;3278:27;:195::i;:::-;3597:23;;:46;;;;3717;;3257:216;;-1:-1:-1;3518:255:24;;3547:9;;3570:13;;3597:46;;3518:15;:255::i;:::-;3864:24;;;;;:47;;;;3986;;3783:260;;3812:10;;3836:14;;3864:47;;;3783:15;:260::i;:::-;4095:205;4126:9;4149:12;4175:13;:23;;;4212:13;:41;;;4267:18;:23;;;4095:17;:205::i;:::-;4310:209;4341:10;4365:12;4391:14;:24;;;4429:14;:42;;;4485:18;:24;;;4310:17;:209::i;:::-;4584:134;4617:9;4640:10;4664:12;4690:18;4584:19;:134::i;:::-;-1:-1:-1;;1165:5:56;1156:14;;;;;;-1:-1:-1;1870:2891:24;;-1:-1:-1;;;;1870:2891:24:o;2139:1515:27:-;2310:30;;:::i;:::-;2403;2436:104;2468:5;2487:20;2521:9;2436:18;:104::i;:::-;2403:137;;3116:3;3037:17;2972;2966:24;2870:2;2851:17;2847:26;2762:7;2691:3;2661:532;3209:7;3206:2;;;3261:17;3255:24;3242:11;3235:45;3355:2;3336:17;3332:26;3326:33;3321:2;3308:11;3304:20;3297:63;3435:2;3416:17;3412:26;3406:33;3401:2;3388:11;3384:20;3377:63;3515:2;3496:17;3492:26;3486:33;3481:2;3468:11;3464:20;3457:63;3206:2;-1:-1:-1;2139:1515:27;;;;;;;:::o;897:51:22:-;;;;;;;;;;;;;;;:::o;15747:260:27:-;15856:20;939:6:56;;15856:20:27;;939:6:56;;938:7;917:72;;;;;;;;;;;;;;-1:-1:-1;;1043:6:56;:13;;;;1052:4;1043:13;;;15879::27;;;15902:99;15922:17;;;15902:99;;15960:30;15980:6;15987:1;15980:9;;;;;;;;;;;;;;;;;;15960:19;:30::i;:::-;15941:3;;15902:99;;;-1:-1:-1;;1165:5:56;1156:14;;;;;;-1:-1:-1;15747:260:27:o;5260:659::-;5473:35;;:::i;:::-;5524:20;5575:9;5628:36;;:::i;:::-;939:6:56;;;;938:7;917:72;;;;;;;;;;;;;;1043:6;:13;;;;1052:4;1043:13;;;5547::27;;;-1:-1:-1;1043:6:56;-1:-1:-1;5570:310:27;5590:17;;;5570:310;;5667:137;5708:6;5715:1;5708:9;;;;;;;;;;;;;;;;;;5735:21;5757:1;5735:24;;;;;;;;;;;;;;;;;;5777:10;5788:1;5777:13;;;;;;;;;;;;;;;;;;5667:23;:137::i;:::-;5628:176;;5818:51;5833:16;5851:17;5818:14;:51::i;:::-;5609:3;;;;;5570:310;;2018:1079:23;2122:20;939:6:56;;2122:20:23;;;;;;939:6:56;;938:7;917:72;;;;;;;;;;;;;;1043:6;:13;;;;1052:4;1043:13;;;2145:26:23;:24;:26::i;:::-;2122:49;-1:-1:-1;2453:26:23;;;2469:10;2453:26;:52;;2495:10;2453:52;;;2490:1;2453:52;2673:24;;;;;;;;:10;:24;;;;;;;;:39;;;;;;;;;;2429:76;;-1:-1:-1;2636:1:23;2617:20;;;-1:-1:-1;2673:39:23;-1:-1:-1;2801:29:23;;;2780:100;;;;;;;;;;;;;;2920:24;;;;;;;;:10;:24;;;;;;;;:39;;;;;;;;;;;;;;:55;;;2990:100;;;;;2962:13;;2990:100;;;;;;;;;;-1:-1:-1;;1165:5:56;1156:14;;;;;;-1:-1:-1;;;2018:1079:23:o;6478:632:27:-;6671:35;;:::i;:::-;6722:20;6773:9;6826:36;;:::i;:::-;6745:6;:13;6722:36;;6785:1;6773:13;;6768:303;6788:17;;;6768:303;;6865:130;6899:6;6906:1;6899:9;;;;;;;;;;;;;;;;;;6926:21;6948:1;6926:24;;;;;;;;;;;;;;;;;;6968:10;6979:1;6968:13;;;;;;;;;;;;;;;;;;6865:16;:130::i;:::-;6826:169;;7009:51;7024:16;7042:17;7009:14;:51::i;:::-;6807:3;;;;;6768:303;;;6478:632;;;;;;;;:::o;2013:154:22:-;2134:26;;;2104:7;2134:26;;;:12;:26;;;;;;;;2013:154;;;;:::o;967:45:26:-;;;;;;;;;;;;;;;:::o;1357:395:27:-;1548:30;;:::i;:::-;939:6:56;;;;938:7;917:72;;;;;;;;;;;;;;1043:6;:13;;;;1052:4;1043:13;;;1608:109:27;1645:5;1664:20;1698:9;1608:23;:109::i;:::-;1165:5:56;1156:14;;;;;;1594:123:27;1357:395;-1:-1:-1;;;;1357:395:27:o;2254:412:25:-;2408:21;939:6:56;;;;938:7;917:72;;;;;;;;;;;;;;1043:6;:13;;;;1052:4;1043:13;;;2432:26:25;:24;:26::i;:::-;2468:32;;;;;;;;:17;:32;;;;;;;;:50;;;;;;;;;;;;;;:61;;;;;;;;;;2544:115;2468:32;;-1:-1:-1;2468:50:25;;2544:115;;;;2468:61;;2544:115;;;;;;;;;;-1:-1:-1;;1165:5:56;1156:14;;;;;;-1:-1:-1;2254:412:25:o;1166:71::-;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;7498:1507:27:-;7696:35;;:::i;:::-;7747:27;7816:20;7867:9;8253:37;8444:36;;:::i;:::-;939:6:56;;;;938:7;917:72;;;;;;;;;;;;;;1043:6;:13;;;;1052:4;1043:13;;;7777:9:27;;:6;;1043::56;7777:9:27;;;;;;;;;;;;;;:24;;;7747:54;;7839:6;:13;7816:36;;7879:1;7867:13;;7862:1104;7882:17;;;7862:1104;;8156:14;8129:6;8136:1;8129:9;;;;;;;;;;;;;;;;;;:24;;:41;;;;8293:70;8301:20;8323:16;:39;;;8293:7;:70::i;:::-;8253:110;;8483:136;8518:6;8525:1;8518:9;;;;;;;;;;;;;;;;;;8545:29;8592:10;8603:1;8592:13;;;;;;;;;8483:136;8444:175;;8704:51;8719:16;8737:17;8704:14;:51::i;:::-;8853:39;;;;:63;-1:-1:-1;8849:107:27;;8936:5;;8849:107;7901:3;;;;;7862:1104;;;-1:-1:-1;;1165:5:56;1156:14;;;;;;-1:-1:-1;7498:1507:27;;;-1:-1:-1;;;;;7498:1507:27:o;16201:419::-;16301:20;16344;16390:38;16484:9;16367:6;:13;16344:36;;16456:12;16431:38;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;16390:79;;16496:1;16484:13;;16479:108;16499:17;;;16479:108;;16553:23;16566:6;16573:1;16566:9;;;;;;;;;;;;;;;;;;16553:12;:23::i;:::-;16537:10;16548:1;16537:13;;;;;;;;;;;;;;;;;;:39;16518:3;;16479:108;;1046:63:25;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;91:20:55:-;;;;;;:::o;3038:4848:25:-;3200:12;3390:22;3631:27;3752:7;3769:9;3788;3807:17;6871:24;3268:1;3249:9;:16;:20;3228:97;;;;;;;;;;;;;;;;3421:23;:9;:21;:23::i;:::-;3415:30;;;;-1:-1:-1;3543:29:25;3518:55;;;;3497:123;;;;;;;;;;;;;;3675:16;3661:31;;;;;;;;;;3631:61;-1:-1:-1;4174:21:25;4157:13;:38;;;;;;;;;4153:3383;;;4211:27;;;;;;;;;;;4153:3383;4520:21;4503:13;:38;;;;;;;;;4499:3037;;;4582:16;;:21;4557:97;;;;;;;;;;;;;;4678:5;;-1:-1:-1;4697:14:25;;4499:3037;4784:20;4767:13;:37;;;;;;;;;4763:2773;;;4845:16;;4865:2;4845:22;4820:99;;;;;;;;;;;;;;4943:9;4953:1;4943:12;;;;;;;;;;;;;;;;;;;;;4937:19;;-1:-1:-1;4974:24:25;:9;4996:1;4974:24;:21;:24;:::i;:::-;4970:28;-1:-1:-1;5016:25:25;:9;5038:2;5016:25;:21;:25;:::i;:::-;5012:29;;5067:102;5094:4;5116:1;5135;5154;5067:102;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;5067:102:25;;;;;5193:26;;;;;;;;;-1:-1:-1;5067:102:25;-1:-1:-1;5233:14:25;;-1:-1:-1;5233:14:25;4763:2773;5324:21;5307:13;:38;;;;;;;;;5303:2233;;;5386:16;;5406:2;5386:22;5361:99;;;;;;;;;;;;;;5484:9;5494:1;5484:12;;;;;;;;;;;;;;;;;;;;;5478:19;;-1:-1:-1;5515:24:25;:9;5537:1;5515:24;:21;:24;:::i;:::-;5511:28;-1:-1:-1;5557:25:25;:9;5579:2;5557:25;:21;:25;:::i;:::-;5553:29;;5608:225;5739:4;5645:116;;;;;;;;;;;;;;;;;;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;5645:116:25;;;5635:127;;;;;;;;;;;;;36:153:-1;66:2;58:11;;36:153;;176:10;;164:23;;139:12;;;;;98:2;89:12;;;;114;36:153;;;299:10;344;;263:2;259:12;;;254:3;250:22;246:30;;311:9;;295:26;;;340:21;;377:20;365:33;;5635:127:25;;;;;;;;;;;;-1:-1:-1;5608:225:25;;;;;;;;;;-1:-1:-1;5635:127:25;-1:-1:-1;5780:1:25;;5799;;-1:-1:-1;5818:1:25;;5608:225;;5303:2233;6081:20;6064:13;:37;;;;;;;;;6060:1476;;;6127:116;6167:4;6189:13;6220:9;6127:22;:116::i;:::-;6117:126;-1:-1:-1;6257:14:25;;6060:1476;6770:23;6753:13;:40;;;;;;;;;6749:787;;;6898:26;:9;:24;:26::i;:::-;7009:32;;;;;;;;:17;:32;;;;;;;;:50;;;;;;;;;;6871:53;;-1:-1:-1;7009:50:25;;7008:51;7004:102;;;7086:5;7079:12;;;;7004:102;7129:153;7172:16;7206:4;7228:13;7259:9;7129:25;:153::i;6749:787::-;7418:23;7401:13;:40;;;;;;;;;7397:139;;;7467:15;;;;:9;:15;;;;;;;;:30;;;;;;;;;;;;;;-1:-1:-1;7511:14:25;;7397:139;7848:31;;;;;;;;;;;3038:4848;;;;;;;;;;;;;:::o;13713:1899:27:-;13896:35;;:::i;:::-;13947:27;14012:20;14063:9;14453:37;14759;15050:36;;:::i;:::-;13977:6;13984:1;13977:9;;;;;;;;;;;;;;;;;;:24;;;13947:54;;14035:6;:13;14012:36;;14075:1;14063:13;;14058:1515;14078:17;;;14058:1515;;14357:14;14330:6;14337:1;14330:9;;;;;;;;;;;;;;;;;;;:24;;:41;14523:39;;14493:70;;14501:20;;14493:7;:70::i;:::-;14453:110;;14799:170;14838:6;14845:1;14838:9;;;;;;;;;;;;;;;;;;:26;;;14882:6;14889:1;14882:9;;;;;;;;;;;;;;;;;;:26;;;14926:29;14799:21;:170::i;:::-;14759:210;;15089:135;15123:6;15130:1;15123:9;;;;;;;;;;;;;;;;;;15150:29;15197:10;15208:1;15197:13;;;;;;;;;15089:135;15050:174;;15309:51;15324:16;15342:17;15309:14;:51::i;:::-;15460:39;;:63;-1:-1:-1;15456:107:27;;15543:5;;15456:107;14097:3;;;;;14058:1515;;;13713:1899;;;;;;;;;;;:::o;3414:374:23:-;3590:30;;:::i;:::-;939:6:56;;;;938:7;917:72;;;;;;;;;;;;;;1043:6;:13;;;;1052:4;1043:13;;;3650:103:23;3681:5;3700:20;3734:9;3650:17;:103::i;1426:1456:26:-;1640:21;;1730:23;;1640:35;:21;:35;1619:100;;;;;;;;;;;;;;1756:113;1774:94;1809:4;1827:13;1854:4;;1774:94;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1774:21:26;;-1:-1:-1;;;;;1774:94:26:i;:::-;1756:17;:113::i;:::-;1956:29;;;;:12;:29;;;;;;1730:139;;-1:-1:-1;1956:29:26;;1955:30;1934:92;;;;;;;;;;;;;;2112:27;;;2129:10;2112:27;2108:410;;2214:137;2252:15;2289:13;2324:9;;2214:137;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;2214:16:26;;-1:-1:-1;;;;;2214:137:26:i;:::-;2189:216;;;;;;;;;;;;;;;;2470:21;:37;;;;;;;;;;2108:410;2559:29;;;;:12;:29;;;;;;;:36;;;;2591:4;2559:36;;;2626:32;2634:4;;2653;;;;2626:32;2653:4;;;;2626:32;;;;;;;;;;;;;;;;;;;;;;;;2605:95;;;;;;;;;;;;;;;;2788:27;;;2805:10;2788:27;2784:92;;2831:21;:34;;;;;;2784:92;1426:1456;;;;;;;:::o;1157:666:22:-;259:5:55;;1254:30:22;;;;;;259:5:55;;245:10;:19;224:85;;;;;;;;;;;;;;1299:10:22;1254:56;;1405:18;:29;;;:31;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1405:31:22;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;1405:31:22;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;1405:31:22;;;;;;;;;1474:26;;;;;;;:12;:26;;;;;;1383:53;;-1:-1:-1;1474:26:22;;;-1:-1:-1;1531:31:22;;1510:104;;;;;;;;;;;;;;1674:26;;;;;;;:12;:26;;;;;;;:47;;;;;;;;;;1736:80;;;;;1674:26;;1796:10;;1736:80;;;;;;;;;;1157:666;;;;:::o;4417:2261:23:-;4504:26;;:::i;:::-;4602:19;4615:5;4602:12;:19::i;:::-;4580;;;;:41;;;;4703:27;;;:6;:27;;;;;;;;4663:37;;;:67;5032:22;;;;:27;5028:157;;;5105:38;5099:45;5075:69;;;;5158:16;;5028:157;5498:22;;;;:27;5494:157;;;5571:38;5565:45;;5494:157;5745:22;;;;5704:37;;;;:63;5700:179;;5813:24;5807:31;;5700:179;6003:27;;;;5984:15;:46;5980:157;;6076:19;6070:26;;5980:157;6206:19;;;;;6196:30;;;;:9;:30;;;;;;;;;6192:143;;;6272:21;6266:28;;6192:143;6402:10;;;;6359:18;;6348:30;;;;;;;;:10;:30;;;;;;;;6379:19;;;;6348:51;;;;;;;;;;:64;6344:177;;;6458:21;6452:28;;6344:177;6624:20;6594:51;;;4417:2261;-1:-1:-1;4417:2261:23:o;4019:124::-;939:6:56;;;;938:7;917:72;;;;;;;;;;;;;;1043:6;:13;;;;1052:4;1043:13;;;4110:26:23;4130:5;4110:19;:26::i;:::-;-1:-1:-1;1165:5:56;1156:14;;;;;;4019:124:23:o;1627:67::-;;;;;;;;;;;;;;;;;;;;;;;;:::o;1528:185:38:-;;;;;;;;;;;;;;;;;;;;;;;;;:::o;9462:1488:27:-;9646:35;;:::i;:::-;9697:27;9762:20;9813:9;10199:37;10390:36;;:::i;:::-;9727:6;9734:1;9727:9;;;;;;;;;;;;;;;;;;:24;;;9697:54;;9785:6;:13;9762:36;;9825:1;9813:13;;9808:1103;9828:17;;;9808:1103;;10102:14;10075:6;10082:1;10075:9;;;;;;;;;;;;;;;;;;:24;;:41;;;;10239:70;10247:20;10269:16;:39;;;10239:7;:70::i;:::-;10199:110;;10429:135;10463:6;10470:1;10463:9;;;;;;;;;;;;;;;;;;10490:29;10537:10;10548:1;10537:13;;;;;;;;;10429:135;10390:174;;10649:51;10664:16;10682:17;10649:14;:51::i;:::-;10798:39;;;;:63;-1:-1:-1;10794:107:27;;10881:5;;10794:107;9847:3;;;;;9808:1103;;;9462:1488;;;;;;;;;;:::o;1301:33:39:-;;;;:::o;11338:1914:27:-;11535:35;;:::i;:::-;11586:27;11651:20;11702:9;12092:37;12398;12689:36;;:::i;:::-;939:6:56;;;;938:7;917:72;;;;;;;;;;;;;;1043:6;:13;;;;1052:4;1043:13;;;11616:9:27;;:6;;1043::56;11616:9:27;;;;;;;;;;;;;;:24;;;11586:54;;11674:6;:13;11651:36;;11714:1;11702:13;;11697:1516;11717:17;;;11697:1516;;11996:14;11969:6;11976:1;11969:9;;;;;;;;;;;;;;;;;;;:24;;:41;12162:39;;12132:70;;12140:20;;12132:7;:70::i;:::-;12092:110;;12438:170;12477:6;12484:1;12477:9;;;;;;;;;12438:170;12398:210;;12728:136;12763:6;12770:1;12763:9;;;;;;;;;;;;;;;;;;12790:29;12837:10;12848:1;12837:13;;;;;;;;;12728:136;12689:175;;12949:51;12964:16;12982:17;12949:14;:51::i;:::-;13100:39;;:63;-1:-1:-1;13096:107:27;;13183:5;;13096:107;11736:3;;;;;11697:1516;;;-1:-1:-1;;1165:5:56;1156:14;;;;;;-1:-1:-1;11338:1914:27;;;-1:-1:-1;;;;;;11338:1914:27:o;1064:36:26:-;;;;;;:::o;333:167:55:-;259:5;;;;245:10;:19;224:85;;;;;;;;;;;;;;429:22;;;;425:69;;467:5;:16;;;;;;;;;;425:69;333:167;:::o;1078:40:21:-;;;;;;;;;;;;;;;;;;;:::o;6995:1583:23:-;7160:30;;:::i;:::-;7234:26;;:::i;:::-;7324:20;7629:33;7745:30;7263:19;7276:5;7263:12;:19::i;:::-;7234:48;;7347:26;:24;:26::i;:::-;7324:49;;7446:120;7479:5;7498:9;7521:12;7547:9;7446:19;:120::i;:::-;7665:70;7673:5;:22;;;7697:9;:37;;;7665:7;:70::i;:::-;7629:106;;7778:55;7785:20;7807:25;7778:6;:55::i;:::-;7745:88;;7872:185;7901:5;7920:9;7943:20;7977:22;8013:11;:34;;;7872:15;:185::i;:::-;8127:51;8148:5;8155:22;8127:20;:51::i;:::-;8113:65;;8231:181;8262:5;8281:12;8307:9;:19;;;8340:9;:37;;;8391:11;8231:17;:181::i;:::-;8451:91;8476:5;8495:12;8521:11;8451;:91::i;:::-;6995:1583;;;;;;;;;:::o;1771:648:41:-;1964:39;;2005:40;;1956:90;;1964:39;1956:7;:90::i;:::-;1914:132;;2106:39;;;;;2147:40;;;;2098:90;;2106:39;2098:7;:90::i;:::-;2056:39;;;:132;2238:29;;;;;2269:30;;;;2230:70;;2238:29;2230:7;:70::i;:::-;2198:29;;;:102;2350:29;;;;;2381:30;;;;2342:70;;2350:29;2342:7;:70::i;:::-;2310:29;;;;:102;;;;-1:-1:-1;1771:648:41:o;4841:307:26:-;4980:21;;4924:7;;4980:21;;4924:7;5036:36;;:74;;5088:22;5036:74;;;5075:10;5036:74;5011:99;4841:307;-1:-1:-1;;;4841:307:26:o;11221:1268:23:-;11513:21;;:52;;11544:20;11513:52;11492:115;;;;;;;;;;;;;;11687:19;;;;:33;;;11683:170;;11761:19;;;;:33;;11784:10;11761:33;11736:106;;;;;;;;;;;;;;11931:18;;;;:32;;;11927:169;;12026:12;12004:34;;:5;:18;;;:34;;;11979:106;;;;;;;;;;;;;;;;12186:37;;;;:42;12182:301;;;12269:146;12307:9;:19;;;12348:5;:18;;;12388:9;12269:16;:146::i;:::-;12244:228;;;;;;;;;;;;;;;;11221:1268;;;;:::o;4931:1075:24:-;5885:64;5893:9;:26;;;5921:10;:27;;;5885:7;:64::i;:::-;5805;5813:9;:26;;;5841:10;:27;;;5805:7;:64::i;:::-;:144;;5784:215;;;;;;;;;;;;;;4931:1075;;:::o;6742:4784::-;7014:59;;:::i;:::-;7185:37;7303;7511:38;7632;7225:68;7233:9;:26;;;7261:31;7225:7;:68::i;:::-;7185:108;;7343:158;7382:9;:26;;;7422:9;:26;;;7462:29;7343:25;:158::i;:::-;7303:198;;7552:70;7560:10;:27;;;7589:32;7552:7;:70::i;:::-;7511:111;;7673:161;7712:10;:27;;;7753:10;:27;;;7794:30;7673:25;:161::i;:::-;7632:202;-1:-1:-1;8592:63:24;;;8588:1672;;8722:24;;;;;;:80;;;8816:24;;:47;;:80;;;8959:24;:47;8910:23;;:46;;:96;9299:26;;;;9343;;;;9387:23;;:46;;;;9256:191;;9343:26;9256:25;:191::i;:::-;9207:23;;:240;8588:1672;;;9528:23;;:78;;;9620:23;;:46;;;;:78;;;9762:23;;:46;;;9712:24;;;;;:96;;;;10098:27;;;;10143;;;;10188:24;;:47;10056:193;;10143:27;10056:24;:193::i;:::-;10006:24;;;;;:47;:243;8588:1672;10382:23;;:46;10442:24;;;;;:47;;10361:138;;10382:46;10361:7;:138::i;:::-;10313:45;;;:186;10627:23;;:46;10687:26;;;;10727:18;;;;10588:167;;10627:46;10687:26;10588:25;:167::i;:::-;10549:23;;:36;;:206;10843:23;;:46;;;10903:26;;;;10943:18;;;;10804:167;;10843:46;10903:26;10804:25;:167::i;:::-;10765:23;;:36;;:206;11101:24;;;;:47;11162:27;;;;11203:19;;;;11062:170;;11101:47;11162:27;11062:25;:170::i;:::-;11022:18;:24;;;:37;;:210;;;;;11282:170;11321:18;:24;;;:47;;;11382:10;:27;;;11423:10;:19;;;11282:25;:170::i;:::-;11242:24;;;;:37;;:210;6742:4784;;;;;;;;;;:::o;12927:2440:23:-;13322:25;;;13301:92;;;;;;;;;;;;;;13621:46;;;;13600:106;;;;;;;;;;;;;;13988:5;:22;;;13914:70;13922:9;:37;;;13961:22;13914:7;:70::i;:::-;:96;;13893:157;;;;;;;;;;;;;;15261:55;15269:5;:22;;;15293;15261:7;:55::i;:::-;15177;15185:22;15209:5;:22;;;15177:7;:55::i;:::-;:139;;15156:204;;;;;;;;;;;;;;12927:2440;;;;;:::o;9384:792::-;9662:72;9670:27;9699:11;:34;;;9662:7;:72::i;:::-;9642:17;;;;:6;:17;;;;;;;;;:92;;;;9821:25;;;;9789:18;;9910:34;;9958;;;;10006:24;;;;10044;;;;10105:20;;;;10139;;;;9771:398;;9642:17;;9771:398;;;;;;;;;;;;;;9860:12;;9886:10;;9910:34;;9958;;10006:24;;10139:20;9771:398;;;;;;;;;;9384:792;;;;;:::o;11972:2233:24:-;12219:25;12247:14;;;;;;;;;;;;;;;;;;;;;;;12219:42;;12305:202;12339:9;:24;;;12377:9;:22;;;12413:10;:23;;;12450:18;:24;;;:47;;;12305:20;:202::i;:::-;12551:25;;;;12590:23;;12627:22;;12663:23;;:46;;;12517:202;;12551:25;12590:23;12627:22;12517:20;:202::i;:::-;12729:189;12763:9;:24;;;12801:9;:22;;;12837:12;12863:18;:45;;;12729:20;:189::i;:::-;12951:185;12985:12;13011:9;:22;;;13047:9;:29;;;13090:18;:23;;;:36;;;12951:20;:185::i;:::-;13146:188;13180:12;13206:10;:23;;;13243:10;:30;;;13287:18;:24;;;:37;;;13146:20;:188::i;:::-;13404:10;:30;;;13371:63;;:9;:29;;;:63;;;13367:832;;;13450:301;13488:12;13518;13548:9;:29;;;13595:142;13624:18;:23;;;:36;;;13682:18;:24;;;:37;;;13595:7;:142::i;:::-;13450:20;:301::i;:::-;13367:832;;;13782:195;13820:12;13850;13880:9;:29;;;13927:18;:23;;;:36;;;13782:20;:195::i;:::-;13991:197;14029:12;14059;14089:10;:30;;;14137:18;:24;;;:37;;;13991:20;:197::i;1011:10099:37:-;5031:4;5025:11;;5321:66;5314:4;5291:28;;;5284:104;;;;1199:30;5445:4;5422:28;;6444:59;;;6770:19;;5736:26;;;6750:40;;;6883:23;;;6877:30;6853:22;;;6846:62;6979:23;;;6973:30;6949:22;;;6942:62;7082:23;;;7076:30;7052:22;;;7045:62;7197:4;7179:23;;7173:30;7149:22;;;7142:62;7297:4;7279:23;;7273:30;7249:22;;;7242:62;7397:4;7379:23;;7373:30;7349:22;;;7342:62;7495:4;7477:23;;7471:30;7447:22;;;7440:62;7594:5;7576:24;;7570:31;7545:23;;;7538:64;7699:5;7681:24;;7675:31;7650:23;;;7643:64;7787:5;7769:24;;7763:31;;7738:23;;;7731:64;;;7895:5;7877:24;;7871:31;7846:23;;;7839:64;7979:5;8106:74;;8268:24;8340:19;;7962:23;;;8544:34;;;5025:11;;5422:28;;;;6536:27;;;;5736:26;;8606:22;;;;8388:23;;;;8464:4;8445:24;;8441:35;6190:1;8698:237;8721:13;8718:1;8715:20;8698:237;;;8791:19;;8771:40;;8860:4;8843:22;;;;8898:23;;;;8749:1;8742:9;8698:237;;;-1:-1:-1;;;;9045:31:37;;;9029:13;9010:33;;;9003:74;;;;9171:17;;9165:24;9237:19;;9441:34;;;9037:4;9503:22;;;;9285:23;;;;9361:4;9342:24;;9338:35;9610:1;9596:237;9619:13;9616:1;9613:20;9596:237;;;9689:19;;9669:40;;9758:4;9741:22;;;;9796:23;;;;9647:1;9640:9;9596:237;;;-1:-1:-1;;;9902:46:37;;;-1:-1:-1;10133:33:37;;;10003:4;9981:27;;;10108:59;;;10281:19;;10472:34;;;9981:27;;10534:22;;;;10329:23;;;;10281:19;10405:4;10386:24;;10382:35;-1:-1:-1;10613:237:37;10636:13;10633:1;10630:20;10613:237;;;10706:19;;10686:40;;10775:4;10758:22;;;;10813:23;;;;10664:1;10657:9;10613:237;;;-1:-1:-1;;;10928:46:37;;;;;10902:73;;-1:-1:-1;11041:4:37;11034:25;-1:-1:-1;;;;;1011:10099:37;;;;;:::o;8809:332:23:-;8927:26;;:::i;:::-;8956:19;8969:5;8956:12;:19::i;:::-;8927:48;;9014:35;9032:5;9039:9;9014:17;:35::i;:::-;9086:48;9107:5;9114:9;:19;;;9086:20;:48::i;16922:513:27:-;17102:30;;:::i;:::-;17162:103;17193:5;17212:20;17246:9;17162:17;:103::i;:::-;17296:34;;;;17148:117;;-1:-1:-1;17296:58:27;;17275:125;;;;;;;;;;;;;502:208:57;588:7;632:6;;;;611:70;;;;;;;;;;;;;;-1:-1:-1;698:5:57;;;502:208;;;;;:::o;8313:448:53:-;8397:13;8458:1;8447;:8;:12;8426:92;;;;;;;;;;;;;;;;8568:8;;8566:1;;8568:12;;;;8566:15;;;;;;;;;;8680:8;;8676:16;;8705:17;;;-1:-1:-1;8566:15:53;;;;;;;8313:448::o;13290:490::-;13411:14;13474:5;13482:2;13474:10;13462:1;:8;:22;;13441:107;;;;;;;;;;;;;;;;-1:-1:-1;13727:13:53;13629:2;13727:13;13721:20;;13290:490::o;8292:1482:25:-;8507:135;;8455:12;;8483:21;;8543:48;;8507:135;;8605:4;;8623:9;;8507:135;;;;;;;;22:32:-1;26:21;;;22:32;6:49;;8507:135:25;;;49:4:-1;25:18;;;61:17;;8507:135:25;182:15:-1;8507:135:25;;;;179:29:-1;;;;160:49;;8943:15:25;;8507:135;;-1:-1:-1;49:4;25:18;;;8816:13:25;8763:3;8735:380;9136:7;9156:422;;;;9596:1;9591:143;;;;9129:605;;9156:422;9246:66;9243:1;9236:77;9341:66;9337:2;9330:78;9436:66;9432:2;9425:78;9531:1;9527:2;9520:13;9560:3;9557:1;9550:14;9591:143;-1:-1:-1;;9706:14:25;;8292:1482;-1:-1:-1;;;;;8292:1482:25:o;8956:482:53:-;9043:14;9106:2;9094:1;:8;:14;;9073:99;;;;;;;;;;;;;;;;9224:29;9236:1;9250:2;9239:1;:8;:13;9224:11;:29::i;:::-;9356:8;;9352:17;;9382;;;-1:-1:-1;9215:38:53;8956:482::o;10213:1561:25:-;10465:165;;10413:12;;10441:21;;10501:51;;10465:165;;10566:4;;10512:13;;10611:9;;10465:165;;;;;;;;22:32:-1;26:21;;;22:32;6:49;;10465:165:25;;;49:4:-1;25:18;;;61:17;;10465:165:25;182:15:-1;10465:165:25;;;;179:29:-1;;;;160:49;;10937:15:25;;10465:165;;-1:-1:-1;49:4;25:18;;;10805:16:25;10751:3;10723:389;11133:7;11153:425;;;;11596:1;11591:143;;;;11126:608;;11153:425;11246:66;11243:1;11236:77;11341:66;11337:2;11330:78;11436:66;11432:2;11425:78;11531:1;11527:2;11520:13;11560:3;11557:1;11550:14;11591:143;11712:7;11706:14;11695:25;;11126:608;-1:-1:-1;;;10213:1561:25;;;;;;;:::o;3123:425:42:-;3287:21;3345:15;;;3324:78;;;;;;;;;;;;;;3429:82;3450:26;3458:9;3469:6;3450:7;:26::i;:::-;3490:11;3429:7;:82::i;:::-;3413:98;3123:425;-1:-1:-1;;;;3123:425:42:o;3175:1222:26:-;827:146:48;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;26:21:-1;;;22:32;;6:49;;827:146:48;;;;;;;;817:157;;3339:14:26;;;;;;827:146:48;;;;817:157;;;;827:146;817:157;36:153:-1;66:2;58:11;;36:153;;176:10;;164:23;;139:12;;;;;98:2;89:12;;;;114;36:153;;;299:10;344;;263:2;259:12;;;254:3;250:22;246:30;;311:9;;295:26;;;340:21;;377:20;365:33;;817:157:48;;;;;;;;;;;3456:15:26;;817:157:48;;-1:-1:-1;3456:15:26;;-1:-1:-1;817:157:48;;-1:-1:-1;817:157:48;;3456:15:26;;;-1:-1:-1;3456:15:26;817:157:48;3456:15:26;36:153:-1;66:2;58:11;;36:153;;176:10;;164:23;;139:12;;;;;98:2;89:12;;;;114;36:153;;;299:10;344;;263:2;259:12;;;254:3;250:22;246:30;;311:9;;295:26;;;340:21;;377:20;365:33;;3456:15:26;;;;;;;;;;;;3834:26;;;3960:15;;3953:29;;;;-1:-1:-1;4105:42:26;4086:62;;;;4069:15;;;4062:87;;;;-1:-1:-1;;4199:2:26;4187:15;;4180:33;-1:-1:-1;;4354:3:26;4336:22;;;;3175:1222;-1:-1:-1;3175:1222:26:o;1834:924:39:-;1985:18;;2297:2;2291:9;2329:66;2314:82;;2446:1;2434:14;;2427:40;;;;2564:2;2552:15;;2545:35;;;;2716:2;2698:21;;;1834:924::o;4211:202:43:-;4300:17;4345:35;4363:16;4373:5;4363:9;:16::i;1371:135:57:-;1456:7;1490:1;1486;:5;:13;;1498:1;1486:13;;;-1:-1:-1;1494:1:57;;1479:20;-1:-1:-1;1371:135:57:o;16651:887:23:-;16802:30;;:::i;:::-;16897:34;;;:59;;;17078:22;;;;17114;;;;17003:143;;16934:22;;17003:25;:143::i;:::-;16966:180;;;17270:22;;;;17306:14;;;;17183:147;;16966:180;17270:22;17183:25;:147::i;:::-;17156:24;;;:174;17442:22;;;;17478:14;;;;17367:135;;17406:22;;17367:25;:135::i;:::-;17340:24;;;:162;16651:887;;;;:::o;17848:932::-;18055:14;;;18027:25;18055:14;;;;;;;;;;;;;;;;;;;;18113:20;;;;18147:18;;18205:34;;18079:170;;18113:20;18147:18;18179:12;;18079:20;:170::i;:::-;18259;18293:5;:20;;;18327:12;18353:5;:18;;;18385:11;:34;;;18259:20;:170::i;:::-;18439:165;18473:12;18499:5;:18;;;18531:5;:25;;;18570:11;:24;;;18439:20;:165::i;:::-;18614:159;18648:12;18674;18700:5;:25;;;18739:11;:24;;;18614:20;:159::i;716:230:57:-;802:7;837:5;;;873:6;;;;852:69;;;;;;;;;;;;;;938:1;931:8;;716:230;;;;;;:::o;51:288::-;137:7;;164:6;;160:45;;;193:1;186:8;;;;160:45;-1:-1:-1;226:5:57;;;230:1;226;:5;262;;;;;;;;:10;241:73;;;;;;;;;;;;;1012:624:42;1180:21;1238:15;;;1217:78;;;;;;;;;;;;;;1328:114;1366:9;1393:11;1422:6;1328:20;:114::i;:::-;1327:115;1306:176;;;;;;;;;;;;;1959:889;2126:21;2184:15;;;2163:78;;;;;;;;;;;;;;2274:113;2311:9;2338:11;2367:6;2274:19;:113::i;:::-;2273:114;2252:175;;;;;;;;;;;;;;2649:162;2670:106;2695:26;2703:9;2714:6;2695:7;:26::i;:::-;2739:23;2747:11;2760:1;2739:7;:23::i;:::-;2670:7;:106::i;2482:5147:22:-;3031:19;3290:18;2718:1;2709:6;:10;:24;;;;;2731:2;2723:10;;:4;:10;;;;2709:24;2705:4918;;;2822:16;;2841:1;-1:-1:-1;2797:109:22;;;;;;;;;;;;;;-1:-1:-1;;3153:2:22;3138:18;;;3111:46;3179:66;3107:156;3311:26;;;;:12;:26;;;;;;;;;;3422:24;;;3397:109;;;;;;;;;;;;;;4923:2;4917:9;5239:15;5234:2;5222:9;5216:16;5212:25;5208:47;5392:14;5387:3;5383:24;5374:7;5370:38;5676:66;5667:7;5660:83;6160:3;6156:1;6147:7;6143:15;6136:28;6216:42;6210:4;6206:53;6201:2;6192:7;6188:16;6181:79;6310:42;6306:2;6302:51;6297:2;6288:7;6284:16;6277:77;6397:6;6391:3;6382:7;6378:17;6371:33;6563:3;6554:7;6550:17;6645:206;6665:5;6655:8;6652:19;6645:206;;;6714:16;;6697:34;;6778:2;6815:18;;;;6764:17;6645:206;;;7417:3;7346:7;7292;7285:5;7281:19;7208:7;7142:1;7067:10;7004:3;6978:512;7517:7;7510:15;7507:2;;;7564:16;7555:7;7548:33;7507:2;4724:2889;;;;;;2482:5147;;;;;;:::o;15564:816:23:-;15828:21;;16220:20;;15828:52;;15859:20;15828:52;15807:115;;;;;;;;;;;;;;15996:19;;;;:33;;;15992:170;;16070:19;;;;:33;;16093:10;16070:33;16045:106;;;;;;;;;;;;;;16243:26;:24;:26::i;:::-;16300:18;;16220:49;;-1:-1:-1;16300:34:23;;;;;;;16279:94;;;;;;;;;;;;;;15564:816;;;:::o;10478:422::-;10626:20;;;;:9;:20;;;;;;;:27;;;;10649:4;10626:27;;;10743:25;;;;10711:18;;10829:20;;;;10863;;;;10691:202;;10636:9;;10691:202;;;;;;;;;;;;;;10782:10;;10863:20;10691:202;;;;;;;;;;10478:422;;:::o;10268:886:53:-;10389:14;10452:5;10460:2;10452:10;10440:1;:8;:22;;10419:135;;;;;;;;;;;;;;;;-1:-1:-1;11056:13:53;10801:2;11056:13;11050:20;11072:42;11046:69;;10268:886::o;345:151:57:-;431:7;454:9;470:1;466;:5;;;;;;;;;345:151;-1:-1:-1;;;;345:151:57:o;4553:1678:43:-;777:457;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;22:32:-1;26:21;;;22:32;6:49;;777:457:43;;;;;;;;767:468;;4639:14;;;;;;;;777:457;;;;;767:468;;;;777:457;767:468;36:153:-1;66:2;58:11;;36:153;;176:10;;164:23;;139:12;;;;;98:2;89:12;;;;114;36:153;;;299:10;344;;263:2;259:12;;;254:3;250:22;246:30;;311:9;;295:26;;;340:21;;377:20;365:33;;767:468:43;;;;;;;;;;;4763:20;;;;4753:31;;767:468;;-1:-1:-1;4763:20:43;-1:-1:-1;767:468:43;;-1:-1:-1;767:468:43;;4753:31;;;-1:-1:-1;4753:31:43;767:468;4753:31;36:153:-1;66:2;58:11;;36:153;;176:10;;164:23;;139:12;;;;;98:2;89:12;;;;114;36:153;;;299:10;344;;263:2;259:12;;;254:3;250:22;246:30;;311:9;;295:26;;;340:21;;377:20;365:33;;4753:31:43;;;;;;;;;;;4833:20;;;;4823:31;;4753;;-1:-1:-1;4833:20:43;-1:-1:-1;4753:31:43;;-1:-1:-1;4753:31:43;;4823;;;-1:-1:-1;4823:31:43;4753;4823;36:153:-1;66:2;58:11;;36:153;;176:10;;164:23;;139:12;;;;;98:2;89:12;;;;114;36:153;;;299:10;344;;263:2;259:12;;;;254:3;250:22;246:30;;311:9;;295:26;;;340:21;;377:20;;;;365:33;;4823:31:43;;;;;;;;;;5620:14;;;5763:11;;5670:3;5659:15;;5800:11;;5710:3;5699:15;;;5837:11;;5903:24;;;5940:32;;;5985;;;6056:3;6040:20;;6109:19;;6141;;;-1:-1:-1;;;6173:19:43;;;-1:-1:-1;6040:20:43;;4553:1678;-1:-1:-1;4553:1678:43:o;4779:1869:42:-;4942:12;;4991:15;;;4970:78;;;;;;;;;;;;;;5868:11;;;:29;;-1:-1:-1;5883:14:42;;5868:29;5864:72;;;5920:5;5913:12;;;;5864:72;6522:11;6459:84;;;;;;;6499:9;6479:6;6459:84;6439:104;;6591:26;6599:9;6610:6;6591:7;:26::i;:::-;6563:24;6571:4;6577:9;6563:7;:24::i;:::-;:54;;;4779:1869;-1:-1:-1;;;;;4779:1869:42:o;6909:918::-;7071:12;;7120:15;;;7099:78;;;;;;;;;;;;;;7250:11;;;:29;;-1:-1:-1;7265:14:42;;7250:29;7246:257;;;7487:5;7480:12;;;;7246:257;7634:11;7571:84;;;;;;;7611:9;7591:6;7571:84;7551:104;;7711:11;7677:31;7685:11;7698:9;7677:7;:31::i;:::-;:45;;;;;;;;7665:57;;7770:26;7778:9;7789:6;7770:7;:26::i;897:514:21:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;;:::o;:::-;;;;;;;;;-1:-1:-1;897:514:21;;;;;;;;;;;;;;;;;:::o;5:118:-1:-;;72:46;110:6;97:20;72:46;;146:693;;261:4;249:17;;245:27;-1:-1;235:2;;286:1;283;276:12;235:2;323:6;310:20;345:85;360:69;422:6;360:69;;;345:85;;;458:21;;;502:4;490:17;;;;336:94;;-1:-1;515:14;;490:17;610:1;595:238;620:6;617:1;614:13;595:238;;;703:3;690:17;682:6;678:30;727:42;765:3;753:10;727:42;;;715:55;;-1:-1;793:4;784:14;;;;812;;;;;642:1;635:9;595:238;;;599:14;228:611;;;;;;;;879:735;;1008:4;996:17;;992:27;-1:-1;982:2;;1033:1;1030;1023:12;982:2;1070:6;1057:20;1092:99;1107:83;1183:6;1107:83;;1092:99;1219:21;;;1263:4;1251:17;;;;1083:108;;-1:-1;1276:14;;1251:17;1371:1;1356:252;1381:6;1378:1;1375:13;1356:252;;;1464:3;1451:17;1443:6;1439:30;1488:56;1540:3;1528:10;1488:56;;;1476:69;;-1:-1;1568:4;1559:14;;;;1587;;;;;1403:1;1396:9;1356:252;;1640:707;;1750:4;1738:17;;1734:27;-1:-1;1724:2;;1775:1;1772;1765:12;1724:2;1812:6;1799:20;1834:80;1849:64;1906:6;1849:64;;1834:80;1825:89;;1931:5;1956:6;1949:5;1942:21;1986:4;1978:6;1974:17;1964:27;;2008:4;2003:3;1999:14;1992:21;;2061:6;2108:3;2100:4;2092:6;2088:17;2083:3;2079:27;2076:36;2073:2;;;2125:1;2122;2115:12;2073:2;2150:1;2135:206;2160:6;2157:1;2154:13;2135:206;;;2218:3;2240:37;2273:3;2261:10;2240:37;;;2228:50;;-1:-1;2301:4;2292:14;;;;2320;;;;;2182:1;2175:9;2135:206;;2355:112;;2419:43;2454:6;2441:20;2419:43;;2474:118;;2541:46;2579:6;2566:20;2541:46;;2599:116;;2665:45;2702:6;2689:20;2665:45;;2722:120;;2799:38;2829:6;2823:13;2799:38;;2863:335;;;2970:4;2958:17;;2954:27;-1:-1;2944:2;;2995:1;2992;2985:12;2944:2;-1:-1;3015:20;;3055:18;3044:30;;3041:2;;;3087:1;3084;3077:12;3041:2;3121:4;3113:6;3109:17;3097:29;;3171:3;3164;3156:6;3152:16;3142:8;3138:31;3135:40;3132:2;;;3188:1;3185;3178:12;3132:2;2937:261;;;;;;3207:432;;3297:4;3285:17;;3281:27;-1:-1;3271:2;;3322:1;3319;3312:12;3271:2;3359:6;3346:20;3381:60;3396:44;3433:6;3396:44;;3381:60;3372:69;;3461:6;3454:5;3447:21;3497:4;3489:6;3485:17;3530:4;3523:5;3519:16;3565:3;3556:6;3551:3;3547:16;3544:25;3541:2;;;3582:1;3579;3572:12;3541:2;3592:41;3626:6;3621:3;3616;3592:41;;;3264:375;;;;;;;;4124:2205;;4232:5;4220:9;4215:3;4211:19;4207:31;4204:2;;;4251:1;4248;4241:12;4204:2;4269:21;4284:5;4269:21;;;4260:30;-1:-1;4348:1;4379:49;4424:3;4404:9;4379:49;;;4355:74;;-1:-1;4498:2;4531:49;4576:3;4552:22;;;4531:49;;;4524:4;4517:5;4513:16;4506:75;4450:142;4657:2;4690:49;4735:3;4726:6;4715:9;4711:22;4690:49;;;4683:4;4676:5;4672:16;4665:75;4602:149;4810:2;4843:49;4888:3;4879:6;4868:9;4864:22;4843:49;;;4836:4;4829:5;4825:16;4818:75;4761:143;4966:3;5000:49;5045:3;5036:6;5025:9;5021:22;5000:49;;;4993:4;4986:5;4982:16;4975:75;4914:147;5123:3;5157:49;5202:3;5193:6;5182:9;5178:22;5157:49;;;5150:4;5143:5;5139:16;5132:75;5071:147;5272:3;5306:49;5351:3;5342:6;5331:9;5327:22;5306:49;;;5299:4;5292:5;5288:16;5281:75;5228:139;5421:3;5455:49;5500:3;5491:6;5480:9;5476:22;5455:49;;;5448:4;5441:5;5437:16;5430:75;5377:139;5583:3;5618:49;5663:3;5654:6;5643:9;5639:22;5618:49;;;5610:5;5603;5599:17;5592:76;5526:153;5729:3;5764:49;5809:3;5800:6;5789:9;5785:22;5764:49;;;5756:5;5749;5745:17;5738:76;5689:136;5913:3;5902:9;5898:19;5885:33;5938:18;5930:6;5927:30;5924:2;;;5970:1;5967;5960:12;5924:2;6006:54;6056:3;6047:6;6036:9;6032:22;6006:54;;;5998:5;5991;5987:17;5980:81;5835:237;6160:3;6149:9;6145:19;6132:33;6185:18;6177:6;6174:30;6171:2;;;6217:1;6214;6207:12;6171:2;6253:54;6303:3;6294:6;6283:9;6279:22;6253:54;;;6245:5;6238;6234:17;6227:81;6082:237;4198:2131;;;;;8705:241;;8809:2;8797:9;8788:7;8784:23;8780:32;8777:2;;;8825:1;8822;8815:12;8777:2;8860:1;8877:53;8922:7;8902:9;8877:53;;8953:366;;;9074:2;9062:9;9053:7;9049:23;9045:32;9042:2;;;9090:1;9087;9080:12;9042:2;9125:1;9142:53;9187:7;9167:9;9142:53;;;9132:63;;9104:97;9232:2;9250:53;9295:7;9286:6;9275:9;9271:22;9250:53;;;9240:63;;9211:98;9036:283;;;;;;9326:360;;;9444:2;9432:9;9423:7;9419:23;9415:32;9412:2;;;9460:1;9457;9450:12;9412:2;9495:1;9512:53;9557:7;9537:9;9512:53;;;9502:63;;9474:97;9602:2;9620:50;9662:7;9653:6;9642:9;9638:22;9620:50;;9693:415;;9841:2;9829:9;9820:7;9816:23;9812:32;9809:2;;;9857:1;9854;9847:12;9809:2;9892:31;;9943:18;9932:30;;9929:2;;;9975:1;9972;9965:12;9929:2;9995:97;10084:7;10075:6;10064:9;10060:22;9995:97;;10115:947;;;;10352:2;10340:9;10331:7;10327:23;10323:32;10320:2;;;10368:1;10365;10358:12;10320:2;10403:31;;10454:18;10443:30;;10440:2;;;10486:1;10483;10476:12;10440:2;10506:97;10595:7;10586:6;10575:9;10571:22;10506:97;;;10496:107;;10382:227;10668:2;10657:9;10653:18;10640:32;10692:18;10684:6;10681:30;10678:2;;;10724:1;10721;10714:12;10678:2;10744:78;10814:7;10805:6;10794:9;10790:22;10744:78;;;10734:88;;10619:209;10887:2;10876:9;10872:18;10859:32;10911:18;10903:6;10900:30;10897:2;;;10943:1;10940;10933:12;10897:2;10963:83;11038:7;11029:6;11018:9;11014:22;10963:83;;;10953:93;;10838:214;10314:748;;;;;;11069:811;;;;11281:2;11269:9;11260:7;11256:23;11252:32;11249:2;;;11297:1;11294;11287:12;11249:2;11332:31;;11383:18;11372:30;;11369:2;;;11415:1;11412;11405:12;11369:2;11435:97;11524:7;11515:6;11504:9;11500:22;11435:97;;;11425:107;;11311:227;11569:2;11587:53;11632:7;11623:6;11612:9;11608:22;11587:53;;11887:241;;11991:2;11979:9;11970:7;11966:23;11962:32;11959:2;;;12007:1;12004;11997:12;11959:2;12042:1;12059:53;12104:7;12084:9;12059:53;;12135:366;;;12256:2;12244:9;12235:7;12231:23;12227:32;12224:2;;;12272:1;12269;12262:12;12224:2;12307:1;12324:53;12369:7;12349:9;12324:53;;12508:615;;;;;12665:2;12653:9;12644:7;12640:23;12636:32;12633:2;;;12681:1;12678;12671:12;12633:2;12716:1;12733:53;12778:7;12758:9;12733:53;;;12723:63;;12695:97;12823:2;12841:53;12886:7;12877:6;12866:9;12862:22;12841:53;;;12831:63;;12802:98;12959:2;12948:9;12944:18;12931:32;12983:18;12975:6;12972:30;12969:2;;;13015:1;13012;13005:12;12969:2;13043:64;13099:7;13090:6;13079:9;13075:22;13043:64;;;12627:496;;;;-1:-1;13025:82;-1:-1;;;;12627:496;13130:595;;;;13277:2;13265:9;13256:7;13252:23;13248:32;13245:2;;;13293:1;13290;13283:12;13245:2;13328:1;13345:53;13390:7;13370:9;13345:53;;;13335:63;;13307:97;13435:2;13453:53;13498:7;13489:6;13478:9;13474:22;13453:53;;;13443:63;;13414:98;13571:2;13560:9;13556:18;13543:32;13595:18;13587:6;13584:30;13581:2;;;13627:1;13624;13617:12;13581:2;13647:62;13701:7;13692:6;13681:9;13677:22;13647:62;;13732:239;;13835:2;13823:9;13814:7;13810:23;13806:32;13803:2;;;13851:1;13848;13841:12;13803:2;13886:1;13903:52;13947:7;13927:9;13903:52;;13978:261;;14092:2;14080:9;14071:7;14067:23;14063:32;14060:2;;;14108:1;14105;14098:12;14060:2;14143:1;14160:63;14215:7;14195:9;14160:63;;14246:373;;14373:2;14361:9;14352:7;14348:23;14344:32;14341:2;;;14389:1;14386;14379:12;14341:2;14424:31;;14475:18;14464:30;;14461:2;;;14507:1;14504;14497:12;14461:2;14527:76;14595:7;14586:6;14575:9;14571:22;14527:76;;14626:1089;;;;;14845:3;14833:9;14824:7;14820:23;14816:33;14813:2;;;14862:1;14859;14852:12;14813:2;14897:31;;14948:18;14937:30;;14934:2;;;14980:1;14977;14970:12;14934:2;15000:76;15068:7;15059:6;15048:9;15044:22;15000:76;;;14990:86;;14876:206;15141:2;15130:9;15126:18;15113:32;15165:18;15157:6;15154:30;15151:2;;;15197:1;15194;15187:12;15151:2;15217:76;15285:7;15276:6;15265:9;15261:22;15217:76;;;15207:86;;15092:207;15358:2;15347:9;15343:18;15330:32;15382:18;15374:6;15371:30;15368:2;;;15414:1;15411;15404:12;15368:2;15434:62;15488:7;15479:6;15468:9;15464:22;15434:62;;;15424:72;;15309:193;15561:2;15550:9;15546:18;15533:32;15585:18;15577:6;15574:30;15571:2;;;15617:1;15614;15607:12;15571:2;15637:62;15691:7;15682:6;15671:9;15667:22;15637:62;;;15627:72;;15512:193;14807:908;;;;;;;;15722:727;;;;15892:2;15880:9;15871:7;15867:23;15863:32;15860:2;;;15908:1;15905;15898:12;15860:2;15943:31;;15994:18;15983:30;;15980:2;;;16026:1;16023;16016:12;15980:2;16046:76;16114:7;16105:6;16094:9;16090:22;16046:76;;;16036:86;;15922:206;16159:2;16177:53;16222:7;16213:6;16202:9;16198:22;16177:53;;16704:865;;;;;;;16897:3;16885:9;16876:7;16872:23;16868:33;16865:2;;;16914:1;16911;16904:12;16865:2;16949:1;16966:53;17011:7;16991:9;16966:53;;;16956:63;;16928:97;17056:2;17074:53;17119:7;17110:6;17099:9;17095:22;17074:53;;;17064:63;;17035:98;17192:2;17181:9;17177:18;17164:32;17216:18;17208:6;17205:30;17202:2;;;17248:1;17245;17238:12;17202:2;17276:64;17332:7;17323:6;17312:9;17308:22;17276:64;;;17258:82;;;;17143:203;17405:2;17394:9;17390:18;17377:32;17429:18;17421:6;17418:30;17415:2;;;17461:1;17458;17451:12;17415:2;17489:64;17545:7;17536:6;17525:9;17521:22;17489:64;;;17471:82;;;;17356:203;16859:710;;;;;;;;;17576:110;17649:31;17674:5;17649:31;;;17644:3;17637:44;17631:55;;;17760:755;;17941:77;18012:5;17941:77;;;18036:6;18031:3;18024:19;18060:4;18055:3;18051:14;18044:21;;18105:79;18178:5;18105:79;;;18205:1;18190:303;18215:6;18212:1;18209:13;18190:303;;;18255:103;18354:3;18345:6;18339:13;18255:103;;;18375:83;18451:6;18375:83;;;18481:4;18472:14;;;;;18365:93;-1:-1;18237:1;18230:9;18190:303;;;-1:-1;18506:3;;17920:595;-1:-1;;;;17920:595;18523:101;18590:28;18612:5;18590:28;;18631:110;18704:31;18729:5;18704:31;;18748:107;18819:30;18843:5;18819:30;;18862:297;;18962:38;18994:5;18962:38;;;19017:6;19012:3;19005:19;19029:63;19085:6;19078:4;19073:3;19069:14;19062:4;19055:5;19051:16;19029:63;;;19124:29;19146:6;19124:29;;;19104:50;;;19117:4;19104:50;;18942:217;-1:-1;;;18942:217;19462:156;19555:57;19606:5;19555:57;;19925:296;20080:2;20068:15;;20117:66;20112:2;20103:12;;20096:88;20212:2;20203:12;;20061:160;20230:296;20385:2;20373:15;;20422:66;20417:2;20408:12;;20401:88;20517:2;20508:12;;20366:160;20535:296;20690:2;20678:15;;20727:66;20722:2;20713:12;;20706:88;20822:2;20813:12;;20671:160;20840:296;20995:2;20983:15;;21032:66;21027:2;21018:12;;21011:88;21127:2;21118:12;;20976:160;21145:296;21300:2;21288:15;;21337:66;21332:2;21323:12;;21316:88;21432:2;21423:12;;21281:160;21450:397;21605:2;21593:15;;21642:66;21637:2;21628:12;;21621:88;21743:66;21738:2;21729:12;;21722:88;21838:2;21829:12;;21586:261;21856:296;22011:2;21999:15;;22048:66;22043:2;22034:12;;22027:88;22143:2;22134:12;;21992:160;22161:296;22316:2;22304:15;;22353:66;22348:2;22339:12;;22332:88;22448:2;22439:12;;22297:160;22466:296;22621:2;22609:15;;22658:66;22653:2;22644:12;;22637:88;22753:2;22744:12;;22602:160;22771:296;22926:2;22914:15;;22963:66;22958:2;22949:12;;22942:88;23058:2;23049:12;;22907:160;23076:296;23231:2;23219:15;;23268:66;23263:2;23254:12;;23247:88;23363:2;23354:12;;23212:160;23381:296;23536:2;23524:15;;23573:66;23568:2;23559:12;;23552:88;23668:2;23659:12;;23517:160;23686:296;23841:2;23829:15;;23878:66;23873:2;23864:12;;23857:88;23973:2;23964:12;;23822:160;23991:296;24146:2;24134:15;;24183:66;24178:2;24169:12;;24162:88;24278:2;24269:12;;24127:160;24296:296;24451:2;24439:15;;24488:66;24483:2;24474:12;;24467:88;24583:2;24574:12;;24432:160;24601:296;24756:2;24744:15;;24793:66;24788:2;24779:12;;24772:88;24888:2;24879:12;;24737:160;24906:296;25061:2;25049:15;;25098:66;25093:2;25084:12;;25077:88;25193:2;25184:12;;25042:160;25211:296;25366:2;25354:15;;25403:66;25398:2;25389:12;;25382:88;25498:2;25489:12;;25347:160;25516:397;25671:2;25659:15;;25708:66;25703:2;25694:12;;25687:88;25809:66;25804:2;25795:12;;25788:88;25904:2;25895:12;;25652:261;25922:296;26077:2;26065:15;;26114:66;26109:2;26100:12;;26093:88;26209:2;26200:12;;26058:160;26227:296;26382:2;26370:15;;26419:66;26414:2;26405:12;;26398:88;26514:2;26505:12;;26363:160;26532:296;26687:2;26675:15;;26724:66;26719:2;26710:12;;26703:88;26819:2;26810:12;;26668:160;26837:296;26992:2;26980:15;;27029:66;27024:2;27015:12;;27008:88;27124:2;27115:12;;26973:160;27142:296;27297:2;27285:15;;27334:66;27329:2;27320:12;;27313:88;27429:2;27420:12;;27278:160;27447:296;27602:2;27590:15;;27639:66;27634:2;27625:12;;27618:88;27734:2;27725:12;;27583:160;27752:296;27907:2;27895:15;;27944:66;27939:2;27930:12;;27923:88;28039:2;28030:12;;27888:160;28057:296;28212:2;28200:15;;28249:66;28244:2;28235:12;;28228:88;28344:2;28335:12;;28193:160;28362:397;28517:2;28505:15;;28554:66;28549:2;28540:12;;28533:88;28655:66;28650:2;28641:12;;28634:88;28750:2;28741:12;;28498:261;28768:296;28923:2;28911:15;;28960:66;28955:2;28946:12;;28939:88;29055:2;29046:12;;28904:160;29073:296;29228:2;29216:15;;29265:66;29260:2;29251:12;;29244:88;29360:2;29351:12;;29209:160;29378:296;29533:2;29521:15;;29570:66;29565:2;29556:12;;29549:88;29665:2;29656:12;;29514:160;29683:296;29838:2;29826:15;;29875:66;29870:2;29861:12;;29854:88;29970:2;29961:12;;29819:160;29988:296;30143:2;30131:15;;30180:66;30175:2;30166:12;;30159:88;30275:2;30266:12;;30124:160;30369:888;30597:22;;30510:4;30501:14;;;30631:61;30505:3;30597:22;30631:61;;;30530:174;30798:4;30791:5;30787:16;30781:23;30816:62;30872:4;30867:3;30863:14;30850:11;30816:62;;;30714:176;30974:4;30967:5;30963:16;30957:23;30992:62;31048:4;31043:3;31039:14;31026:11;30992:62;;;30900:166;31150:4;31143:5;31139:16;31133:23;31168:62;31224:4;31219:3;31215:14;31202:11;31168:62;;32323:815;32548:22;;32478:5;32469:15;;;32582:115;32473:3;32548:22;32582:115;;;32499:210;32786:4;32779:5;32775:16;32769:23;32804:116;32914:4;32909:3;32905:14;32892:11;32804:116;;;32719:213;33030:4;33023:5;33019:16;33013:23;33048:63;33104:5;33099:3;33095:15;33082:11;33048:63;;33206:695;33419:22;;33343:4;33334:14;;;33453:57;33338:3;33419:22;33453:57;;;33363:159;33603:4;33596:5;33592:16;33586:23;33621:62;33677:4;33672:3;33668:14;33655:11;33621:62;;;33532:163;33794:4;33787:5;33783:16;33777:23;33812:62;33868:4;33863:3;33859:14;33846:11;33812:62;;34784:104;34853:29;34876:5;34853:29;;34895:193;35003:2;34988:18;;35017:61;34992:9;35051:6;35017:61;;35095:1057;35427:3;35412:19;;35442:61;35416:9;35476:6;35442:61;;;35514:62;35572:2;35561:9;35557:18;35548:6;35514:62;;;35587;35645:2;35634:9;35630:18;35621:6;35587:62;;;35660;35718:2;35707:9;35703:18;35694:6;35660:62;;;35733:63;35791:3;35780:9;35776:19;35767:6;35733:63;;;35807;35865:3;35854:9;35850:19;35841:6;35807:63;;;35919:9;35913:4;35909:20;35903:3;35892:9;35888:19;35881:49;35944:62;36001:4;35992:6;35944:62;;;35936:70;;36055:9;36049:4;36045:20;36039:3;36028:9;36024:19;36017:49;36080:62;36137:4;36128:6;36080:62;;;36072:70;35398:754;-1:-1;;;;;;;;;;35398:754;36159:547;36351:2;36336:18;;36365:61;36340:9;36399:6;36365:61;;;36474:9;36468:4;36464:20;36459:2;36448:9;36444:18;36437:48;36499:62;36556:4;36547:6;36499:62;;;36491:70;;36609:9;36603:4;36599:20;36594:2;36583:9;36579:18;36572:48;36634:62;36691:4;36682:6;36634:62;;;36626:70;36322:384;-1:-1;;;;;36322:384;36713:433;36917:2;36931:47;;;36902:18;;36992:144;36902:18;37122:6;36992:144;;37153:181;37255:2;37240:18;;37269:55;37244:9;37297:6;37269:55;;37341:193;37449:2;37434:18;;37463:61;37438:9;37497:6;37463:61;;37541:479;37723:2;37708:18;;37737:61;37712:9;37771:6;37737:61;;;37809:62;37867:2;37856:9;37852:18;37843:6;37809:62;;;37919:9;37913:4;37909:20;37904:2;37893:9;37889:18;37882:48;37944:66;38005:4;37996:6;37944:66;;38027:378;38181:2;38166:18;;38195:61;38170:9;38229:6;38195:61;;;38304:9;38298:4;38294:20;38289:2;38278:9;38274:18;38267:48;38329:66;38390:4;38381:6;38329:66;;38412:489;38600:3;38585:19;;38615:61;38589:9;38649:6;38615:61;;;38687:58;38741:2;38730:9;38726:18;38717:6;38687:58;;;38756:62;38814:2;38803:9;38799:18;38790:6;38756:62;;;38829;38887:2;38876:9;38872:18;38863:6;38829:62;;38908:290;39042:2;39027:18;;39056:59;39031:9;39088:6;39056:59;;;39126:62;39184:2;39173:9;39169:18;39160:6;39126:62;;39205:269;39327:2;39341:47;;;39312:18;;39402:62;39312:18;39450:6;39402:62;;39481:233;39609:2;39594:18;;39623:81;39598:9;39677:6;39623:81;;40001:387;40182:2;40196:47;;;40167:18;;40257:121;40167:18;40257:121;;40395:387;40576:2;40590:47;;;40561:18;;40651:121;40561:18;40651:121;;40789:387;40970:2;40984:47;;;40955:18;;41045:121;40955:18;41045:121;;41183:387;41364:2;41378:47;;;41349:18;;41439:121;41349:18;41439:121;;41577:387;41758:2;41772:47;;;41743:18;;41833:121;41743:18;41833:121;;41971:387;42152:2;42166:47;;;42137:18;;42227:121;42137:18;42227:121;;42365:387;42546:2;42560:47;;;42531:18;;42621:121;42531:18;42621:121;;42759:387;42940:2;42954:47;;;42925:18;;43015:121;42925:18;43015:121;;43153:387;43334:2;43348:47;;;43319:18;;43409:121;43319:18;43409:121;;43547:387;43728:2;43742:47;;;43713:18;;43803:121;43713:18;43803:121;;43941:387;44122:2;44136:47;;;44107:18;;44197:121;44107:18;44197:121;;44335:387;44516:2;44530:47;;;44501:18;;44591:121;44501:18;44591:121;;44729:387;44910:2;44924:47;;;44895:18;;44985:121;44895:18;44985:121;;45123:387;45304:2;45318:47;;;45289:18;;45379:121;45289:18;45379:121;;45517:387;45698:2;45712:47;;;45683:18;;45773:121;45683:18;45773:121;;45911:387;46092:2;46106:47;;;46077:18;;46167:121;46077:18;46167:121;;46305:387;46486:2;46500:47;;;46471:18;;46561:121;46471:18;46561:121;;46699:387;46880:2;46894:47;;;46865:18;;46955:121;46865:18;46955:121;;47093:387;47274:2;47288:47;;;47259:18;;47349:121;47259:18;47349:121;;47487:387;47668:2;47682:47;;;47653:18;;47743:121;47653:18;47743:121;;47881:387;48062:2;48076:47;;;48047:18;;48137:121;48047:18;48137:121;;48275:387;48456:2;48470:47;;;48441:18;;48531:121;48441:18;48531:121;;48669:387;48850:2;48864:47;;;48835:18;;48925:121;48835:18;48925:121;;49063:387;49244:2;49258:47;;;49229:18;;49319:121;49229:18;49319:121;;49457:387;49638:2;49652:47;;;49623:18;;49713:121;49623:18;49713:121;;49851:387;50032:2;50046:47;;;50017:18;;50107:121;50017:18;50107:121;;50245:387;50426:2;50440:47;;;50411:18;;50501:121;50411:18;50501:121;;50639:387;50820:2;50834:47;;;50805:18;;50895:121;50805:18;50895:121;;51033:387;51214:2;51228:47;;;51199:18;;51289:121;51199:18;51289:121;;51427:387;51608:2;51622:47;;;51593:18;;51683:121;51593:18;51683:121;;51821:387;52002:2;52016:47;;;51987:18;;52077:121;51987:18;52077:121;;52215:387;52396:2;52410:47;;;52381:18;;52471:121;52381:18;52471:121;;52609:387;52790:2;52804:47;;;52775:18;;52865:121;52775:18;52865:121;;53003:310;53169:3;53154:19;;53184:119;53158:9;53276:6;53184:119;;53320:338;53500:3;53485:19;;53515:133;53489:9;53621:6;53515:133;;53665:301;53827:2;53812:18;;53841:115;53816:9;53929:6;53841:115;;54173:256;54235:2;54229:9;54261:17;;;54336:18;54321:34;;54357:22;;;54318:62;54315:2;;;54393:1;54390;54383:12;54315:2;54409;54402:22;54213:216;;-1:-1;54213:216;54436:263;;54600:18;54592:6;54589:30;54586:2;;;54632:1;54629;54622:12;54586:2;-1:-1;54661:4;54649:17;;;54679:15;;54523:176;55255:254;;55394:18;55386:6;55383:30;55380:2;;;55426:1;55423;55416:12;55380:2;-1:-1;55499:4;55470;55447:17;;;;55466:9;55443:33;55489:15;;55317:192;55783:144;55915:4;55903:17;;55884:43;55936:130;56049:12;;56033:33;56514:128;56594:42;56583:54;;56566:76;56649:92;56722:13;56715:21;;56698:43;56748:79;56817:5;56800:27;56834:151;56913:66;56902:78;;56885:100;57078:88;57156:4;57145:16;;57128:38;57737:135;;57836:31;57861:5;57836:31;;57880:145;57961:6;57956:3;57951;57938:30;-1:-1;58017:1;57999:16;;57992:27;57931:94;58034:268;58099:1;58106:101;58120:6;58117:1;58114:13;58106:101;;;58187:11;;;58181:18;58168:11;;;58161:39;58142:2;58135:10;58106:101;;;58222:6;58219:1;58216:13;58213:2;;;-1:-1;;58287:1;58269:16;;58262:27;58083:219;58310:97;58398:2;58378:14;58394:7;58374:28;;58358:49" - } - } - }, - "sources": { - "2.0.0/extensions/Forwarder/Forwarder.sol": { - "id": 0 - }, - "2.0.0/extensions/Forwarder/MixinAssets.sol": { - "id": 1 - }, - "2.0.0/extensions/Forwarder/MixinExchangeWrapper.sol": { - "id": 2 - }, - "2.0.0/extensions/Forwarder/MixinForwarderCore.sol": { - "id": 3 - }, - "2.0.0/extensions/Forwarder/MixinWeth.sol": { - "id": 4 - }, - "2.0.0/extensions/Forwarder/interfaces/IAssets.sol": { - "id": 5 - }, - "2.0.0/extensions/Forwarder/interfaces/IForwarderCore.sol": { - "id": 6 - }, - "2.0.0/extensions/Forwarder/libs/LibConstants.sol": { - "id": 7 - }, - "2.0.0/extensions/Forwarder/mixins/MAssets.sol": { - "id": 8 - }, - "2.0.0/extensions/Forwarder/mixins/MExchangeWrapper.sol": { - "id": 9 - }, - "2.0.0/extensions/Forwarder/mixins/MWeth.sol": { - "id": 10 - }, - "2.0.0/extensions/OrderValidator/OrderValidator.sol": { - "id": 11 - }, - "2.0.0/multisig/MultiSigWallet.sol": { - "id": 12 - }, - "2.0.0/multisig/MultiSigWalletWithTimeLock.sol": { - "id": 13 - }, - "2.0.0/protocol/AssetProxy/ERC20Proxy.sol": { - "id": 14 - }, - "2.0.0/protocol/AssetProxy/ERC721Proxy.sol": { - "id": 15 - }, - "2.0.0/protocol/AssetProxy/MixinAuthorizable.sol": { - "id": 16 - }, - "2.0.0/protocol/AssetProxy/interfaces/IAssetProxy.sol": { - "id": 17 - }, - "2.0.0/protocol/AssetProxy/interfaces/IAuthorizable.sol": { - "id": 18 - }, - "2.0.0/protocol/AssetProxy/mixins/MAuthorizable.sol": { - "id": 19 - }, - "2.0.0/protocol/AssetProxyOwner/AssetProxyOwner.sol": { - "id": 20 - }, - "2.0.0/protocol/Exchange/Exchange.sol": { - "id": 21 - }, - "2.0.0/protocol/Exchange/MixinAssetProxyDispatcher.sol": { - "id": 22 - }, - "2.0.0/protocol/Exchange/MixinExchangeCore.sol": { - "id": 23 - }, - "2.0.0/protocol/Exchange/MixinMatchOrders.sol": { - "id": 24 - }, - "2.0.0/protocol/Exchange/MixinSignatureValidator.sol": { - "id": 25 - }, - "2.0.0/protocol/Exchange/MixinTransactions.sol": { - "id": 26 - }, - "2.0.0/protocol/Exchange/MixinWrapperFunctions.sol": { - "id": 27 - }, - "2.0.0/protocol/Exchange/interfaces/IAssetProxyDispatcher.sol": { - "id": 28 - }, - "2.0.0/protocol/Exchange/interfaces/IExchange.sol": { - "id": 29 - }, - "2.0.0/protocol/Exchange/interfaces/IExchangeCore.sol": { - "id": 30 - }, - "2.0.0/protocol/Exchange/interfaces/IMatchOrders.sol": { - "id": 31 - }, - "2.0.0/protocol/Exchange/interfaces/ISignatureValidator.sol": { - "id": 32 - }, - "2.0.0/protocol/Exchange/interfaces/ITransactions.sol": { - "id": 33 - }, - "2.0.0/protocol/Exchange/interfaces/IValidator.sol": { - "id": 34 - }, - "2.0.0/protocol/Exchange/interfaces/IWallet.sol": { - "id": 35 - }, - "2.0.0/protocol/Exchange/interfaces/IWrapperFunctions.sol": { - "id": 36 - }, - "2.0.0/protocol/Exchange/libs/LibAbiEncoder.sol": { - "id": 37 - }, - "2.0.0/protocol/Exchange/libs/LibConstants.sol": { - "id": 38 - }, - "2.0.0/protocol/Exchange/libs/LibEIP712.sol": { - "id": 39 - }, - "2.0.0/protocol/Exchange/libs/LibExchangeErrors.sol": { - "id": 40 - }, - "2.0.0/protocol/Exchange/libs/LibFillResults.sol": { - "id": 41 - }, - "2.0.0/protocol/Exchange/libs/LibMath.sol": { - "id": 42 - }, - "2.0.0/protocol/Exchange/libs/LibOrder.sol": { - "id": 43 - }, - "2.0.0/protocol/Exchange/mixins/MAssetProxyDispatcher.sol": { - "id": 44 - }, - "2.0.0/protocol/Exchange/mixins/MExchangeCore.sol": { - "id": 45 - }, - "2.0.0/protocol/Exchange/mixins/MMatchOrders.sol": { - "id": 46 - }, - "2.0.0/protocol/Exchange/mixins/MSignatureValidator.sol": { - "id": 47 - }, - "2.0.0/protocol/Exchange/mixins/MTransactions.sol": { - "id": 48 - }, - "2.0.0/protocol/Exchange/mixins/MWrapperFunctions.sol": { - "id": 49 - }, - "2.0.0/tokens/ERC20Token/IERC20Token.sol": { - "id": 50 - }, - "2.0.0/tokens/ERC721Token/IERC721Token.sol": { - "id": 51 - }, - "2.0.0/tokens/EtherToken/IEtherToken.sol": { - "id": 52 - }, - "2.0.0/utils/LibBytes/LibBytes.sol": { - "id": 53 - }, - "2.0.0/utils/Ownable/IOwnable.sol": { - "id": 54 - }, - "2.0.0/utils/Ownable/Ownable.sol": { - "id": 55 - }, - "2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol": { - "id": 56 - }, - "2.0.0/utils/SafeMath/SafeMath.sol": { - "id": 57 - } - }, - "sourceCodes": { - "2.0.0/extensions/Forwarder/Forwarder.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"./MixinWeth.sol\";\nimport \"./MixinForwarderCore.sol\";\nimport \"./libs/LibConstants.sol\";\nimport \"./MixinAssets.sol\";\nimport \"./MixinExchangeWrapper.sol\";\n\n\n// solhint-disable no-empty-blocks\ncontract Forwarder is\n LibConstants,\n MixinWeth,\n MixinAssets,\n MixinExchangeWrapper,\n MixinForwarderCore\n{\n constructor (\n address _exchange,\n bytes memory _zrxAssetData,\n bytes memory _wethAssetData\n )\n public\n LibConstants(\n _exchange,\n _zrxAssetData,\n _wethAssetData\n )\n MixinForwarderCore()\n {}\n}\n", - "2.0.0/extensions/Forwarder/MixinAssets.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../utils/LibBytes/LibBytes.sol\";\nimport \"../../utils/Ownable/Ownable.sol\";\nimport \"../../tokens/ERC20Token/IERC20Token.sol\";\nimport \"../../tokens/ERC721Token/IERC721Token.sol\";\nimport \"./libs/LibConstants.sol\";\nimport \"./mixins/MAssets.sol\";\n\n\ncontract MixinAssets is\n Ownable,\n LibConstants,\n MAssets\n{\n using LibBytes for bytes;\n\n bytes4 constant internal ERC20_TRANSFER_SELECTOR = bytes4(keccak256(\"transfer(address,uint256)\"));\n\n /// @dev Withdraws assets from this contract. The contract requires a ZRX balance in order to \n /// function optimally, and this function allows the ZRX to be withdrawn by owner. It may also be\n /// used to withdraw assets that were accidentally sent to this contract.\n /// @param assetData Byte array encoded for the respective asset proxy.\n /// @param amount Amount of ERC20 token to withdraw.\n function withdrawAsset(\n bytes assetData,\n uint256 amount\n )\n external\n onlyOwner\n {\n transferAssetToSender(assetData, amount);\n }\n\n /// @dev Transfers given amount of asset to sender.\n /// @param assetData Byte array encoded for the respective asset proxy.\n /// @param amount Amount of asset to transfer to sender.\n function transferAssetToSender(\n bytes memory assetData,\n uint256 amount\n )\n internal\n {\n bytes4 proxyId = assetData.readBytes4(0);\n\n if (proxyId == ERC20_DATA_ID) {\n transferERC20Token(assetData, amount);\n } else if (proxyId == ERC721_DATA_ID) {\n transferERC721Token(assetData, amount);\n } else {\n revert(\"UNSUPPORTED_ASSET_PROXY\");\n }\n }\n\n /// @dev Decodes ERC20 assetData and transfers given amount to sender.\n /// @param assetData Byte array encoded for the respective asset proxy.\n /// @param amount Amount of asset to transfer to sender.\n function transferERC20Token(\n bytes memory assetData,\n uint256 amount\n )\n internal\n {\n address token = assetData.readAddress(16);\n\n // Transfer tokens.\n // We do a raw call so we can check the success separate\n // from the return data.\n bool success = token.call(abi.encodeWithSelector(\n ERC20_TRANSFER_SELECTOR,\n msg.sender,\n amount\n ));\n require(\n success,\n \"TRANSFER_FAILED\"\n );\n \n // Check return data.\n // If there is no return data, we assume the token incorrectly\n // does not return a bool. In this case we expect it to revert\n // on failure, which was handled above.\n // If the token does return data, we require that it is a single\n // value that evaluates to true.\n assembly {\n if returndatasize {\n success := 0\n if eq(returndatasize, 32) {\n // First 64 bytes of memory are reserved scratch space\n returndatacopy(0, 0, 32)\n success := mload(0)\n }\n }\n }\n require(\n success,\n \"TRANSFER_FAILED\"\n );\n }\n\n /// @dev Decodes ERC721 assetData and transfers given amount to sender.\n /// @param assetData Byte array encoded for the respective asset proxy.\n /// @param amount Amount of asset to transfer to sender.\n function transferERC721Token(\n bytes memory assetData,\n uint256 amount\n )\n internal\n {\n require(\n amount == 1,\n \"INVALID_AMOUNT\"\n );\n // Decode asset data.\n address token = assetData.readAddress(16);\n uint256 tokenId = assetData.readUint256(36);\n\n // Perform transfer.\n IERC721Token(token).transferFrom(\n address(this),\n msg.sender,\n tokenId\n );\n }\n}\n", - "2.0.0/extensions/Forwarder/MixinExchangeWrapper.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"./libs/LibConstants.sol\";\nimport \"./mixins/MExchangeWrapper.sol\";\nimport \"../../protocol/Exchange/libs/LibAbiEncoder.sol\";\nimport \"../../protocol/Exchange/libs/LibOrder.sol\";\nimport \"../../protocol/Exchange/libs/LibFillResults.sol\";\nimport \"../../protocol/Exchange/libs/LibMath.sol\";\n\n\ncontract MixinExchangeWrapper is\n LibAbiEncoder,\n LibFillResults,\n LibMath,\n LibConstants,\n MExchangeWrapper\n{\n /// @dev Fills the input order.\n /// Returns false if the transaction would otherwise revert.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n /// @return Amounts filled and fees paid by maker and taker.\n function fillOrderNoThrow(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n internal\n returns (FillResults memory fillResults)\n {\n // ABI encode calldata for `fillOrder`\n bytes memory fillOrderCalldata = abiEncodeFillOrder(\n order,\n takerAssetFillAmount,\n signature\n );\n\n address exchange = address(EXCHANGE);\n\n // Call `fillOrder` and handle any exceptions gracefully\n assembly {\n let success := call(\n gas, // forward all gas\n exchange, // call address of Exchange contract\n 0, // transfer 0 wei\n add(fillOrderCalldata, 32), // pointer to start of input (skip array length in first 32 bytes)\n mload(fillOrderCalldata), // length of input\n fillOrderCalldata, // write output over input\n 128 // output size is 128 bytes\n )\n if success {\n mstore(fillResults, mload(fillOrderCalldata))\n mstore(add(fillResults, 32), mload(add(fillOrderCalldata, 32)))\n mstore(add(fillResults, 64), mload(add(fillOrderCalldata, 64)))\n mstore(add(fillResults, 96), mload(add(fillOrderCalldata, 96)))\n }\n }\n // fillResults values will be 0 by default if call was unsuccessful\n return fillResults;\n }\n\n /// @dev Synchronously executes multiple calls of fillOrder until total amount of WETH has been sold by taker.\n /// Returns false if the transaction would otherwise revert.\n /// @param orders Array of order specifications.\n /// @param wethSellAmount Desired amount of WETH to sell.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketSellWeth(\n LibOrder.Order[] memory orders,\n uint256 wethSellAmount,\n bytes[] memory signatures\n )\n internal\n returns (FillResults memory totalFillResults)\n {\n bytes memory makerAssetData = orders[0].makerAssetData;\n bytes memory wethAssetData = WETH_ASSET_DATA;\n\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n\n // We assume that asset being bought by taker is the same for each order.\n // We assume that asset being sold by taker is WETH for each order.\n orders[i].makerAssetData = makerAssetData;\n orders[i].takerAssetData = wethAssetData;\n\n // Calculate the remaining amount of WETH to sell\n uint256 remainingTakerAssetFillAmount = safeSub(wethSellAmount, totalFillResults.takerAssetFilledAmount);\n\n // Attempt to sell the remaining amount of WETH\n FillResults memory singleFillResults = fillOrderNoThrow(\n orders[i],\n remainingTakerAssetFillAmount,\n signatures[i]\n );\n\n // Update amounts filled and fees paid by maker and taker\n addFillResults(totalFillResults, singleFillResults);\n\n // Stop execution if the entire amount of takerAsset has been sold\n if (totalFillResults.takerAssetFilledAmount >= wethSellAmount) {\n break;\n }\n }\n return totalFillResults;\n }\n\n /// @dev Synchronously executes multiple fill orders in a single transaction until total amount is bought by taker.\n /// Returns false if the transaction would otherwise revert.\n /// The asset being sold by taker must always be WETH.\n /// @param orders Array of order specifications.\n /// @param makerAssetFillAmount Desired amount of makerAsset to buy.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketBuyExactAmountWithWeth(\n LibOrder.Order[] memory orders,\n uint256 makerAssetFillAmount,\n bytes[] memory signatures\n )\n internal\n returns (FillResults memory totalFillResults)\n {\n bytes memory makerAssetData = orders[0].makerAssetData;\n bytes memory wethAssetData = WETH_ASSET_DATA;\n\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n\n // We assume that asset being bought by taker is the same for each order.\n // We assume that asset being sold by taker is WETH for each order.\n orders[i].makerAssetData = makerAssetData;\n orders[i].takerAssetData = wethAssetData;\n\n // Calculate the remaining amount of makerAsset to buy\n uint256 remainingMakerAssetFillAmount = safeSub(makerAssetFillAmount, totalFillResults.makerAssetFilledAmount);\n\n // Convert the remaining amount of makerAsset to buy into remaining amount\n // of takerAsset to sell, assuming entire amount can be sold in the current order\n uint256 remainingTakerAssetFillAmount = getPartialAmountFloor(\n orders[i].takerAssetAmount,\n orders[i].makerAssetAmount,\n remainingMakerAssetFillAmount\n );\n\n // Attempt to sell the remaining amount of takerAsset\n FillResults memory singleFillResults = fillOrderNoThrow(\n orders[i],\n remainingTakerAssetFillAmount,\n signatures[i]\n );\n\n // Update amounts filled and fees paid by maker and taker\n addFillResults(totalFillResults, singleFillResults);\n\n // Stop execution if the entire amount of makerAsset has been bought\n uint256 makerAssetFilledAmount = totalFillResults.makerAssetFilledAmount;\n if (makerAssetFilledAmount >= makerAssetFillAmount) {\n break;\n }\n }\n\n require(\n makerAssetFilledAmount >= makerAssetFillAmount,\n \"COMPLETE_FILL_FAILED\"\n );\n return totalFillResults;\n }\n\n /// @dev Buys zrxBuyAmount of ZRX fee tokens, taking into account ZRX fees for each order. This will guarantee\n /// that at least zrxBuyAmount of ZRX is purchased (sometimes slightly over due to rounding issues).\n /// It is possible that a request to buy 200 ZRX will require purchasing 202 ZRX\n /// as 2 ZRX is required to purchase the 200 ZRX fee tokens. This guarantees at least 200 ZRX for future purchases.\n /// The asset being sold by taker must always be WETH. \n /// @param orders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset.\n /// @param zrxBuyAmount Desired amount of ZRX to buy.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return totalFillResults Amounts filled and fees paid by maker and taker.\n function marketBuyExactZrxWithWeth(\n LibOrder.Order[] memory orders,\n uint256 zrxBuyAmount,\n bytes[] memory signatures\n )\n internal\n returns (FillResults memory totalFillResults)\n {\n // Do nothing if zrxBuyAmount == 0\n if (zrxBuyAmount == 0) {\n return totalFillResults;\n }\n\n bytes memory zrxAssetData = ZRX_ASSET_DATA;\n bytes memory wethAssetData = WETH_ASSET_DATA;\n uint256 zrxPurchased = 0;\n\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n\n // All of these are ZRX/WETH, so we can drop the respective assetData from calldata.\n orders[i].makerAssetData = zrxAssetData;\n orders[i].takerAssetData = wethAssetData;\n\n // Calculate the remaining amount of ZRX to buy.\n uint256 remainingZrxBuyAmount = safeSub(zrxBuyAmount, zrxPurchased);\n\n // Convert the remaining amount of ZRX to buy into remaining amount\n // of WETH to sell, assuming entire amount can be sold in the current order.\n uint256 remainingWethSellAmount = getPartialAmountFloor(\n orders[i].takerAssetAmount,\n safeSub(orders[i].makerAssetAmount, orders[i].takerFee), // our exchange rate after fees \n remainingZrxBuyAmount\n );\n\n // Attempt to sell the remaining amount of WETH.\n FillResults memory singleFillResult = fillOrderNoThrow(\n orders[i],\n safeAdd(remainingWethSellAmount, 1), // we add 1 wei to the fill amount to make up for rounding errors\n signatures[i]\n );\n\n // Update amounts filled and fees paid by maker and taker.\n addFillResults(totalFillResults, singleFillResult);\n zrxPurchased = safeSub(totalFillResults.makerAssetFilledAmount, totalFillResults.takerFeePaid);\n\n // Stop execution if the entire amount of ZRX has been bought.\n if (zrxPurchased >= zrxBuyAmount) {\n break;\n }\n }\n\n require(\n zrxPurchased >= zrxBuyAmount,\n \"COMPLETE_FILL_FAILED\"\n );\n return totalFillResults;\n }\n}\n", - "2.0.0/extensions/Forwarder/MixinForwarderCore.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"./libs/LibConstants.sol\";\nimport \"./mixins/MWeth.sol\";\nimport \"./mixins/MAssets.sol\";\nimport \"./mixins/MExchangeWrapper.sol\";\nimport \"./interfaces/IForwarderCore.sol\";\nimport \"../../utils/LibBytes/LibBytes.sol\";\nimport \"../../protocol/Exchange/libs/LibOrder.sol\";\nimport \"../../protocol/Exchange/libs/LibFillResults.sol\";\nimport \"../../protocol/Exchange/libs/LibMath.sol\";\n\n\ncontract MixinForwarderCore is\n LibFillResults,\n LibMath,\n LibConstants,\n MWeth,\n MAssets,\n MExchangeWrapper,\n IForwarderCore\n{\n using LibBytes for bytes;\n\n /// @dev Constructor approves ERC20 proxy to transfer ZRX and WETH on this contract's behalf.\n constructor ()\n public\n {\n address proxyAddress = EXCHANGE.getAssetProxy(ERC20_DATA_ID);\n require(\n proxyAddress != address(0),\n \"UNREGISTERED_ASSET_PROXY\"\n );\n ETHER_TOKEN.approve(proxyAddress, MAX_UINT);\n ZRX_TOKEN.approve(proxyAddress, MAX_UINT);\n }\n\n /// @dev Purchases as much of orders' makerAssets as possible by selling up to 95% of transaction's ETH value.\n /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract.\n /// 5% of ETH value is reserved for paying fees to order feeRecipients (in ZRX) and forwarding contract feeRecipient (in ETH).\n /// Any ETH not spent will be refunded to sender.\n /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset. \n /// @param signatures Proofs that orders have been created by makers.\n /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees.\n /// @param feeSignatures Proofs that feeOrders have been created by makers.\n /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.\n /// @param feeRecipient Address that will receive ETH when orders are filled.\n /// @return Amounts filled and fees paid by maker and taker for both sets of orders.\n function marketSellOrdersWithEth(\n LibOrder.Order[] memory orders,\n bytes[] memory signatures,\n LibOrder.Order[] memory feeOrders,\n bytes[] memory feeSignatures,\n uint256 feePercentage,\n address feeRecipient\n )\n public\n payable\n returns (\n FillResults memory orderFillResults,\n FillResults memory feeOrderFillResults\n )\n {\n // Convert ETH to WETH.\n convertEthToWeth();\n\n uint256 wethSellAmount;\n uint256 zrxBuyAmount;\n uint256 makerAssetAmountPurchased;\n if (orders[0].makerAssetData.equals(ZRX_ASSET_DATA)) {\n // Calculate amount of WETH that won't be spent on ETH fees.\n wethSellAmount = getPartialAmountFloor(\n PERCENTAGE_DENOMINATOR,\n safeAdd(PERCENTAGE_DENOMINATOR, feePercentage),\n msg.value\n );\n // Market sell available WETH.\n // ZRX fees are paid with this contract's balance.\n orderFillResults = marketSellWeth(\n orders,\n wethSellAmount,\n signatures\n );\n // The fee amount must be deducted from the amount transfered back to sender.\n makerAssetAmountPurchased = safeSub(orderFillResults.makerAssetFilledAmount, orderFillResults.takerFeePaid);\n } else {\n // 5% of WETH is reserved for filling feeOrders and paying feeRecipient.\n wethSellAmount = getPartialAmountFloor(\n MAX_WETH_FILL_PERCENTAGE,\n PERCENTAGE_DENOMINATOR,\n msg.value\n );\n // Market sell 95% of WETH.\n // ZRX fees are payed with this contract's balance.\n orderFillResults = marketSellWeth(\n orders,\n wethSellAmount,\n signatures\n );\n // Buy back all ZRX spent on fees.\n zrxBuyAmount = orderFillResults.takerFeePaid;\n feeOrderFillResults = marketBuyExactZrxWithWeth(\n feeOrders,\n zrxBuyAmount,\n feeSignatures\n );\n makerAssetAmountPurchased = orderFillResults.makerAssetFilledAmount;\n }\n\n // Transfer feePercentage of total ETH spent on primary orders to feeRecipient.\n // Refund remaining ETH to msg.sender.\n transferEthFeeAndRefund(\n orderFillResults.takerAssetFilledAmount,\n feeOrderFillResults.takerAssetFilledAmount,\n feePercentage,\n feeRecipient\n );\n\n // Transfer purchased assets to msg.sender.\n transferAssetToSender(orders[0].makerAssetData, makerAssetAmountPurchased);\n }\n\n /// @dev Attempt to purchase makerAssetFillAmount of makerAsset by selling ETH provided with transaction.\n /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract.\n /// Any ETH not spent will be refunded to sender.\n /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset. \n /// @param makerAssetFillAmount Desired amount of makerAsset to purchase.\n /// @param signatures Proofs that orders have been created by makers.\n /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees.\n /// @param feeSignatures Proofs that feeOrders have been created by makers.\n /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.\n /// @param feeRecipient Address that will receive ETH when orders are filled.\n /// @return Amounts filled and fees paid by maker and taker for both sets of orders.\n function marketBuyOrdersWithEth(\n LibOrder.Order[] memory orders,\n uint256 makerAssetFillAmount,\n bytes[] memory signatures,\n LibOrder.Order[] memory feeOrders,\n bytes[] memory feeSignatures,\n uint256 feePercentage,\n address feeRecipient\n )\n public\n payable\n returns (\n FillResults memory orderFillResults,\n FillResults memory feeOrderFillResults\n )\n {\n // Convert ETH to WETH.\n convertEthToWeth();\n\n uint256 zrxBuyAmount;\n uint256 makerAssetAmountPurchased;\n if (orders[0].makerAssetData.equals(ZRX_ASSET_DATA)) {\n // If the makerAsset is ZRX, it is not necessary to pay fees out of this\n // contracts's ZRX balance because fees are factored into the price of the order.\n orderFillResults = marketBuyExactZrxWithWeth(\n orders,\n makerAssetFillAmount,\n signatures\n );\n // The fee amount must be deducted from the amount transfered back to sender.\n makerAssetAmountPurchased = safeSub(orderFillResults.makerAssetFilledAmount, orderFillResults.takerFeePaid);\n } else {\n // Attemp to purchase desired amount of makerAsset.\n // ZRX fees are payed with this contract's balance.\n orderFillResults = marketBuyExactAmountWithWeth(\n orders,\n makerAssetFillAmount,\n signatures\n );\n // Buy back all ZRX spent on fees.\n zrxBuyAmount = orderFillResults.takerFeePaid;\n feeOrderFillResults = marketBuyExactZrxWithWeth(\n feeOrders,\n zrxBuyAmount,\n feeSignatures\n );\n makerAssetAmountPurchased = orderFillResults.makerAssetFilledAmount;\n }\n\n // Transfer feePercentage of total ETH spent on primary orders to feeRecipient.\n // Refund remaining ETH to msg.sender.\n transferEthFeeAndRefund(\n orderFillResults.takerAssetFilledAmount,\n feeOrderFillResults.takerAssetFilledAmount,\n feePercentage,\n feeRecipient\n );\n\n // Transfer purchased assets to msg.sender.\n transferAssetToSender(orders[0].makerAssetData, makerAssetAmountPurchased);\n }\n}\n", - "2.0.0/extensions/Forwarder/MixinWeth.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../protocol/Exchange/libs/LibMath.sol\";\nimport \"./libs/LibConstants.sol\";\nimport \"./mixins/MWeth.sol\";\n\n\ncontract MixinWeth is\n LibMath,\n LibConstants,\n MWeth\n{\n /// @dev Default payabale function, this allows us to withdraw WETH\n function ()\n public\n payable\n {\n require(\n msg.sender == address(ETHER_TOKEN),\n \"DEFAULT_FUNCTION_WETH_CONTRACT_ONLY\"\n );\n }\n\n /// @dev Converts message call's ETH value into WETH.\n function convertEthToWeth()\n internal\n {\n require(\n msg.value > 0,\n \"INVALID_MSG_VALUE\"\n );\n ETHER_TOKEN.deposit.value(msg.value)();\n }\n\n /// @dev Transfers feePercentage of WETH spent on primary orders to feeRecipient.\n /// Refunds any excess ETH to msg.sender.\n /// @param wethSoldExcludingFeeOrders Amount of WETH sold when filling primary orders.\n /// @param wethSoldForZrx Amount of WETH sold when purchasing ZRX required for primary order fees.\n /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.\n /// @param feeRecipient Address that will receive ETH when orders are filled.\n function transferEthFeeAndRefund(\n uint256 wethSoldExcludingFeeOrders,\n uint256 wethSoldForZrx,\n uint256 feePercentage,\n address feeRecipient\n )\n internal\n {\n // Ensure feePercentage is less than 5%.\n require(\n feePercentage <= MAX_FEE_PERCENTAGE,\n \"FEE_PERCENTAGE_TOO_LARGE\"\n );\n\n // Ensure that no extra WETH owned by this contract has been sold.\n uint256 wethSold = safeAdd(wethSoldExcludingFeeOrders, wethSoldForZrx);\n require(\n wethSold <= msg.value,\n \"OVERSOLD_WETH\"\n );\n\n // Calculate amount of WETH that hasn't been sold.\n uint256 wethRemaining = safeSub(msg.value, wethSold);\n\n // Calculate ETH fee to pay to feeRecipient.\n uint256 ethFee = getPartialAmountFloor(\n feePercentage,\n PERCENTAGE_DENOMINATOR,\n wethSoldExcludingFeeOrders\n );\n\n // Ensure fee is less than amount of WETH remaining.\n require(\n ethFee <= wethRemaining,\n \"INSUFFICIENT_ETH_REMAINING\"\n );\n \n // Do nothing if no WETH remaining\n if (wethRemaining > 0) {\n // Convert remaining WETH to ETH\n ETHER_TOKEN.withdraw(wethRemaining);\n\n // Pay ETH to feeRecipient\n if (ethFee > 0) {\n feeRecipient.transfer(ethFee);\n }\n\n // Refund remaining ETH to msg.sender.\n uint256 ethRefund = safeSub(wethRemaining, ethFee);\n if (ethRefund > 0) {\n msg.sender.transfer(ethRefund);\n }\n }\n }\n}\n", - "2.0.0/extensions/Forwarder/interfaces/IAssets.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\ncontract IAssets {\n\n /// @dev Withdraws assets from this contract. The contract requires a ZRX balance in order to \n /// function optimally, and this function allows the ZRX to be withdrawn by owner. It may also be\n /// used to withdraw assets that were accidentally sent to this contract.\n /// @param assetData Byte array encoded for the respective asset proxy.\n /// @param amount Amount of ERC20 token to withdraw.\n function withdrawAsset(\n bytes assetData,\n uint256 amount\n )\n external;\n}\n", - "2.0.0/extensions/Forwarder/interfaces/IForwarderCore.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../../../protocol/Exchange/libs/LibOrder.sol\";\nimport \"../../../protocol/Exchange/libs/LibFillResults.sol\";\n\n\ncontract IForwarderCore {\n\n /// @dev Purchases as much of orders' makerAssets as possible by selling up to 95% of transaction's ETH value.\n /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract.\n /// 5% of ETH value is reserved for paying fees to order feeRecipients (in ZRX) and forwarding contract feeRecipient (in ETH).\n /// Any ETH not spent will be refunded to sender.\n /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset. \n /// @param signatures Proofs that orders have been created by makers.\n /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees.\n /// @param feeSignatures Proofs that feeOrders have been created by makers.\n /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.\n /// @param feeRecipient Address that will receive ETH when orders are filled.\n /// @return Amounts filled and fees paid by maker and taker for both sets of orders.\n function marketSellOrdersWithEth(\n LibOrder.Order[] memory orders,\n bytes[] memory signatures,\n LibOrder.Order[] memory feeOrders,\n bytes[] memory feeSignatures,\n uint256 feePercentage,\n address feeRecipient\n )\n public\n payable\n returns (\n LibFillResults.FillResults memory orderFillResults,\n LibFillResults.FillResults memory feeOrderFillResults\n );\n\n /// @dev Attempt to purchase makerAssetFillAmount of makerAsset by selling ETH provided with transaction.\n /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract.\n /// Any ETH not spent will be refunded to sender.\n /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset. \n /// @param makerAssetFillAmount Desired amount of makerAsset to purchase.\n /// @param signatures Proofs that orders have been created by makers.\n /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees.\n /// @param feeSignatures Proofs that feeOrders have been created by makers.\n /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.\n /// @param feeRecipient Address that will receive ETH when orders are filled.\n /// @return Amounts filled and fees paid by maker and taker for both sets of orders.\n function marketBuyOrdersWithEth(\n LibOrder.Order[] memory orders,\n uint256 makerAssetFillAmount,\n bytes[] memory signatures,\n LibOrder.Order[] memory feeOrders,\n bytes[] memory feeSignatures,\n uint256 feePercentage,\n address feeRecipient\n )\n public\n payable\n returns (\n LibFillResults.FillResults memory orderFillResults,\n LibFillResults.FillResults memory feeOrderFillResults\n );\n}\n", - "2.0.0/extensions/Forwarder/libs/LibConstants.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../../utils/LibBytes/LibBytes.sol\";\nimport \"../../../protocol/Exchange/interfaces/IExchange.sol\";\nimport \"../../../tokens/EtherToken/IEtherToken.sol\";\nimport \"../../../tokens/ERC20Token/IERC20Token.sol\";\n\n\ncontract LibConstants {\n\n using LibBytes for bytes;\n\n bytes4 constant internal ERC20_DATA_ID = bytes4(keccak256(\"ERC20Token(address)\"));\n bytes4 constant internal ERC721_DATA_ID = bytes4(keccak256(\"ERC721Token(address,uint256)\"));\n uint256 constant internal MAX_UINT = 2**256 - 1;\n uint256 constant internal PERCENTAGE_DENOMINATOR = 10**18; \n uint256 constant internal MAX_FEE_PERCENTAGE = 5 * PERCENTAGE_DENOMINATOR / 100; // 5%\n uint256 constant internal MAX_WETH_FILL_PERCENTAGE = 95 * PERCENTAGE_DENOMINATOR / 100; // 95%\n \n // solhint-disable var-name-mixedcase\n IExchange internal EXCHANGE;\n IEtherToken internal ETHER_TOKEN;\n IERC20Token internal ZRX_TOKEN;\n bytes internal ZRX_ASSET_DATA;\n bytes internal WETH_ASSET_DATA;\n // solhint-enable var-name-mixedcase\n\n constructor (\n address _exchange,\n bytes memory _zrxAssetData,\n bytes memory _wethAssetData\n )\n public\n {\n EXCHANGE = IExchange(_exchange);\n ZRX_ASSET_DATA = _zrxAssetData;\n WETH_ASSET_DATA = _wethAssetData;\n\n address etherToken = _wethAssetData.readAddress(16);\n address zrxToken = _zrxAssetData.readAddress(16);\n ETHER_TOKEN = IEtherToken(etherToken);\n ZRX_TOKEN = IERC20Token(zrxToken);\n }\n}\n", - "2.0.0/extensions/Forwarder/mixins/MAssets.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../interfaces/IAssets.sol\";\n\n\ncontract MAssets is\n IAssets\n{\n /// @dev Transfers given amount of asset to sender.\n /// @param assetData Byte array encoded for the respective asset proxy.\n /// @param amount Amount of asset to transfer to sender.\n function transferAssetToSender(\n bytes memory assetData,\n uint256 amount\n )\n internal;\n\n /// @dev Decodes ERC20 assetData and transfers given amount to sender.\n /// @param assetData Byte array encoded for the respective asset proxy.\n /// @param amount Amount of asset to transfer to sender.\n function transferERC20Token(\n bytes memory assetData,\n uint256 amount\n )\n internal;\n\n /// @dev Decodes ERC721 assetData and transfers given amount to sender.\n /// @param assetData Byte array encoded for the respective asset proxy.\n /// @param amount Amount of asset to transfer to sender.\n function transferERC721Token(\n bytes memory assetData,\n uint256 amount\n )\n internal;\n}\n", - "2.0.0/extensions/Forwarder/mixins/MExchangeWrapper.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../../../protocol/Exchange/libs/LibOrder.sol\";\nimport \"../../../protocol/Exchange/libs/LibFillResults.sol\";\n\n\ncontract MExchangeWrapper {\n\n /// @dev Fills the input order.\n /// Returns false if the transaction would otherwise revert.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n /// @return Amounts filled and fees paid by maker and taker.\n function fillOrderNoThrow(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n internal\n returns (LibFillResults.FillResults memory fillResults);\n\n /// @dev Synchronously executes multiple calls of fillOrder until total amount of WETH has been sold by taker.\n /// Returns false if the transaction would otherwise revert.\n /// @param orders Array of order specifications.\n /// @param wethSellAmount Desired amount of WETH to sell.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketSellWeth(\n LibOrder.Order[] memory orders,\n uint256 wethSellAmount,\n bytes[] memory signatures\n )\n internal\n returns (LibFillResults.FillResults memory totalFillResults);\n\n /// @dev Synchronously executes multiple fill orders in a single transaction until total amount is bought by taker.\n /// Returns false if the transaction would otherwise revert.\n /// The asset being sold by taker must always be WETH.\n /// @param orders Array of order specifications.\n /// @param makerAssetFillAmount Desired amount of makerAsset to buy.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketBuyExactAmountWithWeth(\n LibOrder.Order[] memory orders,\n uint256 makerAssetFillAmount,\n bytes[] memory signatures\n )\n internal\n returns (LibFillResults.FillResults memory totalFillResults);\n\n /// @dev Buys zrxBuyAmount of ZRX fee tokens, taking into account ZRX fees for each order. This will guarantee\n /// that at least zrxBuyAmount of ZRX is purchased (sometimes slightly over due to rounding issues).\n /// It is possible that a request to buy 200 ZRX will require purchasing 202 ZRX\n /// as 2 ZRX is required to purchase the 200 ZRX fee tokens. This guarantees at least 200 ZRX for future purchases.\n /// The asset being sold by taker must always be WETH. \n /// @param orders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset.\n /// @param zrxBuyAmount Desired amount of ZRX to buy.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return totalFillResults Amounts filled and fees paid by maker and taker.\n function marketBuyExactZrxWithWeth(\n LibOrder.Order[] memory orders,\n uint256 zrxBuyAmount,\n bytes[] memory signatures\n )\n internal\n returns (LibFillResults.FillResults memory totalFillResults);\n}\n", - "2.0.0/extensions/Forwarder/mixins/MWeth.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\ncontract MWeth {\n\n /// @dev Converts message call's ETH value into WETH.\n function convertEthToWeth()\n internal;\n\n /// @dev Transfers feePercentage of WETH spent on primary orders to feeRecipient.\n /// Refunds any excess ETH to msg.sender.\n /// @param wethSoldExcludingFeeOrders Amount of WETH sold when filling primary orders.\n /// @param wethSoldForZrx Amount of WETH sold when purchasing ZRX required for primary order fees.\n /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.\n /// @param feeRecipient Address that will receive ETH when orders are filled.\n function transferEthFeeAndRefund(\n uint256 wethSoldExcludingFeeOrders,\n uint256 wethSoldForZrx,\n uint256 feePercentage,\n address feeRecipient\n )\n internal;\n}\n", - "2.0.0/extensions/OrderValidator/OrderValidator.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../../protocol/Exchange/interfaces/IExchange.sol\";\nimport \"../../protocol/Exchange/libs/LibOrder.sol\";\nimport \"../../tokens/ERC20Token/IERC20Token.sol\";\nimport \"../../tokens/ERC721Token/IERC721Token.sol\";\nimport \"../../utils/LibBytes/LibBytes.sol\";\n\n\ncontract OrderValidator {\n\n using LibBytes for bytes;\n\n bytes4 constant internal ERC20_DATA_ID = bytes4(keccak256(\"ERC20Token(address)\"));\n bytes4 constant internal ERC721_DATA_ID = bytes4(keccak256(\"ERC721Token(address,uint256)\"));\n\n struct TraderInfo {\n uint256 makerBalance; // Maker's balance of makerAsset\n uint256 makerAllowance; // Maker's allowance to corresponding AssetProxy\n uint256 takerBalance; // Taker's balance of takerAsset\n uint256 takerAllowance; // Taker's allowance to corresponding AssetProxy\n uint256 makerZrxBalance; // Maker's balance of ZRX\n uint256 makerZrxAllowance; // Maker's allowance of ZRX to ERC20Proxy\n uint256 takerZrxBalance; // Taker's balance of ZRX\n uint256 takerZrxAllowance; // Taker's allowance of ZRX to ERC20Proxy\n }\n\n // solhint-disable var-name-mixedcase\n IExchange internal EXCHANGE;\n bytes internal ZRX_ASSET_DATA;\n // solhint-enable var-name-mixedcase\n\n constructor (address _exchange, bytes memory _zrxAssetData)\n public\n {\n EXCHANGE = IExchange(_exchange);\n ZRX_ASSET_DATA = _zrxAssetData;\n }\n\n /// @dev Fetches information for order and maker/taker of order.\n /// @param order The order structure.\n /// @param takerAddress Address that will be filling the order.\n /// @return OrderInfo and TraderInfo instances for given order.\n function getOrderAndTraderInfo(LibOrder.Order memory order, address takerAddress)\n public\n view\n returns (LibOrder.OrderInfo memory orderInfo, TraderInfo memory traderInfo)\n {\n orderInfo = EXCHANGE.getOrderInfo(order);\n traderInfo = getTraderInfo(order, takerAddress);\n return (orderInfo, traderInfo);\n }\n\n /// @dev Fetches information for all passed in orders and the makers/takers of each order.\n /// @param orders Array of order specifications.\n /// @param takerAddresses Array of taker addresses corresponding to each order.\n /// @return Arrays of OrderInfo and TraderInfo instances that correspond to each order.\n function getOrdersAndTradersInfo(LibOrder.Order[] memory orders, address[] memory takerAddresses)\n public\n view\n returns (LibOrder.OrderInfo[] memory ordersInfo, TraderInfo[] memory tradersInfo)\n {\n ordersInfo = EXCHANGE.getOrdersInfo(orders);\n tradersInfo = getTradersInfo(orders, takerAddresses);\n return (ordersInfo, tradersInfo);\n }\n\n /// @dev Fetches balance and allowances for maker and taker of order.\n /// @param order The order structure.\n /// @param takerAddress Address that will be filling the order.\n /// @return Balances and allowances of maker and taker of order.\n function getTraderInfo(LibOrder.Order memory order, address takerAddress)\n public\n view\n returns (TraderInfo memory traderInfo)\n {\n (traderInfo.makerBalance, traderInfo.makerAllowance) = getBalanceAndAllowance(order.makerAddress, order.makerAssetData);\n (traderInfo.takerBalance, traderInfo.takerAllowance) = getBalanceAndAllowance(takerAddress, order.takerAssetData);\n bytes memory zrxAssetData = ZRX_ASSET_DATA;\n (traderInfo.makerZrxBalance, traderInfo.makerZrxAllowance) = getBalanceAndAllowance(order.makerAddress, zrxAssetData);\n (traderInfo.takerZrxBalance, traderInfo.takerZrxAllowance) = getBalanceAndAllowance(takerAddress, zrxAssetData);\n return traderInfo;\n }\n\n /// @dev Fetches balances and allowances of maker and taker for each provided order.\n /// @param orders Array of order specifications.\n /// @param takerAddresses Array of taker addresses corresponding to each order.\n /// @return Array of balances and allowances for maker and taker of each order.\n function getTradersInfo(LibOrder.Order[] memory orders, address[] memory takerAddresses)\n public\n view\n returns (TraderInfo[] memory)\n {\n uint256 ordersLength = orders.length;\n TraderInfo[] memory tradersInfo = new TraderInfo[](ordersLength);\n for (uint256 i = 0; i != ordersLength; i++) {\n tradersInfo[i] = getTraderInfo(orders[i], takerAddresses[i]);\n }\n return tradersInfo;\n }\n\n /// @dev Fetches token balances and allowances of an address to given assetProxy. Supports ERC20 and ERC721.\n /// @param target Address to fetch balances and allowances of.\n /// @param assetData Encoded data that can be decoded by a specified proxy contract when transferring asset.\n /// @return Balance of asset and allowance set to given proxy of asset.\n /// For ERC721 tokens, these values will always be 1 or 0.\n function getBalanceAndAllowance(address target, bytes memory assetData)\n public\n view\n returns (uint256 balance, uint256 allowance)\n {\n bytes4 assetProxyId = assetData.readBytes4(0);\n address token = assetData.readAddress(16);\n address assetProxy = EXCHANGE.getAssetProxy(assetProxyId);\n\n if (assetProxyId == ERC20_DATA_ID) {\n // Query balance\n balance = IERC20Token(token).balanceOf(target);\n\n // Query allowance\n allowance = IERC20Token(token).allowance(target, assetProxy);\n } else if (assetProxyId == ERC721_DATA_ID) {\n uint256 tokenId = assetData.readUint256(36);\n\n // Query owner of tokenId\n address owner = getERC721TokenOwner(token, tokenId);\n\n // Set balance to 1 if tokenId is owned by target\n balance = target == owner ? 1 : 0;\n\n // Check if ERC721Proxy is approved to spend tokenId\n bool isApproved = IERC721Token(token).isApprovedForAll(target, assetProxy) || IERC721Token(token).getApproved(tokenId) == assetProxy;\n \n // Set alowance to 1 if ERC721Proxy is approved to spend tokenId\n allowance = isApproved ? 1 : 0;\n } else {\n revert(\"UNSUPPORTED_ASSET_PROXY\");\n }\n return (balance, allowance);\n }\n\n /// @dev Fetches token balances and allowances of an address for each given assetProxy. Supports ERC20 and ERC721.\n /// @param target Address to fetch balances and allowances of.\n /// @param assetData Array of encoded byte arrays that can be decoded by a specified proxy contract when transferring asset.\n /// @return Balances and allowances of assets.\n /// For ERC721 tokens, these values will always be 1 or 0.\n function getBalancesAndAllowances(address target, bytes[] memory assetData)\n public\n view\n returns (uint256[] memory, uint256[] memory)\n {\n uint256 length = assetData.length;\n uint256[] memory balances = new uint256[](length);\n uint256[] memory allowances = new uint256[](length);\n for (uint256 i = 0; i != length; i++) {\n (balances[i], allowances[i]) = getBalanceAndAllowance(target, assetData[i]);\n }\n return (balances, allowances);\n }\n\n /// @dev Calls `token.ownerOf(tokenId)`, but returns a null owner instead of reverting on an unowned token.\n /// @param token Address of ERC721 token.\n /// @param tokenId The identifier for the specific NFT.\n /// @return Owner of tokenId or null address if unowned.\n function getERC721TokenOwner(address token, uint256 tokenId)\n public\n view\n returns (address owner)\n {\n assembly {\n // load free memory pointer\n let cdStart := mload(64)\n\n // bytes4(keccak256(ownerOf(uint256))) = 0x6352211e\n mstore(cdStart, 0x6352211e00000000000000000000000000000000000000000000000000000000)\n mstore(add(cdStart, 4), tokenId)\n\n // staticcall `ownerOf(tokenId)`\n // `ownerOf` will revert if tokenId is not owned\n let success := staticcall(\n gas, // forward all gas\n token, // call token contract\n cdStart, // start of calldata\n 36, // length of input is 36 bytes\n cdStart, // write output over input\n 32 // size of output is 32 bytes\n )\n\n // Success implies that tokenId is owned\n // Copy owner from return data if successful\n if success {\n owner := mload(cdStart)\n } \n }\n\n // Owner initialized to address(0), no need to modify if call is unsuccessful\n return owner;\n }\n}\n", - "2.0.0/multisig/MultiSigWallet.sol": "// solhint-disable\npragma solidity ^0.4.15;\n\n\n/// @title Multisignature wallet - Allows multiple parties to agree on transactions before execution.\n/// @author Stefan George - \ncontract MultiSigWallet {\n\n /*\n * Events\n */\n event Confirmation(address indexed sender, uint indexed transactionId);\n event Revocation(address indexed sender, uint indexed transactionId);\n event Submission(uint indexed transactionId);\n event Execution(uint indexed transactionId);\n event ExecutionFailure(uint indexed transactionId);\n event Deposit(address indexed sender, uint value);\n event OwnerAddition(address indexed owner);\n event OwnerRemoval(address indexed owner);\n event RequirementChange(uint required);\n\n /*\n * Constants\n */\n uint constant public MAX_OWNER_COUNT = 50;\n\n /*\n * Storage\n */\n mapping (uint => Transaction) public transactions;\n mapping (uint => mapping (address => bool)) public confirmations;\n mapping (address => bool) public isOwner;\n address[] public owners;\n uint public required;\n uint public transactionCount;\n\n struct Transaction {\n address destination;\n uint value;\n bytes data;\n bool executed;\n }\n\n /*\n * Modifiers\n */\n modifier onlyWallet() {\n require(msg.sender == address(this));\n _;\n }\n\n modifier ownerDoesNotExist(address owner) {\n require(!isOwner[owner]);\n _;\n }\n\n modifier ownerExists(address owner) {\n require(isOwner[owner]);\n _;\n }\n\n modifier transactionExists(uint transactionId) {\n require(transactions[transactionId].destination != 0);\n _;\n }\n\n modifier confirmed(uint transactionId, address owner) {\n require(confirmations[transactionId][owner]);\n _;\n }\n\n modifier notConfirmed(uint transactionId, address owner) {\n require(!confirmations[transactionId][owner]);\n _;\n }\n\n modifier notExecuted(uint transactionId) {\n require(!transactions[transactionId].executed);\n _;\n }\n\n modifier notNull(address _address) {\n require(_address != 0);\n _;\n }\n\n modifier validRequirement(uint ownerCount, uint _required) {\n require(ownerCount <= MAX_OWNER_COUNT\n && _required <= ownerCount\n && _required != 0\n && ownerCount != 0);\n _;\n }\n\n /// @dev Fallback function allows to deposit ether.\n function()\n payable\n {\n if (msg.value > 0)\n Deposit(msg.sender, msg.value);\n }\n\n /*\n * Public functions\n */\n /// @dev Contract constructor sets initial owners and required number of confirmations.\n /// @param _owners List of initial owners.\n /// @param _required Number of required confirmations.\n function MultiSigWallet(address[] _owners, uint _required)\n public\n validRequirement(_owners.length, _required)\n {\n for (uint i=0; i<_owners.length; i++) {\n require(!isOwner[_owners[i]] && _owners[i] != 0);\n isOwner[_owners[i]] = true;\n }\n owners = _owners;\n required = _required;\n }\n\n /// @dev Allows to add a new owner. Transaction has to be sent by wallet.\n /// @param owner Address of new owner.\n function addOwner(address owner)\n public\n onlyWallet\n ownerDoesNotExist(owner)\n notNull(owner)\n validRequirement(owners.length + 1, required)\n {\n isOwner[owner] = true;\n owners.push(owner);\n OwnerAddition(owner);\n }\n\n /// @dev Allows to remove an owner. Transaction has to be sent by wallet.\n /// @param owner Address of owner.\n function removeOwner(address owner)\n public\n onlyWallet\n ownerExists(owner)\n {\n isOwner[owner] = false;\n for (uint i=0; i owners.length)\n changeRequirement(owners.length);\n OwnerRemoval(owner);\n }\n\n /// @dev Allows to replace an owner with a new owner. Transaction has to be sent by wallet.\n /// @param owner Address of owner to be replaced.\n /// @param newOwner Address of new owner.\n function replaceOwner(address owner, address newOwner)\n public\n onlyWallet\n ownerExists(owner)\n ownerDoesNotExist(newOwner)\n {\n for (uint i=0; i\n// solhint-disable not-rely-on-time\ncontract MultiSigWalletWithTimeLock is\n MultiSigWallet\n{\n event ConfirmationTimeSet(uint256 indexed transactionId, uint256 confirmationTime);\n event TimeLockChange(uint256 secondsTimeLocked);\n\n uint256 public secondsTimeLocked;\n\n mapping (uint256 => uint256) public confirmationTimes;\n\n modifier notFullyConfirmed(uint256 transactionId) {\n require(\n !isConfirmed(transactionId),\n \"TX_FULLY_CONFIRMED\"\n );\n _;\n }\n\n modifier fullyConfirmed(uint256 transactionId) {\n require(\n isConfirmed(transactionId),\n \"TX_NOT_FULLY_CONFIRMED\"\n );\n _;\n }\n\n modifier pastTimeLock(uint256 transactionId) {\n require(\n block.timestamp >= confirmationTimes[transactionId] + secondsTimeLocked,\n \"TIME_LOCK_INCOMPLETE\"\n );\n _;\n }\n\n /// @dev Contract constructor sets initial owners, required number of confirmations, and time lock.\n /// @param _owners List of initial owners.\n /// @param _required Number of required confirmations.\n /// @param _secondsTimeLocked Duration needed after a transaction is confirmed and before it becomes executable, in seconds.\n constructor (\n address[] _owners,\n uint256 _required,\n uint256 _secondsTimeLocked\n )\n public\n MultiSigWallet(_owners, _required)\n {\n secondsTimeLocked = _secondsTimeLocked;\n }\n\n /// @dev Changes the duration of the time lock for transactions.\n /// @param _secondsTimeLocked Duration needed after a transaction is confirmed and before it becomes executable, in seconds.\n function changeTimeLock(uint256 _secondsTimeLocked)\n public\n onlyWallet\n {\n secondsTimeLocked = _secondsTimeLocked;\n emit TimeLockChange(_secondsTimeLocked);\n }\n\n /// @dev Allows an owner to confirm a transaction.\n /// @param transactionId Transaction ID.\n function confirmTransaction(uint256 transactionId)\n public\n ownerExists(msg.sender)\n transactionExists(transactionId)\n notConfirmed(transactionId, msg.sender)\n notFullyConfirmed(transactionId)\n {\n confirmations[transactionId][msg.sender] = true;\n emit Confirmation(msg.sender, transactionId);\n if (isConfirmed(transactionId)) {\n setConfirmationTime(transactionId, block.timestamp);\n }\n }\n\n /// @dev Allows anyone to execute a confirmed transaction.\n /// @param transactionId Transaction ID.\n function executeTransaction(uint256 transactionId)\n public\n notExecuted(transactionId)\n fullyConfirmed(transactionId)\n pastTimeLock(transactionId)\n {\n Transaction storage txn = transactions[transactionId];\n txn.executed = true;\n if (external_call(txn.destination, txn.value, txn.data.length, txn.data)) {\n emit Execution(transactionId);\n } else {\n emit ExecutionFailure(transactionId);\n txn.executed = false;\n }\n }\n\n /// @dev Sets the time of when a submission first passed.\n function setConfirmationTime(uint256 transactionId, uint256 confirmationTime)\n internal\n {\n confirmationTimes[transactionId] = confirmationTime;\n emit ConfirmationTimeSet(transactionId, confirmationTime);\n }\n}\n", - "2.0.0/protocol/AssetProxy/ERC20Proxy.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"./MixinAuthorizable.sol\";\n\n\ncontract ERC20Proxy is\n MixinAuthorizable\n{\n // Id of this proxy.\n bytes4 constant internal PROXY_ID = bytes4(keccak256(\"ERC20Token(address)\"));\n \n // solhint-disable-next-line payable-fallback\n function () \n external\n {\n assembly {\n // The first 4 bytes of calldata holds the function selector\n let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000)\n\n // `transferFrom` will be called with the following parameters:\n // assetData Encoded byte array.\n // from Address to transfer asset from.\n // to Address to transfer asset to.\n // amount Amount of asset to transfer.\n // bytes4(keccak256(\"transferFrom(bytes,address,address,uint256)\")) = 0xa85e59e4\n if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) {\n\n // To lookup a value in a mapping, we load from the storage location keccak256(k, p),\n // where k is the key left padded to 32 bytes and p is the storage slot\n let start := mload(64)\n mstore(start, and(caller, 0xffffffffffffffffffffffffffffffffffffffff))\n mstore(add(start, 32), authorized_slot)\n\n // Revert if authorized[msg.sender] == false\n if iszero(sload(keccak256(start, 64))) {\n // Revert with `Error(\"SENDER_NOT_AUTHORIZED\")`\n mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)\n mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)\n mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000)\n mstore(96, 0)\n revert(0, 100)\n }\n\n // `transferFrom`.\n // The function is marked `external`, so no abi decodeding is done for\n // us. Instead, we expect the `calldata` memory to contain the\n // following:\n //\n // | Area | Offset | Length | Contents |\n // |----------|--------|---------|-------------------------------------|\n // | Header | 0 | 4 | function selector |\n // | Params | | 4 * 32 | function parameters: |\n // | | 4 | | 1. offset to assetData (*) |\n // | | 36 | | 2. from |\n // | | 68 | | 3. to |\n // | | 100 | | 4. amount |\n // | Data | | | assetData: |\n // | | 132 | 32 | assetData Length |\n // | | 164 | ** | assetData Contents |\n //\n // (*): offset is computed from start of function parameters, so offset\n // by an additional 4 bytes in the calldata.\n //\n // (**): see table below to compute length of assetData Contents\n //\n // WARNING: The ABIv2 specification allows additional padding between\n // the Params and Data section. This will result in a larger\n // offset to assetData.\n\n // Asset data itself is encoded as follows:\n //\n // | Area | Offset | Length | Contents |\n // |----------|--------|---------|-------------------------------------|\n // | Header | 0 | 4 | function selector |\n // | Params | | 1 * 32 | function parameters: |\n // | | 4 | 12 + 20 | 1. token address |\n\n // We construct calldata for the `token.transferFrom` ABI.\n // The layout of this calldata is in the table below.\n //\n // | Area | Offset | Length | Contents |\n // |----------|--------|---------|-------------------------------------|\n // | Header | 0 | 4 | function selector |\n // | Params | | 3 * 32 | function parameters: |\n // | | 4 | | 1. from |\n // | | 36 | | 2. to |\n // | | 68 | | 3. amount |\n\n /////// Read token address from calldata ///////\n // * The token address is stored in `assetData`.\n //\n // * The \"offset to assetData\" is stored at offset 4 in the calldata (table 1).\n // [assetDataOffsetFromParams = calldataload(4)]\n //\n // * Notes that the \"offset to assetData\" is relative to the \"Params\" area of calldata;\n // add 4 bytes to account for the length of the \"Header\" area (table 1).\n // [assetDataOffsetFromHeader = assetDataOffsetFromParams + 4]\n //\n // * The \"token address\" is offset 32+4=36 bytes into \"assetData\" (tables 1 & 2).\n // [tokenOffset = assetDataOffsetFromHeader + 36 = calldataload(4) + 4 + 36]\n let token := calldataload(add(calldataload(4), 40))\n \n /////// Setup Header Area ///////\n // This area holds the 4-byte `transferFrom` selector.\n // Any trailing data in transferFromSelector will be\n // overwritten in the next `mstore` call.\n mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000)\n \n /////// Setup Params Area ///////\n // We copy the fields `from`, `to` and `amount` in bulk\n // from our own calldata to the new calldata.\n calldatacopy(4, 36, 96)\n\n /////// Call `token.transferFrom` using the calldata ///////\n let success := call(\n gas, // forward all gas\n token, // call address of token contract\n 0, // don't send any ETH\n 0, // pointer to start of input\n 100, // length of input\n 0, // write output over input\n 32 // output size should be 32 bytes\n )\n\n /////// Check return data. ///////\n // If there is no return data, we assume the token incorrectly\n // does not return a bool. In this case we expect it to revert\n // on failure, which was handled above.\n // If the token does return data, we require that it is a single\n // nonzero 32 bytes value.\n // So the transfer succeeded if the call succeeded and either\n // returned nothing, or returned a non-zero 32 byte value. \n success := and(success, or(\n iszero(returndatasize),\n and(\n eq(returndatasize, 32),\n gt(mload(0), 0)\n )\n ))\n if success {\n return(0, 0)\n }\n \n // Revert with `Error(\"TRANSFER_FAILED\")`\n mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)\n mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)\n mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000)\n mstore(96, 0)\n revert(0, 100)\n }\n\n // Revert if undefined function is called\n revert(0, 0)\n }\n }\n\n /// @dev Gets the proxy id associated with the proxy address.\n /// @return Proxy id.\n function getProxyId()\n external\n pure\n returns (bytes4)\n {\n return PROXY_ID;\n }\n}\n", - "2.0.0/protocol/AssetProxy/ERC721Proxy.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"./MixinAuthorizable.sol\";\n\n\ncontract ERC721Proxy is\n MixinAuthorizable\n{\n // Id of this proxy.\n bytes4 constant internal PROXY_ID = bytes4(keccak256(\"ERC721Token(address,uint256)\"));\n\n // solhint-disable-next-line payable-fallback\n function () \n external\n {\n assembly {\n // The first 4 bytes of calldata holds the function selector\n let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000)\n\n // `transferFrom` will be called with the following parameters:\n // assetData Encoded byte array.\n // from Address to transfer asset from.\n // to Address to transfer asset to.\n // amount Amount of asset to transfer.\n // bytes4(keccak256(\"transferFrom(bytes,address,address,uint256)\")) = 0xa85e59e4\n if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) {\n\n // To lookup a value in a mapping, we load from the storage location keccak256(k, p),\n // where k is the key left padded to 32 bytes and p is the storage slot\n let start := mload(64)\n mstore(start, and(caller, 0xffffffffffffffffffffffffffffffffffffffff))\n mstore(add(start, 32), authorized_slot)\n\n // Revert if authorized[msg.sender] == false\n if iszero(sload(keccak256(start, 64))) {\n // Revert with `Error(\"SENDER_NOT_AUTHORIZED\")`\n mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)\n mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)\n mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000)\n mstore(96, 0)\n revert(0, 100)\n }\n\n // `transferFrom`.\n // The function is marked `external`, so no abi decodeding is done for\n // us. Instead, we expect the `calldata` memory to contain the\n // following:\n //\n // | Area | Offset | Length | Contents |\n // |----------|--------|---------|-------------------------------------|\n // | Header | 0 | 4 | function selector |\n // | Params | | 4 * 32 | function parameters: |\n // | | 4 | | 1. offset to assetData (*) |\n // | | 36 | | 2. from |\n // | | 68 | | 3. to |\n // | | 100 | | 4. amount |\n // | Data | | | assetData: |\n // | | 132 | 32 | assetData Length |\n // | | 164 | ** | assetData Contents |\n //\n // (*): offset is computed from start of function parameters, so offset\n // by an additional 4 bytes in the calldata.\n //\n // (**): see table below to compute length of assetData Contents\n //\n // WARNING: The ABIv2 specification allows additional padding between\n // the Params and Data section. This will result in a larger\n // offset to assetData.\n\n // Asset data itself is encoded as follows:\n //\n // | Area | Offset | Length | Contents |\n // |----------|--------|---------|-------------------------------------|\n // | Header | 0 | 4 | function selector |\n // | Params | | 2 * 32 | function parameters: |\n // | | 4 | 12 + 20 | 1. token address |\n // | | 36 | | 2. tokenId |\n \n // We construct calldata for the `token.transferFrom` ABI.\n // The layout of this calldata is in the table below.\n // \n // | Area | Offset | Length | Contents |\n // |----------|--------|---------|-------------------------------------|\n // | Header | 0 | 4 | function selector |\n // | Params | | 3 * 32 | function parameters: |\n // | | 4 | | 1. from |\n // | | 36 | | 2. to |\n // | | 68 | | 3. tokenId |\n\n // There exists only 1 of each token.\n // require(amount == 1, \"INVALID_AMOUNT\")\n if sub(calldataload(100), 1) {\n // Revert with `Error(\"INVALID_AMOUNT\")`\n mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)\n mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)\n mstore(64, 0x0000000e494e56414c49445f414d4f554e540000000000000000000000000000)\n mstore(96, 0)\n revert(0, 100)\n }\n\n /////// Setup Header Area ///////\n // This area holds the 4-byte `transferFrom` selector.\n // Any trailing data in transferFromSelector will be\n // overwritten in the next `mstore` call.\n mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000)\n \n /////// Setup Params Area ///////\n // We copy the fields `from` and `to` in bulk\n // from our own calldata to the new calldata.\n calldatacopy(4, 36, 64)\n\n // Copy `tokenId` field from our own calldata to the new calldata.\n let assetDataOffset := calldataload(4)\n calldatacopy(68, add(assetDataOffset, 72), 32)\n\n /////// Call `token.transferFrom` using the calldata ///////\n let token := calldataload(add(assetDataOffset, 40))\n let success := call(\n gas, // forward all gas\n token, // call address of token contract\n 0, // don't send any ETH\n 0, // pointer to start of input\n 100, // length of input\n 0, // write output to null\n 0 // output size is 0 bytes\n )\n if success {\n return(0, 0)\n }\n \n // Revert with `Error(\"TRANSFER_FAILED\")`\n mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)\n mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)\n mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000)\n mstore(96, 0)\n revert(0, 100)\n }\n\n // Revert if undefined function is called\n revert(0, 0)\n }\n }\n\n /// @dev Gets the proxy id associated with the proxy address.\n /// @return Proxy id.\n function getProxyId()\n external\n pure\n returns (bytes4)\n {\n return PROXY_ID;\n }\n}\n", - "2.0.0/protocol/AssetProxy/MixinAuthorizable.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../utils/Ownable/Ownable.sol\";\nimport \"./mixins/MAuthorizable.sol\";\n\n\ncontract MixinAuthorizable is\n Ownable,\n MAuthorizable\n{\n /// @dev Only authorized addresses can invoke functions with this modifier.\n modifier onlyAuthorized {\n require(\n authorized[msg.sender],\n \"SENDER_NOT_AUTHORIZED\"\n );\n _;\n }\n\n mapping (address => bool) public authorized;\n address[] public authorities;\n\n /// @dev Authorizes an address.\n /// @param target Address to authorize.\n function addAuthorizedAddress(address target)\n external\n onlyOwner\n {\n require(\n !authorized[target],\n \"TARGET_ALREADY_AUTHORIZED\"\n );\n\n authorized[target] = true;\n authorities.push(target);\n emit AuthorizedAddressAdded(target, msg.sender);\n }\n\n /// @dev Removes authorizion of an address.\n /// @param target Address to remove authorization from.\n function removeAuthorizedAddress(address target)\n external\n onlyOwner\n {\n require(\n authorized[target],\n \"TARGET_NOT_AUTHORIZED\"\n );\n\n delete authorized[target];\n for (uint256 i = 0; i < authorities.length; i++) {\n if (authorities[i] == target) {\n authorities[i] = authorities[authorities.length - 1];\n authorities.length -= 1;\n break;\n }\n }\n emit AuthorizedAddressRemoved(target, msg.sender);\n }\n\n /// @dev Removes authorizion of an address.\n /// @param target Address to remove authorization from.\n /// @param index Index of target in authorities array.\n function removeAuthorizedAddressAtIndex(\n address target,\n uint256 index\n )\n external\n onlyOwner\n {\n require(\n authorized[target],\n \"TARGET_NOT_AUTHORIZED\"\n );\n require(\n index < authorities.length,\n \"INDEX_OUT_OF_BOUNDS\"\n );\n require(\n authorities[index] == target,\n \"AUTHORIZED_ADDRESS_MISMATCH\"\n );\n\n delete authorized[target];\n authorities[index] = authorities[authorities.length - 1];\n authorities.length -= 1;\n emit AuthorizedAddressRemoved(target, msg.sender);\n }\n\n /// @dev Gets all authorized addresses.\n /// @return Array of authorized addresses.\n function getAuthorizedAddresses()\n external\n view\n returns (address[] memory)\n {\n return authorities;\n }\n}\n", - "2.0.0/protocol/AssetProxy/interfaces/IAssetProxy.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"./IAuthorizable.sol\";\n\n\ncontract IAssetProxy is\n IAuthorizable\n{\n /// @dev Transfers assets. Either succeeds or throws.\n /// @param assetData Byte array encoded for the respective asset proxy.\n /// @param from Address to transfer asset from.\n /// @param to Address to transfer asset to.\n /// @param amount Amount of asset to transfer.\n function transferFrom(\n bytes assetData,\n address from,\n address to,\n uint256 amount\n )\n external;\n \n /// @dev Gets the proxy id associated with the proxy address.\n /// @return Proxy id.\n function getProxyId()\n external\n pure\n returns (bytes4);\n}\n", - "2.0.0/protocol/AssetProxy/interfaces/IAuthorizable.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../../utils/Ownable/IOwnable.sol\";\n\n\ncontract IAuthorizable is\n IOwnable\n{\n /// @dev Authorizes an address.\n /// @param target Address to authorize.\n function addAuthorizedAddress(address target)\n external;\n\n /// @dev Removes authorizion of an address.\n /// @param target Address to remove authorization from.\n function removeAuthorizedAddress(address target)\n external;\n\n /// @dev Removes authorizion of an address.\n /// @param target Address to remove authorization from.\n /// @param index Index of target in authorities array.\n function removeAuthorizedAddressAtIndex(\n address target,\n uint256 index\n )\n external;\n \n /// @dev Gets all authorized addresses.\n /// @return Array of authorized addresses.\n function getAuthorizedAddresses()\n external\n view\n returns (address[] memory);\n}\n", - "2.0.0/protocol/AssetProxy/mixins/MAuthorizable.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../interfaces/IAuthorizable.sol\";\n\n\ncontract MAuthorizable is\n IAuthorizable\n{\n // Event logged when a new address is authorized.\n event AuthorizedAddressAdded(\n address indexed target,\n address indexed caller\n );\n\n // Event logged when a currently authorized address is unauthorized.\n event AuthorizedAddressRemoved(\n address indexed target,\n address indexed caller\n );\n\n /// @dev Only authorized addresses can invoke functions with this modifier.\n modifier onlyAuthorized { revert(); _; }\n}\n", - "2.0.0/protocol/AssetProxyOwner/AssetProxyOwner.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../multisig/MultiSigWalletWithTimeLock.sol\";\nimport \"../../utils/LibBytes/LibBytes.sol\";\n\n\ncontract AssetProxyOwner is\n MultiSigWalletWithTimeLock\n{\n using LibBytes for bytes;\n\n event AssetProxyRegistration(address assetProxyContract, bool isRegistered);\n\n // Mapping of AssetProxy contract address =>\n // if this contract is allowed to call the AssetProxy's `removeAuthorizedAddressAtIndex` method without a time lock.\n mapping (address => bool) public isAssetProxyRegistered;\n\n bytes4 constant internal REMOVE_AUTHORIZED_ADDRESS_AT_INDEX_SELECTOR = bytes4(keccak256(\"removeAuthorizedAddressAtIndex(address,uint256)\"));\n\n /// @dev Function will revert if the transaction does not call `removeAuthorizedAddressAtIndex`\n /// on an approved AssetProxy contract.\n modifier validRemoveAuthorizedAddressAtIndexTx(uint256 transactionId) {\n Transaction storage txn = transactions[transactionId];\n require(\n isAssetProxyRegistered[txn.destination],\n \"UNREGISTERED_ASSET_PROXY\"\n );\n require(\n txn.data.readBytes4(0) == REMOVE_AUTHORIZED_ADDRESS_AT_INDEX_SELECTOR,\n \"INVALID_FUNCTION_SELECTOR\"\n );\n _;\n }\n\n /// @dev Contract constructor sets initial owners, required number of confirmations,\n /// time lock, and list of AssetProxy addresses.\n /// @param _owners List of initial owners.\n /// @param _assetProxyContracts Array of AssetProxy contract addresses.\n /// @param _required Number of required confirmations.\n /// @param _secondsTimeLocked Duration needed after a transaction is confirmed and before it becomes executable, in seconds.\n constructor (\n address[] memory _owners,\n address[] memory _assetProxyContracts,\n uint256 _required,\n uint256 _secondsTimeLocked\n )\n public\n MultiSigWalletWithTimeLock(_owners, _required, _secondsTimeLocked)\n {\n for (uint256 i = 0; i < _assetProxyContracts.length; i++) {\n address assetProxy = _assetProxyContracts[i];\n require(\n assetProxy != address(0),\n \"INVALID_ASSET_PROXY\"\n );\n isAssetProxyRegistered[assetProxy] = true;\n }\n }\n\n /// @dev Registers or deregisters an AssetProxy to be able to execute\n /// `removeAuthorizedAddressAtIndex` without a timelock.\n /// @param assetProxyContract Address of AssetProxy contract.\n /// @param isRegistered Status of approval for AssetProxy contract.\n function registerAssetProxy(address assetProxyContract, bool isRegistered)\n public\n onlyWallet\n notNull(assetProxyContract)\n {\n isAssetProxyRegistered[assetProxyContract] = isRegistered;\n emit AssetProxyRegistration(assetProxyContract, isRegistered);\n }\n\n /// @dev Allows execution of `removeAuthorizedAddressAtIndex` without time lock.\n /// @param transactionId Transaction ID.\n function executeRemoveAuthorizedAddressAtIndex(uint256 transactionId)\n public\n notExecuted(transactionId)\n fullyConfirmed(transactionId)\n validRemoveAuthorizedAddressAtIndexTx(transactionId)\n {\n Transaction storage txn = transactions[transactionId];\n txn.executed = true;\n if (external_call(txn.destination, txn.value, txn.data.length, txn.data)) {\n emit Execution(transactionId);\n } else {\n emit ExecutionFailure(transactionId);\n txn.executed = false;\n }\n }\n}\n", - "2.0.0/protocol/Exchange/Exchange.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"./MixinExchangeCore.sol\";\nimport \"./MixinSignatureValidator.sol\";\nimport \"./MixinWrapperFunctions.sol\";\nimport \"./MixinAssetProxyDispatcher.sol\";\nimport \"./MixinTransactions.sol\";\nimport \"./MixinMatchOrders.sol\";\n\n\n// solhint-disable no-empty-blocks\ncontract Exchange is\n MixinExchangeCore,\n MixinMatchOrders,\n MixinSignatureValidator,\n MixinTransactions,\n MixinAssetProxyDispatcher,\n MixinWrapperFunctions\n{\n string constant public VERSION = \"2.0.0\";\n\n // Mixins are instantiated in the order they are inherited\n constructor ()\n public\n MixinExchangeCore()\n MixinMatchOrders()\n MixinSignatureValidator()\n MixinTransactions()\n MixinAssetProxyDispatcher()\n MixinWrapperFunctions()\n {}\n}\n", - "2.0.0/protocol/Exchange/MixinAssetProxyDispatcher.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../utils/Ownable/Ownable.sol\";\nimport \"./mixins/MAssetProxyDispatcher.sol\";\nimport \"../AssetProxy/interfaces/IAssetProxy.sol\";\n\n\ncontract MixinAssetProxyDispatcher is\n Ownable,\n MAssetProxyDispatcher\n{\n // Mapping from Asset Proxy Id's to their respective Asset Proxy\n mapping (bytes4 => IAssetProxy) public assetProxies;\n\n /// @dev Registers an asset proxy to its asset proxy id.\n /// Once an asset proxy is registered, it cannot be unregistered.\n /// @param assetProxy Address of new asset proxy to register.\n function registerAssetProxy(address assetProxy)\n external\n onlyOwner\n {\n IAssetProxy assetProxyContract = IAssetProxy(assetProxy);\n\n // Ensure that no asset proxy exists with current id.\n bytes4 assetProxyId = assetProxyContract.getProxyId();\n address currentAssetProxy = assetProxies[assetProxyId];\n require(\n currentAssetProxy == address(0),\n \"ASSET_PROXY_ALREADY_EXISTS\"\n );\n\n // Add asset proxy and log registration.\n assetProxies[assetProxyId] = assetProxyContract;\n emit AssetProxyRegistered(\n assetProxyId,\n assetProxy\n );\n }\n\n /// @dev Gets an asset proxy.\n /// @param assetProxyId Id of the asset proxy.\n /// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered.\n function getAssetProxy(bytes4 assetProxyId)\n external\n view\n returns (address)\n {\n return assetProxies[assetProxyId];\n }\n\n /// @dev Forwards arguments to assetProxy and calls `transferFrom`. Either succeeds or throws.\n /// @param assetData Byte array encoded for the asset.\n /// @param from Address to transfer token from.\n /// @param to Address to transfer token to.\n /// @param amount Amount of token to transfer.\n function dispatchTransferFrom(\n bytes memory assetData,\n address from,\n address to,\n uint256 amount\n )\n internal\n {\n // Do nothing if no amount should be transferred.\n if (amount > 0 && from != to) {\n // Ensure assetData length is valid\n require(\n assetData.length > 3,\n \"LENGTH_GREATER_THAN_3_REQUIRED\"\n );\n \n // Lookup assetProxy. We do not use `LibBytes.readBytes4` for gas efficiency reasons.\n bytes4 assetProxyId;\n assembly {\n assetProxyId := and(mload(\n add(assetData, 32)),\n 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000\n )\n }\n address assetProxy = assetProxies[assetProxyId];\n\n // Ensure that assetProxy exists\n require(\n assetProxy != address(0),\n \"ASSET_PROXY_DOES_NOT_EXIST\"\n );\n \n // We construct calldata for the `assetProxy.transferFrom` ABI.\n // The layout of this calldata is in the table below.\n // \n // | Area | Offset | Length | Contents |\n // | -------- |--------|---------|-------------------------------------------- |\n // | Header | 0 | 4 | function selector |\n // | Params | | 4 * 32 | function parameters: |\n // | | 4 | | 1. offset to assetData (*) |\n // | | 36 | | 2. from |\n // | | 68 | | 3. to |\n // | | 100 | | 4. amount |\n // | Data | | | assetData: |\n // | | 132 | 32 | assetData Length |\n // | | 164 | ** | assetData Contents |\n\n assembly {\n /////// Setup State ///////\n // `cdStart` is the start of the calldata for `assetProxy.transferFrom` (equal to free memory ptr).\n let cdStart := mload(64)\n // `dataAreaLength` is the total number of words needed to store `assetData`\n // As-per the ABI spec, this value is padded up to the nearest multiple of 32,\n // and includes 32-bytes for length.\n let dataAreaLength := and(add(mload(assetData), 63), 0xFFFFFFFFFFFE0)\n // `cdEnd` is the end of the calldata for `assetProxy.transferFrom`.\n let cdEnd := add(cdStart, add(132, dataAreaLength))\n\n \n /////// Setup Header Area ///////\n // This area holds the 4-byte `transferFromSelector`.\n // bytes4(keccak256(\"transferFrom(bytes,address,address,uint256)\")) = 0xa85e59e4\n mstore(cdStart, 0xa85e59e400000000000000000000000000000000000000000000000000000000)\n \n /////// Setup Params Area ///////\n // Each parameter is padded to 32-bytes. The entire Params Area is 128 bytes.\n // Notes:\n // 1. The offset to `assetData` is the length of the Params Area (128 bytes).\n // 2. A 20-byte mask is applied to addresses to zero-out the unused bytes.\n mstore(add(cdStart, 4), 128)\n mstore(add(cdStart, 36), and(from, 0xffffffffffffffffffffffffffffffffffffffff))\n mstore(add(cdStart, 68), and(to, 0xffffffffffffffffffffffffffffffffffffffff))\n mstore(add(cdStart, 100), amount)\n \n /////// Setup Data Area ///////\n // This area holds `assetData`.\n let dataArea := add(cdStart, 132)\n // solhint-disable-next-line no-empty-blocks\n for {} lt(dataArea, cdEnd) {} {\n mstore(dataArea, mload(assetData))\n dataArea := add(dataArea, 32)\n assetData := add(assetData, 32)\n }\n\n /////// Call `assetProxy.transferFrom` using the constructed calldata ///////\n let success := call(\n gas, // forward all gas\n assetProxy, // call address of asset proxy\n 0, // don't send any ETH\n cdStart, // pointer to start of input\n sub(cdEnd, cdStart), // length of input \n cdStart, // write output over input\n 512 // reserve 512 bytes for output\n )\n if iszero(success) {\n revert(cdStart, returndatasize())\n }\n }\n }\n }\n}\n", - "2.0.0/protocol/Exchange/MixinExchangeCore.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../../utils/ReentrancyGuard/ReentrancyGuard.sol\";\nimport \"./libs/LibConstants.sol\";\nimport \"./libs/LibFillResults.sol\";\nimport \"./libs/LibOrder.sol\";\nimport \"./libs/LibMath.sol\";\nimport \"./mixins/MExchangeCore.sol\";\nimport \"./mixins/MSignatureValidator.sol\";\nimport \"./mixins/MTransactions.sol\";\nimport \"./mixins/MAssetProxyDispatcher.sol\";\n\n\ncontract MixinExchangeCore is\n ReentrancyGuard,\n LibConstants,\n LibMath,\n LibOrder,\n LibFillResults,\n MAssetProxyDispatcher,\n MExchangeCore,\n MSignatureValidator,\n MTransactions\n{\n // Mapping of orderHash => amount of takerAsset already bought by maker\n mapping (bytes32 => uint256) public filled;\n\n // Mapping of orderHash => cancelled\n mapping (bytes32 => bool) public cancelled;\n\n // Mapping of makerAddress => senderAddress => lowest salt an order can have in order to be fillable\n // Orders with specified senderAddress and with a salt less than their epoch are considered cancelled\n mapping (address => mapping (address => uint256)) public orderEpoch;\n\n /// @dev Cancels all orders created by makerAddress with a salt less than or equal to the targetOrderEpoch\n /// and senderAddress equal to msg.sender (or null address if msg.sender == makerAddress).\n /// @param targetOrderEpoch Orders created with a salt less or equal to this value will be cancelled.\n function cancelOrdersUpTo(uint256 targetOrderEpoch)\n external\n nonReentrant\n {\n address makerAddress = getCurrentContextAddress();\n // If this function is called via `executeTransaction`, we only update the orderEpoch for the makerAddress/msg.sender combination.\n // This allows external filter contracts to add rules to how orders are cancelled via this function.\n address senderAddress = makerAddress == msg.sender ? address(0) : msg.sender;\n\n // orderEpoch is initialized to 0, so to cancelUpTo we need salt + 1\n uint256 newOrderEpoch = targetOrderEpoch + 1; \n uint256 oldOrderEpoch = orderEpoch[makerAddress][senderAddress];\n\n // Ensure orderEpoch is monotonically increasing\n require(\n newOrderEpoch > oldOrderEpoch, \n \"INVALID_NEW_ORDER_EPOCH\"\n );\n\n // Update orderEpoch\n orderEpoch[makerAddress][senderAddress] = newOrderEpoch;\n emit CancelUpTo(\n makerAddress,\n senderAddress,\n newOrderEpoch\n );\n }\n\n /// @dev Fills the input order.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n /// @return Amounts filled and fees paid by maker and taker.\n function fillOrder(\n Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n public\n nonReentrant\n returns (FillResults memory fillResults)\n {\n fillResults = fillOrderInternal(\n order,\n takerAssetFillAmount,\n signature\n );\n return fillResults;\n }\n\n /// @dev After calling, the order can not be filled anymore.\n /// Throws if order is invalid or sender does not have permission to cancel.\n /// @param order Order to cancel. Order must be OrderStatus.FILLABLE.\n function cancelOrder(Order memory order)\n public\n nonReentrant\n {\n cancelOrderInternal(order);\n }\n\n /// @dev Gets information about an order: status, hash, and amount filled.\n /// @param order Order to gather information on.\n /// @return OrderInfo Information about the order and its state.\n /// See LibOrder.OrderInfo for a complete description.\n function getOrderInfo(Order memory order)\n public\n view\n returns (OrderInfo memory orderInfo)\n {\n // Compute the order hash\n orderInfo.orderHash = getOrderHash(order);\n\n // Fetch filled amount\n orderInfo.orderTakerAssetFilledAmount = filled[orderInfo.orderHash];\n\n // If order.makerAssetAmount is zero, we also reject the order.\n // While the Exchange contract handles them correctly, they create\n // edge cases in the supporting infrastructure because they have\n // an 'infinite' price when computed by a simple division.\n if (order.makerAssetAmount == 0) {\n orderInfo.orderStatus = uint8(OrderStatus.INVALID_MAKER_ASSET_AMOUNT);\n return orderInfo;\n }\n\n // If order.takerAssetAmount is zero, then the order will always\n // be considered filled because 0 == takerAssetAmount == orderTakerAssetFilledAmount\n // Instead of distinguishing between unfilled and filled zero taker\n // amount orders, we choose not to support them.\n if (order.takerAssetAmount == 0) {\n orderInfo.orderStatus = uint8(OrderStatus.INVALID_TAKER_ASSET_AMOUNT);\n return orderInfo;\n }\n\n // Validate order availability\n if (orderInfo.orderTakerAssetFilledAmount >= order.takerAssetAmount) {\n orderInfo.orderStatus = uint8(OrderStatus.FULLY_FILLED);\n return orderInfo;\n }\n\n // Validate order expiration\n // solhint-disable-next-line not-rely-on-time\n if (block.timestamp >= order.expirationTimeSeconds) {\n orderInfo.orderStatus = uint8(OrderStatus.EXPIRED);\n return orderInfo;\n }\n\n // Check if order has been cancelled\n if (cancelled[orderInfo.orderHash]) {\n orderInfo.orderStatus = uint8(OrderStatus.CANCELLED);\n return orderInfo;\n }\n if (orderEpoch[order.makerAddress][order.senderAddress] > order.salt) {\n orderInfo.orderStatus = uint8(OrderStatus.CANCELLED);\n return orderInfo;\n }\n\n // All other statuses are ruled out: order is Fillable\n orderInfo.orderStatus = uint8(OrderStatus.FILLABLE);\n return orderInfo;\n }\n\n /// @dev Fills the input order.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n /// @return Amounts filled and fees paid by maker and taker.\n function fillOrderInternal(\n Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n internal\n returns (FillResults memory fillResults)\n {\n // Fetch order info\n OrderInfo memory orderInfo = getOrderInfo(order);\n\n // Fetch taker address\n address takerAddress = getCurrentContextAddress();\n \n // Assert that the order is fillable by taker\n assertFillableOrder(\n order,\n orderInfo,\n takerAddress,\n signature\n );\n \n // Get amount of takerAsset to fill\n uint256 remainingTakerAssetAmount = safeSub(order.takerAssetAmount, orderInfo.orderTakerAssetFilledAmount);\n uint256 takerAssetFilledAmount = min256(takerAssetFillAmount, remainingTakerAssetAmount);\n\n // Validate context\n assertValidFill(\n order,\n orderInfo,\n takerAssetFillAmount,\n takerAssetFilledAmount,\n fillResults.makerAssetFilledAmount\n );\n\n // Compute proportional fill amounts\n fillResults = calculateFillResults(order, takerAssetFilledAmount);\n\n // Update exchange internal state\n updateFilledState(\n order,\n takerAddress,\n orderInfo.orderHash,\n orderInfo.orderTakerAssetFilledAmount,\n fillResults\n );\n \n // Settle order\n settleOrder(\n order,\n takerAddress,\n fillResults\n );\n\n return fillResults;\n }\n\n /// @dev After calling, the order can not be filled anymore.\n /// Throws if order is invalid or sender does not have permission to cancel.\n /// @param order Order to cancel. Order must be OrderStatus.FILLABLE.\n function cancelOrderInternal(Order memory order)\n internal\n {\n // Fetch current order status\n OrderInfo memory orderInfo = getOrderInfo(order);\n\n // Validate context\n assertValidCancel(order, orderInfo);\n\n // Perform cancel\n updateCancelledState(order, orderInfo.orderHash);\n }\n\n /// @dev Updates state with results of a fill order.\n /// @param order that was filled.\n /// @param takerAddress Address of taker who filled the order.\n /// @param orderTakerAssetFilledAmount Amount of order already filled.\n function updateFilledState(\n Order memory order,\n address takerAddress,\n bytes32 orderHash,\n uint256 orderTakerAssetFilledAmount,\n FillResults memory fillResults\n )\n internal\n {\n // Update state\n filled[orderHash] = safeAdd(orderTakerAssetFilledAmount, fillResults.takerAssetFilledAmount);\n\n // Log order\n emit Fill(\n order.makerAddress,\n order.feeRecipientAddress,\n takerAddress,\n msg.sender,\n fillResults.makerAssetFilledAmount,\n fillResults.takerAssetFilledAmount,\n fillResults.makerFeePaid,\n fillResults.takerFeePaid,\n orderHash,\n order.makerAssetData,\n order.takerAssetData\n );\n }\n\n /// @dev Updates state with results of cancelling an order.\n /// State is only updated if the order is currently fillable.\n /// Otherwise, updating state would have no effect.\n /// @param order that was cancelled.\n /// @param orderHash Hash of order that was cancelled.\n function updateCancelledState(\n Order memory order,\n bytes32 orderHash\n )\n internal\n {\n // Perform cancel\n cancelled[orderHash] = true;\n\n // Log cancel\n emit Cancel(\n order.makerAddress,\n order.feeRecipientAddress,\n msg.sender,\n orderHash,\n order.makerAssetData,\n order.takerAssetData\n );\n }\n \n /// @dev Validates context for fillOrder. Succeeds or throws.\n /// @param order to be filled.\n /// @param orderInfo OrderStatus, orderHash, and amount already filled of order.\n /// @param takerAddress Address of order taker.\n /// @param signature Proof that the orders was created by its maker.\n function assertFillableOrder(\n Order memory order,\n OrderInfo memory orderInfo,\n address takerAddress,\n bytes memory signature\n )\n internal\n view\n {\n // An order can only be filled if its status is FILLABLE.\n require(\n orderInfo.orderStatus == uint8(OrderStatus.FILLABLE),\n \"ORDER_UNFILLABLE\"\n );\n \n // Validate sender is allowed to fill this order\n if (order.senderAddress != address(0)) {\n require(\n order.senderAddress == msg.sender,\n \"INVALID_SENDER\"\n );\n }\n \n // Validate taker is allowed to fill this order\n if (order.takerAddress != address(0)) {\n require(\n order.takerAddress == takerAddress,\n \"INVALID_TAKER\"\n );\n }\n \n // Validate Maker signature (check only if first time seen)\n if (orderInfo.orderTakerAssetFilledAmount == 0) {\n require(\n isValidSignature(\n orderInfo.orderHash,\n order.makerAddress,\n signature\n ),\n \"INVALID_ORDER_SIGNATURE\"\n );\n }\n }\n \n /// @dev Validates context for fillOrder. Succeeds or throws.\n /// @param order to be filled.\n /// @param orderInfo OrderStatus, orderHash, and amount already filled of order.\n /// @param takerAssetFillAmount Desired amount of order to fill by taker.\n /// @param takerAssetFilledAmount Amount of takerAsset that will be filled.\n /// @param makerAssetFilledAmount Amount of makerAsset that will be transfered.\n function assertValidFill(\n Order memory order,\n OrderInfo memory orderInfo,\n uint256 takerAssetFillAmount, // TODO: use FillResults\n uint256 takerAssetFilledAmount,\n uint256 makerAssetFilledAmount\n )\n internal\n view\n {\n // Revert if fill amount is invalid\n // TODO: reconsider necessity for v2.1\n require(\n takerAssetFillAmount != 0,\n \"INVALID_TAKER_AMOUNT\"\n );\n \n // Make sure taker does not pay more than desired amount\n // NOTE: This assertion should never fail, it is here\n // as an extra defence against potential bugs.\n require(\n takerAssetFilledAmount <= takerAssetFillAmount,\n \"TAKER_OVERPAY\"\n );\n \n // Make sure order is not overfilled\n // NOTE: This assertion should never fail, it is here\n // as an extra defence against potential bugs.\n require(\n safeAdd(orderInfo.orderTakerAssetFilledAmount, takerAssetFilledAmount) <= order.takerAssetAmount,\n \"ORDER_OVERFILL\"\n );\n \n // Make sure order is filled at acceptable price.\n // The order has an implied price from the makers perspective:\n // order price = order.makerAssetAmount / order.takerAssetAmount\n // i.e. the number of makerAsset maker is paying per takerAsset. The\n // maker is guaranteed to get this price or a better (lower) one. The\n // actual price maker is getting in this fill is:\n // fill price = makerAssetFilledAmount / takerAssetFilledAmount\n // We need `fill price <= order price` for the fill to be fair to maker.\n // This amounts to:\n // makerAssetFilledAmount order.makerAssetAmount\n // ------------------------ <= -----------------------\n // takerAssetFilledAmount order.takerAssetAmount\n // or, equivalently:\n // makerAssetFilledAmount * order.takerAssetAmount <=\n // order.makerAssetAmount * takerAssetFilledAmount\n // NOTE: This assertion should never fail, it is here\n // as an extra defence against potential bugs.\n require(\n safeMul(makerAssetFilledAmount, order.takerAssetAmount)\n <= \n safeMul(order.makerAssetAmount, takerAssetFilledAmount),\n \"INVALID_FILL_PRICE\"\n );\n }\n\n /// @dev Validates context for cancelOrder. Succeeds or throws.\n /// @param order to be cancelled.\n /// @param orderInfo OrderStatus, orderHash, and amount already filled of order.\n function assertValidCancel(\n Order memory order,\n OrderInfo memory orderInfo\n )\n internal\n view\n {\n // Ensure order is valid\n // An order can only be cancelled if its status is FILLABLE.\n require(\n orderInfo.orderStatus == uint8(OrderStatus.FILLABLE),\n \"ORDER_UNFILLABLE\"\n );\n\n // Validate sender is allowed to cancel this order\n if (order.senderAddress != address(0)) {\n require(\n order.senderAddress == msg.sender,\n \"INVALID_SENDER\"\n );\n }\n\n // Validate transaction signed by maker\n address makerAddress = getCurrentContextAddress();\n require(\n order.makerAddress == makerAddress,\n \"INVALID_MAKER\"\n );\n }\n\n /// @dev Calculates amounts filled and fees paid by maker and taker.\n /// @param order to be filled.\n /// @param takerAssetFilledAmount Amount of takerAsset that will be filled.\n /// @return fillResults Amounts filled and fees paid by maker and taker.\n function calculateFillResults(\n Order memory order,\n uint256 takerAssetFilledAmount\n )\n internal\n pure\n returns (FillResults memory fillResults)\n {\n // Compute proportional transfer amounts\n fillResults.takerAssetFilledAmount = takerAssetFilledAmount;\n fillResults.makerAssetFilledAmount = safeGetPartialAmountFloor(\n takerAssetFilledAmount,\n order.takerAssetAmount,\n order.makerAssetAmount\n );\n fillResults.makerFeePaid = safeGetPartialAmountFloor(\n fillResults.makerAssetFilledAmount,\n order.makerAssetAmount,\n order.makerFee\n );\n fillResults.takerFeePaid = safeGetPartialAmountFloor(\n takerAssetFilledAmount,\n order.takerAssetAmount,\n order.takerFee\n );\n\n return fillResults;\n }\n\n /// @dev Settles an order by transferring assets between counterparties.\n /// @param order Order struct containing order specifications.\n /// @param takerAddress Address selling takerAsset and buying makerAsset.\n /// @param fillResults Amounts to be filled and fees paid by maker and taker.\n function settleOrder(\n LibOrder.Order memory order,\n address takerAddress,\n LibFillResults.FillResults memory fillResults\n )\n private\n {\n bytes memory zrxAssetData = ZRX_ASSET_DATA;\n dispatchTransferFrom(\n order.makerAssetData,\n order.makerAddress,\n takerAddress,\n fillResults.makerAssetFilledAmount\n );\n dispatchTransferFrom(\n order.takerAssetData,\n takerAddress,\n order.makerAddress,\n fillResults.takerAssetFilledAmount\n );\n dispatchTransferFrom(\n zrxAssetData,\n order.makerAddress,\n order.feeRecipientAddress,\n fillResults.makerFeePaid\n );\n dispatchTransferFrom(\n zrxAssetData,\n takerAddress,\n order.feeRecipientAddress,\n fillResults.takerFeePaid\n );\n }\n}\n", - "2.0.0/protocol/Exchange/MixinMatchOrders.sol": "/*\n Copyright 2018 ZeroEx Intl.\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n http://www.apache.org/licenses/LICENSE-2.0\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../../utils/ReentrancyGuard/ReentrancyGuard.sol\";\nimport \"./libs/LibConstants.sol\";\nimport \"./libs/LibMath.sol\";\nimport \"./libs/LibOrder.sol\";\nimport \"./libs/LibFillResults.sol\";\nimport \"./mixins/MExchangeCore.sol\";\nimport \"./mixins/MMatchOrders.sol\";\nimport \"./mixins/MTransactions.sol\";\nimport \"./mixins/MAssetProxyDispatcher.sol\";\n\n\ncontract MixinMatchOrders is\n ReentrancyGuard,\n LibConstants,\n LibMath,\n MAssetProxyDispatcher,\n MExchangeCore,\n MMatchOrders,\n MTransactions\n{\n /// @dev Match two complementary orders that have a profitable spread.\n /// Each order is filled at their respective price point. However, the calculations are\n /// carried out as though the orders are both being filled at the right order's price point.\n /// The profit made by the left order goes to the taker (who matched the two orders).\n /// @param leftOrder First order to match.\n /// @param rightOrder Second order to match.\n /// @param leftSignature Proof that order was created by the left maker.\n /// @param rightSignature Proof that order was created by the right maker.\n /// @return matchedFillResults Amounts filled and fees paid by maker and taker of matched orders.\n function matchOrders(\n LibOrder.Order memory leftOrder,\n LibOrder.Order memory rightOrder,\n bytes memory leftSignature,\n bytes memory rightSignature\n )\n public\n nonReentrant\n returns (LibFillResults.MatchedFillResults memory matchedFillResults)\n {\n // We assume that rightOrder.takerAssetData == leftOrder.makerAssetData and rightOrder.makerAssetData == leftOrder.takerAssetData.\n // If this assumption isn't true, the match will fail at signature validation.\n rightOrder.makerAssetData = leftOrder.takerAssetData;\n rightOrder.takerAssetData = leftOrder.makerAssetData;\n\n // Get left & right order info\n LibOrder.OrderInfo memory leftOrderInfo = getOrderInfo(leftOrder);\n LibOrder.OrderInfo memory rightOrderInfo = getOrderInfo(rightOrder);\n\n // Fetch taker address\n address takerAddress = getCurrentContextAddress();\n \n // Either our context is valid or we revert\n assertFillableOrder(\n leftOrder,\n leftOrderInfo,\n takerAddress,\n leftSignature\n );\n assertFillableOrder(\n rightOrder,\n rightOrderInfo,\n takerAddress,\n rightSignature\n );\n assertValidMatch(leftOrder, rightOrder);\n\n // Compute proportional fill amounts\n matchedFillResults = calculateMatchedFillResults(\n leftOrder,\n rightOrder,\n leftOrderInfo.orderTakerAssetFilledAmount,\n rightOrderInfo.orderTakerAssetFilledAmount\n );\n\n // Validate fill contexts\n assertValidFill(\n leftOrder,\n leftOrderInfo,\n matchedFillResults.left.takerAssetFilledAmount,\n matchedFillResults.left.takerAssetFilledAmount,\n matchedFillResults.left.makerAssetFilledAmount\n );\n assertValidFill(\n rightOrder,\n rightOrderInfo,\n matchedFillResults.right.takerAssetFilledAmount,\n matchedFillResults.right.takerAssetFilledAmount,\n matchedFillResults.right.makerAssetFilledAmount\n );\n \n // Update exchange state\n updateFilledState(\n leftOrder,\n takerAddress,\n leftOrderInfo.orderHash,\n leftOrderInfo.orderTakerAssetFilledAmount,\n matchedFillResults.left\n );\n updateFilledState(\n rightOrder,\n takerAddress,\n rightOrderInfo.orderHash,\n rightOrderInfo.orderTakerAssetFilledAmount,\n matchedFillResults.right\n );\n\n // Settle matched orders. Succeeds or throws.\n settleMatchedOrders(\n leftOrder,\n rightOrder,\n takerAddress,\n matchedFillResults\n );\n\n return matchedFillResults;\n }\n\n /// @dev Validates context for matchOrders. Succeeds or throws.\n /// @param leftOrder First order to match.\n /// @param rightOrder Second order to match.\n function assertValidMatch(\n LibOrder.Order memory leftOrder,\n LibOrder.Order memory rightOrder\n )\n internal\n pure\n {\n // Make sure there is a profitable spread.\n // There is a profitable spread iff the cost per unit bought (OrderA.MakerAmount/OrderA.TakerAmount) for each order is greater\n // than the profit per unit sold of the matched order (OrderB.TakerAmount/OrderB.MakerAmount).\n // This is satisfied by the equations below:\n // / >= / \n // AND\n // / >= / \n // These equations can be combined to get the following:\n require(\n safeMul(leftOrder.makerAssetAmount, rightOrder.makerAssetAmount) >=\n safeMul(leftOrder.takerAssetAmount, rightOrder.takerAssetAmount),\n \"NEGATIVE_SPREAD_REQUIRED\"\n );\n }\n\n /// @dev Calculates fill amounts for the matched orders.\n /// Each order is filled at their respective price point. However, the calculations are\n /// carried out as though the orders are both being filled at the right order's price point.\n /// The profit made by the leftOrder order goes to the taker (who matched the two orders).\n /// @param leftOrder First order to match.\n /// @param rightOrder Second order to match.\n /// @param leftOrderTakerAssetFilledAmount Amount of left order already filled.\n /// @param rightOrderTakerAssetFilledAmount Amount of right order already filled.\n /// @param matchedFillResults Amounts to fill and fees to pay by maker and taker of matched orders.\n function calculateMatchedFillResults(\n LibOrder.Order memory leftOrder,\n LibOrder.Order memory rightOrder,\n uint256 leftOrderTakerAssetFilledAmount,\n uint256 rightOrderTakerAssetFilledAmount\n )\n internal\n pure\n returns (LibFillResults.MatchedFillResults memory matchedFillResults)\n {\n // Derive maker asset amounts for left & right orders, given store taker assert amounts\n uint256 leftTakerAssetAmountRemaining = safeSub(leftOrder.takerAssetAmount, leftOrderTakerAssetFilledAmount);\n uint256 leftMakerAssetAmountRemaining = safeGetPartialAmountFloor(\n leftOrder.makerAssetAmount,\n leftOrder.takerAssetAmount,\n leftTakerAssetAmountRemaining\n );\n uint256 rightTakerAssetAmountRemaining = safeSub(rightOrder.takerAssetAmount, rightOrderTakerAssetFilledAmount);\n uint256 rightMakerAssetAmountRemaining = safeGetPartialAmountFloor(\n rightOrder.makerAssetAmount,\n rightOrder.takerAssetAmount,\n rightTakerAssetAmountRemaining\n );\n\n // Calculate fill results for maker and taker assets: at least one order will be fully filled.\n // The maximum amount the left maker can buy is `leftTakerAssetAmountRemaining`\n // The maximum amount the right maker can sell is `rightMakerAssetAmountRemaining`\n // We have two distinct cases for calculating the fill results:\n // Case 1.\n // If the left maker can buy more than the right maker can sell, then only the right order is fully filled.\n // If the left maker can buy exactly what the right maker can sell, then both orders are fully filled.\n // Case 2.\n // If the left maker cannot buy more than the right maker can sell, then only the left order is fully filled.\n if (leftTakerAssetAmountRemaining >= rightMakerAssetAmountRemaining) {\n // Case 1: Right order is fully filled\n matchedFillResults.right.makerAssetFilledAmount = rightMakerAssetAmountRemaining;\n matchedFillResults.right.takerAssetFilledAmount = rightTakerAssetAmountRemaining;\n matchedFillResults.left.takerAssetFilledAmount = matchedFillResults.right.makerAssetFilledAmount;\n // Round down to ensure the maker's exchange rate does not exceed the price specified by the order. \n // We favor the maker when the exchange rate must be rounded.\n matchedFillResults.left.makerAssetFilledAmount = safeGetPartialAmountFloor(\n leftOrder.makerAssetAmount,\n leftOrder.takerAssetAmount,\n matchedFillResults.left.takerAssetFilledAmount\n );\n } else {\n // Case 2: Left order is fully filled\n matchedFillResults.left.makerAssetFilledAmount = leftMakerAssetAmountRemaining;\n matchedFillResults.left.takerAssetFilledAmount = leftTakerAssetAmountRemaining;\n matchedFillResults.right.makerAssetFilledAmount = matchedFillResults.left.takerAssetFilledAmount;\n // Round up to ensure the maker's exchange rate does not exceed the price specified by the order.\n // We favor the maker when the exchange rate must be rounded.\n matchedFillResults.right.takerAssetFilledAmount = safeGetPartialAmountCeil(\n rightOrder.takerAssetAmount,\n rightOrder.makerAssetAmount,\n matchedFillResults.right.makerAssetFilledAmount\n );\n }\n\n // Calculate amount given to taker\n matchedFillResults.leftMakerAssetSpreadAmount = safeSub(\n matchedFillResults.left.makerAssetFilledAmount,\n matchedFillResults.right.takerAssetFilledAmount\n );\n\n // Compute fees for left order\n matchedFillResults.left.makerFeePaid = safeGetPartialAmountFloor(\n matchedFillResults.left.makerAssetFilledAmount,\n leftOrder.makerAssetAmount,\n leftOrder.makerFee\n );\n matchedFillResults.left.takerFeePaid = safeGetPartialAmountFloor(\n matchedFillResults.left.takerAssetFilledAmount,\n leftOrder.takerAssetAmount,\n leftOrder.takerFee\n );\n\n // Compute fees for right order\n matchedFillResults.right.makerFeePaid = safeGetPartialAmountFloor(\n matchedFillResults.right.makerAssetFilledAmount,\n rightOrder.makerAssetAmount,\n rightOrder.makerFee\n );\n matchedFillResults.right.takerFeePaid = safeGetPartialAmountFloor(\n matchedFillResults.right.takerAssetFilledAmount,\n rightOrder.takerAssetAmount,\n rightOrder.takerFee\n );\n\n // Return fill results\n return matchedFillResults;\n }\n\n /// @dev Settles matched order by transferring appropriate funds between order makers, taker, and fee recipient.\n /// @param leftOrder First matched order.\n /// @param rightOrder Second matched order.\n /// @param takerAddress Address that matched the orders. The taker receives the spread between orders as profit.\n /// @param matchedFillResults Struct holding amounts to transfer between makers, taker, and fee recipients.\n function settleMatchedOrders(\n LibOrder.Order memory leftOrder,\n LibOrder.Order memory rightOrder,\n address takerAddress,\n LibFillResults.MatchedFillResults memory matchedFillResults\n )\n private\n {\n bytes memory zrxAssetData = ZRX_ASSET_DATA;\n // Order makers and taker\n dispatchTransferFrom(\n leftOrder.makerAssetData,\n leftOrder.makerAddress,\n rightOrder.makerAddress,\n matchedFillResults.right.takerAssetFilledAmount\n );\n dispatchTransferFrom(\n rightOrder.makerAssetData,\n rightOrder.makerAddress,\n leftOrder.makerAddress,\n matchedFillResults.left.takerAssetFilledAmount\n );\n dispatchTransferFrom(\n leftOrder.makerAssetData,\n leftOrder.makerAddress,\n takerAddress,\n matchedFillResults.leftMakerAssetSpreadAmount\n );\n\n // Maker fees\n dispatchTransferFrom(\n zrxAssetData,\n leftOrder.makerAddress,\n leftOrder.feeRecipientAddress,\n matchedFillResults.left.makerFeePaid\n );\n dispatchTransferFrom(\n zrxAssetData,\n rightOrder.makerAddress,\n rightOrder.feeRecipientAddress,\n matchedFillResults.right.makerFeePaid\n );\n\n // Taker fees\n if (leftOrder.feeRecipientAddress == rightOrder.feeRecipientAddress) {\n dispatchTransferFrom(\n zrxAssetData,\n takerAddress,\n leftOrder.feeRecipientAddress,\n safeAdd(\n matchedFillResults.left.takerFeePaid,\n matchedFillResults.right.takerFeePaid\n )\n );\n } else {\n dispatchTransferFrom(\n zrxAssetData,\n takerAddress,\n leftOrder.feeRecipientAddress,\n matchedFillResults.left.takerFeePaid\n );\n dispatchTransferFrom(\n zrxAssetData,\n takerAddress,\n rightOrder.feeRecipientAddress,\n matchedFillResults.right.takerFeePaid\n );\n }\n }\n}\n", - "2.0.0/protocol/Exchange/MixinSignatureValidator.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../utils/LibBytes/LibBytes.sol\";\nimport \"../../utils/ReentrancyGuard/ReentrancyGuard.sol\";\nimport \"./mixins/MSignatureValidator.sol\";\nimport \"./mixins/MTransactions.sol\";\nimport \"./interfaces/IWallet.sol\";\nimport \"./interfaces/IValidator.sol\";\n\n\ncontract MixinSignatureValidator is\n ReentrancyGuard,\n MSignatureValidator,\n MTransactions\n{\n using LibBytes for bytes;\n \n // Mapping of hash => signer => signed\n mapping (bytes32 => mapping (address => bool)) public preSigned;\n\n // Mapping of signer => validator => approved\n mapping (address => mapping (address => bool)) public allowedValidators;\n\n /// @dev Approves a hash on-chain using any valid signature type.\n /// After presigning a hash, the preSign signature type will become valid for that hash and signer.\n /// @param signerAddress Address that should have signed the given hash.\n /// @param signature Proof that the hash has been signed by signer.\n function preSign(\n bytes32 hash,\n address signerAddress,\n bytes signature\n )\n external\n {\n if (signerAddress != msg.sender) {\n require(\n isValidSignature(\n hash,\n signerAddress,\n signature\n ),\n \"INVALID_SIGNATURE\"\n );\n }\n preSigned[hash][signerAddress] = true;\n }\n\n /// @dev Approves/unnapproves a Validator contract to verify signatures on signer's behalf.\n /// @param validatorAddress Address of Validator contract.\n /// @param approval Approval or disapproval of Validator contract.\n function setSignatureValidatorApproval(\n address validatorAddress,\n bool approval\n )\n external\n nonReentrant\n {\n address signerAddress = getCurrentContextAddress();\n allowedValidators[signerAddress][validatorAddress] = approval;\n emit SignatureValidatorApproval(\n signerAddress,\n validatorAddress,\n approval\n );\n }\n\n /// @dev Verifies that a hash has been signed by the given signer.\n /// @param hash Any 32 byte hash.\n /// @param signerAddress Address that should have signed the given hash.\n /// @param signature Proof that the hash has been signed by signer.\n /// @return True if the address recovered from the provided signature matches the input signer address.\n function isValidSignature(\n bytes32 hash,\n address signerAddress,\n bytes memory signature\n )\n public\n view\n returns (bool isValid)\n {\n require(\n signature.length > 0,\n \"LENGTH_GREATER_THAN_0_REQUIRED\"\n );\n\n // Pop last byte off of signature byte array.\n uint8 signatureTypeRaw = uint8(signature.popLastByte());\n\n // Ensure signature is supported\n require(\n signatureTypeRaw < uint8(SignatureType.NSignatureTypes),\n \"SIGNATURE_UNSUPPORTED\"\n );\n\n SignatureType signatureType = SignatureType(signatureTypeRaw);\n\n // Variables are not scoped in Solidity.\n uint8 v;\n bytes32 r;\n bytes32 s;\n address recovered;\n\n // Always illegal signature.\n // This is always an implicit option since a signer can create a\n // signature array with invalid type or length. We may as well make\n // it an explicit option. This aids testing and analysis. It is\n // also the initialization value for the enum type.\n if (signatureType == SignatureType.Illegal) {\n revert(\"SIGNATURE_ILLEGAL\");\n\n // Always invalid signature.\n // Like Illegal, this is always implicitly available and therefore\n // offered explicitly. It can be implicitly created by providing\n // a correctly formatted but incorrect signature.\n } else if (signatureType == SignatureType.Invalid) {\n require(\n signature.length == 0,\n \"LENGTH_0_REQUIRED\"\n );\n isValid = false;\n return isValid;\n\n // Signature using EIP712\n } else if (signatureType == SignatureType.EIP712) {\n require(\n signature.length == 65,\n \"LENGTH_65_REQUIRED\"\n );\n v = uint8(signature[0]);\n r = signature.readBytes32(1);\n s = signature.readBytes32(33);\n recovered = ecrecover(\n hash,\n v,\n r,\n s\n );\n isValid = signerAddress == recovered;\n return isValid;\n\n // Signed using web3.eth_sign\n } else if (signatureType == SignatureType.EthSign) {\n require(\n signature.length == 65,\n \"LENGTH_65_REQUIRED\"\n );\n v = uint8(signature[0]);\n r = signature.readBytes32(1);\n s = signature.readBytes32(33);\n recovered = ecrecover(\n keccak256(abi.encodePacked(\n \"\\x19Ethereum Signed Message:\\n32\",\n hash\n )),\n v,\n r,\n s\n );\n isValid = signerAddress == recovered;\n return isValid;\n\n // Signature verified by wallet contract.\n // If used with an order, the maker of the order is the wallet contract.\n } else if (signatureType == SignatureType.Wallet) {\n isValid = isValidWalletSignature(\n hash,\n signerAddress,\n signature\n );\n return isValid;\n\n // Signature verified by validator contract.\n // If used with an order, the maker of the order can still be an EOA.\n // A signature using this type should be encoded as:\n // | Offset | Length | Contents |\n // | 0x00 | x | Signature to validate |\n // | 0x00 + x | 20 | Address of validator contract |\n // | 0x14 + x | 1 | Signature type is always \"\\x06\" |\n } else if (signatureType == SignatureType.Validator) {\n // Pop last 20 bytes off of signature byte array.\n address validatorAddress = signature.popLast20Bytes();\n \n // Ensure signer has approved validator.\n if (!allowedValidators[signerAddress][validatorAddress]) {\n return false;\n }\n isValid = isValidValidatorSignature(\n validatorAddress,\n hash,\n signerAddress,\n signature\n );\n return isValid;\n\n // Signer signed hash previously using the preSign function.\n } else if (signatureType == SignatureType.PreSigned) {\n isValid = preSigned[hash][signerAddress];\n return isValid;\n }\n\n // Anything else is illegal (We do not return false because\n // the signature may actually be valid, just not in a format\n // that we currently support. In this case returning false\n // may lead the caller to incorrectly believe that the\n // signature was invalid.)\n revert(\"SIGNATURE_UNSUPPORTED\");\n }\n\n /// @dev Verifies signature using logic defined by Wallet contract.\n /// @param hash Any 32 byte hash.\n /// @param walletAddress Address that should have signed the given hash\n /// and defines its own signature verification method.\n /// @param signature Proof that the hash has been signed by signer.\n /// @return True if signature is valid for given wallet..\n function isValidWalletSignature(\n bytes32 hash,\n address walletAddress,\n bytes signature\n )\n internal\n view\n returns (bool isValid)\n {\n bytes memory calldata = abi.encodeWithSelector(\n IWallet(walletAddress).isValidSignature.selector,\n hash,\n signature\n );\n assembly {\n let cdStart := add(calldata, 32)\n let success := staticcall(\n gas, // forward all gas\n walletAddress, // address of Wallet contract\n cdStart, // pointer to start of input\n mload(calldata), // length of input\n cdStart, // write output over input\n 32 // output size is 32 bytes\n )\n\n switch success\n case 0 {\n // Revert with `Error(\"WALLET_ERROR\")`\n mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)\n mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)\n mstore(64, 0x0000000c57414c4c45545f4552524f5200000000000000000000000000000000)\n mstore(96, 0)\n revert(0, 100)\n }\n case 1 {\n // Signature is valid if call did not revert and returned true\n isValid := mload(cdStart)\n }\n }\n return isValid;\n }\n\n /// @dev Verifies signature using logic defined by Validator contract.\n /// @param validatorAddress Address of validator contract.\n /// @param hash Any 32 byte hash.\n /// @param signerAddress Address that should have signed the given hash.\n /// @param signature Proof that the hash has been signed by signer.\n /// @return True if the address recovered from the provided signature matches the input signer address.\n function isValidValidatorSignature(\n address validatorAddress,\n bytes32 hash,\n address signerAddress,\n bytes signature\n )\n internal\n view\n returns (bool isValid)\n {\n bytes memory calldata = abi.encodeWithSelector(\n IValidator(signerAddress).isValidSignature.selector,\n hash,\n signerAddress,\n signature\n );\n assembly {\n let cdStart := add(calldata, 32)\n let success := staticcall(\n gas, // forward all gas\n validatorAddress, // address of Validator contract\n cdStart, // pointer to start of input\n mload(calldata), // length of input\n cdStart, // write output over input\n 32 // output size is 32 bytes\n )\n\n switch success\n case 0 {\n // Revert with `Error(\"VALIDATOR_ERROR\")`\n mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)\n mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)\n mstore(64, 0x0000000f56414c494441544f525f4552524f5200000000000000000000000000)\n mstore(96, 0)\n revert(0, 100)\n }\n case 1 {\n // Signature is valid if call did not revert and returned true\n isValid := mload(cdStart)\n }\n }\n return isValid;\n }\n}\n", - "2.0.0/protocol/Exchange/MixinTransactions.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\npragma solidity 0.4.24;\n\nimport \"./libs/LibExchangeErrors.sol\";\nimport \"./mixins/MSignatureValidator.sol\";\nimport \"./mixins/MTransactions.sol\";\nimport \"./libs/LibEIP712.sol\";\n\n\ncontract MixinTransactions is\n LibEIP712,\n MSignatureValidator,\n MTransactions\n{\n // Mapping of transaction hash => executed\n // This prevents transactions from being executed more than once.\n mapping (bytes32 => bool) public transactions;\n\n // Address of current transaction signer\n address public currentContextAddress;\n\n /// @dev Executes an exchange method call in the context of signer.\n /// @param salt Arbitrary number to ensure uniqueness of transaction hash.\n /// @param signerAddress Address of transaction signer.\n /// @param data AbiV2 encoded calldata.\n /// @param signature Proof of signer transaction by signer.\n function executeTransaction(\n uint256 salt,\n address signerAddress,\n bytes data,\n bytes signature\n )\n external\n {\n // Prevent reentrancy\n require(\n currentContextAddress == address(0),\n \"REENTRANCY_ILLEGAL\"\n );\n\n bytes32 transactionHash = hashEIP712Message(hashZeroExTransaction(\n salt,\n signerAddress,\n data\n ));\n\n // Validate transaction has not been executed\n require(\n !transactions[transactionHash],\n \"INVALID_TX_HASH\"\n );\n\n // Transaction always valid if signer is sender of transaction\n if (signerAddress != msg.sender) {\n // Validate signature\n require(\n isValidSignature(\n transactionHash,\n signerAddress,\n signature\n ),\n \"INVALID_TX_SIGNATURE\"\n );\n\n // Set the current transaction signer\n currentContextAddress = signerAddress;\n }\n\n // Execute transaction\n transactions[transactionHash] = true;\n require(\n address(this).delegatecall(data),\n \"FAILED_EXECUTION\"\n );\n\n // Reset current transaction signer if it was previously updated\n if (signerAddress != msg.sender) {\n currentContextAddress = address(0);\n }\n }\n\n /// @dev Calculates EIP712 hash of the Transaction.\n /// @param salt Arbitrary number to ensure uniqueness of transaction hash.\n /// @param signerAddress Address of transaction signer.\n /// @param data AbiV2 encoded calldata.\n /// @return EIP712 hash of the Transaction.\n function hashZeroExTransaction(\n uint256 salt,\n address signerAddress,\n bytes memory data\n )\n internal\n pure\n returns (bytes32 result)\n {\n bytes32 schemaHash = EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH;\n bytes32 dataHash = keccak256(data);\n\n // Assembly for more efficiently computing:\n // keccak256(abi.encodePacked(\n // EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH,\n // salt,\n // bytes32(signerAddress),\n // keccak256(data)\n // ));\n\n assembly {\n // Load free memory pointer\n let memPtr := mload(64)\n\n mstore(memPtr, schemaHash) // hash of schema\n mstore(add(memPtr, 32), salt) // salt\n mstore(add(memPtr, 64), and(signerAddress, 0xffffffffffffffffffffffffffffffffffffffff)) // signerAddress\n mstore(add(memPtr, 96), dataHash) // hash of data\n\n // Compute hash\n result := keccak256(memPtr, 128)\n }\n return result;\n }\n\n /// @dev The current function will be called in the context of this address (either 0x transaction signer or `msg.sender`).\n /// If calling a fill function, this address will represent the taker.\n /// If calling a cancel function, this address will represent the maker.\n /// @return Signer of 0x transaction if entry point is `executeTransaction`.\n /// `msg.sender` if entry point is any other function.\n function getCurrentContextAddress()\n internal\n view\n returns (address)\n {\n address currentContextAddress_ = currentContextAddress;\n address contextAddress = currentContextAddress_ == address(0) ? msg.sender : currentContextAddress_;\n return contextAddress;\n }\n}\n", - "2.0.0/protocol/Exchange/MixinWrapperFunctions.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../../utils/ReentrancyGuard/ReentrancyGuard.sol\";\nimport \"./libs/LibMath.sol\";\nimport \"./libs/LibOrder.sol\";\nimport \"./libs/LibFillResults.sol\";\nimport \"./libs/LibAbiEncoder.sol\";\nimport \"./mixins/MExchangeCore.sol\";\nimport \"./mixins/MWrapperFunctions.sol\";\n\n\ncontract MixinWrapperFunctions is\n ReentrancyGuard,\n LibMath,\n LibFillResults,\n LibAbiEncoder,\n MExchangeCore,\n MWrapperFunctions\n{\n /// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n function fillOrKillOrder(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n public\n nonReentrant\n returns (FillResults memory fillResults)\n {\n fillResults = fillOrKillOrderInternal(\n order,\n takerAssetFillAmount,\n signature\n );\n return fillResults;\n }\n\n /// @dev Fills the input order.\n /// Returns false if the transaction would otherwise revert.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n /// @return Amounts filled and fees paid by maker and taker.\n function fillOrderNoThrow(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n public\n returns (FillResults memory fillResults)\n {\n // ABI encode calldata for `fillOrder`\n bytes memory fillOrderCalldata = abiEncodeFillOrder(\n order,\n takerAssetFillAmount,\n signature\n );\n\n // Delegate to `fillOrder` and handle any exceptions gracefully\n assembly {\n let success := delegatecall(\n gas, // forward all gas\n address, // call address of this contract\n add(fillOrderCalldata, 32), // pointer to start of input (skip array length in first 32 bytes)\n mload(fillOrderCalldata), // length of input\n fillOrderCalldata, // write output over input\n 128 // output size is 128 bytes\n )\n if success {\n mstore(fillResults, mload(fillOrderCalldata))\n mstore(add(fillResults, 32), mload(add(fillOrderCalldata, 32)))\n mstore(add(fillResults, 64), mload(add(fillOrderCalldata, 64)))\n mstore(add(fillResults, 96), mload(add(fillOrderCalldata, 96)))\n }\n }\n // fillResults values will be 0 by default if call was unsuccessful\n return fillResults;\n }\n\n /// @dev Synchronously executes multiple calls of fillOrder.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n /// NOTE: makerAssetFilledAmount and takerAssetFilledAmount may include amounts filled of different assets.\n function batchFillOrders(\n LibOrder.Order[] memory orders,\n uint256[] memory takerAssetFillAmounts,\n bytes[] memory signatures\n )\n public\n nonReentrant\n returns (FillResults memory totalFillResults)\n {\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n FillResults memory singleFillResults = fillOrderInternal(\n orders[i],\n takerAssetFillAmounts[i],\n signatures[i]\n );\n addFillResults(totalFillResults, singleFillResults);\n }\n return totalFillResults;\n }\n\n /// @dev Synchronously executes multiple calls of fillOrKill.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n /// NOTE: makerAssetFilledAmount and takerAssetFilledAmount may include amounts filled of different assets.\n function batchFillOrKillOrders(\n LibOrder.Order[] memory orders,\n uint256[] memory takerAssetFillAmounts,\n bytes[] memory signatures\n )\n public\n nonReentrant\n returns (FillResults memory totalFillResults)\n {\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n FillResults memory singleFillResults = fillOrKillOrderInternal(\n orders[i],\n takerAssetFillAmounts[i],\n signatures[i]\n );\n addFillResults(totalFillResults, singleFillResults);\n }\n return totalFillResults;\n }\n\n /// @dev Fills an order with specified parameters and ECDSA signature.\n /// Returns false if the transaction would otherwise revert.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n /// NOTE: makerAssetFilledAmount and takerAssetFilledAmount may include amounts filled of different assets.\n function batchFillOrdersNoThrow(\n LibOrder.Order[] memory orders,\n uint256[] memory takerAssetFillAmounts,\n bytes[] memory signatures\n )\n public\n returns (FillResults memory totalFillResults)\n {\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n FillResults memory singleFillResults = fillOrderNoThrow(\n orders[i],\n takerAssetFillAmounts[i],\n signatures[i]\n );\n addFillResults(totalFillResults, singleFillResults);\n }\n return totalFillResults;\n }\n\n /// @dev Synchronously executes multiple calls of fillOrder until total amount of takerAsset is sold by taker.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketSellOrders(\n LibOrder.Order[] memory orders,\n uint256 takerAssetFillAmount,\n bytes[] memory signatures\n )\n public\n nonReentrant\n returns (FillResults memory totalFillResults)\n {\n bytes memory takerAssetData = orders[0].takerAssetData;\n \n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n\n // We assume that asset being sold by taker is the same for each order.\n // Rather than passing this in as calldata, we use the takerAssetData from the first order in all later orders.\n orders[i].takerAssetData = takerAssetData;\n\n // Calculate the remaining amount of takerAsset to sell\n uint256 remainingTakerAssetFillAmount = safeSub(takerAssetFillAmount, totalFillResults.takerAssetFilledAmount);\n\n // Attempt to sell the remaining amount of takerAsset\n FillResults memory singleFillResults = fillOrderInternal(\n orders[i],\n remainingTakerAssetFillAmount,\n signatures[i]\n );\n\n // Update amounts filled and fees paid by maker and taker\n addFillResults(totalFillResults, singleFillResults);\n\n // Stop execution if the entire amount of takerAsset has been sold\n if (totalFillResults.takerAssetFilledAmount >= takerAssetFillAmount) {\n break;\n }\n }\n return totalFillResults;\n }\n\n /// @dev Synchronously executes multiple calls of fillOrder until total amount of takerAsset is sold by taker.\n /// Returns false if the transaction would otherwise revert.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketSellOrdersNoThrow(\n LibOrder.Order[] memory orders,\n uint256 takerAssetFillAmount,\n bytes[] memory signatures\n )\n public\n returns (FillResults memory totalFillResults)\n {\n bytes memory takerAssetData = orders[0].takerAssetData;\n\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n\n // We assume that asset being sold by taker is the same for each order.\n // Rather than passing this in as calldata, we use the takerAssetData from the first order in all later orders.\n orders[i].takerAssetData = takerAssetData;\n\n // Calculate the remaining amount of takerAsset to sell\n uint256 remainingTakerAssetFillAmount = safeSub(takerAssetFillAmount, totalFillResults.takerAssetFilledAmount);\n\n // Attempt to sell the remaining amount of takerAsset\n FillResults memory singleFillResults = fillOrderNoThrow(\n orders[i],\n remainingTakerAssetFillAmount,\n signatures[i]\n );\n\n // Update amounts filled and fees paid by maker and taker\n addFillResults(totalFillResults, singleFillResults);\n\n // Stop execution if the entire amount of takerAsset has been sold\n if (totalFillResults.takerAssetFilledAmount >= takerAssetFillAmount) {\n break;\n }\n }\n return totalFillResults;\n }\n\n /// @dev Synchronously executes multiple calls of fillOrder until total amount of makerAsset is bought by taker.\n /// @param orders Array of order specifications.\n /// @param makerAssetFillAmount Desired amount of makerAsset to buy.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketBuyOrders(\n LibOrder.Order[] memory orders,\n uint256 makerAssetFillAmount,\n bytes[] memory signatures\n )\n public\n nonReentrant\n returns (FillResults memory totalFillResults)\n {\n bytes memory makerAssetData = orders[0].makerAssetData;\n\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n\n // We assume that asset being bought by taker is the same for each order.\n // Rather than passing this in as calldata, we copy the makerAssetData from the first order onto all later orders.\n orders[i].makerAssetData = makerAssetData;\n\n // Calculate the remaining amount of makerAsset to buy\n uint256 remainingMakerAssetFillAmount = safeSub(makerAssetFillAmount, totalFillResults.makerAssetFilledAmount);\n\n // Convert the remaining amount of makerAsset to buy into remaining amount\n // of takerAsset to sell, assuming entire amount can be sold in the current order\n uint256 remainingTakerAssetFillAmount = getPartialAmountFloor(\n orders[i].takerAssetAmount,\n orders[i].makerAssetAmount,\n remainingMakerAssetFillAmount\n );\n\n // Attempt to sell the remaining amount of takerAsset\n FillResults memory singleFillResults = fillOrderInternal(\n orders[i],\n remainingTakerAssetFillAmount,\n signatures[i]\n );\n\n // Update amounts filled and fees paid by maker and taker\n addFillResults(totalFillResults, singleFillResults);\n\n // Stop execution if the entire amount of makerAsset has been bought\n if (totalFillResults.makerAssetFilledAmount >= makerAssetFillAmount) {\n break;\n }\n }\n return totalFillResults;\n }\n\n /// @dev Synchronously executes multiple fill orders in a single transaction until total amount is bought by taker.\n /// Returns false if the transaction would otherwise revert.\n /// @param orders Array of order specifications.\n /// @param makerAssetFillAmount Desired amount of makerAsset to buy.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketBuyOrdersNoThrow(\n LibOrder.Order[] memory orders,\n uint256 makerAssetFillAmount,\n bytes[] memory signatures\n )\n public\n returns (FillResults memory totalFillResults)\n {\n bytes memory makerAssetData = orders[0].makerAssetData;\n\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n\n // We assume that asset being bought by taker is the same for each order.\n // Rather than passing this in as calldata, we copy the makerAssetData from the first order onto all later orders.\n orders[i].makerAssetData = makerAssetData;\n\n // Calculate the remaining amount of makerAsset to buy\n uint256 remainingMakerAssetFillAmount = safeSub(makerAssetFillAmount, totalFillResults.makerAssetFilledAmount);\n\n // Convert the remaining amount of makerAsset to buy into remaining amount\n // of takerAsset to sell, assuming entire amount can be sold in the current order\n uint256 remainingTakerAssetFillAmount = getPartialAmountFloor(\n orders[i].takerAssetAmount,\n orders[i].makerAssetAmount,\n remainingMakerAssetFillAmount\n );\n\n // Attempt to sell the remaining amount of takerAsset\n FillResults memory singleFillResults = fillOrderNoThrow(\n orders[i],\n remainingTakerAssetFillAmount,\n signatures[i]\n );\n\n // Update amounts filled and fees paid by maker and taker\n addFillResults(totalFillResults, singleFillResults);\n\n // Stop execution if the entire amount of makerAsset has been bought\n if (totalFillResults.makerAssetFilledAmount >= makerAssetFillAmount) {\n break;\n }\n }\n return totalFillResults;\n }\n\n /// @dev Synchronously cancels multiple orders in a single transaction.\n /// @param orders Array of order specifications.\n function batchCancelOrders(LibOrder.Order[] memory orders)\n public\n nonReentrant\n {\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n cancelOrderInternal(orders[i]);\n }\n }\n\n /// @dev Fetches information for all passed in orders.\n /// @param orders Array of order specifications.\n /// @return Array of OrderInfo instances that correspond to each order.\n function getOrdersInfo(LibOrder.Order[] memory orders)\n public\n view\n returns (LibOrder.OrderInfo[] memory)\n {\n uint256 ordersLength = orders.length;\n LibOrder.OrderInfo[] memory ordersInfo = new LibOrder.OrderInfo[](ordersLength);\n for (uint256 i = 0; i != ordersLength; i++) {\n ordersInfo[i] = getOrderInfo(orders[i]);\n }\n return ordersInfo;\n }\n\n /// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n function fillOrKillOrderInternal(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n internal\n returns (FillResults memory fillResults)\n {\n fillResults = fillOrderInternal(\n order,\n takerAssetFillAmount,\n signature\n );\n require(\n fillResults.takerAssetFilledAmount == takerAssetFillAmount,\n \"COMPLETE_FILL_FAILED\"\n );\n return fillResults;\n }\n}\n", - "2.0.0/protocol/Exchange/interfaces/IAssetProxyDispatcher.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\ncontract IAssetProxyDispatcher {\n\n /// @dev Registers an asset proxy to its asset proxy id.\n /// Once an asset proxy is registered, it cannot be unregistered.\n /// @param assetProxy Address of new asset proxy to register.\n function registerAssetProxy(address assetProxy)\n external;\n\n /// @dev Gets an asset proxy.\n /// @param assetProxyId Id of the asset proxy.\n /// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered.\n function getAssetProxy(bytes4 assetProxyId)\n external\n view\n returns (address);\n}\n", - "2.0.0/protocol/Exchange/interfaces/IExchange.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"./IExchangeCore.sol\";\nimport \"./IMatchOrders.sol\";\nimport \"./ISignatureValidator.sol\";\nimport \"./ITransactions.sol\";\nimport \"./IAssetProxyDispatcher.sol\";\nimport \"./IWrapperFunctions.sol\";\n\n\n// solhint-disable no-empty-blocks\ncontract IExchange is\n IExchangeCore,\n IMatchOrders,\n ISignatureValidator,\n ITransactions,\n IAssetProxyDispatcher,\n IWrapperFunctions\n{}\n", - "2.0.0/protocol/Exchange/interfaces/IExchangeCore.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../libs/LibOrder.sol\";\nimport \"../libs/LibFillResults.sol\";\n\n\ncontract IExchangeCore {\n\n /// @dev Cancels all orders created by makerAddress with a salt less than or equal to the targetOrderEpoch\n /// and senderAddress equal to msg.sender (or null address if msg.sender == makerAddress).\n /// @param targetOrderEpoch Orders created with a salt less or equal to this value will be cancelled.\n function cancelOrdersUpTo(uint256 targetOrderEpoch)\n external;\n\n /// @dev Fills the input order.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n /// @return Amounts filled and fees paid by maker and taker.\n function fillOrder(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n public\n returns (LibFillResults.FillResults memory fillResults);\n\n /// @dev After calling, the order can not be filled anymore.\n /// @param order Order struct containing order specifications.\n function cancelOrder(LibOrder.Order memory order)\n public;\n\n /// @dev Gets information about an order: status, hash, and amount filled.\n /// @param order Order to gather information on.\n /// @return OrderInfo Information about the order and its state.\n /// See LibOrder.OrderInfo for a complete description.\n function getOrderInfo(LibOrder.Order memory order)\n public\n view\n returns (LibOrder.OrderInfo memory orderInfo);\n}\n", - "2.0.0/protocol/Exchange/interfaces/IMatchOrders.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../libs/LibOrder.sol\";\nimport \"../libs/LibFillResults.sol\";\n\n\ncontract IMatchOrders {\n\n /// @dev Match two complementary orders that have a profitable spread.\n /// Each order is filled at their respective price point. However, the calculations are\n /// carried out as though the orders are both being filled at the right order's price point.\n /// The profit made by the left order goes to the taker (who matched the two orders).\n /// @param leftOrder First order to match.\n /// @param rightOrder Second order to match.\n /// @param leftSignature Proof that order was created by the left maker.\n /// @param rightSignature Proof that order was created by the right maker.\n /// @return matchedFillResults Amounts filled and fees paid by maker and taker of matched orders.\n function matchOrders(\n LibOrder.Order memory leftOrder,\n LibOrder.Order memory rightOrder,\n bytes memory leftSignature,\n bytes memory rightSignature\n )\n public\n returns (LibFillResults.MatchedFillResults memory matchedFillResults);\n}\n", - "2.0.0/protocol/Exchange/interfaces/ISignatureValidator.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\ncontract ISignatureValidator {\n\n /// @dev Approves a hash on-chain using any valid signature type.\n /// After presigning a hash, the preSign signature type will become valid for that hash and signer.\n /// @param signerAddress Address that should have signed the given hash.\n /// @param signature Proof that the hash has been signed by signer.\n function preSign(\n bytes32 hash,\n address signerAddress,\n bytes signature\n )\n external;\n \n /// @dev Approves/unnapproves a Validator contract to verify signatures on signer's behalf.\n /// @param validatorAddress Address of Validator contract.\n /// @param approval Approval or disapproval of Validator contract.\n function setSignatureValidatorApproval(\n address validatorAddress,\n bool approval\n )\n external;\n\n /// @dev Verifies that a signature is valid.\n /// @param hash Message hash that is signed.\n /// @param signerAddress Address of signer.\n /// @param signature Proof of signing.\n /// @return Validity of order signature.\n function isValidSignature(\n bytes32 hash,\n address signerAddress,\n bytes memory signature\n )\n public\n view\n returns (bool isValid);\n}\n", - "2.0.0/protocol/Exchange/interfaces/ITransactions.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\npragma solidity 0.4.24;\n\n\ncontract ITransactions {\n\n /// @dev Executes an exchange method call in the context of signer.\n /// @param salt Arbitrary number to ensure uniqueness of transaction hash.\n /// @param signerAddress Address of transaction signer.\n /// @param data AbiV2 encoded calldata.\n /// @param signature Proof of signer transaction by signer.\n function executeTransaction(\n uint256 salt,\n address signerAddress,\n bytes data,\n bytes signature\n )\n external;\n}\n", - "2.0.0/protocol/Exchange/interfaces/IValidator.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\ncontract IValidator {\n\n /// @dev Verifies that a signature is valid.\n /// @param hash Message hash that is signed.\n /// @param signerAddress Address that should have signed the given hash.\n /// @param signature Proof of signing.\n /// @return Validity of order signature.\n function isValidSignature(\n bytes32 hash,\n address signerAddress,\n bytes signature\n )\n external\n view\n returns (bool isValid);\n}\n", - "2.0.0/protocol/Exchange/interfaces/IWallet.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\ncontract IWallet {\n\n /// @dev Verifies that a signature is valid.\n /// @param hash Message hash that is signed.\n /// @param signature Proof of signing.\n /// @return Validity of order signature.\n function isValidSignature(\n bytes32 hash,\n bytes signature\n )\n external\n view\n returns (bool isValid);\n}\n", - "2.0.0/protocol/Exchange/interfaces/IWrapperFunctions.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../libs/LibOrder.sol\";\nimport \"../libs/LibFillResults.sol\";\n\n\ncontract IWrapperFunctions {\n\n /// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.\n /// @param order LibOrder.Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n function fillOrKillOrder(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n public\n returns (LibFillResults.FillResults memory fillResults);\n\n /// @dev Fills an order with specified parameters and ECDSA signature.\n /// Returns false if the transaction would otherwise revert.\n /// @param order LibOrder.Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n /// @return Amounts filled and fees paid by maker and taker.\n function fillOrderNoThrow(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n public\n returns (LibFillResults.FillResults memory fillResults);\n\n /// @dev Synchronously executes multiple calls of fillOrder.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function batchFillOrders(\n LibOrder.Order[] memory orders,\n uint256[] memory takerAssetFillAmounts,\n bytes[] memory signatures\n )\n public\n returns (LibFillResults.FillResults memory totalFillResults);\n\n /// @dev Synchronously executes multiple calls of fillOrKill.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function batchFillOrKillOrders(\n LibOrder.Order[] memory orders,\n uint256[] memory takerAssetFillAmounts,\n bytes[] memory signatures\n )\n public\n returns (LibFillResults.FillResults memory totalFillResults);\n\n /// @dev Fills an order with specified parameters and ECDSA signature.\n /// Returns false if the transaction would otherwise revert.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function batchFillOrdersNoThrow(\n LibOrder.Order[] memory orders,\n uint256[] memory takerAssetFillAmounts,\n bytes[] memory signatures\n )\n public\n returns (LibFillResults.FillResults memory totalFillResults);\n\n /// @dev Synchronously executes multiple calls of fillOrder until total amount of takerAsset is sold by taker.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketSellOrders(\n LibOrder.Order[] memory orders,\n uint256 takerAssetFillAmount,\n bytes[] memory signatures\n )\n public\n returns (LibFillResults.FillResults memory totalFillResults);\n\n /// @dev Synchronously executes multiple calls of fillOrder until total amount of takerAsset is sold by taker.\n /// Returns false if the transaction would otherwise revert.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketSellOrdersNoThrow(\n LibOrder.Order[] memory orders,\n uint256 takerAssetFillAmount,\n bytes[] memory signatures\n )\n public\n returns (LibFillResults.FillResults memory totalFillResults);\n\n /// @dev Synchronously executes multiple calls of fillOrder until total amount of makerAsset is bought by taker.\n /// @param orders Array of order specifications.\n /// @param makerAssetFillAmount Desired amount of makerAsset to buy.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketBuyOrders(\n LibOrder.Order[] memory orders,\n uint256 makerAssetFillAmount,\n bytes[] memory signatures\n )\n public\n returns (LibFillResults.FillResults memory totalFillResults);\n\n /// @dev Synchronously executes multiple fill orders in a single transaction until total amount is bought by taker.\n /// Returns false if the transaction would otherwise revert.\n /// @param orders Array of order specifications.\n /// @param makerAssetFillAmount Desired amount of makerAsset to buy.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketBuyOrdersNoThrow(\n LibOrder.Order[] memory orders,\n uint256 makerAssetFillAmount,\n bytes[] memory signatures\n )\n public\n returns (LibFillResults.FillResults memory totalFillResults);\n\n /// @dev Synchronously cancels multiple orders in a single transaction.\n /// @param orders Array of order specifications.\n function batchCancelOrders(LibOrder.Order[] memory orders)\n public;\n\n /// @dev Fetches information for all passed in orders\n /// @param orders Array of order specifications.\n /// @return Array of OrderInfo instances that correspond to each order.\n function getOrdersInfo(LibOrder.Order[] memory orders)\n public\n view\n returns (LibOrder.OrderInfo[] memory);\n}\n", - "2.0.0/protocol/Exchange/libs/LibAbiEncoder.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"./LibOrder.sol\";\n\n\ncontract LibAbiEncoder {\n\n /// @dev ABI encodes calldata for `fillOrder`.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n /// @return ABI encoded calldata for `fillOrder`.\n function abiEncodeFillOrder(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n internal\n pure\n returns (bytes memory fillOrderCalldata)\n {\n // We need to call MExchangeCore.fillOrder using a delegatecall in\n // assembly so that we can intercept a call that throws. For this, we\n // need the input encoded in memory in the Ethereum ABIv2 format [1].\n\n // | Area | Offset | Length | Contents |\n // | -------- |--------|---------|-------------------------------------------- |\n // | Header | 0x00 | 4 | function selector |\n // | Params | | 3 * 32 | function parameters: |\n // | | 0x00 | | 1. offset to order (*) |\n // | | 0x20 | | 2. takerAssetFillAmount |\n // | | 0x40 | | 3. offset to signature (*) |\n // | Data | | 12 * 32 | order: |\n // | | 0x000 | | 1. senderAddress |\n // | | 0x020 | | 2. makerAddress |\n // | | 0x040 | | 3. takerAddress |\n // | | 0x060 | | 4. feeRecipientAddress |\n // | | 0x080 | | 5. makerAssetAmount |\n // | | 0x0A0 | | 6. takerAssetAmount |\n // | | 0x0C0 | | 7. makerFeeAmount |\n // | | 0x0E0 | | 8. takerFeeAmount |\n // | | 0x100 | | 9. expirationTimeSeconds |\n // | | 0x120 | | 10. salt |\n // | | 0x140 | | 11. Offset to makerAssetData (*) |\n // | | 0x160 | | 12. Offset to takerAssetData (*) |\n // | | 0x180 | 32 | makerAssetData Length |\n // | | 0x1A0 | ** | makerAssetData Contents |\n // | | 0x1C0 | 32 | takerAssetData Length |\n // | | 0x1E0 | ** | takerAssetData Contents |\n // | | 0x200 | 32 | signature Length |\n // | | 0x220 | ** | signature Contents |\n\n // * Offsets are calculated from the beginning of the current area: Header, Params, Data:\n // An offset stored in the Params area is calculated from the beginning of the Params section.\n // An offset stored in the Data area is calculated from the beginning of the Data section.\n\n // ** The length of dynamic array contents are stored in the field immediately preceeding the contents.\n\n // [1]: https://solidity.readthedocs.io/en/develop/abi-spec.html\n\n assembly {\n\n // Areas below may use the following variables:\n // 1. Start -- Start of this area in memory\n // 2. End -- End of this area in memory. This value may\n // be precomputed (before writing contents),\n // or it may be computed as contents are written.\n // 3. Offset -- Current offset into area. If an area's End\n // is precomputed, this variable tracks the\n // offsets of contents as they are written.\n\n /////// Setup Header Area ///////\n // Load free memory pointer\n fillOrderCalldata := mload(0x40)\n // bytes4(keccak256(\"fillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes)\"))\n // = 0xb4be83d5\n // Leave 0x20 bytes to store the length\n mstore(add(fillOrderCalldata, 0x20), 0xb4be83d500000000000000000000000000000000000000000000000000000000)\n let headerAreaEnd := add(fillOrderCalldata, 0x24)\n\n /////// Setup Params Area ///////\n // This area is preallocated and written to later.\n // This is because we need to fill in offsets that have not yet been calculated.\n let paramsAreaStart := headerAreaEnd\n let paramsAreaEnd := add(paramsAreaStart, 0x60)\n let paramsAreaOffset := paramsAreaStart\n\n /////// Setup Data Area ///////\n let dataAreaStart := paramsAreaEnd\n let dataAreaEnd := dataAreaStart\n\n // Offset from the source data we're reading from\n let sourceOffset := order\n // arrayLenBytes and arrayLenWords track the length of a dynamically-allocated bytes array.\n let arrayLenBytes := 0\n let arrayLenWords := 0\n\n /////// Write order Struct ///////\n // Write memory location of Order, relative to the start of the\n // parameter list, then increment the paramsAreaOffset respectively.\n mstore(paramsAreaOffset, sub(dataAreaEnd, paramsAreaStart))\n paramsAreaOffset := add(paramsAreaOffset, 0x20)\n\n // Write values for each field in the order\n // It would be nice to use a loop, but we save on gas by writing\n // the stores sequentially.\n mstore(dataAreaEnd, mload(sourceOffset)) // makerAddress\n mstore(add(dataAreaEnd, 0x20), mload(add(sourceOffset, 0x20))) // takerAddress\n mstore(add(dataAreaEnd, 0x40), mload(add(sourceOffset, 0x40))) // feeRecipientAddress\n mstore(add(dataAreaEnd, 0x60), mload(add(sourceOffset, 0x60))) // senderAddress\n mstore(add(dataAreaEnd, 0x80), mload(add(sourceOffset, 0x80))) // makerAssetAmount\n mstore(add(dataAreaEnd, 0xA0), mload(add(sourceOffset, 0xA0))) // takerAssetAmount\n mstore(add(dataAreaEnd, 0xC0), mload(add(sourceOffset, 0xC0))) // makerFeeAmount\n mstore(add(dataAreaEnd, 0xE0), mload(add(sourceOffset, 0xE0))) // takerFeeAmount\n mstore(add(dataAreaEnd, 0x100), mload(add(sourceOffset, 0x100))) // expirationTimeSeconds\n mstore(add(dataAreaEnd, 0x120), mload(add(sourceOffset, 0x120))) // salt\n mstore(add(dataAreaEnd, 0x140), mload(add(sourceOffset, 0x140))) // Offset to makerAssetData\n mstore(add(dataAreaEnd, 0x160), mload(add(sourceOffset, 0x160))) // Offset to takerAssetData\n dataAreaEnd := add(dataAreaEnd, 0x180)\n sourceOffset := add(sourceOffset, 0x180)\n\n // Write offset to \n mstore(add(dataAreaStart, mul(10, 0x20)), sub(dataAreaEnd, dataAreaStart))\n\n // Calculate length of \n sourceOffset := mload(add(order, 0x140)) // makerAssetData\n arrayLenBytes := mload(sourceOffset)\n sourceOffset := add(sourceOffset, 0x20)\n arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20)\n\n // Write length of \n mstore(dataAreaEnd, arrayLenBytes)\n dataAreaEnd := add(dataAreaEnd, 0x20)\n\n // Write contents of \n for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} {\n mstore(dataAreaEnd, mload(sourceOffset))\n dataAreaEnd := add(dataAreaEnd, 0x20)\n sourceOffset := add(sourceOffset, 0x20)\n }\n\n // Write offset to \n mstore(add(dataAreaStart, mul(11, 0x20)), sub(dataAreaEnd, dataAreaStart))\n\n // Calculate length of \n sourceOffset := mload(add(order, 0x160)) // takerAssetData\n arrayLenBytes := mload(sourceOffset)\n sourceOffset := add(sourceOffset, 0x20)\n arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20)\n\n // Write length of \n mstore(dataAreaEnd, arrayLenBytes)\n dataAreaEnd := add(dataAreaEnd, 0x20)\n\n // Write contents of \n for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} {\n mstore(dataAreaEnd, mload(sourceOffset))\n dataAreaEnd := add(dataAreaEnd, 0x20)\n sourceOffset := add(sourceOffset, 0x20)\n }\n\n /////// Write takerAssetFillAmount ///////\n mstore(paramsAreaOffset, takerAssetFillAmount)\n paramsAreaOffset := add(paramsAreaOffset, 0x20)\n\n /////// Write signature ///////\n // Write offset to paramsArea\n mstore(paramsAreaOffset, sub(dataAreaEnd, paramsAreaStart))\n\n // Calculate length of signature\n sourceOffset := signature\n arrayLenBytes := mload(sourceOffset)\n sourceOffset := add(sourceOffset, 0x20)\n arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20)\n\n // Write length of signature\n mstore(dataAreaEnd, arrayLenBytes)\n dataAreaEnd := add(dataAreaEnd, 0x20)\n\n // Write contents of signature\n for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} {\n mstore(dataAreaEnd, mload(sourceOffset))\n dataAreaEnd := add(dataAreaEnd, 0x20)\n sourceOffset := add(sourceOffset, 0x20)\n }\n\n // Set length of calldata\n mstore(fillOrderCalldata, sub(dataAreaEnd, add(fillOrderCalldata, 0x20)))\n\n // Increment free memory pointer\n mstore(0x40, dataAreaEnd)\n }\n\n return fillOrderCalldata;\n }\n}\n", - "2.0.0/protocol/Exchange/libs/LibConstants.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\n// solhint-disable max-line-length\ncontract LibConstants {\n \n // Asset data for ZRX token. Used for fee transfers.\n // @TODO: Hardcode constant when we deploy. Currently \n // not constant to make testing easier.\n\n // The proxyId for ZRX_ASSET_DATA is bytes4(keccak256(\"ERC20Token(address)\")) = 0xf47261b0\n \n // Kovan ZRX address is 0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570.\n // The ABI encoded proxyId and address is 0xf47261b00000000000000000000000006ff6c0ff1d68b964901f986d4c9fa3ac68346570\n // bytes constant public ZRX_ASSET_DATA = \"\\xf4\\x72\\x61\\xb0\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x6f\\xf6\\xc0\\xff\\x1d\\x68\\xb9\\x64\\x90\\x1f\\x98\\x6d\\x4c\\x9f\\xa3\\xac\\x68\\x34\\x65\\x70\";\n \n // Mainnet ZRX address is 0xe41d2489571d322189246dafa5ebde1f4699f498.\n // The ABI encoded proxyId and address is 0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498\n bytes constant public ZRX_ASSET_DATA = \"\\xf4\\x72\\x61\\xb0\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xe4\\x1d\\x24\\x89\\x57\\x1d\\x32\\x21\\x89\\x24\\x6d\\xaf\\xa5\\xeb\\xde\\x1f\\x46\\x99\\xf4\\x98\";\n}\n// solhint-enable max-line-length\n", - "2.0.0/protocol/Exchange/libs/LibEIP712.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\ncontract LibEIP712 {\n\n // EIP191 header for EIP712 prefix\n string constant internal EIP191_HEADER = \"\\x19\\x01\";\n\n // EIP712 Domain Name value\n string constant internal EIP712_DOMAIN_NAME = \"0x Protocol\";\n\n // EIP712 Domain Version value\n string constant internal EIP712_DOMAIN_VERSION = \"2\";\n\n // Hash of the EIP712 Domain Separator Schema\n bytes32 constant internal EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256(abi.encodePacked(\n \"EIP712Domain(\",\n \"string name,\",\n \"string version,\",\n \"address verifyingContract\",\n \")\"\n ));\n\n // Hash of the EIP712 Domain Separator data\n // solhint-disable-next-line var-name-mixedcase\n bytes32 public EIP712_DOMAIN_HASH;\n\n constructor ()\n public\n {\n EIP712_DOMAIN_HASH = keccak256(abi.encodePacked(\n EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH,\n keccak256(bytes(EIP712_DOMAIN_NAME)),\n keccak256(bytes(EIP712_DOMAIN_VERSION)),\n bytes32(address(this))\n ));\n }\n\n /// @dev Calculates EIP712 encoding for a hash struct in this EIP712 Domain.\n /// @param hashStruct The EIP712 hash struct.\n /// @return EIP712 hash applied to this EIP712 Domain.\n function hashEIP712Message(bytes32 hashStruct)\n internal\n view\n returns (bytes32 result)\n {\n bytes32 eip712DomainHash = EIP712_DOMAIN_HASH;\n\n // Assembly for more efficient computing:\n // keccak256(abi.encodePacked(\n // EIP191_HEADER,\n // EIP712_DOMAIN_HASH,\n // hashStruct \n // ));\n\n assembly {\n // Load free memory pointer\n let memPtr := mload(64)\n\n mstore(memPtr, 0x1901000000000000000000000000000000000000000000000000000000000000) // EIP191 header\n mstore(add(memPtr, 2), eip712DomainHash) // EIP712 domain hash\n mstore(add(memPtr, 34), hashStruct) // Hash of struct\n\n // Compute hash\n result := keccak256(memPtr, 66)\n }\n return result;\n }\n}\n", - "2.0.0/protocol/Exchange/libs/LibExchangeErrors.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\n// solhint-disable\npragma solidity 0.4.24;\n\n\n/// @dev This contract documents the revert reasons used in the Exchange contract.\n/// This contract is intended to serve as a reference, but is not actually used for efficiency reasons.\ncontract LibExchangeErrors {\n\n /// Order validation errors ///\n string constant ORDER_UNFILLABLE = \"ORDER_UNFILLABLE\"; // Order cannot be filled.\n string constant INVALID_MAKER = \"INVALID_MAKER\"; // Invalid makerAddress.\n string constant INVALID_TAKER = \"INVALID_TAKER\"; // Invalid takerAddress.\n string constant INVALID_SENDER = \"INVALID_SENDER\"; // Invalid `msg.sender`.\n string constant INVALID_ORDER_SIGNATURE = \"INVALID_ORDER_SIGNATURE\"; // Signature validation failed. \n \n /// fillOrder validation errors ///\n string constant INVALID_TAKER_AMOUNT = \"INVALID_TAKER_AMOUNT\"; // takerAssetFillAmount cannot equal 0.\n string constant ROUNDING_ERROR = \"ROUNDING_ERROR\"; // Rounding error greater than 0.1% of takerAssetFillAmount. \n \n /// Signature validation errors ///\n string constant INVALID_SIGNATURE = \"INVALID_SIGNATURE\"; // Signature validation failed. \n string constant SIGNATURE_ILLEGAL = \"SIGNATURE_ILLEGAL\"; // Signature type is illegal.\n string constant SIGNATURE_UNSUPPORTED = \"SIGNATURE_UNSUPPORTED\"; // Signature type unsupported.\n \n /// cancelOrdersUptTo errors ///\n string constant INVALID_NEW_ORDER_EPOCH = \"INVALID_NEW_ORDER_EPOCH\"; // Specified salt must be greater than or equal to existing orderEpoch.\n\n /// fillOrKillOrder errors ///\n string constant COMPLETE_FILL_FAILED = \"COMPLETE_FILL_FAILED\"; // Desired takerAssetFillAmount could not be completely filled. \n\n /// matchOrders errors ///\n string constant NEGATIVE_SPREAD_REQUIRED = \"NEGATIVE_SPREAD_REQUIRED\"; // Matched orders must have a negative spread.\n\n /// Transaction errors ///\n string constant REENTRANCY_ILLEGAL = \"REENTRANCY_ILLEGAL\"; // Recursive reentrancy is not allowed. \n string constant INVALID_TX_HASH = \"INVALID_TX_HASH\"; // Transaction has already been executed. \n string constant INVALID_TX_SIGNATURE = \"INVALID_TX_SIGNATURE\"; // Signature validation failed. \n string constant FAILED_EXECUTION = \"FAILED_EXECUTION\"; // Transaction execution failed. \n \n /// registerAssetProxy errors ///\n string constant ASSET_PROXY_ALREADY_EXISTS = \"ASSET_PROXY_ALREADY_EXISTS\"; // AssetProxy with same id already exists.\n\n /// dispatchTransferFrom errors ///\n string constant ASSET_PROXY_DOES_NOT_EXIST = \"ASSET_PROXY_DOES_NOT_EXIST\"; // No assetProxy registered at given id.\n string constant TRANSFER_FAILED = \"TRANSFER_FAILED\"; // Asset transfer unsuccesful.\n\n /// Length validation errors ///\n string constant LENGTH_GREATER_THAN_0_REQUIRED = \"LENGTH_GREATER_THAN_0_REQUIRED\"; // Byte array must have a length greater than 0.\n string constant LENGTH_GREATER_THAN_3_REQUIRED = \"LENGTH_GREATER_THAN_3_REQUIRED\"; // Byte array must have a length greater than 3.\n string constant LENGTH_0_REQUIRED = \"LENGTH_0_REQUIRED\"; // Byte array must have a length of 0.\n string constant LENGTH_65_REQUIRED = \"LENGTH_65_REQUIRED\"; // Byte array must have a length of 65.\n}\n", - "2.0.0/protocol/Exchange/libs/LibFillResults.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../../utils/SafeMath/SafeMath.sol\";\n\n\ncontract LibFillResults is\n SafeMath\n{\n struct FillResults {\n uint256 makerAssetFilledAmount; // Total amount of makerAsset(s) filled.\n uint256 takerAssetFilledAmount; // Total amount of takerAsset(s) filled.\n uint256 makerFeePaid; // Total amount of ZRX paid by maker(s) to feeRecipient(s).\n uint256 takerFeePaid; // Total amount of ZRX paid by taker to feeRecipients(s).\n }\n\n struct MatchedFillResults {\n FillResults left; // Amounts filled and fees paid of left order.\n FillResults right; // Amounts filled and fees paid of right order.\n uint256 leftMakerAssetSpreadAmount; // Spread between price of left and right order, denominated in the left order's makerAsset, paid to taker.\n }\n\n /// @dev Adds properties of both FillResults instances.\n /// Modifies the first FillResults instance specified.\n /// @param totalFillResults Fill results instance that will be added onto.\n /// @param singleFillResults Fill results instance that will be added to totalFillResults.\n function addFillResults(FillResults memory totalFillResults, FillResults memory singleFillResults)\n internal\n pure\n {\n totalFillResults.makerAssetFilledAmount = safeAdd(totalFillResults.makerAssetFilledAmount, singleFillResults.makerAssetFilledAmount);\n totalFillResults.takerAssetFilledAmount = safeAdd(totalFillResults.takerAssetFilledAmount, singleFillResults.takerAssetFilledAmount);\n totalFillResults.makerFeePaid = safeAdd(totalFillResults.makerFeePaid, singleFillResults.makerFeePaid);\n totalFillResults.takerFeePaid = safeAdd(totalFillResults.takerFeePaid, singleFillResults.takerFeePaid);\n }\n}\n", - "2.0.0/protocol/Exchange/libs/LibMath.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../../utils/SafeMath/SafeMath.sol\";\n\n\ncontract LibMath is\n SafeMath\n{\n /// @dev Calculates partial value given a numerator and denominator rounded down.\n /// Reverts if rounding error is >= 0.1%\n /// @param numerator Numerator.\n /// @param denominator Denominator.\n /// @param target Value to calculate partial of.\n /// @return Partial value of target rounded down.\n function safeGetPartialAmountFloor(\n uint256 numerator,\n uint256 denominator,\n uint256 target\n )\n internal\n pure\n returns (uint256 partialAmount)\n {\n require(\n denominator > 0,\n \"DIVISION_BY_ZERO\"\n );\n\n require(\n !isRoundingErrorFloor(\n numerator,\n denominator,\n target\n ),\n \"ROUNDING_ERROR\"\n );\n \n partialAmount = safeDiv(\n safeMul(numerator, target),\n denominator\n );\n return partialAmount;\n }\n\n /// @dev Calculates partial value given a numerator and denominator rounded down.\n /// Reverts if rounding error is >= 0.1%\n /// @param numerator Numerator.\n /// @param denominator Denominator.\n /// @param target Value to calculate partial of.\n /// @return Partial value of target rounded up.\n function safeGetPartialAmountCeil(\n uint256 numerator,\n uint256 denominator,\n uint256 target\n )\n internal\n pure\n returns (uint256 partialAmount)\n {\n require(\n denominator > 0,\n \"DIVISION_BY_ZERO\"\n );\n\n require(\n !isRoundingErrorCeil(\n numerator,\n denominator,\n target\n ),\n \"ROUNDING_ERROR\"\n );\n \n // safeDiv computes `floor(a / b)`. We use the identity (a, b integer):\n // ceil(a / b) = floor((a + b - 1) / b)\n // To implement `ceil(a / b)` using safeDiv.\n partialAmount = safeDiv(\n safeAdd(\n safeMul(numerator, target),\n safeSub(denominator, 1)\n ),\n denominator\n );\n return partialAmount;\n }\n\n /// @dev Calculates partial value given a numerator and denominator rounded down.\n /// @param numerator Numerator.\n /// @param denominator Denominator.\n /// @param target Value to calculate partial of.\n /// @return Partial value of target rounded down.\n function getPartialAmountFloor(\n uint256 numerator,\n uint256 denominator,\n uint256 target\n )\n internal\n pure\n returns (uint256 partialAmount)\n {\n require(\n denominator > 0,\n \"DIVISION_BY_ZERO\"\n );\n\n partialAmount = safeDiv(\n safeMul(numerator, target),\n denominator\n );\n return partialAmount;\n }\n \n /// @dev Calculates partial value given a numerator and denominator rounded down.\n /// @param numerator Numerator.\n /// @param denominator Denominator.\n /// @param target Value to calculate partial of.\n /// @return Partial value of target rounded up.\n function getPartialAmountCeil(\n uint256 numerator,\n uint256 denominator,\n uint256 target\n )\n internal\n pure\n returns (uint256 partialAmount)\n {\n require(\n denominator > 0,\n \"DIVISION_BY_ZERO\"\n );\n\n // safeDiv computes `floor(a / b)`. We use the identity (a, b integer):\n // ceil(a / b) = floor((a + b - 1) / b)\n // To implement `ceil(a / b)` using safeDiv.\n partialAmount = safeDiv(\n safeAdd(\n safeMul(numerator, target),\n safeSub(denominator, 1)\n ),\n denominator\n );\n return partialAmount;\n }\n \n /// @dev Checks if rounding error >= 0.1% when rounding down.\n /// @param numerator Numerator.\n /// @param denominator Denominator.\n /// @param target Value to multiply with numerator/denominator.\n /// @return Rounding error is present.\n function isRoundingErrorFloor(\n uint256 numerator,\n uint256 denominator,\n uint256 target\n )\n internal\n pure\n returns (bool isError)\n {\n require(\n denominator > 0,\n \"DIVISION_BY_ZERO\"\n );\n \n // The absolute rounding error is the difference between the rounded\n // value and the ideal value. The relative rounding error is the\n // absolute rounding error divided by the absolute value of the\n // ideal value. This is undefined when the ideal value is zero.\n //\n // The ideal value is `numerator * target / denominator`.\n // Let's call `numerator * target % denominator` the remainder.\n // The absolute error is `remainder / denominator`.\n //\n // When the ideal value is zero, we require the absolute error to\n // be zero. Fortunately, this is always the case. The ideal value is\n // zero iff `numerator == 0` and/or `target == 0`. In this case the\n // remainder and absolute error are also zero. \n if (target == 0 || numerator == 0) {\n return false;\n }\n \n // Otherwise, we want the relative rounding error to be strictly\n // less than 0.1%.\n // The relative error is `remainder / (numerator * target)`.\n // We want the relative error less than 1 / 1000:\n // remainder / (numerator * denominator) < 1 / 1000\n // or equivalently:\n // 1000 * remainder < numerator * target\n // so we have a rounding error iff:\n // 1000 * remainder >= numerator * target\n uint256 remainder = mulmod(\n target,\n numerator,\n denominator\n );\n isError = safeMul(1000, remainder) >= safeMul(numerator, target);\n return isError;\n }\n \n /// @dev Checks if rounding error >= 0.1% when rounding up.\n /// @param numerator Numerator.\n /// @param denominator Denominator.\n /// @param target Value to multiply with numerator/denominator.\n /// @return Rounding error is present.\n function isRoundingErrorCeil(\n uint256 numerator,\n uint256 denominator,\n uint256 target\n )\n internal\n pure\n returns (bool isError)\n {\n require(\n denominator > 0,\n \"DIVISION_BY_ZERO\"\n );\n \n // See the comments in `isRoundingError`.\n if (target == 0 || numerator == 0) {\n // When either is zero, the ideal value and rounded value are zero\n // and there is no rounding error. (Although the relative error\n // is undefined.)\n return false;\n }\n // Compute remainder as before\n uint256 remainder = mulmod(\n target,\n numerator,\n denominator\n );\n remainder = safeSub(denominator, remainder) % denominator;\n isError = safeMul(1000, remainder) >= safeMul(numerator, target);\n return isError;\n }\n}\n", - "2.0.0/protocol/Exchange/libs/LibOrder.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"./LibEIP712.sol\";\n\n\ncontract LibOrder is\n LibEIP712\n{\n // Hash for the EIP712 Order Schema\n bytes32 constant internal EIP712_ORDER_SCHEMA_HASH = keccak256(abi.encodePacked(\n \"Order(\",\n \"address makerAddress,\",\n \"address takerAddress,\",\n \"address feeRecipientAddress,\",\n \"address senderAddress,\",\n \"uint256 makerAssetAmount,\",\n \"uint256 takerAssetAmount,\",\n \"uint256 makerFee,\",\n \"uint256 takerFee,\",\n \"uint256 expirationTimeSeconds,\",\n \"uint256 salt,\",\n \"bytes makerAssetData,\",\n \"bytes takerAssetData\",\n \")\"\n ));\n\n // A valid order remains fillable until it is expired, fully filled, or cancelled.\n // An order's state is unaffected by external factors, like account balances.\n enum OrderStatus {\n INVALID, // Default value\n INVALID_MAKER_ASSET_AMOUNT, // Order does not have a valid maker asset amount\n INVALID_TAKER_ASSET_AMOUNT, // Order does not have a valid taker asset amount\n FILLABLE, // Order is fillable\n EXPIRED, // Order has already expired\n FULLY_FILLED, // Order is fully filled\n CANCELLED // Order has been cancelled\n }\n\n // solhint-disable max-line-length\n struct Order {\n address makerAddress; // Address that created the order. \n address takerAddress; // Address that is allowed to fill the order. If set to 0, any address is allowed to fill the order. \n address feeRecipientAddress; // Address that will recieve fees when order is filled. \n address senderAddress; // Address that is allowed to call Exchange contract methods that affect this order. If set to 0, any address is allowed to call these methods.\n uint256 makerAssetAmount; // Amount of makerAsset being offered by maker. Must be greater than 0. \n uint256 takerAssetAmount; // Amount of takerAsset being bid on by maker. Must be greater than 0. \n uint256 makerFee; // Amount of ZRX paid to feeRecipient by maker when order is filled. If set to 0, no transfer of ZRX from maker to feeRecipient will be attempted.\n uint256 takerFee; // Amount of ZRX paid to feeRecipient by taker when order is filled. If set to 0, no transfer of ZRX from taker to feeRecipient will be attempted.\n uint256 expirationTimeSeconds; // Timestamp in seconds at which order expires. \n uint256 salt; // Arbitrary number to facilitate uniqueness of the order's hash. \n bytes makerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring makerAsset. The last byte references the id of this proxy.\n bytes takerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring takerAsset. The last byte references the id of this proxy.\n }\n // solhint-enable max-line-length\n\n struct OrderInfo {\n uint8 orderStatus; // Status that describes order's validity and fillability.\n bytes32 orderHash; // EIP712 hash of the order (see LibOrder.getOrderHash).\n uint256 orderTakerAssetFilledAmount; // Amount of order that has already been filled.\n }\n\n /// @dev Calculates Keccak-256 hash of the order.\n /// @param order The order structure.\n /// @return Keccak-256 EIP712 hash of the order.\n function getOrderHash(Order memory order)\n internal\n view\n returns (bytes32 orderHash)\n {\n orderHash = hashEIP712Message(hashOrder(order));\n return orderHash;\n }\n\n /// @dev Calculates EIP712 hash of the order.\n /// @param order The order structure.\n /// @return EIP712 hash of the order.\n function hashOrder(Order memory order)\n internal\n pure\n returns (bytes32 result)\n {\n bytes32 schemaHash = EIP712_ORDER_SCHEMA_HASH;\n bytes32 makerAssetDataHash = keccak256(order.makerAssetData);\n bytes32 takerAssetDataHash = keccak256(order.takerAssetData);\n\n // Assembly for more efficiently computing:\n // keccak256(abi.encodePacked(\n // EIP712_ORDER_SCHEMA_HASH,\n // bytes32(order.makerAddress),\n // bytes32(order.takerAddress),\n // bytes32(order.feeRecipientAddress),\n // bytes32(order.senderAddress),\n // order.makerAssetAmount,\n // order.takerAssetAmount,\n // order.makerFee,\n // order.takerFee,\n // order.expirationTimeSeconds,\n // order.salt,\n // keccak256(order.makerAssetData),\n // keccak256(order.takerAssetData)\n // ));\n\n assembly {\n // Calculate memory addresses that will be swapped out before hashing\n let pos1 := sub(order, 32)\n let pos2 := add(order, 320)\n let pos3 := add(order, 352)\n\n // Backup\n let temp1 := mload(pos1)\n let temp2 := mload(pos2)\n let temp3 := mload(pos3)\n \n // Hash in place\n mstore(pos1, schemaHash)\n mstore(pos2, makerAssetDataHash)\n mstore(pos3, takerAssetDataHash)\n result := keccak256(pos1, 416)\n \n // Restore\n mstore(pos1, temp1)\n mstore(pos2, temp2)\n mstore(pos3, temp3)\n }\n return result;\n }\n}\n", - "2.0.0/protocol/Exchange/mixins/MAssetProxyDispatcher.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../interfaces/IAssetProxyDispatcher.sol\";\n\n\ncontract MAssetProxyDispatcher is\n IAssetProxyDispatcher\n{\n // Logs registration of new asset proxy\n event AssetProxyRegistered(\n bytes4 id, // Id of new registered AssetProxy.\n address assetProxy // Address of new registered AssetProxy.\n );\n\n /// @dev Forwards arguments to assetProxy and calls `transferFrom`. Either succeeds or throws.\n /// @param assetData Byte array encoded for the asset.\n /// @param from Address to transfer token from.\n /// @param to Address to transfer token to.\n /// @param amount Amount of token to transfer.\n function dispatchTransferFrom(\n bytes memory assetData,\n address from,\n address to,\n uint256 amount\n )\n internal;\n}\n", - "2.0.0/protocol/Exchange/mixins/MExchangeCore.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../libs/LibOrder.sol\";\nimport \"../libs/LibFillResults.sol\";\nimport \"../interfaces/IExchangeCore.sol\";\n\n\ncontract MExchangeCore is\n IExchangeCore\n{\n // Fill event is emitted whenever an order is filled.\n event Fill(\n address indexed makerAddress, // Address that created the order. \n address indexed feeRecipientAddress, // Address that received fees.\n address takerAddress, // Address that filled the order.\n address senderAddress, // Address that called the Exchange contract (msg.sender).\n uint256 makerAssetFilledAmount, // Amount of makerAsset sold by maker and bought by taker. \n uint256 takerAssetFilledAmount, // Amount of takerAsset sold by taker and bought by maker.\n uint256 makerFeePaid, // Amount of ZRX paid to feeRecipient by maker.\n uint256 takerFeePaid, // Amount of ZRX paid to feeRecipient by taker.\n bytes32 indexed orderHash, // EIP712 hash of order (see LibOrder.getOrderHash).\n bytes makerAssetData, // Encoded data specific to makerAsset. \n bytes takerAssetData // Encoded data specific to takerAsset.\n );\n\n // Cancel event is emitted whenever an individual order is cancelled.\n event Cancel(\n address indexed makerAddress, // Address that created the order. \n address indexed feeRecipientAddress, // Address that would have recieved fees if order was filled. \n address senderAddress, // Address that called the Exchange contract (msg.sender).\n bytes32 indexed orderHash, // EIP712 hash of order (see LibOrder.getOrderHash).\n bytes makerAssetData, // Encoded data specific to makerAsset. \n bytes takerAssetData // Encoded data specific to takerAsset.\n );\n\n // CancelUpTo event is emitted whenever `cancelOrdersUpTo` is executed succesfully.\n event CancelUpTo(\n address indexed makerAddress, // Orders cancelled must have been created by this address.\n address indexed senderAddress, // Orders cancelled must have a `senderAddress` equal to this address.\n uint256 orderEpoch // Orders with specified makerAddress and senderAddress with a salt less than this value are considered cancelled.\n );\n\n /// @dev Fills the input order.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n /// @return Amounts filled and fees paid by maker and taker.\n function fillOrderInternal(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n internal\n returns (LibFillResults.FillResults memory fillResults);\n\n /// @dev After calling, the order can not be filled anymore.\n /// @param order Order struct containing order specifications.\n function cancelOrderInternal(LibOrder.Order memory order)\n internal;\n\n /// @dev Updates state with results of a fill order.\n /// @param order that was filled.\n /// @param takerAddress Address of taker who filled the order.\n /// @param orderTakerAssetFilledAmount Amount of order already filled.\n /// @return fillResults Amounts filled and fees paid by maker and taker.\n function updateFilledState(\n LibOrder.Order memory order,\n address takerAddress,\n bytes32 orderHash,\n uint256 orderTakerAssetFilledAmount,\n LibFillResults.FillResults memory fillResults\n )\n internal;\n\n /// @dev Updates state with results of cancelling an order.\n /// State is only updated if the order is currently fillable.\n /// Otherwise, updating state would have no effect.\n /// @param order that was cancelled.\n /// @param orderHash Hash of order that was cancelled.\n function updateCancelledState(\n LibOrder.Order memory order,\n bytes32 orderHash\n )\n internal;\n \n /// @dev Validates context for fillOrder. Succeeds or throws.\n /// @param order to be filled.\n /// @param orderInfo OrderStatus, orderHash, and amount already filled of order.\n /// @param takerAddress Address of order taker.\n /// @param signature Proof that the orders was created by its maker.\n function assertFillableOrder(\n LibOrder.Order memory order,\n LibOrder.OrderInfo memory orderInfo,\n address takerAddress,\n bytes memory signature\n )\n internal\n view;\n \n /// @dev Validates context for fillOrder. Succeeds or throws.\n /// @param order to be filled.\n /// @param orderInfo Status, orderHash, and amount already filled of order.\n /// @param takerAssetFillAmount Desired amount of order to fill by taker.\n /// @param takerAssetFilledAmount Amount of takerAsset that will be filled.\n /// @param makerAssetFilledAmount Amount of makerAsset that will be transfered.\n function assertValidFill(\n LibOrder.Order memory order,\n LibOrder.OrderInfo memory orderInfo,\n uint256 takerAssetFillAmount,\n uint256 takerAssetFilledAmount,\n uint256 makerAssetFilledAmount\n )\n internal\n view;\n\n /// @dev Validates context for cancelOrder. Succeeds or throws.\n /// @param order to be cancelled.\n /// @param orderInfo OrderStatus, orderHash, and amount already filled of order.\n function assertValidCancel(\n LibOrder.Order memory order,\n LibOrder.OrderInfo memory orderInfo\n )\n internal\n view;\n\n /// @dev Calculates amounts filled and fees paid by maker and taker.\n /// @param order to be filled.\n /// @param takerAssetFilledAmount Amount of takerAsset that will be filled.\n /// @return fillResults Amounts filled and fees paid by maker and taker.\n function calculateFillResults(\n LibOrder.Order memory order,\n uint256 takerAssetFilledAmount\n )\n internal\n pure\n returns (LibFillResults.FillResults memory fillResults);\n\n}\n", - "2.0.0/protocol/Exchange/mixins/MMatchOrders.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../libs/LibOrder.sol\";\nimport \"../libs/LibFillResults.sol\";\nimport \"../interfaces/IMatchOrders.sol\";\n\n\ncontract MMatchOrders is\n IMatchOrders\n{\n /// @dev Validates context for matchOrders. Succeeds or throws.\n /// @param leftOrder First order to match.\n /// @param rightOrder Second order to match.\n function assertValidMatch(\n LibOrder.Order memory leftOrder,\n LibOrder.Order memory rightOrder\n )\n internal\n pure;\n\n /// @dev Calculates fill amounts for the matched orders.\n /// Each order is filled at their respective price point. However, the calculations are\n /// carried out as though the orders are both being filled at the right order's price point.\n /// The profit made by the leftOrder order goes to the taker (who matched the two orders).\n /// @param leftOrder First order to match.\n /// @param rightOrder Second order to match.\n /// @param leftOrderTakerAssetFilledAmount Amount of left order already filled.\n /// @param rightOrderTakerAssetFilledAmount Amount of right order already filled.\n /// @param matchedFillResults Amounts to fill and fees to pay by maker and taker of matched orders.\n function calculateMatchedFillResults(\n LibOrder.Order memory leftOrder,\n LibOrder.Order memory rightOrder,\n uint256 leftOrderTakerAssetFilledAmount,\n uint256 rightOrderTakerAssetFilledAmount\n )\n internal\n pure\n returns (LibFillResults.MatchedFillResults memory matchedFillResults);\n\n}\n", - "2.0.0/protocol/Exchange/mixins/MSignatureValidator.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../interfaces/ISignatureValidator.sol\";\n\n\ncontract MSignatureValidator is\n ISignatureValidator\n{\n event SignatureValidatorApproval(\n address indexed signerAddress, // Address that approves or disapproves a contract to verify signatures.\n address indexed validatorAddress, // Address of signature validator contract.\n bool approved // Approval or disapproval of validator contract.\n );\n\n // Allowed signature types.\n enum SignatureType {\n Illegal, // 0x00, default value\n Invalid, // 0x01\n EIP712, // 0x02\n EthSign, // 0x03\n Wallet, // 0x04\n Validator, // 0x05\n PreSigned, // 0x06\n NSignatureTypes // 0x07, number of signature types. Always leave at end.\n }\n\n /// @dev Verifies signature using logic defined by Wallet contract.\n /// @param hash Any 32 byte hash.\n /// @param walletAddress Address that should have signed the given hash\n /// and defines its own signature verification method.\n /// @param signature Proof that the hash has been signed by signer.\n /// @return True if the address recovered from the provided signature matches the input signer address.\n function isValidWalletSignature(\n bytes32 hash,\n address walletAddress,\n bytes signature\n )\n internal\n view\n returns (bool isValid);\n\n /// @dev Verifies signature using logic defined by Validator contract.\n /// @param validatorAddress Address of validator contract.\n /// @param hash Any 32 byte hash.\n /// @param signerAddress Address that should have signed the given hash.\n /// @param signature Proof that the hash has been signed by signer.\n /// @return True if the address recovered from the provided signature matches the input signer address.\n function isValidValidatorSignature(\n address validatorAddress,\n bytes32 hash,\n address signerAddress,\n bytes signature\n )\n internal\n view\n returns (bool isValid);\n}\n", - "2.0.0/protocol/Exchange/mixins/MTransactions.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\npragma solidity 0.4.24;\n\nimport \"../interfaces/ITransactions.sol\";\n\n\ncontract MTransactions is\n ITransactions\n{\n // Hash for the EIP712 ZeroEx Transaction Schema\n bytes32 constant internal EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH = keccak256(abi.encodePacked(\n \"ZeroExTransaction(\",\n \"uint256 salt,\",\n \"address signerAddress,\",\n \"bytes data\",\n \")\"\n ));\n\n /// @dev Calculates EIP712 hash of the Transaction.\n /// @param salt Arbitrary number to ensure uniqueness of transaction hash.\n /// @param signerAddress Address of transaction signer.\n /// @param data AbiV2 encoded calldata.\n /// @return EIP712 hash of the Transaction.\n function hashZeroExTransaction(\n uint256 salt,\n address signerAddress,\n bytes memory data\n )\n internal\n pure\n returns (bytes32 result);\n\n /// @dev The current function will be called in the context of this address (either 0x transaction signer or `msg.sender`).\n /// If calling a fill function, this address will represent the taker.\n /// If calling a cancel function, this address will represent the maker.\n /// @return Signer of 0x transaction if entry point is `executeTransaction`.\n /// `msg.sender` if entry point is any other function.\n function getCurrentContextAddress()\n internal\n view\n returns (address);\n}\n", - "2.0.0/protocol/Exchange/mixins/MWrapperFunctions.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../libs/LibOrder.sol\";\nimport \"../libs/LibFillResults.sol\";\nimport \"../interfaces/IWrapperFunctions.sol\";\n\n\ncontract MWrapperFunctions is \n IWrapperFunctions\n{\n /// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.\n /// @param order LibOrder.Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n function fillOrKillOrderInternal(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n internal\n returns (LibFillResults.FillResults memory fillResults);\n}\n", - "2.0.0/tokens/ERC20Token/IERC20Token.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\ncontract IERC20Token {\n\n // solhint-disable no-simple-event-func-name\n event Transfer(\n address indexed _from,\n address indexed _to,\n uint256 _value\n );\n\n event Approval(\n address indexed _owner,\n address indexed _spender,\n uint256 _value\n );\n\n /// @dev send `value` token to `to` from `msg.sender`\n /// @param _to The address of the recipient\n /// @param _value The amount of token to be transferred\n /// @return True if transfer was successful\n function transfer(address _to, uint256 _value)\n external\n returns (bool);\n\n /// @dev send `value` token to `to` from `from` on the condition it is approved by `from`\n /// @param _from The address of the sender\n /// @param _to The address of the recipient\n /// @param _value The amount of token to be transferred\n /// @return True if transfer was successful\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n )\n external\n returns (bool);\n \n /// @dev `msg.sender` approves `_spender` to spend `_value` tokens\n /// @param _spender The address of the account able to transfer the tokens\n /// @param _value The amount of wei to be approved for transfer\n /// @return Always true if the call has enough gas to complete execution\n function approve(address _spender, uint256 _value)\n external\n returns (bool);\n\n /// @dev Query total supply of token\n /// @return Total supply of token\n function totalSupply()\n external\n view\n returns (uint256);\n \n /// @param _owner The address from which the balance will be retrieved\n /// @return Balance of owner\n function balanceOf(address _owner)\n external\n view\n returns (uint256);\n\n /// @param _owner The address of the account owning tokens\n /// @param _spender The address of the account able to transfer the tokens\n /// @return Amount of remaining tokens allowed to spent\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256);\n}\n", - "2.0.0/tokens/ERC721Token/IERC721Token.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\ncontract IERC721Token {\n\n /// @dev This emits when ownership of any NFT changes by any mechanism.\n /// This event emits when NFTs are created (`from` == 0) and destroyed\n /// (`to` == 0). Exception: during contract creation, any number of NFTs\n /// may be created and assigned without emitting Transfer. At the time of\n /// any transfer, the approved address for that NFT (if any) is reset to none.\n event Transfer(\n address indexed _from,\n address indexed _to,\n uint256 indexed _tokenId\n );\n\n /// @dev This emits when the approved address for an NFT is changed or\n /// reaffirmed. The zero address indicates there is no approved address.\n /// When a Transfer event emits, this also indicates that the approved\n /// address for that NFT (if any) is reset to none.\n event Approval(\n address indexed _owner,\n address indexed _approved,\n uint256 indexed _tokenId\n );\n\n /// @dev This emits when an operator is enabled or disabled for an owner.\n /// The operator can manage all NFTs of the owner.\n event ApprovalForAll(\n address indexed _owner,\n address indexed _operator,\n bool _approved\n );\n\n /// @notice Transfers the ownership of an NFT from one address to another address\n /// @dev Throws unless `msg.sender` is the current owner, an authorized\n /// perator, or the approved address for this NFT. Throws if `_from` is\n /// not the current owner. Throws if `_to` is the zero address. Throws if\n /// `_tokenId` is not a valid NFT. When transfer is complete, this function\n /// checks if `_to` is a smart contract (code size > 0). If so, it calls\n /// `onERC721Received` on `_to` and throws if the return value is not\n /// `bytes4(keccak256(\"onERC721Received(address,address,uint256,bytes)\"))`.\n /// @param _from The current owner of the NFT\n /// @param _to The new owner\n /// @param _tokenId The NFT to transfer\n /// @param _data Additional data with no specified format, sent in call to `_to`\n function safeTransferFrom(\n address _from,\n address _to,\n uint256 _tokenId,\n bytes _data\n )\n external;\n\n /// @notice Transfers the ownership of an NFT from one address to another address\n /// @dev This works identically to the other function with an extra data parameter,\n /// except this function just sets data to \"\".\n /// @param _from The current owner of the NFT\n /// @param _to The new owner\n /// @param _tokenId The NFT to transfer\n function safeTransferFrom(\n address _from,\n address _to,\n uint256 _tokenId\n )\n external;\n\n /// @notice Change or reaffirm the approved address for an NFT\n /// @dev The zero address indicates there is no approved address.\n /// Throws unless `msg.sender` is the current NFT owner, or an authorized\n /// operator of the current owner.\n /// @param _approved The new approved NFT controller\n /// @param _tokenId The NFT to approve\n function approve(address _approved, uint256 _tokenId)\n external;\n\n /// @notice Enable or disable approval for a third party (\"operator\") to manage\n /// all of `msg.sender`'s assets\n /// @dev Emits the ApprovalForAll event. The contract MUST allow\n /// multiple operators per owner.\n /// @param _operator Address to add to the set of authorized operators\n /// @param _approved True if the operator is approved, false to revoke approval\n function setApprovalForAll(address _operator, bool _approved)\n external;\n\n /// @notice Count all NFTs assigned to an owner\n /// @dev NFTs assigned to the zero address are considered invalid, and this\n /// function throws for queries about the zero address.\n /// @param _owner An address for whom to query the balance\n /// @return The number of NFTs owned by `_owner`, possibly zero\n function balanceOf(address _owner)\n external\n view\n returns (uint256);\n\n /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE\n /// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE\n /// THEY MAY BE PERMANENTLY LOST\n /// @dev Throws unless `msg.sender` is the current owner, an authorized\n /// operator, or the approved address for this NFT. Throws if `_from` is\n /// not the current owner. Throws if `_to` is the zero address. Throws if\n /// `_tokenId` is not a valid NFT.\n /// @param _from The current owner of the NFT\n /// @param _to The new owner\n /// @param _tokenId The NFT to transfer\n function transferFrom(\n address _from,\n address _to,\n uint256 _tokenId\n )\n public;\n\n /// @notice Find the owner of an NFT\n /// @dev NFTs assigned to zero address are considered invalid, and queries\n /// about them do throw.\n /// @param _tokenId The identifier for an NFT\n /// @return The address of the owner of the NFT\n function ownerOf(uint256 _tokenId)\n public\n view\n returns (address);\n\n /// @notice Get the approved address for a single NFT\n /// @dev Throws if `_tokenId` is not a valid NFT.\n /// @param _tokenId The NFT to find the approved address for\n /// @return The approved address for this NFT, or the zero address if there is none\n function getApproved(uint256 _tokenId) \n public\n view\n returns (address);\n \n /// @notice Query if an address is an authorized operator for another address\n /// @param _owner The address that owns the NFTs\n /// @param _operator The address that acts on behalf of the owner\n /// @return True if `_operator` is an approved operator for `_owner`, false otherwise\n function isApprovedForAll(address _owner, address _operator)\n public\n view\n returns (bool);\n}\n", - "2.0.0/tokens/EtherToken/IEtherToken.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../ERC20Token/IERC20Token.sol\";\n\n\ncontract IEtherToken is\n IERC20Token\n{\n function deposit()\n public\n payable;\n \n function withdraw(uint256 amount)\n public;\n}\n", - "2.0.0/utils/LibBytes/LibBytes.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\nlibrary LibBytes {\n\n using LibBytes for bytes;\n\n /// @dev Gets the memory address for a byte array.\n /// @param input Byte array to lookup.\n /// @return memoryAddress Memory address of byte array. This\n /// points to the header of the byte array which contains\n /// the length.\n function rawAddress(bytes memory input)\n internal\n pure\n returns (uint256 memoryAddress)\n {\n assembly {\n memoryAddress := input\n }\n return memoryAddress;\n }\n \n /// @dev Gets the memory address for the contents of a byte array.\n /// @param input Byte array to lookup.\n /// @return memoryAddress Memory address of the contents of the byte array.\n function contentAddress(bytes memory input)\n internal\n pure\n returns (uint256 memoryAddress)\n {\n assembly {\n memoryAddress := add(input, 32)\n }\n return memoryAddress;\n }\n\n /// @dev Copies `length` bytes from memory location `source` to `dest`.\n /// @param dest memory address to copy bytes to.\n /// @param source memory address to copy bytes from.\n /// @param length number of bytes to copy.\n function memCopy(\n uint256 dest,\n uint256 source,\n uint256 length\n )\n internal\n pure\n {\n if (length < 32) {\n // Handle a partial word by reading destination and masking\n // off the bits we are interested in.\n // This correctly handles overlap, zero lengths and source == dest\n assembly {\n let mask := sub(exp(256, sub(32, length)), 1)\n let s := and(mload(source), not(mask))\n let d := and(mload(dest), mask)\n mstore(dest, or(s, d))\n }\n } else {\n // Skip the O(length) loop when source == dest.\n if (source == dest) {\n return;\n }\n\n // For large copies we copy whole words at a time. The final\n // word is aligned to the end of the range (instead of after the\n // previous) to handle partial words. So a copy will look like this:\n //\n // ####\n // ####\n // ####\n // ####\n //\n // We handle overlap in the source and destination range by\n // changing the copying direction. This prevents us from\n // overwriting parts of source that we still need to copy.\n //\n // This correctly handles source == dest\n //\n if (source > dest) {\n assembly {\n // We subtract 32 from `sEnd` and `dEnd` because it\n // is easier to compare with in the loop, and these\n // are also the addresses we need for copying the\n // last bytes.\n length := sub(length, 32)\n let sEnd := add(source, length)\n let dEnd := add(dest, length)\n\n // Remember the last 32 bytes of source\n // This needs to be done here and not after the loop\n // because we may have overwritten the last bytes in\n // source already due to overlap.\n let last := mload(sEnd)\n\n // Copy whole words front to back\n // Note: the first check is always true,\n // this could have been a do-while loop.\n // solhint-disable-next-line no-empty-blocks\n for {} lt(source, sEnd) {} {\n mstore(dest, mload(source))\n source := add(source, 32)\n dest := add(dest, 32)\n }\n \n // Write the last 32 bytes\n mstore(dEnd, last)\n }\n } else {\n assembly {\n // We subtract 32 from `sEnd` and `dEnd` because those\n // are the starting points when copying a word at the end.\n length := sub(length, 32)\n let sEnd := add(source, length)\n let dEnd := add(dest, length)\n\n // Remember the first 32 bytes of source\n // This needs to be done here and not after the loop\n // because we may have overwritten the first bytes in\n // source already due to overlap.\n let first := mload(source)\n\n // Copy whole words back to front\n // We use a signed comparisson here to allow dEnd to become\n // negative (happens when source and dest < 32). Valid\n // addresses in local memory will never be larger than\n // 2**255, so they can be safely re-interpreted as signed.\n // Note: the first check is always true,\n // this could have been a do-while loop.\n // solhint-disable-next-line no-empty-blocks\n for {} slt(dest, dEnd) {} {\n mstore(dEnd, mload(sEnd))\n sEnd := sub(sEnd, 32)\n dEnd := sub(dEnd, 32)\n }\n \n // Write the first 32 bytes\n mstore(dest, first)\n }\n }\n }\n }\n\n /// @dev Returns a slices from a byte array.\n /// @param b The byte array to take a slice from.\n /// @param from The starting index for the slice (inclusive).\n /// @param to The final index for the slice (exclusive).\n /// @return result The slice containing bytes at indices [from, to)\n function slice(\n bytes memory b,\n uint256 from,\n uint256 to\n )\n internal\n pure\n returns (bytes memory result)\n {\n require(\n from <= to,\n \"FROM_LESS_THAN_TO_REQUIRED\"\n );\n require(\n to < b.length,\n \"TO_LESS_THAN_LENGTH_REQUIRED\"\n );\n \n // Create a new bytes structure and copy contents\n result = new bytes(to - from);\n memCopy(\n result.contentAddress(),\n b.contentAddress() + from,\n result.length\n );\n return result;\n }\n \n /// @dev Returns a slice from a byte array without preserving the input.\n /// @param b The byte array to take a slice from. Will be destroyed in the process.\n /// @param from The starting index for the slice (inclusive).\n /// @param to The final index for the slice (exclusive).\n /// @return result The slice containing bytes at indices [from, to)\n /// @dev When `from == 0`, the original array will match the slice. In other cases its state will be corrupted.\n function sliceDestructive(\n bytes memory b,\n uint256 from,\n uint256 to\n )\n internal\n pure\n returns (bytes memory result)\n {\n require(\n from <= to,\n \"FROM_LESS_THAN_TO_REQUIRED\"\n );\n require(\n to < b.length,\n \"TO_LESS_THAN_LENGTH_REQUIRED\"\n );\n \n // Create a new bytes structure around [from, to) in-place.\n assembly {\n result := add(b, from)\n mstore(result, sub(to, from))\n }\n return result;\n }\n\n /// @dev Pops the last byte off of a byte array by modifying its length.\n /// @param b Byte array that will be modified.\n /// @return The byte that was popped off.\n function popLastByte(bytes memory b)\n internal\n pure\n returns (bytes1 result)\n {\n require(\n b.length > 0,\n \"GREATER_THAN_ZERO_LENGTH_REQUIRED\"\n );\n\n // Store last byte.\n result = b[b.length - 1];\n\n assembly {\n // Decrement length of byte array.\n let newLen := sub(mload(b), 1)\n mstore(b, newLen)\n }\n return result;\n }\n\n /// @dev Pops the last 20 bytes off of a byte array by modifying its length.\n /// @param b Byte array that will be modified.\n /// @return The 20 byte address that was popped off.\n function popLast20Bytes(bytes memory b)\n internal\n pure\n returns (address result)\n {\n require(\n b.length >= 20,\n \"GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED\"\n );\n\n // Store last 20 bytes.\n result = readAddress(b, b.length - 20);\n\n assembly {\n // Subtract 20 from byte array length.\n let newLen := sub(mload(b), 20)\n mstore(b, newLen)\n }\n return result;\n }\n\n /// @dev Tests equality of two byte arrays.\n /// @param lhs First byte array to compare.\n /// @param rhs Second byte array to compare.\n /// @return True if arrays are the same. False otherwise.\n function equals(\n bytes memory lhs,\n bytes memory rhs\n )\n internal\n pure\n returns (bool equal)\n {\n // Keccak gas cost is 30 + numWords * 6. This is a cheap way to compare.\n // We early exit on unequal lengths, but keccak would also correctly\n // handle this.\n return lhs.length == rhs.length && keccak256(lhs) == keccak256(rhs);\n }\n\n /// @dev Reads an address from a position in a byte array.\n /// @param b Byte array containing an address.\n /// @param index Index in byte array of address.\n /// @return address from byte array.\n function readAddress(\n bytes memory b,\n uint256 index\n )\n internal\n pure\n returns (address result)\n {\n require(\n b.length >= index + 20, // 20 is length of address\n \"GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED\"\n );\n\n // Add offset to index:\n // 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)\n // 2. Account for size difference between address length and 32-byte storage word (subtract 12 from index)\n index += 20;\n\n // Read address from array memory\n assembly {\n // 1. Add index to address of bytes array\n // 2. Load 32-byte word from memory\n // 3. Apply 20-byte mask to obtain address\n result := and(mload(add(b, index)), 0xffffffffffffffffffffffffffffffffffffffff)\n }\n return result;\n }\n\n /// @dev Writes an address into a specific position in a byte array.\n /// @param b Byte array to insert address into.\n /// @param index Index in byte array of address.\n /// @param input Address to put into byte array.\n function writeAddress(\n bytes memory b,\n uint256 index,\n address input\n )\n internal\n pure\n {\n require(\n b.length >= index + 20, // 20 is length of address\n \"GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED\"\n );\n\n // Add offset to index:\n // 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)\n // 2. Account for size difference between address length and 32-byte storage word (subtract 12 from index)\n index += 20;\n\n // Store address into array memory\n assembly {\n // The address occupies 20 bytes and mstore stores 32 bytes.\n // First fetch the 32-byte word where we'll be storing the address, then\n // apply a mask so we have only the bytes in the word that the address will not occupy.\n // Then combine these bytes with the address and store the 32 bytes back to memory with mstore.\n\n // 1. Add index to address of bytes array\n // 2. Load 32-byte word from memory\n // 3. Apply 12-byte mask to obtain extra bytes occupying word of memory where we'll store the address\n let neighbors := and(\n mload(add(b, index)),\n 0xffffffffffffffffffffffff0000000000000000000000000000000000000000\n )\n \n // Make sure input address is clean.\n // (Solidity does not guarantee this)\n input := and(input, 0xffffffffffffffffffffffffffffffffffffffff)\n\n // Store the neighbors and address into memory\n mstore(add(b, index), xor(input, neighbors))\n }\n }\n\n /// @dev Reads a bytes32 value from a position in a byte array.\n /// @param b Byte array containing a bytes32 value.\n /// @param index Index in byte array of bytes32 value.\n /// @return bytes32 value from byte array.\n function readBytes32(\n bytes memory b,\n uint256 index\n )\n internal\n pure\n returns (bytes32 result)\n {\n require(\n b.length >= index + 32,\n \"GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED\"\n );\n\n // Arrays are prefixed by a 256 bit length parameter\n index += 32;\n\n // Read the bytes32 from array memory\n assembly {\n result := mload(add(b, index))\n }\n return result;\n }\n\n /// @dev Writes a bytes32 into a specific position in a byte array.\n /// @param b Byte array to insert into.\n /// @param index Index in byte array of .\n /// @param input bytes32 to put into byte array.\n function writeBytes32(\n bytes memory b,\n uint256 index,\n bytes32 input\n )\n internal\n pure\n {\n require(\n b.length >= index + 32,\n \"GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED\"\n );\n\n // Arrays are prefixed by a 256 bit length parameter\n index += 32;\n\n // Read the bytes32 from array memory\n assembly {\n mstore(add(b, index), input)\n }\n }\n\n /// @dev Reads a uint256 value from a position in a byte array.\n /// @param b Byte array containing a uint256 value.\n /// @param index Index in byte array of uint256 value.\n /// @return uint256 value from byte array.\n function readUint256(\n bytes memory b,\n uint256 index\n )\n internal\n pure\n returns (uint256 result)\n {\n result = uint256(readBytes32(b, index));\n return result;\n }\n\n /// @dev Writes a uint256 into a specific position in a byte array.\n /// @param b Byte array to insert into.\n /// @param index Index in byte array of .\n /// @param input uint256 to put into byte array.\n function writeUint256(\n bytes memory b,\n uint256 index,\n uint256 input\n )\n internal\n pure\n {\n writeBytes32(b, index, bytes32(input));\n }\n\n /// @dev Reads an unpadded bytes4 value from a position in a byte array.\n /// @param b Byte array containing a bytes4 value.\n /// @param index Index in byte array of bytes4 value.\n /// @return bytes4 value from byte array.\n function readBytes4(\n bytes memory b,\n uint256 index\n )\n internal\n pure\n returns (bytes4 result)\n {\n require(\n b.length >= index + 4,\n \"GREATER_OR_EQUAL_TO_4_LENGTH_REQUIRED\"\n );\n\n // Arrays are prefixed by a 32 byte length field\n index += 32;\n\n // Read the bytes4 from array memory\n assembly {\n result := mload(add(b, index))\n // Solidity does not require us to clean the trailing bytes.\n // We do it anyway\n result := and(result, 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000)\n }\n return result;\n }\n\n /// @dev Reads nested bytes from a specific position.\n /// @dev NOTE: the returned value overlaps with the input value.\n /// Both should be treated as immutable.\n /// @param b Byte array containing nested bytes.\n /// @param index Index of nested bytes.\n /// @return result Nested bytes.\n function readBytesWithLength(\n bytes memory b,\n uint256 index\n )\n internal\n pure\n returns (bytes memory result)\n {\n // Read length of nested bytes\n uint256 nestedBytesLength = readUint256(b, index);\n index += 32;\n\n // Assert length of is valid, given\n // length of nested bytes\n require(\n b.length >= index + nestedBytesLength,\n \"GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED\"\n );\n \n // Return a pointer to the byte array as it exists inside `b`\n assembly {\n result := add(b, index)\n }\n return result;\n }\n\n /// @dev Inserts bytes at a specific position in a byte array.\n /// @param b Byte array to insert into.\n /// @param index Index in byte array of .\n /// @param input bytes to insert.\n function writeBytesWithLength(\n bytes memory b,\n uint256 index,\n bytes memory input\n )\n internal\n pure\n {\n // Assert length of is valid, given\n // length of input\n require(\n b.length >= index + 32 + input.length, // 32 bytes to store length\n \"GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED\"\n );\n\n // Copy into \n memCopy(\n b.contentAddress() + index,\n input.rawAddress(), // includes length of \n input.length + 32 // +32 bytes to store length\n );\n }\n\n /// @dev Performs a deep copy of a byte array onto another byte array of greater than or equal length.\n /// @param dest Byte array that will be overwritten with source bytes.\n /// @param source Byte array to copy onto dest bytes.\n function deepCopyBytes(\n bytes memory dest,\n bytes memory source\n )\n internal\n pure\n {\n uint256 sourceLen = source.length;\n // Dest length must be >= source length, or some bytes would not be copied.\n require(\n dest.length >= sourceLen,\n \"GREATER_OR_EQUAL_TO_SOURCE_BYTES_LENGTH_REQUIRED\"\n );\n memCopy(\n dest.contentAddress(),\n source.contentAddress(),\n sourceLen\n );\n }\n}\n", - "2.0.0/utils/Ownable/IOwnable.sol": "pragma solidity 0.4.24;\n\n\ncontract IOwnable {\n\n function transferOwnership(address newOwner)\n public;\n}\n", - "2.0.0/utils/Ownable/Ownable.sol": "pragma solidity 0.4.24;\n\nimport \"./IOwnable.sol\";\n\n\ncontract Ownable is\n IOwnable\n{\n address public owner;\n\n constructor ()\n public\n {\n owner = msg.sender;\n }\n\n modifier onlyOwner() {\n require(\n msg.sender == owner,\n \"ONLY_CONTRACT_OWNER\"\n );\n _;\n }\n\n function transferOwnership(address newOwner)\n public\n onlyOwner\n {\n if (newOwner != address(0)) {\n owner = newOwner;\n }\n }\n}\n", - "2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\ncontract ReentrancyGuard {\n\n // Locked state of mutex\n bool private locked = false;\n\n /// @dev Functions with this modifer cannot be reentered. The mutex will be locked\n /// before function execution and unlocked after.\n modifier nonReentrant() {\n // Ensure mutex is unlocked\n require(\n !locked,\n \"REENTRANCY_ILLEGAL\"\n );\n\n // Lock mutex before function call\n locked = true;\n\n // Perform function call\n _;\n\n // Unlock mutex after function call\n locked = false;\n }\n}\n", - "2.0.0/utils/SafeMath/SafeMath.sol": "pragma solidity 0.4.24;\n\n\ncontract SafeMath {\n\n function safeMul(uint256 a, uint256 b)\n internal\n pure\n returns (uint256)\n {\n if (a == 0) {\n return 0;\n }\n uint256 c = a * b;\n require(\n c / a == b,\n \"UINT256_OVERFLOW\"\n );\n return c;\n }\n\n function safeDiv(uint256 a, uint256 b)\n internal\n pure\n returns (uint256)\n {\n uint256 c = a / b;\n return c;\n }\n\n function safeSub(uint256 a, uint256 b)\n internal\n pure\n returns (uint256)\n {\n require(\n b <= a,\n \"UINT256_UNDERFLOW\"\n );\n return a - b;\n }\n\n function safeAdd(uint256 a, uint256 b)\n internal\n pure\n returns (uint256)\n {\n uint256 c = a + b;\n require(\n c >= a,\n \"UINT256_OVERFLOW\"\n );\n return c;\n }\n\n function max64(uint64 a, uint64 b)\n internal\n pure\n returns (uint256)\n {\n return a >= b ? a : b;\n }\n\n function min64(uint64 a, uint64 b)\n internal\n pure\n returns (uint256)\n {\n return a < b ? a : b;\n }\n\n function max256(uint256 a, uint256 b)\n internal\n pure\n returns (uint256)\n {\n return a >= b ? a : b;\n }\n\n function min256(uint256 a, uint256 b)\n internal\n pure\n returns (uint256)\n {\n return a < b ? a : b;\n }\n}\n" - }, - "sourceTreeHashHex": "0xd4b411ae8c566bfc879b9880fcf5306a0b0b7051368f0c84ad27da6b5a6f0637", - "compiler": { - "name": "solc", - "version": "soljson-v0.4.24+commit.e67f0147.js", - "settings": { - "optimizer": { - "enabled": true, - "runs": 1000000 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode.object", - "evm.bytecode.sourceMap", - "evm.deployedBytecode.object", - "evm.deployedBytecode.sourceMap" - ] - } - } - } - }, - "networks": { - "1": { - "address": "0x4f833a24e1f95d70f028921e27040ca56e09ab0b", - "links": {}, - "constructorArgs": "[]" - }, - "3": { - "address": "0x4530c0483a1633c7a1c97d2c53721caff2caaaaf", - "links": {}, - "constructorArgs": "[\"0xf47261b0000000000000000000000000ff67881f8d12f372d91baae9752eb3631ff0ed00\"]" - }, - "42": { - "address": "0x35dd2932454449b14cee11a94d3674a936d5d7b2", - "links": {}, - "constructorArgs": "[\"0xf47261b00000000000000000000000002002d3812f58e35f0ea1ffbf80a75a38c32175fa\"]" - }, - "50": { - "address": "0x48bacb9266a570d521063ef5dd96e61686dbe788", - "links": {}, - "constructorArgs": "[\"0xf47261b0000000000000000000000000871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c\"]" - } - } -} diff --git a/packages/pipeline/src/data_types/events/exchange_events.ts b/packages/pipeline/src/data_types/events/exchange_events.ts index a406d27fb..30ef058f3 100644 --- a/packages/pipeline/src/data_types/events/exchange_events.ts +++ b/packages/pipeline/src/data_types/events/exchange_events.ts @@ -1,3 +1,4 @@ +import { Exchange } from '@0xproject/contract-artifacts'; import { ExchangeCancelEventArgs, ExchangeCancelUpToEventArgs, @@ -9,7 +10,6 @@ import { AssetProxyId, ERC721AssetData } from '@0xproject/types'; import { LogWithDecodedArgs } from 'ethereum-types'; import * as R from 'ramda'; -import { artifacts } from '../../artifacts'; import { EventsResponse } from '../../data_sources/etherscan'; import { ExchangeCancelEvent } from '../../entities/ExchangeCancelEvent'; import { ExchangeCancelUpToEvent } from '../../entities/ExchangeCancelUpToEvent'; @@ -20,12 +20,10 @@ import { convertResponseToLogEntry, decodeLogEntry } from './event_utils'; export type ExchangeEventEntity = ExchangeFillEvent | ExchangeCancelEvent | ExchangeCancelUpToEvent; -const exchangeContractAbi = artifacts.Exchange.compilerOutput.abi; - export function parseExchangeEvents(rawEventsResponse: EventsResponse): ExchangeEventEntity[] { const logEntries = R.map(convertResponseToLogEntry, rawEventsResponse.result); const decodedLogEntries = R.map( - eventResponse => decodeLogEntry(exchangeContractAbi, eventResponse), + eventResponse => decodeLogEntry(Exchange.compilerOutput.abi, eventResponse), logEntries, ); const filteredLogEntries = R.filter(shouldIncludeLogEntry, decodedLogEntries); diff --git a/packages/pipeline/src/global.d.ts b/packages/pipeline/src/global.d.ts deleted file mode 100644 index 94e63a32d..000000000 --- a/packages/pipeline/src/global.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -declare module '*.json' { - const json: any; - /* tslint:disable */ - export default json; - /* tslint:enable */ -} diff --git a/packages/pipeline/test/data_types/events/event_utils_test.ts b/packages/pipeline/test/data_types/events/event_utils_test.ts index 91a73c47d..731819106 100644 --- a/packages/pipeline/test/data_types/events/event_utils_test.ts +++ b/packages/pipeline/test/data_types/events/event_utils_test.ts @@ -1,10 +1,9 @@ +import { Exchange } from '@0xproject/contract-artifacts'; import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; import { DecodedLogArgs, LogEntry, LogWithDecodedArgs } from 'ethereum-types'; import 'mocha'; -import { artifacts } from '../../../src/artifacts'; - import { EventsResponseResult } from '../../../src/data_sources/etherscan'; import { convertResponseToLogEntry, decodeLogEntry } from '../../../src/data_types/events/event_utils'; import { chaiSetup } from '../../utils/chai_setup'; @@ -80,7 +79,7 @@ describe('event_utils', () => { takerAssetData: '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', }, }; - const actual = decodeLogEntry(artifacts.Exchange.compilerOutput.abi, input); + const actual = decodeLogEntry(Exchange.compilerOutput.abi, input); expect(actual).deep.equal(expected); }); }); -- cgit v1.2.3 From 91e7485ecc4e780ef454b642a52c3d93faa95af1 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Wed, 17 Oct 2018 16:44:07 -0700 Subject: Update to use ContractWrappers + Infura instead of Etherscan --- packages/pipeline/package.json | 22 +- .../contract-wrappers/exchange_events.ts | 51 +++ .../pipeline/src/data_sources/etherscan/index.ts | 52 --- .../pipeline/src/data_types/events/event_utils.ts | 35 -- .../src/data_types/events/exchange_events.ts | 130 ------- .../pipeline/src/data_types/sra_orders/index.ts | 54 --- packages/pipeline/src/index.ts | 43 +-- packages/pipeline/src/parsers/events/index.ts | 111 ++++++ packages/pipeline/src/parsers/sra_orders/index.ts | 54 +++ .../test/data_types/events/event_utils_test.ts | 86 ----- .../test/data_types/events/exchange_events_test.ts | 77 ---- .../test/data_types/sra_orders/index_test.ts | 79 ---- .../pipeline/test/parsers/events/index_test.ts | 77 ++++ .../pipeline/test/parsers/sra_orders/index_test.ts | 70 ++++ yarn.lock | 411 +++------------------ 15 files changed, 445 insertions(+), 907 deletions(-) create mode 100644 packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts delete mode 100644 packages/pipeline/src/data_sources/etherscan/index.ts delete mode 100644 packages/pipeline/src/data_types/events/event_utils.ts delete mode 100644 packages/pipeline/src/data_types/events/exchange_events.ts delete mode 100644 packages/pipeline/src/data_types/sra_orders/index.ts create mode 100644 packages/pipeline/src/parsers/events/index.ts create mode 100644 packages/pipeline/src/parsers/sra_orders/index.ts delete mode 100644 packages/pipeline/test/data_types/events/event_utils_test.ts delete mode 100644 packages/pipeline/test/data_types/events/exchange_events_test.ts delete mode 100644 packages/pipeline/test/data_types/sra_orders/index_test.ts create mode 100644 packages/pipeline/test/parsers/events/index_test.ts create mode 100644 packages/pipeline/test/parsers/sra_orders/index_test.ts diff --git a/packages/pipeline/package.json b/packages/pipeline/package.json index be23bfe2c..0071fab2c 100644 --- a/packages/pipeline/package.json +++ b/packages/pipeline/package.json @@ -1,5 +1,5 @@ { - "name": "@0xproject/pipeline", + "name": "@0x/pipeline", "version": "0.0.1", "private": true, "description": "Data pipeline for offline analysis", @@ -25,7 +25,8 @@ }, "license": "Apache-2.0", "devDependencies": { - "@0xproject/tslint-config": "^1.0.7", + "@types/ramda": "^0.25.38", + "@0x/tslint-config": "^1.0.9", "chai": "^4.1.2", "chai-as-promised": "^7.1.1", "chai-bignumber": "^2.0.2", @@ -35,14 +36,15 @@ "typescript": "3.0.1" }, "dependencies": { - "@0xproject/contract-artifacts": "^1.0.0", - "@0xproject/connect": "^2.0.4", - "@0xproject/contract-wrappers": "^1.0.1", - "@0xproject/order-utils": "^1.0.2", - "@0xproject/subproviders": "^2.0.2", - "@0xproject/types": "^1.0.1", - "@0xproject/utils": "^1.0.8", - "@types/ramda": "^0.25.38", + "@0x/dev-utils": "^1.0.13", + "@0x/contract-artifacts": "^1.0.1", + "@0x/connect": "^3.0.2", + "@0x/contract-wrappers": "^3.0.0", + "@0x/order-utils": "^2.0.0", + "@0x/subproviders": "^2.1.0", + "@0x/types": "^1.2.0", + "@0x/utils": "^2.0.3", + "@0x/web3-wrapper": "^3.1.0", "axios": "^0.18.0", "ethereum-types": "^1.0.6", "ramda": "^0.25.0", diff --git a/packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts b/packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts new file mode 100644 index 000000000..77217c601 --- /dev/null +++ b/packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts @@ -0,0 +1,51 @@ +import { ContractWrappers, ExchangeEvents, ExchangeFillEventArgs, ExchangeWrapper } from '@0xproject/contract-wrappers'; +import { Web3ProviderEngine } from '@0xproject/subproviders'; +import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { LogWithDecodedArgs } from 'ethereum-types'; + +const BLOCK_FINALITY_THRESHOLD = 10; // When to consider blocks as final. Used to compute default toBlock. +const NUM_BLOCKS_PER_QUERY = 100000; // Number of blocks to query for events at a time. +const EXCHANGE_START_BLOCK = 6271590; // Block number when the Exchange contract was deployed to mainnet. + +export class ExchangeEventsSource { + private _exchangeWrapper: ExchangeWrapper; + private _web3Wrapper: Web3Wrapper; + constructor(provider: Web3ProviderEngine, networkId: number) { + this._web3Wrapper = new Web3Wrapper(provider); + const contractWrappers = new ContractWrappers(provider, { networkId }); + this._exchangeWrapper = contractWrappers.exchange; + } + + // TODO(albrow): Get Cancel and CancelUpTo events. + + public async getFillEventsAsync( + fromBlock: number = EXCHANGE_START_BLOCK, + toBlock?: number, + ): Promise>> { + const calculatedToBlock = + toBlock === undefined + ? (await this._web3Wrapper.getBlockNumberAsync()) - BLOCK_FINALITY_THRESHOLD + : toBlock; + let events: Array> = []; + for (let currFromBlock = fromBlock; currFromBlock <= calculatedToBlock; currFromBlock += NUM_BLOCKS_PER_QUERY) { + events = events.concat( + await this._getFillEventsForRangeAsync(currFromBlock, currFromBlock + NUM_BLOCKS_PER_QUERY - 1), + ); + } + return events; + } + + private async _getFillEventsForRangeAsync( + fromBlock: number, + toBlock: number, + ): Promise>> { + return this._exchangeWrapper.getLogsAsync( + ExchangeEvents.Fill, + { + fromBlock, + toBlock, + }, + {}, + ); + } +} diff --git a/packages/pipeline/src/data_sources/etherscan/index.ts b/packages/pipeline/src/data_sources/etherscan/index.ts deleted file mode 100644 index 044fff02e..000000000 --- a/packages/pipeline/src/data_sources/etherscan/index.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { default as axios } from 'axios'; -import { BlockParam, BlockParamLiteral } from 'ethereum-types'; - -const ETHERSCAN_URL = 'https://api.etherscan.io/api'; - -export class Etherscan { - private readonly _apiKey: string; - constructor(apiKey: string) { - this._apiKey = apiKey; - } - - /** - * Gets the raw events for a specific contract and block range. - * @param contractAddress The address of the contract to get the events for. - * @param fromBlock The start of the block range to get events for (inclusive). - * @param toBlock The end of the block range to get events for (inclusive). - * @returns A list of decoded events. - */ - public async getContractEventsAsync( - contractAddress: string, - fromBlock: BlockParam = BlockParamLiteral.Earliest, - toBlock: BlockParam = BlockParamLiteral.Latest, - ): Promise { - const fullURL = `${ETHERSCAN_URL}?module=logs&action=getLogs&address=${contractAddress}&fromBlock=${fromBlock}&toBlock=${toBlock}&apikey=${ - this._apiKey - }`; - const resp = await axios.get(fullURL); - // TODO(albrow): Check response code. - return resp.data; - } -} - -// Raw events response from etherescan.io -export interface EventsResponse { - status: string; - message: string; - result: EventsResponseResult[]; -} - -// Events as represented in the response from etherscan.io -export interface EventsResponseResult { - address: string; - topics: string[]; - data: string; - blockNumber: string; - timeStamp: string; - gasPrice: string; - gasUsed: string; - logIndex: string; - transactionHash: string; - transactionIndex: string; -} diff --git a/packages/pipeline/src/data_types/events/event_utils.ts b/packages/pipeline/src/data_types/events/event_utils.ts deleted file mode 100644 index 6be964807..000000000 --- a/packages/pipeline/src/data_types/events/event_utils.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { AbiDecoder } from '@0xproject/utils'; -import { AbiDefinition, LogEntry, LogWithDecodedArgs } from 'ethereum-types'; - -import { EventsResponseResult } from '../../data_sources/etherscan'; - -const hexRadix = 16; - -function hexToInt(hex: string): number { - return parseInt(hex.replace('0x', ''), hexRadix); -} - -// Converts a raw event response to a LogEntry -export function convertResponseToLogEntry(result: EventsResponseResult): LogEntry { - return { - logIndex: hexToInt(result.logIndex), - transactionIndex: hexToInt(result.transactionIndex), - transactionHash: result.transactionHash, - blockHash: '', - blockNumber: hexToInt(result.blockNumber), - address: result.address, - data: result.data, - topics: result.topics, - }; -} - -// Decodes a LogEntry into a LogWithDecodedArgs -export function decodeLogEntry( - contractAbi: AbiDefinition[], - log: LogEntry, -): LogWithDecodedArgs { - const abiDecoder = new AbiDecoder([contractAbi]); - const logWithDecodedArgs = abiDecoder.tryToDecodeLogOrNoop(log); - // tslint:disable-next-line:no-unnecessary-type-assertion - return logWithDecodedArgs as LogWithDecodedArgs; -} diff --git a/packages/pipeline/src/data_types/events/exchange_events.ts b/packages/pipeline/src/data_types/events/exchange_events.ts deleted file mode 100644 index 30ef058f3..000000000 --- a/packages/pipeline/src/data_types/events/exchange_events.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { Exchange } from '@0xproject/contract-artifacts'; -import { - ExchangeCancelEventArgs, - ExchangeCancelUpToEventArgs, - ExchangeEventArgs, - ExchangeFillEventArgs, -} from '@0xproject/contract-wrappers'; -import { assetDataUtils } from '@0xproject/order-utils'; -import { AssetProxyId, ERC721AssetData } from '@0xproject/types'; -import { LogWithDecodedArgs } from 'ethereum-types'; -import * as R from 'ramda'; - -import { EventsResponse } from '../../data_sources/etherscan'; -import { ExchangeCancelEvent } from '../../entities/ExchangeCancelEvent'; -import { ExchangeCancelUpToEvent } from '../../entities/ExchangeCancelUpToEvent'; -import { ExchangeFillEvent } from '../../entities/ExchangeFillEvent'; -import { bigNumbertoStringOrNull } from '../../utils'; - -import { convertResponseToLogEntry, decodeLogEntry } from './event_utils'; - -export type ExchangeEventEntity = ExchangeFillEvent | ExchangeCancelEvent | ExchangeCancelUpToEvent; - -export function parseExchangeEvents(rawEventsResponse: EventsResponse): ExchangeEventEntity[] { - const logEntries = R.map(convertResponseToLogEntry, rawEventsResponse.result); - const decodedLogEntries = R.map( - eventResponse => decodeLogEntry(Exchange.compilerOutput.abi, eventResponse), - logEntries, - ); - const filteredLogEntries = R.filter(shouldIncludeLogEntry, decodedLogEntries); - return R.map(_convertToEntity, filteredLogEntries); -} - -export function shouldIncludeLogEntry(logEntry: LogWithDecodedArgs): boolean { - if (!R.contains(logEntry.event, ['Fill', 'Cancel', 'CancelUpTo'])) { - return false; - } else if (logEntry.logIndex == null || isNaN(logEntry.logIndex)) { - return false; - } - return true; -} - -export function _convertToEntity(eventLog: LogWithDecodedArgs): ExchangeEventEntity { - switch (eventLog.event) { - case 'Fill': - return _convertToExchangeFillEvent(eventLog as LogWithDecodedArgs); - case 'Cancel': - return _convertToExchangeCancelEvent(eventLog as LogWithDecodedArgs); - case 'CancelUpTo': - return _convertToExchangeCancelUpToEvent(eventLog as LogWithDecodedArgs); - default: - throw new Error('unexpected eventLog.event type: ' + eventLog.event); - } -} - -export function _convertToExchangeFillEvent(eventLog: LogWithDecodedArgs): ExchangeFillEvent { - const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.makerAssetData); - const makerAssetType = makerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; - const takerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.takerAssetData); - const takerAssetType = takerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; - const exchangeFillEvent = new ExchangeFillEvent(); - exchangeFillEvent.contractAddress = eventLog.address as string; - exchangeFillEvent.blockNumber = eventLog.blockNumber as number; - exchangeFillEvent.logIndex = eventLog.logIndex as number; - exchangeFillEvent.rawData = eventLog.data as string; - exchangeFillEvent.makerAddress = eventLog.args.makerAddress.toString(); - exchangeFillEvent.takerAddress = eventLog.args.takerAddress.toString(); - exchangeFillEvent.feeRecepientAddress = eventLog.args.feeRecipientAddress; - exchangeFillEvent.senderAddress = eventLog.args.senderAddress; - exchangeFillEvent.makerAssetFilledAmount = eventLog.args.makerAssetFilledAmount.toString(); - exchangeFillEvent.takerAssetFilledAmount = eventLog.args.takerAssetFilledAmount.toString(); - exchangeFillEvent.makerFeePaid = eventLog.args.makerFeePaid.toString(); - exchangeFillEvent.takerFeePaid = eventLog.args.takerFeePaid.toString(); - exchangeFillEvent.orderHash = eventLog.args.orderHash; - exchangeFillEvent.rawMakerAssetData = eventLog.args.makerAssetData; - exchangeFillEvent.makerAssetType = makerAssetType; - exchangeFillEvent.makerAssetProxyId = makerAssetData.assetProxyId; - exchangeFillEvent.makerTokenAddress = makerAssetData.tokenAddress; - exchangeFillEvent.makerTokenId = bigNumbertoStringOrNull((makerAssetData as ERC721AssetData).tokenId); - exchangeFillEvent.rawTakerAssetData = eventLog.args.takerAssetData; - exchangeFillEvent.takerAssetType = takerAssetType; - exchangeFillEvent.takerAssetProxyId = takerAssetData.assetProxyId; - exchangeFillEvent.takerTokenAddress = takerAssetData.tokenAddress; - exchangeFillEvent.takerTokenId = bigNumbertoStringOrNull((takerAssetData as ERC721AssetData).tokenId); - return exchangeFillEvent; -} - -export function _convertToExchangeCancelEvent( - eventLog: LogWithDecodedArgs, -): ExchangeCancelEvent { - const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.makerAssetData); - const makerAssetType = makerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; - const takerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.takerAssetData); - const takerAssetType = takerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; - const exchangeCancelEvent = new ExchangeCancelEvent(); - exchangeCancelEvent.contractAddress = eventLog.address as string; - exchangeCancelEvent.blockNumber = eventLog.blockNumber as number; - exchangeCancelEvent.logIndex = eventLog.logIndex as number; - exchangeCancelEvent.rawData = eventLog.data as string; - exchangeCancelEvent.makerAddress = eventLog.args.makerAddress.toString(); - exchangeCancelEvent.takerAddress = - eventLog.args.takerAddress == null ? null : eventLog.args.takerAddress.toString(); - exchangeCancelEvent.feeRecepientAddress = eventLog.args.feeRecipientAddress; - exchangeCancelEvent.senderAddress = eventLog.args.senderAddress; - exchangeCancelEvent.orderHash = eventLog.args.orderHash; - exchangeCancelEvent.rawMakerAssetData = eventLog.args.makerAssetData; - exchangeCancelEvent.makerAssetType = makerAssetType; - exchangeCancelEvent.makerAssetProxyId = makerAssetData.assetProxyId; - exchangeCancelEvent.makerTokenAddress = makerAssetData.tokenAddress; - exchangeCancelEvent.makerTokenId = bigNumbertoStringOrNull((makerAssetData as ERC721AssetData).tokenId); - exchangeCancelEvent.rawTakerAssetData = eventLog.args.takerAssetData; - exchangeCancelEvent.takerAssetType = takerAssetType; - exchangeCancelEvent.takerAssetProxyId = takerAssetData.assetProxyId; - exchangeCancelEvent.takerTokenAddress = takerAssetData.tokenAddress; - exchangeCancelEvent.takerTokenId = bigNumbertoStringOrNull((takerAssetData as ERC721AssetData).tokenId); - return exchangeCancelEvent; -} - -export function _convertToExchangeCancelUpToEvent( - eventLog: LogWithDecodedArgs, -): ExchangeCancelUpToEvent { - const exchangeCancelUpToEvent = new ExchangeCancelUpToEvent(); - exchangeCancelUpToEvent.contractAddress = eventLog.address as string; - exchangeCancelUpToEvent.blockNumber = eventLog.blockNumber as number; - exchangeCancelUpToEvent.logIndex = eventLog.logIndex as number; - exchangeCancelUpToEvent.rawData = eventLog.data as string; - exchangeCancelUpToEvent.makerAddress = eventLog.args.makerAddress.toString(); - exchangeCancelUpToEvent.senderAddress = eventLog.args.senderAddress.toString(); - exchangeCancelUpToEvent.orderEpoch = eventLog.args.orderEpoch.toString(); - return exchangeCancelUpToEvent; -} diff --git a/packages/pipeline/src/data_types/sra_orders/index.ts b/packages/pipeline/src/data_types/sra_orders/index.ts deleted file mode 100644 index fb2b74dfe..000000000 --- a/packages/pipeline/src/data_types/sra_orders/index.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { APIOrder, OrdersResponse } from '@0xproject/connect'; -import { assetDataUtils, orderHashUtils } from '@0xproject/order-utils'; -import { AssetProxyId, ERC721AssetData } from '@0xproject/types'; -import * as R from 'ramda'; - -import { SraOrder } from '../../entities/SraOrder'; -import { bigNumbertoStringOrNull } from '../../utils'; - -export function parseSraOrders(rawOrdersResponse: OrdersResponse): SraOrder[] { - return R.map(_convertToEntity, rawOrdersResponse.records); -} - -export function _convertToEntity(apiOrder: APIOrder): SraOrder { - // TODO(albrow): refactor out common asset data decoding code. - const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(apiOrder.order.makerAssetData); - const makerAssetType = makerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; - const takerAssetData = assetDataUtils.decodeAssetDataOrThrow(apiOrder.order.takerAssetData); - const takerAssetType = takerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; - - const sraOrder = new SraOrder(); - sraOrder.exchangeAddress = apiOrder.order.exchangeAddress; - sraOrder.orderHashHex = orderHashUtils.getOrderHashHex(apiOrder.order); - - // TODO(albrow): Set these fields to the correct values upstack. - sraOrder.lastUpdatedTimestamp = 0; - sraOrder.firstSeenTimestamp = 0; - - sraOrder.makerAddress = apiOrder.order.makerAddress; - sraOrder.takerAddress = apiOrder.order.takerAddress; - sraOrder.feeRecipientAddress = apiOrder.order.feeRecipientAddress; - sraOrder.senderAddress = apiOrder.order.senderAddress; - sraOrder.makerAssetAmount = apiOrder.order.makerAssetAmount.toString(); - sraOrder.takerAssetAmount = apiOrder.order.takerAssetAmount.toString(); - sraOrder.makerFee = apiOrder.order.makerFee.toString(); - sraOrder.takerFee = apiOrder.order.takerFee.toString(); - sraOrder.expirationTimeSeconds = apiOrder.order.expirationTimeSeconds.toString(); - sraOrder.salt = apiOrder.order.salt.toString(); - sraOrder.signature = apiOrder.order.signature; - - sraOrder.rawMakerAssetData = apiOrder.order.makerAssetData; - sraOrder.makerAssetType = makerAssetType; - sraOrder.makerAssetProxyId = makerAssetData.assetProxyId; - sraOrder.makerTokenAddress = makerAssetData.tokenAddress; - sraOrder.makerTokenId = bigNumbertoStringOrNull((makerAssetData as ERC721AssetData).tokenId); - sraOrder.rawTakerAssetData = apiOrder.order.takerAssetData; - sraOrder.takerAssetType = takerAssetType; - sraOrder.takerAssetProxyId = takerAssetData.assetProxyId; - sraOrder.takerTokenAddress = takerAssetData.tokenAddress; - sraOrder.takerTokenId = bigNumbertoStringOrNull((takerAssetData as ERC721AssetData).tokenId); - - sraOrder.metaDataJson = JSON.stringify(apiOrder.metaData); - - return sraOrder; -} diff --git a/packages/pipeline/src/index.ts b/packages/pipeline/src/index.ts index a1dbb35ff..77c92cc34 100644 --- a/packages/pipeline/src/index.ts +++ b/packages/pipeline/src/index.ts @@ -1,52 +1,43 @@ import { HttpClient } from '@0xproject/connect'; +import { web3Factory } from '@0xproject/dev-utils'; import 'reflect-metadata'; import { Connection, createConnection } from 'typeorm'; -import { Etherscan } from './data_sources/etherscan'; -import { parseExchangeEvents } from './data_types/events/exchange_events'; -import { parseSraOrders } from './data_types/sra_orders'; -import { ExchangeCancelEvent } from './entities/ExchangeCancelEvent'; -import { ExchangeCancelUpToEvent } from './entities/ExchangeCancelUpToEvent'; -import { ExchangeFillEvent } from './entities/ExchangeFillEvent'; +import { ExchangeEventsSource } from './data_sources/contract-wrappers/exchange_events'; import { SraOrder } from './entities/SraOrder'; import { config } from './ormconfig'; - -const etherscan = new Etherscan(process.env.ETHERSCAN_API_KEY as string); -const EXCHANGE_ADDRESS = '0x4f833a24e1f95d70f028921e27040ca56e09ab0b'; +import { parseExchangeEvents } from './parsers/events'; +import { parseSraOrders } from './parsers/sra_orders'; let connection: Connection; (async () => { connection = await createConnection(config); await getExchangeEventsAsync(); - await getSraOrdersAsync(); + // await getSraOrdersAsync(); })(); +// TODO(albrow): Separately: Errors do not appear to be handled correctly. If you use the +// wrong rpcUrl it just returns early with no error. async function getExchangeEventsAsync(): Promise { - const fillRepository = connection.getRepository(ExchangeFillEvent); - const cancelRepository = connection.getRepository(ExchangeCancelEvent); - const cancelUpToRepository = connection.getRepository(ExchangeCancelUpToEvent); - console.log( - `found ${(await fillRepository.count()) + - (await cancelRepository.count()) + - (await cancelUpToRepository.count())} existing events`, - ); - const rawEvents = await etherscan.getContractEventsAsync(EXCHANGE_ADDRESS); - const events = parseExchangeEvents(rawEvents); + const provider = web3Factory.getRpcProvider({ + rpcUrl: 'https://mainnet.infura.io', + }); + const exchangeEvents = new ExchangeEventsSource(provider, 1); + const eventLogs = await exchangeEvents.getFillEventsAsync(); + const events = parseExchangeEvents(eventLogs); + console.log('Got events: ' + events.length); for (const event of events) { await event.save(); } - console.log( - `now there are ${(await fillRepository.count()) + - (await cancelRepository.count()) + - (await cancelUpToRepository.count())} total events`, - ); + console.log('Saved events.'); + console.log('Exiting process'); + process.exit(0); } async function getSraOrdersAsync(): Promise { const orderRepository = connection.getRepository(SraOrder); console.log(`found ${await orderRepository.count()} existing orders`); - const sraUrl = 'https://api.radarrelay.com/0x/v2'; const connect = new HttpClient(sraUrl); const rawOrders = await connect.getOrdersAsync(); diff --git a/packages/pipeline/src/parsers/events/index.ts b/packages/pipeline/src/parsers/events/index.ts new file mode 100644 index 000000000..66f382dda --- /dev/null +++ b/packages/pipeline/src/parsers/events/index.ts @@ -0,0 +1,111 @@ +import { + ExchangeCancelEventArgs, + ExchangeCancelUpToEventArgs, + ExchangeEventArgs, + ExchangeFillEventArgs, +} from '@0xproject/contract-wrappers'; +import { assetDataUtils } from '@0xproject/order-utils'; +import { AssetProxyId, ERC721AssetData } from '@0xproject/types'; +import { LogWithDecodedArgs } from 'ethereum-types'; +import * as R from 'ramda'; + +import { ExchangeCancelEvent } from '../../entities/ExchangeCancelEvent'; +import { ExchangeCancelUpToEvent } from '../../entities/ExchangeCancelUpToEvent'; +import { ExchangeFillEvent } from '../../entities/ExchangeFillEvent'; +import { bigNumbertoStringOrNull } from '../../utils'; + +export type ExchangeEventEntity = ExchangeFillEvent | ExchangeCancelEvent | ExchangeCancelUpToEvent; + +export const parseExchangeEvents: ( + eventLogs: Array>, +) => ExchangeEventEntity[] = R.map(_convertToEntity); + +export function _convertToEntity(eventLog: LogWithDecodedArgs): ExchangeEventEntity { + switch (eventLog.event) { + case 'Fill': + return _convertToExchangeFillEvent(eventLog as LogWithDecodedArgs); + case 'Cancel': + return _convertToExchangeCancelEvent(eventLog as LogWithDecodedArgs); + case 'CancelUpTo': + return _convertToExchangeCancelUpToEvent(eventLog as LogWithDecodedArgs); + default: + throw new Error('unexpected eventLog.event type: ' + eventLog.event); + } +} + +export function _convertToExchangeFillEvent(eventLog: LogWithDecodedArgs): ExchangeFillEvent { + const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.makerAssetData); + const makerAssetType = makerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; + const takerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.takerAssetData); + const takerAssetType = takerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; + const exchangeFillEvent = new ExchangeFillEvent(); + exchangeFillEvent.contractAddress = eventLog.address as string; + exchangeFillEvent.blockNumber = eventLog.blockNumber as number; + exchangeFillEvent.logIndex = eventLog.logIndex as number; + exchangeFillEvent.rawData = eventLog.data as string; + exchangeFillEvent.makerAddress = eventLog.args.makerAddress.toString(); + exchangeFillEvent.takerAddress = eventLog.args.takerAddress.toString(); + exchangeFillEvent.feeRecepientAddress = eventLog.args.feeRecipientAddress; + exchangeFillEvent.senderAddress = eventLog.args.senderAddress; + exchangeFillEvent.makerAssetFilledAmount = eventLog.args.makerAssetFilledAmount.toString(); + exchangeFillEvent.takerAssetFilledAmount = eventLog.args.takerAssetFilledAmount.toString(); + exchangeFillEvent.makerFeePaid = eventLog.args.makerFeePaid.toString(); + exchangeFillEvent.takerFeePaid = eventLog.args.takerFeePaid.toString(); + exchangeFillEvent.orderHash = eventLog.args.orderHash; + exchangeFillEvent.rawMakerAssetData = eventLog.args.makerAssetData; + exchangeFillEvent.makerAssetType = makerAssetType; + exchangeFillEvent.makerAssetProxyId = makerAssetData.assetProxyId; + exchangeFillEvent.makerTokenAddress = makerAssetData.tokenAddress; + exchangeFillEvent.makerTokenId = bigNumbertoStringOrNull((makerAssetData as ERC721AssetData).tokenId); + exchangeFillEvent.rawTakerAssetData = eventLog.args.takerAssetData; + exchangeFillEvent.takerAssetType = takerAssetType; + exchangeFillEvent.takerAssetProxyId = takerAssetData.assetProxyId; + exchangeFillEvent.takerTokenAddress = takerAssetData.tokenAddress; + exchangeFillEvent.takerTokenId = bigNumbertoStringOrNull((takerAssetData as ERC721AssetData).tokenId); + return exchangeFillEvent; +} + +export function _convertToExchangeCancelEvent( + eventLog: LogWithDecodedArgs, +): ExchangeCancelEvent { + const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.makerAssetData); + const makerAssetType = makerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; + const takerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.takerAssetData); + const takerAssetType = takerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; + const exchangeCancelEvent = new ExchangeCancelEvent(); + exchangeCancelEvent.contractAddress = eventLog.address as string; + exchangeCancelEvent.blockNumber = eventLog.blockNumber as number; + exchangeCancelEvent.logIndex = eventLog.logIndex as number; + exchangeCancelEvent.rawData = eventLog.data as string; + exchangeCancelEvent.makerAddress = eventLog.args.makerAddress.toString(); + exchangeCancelEvent.takerAddress = + eventLog.args.takerAddress == null ? null : eventLog.args.takerAddress.toString(); + exchangeCancelEvent.feeRecepientAddress = eventLog.args.feeRecipientAddress; + exchangeCancelEvent.senderAddress = eventLog.args.senderAddress; + exchangeCancelEvent.orderHash = eventLog.args.orderHash; + exchangeCancelEvent.rawMakerAssetData = eventLog.args.makerAssetData; + exchangeCancelEvent.makerAssetType = makerAssetType; + exchangeCancelEvent.makerAssetProxyId = makerAssetData.assetProxyId; + exchangeCancelEvent.makerTokenAddress = makerAssetData.tokenAddress; + exchangeCancelEvent.makerTokenId = bigNumbertoStringOrNull((makerAssetData as ERC721AssetData).tokenId); + exchangeCancelEvent.rawTakerAssetData = eventLog.args.takerAssetData; + exchangeCancelEvent.takerAssetType = takerAssetType; + exchangeCancelEvent.takerAssetProxyId = takerAssetData.assetProxyId; + exchangeCancelEvent.takerTokenAddress = takerAssetData.tokenAddress; + exchangeCancelEvent.takerTokenId = bigNumbertoStringOrNull((takerAssetData as ERC721AssetData).tokenId); + return exchangeCancelEvent; +} + +export function _convertToExchangeCancelUpToEvent( + eventLog: LogWithDecodedArgs, +): ExchangeCancelUpToEvent { + const exchangeCancelUpToEvent = new ExchangeCancelUpToEvent(); + exchangeCancelUpToEvent.contractAddress = eventLog.address as string; + exchangeCancelUpToEvent.blockNumber = eventLog.blockNumber as number; + exchangeCancelUpToEvent.logIndex = eventLog.logIndex as number; + exchangeCancelUpToEvent.rawData = eventLog.data as string; + exchangeCancelUpToEvent.makerAddress = eventLog.args.makerAddress.toString(); + exchangeCancelUpToEvent.senderAddress = eventLog.args.senderAddress.toString(); + exchangeCancelUpToEvent.orderEpoch = eventLog.args.orderEpoch.toString(); + return exchangeCancelUpToEvent; +} diff --git a/packages/pipeline/src/parsers/sra_orders/index.ts b/packages/pipeline/src/parsers/sra_orders/index.ts new file mode 100644 index 000000000..fb2b74dfe --- /dev/null +++ b/packages/pipeline/src/parsers/sra_orders/index.ts @@ -0,0 +1,54 @@ +import { APIOrder, OrdersResponse } from '@0xproject/connect'; +import { assetDataUtils, orderHashUtils } from '@0xproject/order-utils'; +import { AssetProxyId, ERC721AssetData } from '@0xproject/types'; +import * as R from 'ramda'; + +import { SraOrder } from '../../entities/SraOrder'; +import { bigNumbertoStringOrNull } from '../../utils'; + +export function parseSraOrders(rawOrdersResponse: OrdersResponse): SraOrder[] { + return R.map(_convertToEntity, rawOrdersResponse.records); +} + +export function _convertToEntity(apiOrder: APIOrder): SraOrder { + // TODO(albrow): refactor out common asset data decoding code. + const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(apiOrder.order.makerAssetData); + const makerAssetType = makerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; + const takerAssetData = assetDataUtils.decodeAssetDataOrThrow(apiOrder.order.takerAssetData); + const takerAssetType = takerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; + + const sraOrder = new SraOrder(); + sraOrder.exchangeAddress = apiOrder.order.exchangeAddress; + sraOrder.orderHashHex = orderHashUtils.getOrderHashHex(apiOrder.order); + + // TODO(albrow): Set these fields to the correct values upstack. + sraOrder.lastUpdatedTimestamp = 0; + sraOrder.firstSeenTimestamp = 0; + + sraOrder.makerAddress = apiOrder.order.makerAddress; + sraOrder.takerAddress = apiOrder.order.takerAddress; + sraOrder.feeRecipientAddress = apiOrder.order.feeRecipientAddress; + sraOrder.senderAddress = apiOrder.order.senderAddress; + sraOrder.makerAssetAmount = apiOrder.order.makerAssetAmount.toString(); + sraOrder.takerAssetAmount = apiOrder.order.takerAssetAmount.toString(); + sraOrder.makerFee = apiOrder.order.makerFee.toString(); + sraOrder.takerFee = apiOrder.order.takerFee.toString(); + sraOrder.expirationTimeSeconds = apiOrder.order.expirationTimeSeconds.toString(); + sraOrder.salt = apiOrder.order.salt.toString(); + sraOrder.signature = apiOrder.order.signature; + + sraOrder.rawMakerAssetData = apiOrder.order.makerAssetData; + sraOrder.makerAssetType = makerAssetType; + sraOrder.makerAssetProxyId = makerAssetData.assetProxyId; + sraOrder.makerTokenAddress = makerAssetData.tokenAddress; + sraOrder.makerTokenId = bigNumbertoStringOrNull((makerAssetData as ERC721AssetData).tokenId); + sraOrder.rawTakerAssetData = apiOrder.order.takerAssetData; + sraOrder.takerAssetType = takerAssetType; + sraOrder.takerAssetProxyId = takerAssetData.assetProxyId; + sraOrder.takerTokenAddress = takerAssetData.tokenAddress; + sraOrder.takerTokenId = bigNumbertoStringOrNull((takerAssetData as ERC721AssetData).tokenId); + + sraOrder.metaDataJson = JSON.stringify(apiOrder.metaData); + + return sraOrder; +} diff --git a/packages/pipeline/test/data_types/events/event_utils_test.ts b/packages/pipeline/test/data_types/events/event_utils_test.ts deleted file mode 100644 index 731819106..000000000 --- a/packages/pipeline/test/data_types/events/event_utils_test.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { Exchange } from '@0xproject/contract-artifacts'; -import { BigNumber } from '@0xproject/utils'; -import * as chai from 'chai'; -import { DecodedLogArgs, LogEntry, LogWithDecodedArgs } from 'ethereum-types'; -import 'mocha'; - -import { EventsResponseResult } from '../../../src/data_sources/etherscan'; -import { convertResponseToLogEntry, decodeLogEntry } from '../../../src/data_types/events/event_utils'; -import { chaiSetup } from '../../utils/chai_setup'; - -chaiSetup.configure(); -const expect = chai.expect; - -describe('event_utils', () => { - describe('convertResponseToLogEntry', () => { - it('converts EventsResponseResult to LogEntry', () => { - const input: EventsResponseResult = { - address: '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', - topics: [ - '0x82af639571738f4ebd4268fb0363d8957ebe1bbb9e78dba5ebd69eed39b154f0', - '0x00000000000000000000000067032ef7be8fa07c4335d0134099db0f3875e930', - '0x0000000000000000000000000000000000000000000000000000000000000000', - ], - data: '0x00000000000000000000000000000000000000000000000000000165f2d3f94d', - blockNumber: '0x61127b', - timeStamp: '0x5ba2878e', - gasPrice: '0x1a13b8600', - gasUsed: '0xd9dc', - logIndex: '0x63', - transactionHash: '0xa3f71931ddab6e758b9d1755b2715b376759f49f23fff60755f7e073367d61b5', - transactionIndex: '0x35', - }; - const expected: LogEntry = { - logIndex: 99, - transactionIndex: 53, - transactionHash: input.transactionHash, - blockHash: '', - blockNumber: 6361723, - address: input.address, - data: input.data, - topics: input.topics, - }; - const actual = convertResponseToLogEntry(input); - expect(actual).deep.equal(expected); - }); - }); - describe('decodeLogEntry', () => { - it('decodes LogEntry into LogWithDecodedArgs', () => { - const input: LogEntry = { - logIndex: 96, - transactionIndex: 52, - transactionHash: '0x02b59043e9b38b430c8c66abe67ab4a9e5509def8f8552b54231e88db1839831', - blockHash: '', - blockNumber: 6361723, - address: '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', - data: - '0x00000000000000000000000067032ef7be8fa07c4335d0134099db0f3875e93000000000000000000000000067032ef7be8fa07c4335d0134099db0f3875e930000000000000000000000000000000000000000000000000000000174876e8000000000000000000000000000000000000000000000000000000000013ab668000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000', - topics: [ - '0x0bcc4c97732e47d9946f229edb95f5b6323f601300e4690de719993f3c371129', - '0x0000000000000000000000003f7f832abb3be28442c0e48b7222e02b322c78f3', - '0x000000000000000000000000a258b39954cef5cb142fd567a46cddb31a670124', - '0x523404b4e6f847d9aefcf5be024be396449b4635590291fd7a28a8c940843858', - ], - }; - const expected: LogWithDecodedArgs = { - ...input, - event: 'Fill', - args: { - makerAddress: '0x3f7f832abb3be28442c0e48b7222e02b322c78f3', - feeRecipientAddress: '0xa258b39954cef5cb142fd567a46cddb31a670124', - takerAddress: '0x67032ef7be8fa07c4335d0134099db0f3875e930', - senderAddress: '0x67032ef7be8fa07c4335d0134099db0f3875e930', - makerAssetFilledAmount: new BigNumber('100000000000'), - takerAssetFilledAmount: new BigNumber('330000000'), - makerFeePaid: new BigNumber('0'), - takerFeePaid: new BigNumber('0'), - orderHash: '0x523404b4e6f847d9aefcf5be024be396449b4635590291fd7a28a8c940843858', - makerAssetData: '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498', - takerAssetData: '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - }, - }; - const actual = decodeLogEntry(Exchange.compilerOutput.abi, input); - expect(actual).deep.equal(expected); - }); - }); -}); diff --git a/packages/pipeline/test/data_types/events/exchange_events_test.ts b/packages/pipeline/test/data_types/events/exchange_events_test.ts deleted file mode 100644 index f1432892d..000000000 --- a/packages/pipeline/test/data_types/events/exchange_events_test.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { ExchangeFillEventArgs } from '@0xproject/contract-wrappers'; -import { BigNumber } from '@0xproject/utils'; -import * as chai from 'chai'; -import { LogWithDecodedArgs } from 'ethereum-types'; -import 'mocha'; - -import { _convertToEntity } from '../../../src/data_types/events/exchange_events'; -import { ExchangeFillEvent } from '../../../src/entities/ExchangeFillEvent'; -import { chaiSetup } from '../../utils/chai_setup'; - -chaiSetup.configure(); -const expect = chai.expect; - -// tslint:disable:custom-no-magic-numbers -describe('exchange_events', () => { - describe('_convertToEntity', () => { - it('converts LogWithDecodedArgs to ExchangeFillEvent entity', () => { - const input: LogWithDecodedArgs = { - logIndex: 102, - transactionIndex: 38, - transactionHash: '0x6dd106d002873746072fc5e496dd0fb2541b68c77bcf9184ae19a42fd33657fe', - blockHash: '', - blockNumber: 6276262, - address: '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', - data: - '0x000000000000000000000000f6da68519f78b0d0bc93c701e86affcb75c92428000000000000000000000000f6da68519f78b0d0bc93c701e86affcb75c92428000000000000000000000000000000000000000000000000002386f26fc10000000000000000000000000000000000000000000000000000016345785d8a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f49800000000000000000000000000000000000000000000000000000000', - topics: [ - '0x0bcc4c97732e47d9946f229edb95f5b6323f601300e4690de719993f3c371129', - '0x000000000000000000000000f6da68519f78b0d0bc93c701e86affcb75c92428', - '0x000000000000000000000000c370d2a5920344aa6b7d8d11250e3e861434cbdd', - '0xab12ed2cbaa5615ab690b9da75a46e53ddfcf3f1a68655b5fe0d94c75a1aac4a', - ], - event: 'Fill', - args: { - makerAddress: '0xf6da68519f78b0d0bc93c701e86affcb75c92428', - feeRecipientAddress: '0xc370d2a5920344aa6b7d8d11250e3e861434cbdd', - takerAddress: '0xf6da68519f78b0d0bc93c701e86affcb75c92428', - senderAddress: '0xf6da68519f78b0d0bc93c701e86affcb75c92428', - makerAssetFilledAmount: new BigNumber('10000000000000000'), - takerAssetFilledAmount: new BigNumber('100000000000000000'), - makerFeePaid: new BigNumber('0'), - takerFeePaid: new BigNumber('0'), - orderHash: '0xab12ed2cbaa5615ab690b9da75a46e53ddfcf3f1a68655b5fe0d94c75a1aac4a', - makerAssetData: '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - takerAssetData: '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498', - }, - }; - const expected = new ExchangeFillEvent(); - expected.contractAddress = '0x4f833a24e1f95d70f028921e27040ca56e09ab0b'; - expected.blockNumber = 6276262; - expected.logIndex = 102; - expected.rawData = - '0x000000000000000000000000f6da68519f78b0d0bc93c701e86affcb75c92428000000000000000000000000f6da68519f78b0d0bc93c701e86affcb75c92428000000000000000000000000000000000000000000000000002386f26fc10000000000000000000000000000000000000000000000000000016345785d8a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f49800000000000000000000000000000000000000000000000000000000'; - expected.makerAddress = '0xf6da68519f78b0d0bc93c701e86affcb75c92428'; - expected.takerAddress = '0xf6da68519f78b0d0bc93c701e86affcb75c92428'; - expected.feeRecepientAddress = '0xc370d2a5920344aa6b7d8d11250e3e861434cbdd'; - expected.senderAddress = '0xf6da68519f78b0d0bc93c701e86affcb75c92428'; - expected.makerAssetFilledAmount = '10000000000000000'; - expected.takerAssetFilledAmount = '100000000000000000'; - expected.makerFeePaid = '0'; - expected.takerFeePaid = '0'; - expected.orderHash = '0xab12ed2cbaa5615ab690b9da75a46e53ddfcf3f1a68655b5fe0d94c75a1aac4a'; - expected.rawMakerAssetData = '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; - expected.makerAssetType = 'erc20'; - expected.makerAssetProxyId = '0xf47261b0'; - expected.makerTokenAddress = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; - expected.makerTokenId = null; - expected.rawTakerAssetData = '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498'; - expected.takerAssetType = 'erc20'; - expected.takerAssetProxyId = '0xf47261b0'; - expected.takerTokenAddress = '0xe41d2489571d322189246dafa5ebde1f4699f498'; - expected.takerTokenId = null; - const actual = _convertToEntity(input); - expect(actual).deep.equal(expected); - }); - }); -}); diff --git a/packages/pipeline/test/data_types/sra_orders/index_test.ts b/packages/pipeline/test/data_types/sra_orders/index_test.ts deleted file mode 100644 index 174f89b4f..000000000 --- a/packages/pipeline/test/data_types/sra_orders/index_test.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { APIOrder } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; -import * as chai from 'chai'; -import 'mocha'; -import { Connection, createConnection } from 'typeorm'; - -import { _convertToEntity } from '../../../src/data_types/sra_orders'; -import { SraOrder } from '../../../src/entities/SraOrder'; -import { chaiSetup } from '../../utils/chai_setup'; - -import { config } from '../../../src/ormconfig'; - -chaiSetup.configure(); -const expect = chai.expect; - -// tslint:disable:custom-no-magic-numbers -describe('sra_orders', () => { - describe('_convertToEntity', () => { - before(async () => { - // HACK(albrow): We don't actually use this connection but it seems - // to be required because chai calls the inspect method of the - // entity and that method requires a "default" connection. - await createConnection(config); - }); - it('converts ApiOrder to SraOrder entity', () => { - const input: APIOrder = { - order: { - makerAddress: '0xb45df06e38540a675fdb5b598abf2c0dbe9d6b81', - takerAddress: '0x0000000000000000000000000000000000000000', - feeRecipientAddress: '0xa258b39954cef5cb142fd567a46cddb31a670124', - senderAddress: '0x0000000000000000000000000000000000000000', - makerAssetAmount: new BigNumber('1619310371000000000'), - takerAssetAmount: new BigNumber('8178335207070707070707'), - makerFee: new BigNumber('0'), - takerFee: new BigNumber('0'), - exchangeAddress: '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', - expirationTimeSeconds: new BigNumber('1538529488'), - signature: - '0x1b5a5d672b0d647b5797387ccbb89d822d5d2e873346b014f4ff816ff0783f2a7a0d2824d2d7042ec8ea375bc7f870963e1cb8248f1db03ddf125e27b5963aa11f03', - salt: new BigNumber('1537924688891'), - makerAssetData: '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - takerAssetData: '0xf47261b000000000000000000000000042d6622dece394b54999fbd73d108123806f6a18', - }, - metaData: { isThisArbitraryData: true, powerLevel: 9001 }, - }; - const expected = new SraOrder(); - expected.exchangeAddress = '0x4f833a24e1f95d70f028921e27040ca56e09ab0b'; - expected.orderHashHex = '0x1bdbeb0d088a33da28b9ee6d94e8771452f90f4a69107da2fa75195d61b9a1c9'; - expected.lastUpdatedTimestamp = 0; - expected.firstSeenTimestamp = 0; - expected.makerAddress = '0xb45df06e38540a675fdb5b598abf2c0dbe9d6b81'; - expected.takerAddress = '0x0000000000000000000000000000000000000000'; - expected.feeRecipientAddress = '0xa258b39954cef5cb142fd567a46cddb31a670124'; - expected.senderAddress = '0x0000000000000000000000000000000000000000'; - expected.makerAssetAmount = '1619310371000000000'; - expected.takerAssetAmount = '8178335207070707070707'; - expected.makerFee = '0'; - expected.takerFee = '0'; - expected.expirationTimeSeconds = '1538529488'; - expected.salt = '1537924688891'; - expected.signature = - '0x1b5a5d672b0d647b5797387ccbb89d822d5d2e873346b014f4ff816ff0783f2a7a0d2824d2d7042ec8ea375bc7f870963e1cb8248f1db03ddf125e27b5963aa11f03'; - expected.rawMakerAssetData = '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; - expected.makerAssetType = 'erc20'; - expected.makerAssetProxyId = '0xf47261b0'; - expected.makerTokenAddress = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; - expected.makerTokenId = null; - expected.rawTakerAssetData = '0xf47261b000000000000000000000000042d6622dece394b54999fbd73d108123806f6a18'; - expected.takerAssetType = 'erc20'; - expected.takerAssetProxyId = '0xf47261b0'; - expected.takerTokenAddress = '0x42d6622dece394b54999fbd73d108123806f6a18'; - expected.takerTokenId = null; - expected.metaDataJson = '{"isThisArbitraryData":true,"powerLevel":9001}'; - - const actual = _convertToEntity(input); - expect(actual).deep.equal(expected); - }); - }); -}); diff --git a/packages/pipeline/test/parsers/events/index_test.ts b/packages/pipeline/test/parsers/events/index_test.ts new file mode 100644 index 000000000..2a2db1a94 --- /dev/null +++ b/packages/pipeline/test/parsers/events/index_test.ts @@ -0,0 +1,77 @@ +import { ExchangeFillEventArgs } from '@0xproject/contract-wrappers'; +import { BigNumber } from '@0xproject/utils'; +import * as chai from 'chai'; +import { LogWithDecodedArgs } from 'ethereum-types'; +import 'mocha'; + +import { ExchangeFillEvent } from '../../../src/entities/ExchangeFillEvent'; +import { _convertToEntity } from '../../../src/parsers/events'; +import { chaiSetup } from '../../utils/chai_setup'; + +chaiSetup.configure(); +const expect = chai.expect; + +// tslint:disable:custom-no-magic-numbers +describe('exchange_events', () => { + describe('_convertToEntity', () => { + it('converts LogWithDecodedArgs to ExchangeFillEvent entity', () => { + const input: LogWithDecodedArgs = { + logIndex: 102, + transactionIndex: 38, + transactionHash: '0x6dd106d002873746072fc5e496dd0fb2541b68c77bcf9184ae19a42fd33657fe', + blockHash: '', + blockNumber: 6276262, + address: '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', + data: + '0x000000000000000000000000f6da68519f78b0d0bc93c701e86affcb75c92428000000000000000000000000f6da68519f78b0d0bc93c701e86affcb75c92428000000000000000000000000000000000000000000000000002386f26fc10000000000000000000000000000000000000000000000000000016345785d8a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f49800000000000000000000000000000000000000000000000000000000', + topics: [ + '0x0bcc4c97732e47d9946f229edb95f5b6323f601300e4690de719993f3c371129', + '0x000000000000000000000000f6da68519f78b0d0bc93c701e86affcb75c92428', + '0x000000000000000000000000c370d2a5920344aa6b7d8d11250e3e861434cbdd', + '0xab12ed2cbaa5615ab690b9da75a46e53ddfcf3f1a68655b5fe0d94c75a1aac4a', + ], + event: 'Fill', + args: { + makerAddress: '0xf6da68519f78b0d0bc93c701e86affcb75c92428', + feeRecipientAddress: '0xc370d2a5920344aa6b7d8d11250e3e861434cbdd', + takerAddress: '0xf6da68519f78b0d0bc93c701e86affcb75c92428', + senderAddress: '0xf6da68519f78b0d0bc93c701e86affcb75c92428', + makerAssetFilledAmount: new BigNumber('10000000000000000'), + takerAssetFilledAmount: new BigNumber('100000000000000000'), + makerFeePaid: new BigNumber('0'), + takerFeePaid: new BigNumber('0'), + orderHash: '0xab12ed2cbaa5615ab690b9da75a46e53ddfcf3f1a68655b5fe0d94c75a1aac4a', + makerAssetData: '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + takerAssetData: '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498', + }, + }; + const expected = new ExchangeFillEvent(); + expected.contractAddress = '0x4f833a24e1f95d70f028921e27040ca56e09ab0b'; + expected.blockNumber = 6276262; + expected.logIndex = 102; + expected.rawData = + '0x000000000000000000000000f6da68519f78b0d0bc93c701e86affcb75c92428000000000000000000000000f6da68519f78b0d0bc93c701e86affcb75c92428000000000000000000000000000000000000000000000000002386f26fc10000000000000000000000000000000000000000000000000000016345785d8a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f49800000000000000000000000000000000000000000000000000000000'; + expected.makerAddress = '0xf6da68519f78b0d0bc93c701e86affcb75c92428'; + expected.takerAddress = '0xf6da68519f78b0d0bc93c701e86affcb75c92428'; + expected.feeRecepientAddress = '0xc370d2a5920344aa6b7d8d11250e3e861434cbdd'; + expected.senderAddress = '0xf6da68519f78b0d0bc93c701e86affcb75c92428'; + expected.makerAssetFilledAmount = '10000000000000000'; + expected.takerAssetFilledAmount = '100000000000000000'; + expected.makerFeePaid = '0'; + expected.takerFeePaid = '0'; + expected.orderHash = '0xab12ed2cbaa5615ab690b9da75a46e53ddfcf3f1a68655b5fe0d94c75a1aac4a'; + expected.rawMakerAssetData = '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; + expected.makerAssetType = 'erc20'; + expected.makerAssetProxyId = '0xf47261b0'; + expected.makerTokenAddress = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; + expected.makerTokenId = null; + expected.rawTakerAssetData = '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498'; + expected.takerAssetType = 'erc20'; + expected.takerAssetProxyId = '0xf47261b0'; + expected.takerTokenAddress = '0xe41d2489571d322189246dafa5ebde1f4699f498'; + expected.takerTokenId = null; + const actual = _convertToEntity(input); + expect(actual).deep.equal(expected); + }); + }); +}); diff --git a/packages/pipeline/test/parsers/sra_orders/index_test.ts b/packages/pipeline/test/parsers/sra_orders/index_test.ts new file mode 100644 index 000000000..952a6f3c6 --- /dev/null +++ b/packages/pipeline/test/parsers/sra_orders/index_test.ts @@ -0,0 +1,70 @@ +import { APIOrder } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; +import * as chai from 'chai'; +import 'mocha'; + +import { SraOrder } from '../../../src/entities/SraOrder'; +import { _convertToEntity } from '../../../src/parsers/sra_orders'; +import { chaiSetup } from '../../utils/chai_setup'; + +chaiSetup.configure(); +const expect = chai.expect; + +// tslint:disable:custom-no-magic-numbers +describe('sra_orders', () => { + describe('_convertToEntity', () => { + it('converts ApiOrder to SraOrder entity', () => { + const input: APIOrder = { + order: { + makerAddress: '0xb45df06e38540a675fdb5b598abf2c0dbe9d6b81', + takerAddress: '0x0000000000000000000000000000000000000000', + feeRecipientAddress: '0xa258b39954cef5cb142fd567a46cddb31a670124', + senderAddress: '0x0000000000000000000000000000000000000000', + makerAssetAmount: new BigNumber('1619310371000000000'), + takerAssetAmount: new BigNumber('8178335207070707070707'), + makerFee: new BigNumber('0'), + takerFee: new BigNumber('0'), + exchangeAddress: '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', + expirationTimeSeconds: new BigNumber('1538529488'), + signature: + '0x1b5a5d672b0d647b5797387ccbb89d822d5d2e873346b014f4ff816ff0783f2a7a0d2824d2d7042ec8ea375bc7f870963e1cb8248f1db03ddf125e27b5963aa11f03', + salt: new BigNumber('1537924688891'), + makerAssetData: '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + takerAssetData: '0xf47261b000000000000000000000000042d6622dece394b54999fbd73d108123806f6a18', + }, + metaData: { isThisArbitraryData: true, powerLevel: 9001 }, + }; + const expected = new SraOrder(); + expected.exchangeAddress = '0x4f833a24e1f95d70f028921e27040ca56e09ab0b'; + expected.orderHashHex = '0x1bdbeb0d088a33da28b9ee6d94e8771452f90f4a69107da2fa75195d61b9a1c9'; + expected.lastUpdatedTimestamp = 0; + expected.firstSeenTimestamp = 0; + expected.makerAddress = '0xb45df06e38540a675fdb5b598abf2c0dbe9d6b81'; + expected.takerAddress = '0x0000000000000000000000000000000000000000'; + expected.feeRecipientAddress = '0xa258b39954cef5cb142fd567a46cddb31a670124'; + expected.senderAddress = '0x0000000000000000000000000000000000000000'; + expected.makerAssetAmount = '1619310371000000000'; + expected.takerAssetAmount = '8178335207070707070707'; + expected.makerFee = '0'; + expected.takerFee = '0'; + expected.expirationTimeSeconds = '1538529488'; + expected.salt = '1537924688891'; + expected.signature = + '0x1b5a5d672b0d647b5797387ccbb89d822d5d2e873346b014f4ff816ff0783f2a7a0d2824d2d7042ec8ea375bc7f870963e1cb8248f1db03ddf125e27b5963aa11f03'; + expected.rawMakerAssetData = '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; + expected.makerAssetType = 'erc20'; + expected.makerAssetProxyId = '0xf47261b0'; + expected.makerTokenAddress = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; + expected.makerTokenId = null; + expected.rawTakerAssetData = '0xf47261b000000000000000000000000042d6622dece394b54999fbd73d108123806f6a18'; + expected.takerAssetType = 'erc20'; + expected.takerAssetProxyId = '0xf47261b0'; + expected.takerTokenAddress = '0x42d6622dece394b54999fbd73d108123806f6a18'; + expected.takerTokenId = null; + expected.metaDataJson = '{"isThisArbitraryData":true,"powerLevel":9001}'; + + const actual = _convertToEntity(input); + expect(actual).deep.equal(expected); + }); + }); +}); diff --git a/yarn.lock b/yarn.lock index 38318d0a7..c3546946c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -472,294 +472,67 @@ npmlog "^4.1.2" write-file-atomic "^2.3.0" -"@0xproject/assert@^0.2.14": - version "0.2.14" - resolved "https://registry.yarnpkg.com/@0xproject/assert/-/assert-0.2.14.tgz#7d5a373fedc8eb482716b730f4dddf3ef33bfa29" - dependencies: - "@0xproject/json-schemas" "^0.8.3" - "@0xproject/typescript-typings" "^0.4.3" - "@0xproject/utils" "^0.7.3" - lodash "4.17.10" - valid-url "1.0.9" - -"@0xproject/assert@^1.0.10", "@0xproject/assert@^1.0.11", "@0xproject/assert@^1.0.13": - version "1.0.13" - resolved "https://registry.yarnpkg.com/@0xproject/assert/-/assert-1.0.13.tgz#e370ccce08933dd2a970bdd02b92e59c65dd75d4" +"@0x/abi-gen-wrappers@^1.0.2": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@0x/abi-gen-wrappers/-/abi-gen-wrappers-1.1.0.tgz#d4e4f10699b5da6bcfadc3842f165fc59f686e09" dependencies: - "@0xproject/json-schemas" "^1.0.7" - "@0xproject/typescript-typings" "^3.0.2" - "@0xproject/utils" "^2.0.2" - lodash "^4.17.5" - valid-url "^1.0.9" + "@0x/base-contract" "^3.0.7" -"@0xproject/base-contract@^2.0.4": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@0xproject/base-contract/-/base-contract-2.0.5.tgz#caca54b33efeb82c53142a96becc6871c96974d6" +"@0x/contract-addresses@^1.1.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@0x/contract-addresses/-/contract-addresses-1.2.0.tgz#aa5001d73adb1ec9cc58ab4f6e0cac0acc4ad0a8" dependencies: - "@0xproject/typescript-typings" "^2.0.2" - "@0xproject/utils" "^1.0.11" - "@0xproject/web3-wrapper" "^3.0.1" - ethereum-types "^1.0.8" - ethers "3.0.22" lodash "^4.17.5" -"@0xproject/base-contract@^3.0.1": +"@0x/contract-wrappers@^3.0.0": version "3.0.1" - resolved "https://registry.yarnpkg.com/@0xproject/base-contract/-/base-contract-3.0.1.tgz#ae6ff8c4492affcb015324d6a1a8de72b22e1f8b" - dependencies: - "@0xproject/typescript-typings" "^3.0.2" - "@0xproject/utils" "^2.0.2" - "@0xproject/web3-wrapper" "^3.0.3" - ethereum-types "^1.0.11" - ethers "4.0.0-beta.14" - lodash "^4.17.5" - -"@0xproject/connect@^2.0.4": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@0xproject/connect/-/connect-2.0.4.tgz#d61cd382edbb80120c8efce91dc85d2c668a5c5e" - dependencies: - "@0xproject/assert" "^1.0.11" - "@0xproject/json-schemas" "^1.0.4" - "@0xproject/order-utils" "^1.0.5" - "@0xproject/types" "^1.1.1" - "@0xproject/typescript-typings" "^2.0.2" - "@0xproject/utils" "^1.0.11" - lodash "^4.17.5" - query-string "^5.0.1" - sinon "^4.0.0" - uuid "^3.3.2" - websocket "^1.0.25" - -"@0xproject/contract-wrappers@^1.0.1": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@0xproject/contract-wrappers/-/contract-wrappers-1.0.5.tgz#5a522b3fd8404b8c42169d5be7e3b6903a7b1356" - dependencies: - "@0xproject/assert" "^1.0.10" - "@0xproject/base-contract" "^2.0.4" - "@0xproject/fill-scenarios" "^1.0.4" - "@0xproject/json-schemas" "^1.0.3" - "@0xproject/order-utils" "^1.0.4" - "@0xproject/types" "^1.1.0" - "@0xproject/typescript-typings" "^2.0.1" - "@0xproject/utils" "^1.0.10" - "@0xproject/web3-wrapper" "^3.0.0" - ethereum-types "^1.0.7" - ethereumjs-blockstream "5.0.0" + resolved "https://registry.yarnpkg.com/@0x/contract-wrappers/-/contract-wrappers-3.0.1.tgz#e25f5812595d994ff66e36aa2aa99eb53becfc7c" + dependencies: + "@0x/abi-gen-wrappers" "^1.0.2" + "@0x/assert" "^1.0.15" + "@0x/contract-addresses" "^1.1.0" + "@0x/contract-artifacts" "^1.1.0" + "@0x/fill-scenarios" "^1.0.9" + "@0x/json-schemas" "^2.0.1" + "@0x/order-utils" "^2.0.1" + "@0x/types" "^1.2.1" + "@0x/typescript-typings" "^3.0.4" + "@0x/utils" "^2.0.4" + "@0x/web3-wrapper" "^3.1.1" + ethereum-types "^1.1.2" + ethereumjs-blockstream "6.0.0" ethereumjs-util "^5.1.1" - ethers "3.0.22" + ethers "~4.0.4" js-sha3 "^0.7.0" lodash "^4.17.5" uuid "^3.1.0" -"@0xproject/fill-scenarios@^1.0.4": - version "1.0.7" - resolved "https://registry.yarnpkg.com/@0xproject/fill-scenarios/-/fill-scenarios-1.0.7.tgz#3a5e7acec46065ccbd7f50d4052d653242aa2da2" - dependencies: - "@0xproject/base-contract" "^3.0.1" - "@0xproject/order-utils" "^1.0.7" - "@0xproject/types" "^1.1.4" - "@0xproject/typescript-typings" "^3.0.2" - "@0xproject/utils" "^2.0.2" - "@0xproject/web3-wrapper" "^3.0.3" - ethereum-types "^1.0.11" - ethers "4.0.0-beta.14" - lodash "^4.17.5" - -"@0xproject/json-schemas@^0.8.3": - version "0.8.3" - resolved "https://registry.yarnpkg.com/@0xproject/json-schemas/-/json-schemas-0.8.3.tgz#455e6219a6bd05e990392165192a983a9ab89f26" - dependencies: - "@0xproject/typescript-typings" "^0.4.3" - "@types/node" "9.6.0" - jsonschema "1.2.2" - lodash.values "4.3.0" - -"@0xproject/json-schemas@^1.0.3", "@0xproject/json-schemas@^1.0.4", "@0xproject/json-schemas@^1.0.7": - version "1.0.7" - resolved "https://registry.yarnpkg.com/@0xproject/json-schemas/-/json-schemas-1.0.7.tgz#64b5692a1bcc5938ce2da01fc2f8aecd72ec35be" - dependencies: - "@0xproject/typescript-typings" "^3.0.2" - "@types/node" "*" - jsonschema "^1.2.0" - lodash.values "^4.3.0" - -"@0xproject/npm-cli-login@^0.0.11": - version "0.0.11" - resolved "https://registry.yarnpkg.com/@0xproject/npm-cli-login/-/npm-cli-login-0.0.11.tgz#3f1ec06112ce62aad300ff0575358f68aeecde2e" - dependencies: - npm-registry-client "7.0.9" - -"@0xproject/order-utils@^1.0.2", "@0xproject/order-utils@^1.0.4", "@0xproject/order-utils@^1.0.5", "@0xproject/order-utils@^1.0.7": - version "1.0.7" - resolved "https://registry.yarnpkg.com/@0xproject/order-utils/-/order-utils-1.0.7.tgz#475cd5f1a11dc7816847abb4d3fd17bbaf32bf4f" - dependencies: - "@0xproject/assert" "^1.0.13" - "@0xproject/base-contract" "^3.0.1" - "@0xproject/json-schemas" "^1.0.7" - "@0xproject/types" "^1.1.4" - "@0xproject/typescript-typings" "^3.0.2" - "@0xproject/utils" "^2.0.2" - "@0xproject/web3-wrapper" "^3.0.3" +"@0x/order-utils@^2.0.0", "@0x/order-utils@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@0x/order-utils/-/order-utils-2.0.1.tgz#8c46d7aeb9e2cce54a0822824c12427cbe5f7eb4" + dependencies: + "@0x/abi-gen-wrappers" "^1.0.2" + "@0x/assert" "^1.0.15" + "@0x/base-contract" "^3.0.3" + "@0x/contract-artifacts" "^1.1.0" + "@0x/json-schemas" "^2.0.1" + "@0x/types" "^1.2.1" + "@0x/typescript-typings" "^3.0.4" + "@0x/utils" "^2.0.4" + "@0x/web3-wrapper" "^3.1.1" "@types/node" "*" bn.js "^4.11.8" - ethereum-types "^1.0.11" + ethereum-types "^1.1.2" ethereumjs-abi "0.6.5" ethereumjs-util "^5.1.1" - ethers "4.0.0-beta.14" - lodash "^4.17.5" - -"@0xproject/subproviders@^2.0.2": - version "2.0.7" - resolved "https://registry.yarnpkg.com/@0xproject/subproviders/-/subproviders-2.0.7.tgz#0b126f6d6df0ad7bd65689fae5a2d109991f019e" - dependencies: - "@0xproject/assert" "^1.0.13" - "@0xproject/types" "^1.1.4" - "@0xproject/typescript-typings" "^3.0.2" - "@0xproject/utils" "^2.0.2" - "@0xproject/web3-wrapper" "^3.0.3" - "@ledgerhq/hw-app-eth" "^4.3.0" - "@ledgerhq/hw-transport-u2f" "^4.3.0" - "@types/eth-lightwallet" "^3.0.0" - "@types/ganache-core" "^2.1.0" - "@types/hdkey" "^0.7.0" - bip39 "^2.5.0" - bn.js "^4.11.8" - eth-lightwallet "^3.0.1" - ethereum-types "^1.0.11" - ethereumjs-tx "^1.3.5" - ethereumjs-util "^5.1.1" - ganache-core "0xProject/ganache-core#monorepo-dep" - hdkey "^0.7.1" - json-rpc-error "2.0.0" + ethers "~4.0.4" lodash "^4.17.5" - semaphore-async-await "^1.5.1" - web3-provider-engine "14.0.6" - optionalDependencies: - "@ledgerhq/hw-transport-node-hid" "^4.3.0" -"@0xproject/tslint-config@^1.0.7": - version "1.0.8" - resolved "https://registry.yarnpkg.com/@0xproject/tslint-config/-/tslint-config-1.0.8.tgz#cfdce03b84dd2e108278b0024f89fe30218299e0" - dependencies: - lodash "^4.17.5" - tslint "5.11.0" - tslint-eslint-rules "5.4.0" - tslint-react "^3.2.0" - tsutils "3.0.0" - -"@0xproject/types@^0.8.2": - version "0.8.2" - resolved "https://registry.yarnpkg.com/@0xproject/types/-/types-0.8.2.tgz#6f936b73bfb6f017b5102002d97da0881da92d1b" - dependencies: - "@types/node" "9.6.0" - bignumber.js "~4.1.0" - -"@0xproject/types@^1.0.1", "@0xproject/types@^1.1.0", "@0xproject/types@^1.1.1", "@0xproject/types@^1.1.4": - version "1.1.4" - resolved "https://registry.yarnpkg.com/@0xproject/types/-/types-1.1.4.tgz#3ffd65e670d6a21dab19ee0ffd5fad0056291b8e" - dependencies: - "@types/node" "*" - bignumber.js "~4.1.0" - ethereum-types "^1.0.11" - -"@0xproject/typescript-typings@^0.4.3": - version "0.4.3" - resolved "https://registry.yarnpkg.com/@0xproject/typescript-typings/-/typescript-typings-0.4.3.tgz#f99f939a43f2764ad7182fcd78a71212a1d76d96" - dependencies: - "@0xproject/types" "^0.8.2" - bignumber.js "~4.1.0" - ethereum-types "^0.0.2" - -"@0xproject/typescript-typings@^1.0.3": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@0xproject/typescript-typings/-/typescript-typings-1.0.5.tgz#a808443419f26a7b90d63d1afd3efbfb48644184" - dependencies: - "@types/bn.js" "^4.11.0" - "@types/react" "*" - bignumber.js "~4.1.0" - ethereum-types "^1.0.5" - popper.js "1.14.3" - -"@0xproject/typescript-typings@^2.0.1", "@0xproject/typescript-typings@^2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@0xproject/typescript-typings/-/typescript-typings-2.0.2.tgz#1812f64e341f1d24c09b8b5a951cbde0e5fff9c2" - dependencies: - "@types/bn.js" "^4.11.0" - "@types/react" "*" - bignumber.js "~4.1.0" - ethereum-types "^1.0.8" - popper.js "1.14.3" - -"@0xproject/typescript-typings@^3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@0xproject/typescript-typings/-/typescript-typings-3.0.2.tgz#797416af57618304cffb7a0053cf1822725b8dcd" - dependencies: - "@types/bn.js" "^4.11.0" - "@types/react" "*" - bignumber.js "~4.1.0" - ethereum-types "^1.0.11" - popper.js "1.14.3" - -"@0xproject/utils@^0.7.3": - version "0.7.3" - resolved "https://registry.yarnpkg.com/@0xproject/utils/-/utils-0.7.3.tgz#ffa7c6da9bf0dd3e13694f185dcfc48a8981ff05" - dependencies: - "@0xproject/typescript-typings" "^0.4.3" - "@types/node" "9.6.0" - bignumber.js "~4.1.0" - ethereum-types "^0.0.2" - ethereumjs-util "^5.1.1" - ethers "3.0.22" - js-sha3 "0.7.0" - lodash "4.17.10" - web3 "0.20.6" - -"@0xproject/utils@^1.0.10", "@0xproject/utils@^1.0.11", "@0xproject/utils@^1.0.4", "@0xproject/utils@^1.0.8": - version "1.0.11" - resolved "https://registry.yarnpkg.com/@0xproject/utils/-/utils-1.0.11.tgz#5b53e7d9d4dbe68e219049218c9db04e97c37429" - dependencies: - "@0xproject/types" "^1.1.1" - "@0xproject/typescript-typings" "^2.0.2" - "@types/node" "*" - abortcontroller-polyfill "^1.1.9" - bignumber.js "~4.1.0" - detect-node "2.0.3" - ethereum-types "^1.0.8" - ethereumjs-util "^5.1.1" - ethers "3.0.22" - isomorphic-fetch "^2.2.1" - js-sha3 "^0.7.0" - lodash "^4.17.5" - -"@0xproject/utils@^2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@0xproject/utils/-/utils-2.0.2.tgz#5a0cba11ea8378378a600ed7b25c82834038c932" - dependencies: - "@0xproject/types" "^1.1.4" - "@0xproject/typescript-typings" "^3.0.2" - "@types/node" "*" - abortcontroller-polyfill "^1.1.9" - bignumber.js "~4.1.0" - detect-node "2.0.3" - ethereum-types "^1.0.11" - ethereumjs-util "^5.1.1" - ethers "4.0.0-beta.14" - isomorphic-fetch "^2.2.1" - js-sha3 "^0.7.0" - lodash "^4.17.5" - -"@0xproject/web3-wrapper@^3.0.0", "@0xproject/web3-wrapper@^3.0.1", "@0xproject/web3-wrapper@^3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@0xproject/web3-wrapper/-/web3-wrapper-3.0.3.tgz#2faaf3fa75308480efbbbb75416a1e416596d989" +"@0xproject/npm-cli-login@^0.0.11": + version "0.0.11" + resolved "https://registry.yarnpkg.com/@0xproject/npm-cli-login/-/npm-cli-login-0.0.11.tgz#3f1ec06112ce62aad300ff0575358f68aeecde2e" dependencies: - "@0xproject/assert" "^1.0.13" - "@0xproject/json-schemas" "^1.0.7" - "@0xproject/typescript-typings" "^3.0.2" - "@0xproject/utils" "^2.0.2" - ethereum-types "^1.0.11" - ethereumjs-util "^5.1.1" - ethers "4.0.0-beta.14" - lodash "^4.17.5" + npm-registry-client "7.0.9" "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.0.0-beta.35": version "7.0.0" @@ -831,13 +604,6 @@ "@ledgerhq/hw-transport" "^4.24.0" u2f-api "0.2.7" -"@ledgerhq/hw-transport-u2f@^4.3.0": - version "4.31.0" - resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-u2f/-/hw-transport-u2f-4.31.0.tgz#b3c97e7f8e42a3b43372853f33a713cb5c49efdc" - dependencies: - "@ledgerhq/hw-transport" "^4.24.0" - u2f-api "0.2.7" - "@ledgerhq/hw-transport@^4.24.0": version "4.24.0" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-4.24.0.tgz#8def925d8c2e1f73d15128d9e27ead729870be58" @@ -1719,10 +1485,6 @@ version "9.6.5" resolved "https://registry.yarnpkg.com/@types/node/-/node-9.6.5.tgz#ee700810fdf49ac1c399fc5980b7559b3e5a381d" -"@types/node@9.6.0": - version "9.6.0" - resolved "http://registry.npmjs.org/@types/node/-/node-9.6.0.tgz#d3480ee666df9784b1001a1872a2f6ccefb6c2d7" - "@types/node@^10.3.2": version "10.9.4" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.9.4.tgz#0f4cb2dc7c1de6096055357f70179043c33e9897" @@ -1731,10 +1493,6 @@ version "10.5.7" resolved "https://registry.npmjs.org/@types/node/-/node-10.5.7.tgz#960d9feb3ade2233bcc9843c918d740b4f78a7cf" -"@types/node@^8.0.53": - version "8.10.36" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.36.tgz#eac05d576fbcd0b4ea3c912dc58c20475c08d9e4" - "@types/numeral@^0.0.22": version "0.0.22" resolved "https://registry.yarnpkg.com/@types/numeral/-/numeral-0.0.22.tgz#86bef1f0a2d743afdc2ef3168d45f2905e1a0b93" @@ -6158,13 +5916,6 @@ ethereum-common@^0.0.18: version "0.0.18" resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.0.18.tgz#2fdc3576f232903358976eb39da783213ff9523f" -ethereum-types@^0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/ethereum-types/-/ethereum-types-0.0.2.tgz#6ef6faf46a24697cbf66b6c8a0ecf2095ce58c38" - dependencies: - "@types/node" "^8.0.53" - bignumber.js "~4.1.0" - ethereumjs-abi@0.6.5, ethereumjs-abi@^0.6.5: version "0.6.5" resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.5.tgz#5a637ef16ab43473fa72a29ad90871405b3f5241" @@ -6206,14 +5957,6 @@ ethereumjs-block@~1.2.2: ethereumjs-util "^4.0.1" merkle-patricia-tree "^2.1.2" -ethereumjs-blockstream@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ethereumjs-blockstream/-/ethereumjs-blockstream-5.0.0.tgz#63bfe9185757329a32822d5815562eb1dcd75d71" - dependencies: - immutable "3.8.2" - source-map-support "0.5.6" - uuid "3.2.1" - ethereumjs-blockstream@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/ethereumjs-blockstream/-/ethereumjs-blockstream-6.0.0.tgz#79d726d1f358935eb65195e91d40344c31e87eff" @@ -6314,36 +6057,6 @@ ethereumjs-wallet@0.6.0: utf8 "^2.1.1" uuid "^2.0.1" -ethers@3.0.22: - version "3.0.22" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-3.0.22.tgz#7fab1ea16521705837aa43c15831877b2716b436" - dependencies: - aes-js "3.0.0" - bn.js "^4.4.0" - elliptic "6.3.3" - hash.js "^1.0.0" - inherits "2.0.1" - js-sha3 "0.5.7" - scrypt-js "2.0.3" - setimmediate "1.0.4" - uuid "2.0.1" - xmlhttprequest "1.8.0" - -ethers@4.0.0-beta.14: - version "4.0.0-beta.14" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.0-beta.14.tgz#76aa9257b9c93a7604ff4dc11f2a445d07f6459d" - dependencies: - "@types/node" "^10.3.2" - aes-js "3.0.0" - bn.js "^4.4.0" - elliptic "6.3.3" - hash.js "1.1.3" - js-sha3 "0.5.7" - scrypt-js "2.0.3" - setimmediate "1.0.4" - uuid "2.0.1" - xmlhttprequest "1.8.0" - ethers@~4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.4.tgz#d3f85e8b27f4b59537e06526439b0fb15b44dc65" @@ -9269,14 +8982,14 @@ js-sha3@0.5.7: version "0.5.7" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" -js-sha3@0.7.0, js-sha3@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.7.0.tgz#0a5c57b36f79882573b2d84051f8bb85dd1bd63a" - js-sha3@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.3.1.tgz#86122802142f0828502a0d1dee1d95e253bb0243" +js-sha3@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.7.0.tgz#0a5c57b36f79882573b2d84051f8bb85dd1bd63a" + js-tokens@^3.0.0, js-tokens@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" @@ -9460,10 +9173,6 @@ jsonschema@*, jsonschema@1.2.4, jsonschema@^1.2.0: version "1.2.4" resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.2.4.tgz#a46bac5d3506a254465bc548876e267c6d0d6464" -jsonschema@1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.2.2.tgz#83ab9c63d65bf4d596f91d81195e78772f6452bc" - jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -10154,7 +9863,7 @@ lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" -lodash.values@4.3.0, lodash.values@^4.3.0: +lodash.values@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.values/-/lodash.values-4.3.0.tgz#a3a6c2b0ebecc5c2cba1c17e6e620fe81b53d347" @@ -10164,10 +9873,6 @@ lodash.words@^3.0.0: dependencies: lodash._root "^3.0.0" -lodash@4.17.10, lodash@^4.17.10: - version "4.17.10" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" - lodash@=4.17.4: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -10184,6 +9889,10 @@ lodash@^4.14.0, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1, lodash@^4.3.0, lo version "4.17.5" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" +lodash@^4.17.10: + version "4.17.10" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" + lodash@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/lodash/-/lodash-1.0.2.tgz#8f57560c83b59fc270bd3d561b690043430e2551" @@ -13950,10 +13659,6 @@ scrypt-async@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/scrypt-async/-/scrypt-async-1.3.1.tgz#a11fd6fac981b4b823ee01dee0221169500ddae9" -scrypt-js@2.0.3: - version "2.0.3" - resolved "http://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.3.tgz#bb0040be03043da9a012a2cea9fc9f852cfc87d4" - scrypt-js@2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.4.tgz#32f8c5149f0797672e551c07e230f834b6af5f16" @@ -16184,7 +15889,7 @@ v8flags@^2.0.2: dependencies: user-home "^1.1.1" -valid-url@1.0.9, valid-url@^1.0.9: +valid-url@^1.0.9: version "1.0.9" resolved "https://registry.yarnpkg.com/valid-url/-/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200" @@ -16631,16 +16336,6 @@ web3@0.20.2: xhr2 "*" xmlhttprequest "*" -web3@0.20.6: - version "0.20.6" - resolved "http://registry.npmjs.org/web3/-/web3-0.20.6.tgz#3e97306ae024fb24e10a3d75c884302562215120" - dependencies: - bignumber.js "git+https://github.com/frozeman/bignumber.js-nolookahead.git" - crypto-js "^3.1.4" - utf8 "^2.1.1" - xhr2 "*" - xmlhttprequest "*" - web3@^0.18.0: version "0.18.4" resolved "https://registry.yarnpkg.com/web3/-/web3-0.18.4.tgz#81ec1784145491f2eaa8955b31c06049e07c5e7d" -- cgit v1.2.3 From bbe1a843ef44e518fc957ea36325d5a730502de5 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Thu, 18 Oct 2018 13:44:09 -0700 Subject: Update pipeline to use new @0x npm org for imports --- .../pipeline/src/data_sources/contract-wrappers/exchange_events.ts | 6 +++--- packages/pipeline/src/index.ts | 4 ++-- packages/pipeline/src/parsers/events/index.ts | 6 +++--- packages/pipeline/src/parsers/sra_orders/index.ts | 6 +++--- packages/pipeline/src/utils/index.ts | 2 +- packages/pipeline/test/parsers/events/index_test.ts | 4 ++-- packages/pipeline/test/parsers/sra_orders/index_test.ts | 4 ++-- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts b/packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts index 77217c601..46f115705 100644 --- a/packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts +++ b/packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts @@ -1,6 +1,6 @@ -import { ContractWrappers, ExchangeEvents, ExchangeFillEventArgs, ExchangeWrapper } from '@0xproject/contract-wrappers'; -import { Web3ProviderEngine } from '@0xproject/subproviders'; -import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { ContractWrappers, ExchangeEvents, ExchangeFillEventArgs, ExchangeWrapper } from '@0x/contract-wrappers'; +import { Web3ProviderEngine } from '@0x/subproviders'; +import { Web3Wrapper } from '@0x/web3-wrapper'; import { LogWithDecodedArgs } from 'ethereum-types'; const BLOCK_FINALITY_THRESHOLD = 10; // When to consider blocks as final. Used to compute default toBlock. diff --git a/packages/pipeline/src/index.ts b/packages/pipeline/src/index.ts index 77c92cc34..d4bca65c5 100644 --- a/packages/pipeline/src/index.ts +++ b/packages/pipeline/src/index.ts @@ -1,5 +1,5 @@ -import { HttpClient } from '@0xproject/connect'; -import { web3Factory } from '@0xproject/dev-utils'; +import { HttpClient } from '@0x/connect'; +import { web3Factory } from '@0x/dev-utils'; import 'reflect-metadata'; import { Connection, createConnection } from 'typeorm'; diff --git a/packages/pipeline/src/parsers/events/index.ts b/packages/pipeline/src/parsers/events/index.ts index 66f382dda..b9b4d02cb 100644 --- a/packages/pipeline/src/parsers/events/index.ts +++ b/packages/pipeline/src/parsers/events/index.ts @@ -3,9 +3,9 @@ import { ExchangeCancelUpToEventArgs, ExchangeEventArgs, ExchangeFillEventArgs, -} from '@0xproject/contract-wrappers'; -import { assetDataUtils } from '@0xproject/order-utils'; -import { AssetProxyId, ERC721AssetData } from '@0xproject/types'; +} from '@0x/contract-wrappers'; +import { assetDataUtils } from '@0x/order-utils'; +import { AssetProxyId, ERC721AssetData } from '@0x/types'; import { LogWithDecodedArgs } from 'ethereum-types'; import * as R from 'ramda'; diff --git a/packages/pipeline/src/parsers/sra_orders/index.ts b/packages/pipeline/src/parsers/sra_orders/index.ts index fb2b74dfe..39c222afd 100644 --- a/packages/pipeline/src/parsers/sra_orders/index.ts +++ b/packages/pipeline/src/parsers/sra_orders/index.ts @@ -1,6 +1,6 @@ -import { APIOrder, OrdersResponse } from '@0xproject/connect'; -import { assetDataUtils, orderHashUtils } from '@0xproject/order-utils'; -import { AssetProxyId, ERC721AssetData } from '@0xproject/types'; +import { APIOrder, OrdersResponse } from '@0x/connect'; +import { assetDataUtils, orderHashUtils } from '@0x/order-utils'; +import { AssetProxyId, ERC721AssetData } from '@0x/types'; import * as R from 'ramda'; import { SraOrder } from '../../entities/SraOrder'; diff --git a/packages/pipeline/src/utils/index.ts b/packages/pipeline/src/utils/index.ts index 8fe7f9685..ebc1f8e0f 100644 --- a/packages/pipeline/src/utils/index.ts +++ b/packages/pipeline/src/utils/index.ts @@ -1,4 +1,4 @@ -import { BigNumber } from '@0xproject/utils'; +import { BigNumber } from '@0x/utils'; export function bigNumbertoStringOrNull(n: BigNumber): string | null { if (n == null) { diff --git a/packages/pipeline/test/parsers/events/index_test.ts b/packages/pipeline/test/parsers/events/index_test.ts index 2a2db1a94..0730e4013 100644 --- a/packages/pipeline/test/parsers/events/index_test.ts +++ b/packages/pipeline/test/parsers/events/index_test.ts @@ -1,5 +1,5 @@ -import { ExchangeFillEventArgs } from '@0xproject/contract-wrappers'; -import { BigNumber } from '@0xproject/utils'; +import { ExchangeFillEventArgs } from '@0x/contract-wrappers'; +import { BigNumber } from '@0x/utils'; import * as chai from 'chai'; import { LogWithDecodedArgs } from 'ethereum-types'; import 'mocha'; diff --git a/packages/pipeline/test/parsers/sra_orders/index_test.ts b/packages/pipeline/test/parsers/sra_orders/index_test.ts index 952a6f3c6..eb181ece5 100644 --- a/packages/pipeline/test/parsers/sra_orders/index_test.ts +++ b/packages/pipeline/test/parsers/sra_orders/index_test.ts @@ -1,5 +1,5 @@ -import { APIOrder } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; +import { APIOrder } from '@0x/types'; +import { BigNumber } from '@0x/utils'; import * as chai from 'chai'; import 'mocha'; -- cgit v1.2.3 From 3a19faa5ff8a66395c5eb8d1400338cefaa21187 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Thu, 18 Oct 2018 17:44:48 -0700 Subject: Update script to work with existing v1 pipeline data --- packages/pipeline/package.json | 7 +- .../contract-wrappers/exchange_events.ts | 5 +- .../pipeline/src/entities/ExchangeCancelEvent.ts | 5 +- .../src/entities/ExchangeCancelUpToEvent.ts | 5 +- .../pipeline/src/entities/ExchangeFillEvent.ts | 7 +- packages/pipeline/src/entities/SraOrder.ts | 4 +- packages/pipeline/src/index.ts | 76 +++++++++++++++------- packages/pipeline/src/ormconfig.ts | 15 ++++- packages/pipeline/src/parsers/events/index.ts | 3 +- .../pipeline/test/parsers/events/index_test.ts | 3 +- yarn.lock | 65 ++++++++++++++++++ 11 files changed, 155 insertions(+), 40 deletions(-) diff --git a/packages/pipeline/package.json b/packages/pipeline/package.json index 0071fab2c..47033bf33 100644 --- a/packages/pipeline/package.json +++ b/packages/pipeline/package.json @@ -25,8 +25,8 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/ramda": "^0.25.38", "@0x/tslint-config": "^1.0.9", + "@types/ramda": "^0.25.38", "chai": "^4.1.2", "chai-as-promised": "^7.1.1", "chai-bignumber": "^2.0.2", @@ -36,10 +36,10 @@ "typescript": "3.0.1" }, "dependencies": { - "@0x/dev-utils": "^1.0.13", - "@0x/contract-artifacts": "^1.0.1", "@0x/connect": "^3.0.2", + "@0x/contract-artifacts": "^1.0.1", "@0x/contract-wrappers": "^3.0.0", + "@0x/dev-utils": "^1.0.13", "@0x/order-utils": "^2.0.0", "@0x/subproviders": "^2.1.0", "@0x/types": "^1.2.0", @@ -47,6 +47,7 @@ "@0x/web3-wrapper": "^3.1.0", "axios": "^0.18.0", "ethereum-types": "^1.0.6", + "pg": "^7.5.0", "ramda": "^0.25.0", "reflect-metadata": "^0.1.12", "sqlite3": "^4.0.2", diff --git a/packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts b/packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts index 46f115705..5ce8381cd 100644 --- a/packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts +++ b/packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts @@ -29,7 +29,10 @@ export class ExchangeEventsSource { let events: Array> = []; for (let currFromBlock = fromBlock; currFromBlock <= calculatedToBlock; currFromBlock += NUM_BLOCKS_PER_QUERY) { events = events.concat( - await this._getFillEventsForRangeAsync(currFromBlock, currFromBlock + NUM_BLOCKS_PER_QUERY - 1), + await this._getFillEventsForRangeAsync( + currFromBlock, + Math.min(currFromBlock + NUM_BLOCKS_PER_QUERY - 1, calculatedToBlock), + ), ); } return events; diff --git a/packages/pipeline/src/entities/ExchangeCancelEvent.ts b/packages/pipeline/src/entities/ExchangeCancelEvent.ts index 7010ab9f2..698b9e2ec 100644 --- a/packages/pipeline/src/entities/ExchangeCancelEvent.ts +++ b/packages/pipeline/src/entities/ExchangeCancelEvent.ts @@ -1,15 +1,16 @@ -import { BaseEntity, Column, Entity, PrimaryColumn } from 'typeorm'; +import { Column, Entity, PrimaryColumn } from 'typeorm'; import { AssetType } from '../types'; @Entity() -export class ExchangeCancelEvent extends BaseEntity { +export class ExchangeCancelEvent { @PrimaryColumn() public contractAddress!: string; @PrimaryColumn() public logIndex!: number; @PrimaryColumn() public blockNumber!: number; @Column() public rawData!: string; + // TODO(albrow): Include transaction hash @Column() public makerAddress!: string; @Column({ nullable: true, type: String }) public takerAddress!: string; diff --git a/packages/pipeline/src/entities/ExchangeCancelUpToEvent.ts b/packages/pipeline/src/entities/ExchangeCancelUpToEvent.ts index 03a02b069..3ca75ccf7 100644 --- a/packages/pipeline/src/entities/ExchangeCancelUpToEvent.ts +++ b/packages/pipeline/src/entities/ExchangeCancelUpToEvent.ts @@ -1,11 +1,12 @@ -import { BaseEntity, Column, Entity, PrimaryColumn } from 'typeorm'; +import { Column, Entity, PrimaryColumn } from 'typeorm'; @Entity() -export class ExchangeCancelUpToEvent extends BaseEntity { +export class ExchangeCancelUpToEvent { @PrimaryColumn() public contractAddress!: string; @PrimaryColumn() public logIndex!: number; @PrimaryColumn() public blockNumber!: number; + // TODO(albrow): Include transaction hash @Column() public rawData!: string; @Column() public makerAddress!: string; diff --git a/packages/pipeline/src/entities/ExchangeFillEvent.ts b/packages/pipeline/src/entities/ExchangeFillEvent.ts index 5eafa7449..6e549af93 100644 --- a/packages/pipeline/src/entities/ExchangeFillEvent.ts +++ b/packages/pipeline/src/entities/ExchangeFillEvent.ts @@ -1,18 +1,19 @@ -import { BaseEntity, Column, Entity, PrimaryColumn } from 'typeorm'; +import { Column, Entity, PrimaryColumn } from 'typeorm'; import { AssetType } from '../types'; @Entity() -export class ExchangeFillEvent extends BaseEntity { +export class ExchangeFillEvent { @PrimaryColumn() public contractAddress!: string; @PrimaryColumn() public logIndex!: number; @PrimaryColumn() public blockNumber!: number; @Column() public rawData!: string; + @Column() public transactionHash!: string; @Column() public makerAddress!: string; @Column() public takerAddress!: string; - @Column() public feeRecepientAddress!: string; + @Column() public feeRecipientAddress!: string; @Column() public senderAddress!: string; @Column() public makerAssetFilledAmount!: string; @Column() public takerAssetFilledAmount!: string; diff --git a/packages/pipeline/src/entities/SraOrder.ts b/packages/pipeline/src/entities/SraOrder.ts index e4987df57..a22f7c4e5 100644 --- a/packages/pipeline/src/entities/SraOrder.ts +++ b/packages/pipeline/src/entities/SraOrder.ts @@ -1,9 +1,9 @@ -import { BaseEntity, Column, Entity, PrimaryColumn } from 'typeorm'; +import { Column, Entity, PrimaryColumn } from 'typeorm'; import { AssetType } from '../types'; @Entity() -export class SraOrder extends BaseEntity { +export class SraOrder { @PrimaryColumn() public exchangeAddress!: string; @PrimaryColumn() public orderHashHex!: string; diff --git a/packages/pipeline/src/index.ts b/packages/pipeline/src/index.ts index d4bca65c5..58646fc56 100644 --- a/packages/pipeline/src/index.ts +++ b/packages/pipeline/src/index.ts @@ -1,50 +1,78 @@ -import { HttpClient } from '@0x/connect'; import { web3Factory } from '@0x/dev-utils'; import 'reflect-metadata'; import { Connection, createConnection } from 'typeorm'; import { ExchangeEventsSource } from './data_sources/contract-wrappers/exchange_events'; -import { SraOrder } from './entities/SraOrder'; -import { config } from './ormconfig'; +import { deployConfig } from './ormconfig'; import { parseExchangeEvents } from './parsers/events'; -import { parseSraOrders } from './parsers/sra_orders'; +import { ExchangeFillEvent } from './entities/ExchangeFillEvent'; let connection: Connection; (async () => { - connection = await createConnection(config); + connection = await createConnection(deployConfig); await getExchangeEventsAsync(); - // await getSraOrdersAsync(); + await mergeExchangeEventsAsync(); + console.log('Exiting process'); + process.exit(0); })(); // TODO(albrow): Separately: Errors do not appear to be handled correctly. If you use the // wrong rpcUrl it just returns early with no error. async function getExchangeEventsAsync(): Promise { + console.log('Getting event logs...'); const provider = web3Factory.getRpcProvider({ rpcUrl: 'https://mainnet.infura.io', }); + const eventsRepository = connection.getRepository(ExchangeFillEvent); const exchangeEvents = new ExchangeEventsSource(provider, 1); const eventLogs = await exchangeEvents.getFillEventsAsync(); + console.log('Parsing events...'); const events = parseExchangeEvents(eventLogs); - console.log('Got events: ' + events.length); - for (const event of events) { - await event.save(); - } + console.log(`Retrieved and parsed ${events.length} total events.`); + console.log('Saving events...'); + eventsRepository.save(events); console.log('Saved events.'); - console.log('Exiting process'); - process.exit(0); } -async function getSraOrdersAsync(): Promise { - const orderRepository = connection.getRepository(SraOrder); - console.log(`found ${await orderRepository.count()} existing orders`); - const sraUrl = 'https://api.radarrelay.com/0x/v2'; - const connect = new HttpClient(sraUrl); - const rawOrders = await connect.getOrdersAsync(); - const orders = parseSraOrders(rawOrders); - for (const order of orders) { - order.sourceUrl = sraUrl; - await order.save(); - } - console.log(`now there are ${await orderRepository.count()} total orders`); +const insertEventsRawQuery = `INSERT INTO events_raw ( + event_type, + error_id, + order_hash, + maker, + maker_amount, + maker_fee, + maker_token, + taker, + taker_amount, + taker_fee, + taker_token, + txn_hash, + fee_recipient, + block_number, + log_index +) +( + SELECT + 'LogFill', + null, + "orderHash", + "makerAddress", + "makerAssetFilledAmount"::numeric(78), + "makerFeePaid"::numeric(78), + "makerTokenAddress", + "takerAddress", + "takerAssetFilledAmount"::numeric(78), + "takerFeePaid"::numeric(78), + "takerTokenAddress", + "transactionHash", + "feeRecipientAddress", + "blockNumber", + "logIndex" + FROM exchange_fill_event +) ON CONFLICT (order_hash, txn_hash, log_index) DO NOTHING`; + +async function mergeExchangeEventsAsync(): Promise { + console.log('Merging results into events_raw...'); + await connection.query(insertEventsRawQuery); } diff --git a/packages/pipeline/src/ormconfig.ts b/packages/pipeline/src/ormconfig.ts index 48e316078..eaafeb7d5 100644 --- a/packages/pipeline/src/ormconfig.ts +++ b/packages/pipeline/src/ormconfig.ts @@ -1,6 +1,6 @@ import { ConnectionOptions } from 'typeorm'; -export const config: ConnectionOptions = { +export const testConfig: ConnectionOptions = { type: 'sqlite', database: 'database.sqlite', synchronize: true, @@ -12,3 +12,16 @@ export const config: ConnectionOptions = { migrationsDir: 'lib/src/migrations', }, }; + +export const deployConfig: ConnectionOptions = { + type: 'postgres', + url: process.env.ZEROEX_DATA_PIPELINE_DB_URL, + synchronize: true, + logging: false, + entities: ['./lib/src/entities/**/*.js'], + migrations: ['./lib/src/migrations/**/*.js'], + cli: { + entitiesDir: 'lib/src/entities', + migrationsDir: 'lib/src/migrations', + }, +}; diff --git a/packages/pipeline/src/parsers/events/index.ts b/packages/pipeline/src/parsers/events/index.ts index b9b4d02cb..abfb7ddb6 100644 --- a/packages/pipeline/src/parsers/events/index.ts +++ b/packages/pipeline/src/parsers/events/index.ts @@ -43,9 +43,10 @@ export function _convertToExchangeFillEvent(eventLog: LogWithDecodedArgs { expected.logIndex = 102; expected.rawData = '0x000000000000000000000000f6da68519f78b0d0bc93c701e86affcb75c92428000000000000000000000000f6da68519f78b0d0bc93c701e86affcb75c92428000000000000000000000000000000000000000000000000002386f26fc10000000000000000000000000000000000000000000000000000016345785d8a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f49800000000000000000000000000000000000000000000000000000000'; + expected.transactionHash = '0x6dd106d002873746072fc5e496dd0fb2541b68c77bcf9184ae19a42fd33657fe'; expected.makerAddress = '0xf6da68519f78b0d0bc93c701e86affcb75c92428'; expected.takerAddress = '0xf6da68519f78b0d0bc93c701e86affcb75c92428'; - expected.feeRecepientAddress = '0xc370d2a5920344aa6b7d8d11250e3e861434cbdd'; + expected.feeRecipientAddress = '0xc370d2a5920344aa6b7d8d11250e3e861434cbdd'; expected.senderAddress = '0xf6da68519f78b0d0bc93c701e86affcb75c92428'; expected.makerAssetFilledAmount = '10000000000000000'; expected.takerAssetFilledAmount = '100000000000000000'; diff --git a/yarn.lock b/yarn.lock index c3546946c..195c31a55 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3475,6 +3475,10 @@ buffer-to-arraybuffer@^0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz#6064a40fa76eb43c723aba9ef8f6e1216d10511a" +buffer-writer@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-1.0.1.tgz#22a936901e3029afcd7547eb4487ceb697a3bf08" + buffer-xor@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" @@ -11471,6 +11475,10 @@ package-json@^4.0.0, package-json@^4.0.1: registry-url "^3.0.3" semver "^5.1.0" +packet-reader@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/packet-reader/-/packet-reader-0.3.1.tgz#cd62e60af8d7fea8a705ec4ff990871c46871f27" + pacote@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/pacote/-/pacote-9.1.0.tgz#59810859bbd72984dcb267269259375d32f391e5" @@ -11736,6 +11744,41 @@ performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" +pg-connection-string@0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-0.1.3.tgz#da1847b20940e42ee1492beaf65d49d91b245df7" + +pg-pool@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-2.0.3.tgz#c022032c8949f312a4f91fb6409ce04076be3257" + +pg-types@~1.12.1: + version "1.12.1" + resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-1.12.1.tgz#d64087e3903b58ffaad279e7595c52208a14c3d2" + dependencies: + postgres-array "~1.0.0" + postgres-bytea "~1.0.0" + postgres-date "~1.0.0" + postgres-interval "^1.1.0" + +pg@^7.5.0: + version "7.5.0" + resolved "https://registry.yarnpkg.com/pg/-/pg-7.5.0.tgz#c2853bef2fcb91424ba2f649fd951ce866a84760" + dependencies: + buffer-writer "1.0.1" + packet-reader "0.3.1" + pg-connection-string "0.1.3" + pg-pool "~2.0.3" + pg-types "~1.12.1" + pgpass "1.x" + semver "4.3.2" + +pgpass@1.x: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pgpass/-/pgpass-1.0.2.tgz#2a7bb41b6065b67907e91da1b07c1847c877b306" + dependencies: + split "^1.0.0" + pify@^2.0.0, pify@^2.2.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -12064,6 +12107,24 @@ postcss@^6.0.1: source-map "^0.6.1" supports-color "^5.3.0" +postgres-array@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-1.0.3.tgz#c561fc3b266b21451fc6555384f4986d78ec80f5" + +postgres-bytea@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35" + +postgres-date@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.3.tgz#e2d89702efdb258ff9d9cee0fe91bd06975257a8" + +postgres-interval@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-1.1.2.tgz#bf71ff902635f21cb241a013fc421d81d1db15a9" + dependencies: + xtend "^4.0.0" + prebuild-install@^2.2.2: version "2.5.3" resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-2.5.3.tgz#9f65f242782d370296353710e9bc843490c19f69" @@ -13756,6 +13817,10 @@ semver-sort@0.0.4: version "5.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" +semver@4.3.2: + version "4.3.2" + resolved "http://registry.npmjs.org/semver/-/semver-4.3.2.tgz#c7a07158a80bedd052355b770d82d6640f803be7" + semver@^4.1.0: version "4.3.6" resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" -- cgit v1.2.3 From af2546bc58e49b2f99bcd2c1b81ceebed6f7b6d9 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Thu, 18 Oct 2018 17:48:52 -0700 Subject: Rename index.ts -> merge_v2_events --- packages/pipeline/src/index.ts | 78 -------------------------------- packages/pipeline/src/merge_v2_events.ts | 78 ++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 78 deletions(-) delete mode 100644 packages/pipeline/src/index.ts create mode 100644 packages/pipeline/src/merge_v2_events.ts diff --git a/packages/pipeline/src/index.ts b/packages/pipeline/src/index.ts deleted file mode 100644 index 58646fc56..000000000 --- a/packages/pipeline/src/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { web3Factory } from '@0x/dev-utils'; -import 'reflect-metadata'; -import { Connection, createConnection } from 'typeorm'; - -import { ExchangeEventsSource } from './data_sources/contract-wrappers/exchange_events'; -import { deployConfig } from './ormconfig'; -import { parseExchangeEvents } from './parsers/events'; -import { ExchangeFillEvent } from './entities/ExchangeFillEvent'; - -let connection: Connection; - -(async () => { - connection = await createConnection(deployConfig); - await getExchangeEventsAsync(); - await mergeExchangeEventsAsync(); - console.log('Exiting process'); - process.exit(0); -})(); - -// TODO(albrow): Separately: Errors do not appear to be handled correctly. If you use the -// wrong rpcUrl it just returns early with no error. -async function getExchangeEventsAsync(): Promise { - console.log('Getting event logs...'); - const provider = web3Factory.getRpcProvider({ - rpcUrl: 'https://mainnet.infura.io', - }); - const eventsRepository = connection.getRepository(ExchangeFillEvent); - const exchangeEvents = new ExchangeEventsSource(provider, 1); - const eventLogs = await exchangeEvents.getFillEventsAsync(); - console.log('Parsing events...'); - const events = parseExchangeEvents(eventLogs); - console.log(`Retrieved and parsed ${events.length} total events.`); - console.log('Saving events...'); - eventsRepository.save(events); - console.log('Saved events.'); -} - -const insertEventsRawQuery = `INSERT INTO events_raw ( - event_type, - error_id, - order_hash, - maker, - maker_amount, - maker_fee, - maker_token, - taker, - taker_amount, - taker_fee, - taker_token, - txn_hash, - fee_recipient, - block_number, - log_index -) -( - SELECT - 'LogFill', - null, - "orderHash", - "makerAddress", - "makerAssetFilledAmount"::numeric(78), - "makerFeePaid"::numeric(78), - "makerTokenAddress", - "takerAddress", - "takerAssetFilledAmount"::numeric(78), - "takerFeePaid"::numeric(78), - "takerTokenAddress", - "transactionHash", - "feeRecipientAddress", - "blockNumber", - "logIndex" - FROM exchange_fill_event -) ON CONFLICT (order_hash, txn_hash, log_index) DO NOTHING`; - -async function mergeExchangeEventsAsync(): Promise { - console.log('Merging results into events_raw...'); - await connection.query(insertEventsRawQuery); -} diff --git a/packages/pipeline/src/merge_v2_events.ts b/packages/pipeline/src/merge_v2_events.ts new file mode 100644 index 000000000..58646fc56 --- /dev/null +++ b/packages/pipeline/src/merge_v2_events.ts @@ -0,0 +1,78 @@ +import { web3Factory } from '@0x/dev-utils'; +import 'reflect-metadata'; +import { Connection, createConnection } from 'typeorm'; + +import { ExchangeEventsSource } from './data_sources/contract-wrappers/exchange_events'; +import { deployConfig } from './ormconfig'; +import { parseExchangeEvents } from './parsers/events'; +import { ExchangeFillEvent } from './entities/ExchangeFillEvent'; + +let connection: Connection; + +(async () => { + connection = await createConnection(deployConfig); + await getExchangeEventsAsync(); + await mergeExchangeEventsAsync(); + console.log('Exiting process'); + process.exit(0); +})(); + +// TODO(albrow): Separately: Errors do not appear to be handled correctly. If you use the +// wrong rpcUrl it just returns early with no error. +async function getExchangeEventsAsync(): Promise { + console.log('Getting event logs...'); + const provider = web3Factory.getRpcProvider({ + rpcUrl: 'https://mainnet.infura.io', + }); + const eventsRepository = connection.getRepository(ExchangeFillEvent); + const exchangeEvents = new ExchangeEventsSource(provider, 1); + const eventLogs = await exchangeEvents.getFillEventsAsync(); + console.log('Parsing events...'); + const events = parseExchangeEvents(eventLogs); + console.log(`Retrieved and parsed ${events.length} total events.`); + console.log('Saving events...'); + eventsRepository.save(events); + console.log('Saved events.'); +} + +const insertEventsRawQuery = `INSERT INTO events_raw ( + event_type, + error_id, + order_hash, + maker, + maker_amount, + maker_fee, + maker_token, + taker, + taker_amount, + taker_fee, + taker_token, + txn_hash, + fee_recipient, + block_number, + log_index +) +( + SELECT + 'LogFill', + null, + "orderHash", + "makerAddress", + "makerAssetFilledAmount"::numeric(78), + "makerFeePaid"::numeric(78), + "makerTokenAddress", + "takerAddress", + "takerAssetFilledAmount"::numeric(78), + "takerFeePaid"::numeric(78), + "takerTokenAddress", + "transactionHash", + "feeRecipientAddress", + "blockNumber", + "logIndex" + FROM exchange_fill_event +) ON CONFLICT (order_hash, txn_hash, log_index) DO NOTHING`; + +async function mergeExchangeEventsAsync(): Promise { + console.log('Merging results into events_raw...'); + await connection.query(insertEventsRawQuery); +} -- cgit v1.2.3 From 7945d2ea62f3a2bca363f9a37b471e02531490c7 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Fri, 19 Oct 2018 13:50:48 -0700 Subject: Enable verbose logging --- packages/pipeline/src/ormconfig.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pipeline/src/ormconfig.ts b/packages/pipeline/src/ormconfig.ts index eaafeb7d5..6201d4f37 100644 --- a/packages/pipeline/src/ormconfig.ts +++ b/packages/pipeline/src/ormconfig.ts @@ -17,7 +17,7 @@ export const deployConfig: ConnectionOptions = { type: 'postgres', url: process.env.ZEROEX_DATA_PIPELINE_DB_URL, synchronize: true, - logging: false, + logging: true, entities: ['./lib/src/entities/**/*.js'], migrations: ['./lib/src/migrations/**/*.js'], cli: { -- cgit v1.2.3 From aeff948c9a89b07117a714cc7af1c1a3c810f0b7 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Fri, 19 Oct 2018 14:05:35 -0700 Subject: Fix bugs in merge_v2_events script and disable verbose logging --- packages/pipeline/src/merge_v2_events.ts | 5 ++++- packages/pipeline/src/ormconfig.ts | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/pipeline/src/merge_v2_events.ts b/packages/pipeline/src/merge_v2_events.ts index 58646fc56..71d5e1806 100644 --- a/packages/pipeline/src/merge_v2_events.ts +++ b/packages/pipeline/src/merge_v2_events.ts @@ -31,7 +31,10 @@ async function getExchangeEventsAsync(): Promise { const events = parseExchangeEvents(eventLogs); console.log(`Retrieved and parsed ${events.length} total events.`); console.log('Saving events...'); - eventsRepository.save(events); + for (const event of events) { + await eventsRepository.save(event); + } + await eventsRepository.save(events); console.log('Saved events.'); } diff --git a/packages/pipeline/src/ormconfig.ts b/packages/pipeline/src/ormconfig.ts index 6201d4f37..eaafeb7d5 100644 --- a/packages/pipeline/src/ormconfig.ts +++ b/packages/pipeline/src/ormconfig.ts @@ -17,7 +17,7 @@ export const deployConfig: ConnectionOptions = { type: 'postgres', url: process.env.ZEROEX_DATA_PIPELINE_DB_URL, synchronize: true, - logging: true, + logging: false, entities: ['./lib/src/entities/**/*.js'], migrations: ['./lib/src/migrations/**/*.js'], cli: { -- cgit v1.2.3 From d959b3e23402840661f45a2b5ecd5ca64dcdabf3 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Mon, 22 Oct 2018 16:09:35 -0700 Subject: chore: Fix tslint config. --- packages/pipeline/src/merge_v2_events.ts | 2 +- packages/pipeline/tslint.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/pipeline/src/merge_v2_events.ts b/packages/pipeline/src/merge_v2_events.ts index 71d5e1806..24439423f 100644 --- a/packages/pipeline/src/merge_v2_events.ts +++ b/packages/pipeline/src/merge_v2_events.ts @@ -3,9 +3,9 @@ import 'reflect-metadata'; import { Connection, createConnection } from 'typeorm'; import { ExchangeEventsSource } from './data_sources/contract-wrappers/exchange_events'; +import { ExchangeFillEvent } from './entities/ExchangeFillEvent'; import { deployConfig } from './ormconfig'; import { parseExchangeEvents } from './parsers/events'; -import { ExchangeFillEvent } from './entities/ExchangeFillEvent'; let connection: Connection; diff --git a/packages/pipeline/tslint.json b/packages/pipeline/tslint.json index ffaefe83a..dd9053357 100644 --- a/packages/pipeline/tslint.json +++ b/packages/pipeline/tslint.json @@ -1,3 +1,3 @@ { - "extends": ["@0xproject/tslint-config"] + "extends": ["@0x/tslint-config"] } -- cgit v1.2.3 From bb440b683a7a4d966694de2b897f51f22dadb31c Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Tue, 23 Oct 2018 16:03:52 -0700 Subject: Implement support for getting and parsing blocks and transactions --- packages/pipeline/src/data_sources/web3/index.ts | 22 ++++++++ packages/pipeline/src/entities/Block.ts | 9 ++++ packages/pipeline/src/entities/Transaction.ts | 11 ++++ packages/pipeline/src/index.ts | 68 ++++++++++++++++++++++++ packages/pipeline/src/parsers/web3/index.ts | 39 ++++++++++++++ 5 files changed, 149 insertions(+) create mode 100644 packages/pipeline/src/data_sources/web3/index.ts create mode 100644 packages/pipeline/src/entities/Block.ts create mode 100644 packages/pipeline/src/entities/Transaction.ts create mode 100644 packages/pipeline/src/index.ts create mode 100644 packages/pipeline/src/parsers/web3/index.ts diff --git a/packages/pipeline/src/data_sources/web3/index.ts b/packages/pipeline/src/data_sources/web3/index.ts new file mode 100644 index 000000000..64e909939 --- /dev/null +++ b/packages/pipeline/src/data_sources/web3/index.ts @@ -0,0 +1,22 @@ +import { Web3ProviderEngine } from '@0x/subproviders'; +import { Web3Wrapper } from '@0x/web3-wrapper'; +import { BlockWithoutTransactionData, Transaction } from 'ethereum-types'; + +export class Web3Source { + private _web3Wrapper: Web3Wrapper; + constructor(provider: Web3ProviderEngine) { + this._web3Wrapper = new Web3Wrapper(provider); + } + + public async getBlockInfoAsync(blockNumber: number): Promise { + const block = await this._web3Wrapper.getBlockIfExistsAsync(blockNumber); + if (block == null) { + return Promise.reject(new Error('Could not find block for given block number: ' + blockNumber)); + } + return block; + } + + public async getTransactionInfoAsync(txHash: string): Promise { + return this._web3Wrapper.getTransactionByHashAsync(txHash); + } +} diff --git a/packages/pipeline/src/entities/Block.ts b/packages/pipeline/src/entities/Block.ts new file mode 100644 index 000000000..49e0ef840 --- /dev/null +++ b/packages/pipeline/src/entities/Block.ts @@ -0,0 +1,9 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +@Entity() +export class Block { + @PrimaryColumn() public hash!: string; + @PrimaryColumn() public number!: number; + + @Column() public unixTimestampSeconds!: number; +} diff --git a/packages/pipeline/src/entities/Transaction.ts b/packages/pipeline/src/entities/Transaction.ts new file mode 100644 index 000000000..d89d44746 --- /dev/null +++ b/packages/pipeline/src/entities/Transaction.ts @@ -0,0 +1,11 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +@Entity() +export class Transaction { + @PrimaryColumn() public transactionHash!: string; + @PrimaryColumn() public blockHash!: string; + @PrimaryColumn() public blockNumber!: number; + + @Column() public gasUsed!: number; + @Column() public gasPrice!: number; +} diff --git a/packages/pipeline/src/index.ts b/packages/pipeline/src/index.ts new file mode 100644 index 000000000..ad0e4c68f --- /dev/null +++ b/packages/pipeline/src/index.ts @@ -0,0 +1,68 @@ +import { web3Factory } from '@0x/dev-utils'; +import { Web3ProviderEngine } from '@0x/subproviders'; +import 'reflect-metadata'; +import { Connection, createConnection } from 'typeorm'; + +import { ExchangeEventsSource } from './data_sources/contract-wrappers/exchange_events'; +import { Web3Source } from './data_sources/web3'; +import { Block } from './entities/Block'; +import { ExchangeFillEvent } from './entities/ExchangeFillEvent'; +import { Transaction } from './entities/Transaction'; +import { testConfig } from './ormconfig'; +import { parseExchangeEvents } from './parsers/events'; +import { parseBlock, parseTransaction } from './parsers/web3'; + +const EXCHANGE_START_BLOCK = 6271590; // Block number when the Exchange contract was deployed to mainnet. + +let connection: Connection; + +(async () => { + connection = await createConnection(testConfig); + const provider = web3Factory.getRpcProvider({ + rpcUrl: 'https://mainnet.infura.io', + }); + await getExchangeEventsAsync(provider); + await getBlockAsync(provider); + await getTransactionAsync(provider); + console.log('Exiting process'); + process.exit(0); +})(); + +async function getExchangeEventsAsync(provider: Web3ProviderEngine): Promise { + console.log('Getting event logs...'); + const eventsRepository = connection.getRepository(ExchangeFillEvent); + const exchangeEvents = new ExchangeEventsSource(provider, 1); + const eventLogs = await exchangeEvents.getFillEventsAsync(EXCHANGE_START_BLOCK, EXCHANGE_START_BLOCK + 100000); + console.log('Parsing events...'); + const events = parseExchangeEvents(eventLogs); + console.log(`Retrieved and parsed ${events.length} total events.`); + console.log('Saving events...'); + for (const event of events) { + await eventsRepository.save(event); + } + console.log('Saved events.'); +} + +async function getBlockAsync(provider: Web3ProviderEngine): Promise { + console.log('Getting block info...'); + const blocksRepository = connection.getRepository(Block); + const web3Source = new Web3Source(provider); + const rawBlock = await web3Source.getBlockInfoAsync(EXCHANGE_START_BLOCK); + const block = parseBlock(rawBlock); + console.log('Saving block info...'); + await blocksRepository.save(block); + console.log('Done saving block.'); +} + +async function getTransactionAsync(provider: Web3ProviderEngine): Promise { + console.log('Getting tx info...'); + const txsRepository = connection.getRepository(Transaction); + const web3Source = new Web3Source(provider); + const rawTx = await web3Source.getTransactionInfoAsync( + '0x6dd106d002873746072fc5e496dd0fb2541b68c77bcf9184ae19a42fd33657fe', + ); + const tx = parseTransaction(rawTx); + console.log('Saving tx info...'); + await txsRepository.save(tx); + console.log('Done saving tx.'); +} diff --git a/packages/pipeline/src/parsers/web3/index.ts b/packages/pipeline/src/parsers/web3/index.ts new file mode 100644 index 000000000..c6647c966 --- /dev/null +++ b/packages/pipeline/src/parsers/web3/index.ts @@ -0,0 +1,39 @@ +import { BlockWithoutTransactionData, Transaction as EthTransaction } from 'ethereum-types'; + +import { Block } from '../../entities/Block'; +import { Transaction } from '../../entities/Transaction'; + +export function parseBlock(rawBlock: BlockWithoutTransactionData): Block { + if (rawBlock.hash == null) { + throw new Error('Tried to parse raw block but hash was null'); + } + if (rawBlock.number == null) { + throw new Error('Tried to parse raw block but number was null'); + } + + const block = new Block(); + block.hash = rawBlock.hash; + block.number = rawBlock.number; + block.unixTimestampSeconds = rawBlock.timestamp; + return block; +} + +export function parseTransaction(rawTransaction: EthTransaction): Transaction { + if (rawTransaction.blockHash == null) { + throw new Error('Tried to parse raw transaction but blockHash was null'); + } + if (rawTransaction.blockNumber == null) { + throw new Error('Tried to parse raw transaction but blockNumber was null'); + } + + const tx = new Transaction(); + tx.transactionHash = rawTransaction.hash; + tx.blockHash = rawTransaction.blockHash; + tx.blockNumber = rawTransaction.blockNumber; + + tx.gasUsed = rawTransaction.gas; + // TODO(albrow) figure out bignum solution. + tx.gasPrice = rawTransaction.gasPrice.toNumber(); + + return tx; +} -- cgit v1.2.3 From 5e22a862b77d4947361342972c28a8552d13018e Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Wed, 24 Oct 2018 18:34:20 -0700 Subject: Implement fetching and parsing relayer info --- packages/pipeline/package.json | 1 + .../src/data_sources/relayer-registry/index.ts | 33 ++++++++++++++++++++++ packages/pipeline/src/entities/Relayer.ts | 22 +++++++++++++++ packages/pipeline/src/index.ts | 29 +++++++++++++++---- .../pipeline/src/parsers/relayer_registry/index.ts | 28 ++++++++++++++++++ yarn.lock | 8 +++++- 6 files changed, 114 insertions(+), 7 deletions(-) create mode 100644 packages/pipeline/src/data_sources/relayer-registry/index.ts create mode 100644 packages/pipeline/src/entities/Relayer.ts create mode 100644 packages/pipeline/src/parsers/relayer_registry/index.ts diff --git a/packages/pipeline/package.json b/packages/pipeline/package.json index 47033bf33..253da3f19 100644 --- a/packages/pipeline/package.json +++ b/packages/pipeline/package.json @@ -27,6 +27,7 @@ "devDependencies": { "@0x/tslint-config": "^1.0.9", "@types/ramda": "^0.25.38", + "@types/axios": "^0.14.0", "chai": "^4.1.2", "chai-as-promised": "^7.1.1", "chai-bignumber": "^2.0.2", diff --git a/packages/pipeline/src/data_sources/relayer-registry/index.ts b/packages/pipeline/src/data_sources/relayer-registry/index.ts new file mode 100644 index 000000000..c97b50d27 --- /dev/null +++ b/packages/pipeline/src/data_sources/relayer-registry/index.ts @@ -0,0 +1,33 @@ +import axios from 'axios'; + +export interface RelayerResponse { + name: string; + homepage_url: string; + app_url: string; + header_img: string; + logo_img: string; + networks: RelayerResponseNetwork[]; +} + +export interface RelayerResponseNetwork { + networkId: number; + sra_http_endpoint?: string; + sra_ws_endpoint?: string; + static_order_fields?: { + fee_recipient_addresses?: string[]; + taker_addresses?: string[]; + }; +} + +export class RelayerRegistrySource { + private _url: string; + + constructor(url: string) { + this._url = url; + } + + public async getRelayerInfoAsync(): Promise { + const resp = await axios.get(this._url); + return resp.data; + } +} diff --git a/packages/pipeline/src/entities/Relayer.ts b/packages/pipeline/src/entities/Relayer.ts new file mode 100644 index 000000000..ebdcbf345 --- /dev/null +++ b/packages/pipeline/src/entities/Relayer.ts @@ -0,0 +1,22 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +@Entity() +export class Relayer { + @PrimaryColumn() public name!: string; + + @Column() public url!: string; + @Column({ nullable: true, type: String }) + public sraHttpEndpoint!: string | null; + @Column({ nullable: true, type: String }) + public sraWsEndpoint!: string | null; + @Column({ nullable: true, type: String }) + public appUrl!: string | null; + + // TODO(albrow): Add exchange contract or protocol version? + // TODO(albrow): Add network ids for addresses? + + @Column({ type: 'varchar', array: true }) + public feeRecipientAddresses!: string[]; + @Column({ type: 'varchar', array: true }) + public takerAddresses!: string[]; +} diff --git a/packages/pipeline/src/index.ts b/packages/pipeline/src/index.ts index ad0e4c68f..9483eb257 100644 --- a/packages/pipeline/src/index.ts +++ b/packages/pipeline/src/index.ts @@ -4,27 +4,31 @@ import 'reflect-metadata'; import { Connection, createConnection } from 'typeorm'; import { ExchangeEventsSource } from './data_sources/contract-wrappers/exchange_events'; +import { RelayerRegistrySource } from './data_sources/relayer-registry'; import { Web3Source } from './data_sources/web3'; import { Block } from './entities/Block'; import { ExchangeFillEvent } from './entities/ExchangeFillEvent'; +import { Relayer } from './entities/Relayer'; import { Transaction } from './entities/Transaction'; -import { testConfig } from './ormconfig'; +import { deployConfig } from './ormconfig'; import { parseExchangeEvents } from './parsers/events'; +import { parseRelayers } from './parsers/relayer_registry'; import { parseBlock, parseTransaction } from './parsers/web3'; const EXCHANGE_START_BLOCK = 6271590; // Block number when the Exchange contract was deployed to mainnet. +const RELAYER_REGISTRY_URL = 'https://raw.githubusercontent.com/0xProject/0x-relayer-registry/master/relayers.json'; let connection: Connection; (async () => { - connection = await createConnection(testConfig); + connection = await createConnection(deployConfig); const provider = web3Factory.getRpcProvider({ rpcUrl: 'https://mainnet.infura.io', }); - await getExchangeEventsAsync(provider); - await getBlockAsync(provider); - await getTransactionAsync(provider); - console.log('Exiting process'); + // await getExchangeEventsAsync(provider); + // await getBlockAsync(provider); + // await getTransactionAsync(provider); + await getRelayers(RELAYER_REGISTRY_URL); process.exit(0); })(); @@ -66,3 +70,16 @@ async function getTransactionAsync(provider: Web3ProviderEngine): Promise await txsRepository.save(tx); console.log('Done saving tx.'); } + +async function getRelayers(url: string): Promise { + console.log('Getting relayer info...'); + const relayerRepository = connection.getRepository(Relayer); + const relayerSource = new RelayerRegistrySource(RELAYER_REGISTRY_URL); + const relayersResp = await relayerSource.getRelayerInfoAsync(); + const relayers = parseRelayers(relayersResp); + console.log('Saving relayer info...'); + // for (const relayer of relayers) { + await relayerRepository.save(relayers); + // } + console.log('Done saving relayers.'); +} diff --git a/packages/pipeline/src/parsers/relayer_registry/index.ts b/packages/pipeline/src/parsers/relayer_registry/index.ts new file mode 100644 index 000000000..6eca10167 --- /dev/null +++ b/packages/pipeline/src/parsers/relayer_registry/index.ts @@ -0,0 +1,28 @@ +import * as R from 'ramda'; + +import { RelayerResponse, RelayerResponseNetwork } from '../../data_sources/relayer-registry'; +import { Relayer } from '../../entities/Relayer'; + +export const parseRelayers = R.map(parseRelayer); + +function parseRelayer(relayerResp: RelayerResponse): Relayer { + const relayer = new Relayer(); + relayer.name = relayerResp.name; + relayer.url = relayerResp.homepage_url; + relayer.appUrl = relayerResp.app_url; + const mainnet = getMainNetwork(relayerResp); + if (mainnet !== undefined) { + relayer.sraHttpEndpoint = mainnet.sra_http_endpoint || null; + relayer.sraWsEndpoint = mainnet.sra_ws_endpoint || null; + relayer.feeRecipientAddresses = R.path(['static_order_fields', 'fee_recipient_addresses'], mainnet) || []; + relayer.takerAddresses = R.path(['static_order_fields', 'taker_addresses'], mainnet) || []; + } else { + relayer.feeRecipientAddresses = []; + relayer.takerAddresses = []; + } + return relayer; +} + +function getMainNetwork(relayerResp: RelayerResponse): RelayerResponseNetwork | undefined { + return R.find(network => network.networkId === 1, relayerResp.networks); +} diff --git a/yarn.lock b/yarn.lock index 195c31a55..b1de13e68 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1231,6 +1231,12 @@ version "0.4.1" resolved "https://registry.yarnpkg.com/@types/accounting/-/accounting-0.4.1.tgz#865d9f5694fd7c438fba34eb4bc82eec6f34cdd5" +"@types/axios@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@types/axios/-/axios-0.14.0.tgz#ec2300fbe7d7dddd7eb9d3abf87999964cafce46" + dependencies: + axios "*" + "@types/bintrees@^1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@types/bintrees/-/bintrees-1.0.2.tgz#0dfdce4eeebdf90427bd35b0e79dc248b3d157a6" @@ -2427,7 +2433,7 @@ aws4@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" -axios@^0.18.0: +axios@*, axios@^0.18.0: version "0.18.0" resolved "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz#32d53e4851efdc0a11993b6cd000789d70c05102" dependencies: -- cgit v1.2.3 From 21d4807206040e945aa4d4b224e20503873f8e50 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Tue, 6 Nov 2018 10:40:07 -0800 Subject: Add build:ci to pipeline package.json --- packages/pipeline/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/pipeline/package.json b/packages/pipeline/package.json index 253da3f19..3380a970a 100644 --- a/packages/pipeline/package.json +++ b/packages/pipeline/package.json @@ -5,6 +5,7 @@ "description": "Data pipeline for offline analysis", "scripts": { "build": "yarn tsc -b", + "build:ci": "yarn build", "test": "yarn run_mocha", "rebuild_and_test": "run-s build test", "test:circleci": "yarn test:coverage", -- cgit v1.2.3 From dca2a4e9c2712f67852bed4ae6ae76c6434f7e56 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Tue, 6 Nov 2018 12:53:59 -0800 Subject: Remove outdated info from README --- packages/pipeline/README.md | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/packages/pipeline/README.md b/packages/pipeline/README.md index 594454bd0..df54de21c 100644 --- a/packages/pipeline/README.md +++ b/packages/pipeline/README.md @@ -8,27 +8,6 @@ We strongly recommend that the community help us make improvements and determine Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. -## Local Dev Setup - -Requires Node version 6.9.5 or higher. - -Add the following to your `.env` file: - -``` -REDSHIFT_USER -REDSHIFT_DB -REDSHIFT_PASSWORD -REDSHIFT_PORT -REDSHIFT_HOST -WEB3_PROVIDER_URL -``` - -Running a script example: - -``` -node ./lib/scripts/scrape_data.js --type tokens -``` - ### Install dependencies: ```bash -- cgit v1.2.3 From 8248fbb231a0c0acd59fd1f265114dd6dc78a253 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Wed, 7 Nov 2018 15:29:34 -0800 Subject: Update relayer code to use new relayer-registry format --- packages/pipeline/src/data_sources/relayer-registry/index.ts | 4 ++-- packages/pipeline/src/entities/Relayer.ts | 3 ++- packages/pipeline/src/index.ts | 5 ++++- packages/pipeline/src/parsers/relayer_registry/index.ts | 8 ++++++-- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/pipeline/src/data_sources/relayer-registry/index.ts b/packages/pipeline/src/data_sources/relayer-registry/index.ts index c97b50d27..8199dae14 100644 --- a/packages/pipeline/src/data_sources/relayer-registry/index.ts +++ b/packages/pipeline/src/data_sources/relayer-registry/index.ts @@ -26,8 +26,8 @@ export class RelayerRegistrySource { this._url = url; } - public async getRelayerInfoAsync(): Promise { - const resp = await axios.get(this._url); + public async getRelayerInfoAsync(): Promise> { + const resp = await axios.get>(this._url); return resp.data; } } diff --git a/packages/pipeline/src/entities/Relayer.ts b/packages/pipeline/src/entities/Relayer.ts index ebdcbf345..605355fa8 100644 --- a/packages/pipeline/src/entities/Relayer.ts +++ b/packages/pipeline/src/entities/Relayer.ts @@ -2,8 +2,9 @@ import { Column, Entity, PrimaryColumn } from 'typeorm'; @Entity() export class Relayer { - @PrimaryColumn() public name!: string; + @PrimaryColumn() public uuid!: string; + @Column() public name!: string; @Column() public url!: string; @Column({ nullable: true, type: String }) public sraHttpEndpoint!: string | null; diff --git a/packages/pipeline/src/index.ts b/packages/pipeline/src/index.ts index 9483eb257..b42256d87 100644 --- a/packages/pipeline/src/index.ts +++ b/packages/pipeline/src/index.ts @@ -16,7 +16,10 @@ import { parseRelayers } from './parsers/relayer_registry'; import { parseBlock, parseTransaction } from './parsers/web3'; const EXCHANGE_START_BLOCK = 6271590; // Block number when the Exchange contract was deployed to mainnet. -const RELAYER_REGISTRY_URL = 'https://raw.githubusercontent.com/0xProject/0x-relayer-registry/master/relayers.json'; +// NOTE(albrow): We need to manually update this URL for now. Fix this when we +// have the relayer-registry behind semantic versioning. +const RELAYER_REGISTRY_URL = + 'https://raw.githubusercontent.com/0xProject/0x-relayer-registry/4701c85677d161ea729a466aebbc1826c6aa2c0b/relayers.json'; let connection: Connection; diff --git a/packages/pipeline/src/parsers/relayer_registry/index.ts b/packages/pipeline/src/parsers/relayer_registry/index.ts index 6eca10167..50fd12443 100644 --- a/packages/pipeline/src/parsers/relayer_registry/index.ts +++ b/packages/pipeline/src/parsers/relayer_registry/index.ts @@ -3,10 +3,14 @@ import * as R from 'ramda'; import { RelayerResponse, RelayerResponseNetwork } from '../../data_sources/relayer-registry'; import { Relayer } from '../../entities/Relayer'; -export const parseRelayers = R.map(parseRelayer); +export function parseRelayers(rawResp: Map): Relayer[] { + const parsedAsObject = R.mapObjIndexed(parseRelayer, rawResp); + return R.values(parsedAsObject); +} -function parseRelayer(relayerResp: RelayerResponse): Relayer { +function parseRelayer(relayerResp: RelayerResponse, uuid: string): Relayer { const relayer = new Relayer(); + relayer.uuid = uuid; relayer.name = relayerResp.name; relayer.url = relayerResp.homepage_url; relayer.appUrl = relayerResp.app_url; -- cgit v1.2.3 From 830d0f3b21ae26a2b683c388211e442e82bc7459 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Wed, 7 Nov 2018 15:56:35 -0800 Subject: Use bigint for gasUsed and gasPrice in Transaction --- packages/pipeline/src/entities/Transaction.ts | 6 ++++-- packages/pipeline/src/index.ts | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/pipeline/src/entities/Transaction.ts b/packages/pipeline/src/entities/Transaction.ts index d89d44746..8a68da813 100644 --- a/packages/pipeline/src/entities/Transaction.ts +++ b/packages/pipeline/src/entities/Transaction.ts @@ -6,6 +6,8 @@ export class Transaction { @PrimaryColumn() public blockHash!: string; @PrimaryColumn() public blockNumber!: number; - @Column() public gasUsed!: number; - @Column() public gasPrice!: number; + @Column({ type: 'bigint' }) + public gasUsed!: number; + @Column({ type: 'bigint' }) + public gasPrice!: number; } diff --git a/packages/pipeline/src/index.ts b/packages/pipeline/src/index.ts index b42256d87..165ff9780 100644 --- a/packages/pipeline/src/index.ts +++ b/packages/pipeline/src/index.ts @@ -28,9 +28,9 @@ let connection: Connection; const provider = web3Factory.getRpcProvider({ rpcUrl: 'https://mainnet.infura.io', }); - // await getExchangeEventsAsync(provider); - // await getBlockAsync(provider); - // await getTransactionAsync(provider); + await getExchangeEventsAsync(provider); + await getBlockAsync(provider); + await getTransactionAsync(provider); await getRelayers(RELAYER_REGISTRY_URL); process.exit(0); })(); -- cgit v1.2.3 From 9cb89725c9289a821db0ebb1809bcd1d0e64c02a Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Wed, 7 Nov 2018 15:57:44 -0800 Subject: Remove unused function parameter in index.ts script --- packages/pipeline/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/pipeline/src/index.ts b/packages/pipeline/src/index.ts index 165ff9780..136d7a64b 100644 --- a/packages/pipeline/src/index.ts +++ b/packages/pipeline/src/index.ts @@ -31,7 +31,7 @@ let connection: Connection; await getExchangeEventsAsync(provider); await getBlockAsync(provider); await getTransactionAsync(provider); - await getRelayers(RELAYER_REGISTRY_URL); + await getRelayers(); process.exit(0); })(); @@ -74,7 +74,7 @@ async function getTransactionAsync(provider: Web3ProviderEngine): Promise console.log('Done saving tx.'); } -async function getRelayers(url: string): Promise { +async function getRelayers(): Promise { console.log('Getting relayer info...'); const relayerRepository = connection.getRepository(Relayer); const relayerSource = new RelayerRegistrySource(RELAYER_REGISTRY_URL); -- cgit v1.2.3 From 954c3b9272556723c10fd02ad6b753ac98ca47fa Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Wed, 7 Nov 2018 16:48:25 -0800 Subject: Split index.ts into multiple scripts in scripts/ and detect last known block when pulling events --- packages/pipeline/src/index.ts | 88 ---------------------- packages/pipeline/src/merge_v2_events.ts | 81 -------------------- packages/pipeline/src/scripts/merge_v2_events.ts | 81 ++++++++++++++++++++ .../pipeline/src/scripts/pull_missing_events.ts | 60 +++++++++++++++ .../pipeline/src/scripts/update_relayer_info.ts | 31 ++++++++ 5 files changed, 172 insertions(+), 169 deletions(-) delete mode 100644 packages/pipeline/src/index.ts delete mode 100644 packages/pipeline/src/merge_v2_events.ts create mode 100644 packages/pipeline/src/scripts/merge_v2_events.ts create mode 100644 packages/pipeline/src/scripts/pull_missing_events.ts create mode 100644 packages/pipeline/src/scripts/update_relayer_info.ts diff --git a/packages/pipeline/src/index.ts b/packages/pipeline/src/index.ts deleted file mode 100644 index 136d7a64b..000000000 --- a/packages/pipeline/src/index.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { web3Factory } from '@0x/dev-utils'; -import { Web3ProviderEngine } from '@0x/subproviders'; -import 'reflect-metadata'; -import { Connection, createConnection } from 'typeorm'; - -import { ExchangeEventsSource } from './data_sources/contract-wrappers/exchange_events'; -import { RelayerRegistrySource } from './data_sources/relayer-registry'; -import { Web3Source } from './data_sources/web3'; -import { Block } from './entities/Block'; -import { ExchangeFillEvent } from './entities/ExchangeFillEvent'; -import { Relayer } from './entities/Relayer'; -import { Transaction } from './entities/Transaction'; -import { deployConfig } from './ormconfig'; -import { parseExchangeEvents } from './parsers/events'; -import { parseRelayers } from './parsers/relayer_registry'; -import { parseBlock, parseTransaction } from './parsers/web3'; - -const EXCHANGE_START_BLOCK = 6271590; // Block number when the Exchange contract was deployed to mainnet. -// NOTE(albrow): We need to manually update this URL for now. Fix this when we -// have the relayer-registry behind semantic versioning. -const RELAYER_REGISTRY_URL = - 'https://raw.githubusercontent.com/0xProject/0x-relayer-registry/4701c85677d161ea729a466aebbc1826c6aa2c0b/relayers.json'; - -let connection: Connection; - -(async () => { - connection = await createConnection(deployConfig); - const provider = web3Factory.getRpcProvider({ - rpcUrl: 'https://mainnet.infura.io', - }); - await getExchangeEventsAsync(provider); - await getBlockAsync(provider); - await getTransactionAsync(provider); - await getRelayers(); - process.exit(0); -})(); - -async function getExchangeEventsAsync(provider: Web3ProviderEngine): Promise { - console.log('Getting event logs...'); - const eventsRepository = connection.getRepository(ExchangeFillEvent); - const exchangeEvents = new ExchangeEventsSource(provider, 1); - const eventLogs = await exchangeEvents.getFillEventsAsync(EXCHANGE_START_BLOCK, EXCHANGE_START_BLOCK + 100000); - console.log('Parsing events...'); - const events = parseExchangeEvents(eventLogs); - console.log(`Retrieved and parsed ${events.length} total events.`); - console.log('Saving events...'); - for (const event of events) { - await eventsRepository.save(event); - } - console.log('Saved events.'); -} - -async function getBlockAsync(provider: Web3ProviderEngine): Promise { - console.log('Getting block info...'); - const blocksRepository = connection.getRepository(Block); - const web3Source = new Web3Source(provider); - const rawBlock = await web3Source.getBlockInfoAsync(EXCHANGE_START_BLOCK); - const block = parseBlock(rawBlock); - console.log('Saving block info...'); - await blocksRepository.save(block); - console.log('Done saving block.'); -} - -async function getTransactionAsync(provider: Web3ProviderEngine): Promise { - console.log('Getting tx info...'); - const txsRepository = connection.getRepository(Transaction); - const web3Source = new Web3Source(provider); - const rawTx = await web3Source.getTransactionInfoAsync( - '0x6dd106d002873746072fc5e496dd0fb2541b68c77bcf9184ae19a42fd33657fe', - ); - const tx = parseTransaction(rawTx); - console.log('Saving tx info...'); - await txsRepository.save(tx); - console.log('Done saving tx.'); -} - -async function getRelayers(): Promise { - console.log('Getting relayer info...'); - const relayerRepository = connection.getRepository(Relayer); - const relayerSource = new RelayerRegistrySource(RELAYER_REGISTRY_URL); - const relayersResp = await relayerSource.getRelayerInfoAsync(); - const relayers = parseRelayers(relayersResp); - console.log('Saving relayer info...'); - // for (const relayer of relayers) { - await relayerRepository.save(relayers); - // } - console.log('Done saving relayers.'); -} diff --git a/packages/pipeline/src/merge_v2_events.ts b/packages/pipeline/src/merge_v2_events.ts deleted file mode 100644 index 24439423f..000000000 --- a/packages/pipeline/src/merge_v2_events.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { web3Factory } from '@0x/dev-utils'; -import 'reflect-metadata'; -import { Connection, createConnection } from 'typeorm'; - -import { ExchangeEventsSource } from './data_sources/contract-wrappers/exchange_events'; -import { ExchangeFillEvent } from './entities/ExchangeFillEvent'; -import { deployConfig } from './ormconfig'; -import { parseExchangeEvents } from './parsers/events'; - -let connection: Connection; - -(async () => { - connection = await createConnection(deployConfig); - await getExchangeEventsAsync(); - await mergeExchangeEventsAsync(); - console.log('Exiting process'); - process.exit(0); -})(); - -// TODO(albrow): Separately: Errors do not appear to be handled correctly. If you use the -// wrong rpcUrl it just returns early with no error. -async function getExchangeEventsAsync(): Promise { - console.log('Getting event logs...'); - const provider = web3Factory.getRpcProvider({ - rpcUrl: 'https://mainnet.infura.io', - }); - const eventsRepository = connection.getRepository(ExchangeFillEvent); - const exchangeEvents = new ExchangeEventsSource(provider, 1); - const eventLogs = await exchangeEvents.getFillEventsAsync(); - console.log('Parsing events...'); - const events = parseExchangeEvents(eventLogs); - console.log(`Retrieved and parsed ${events.length} total events.`); - console.log('Saving events...'); - for (const event of events) { - await eventsRepository.save(event); - } - await eventsRepository.save(events); - console.log('Saved events.'); -} - -const insertEventsRawQuery = `INSERT INTO events_raw ( - event_type, - error_id, - order_hash, - maker, - maker_amount, - maker_fee, - maker_token, - taker, - taker_amount, - taker_fee, - taker_token, - txn_hash, - fee_recipient, - block_number, - log_index -) -( - SELECT - 'LogFill', - null, - "orderHash", - "makerAddress", - "makerAssetFilledAmount"::numeric(78), - "makerFeePaid"::numeric(78), - "makerTokenAddress", - "takerAddress", - "takerAssetFilledAmount"::numeric(78), - "takerFeePaid"::numeric(78), - "takerTokenAddress", - "transactionHash", - "feeRecipientAddress", - "blockNumber", - "logIndex" - FROM exchange_fill_event -) ON CONFLICT (order_hash, txn_hash, log_index) DO NOTHING`; - -async function mergeExchangeEventsAsync(): Promise { - console.log('Merging results into events_raw...'); - await connection.query(insertEventsRawQuery); -} diff --git a/packages/pipeline/src/scripts/merge_v2_events.ts b/packages/pipeline/src/scripts/merge_v2_events.ts new file mode 100644 index 000000000..227ece121 --- /dev/null +++ b/packages/pipeline/src/scripts/merge_v2_events.ts @@ -0,0 +1,81 @@ +import { web3Factory } from '@0x/dev-utils'; +import 'reflect-metadata'; +import { Connection, createConnection } from 'typeorm'; + +import { ExchangeEventsSource } from '../data_sources/contract-wrappers/exchange_events'; +import { ExchangeFillEvent } from '../entities/ExchangeFillEvent'; +import { deployConfig } from '../ormconfig'; +import { parseExchangeEvents } from '../parsers/events'; + +let connection: Connection; + +(async () => { + connection = await createConnection(deployConfig); + await getExchangeEventsAsync(); + await mergeExchangeEventsAsync(); + console.log('Exiting process'); + process.exit(0); +})(); + +// TODO(albrow): Separately: Errors do not appear to be handled correctly. If you use the +// wrong rpcUrl it just returns early with no error. +async function getExchangeEventsAsync(): Promise { + console.log('Getting event logs...'); + const provider = web3Factory.getRpcProvider({ + rpcUrl: 'https://mainnet.infura.io', + }); + const eventsRepository = connection.getRepository(ExchangeFillEvent); + const exchangeEvents = new ExchangeEventsSource(provider, 1); + const eventLogs = await exchangeEvents.getFillEventsAsync(); + console.log('Parsing events...'); + const events = parseExchangeEvents(eventLogs); + console.log(`Retrieved and parsed ${events.length} total events.`); + console.log('Saving events...'); + for (const event of events) { + await eventsRepository.save(event); + } + await eventsRepository.save(events); + console.log('Saved events.'); +} + +const insertEventsRawQuery = `INSERT INTO events_raw ( + event_type, + error_id, + order_hash, + maker, + maker_amount, + maker_fee, + maker_token, + taker, + taker_amount, + taker_fee, + taker_token, + txn_hash, + fee_recipient, + block_number, + log_index +) +( + SELECT + 'LogFill', + null, + "orderHash", + "makerAddress", + "makerAssetFilledAmount"::numeric(78), + "makerFeePaid"::numeric(78), + "makerTokenAddress", + "takerAddress", + "takerAssetFilledAmount"::numeric(78), + "takerFeePaid"::numeric(78), + "takerTokenAddress", + "transactionHash", + "feeRecipientAddress", + "blockNumber", + "logIndex" + FROM exchange_fill_event +) ON CONFLICT (order_hash, txn_hash, log_index) DO NOTHING`; + +async function mergeExchangeEventsAsync(): Promise { + console.log('Merging results into events_raw...'); + await connection.query(insertEventsRawQuery); +} diff --git a/packages/pipeline/src/scripts/pull_missing_events.ts b/packages/pipeline/src/scripts/pull_missing_events.ts new file mode 100644 index 000000000..1f71722a3 --- /dev/null +++ b/packages/pipeline/src/scripts/pull_missing_events.ts @@ -0,0 +1,60 @@ +import { web3Factory } from '@0x/dev-utils'; +import { Web3ProviderEngine } from '@0x/subproviders'; +import R = require('ramda'); +import 'reflect-metadata'; +import { Connection, createConnection, Repository } from 'typeorm'; + +import { ExchangeEventsSource } from '../data_sources/contract-wrappers/exchange_events'; +import { ExchangeFillEvent } from '../entities/ExchangeFillEvent'; +import { deployConfig } from '../ormconfig'; +import { parseExchangeEvents } from '../parsers/events'; + +const EXCHANGE_START_BLOCK = 6271590; // Block number when the Exchange contract was deployed to mainnet. +const START_BLOCK_OFFSET = 1000; // Number of blocks before the last known block to consider when updating fill events. +const BATCH_SAVE_SIZE = 1000; // Number of events to save at once. + +let connection: Connection; + +(async () => { + connection = await createConnection(deployConfig); + const provider = web3Factory.getRpcProvider({ + rpcUrl: 'https://mainnet.infura.io', + }); + await getExchangeEventsAsync(provider); + process.exit(0); +})(); + +async function getExchangeEventsAsync(provider: Web3ProviderEngine): Promise { + console.log('Checking existing event logs...'); + const eventsRepository = connection.getRepository(ExchangeFillEvent); + const startBlock = await getStartBlockAsync(eventsRepository); + console.log(`Getting event logs starting at ${startBlock}...`); + const exchangeEvents = new ExchangeEventsSource(provider, 1); + const eventLogs = await exchangeEvents.getFillEventsAsync(startBlock); + console.log('Parsing events...'); + const events = parseExchangeEvents(eventLogs); + console.log(`Retrieved and parsed ${events.length} total events.`); + console.log('Saving events...'); + // Split the events into batches of size BATCH_SAVE_SIZE and save each batch + // in a single request. This reduces round-trip latency to the DB. We need + // to batch this way because saving an extremely large number of events in a + // single request causes problems. + for (const eventsBatch of R.splitEvery(BATCH_SAVE_SIZE, events)) { + await eventsRepository.save(eventsBatch); + } + const totalEvents = await eventsRepository.count(); + console.log(`Done saving events. There are now ${totalEvents} total events.`); +} + +async function getStartBlockAsync(eventsRepository: Repository): Promise { + const fillEventCount = await eventsRepository.count(); + if (fillEventCount === 0) { + console.log('No existing fill events found.'); + return EXCHANGE_START_BLOCK; + } + const queryResult = await connection.query( + 'SELECT "blockNumber" FROM exchange_fill_event ORDER BY "blockNumber" DESC LIMIT 1', + ); + const lastKnownBlock = queryResult[0].blockNumber; + return lastKnownBlock - START_BLOCK_OFFSET; +} diff --git a/packages/pipeline/src/scripts/update_relayer_info.ts b/packages/pipeline/src/scripts/update_relayer_info.ts new file mode 100644 index 000000000..05e045ff4 --- /dev/null +++ b/packages/pipeline/src/scripts/update_relayer_info.ts @@ -0,0 +1,31 @@ +import 'reflect-metadata'; +import { Connection, createConnection } from 'typeorm'; + +import { RelayerRegistrySource } from '../data_sources/relayer-registry'; +import { Relayer } from '../entities/Relayer'; +import { deployConfig } from '../ormconfig'; +import { parseRelayers } from '../parsers/relayer_registry'; + +// NOTE(albrow): We need to manually update this URL for now. Fix this when we +// have the relayer-registry behind semantic versioning. +const RELAYER_REGISTRY_URL = + 'https://raw.githubusercontent.com/0xProject/0x-relayer-registry/4701c85677d161ea729a466aebbc1826c6aa2c0b/relayers.json'; + +let connection: Connection; + +(async () => { + connection = await createConnection(deployConfig); + await getRelayers(); + process.exit(0); +})(); + +async function getRelayers(): Promise { + console.log('Getting latest relayer info...'); + const relayerRepository = connection.getRepository(Relayer); + const relayerSource = new RelayerRegistrySource(RELAYER_REGISTRY_URL); + const relayersResp = await relayerSource.getRelayerInfoAsync(); + const relayers = parseRelayers(relayersResp); + console.log('Saving relayer info...'); + await relayerRepository.save(relayers); + console.log('Done saving relayer info.'); +} -- cgit v1.2.3 From ccad046eb649a60fdf7319a075fa41490d593ae8 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Thu, 8 Nov 2018 10:15:09 -0800 Subject: Reorganize entities. Make scripts work from any directory. --- .../pipeline/src/entities/ExchangeCancelEvent.ts | 35 ------------------ .../src/entities/ExchangeCancelUpToEvent.ts | 16 --------- .../pipeline/src/entities/ExchangeFillEvent.ts | 38 -------------------- packages/pipeline/src/entities/SraOrder.ts | 41 ---------------------- .../pipeline/src/entities/exchange_cancel_event.ts | 35 ++++++++++++++++++ .../src/entities/exchange_cancel_up_to_event.ts | 16 +++++++++ .../pipeline/src/entities/exchange_fill_event.ts | 38 ++++++++++++++++++++ packages/pipeline/src/entities/index.ts | 7 ++++ packages/pipeline/src/entities/sra_order.ts | 41 ++++++++++++++++++++++ packages/pipeline/src/ormconfig.ts | 24 +++++++++++-- packages/pipeline/src/parsers/events/index.ts | 4 +-- .../pipeline/src/parsers/relayer_registry/index.ts | 2 +- packages/pipeline/src/parsers/sra_orders/index.ts | 2 +- packages/pipeline/src/parsers/web3/index.ts | 3 +- packages/pipeline/src/scripts/merge_v2_events.ts | 2 +- .../pipeline/src/scripts/pull_missing_events.ts | 2 +- .../pipeline/src/scripts/update_relayer_info.ts | 2 +- .../pipeline/test/parsers/events/index_test.ts | 2 +- .../pipeline/test/parsers/sra_orders/index_test.ts | 2 +- 19 files changed, 168 insertions(+), 144 deletions(-) delete mode 100644 packages/pipeline/src/entities/ExchangeCancelEvent.ts delete mode 100644 packages/pipeline/src/entities/ExchangeCancelUpToEvent.ts delete mode 100644 packages/pipeline/src/entities/ExchangeFillEvent.ts delete mode 100644 packages/pipeline/src/entities/SraOrder.ts create mode 100644 packages/pipeline/src/entities/exchange_cancel_event.ts create mode 100644 packages/pipeline/src/entities/exchange_cancel_up_to_event.ts create mode 100644 packages/pipeline/src/entities/exchange_fill_event.ts create mode 100644 packages/pipeline/src/entities/index.ts create mode 100644 packages/pipeline/src/entities/sra_order.ts diff --git a/packages/pipeline/src/entities/ExchangeCancelEvent.ts b/packages/pipeline/src/entities/ExchangeCancelEvent.ts deleted file mode 100644 index 698b9e2ec..000000000 --- a/packages/pipeline/src/entities/ExchangeCancelEvent.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Column, Entity, PrimaryColumn } from 'typeorm'; - -import { AssetType } from '../types'; - -@Entity() -export class ExchangeCancelEvent { - @PrimaryColumn() public contractAddress!: string; - @PrimaryColumn() public logIndex!: number; - @PrimaryColumn() public blockNumber!: number; - - @Column() public rawData!: string; - - // TODO(albrow): Include transaction hash - @Column() public makerAddress!: string; - @Column({ nullable: true, type: String }) - public takerAddress!: string; - @Column() public feeRecepientAddress!: string; - @Column() public senderAddress!: string; - @Column() public orderHash!: string; - - @Column() public rawMakerAssetData!: string; - @Column() public makerAssetType!: AssetType; - @Column() public makerAssetProxyId!: string; - @Column() public makerTokenAddress!: string; - @Column({ nullable: true, type: String }) - public makerTokenId!: string | null; - @Column() public rawTakerAssetData!: string; - @Column() public takerAssetType!: AssetType; - @Column() public takerAssetProxyId!: string; - @Column() public takerTokenAddress!: string; - @Column({ nullable: true, type: String }) - public takerTokenId!: string | null; - - // TODO(albrow): Include topics? -} diff --git a/packages/pipeline/src/entities/ExchangeCancelUpToEvent.ts b/packages/pipeline/src/entities/ExchangeCancelUpToEvent.ts deleted file mode 100644 index 3ca75ccf7..000000000 --- a/packages/pipeline/src/entities/ExchangeCancelUpToEvent.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Column, Entity, PrimaryColumn } from 'typeorm'; - -@Entity() -export class ExchangeCancelUpToEvent { - @PrimaryColumn() public contractAddress!: string; - @PrimaryColumn() public logIndex!: number; - @PrimaryColumn() public blockNumber!: number; - - // TODO(albrow): Include transaction hash - @Column() public rawData!: string; - - @Column() public makerAddress!: string; - @Column() public senderAddress!: string; - @Column() public orderEpoch!: string; - // TODO(albrow): Include topics? -} diff --git a/packages/pipeline/src/entities/ExchangeFillEvent.ts b/packages/pipeline/src/entities/ExchangeFillEvent.ts deleted file mode 100644 index 6e549af93..000000000 --- a/packages/pipeline/src/entities/ExchangeFillEvent.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Column, Entity, PrimaryColumn } from 'typeorm'; - -import { AssetType } from '../types'; - -@Entity() -export class ExchangeFillEvent { - @PrimaryColumn() public contractAddress!: string; - @PrimaryColumn() public logIndex!: number; - @PrimaryColumn() public blockNumber!: number; - - @Column() public rawData!: string; - - @Column() public transactionHash!: string; - @Column() public makerAddress!: string; - @Column() public takerAddress!: string; - @Column() public feeRecipientAddress!: string; - @Column() public senderAddress!: string; - @Column() public makerAssetFilledAmount!: string; - @Column() public takerAssetFilledAmount!: string; - @Column() public makerFeePaid!: string; - @Column() public takerFeePaid!: string; - @Column() public orderHash!: string; - - @Column() public rawMakerAssetData!: string; - @Column() public makerAssetType!: AssetType; - @Column() public makerAssetProxyId!: string; - @Column() public makerTokenAddress!: string; - @Column({ nullable: true, type: String }) - public makerTokenId!: string | null; - @Column() public rawTakerAssetData!: string; - @Column() public takerAssetType!: AssetType; - @Column() public takerAssetProxyId!: string; - @Column() public takerTokenAddress!: string; - @Column({ nullable: true, type: String }) - public takerTokenId!: string | null; - - // TODO(albrow): Include topics? -} diff --git a/packages/pipeline/src/entities/SraOrder.ts b/packages/pipeline/src/entities/SraOrder.ts deleted file mode 100644 index a22f7c4e5..000000000 --- a/packages/pipeline/src/entities/SraOrder.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Column, Entity, PrimaryColumn } from 'typeorm'; - -import { AssetType } from '../types'; - -@Entity() -export class SraOrder { - @PrimaryColumn() public exchangeAddress!: string; - @PrimaryColumn() public orderHashHex!: string; - - @Column() public sourceUrl!: string; - @Column() public lastUpdatedTimestamp!: number; - @Column() public firstSeenTimestamp!: number; - - @Column() public makerAddress!: string; - @Column() public takerAddress!: string; - @Column() public feeRecipientAddress!: string; - @Column() public senderAddress!: string; - @Column() public makerAssetAmount!: string; - @Column() public takerAssetAmount!: string; - @Column() public makerFee!: string; - @Column() public takerFee!: string; - @Column() public expirationTimeSeconds!: string; - @Column() public salt!: string; - @Column() public signature!: string; - - @Column() public rawMakerAssetData!: string; - @Column() public makerAssetType!: AssetType; - @Column() public makerAssetProxyId!: string; - @Column() public makerTokenAddress!: string; - @Column({ nullable: true, type: String }) - public makerTokenId!: string | null; - @Column() public rawTakerAssetData!: string; - @Column() public takerAssetType!: AssetType; - @Column() public takerAssetProxyId!: string; - @Column() public takerTokenAddress!: string; - @Column({ nullable: true, type: String }) - public takerTokenId!: string | null; - - // TODO(albrow): Make this optional? - @Column() public metaDataJson!: string; -} diff --git a/packages/pipeline/src/entities/exchange_cancel_event.ts b/packages/pipeline/src/entities/exchange_cancel_event.ts new file mode 100644 index 000000000..698b9e2ec --- /dev/null +++ b/packages/pipeline/src/entities/exchange_cancel_event.ts @@ -0,0 +1,35 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { AssetType } from '../types'; + +@Entity() +export class ExchangeCancelEvent { + @PrimaryColumn() public contractAddress!: string; + @PrimaryColumn() public logIndex!: number; + @PrimaryColumn() public blockNumber!: number; + + @Column() public rawData!: string; + + // TODO(albrow): Include transaction hash + @Column() public makerAddress!: string; + @Column({ nullable: true, type: String }) + public takerAddress!: string; + @Column() public feeRecepientAddress!: string; + @Column() public senderAddress!: string; + @Column() public orderHash!: string; + + @Column() public rawMakerAssetData!: string; + @Column() public makerAssetType!: AssetType; + @Column() public makerAssetProxyId!: string; + @Column() public makerTokenAddress!: string; + @Column({ nullable: true, type: String }) + public makerTokenId!: string | null; + @Column() public rawTakerAssetData!: string; + @Column() public takerAssetType!: AssetType; + @Column() public takerAssetProxyId!: string; + @Column() public takerTokenAddress!: string; + @Column({ nullable: true, type: String }) + public takerTokenId!: string | null; + + // TODO(albrow): Include topics? +} diff --git a/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts b/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts new file mode 100644 index 000000000..3ca75ccf7 --- /dev/null +++ b/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts @@ -0,0 +1,16 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +@Entity() +export class ExchangeCancelUpToEvent { + @PrimaryColumn() public contractAddress!: string; + @PrimaryColumn() public logIndex!: number; + @PrimaryColumn() public blockNumber!: number; + + // TODO(albrow): Include transaction hash + @Column() public rawData!: string; + + @Column() public makerAddress!: string; + @Column() public senderAddress!: string; + @Column() public orderEpoch!: string; + // TODO(albrow): Include topics? +} diff --git a/packages/pipeline/src/entities/exchange_fill_event.ts b/packages/pipeline/src/entities/exchange_fill_event.ts new file mode 100644 index 000000000..6e549af93 --- /dev/null +++ b/packages/pipeline/src/entities/exchange_fill_event.ts @@ -0,0 +1,38 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { AssetType } from '../types'; + +@Entity() +export class ExchangeFillEvent { + @PrimaryColumn() public contractAddress!: string; + @PrimaryColumn() public logIndex!: number; + @PrimaryColumn() public blockNumber!: number; + + @Column() public rawData!: string; + + @Column() public transactionHash!: string; + @Column() public makerAddress!: string; + @Column() public takerAddress!: string; + @Column() public feeRecipientAddress!: string; + @Column() public senderAddress!: string; + @Column() public makerAssetFilledAmount!: string; + @Column() public takerAssetFilledAmount!: string; + @Column() public makerFeePaid!: string; + @Column() public takerFeePaid!: string; + @Column() public orderHash!: string; + + @Column() public rawMakerAssetData!: string; + @Column() public makerAssetType!: AssetType; + @Column() public makerAssetProxyId!: string; + @Column() public makerTokenAddress!: string; + @Column({ nullable: true, type: String }) + public makerTokenId!: string | null; + @Column() public rawTakerAssetData!: string; + @Column() public takerAssetType!: AssetType; + @Column() public takerAssetProxyId!: string; + @Column() public takerTokenAddress!: string; + @Column({ nullable: true, type: String }) + public takerTokenId!: string | null; + + // TODO(albrow): Include topics? +} diff --git a/packages/pipeline/src/entities/index.ts b/packages/pipeline/src/entities/index.ts new file mode 100644 index 000000000..4cd8d9da2 --- /dev/null +++ b/packages/pipeline/src/entities/index.ts @@ -0,0 +1,7 @@ +export { Block } from './block'; +export { ExchangeCancelEvent } from './exchange_cancel_event'; +export { ExchangeCancelUpToEvent } from './exchange_cancel_up_to_event'; +export { ExchangeFillEvent } from './exchange_fill_event'; +export { Relayer } from './relayer'; +export { SraOrder } from './sra_order'; +export { Transaction } from './transaction'; diff --git a/packages/pipeline/src/entities/sra_order.ts b/packages/pipeline/src/entities/sra_order.ts new file mode 100644 index 000000000..a22f7c4e5 --- /dev/null +++ b/packages/pipeline/src/entities/sra_order.ts @@ -0,0 +1,41 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { AssetType } from '../types'; + +@Entity() +export class SraOrder { + @PrimaryColumn() public exchangeAddress!: string; + @PrimaryColumn() public orderHashHex!: string; + + @Column() public sourceUrl!: string; + @Column() public lastUpdatedTimestamp!: number; + @Column() public firstSeenTimestamp!: number; + + @Column() public makerAddress!: string; + @Column() public takerAddress!: string; + @Column() public feeRecipientAddress!: string; + @Column() public senderAddress!: string; + @Column() public makerAssetAmount!: string; + @Column() public takerAssetAmount!: string; + @Column() public makerFee!: string; + @Column() public takerFee!: string; + @Column() public expirationTimeSeconds!: string; + @Column() public salt!: string; + @Column() public signature!: string; + + @Column() public rawMakerAssetData!: string; + @Column() public makerAssetType!: AssetType; + @Column() public makerAssetProxyId!: string; + @Column() public makerTokenAddress!: string; + @Column({ nullable: true, type: String }) + public makerTokenId!: string | null; + @Column() public rawTakerAssetData!: string; + @Column() public takerAssetType!: AssetType; + @Column() public takerAssetProxyId!: string; + @Column() public takerTokenAddress!: string; + @Column({ nullable: true, type: String }) + public takerTokenId!: string | null; + + // TODO(albrow): Make this optional? + @Column() public metaDataJson!: string; +} diff --git a/packages/pipeline/src/ormconfig.ts b/packages/pipeline/src/ormconfig.ts index eaafeb7d5..39b496ace 100644 --- a/packages/pipeline/src/ormconfig.ts +++ b/packages/pipeline/src/ormconfig.ts @@ -1,11 +1,31 @@ import { ConnectionOptions } from 'typeorm'; +import { + Block, + ExchangeCancelEvent, + ExchangeCancelUpToEvent, + ExchangeFillEvent, + Relayer, + SraOrder, + Transaction, +} from './entities'; + +const entities = [ + Block, + ExchangeCancelEvent, + ExchangeCancelUpToEvent, + ExchangeFillEvent, + Relayer, + SraOrder, + Transaction, +]; + export const testConfig: ConnectionOptions = { type: 'sqlite', database: 'database.sqlite', synchronize: true, logging: false, - entities: ['./lib/src/entities/**/*.js'], + entities, migrations: ['./lib/src/migrations/**/*.js'], cli: { entitiesDir: 'lib/src/entities', @@ -18,7 +38,7 @@ export const deployConfig: ConnectionOptions = { url: process.env.ZEROEX_DATA_PIPELINE_DB_URL, synchronize: true, logging: false, - entities: ['./lib/src/entities/**/*.js'], + entities, migrations: ['./lib/src/migrations/**/*.js'], cli: { entitiesDir: 'lib/src/entities', diff --git a/packages/pipeline/src/parsers/events/index.ts b/packages/pipeline/src/parsers/events/index.ts index abfb7ddb6..7211eed76 100644 --- a/packages/pipeline/src/parsers/events/index.ts +++ b/packages/pipeline/src/parsers/events/index.ts @@ -9,9 +9,7 @@ import { AssetProxyId, ERC721AssetData } from '@0x/types'; import { LogWithDecodedArgs } from 'ethereum-types'; import * as R from 'ramda'; -import { ExchangeCancelEvent } from '../../entities/ExchangeCancelEvent'; -import { ExchangeCancelUpToEvent } from '../../entities/ExchangeCancelUpToEvent'; -import { ExchangeFillEvent } from '../../entities/ExchangeFillEvent'; +import { ExchangeCancelEvent, ExchangeCancelUpToEvent, ExchangeFillEvent } from '../../entities'; import { bigNumbertoStringOrNull } from '../../utils'; export type ExchangeEventEntity = ExchangeFillEvent | ExchangeCancelEvent | ExchangeCancelUpToEvent; diff --git a/packages/pipeline/src/parsers/relayer_registry/index.ts b/packages/pipeline/src/parsers/relayer_registry/index.ts index 50fd12443..a61f4e62a 100644 --- a/packages/pipeline/src/parsers/relayer_registry/index.ts +++ b/packages/pipeline/src/parsers/relayer_registry/index.ts @@ -1,7 +1,7 @@ import * as R from 'ramda'; import { RelayerResponse, RelayerResponseNetwork } from '../../data_sources/relayer-registry'; -import { Relayer } from '../../entities/Relayer'; +import { Relayer } from '../../entities'; export function parseRelayers(rawResp: Map): Relayer[] { const parsedAsObject = R.mapObjIndexed(parseRelayer, rawResp); diff --git a/packages/pipeline/src/parsers/sra_orders/index.ts b/packages/pipeline/src/parsers/sra_orders/index.ts index 39c222afd..5cd19b08a 100644 --- a/packages/pipeline/src/parsers/sra_orders/index.ts +++ b/packages/pipeline/src/parsers/sra_orders/index.ts @@ -3,7 +3,7 @@ import { assetDataUtils, orderHashUtils } from '@0x/order-utils'; import { AssetProxyId, ERC721AssetData } from '@0x/types'; import * as R from 'ramda'; -import { SraOrder } from '../../entities/SraOrder'; +import { SraOrder } from '../../entities'; import { bigNumbertoStringOrNull } from '../../utils'; export function parseSraOrders(rawOrdersResponse: OrdersResponse): SraOrder[] { diff --git a/packages/pipeline/src/parsers/web3/index.ts b/packages/pipeline/src/parsers/web3/index.ts index c6647c966..11571278b 100644 --- a/packages/pipeline/src/parsers/web3/index.ts +++ b/packages/pipeline/src/parsers/web3/index.ts @@ -1,7 +1,6 @@ import { BlockWithoutTransactionData, Transaction as EthTransaction } from 'ethereum-types'; -import { Block } from '../../entities/Block'; -import { Transaction } from '../../entities/Transaction'; +import { Block, Transaction } from '../../entities'; export function parseBlock(rawBlock: BlockWithoutTransactionData): Block { if (rawBlock.hash == null) { diff --git a/packages/pipeline/src/scripts/merge_v2_events.ts b/packages/pipeline/src/scripts/merge_v2_events.ts index 227ece121..99a76aa61 100644 --- a/packages/pipeline/src/scripts/merge_v2_events.ts +++ b/packages/pipeline/src/scripts/merge_v2_events.ts @@ -3,7 +3,7 @@ import 'reflect-metadata'; import { Connection, createConnection } from 'typeorm'; import { ExchangeEventsSource } from '../data_sources/contract-wrappers/exchange_events'; -import { ExchangeFillEvent } from '../entities/ExchangeFillEvent'; +import { ExchangeFillEvent } from '../entities'; import { deployConfig } from '../ormconfig'; import { parseExchangeEvents } from '../parsers/events'; diff --git a/packages/pipeline/src/scripts/pull_missing_events.ts b/packages/pipeline/src/scripts/pull_missing_events.ts index 1f71722a3..a108f012f 100644 --- a/packages/pipeline/src/scripts/pull_missing_events.ts +++ b/packages/pipeline/src/scripts/pull_missing_events.ts @@ -5,7 +5,7 @@ import 'reflect-metadata'; import { Connection, createConnection, Repository } from 'typeorm'; import { ExchangeEventsSource } from '../data_sources/contract-wrappers/exchange_events'; -import { ExchangeFillEvent } from '../entities/ExchangeFillEvent'; +import { ExchangeFillEvent } from '../entities'; import { deployConfig } from '../ormconfig'; import { parseExchangeEvents } from '../parsers/events'; diff --git a/packages/pipeline/src/scripts/update_relayer_info.ts b/packages/pipeline/src/scripts/update_relayer_info.ts index 05e045ff4..f54e16b6c 100644 --- a/packages/pipeline/src/scripts/update_relayer_info.ts +++ b/packages/pipeline/src/scripts/update_relayer_info.ts @@ -2,7 +2,7 @@ import 'reflect-metadata'; import { Connection, createConnection } from 'typeorm'; import { RelayerRegistrySource } from '../data_sources/relayer-registry'; -import { Relayer } from '../entities/Relayer'; +import { Relayer } from '../entities'; import { deployConfig } from '../ormconfig'; import { parseRelayers } from '../parsers/relayer_registry'; diff --git a/packages/pipeline/test/parsers/events/index_test.ts b/packages/pipeline/test/parsers/events/index_test.ts index 451988f8e..fdd07f246 100644 --- a/packages/pipeline/test/parsers/events/index_test.ts +++ b/packages/pipeline/test/parsers/events/index_test.ts @@ -4,7 +4,7 @@ import * as chai from 'chai'; import { LogWithDecodedArgs } from 'ethereum-types'; import 'mocha'; -import { ExchangeFillEvent } from '../../../src/entities/ExchangeFillEvent'; +import { ExchangeFillEvent } from '../../../src/entities'; import { _convertToEntity } from '../../../src/parsers/events'; import { chaiSetup } from '../../utils/chai_setup'; diff --git a/packages/pipeline/test/parsers/sra_orders/index_test.ts b/packages/pipeline/test/parsers/sra_orders/index_test.ts index eb181ece5..fee32a0a7 100644 --- a/packages/pipeline/test/parsers/sra_orders/index_test.ts +++ b/packages/pipeline/test/parsers/sra_orders/index_test.ts @@ -3,7 +3,7 @@ import { BigNumber } from '@0x/utils'; import * as chai from 'chai'; import 'mocha'; -import { SraOrder } from '../../../src/entities/SraOrder'; +import { SraOrder } from '../../../src/entities'; import { _convertToEntity } from '../../../src/parsers/sra_orders'; import { chaiSetup } from '../../utils/chai_setup'; -- cgit v1.2.3 From 53cc9e9bedc6adf2247a190dbb758c9411033cab Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Thu, 8 Nov 2018 10:53:18 -0800 Subject: Rename table and column names --- packages/pipeline/src/entities/Block.ts | 5 +- packages/pipeline/src/entities/Relayer.ts | 12 ++-- packages/pipeline/src/entities/Transaction.ts | 15 ++-- .../pipeline/src/entities/exchange_cancel_event.ts | 56 +++++++++------ .../src/entities/exchange_cancel_up_to_event.ts | 23 +++--- .../pipeline/src/entities/exchange_fill_event.ts | 72 ++++++++++++------- packages/pipeline/src/entities/sra_order.ts | 81 ++++++++++++++-------- packages/pipeline/src/parsers/events/index.ts | 2 +- packages/pipeline/src/parsers/sra_orders/index.ts | 2 +- .../pipeline/test/parsers/sra_orders/index_test.ts | 2 +- 10 files changed, 172 insertions(+), 98 deletions(-) diff --git a/packages/pipeline/src/entities/Block.ts b/packages/pipeline/src/entities/Block.ts index 49e0ef840..5bd51f3d2 100644 --- a/packages/pipeline/src/entities/Block.ts +++ b/packages/pipeline/src/entities/Block.ts @@ -1,9 +1,10 @@ import { Column, Entity, PrimaryColumn } from 'typeorm'; -@Entity() +@Entity({ name: 'blocks' }) export class Block { @PrimaryColumn() public hash!: string; @PrimaryColumn() public number!: number; - @Column() public unixTimestampSeconds!: number; + @Column({ name: 'unix_timestamp_seconds' }) + public unixTimestampSeconds!: number; } diff --git a/packages/pipeline/src/entities/Relayer.ts b/packages/pipeline/src/entities/Relayer.ts index 605355fa8..94b5232a8 100644 --- a/packages/pipeline/src/entities/Relayer.ts +++ b/packages/pipeline/src/entities/Relayer.ts @@ -1,23 +1,23 @@ import { Column, Entity, PrimaryColumn } from 'typeorm'; -@Entity() +@Entity({ name: 'relayers' }) export class Relayer { @PrimaryColumn() public uuid!: string; @Column() public name!: string; @Column() public url!: string; - @Column({ nullable: true, type: String }) + @Column({ nullable: true, type: String, name: 'sra_http_endpoint' }) public sraHttpEndpoint!: string | null; - @Column({ nullable: true, type: String }) + @Column({ nullable: true, type: String, name: 'sra_ws_endpoint' }) public sraWsEndpoint!: string | null; - @Column({ nullable: true, type: String }) + @Column({ nullable: true, type: String, name: 'app_url' }) public appUrl!: string | null; // TODO(albrow): Add exchange contract or protocol version? // TODO(albrow): Add network ids for addresses? - @Column({ type: 'varchar', array: true }) + @Column({ type: 'varchar', array: true, name: 'fee_recipient_addresses' }) public feeRecipientAddresses!: string[]; - @Column({ type: 'varchar', array: true }) + @Column({ type: 'varchar', array: true, name: 'taker_addresses' }) public takerAddresses!: string[]; } diff --git a/packages/pipeline/src/entities/Transaction.ts b/packages/pipeline/src/entities/Transaction.ts index 8a68da813..eb2883fda 100644 --- a/packages/pipeline/src/entities/Transaction.ts +++ b/packages/pipeline/src/entities/Transaction.ts @@ -1,13 +1,16 @@ import { Column, Entity, PrimaryColumn } from 'typeorm'; -@Entity() +@Entity({ name: 'transactions' }) export class Transaction { - @PrimaryColumn() public transactionHash!: string; - @PrimaryColumn() public blockHash!: string; - @PrimaryColumn() public blockNumber!: number; + @PrimaryColumn({ name: 'transaction_hash' }) + public transactionHash!: string; + @PrimaryColumn({ name: 'block_hash' }) + public blockHash!: string; + @PrimaryColumn({ name: 'block_number' }) + public blockNumber!: number; - @Column({ type: 'bigint' }) + @Column({ type: 'bigint', name: 'gas_used' }) public gasUsed!: number; - @Column({ type: 'bigint' }) + @Column({ type: 'bigint', name: 'gas_price' }) public gasPrice!: number; } diff --git a/packages/pipeline/src/entities/exchange_cancel_event.ts b/packages/pipeline/src/entities/exchange_cancel_event.ts index 698b9e2ec..6d0c3bc32 100644 --- a/packages/pipeline/src/entities/exchange_cancel_event.ts +++ b/packages/pipeline/src/entities/exchange_cancel_event.ts @@ -2,33 +2,49 @@ import { Column, Entity, PrimaryColumn } from 'typeorm'; import { AssetType } from '../types'; -@Entity() +@Entity({ name: 'exchange_cancel_events' }) export class ExchangeCancelEvent { - @PrimaryColumn() public contractAddress!: string; - @PrimaryColumn() public logIndex!: number; - @PrimaryColumn() public blockNumber!: number; + @PrimaryColumn({ name: 'contract_address' }) + public contractAddress!: string; + @PrimaryColumn({ name: 'log_index' }) + public logIndex!: number; + @PrimaryColumn({ name: 'block_number' }) + public blockNumber!: number; - @Column() public rawData!: string; + @Column({ name: 'raw_data' }) + public rawData!: string; // TODO(albrow): Include transaction hash - @Column() public makerAddress!: string; - @Column({ nullable: true, type: String }) + @Column({ name: 'maker_address' }) + public makerAddress!: string; + @Column({ nullable: true, type: String, name: 'taker_address' }) public takerAddress!: string; - @Column() public feeRecepientAddress!: string; - @Column() public senderAddress!: string; - @Column() public orderHash!: string; + @Column({ name: 'fee_recipient_address' }) + public feeRecipientAddress!: string; + @Column({ name: 'sender_address' }) + public senderAddress!: string; + @Column({ name: 'order_hash' }) + public orderHash!: string; - @Column() public rawMakerAssetData!: string; - @Column() public makerAssetType!: AssetType; - @Column() public makerAssetProxyId!: string; - @Column() public makerTokenAddress!: string; - @Column({ nullable: true, type: String }) + @Column({ name: 'raw_maker_asset_data' }) + public rawMakerAssetData!: string; + @Column({ name: 'maker_asset_type' }) + public makerAssetType!: AssetType; + @Column({ name: 'maker_asset_proxy_id' }) + public makerAssetProxyId!: string; + @Column({ name: 'maker_token_address' }) + public makerTokenAddress!: string; + @Column({ nullable: true, type: String, name: 'maker_token_id' }) public makerTokenId!: string | null; - @Column() public rawTakerAssetData!: string; - @Column() public takerAssetType!: AssetType; - @Column() public takerAssetProxyId!: string; - @Column() public takerTokenAddress!: string; - @Column({ nullable: true, type: String }) + @Column({ name: 'raw_taker_asset_data' }) + public rawTakerAssetData!: string; + @Column({ name: 'taker_asset_type' }) + public takerAssetType!: AssetType; + @Column({ name: 'taker_asset_proxy_id' }) + public takerAssetProxyId!: string; + @Column({ name: 'taker_token_address' }) + public takerTokenAddress!: string; + @Column({ nullable: true, type: String, name: 'taker_token_id' }) public takerTokenId!: string | null; // TODO(albrow): Include topics? diff --git a/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts b/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts index 3ca75ccf7..891550050 100644 --- a/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts +++ b/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts @@ -1,16 +1,23 @@ import { Column, Entity, PrimaryColumn } from 'typeorm'; -@Entity() +@Entity({ name: 'exchange_cancel_up_to_event' }) export class ExchangeCancelUpToEvent { - @PrimaryColumn() public contractAddress!: string; - @PrimaryColumn() public logIndex!: number; - @PrimaryColumn() public blockNumber!: number; + @PrimaryColumn({ name: 'contract_address' }) + public contractAddress!: string; + @PrimaryColumn({ name: 'log_index' }) + public logIndex!: number; + @PrimaryColumn({ name: 'block_number' }) + public blockNumber!: number; // TODO(albrow): Include transaction hash - @Column() public rawData!: string; + @Column({ name: 'raw_data' }) + public rawData!: string; - @Column() public makerAddress!: string; - @Column() public senderAddress!: string; - @Column() public orderEpoch!: string; + @Column({ name: 'maker_address' }) + public makerAddress!: string; + @Column({ name: 'sender_address' }) + public senderAddress!: string; + @Column({ name: 'order_epoch' }) + public orderEpoch!: string; // TODO(albrow): Include topics? } diff --git a/packages/pipeline/src/entities/exchange_fill_event.ts b/packages/pipeline/src/entities/exchange_fill_event.ts index 6e549af93..0c5546d7e 100644 --- a/packages/pipeline/src/entities/exchange_fill_event.ts +++ b/packages/pipeline/src/entities/exchange_fill_event.ts @@ -2,36 +2,58 @@ import { Column, Entity, PrimaryColumn } from 'typeorm'; import { AssetType } from '../types'; -@Entity() +@Entity({ name: 'exchange_fill_events' }) export class ExchangeFillEvent { - @PrimaryColumn() public contractAddress!: string; - @PrimaryColumn() public logIndex!: number; - @PrimaryColumn() public blockNumber!: number; + @PrimaryColumn({ name: 'contract_address' }) + public contractAddress!: string; + @PrimaryColumn({ name: 'log_index' }) + public logIndex!: number; + @PrimaryColumn({ name: 'block_number' }) + public blockNumber!: number; - @Column() public rawData!: string; + @Column({ name: 'raw_data' }) + public rawData!: string; - @Column() public transactionHash!: string; - @Column() public makerAddress!: string; - @Column() public takerAddress!: string; - @Column() public feeRecipientAddress!: string; - @Column() public senderAddress!: string; - @Column() public makerAssetFilledAmount!: string; - @Column() public takerAssetFilledAmount!: string; - @Column() public makerFeePaid!: string; - @Column() public takerFeePaid!: string; - @Column() public orderHash!: string; + @Column({ name: 'transaction_hash' }) + public transactionHash!: string; + @Column({ name: 'maker_address' }) + public makerAddress!: string; + @Column({ name: 'taker_address' }) + public takerAddress!: string; + @Column({ name: 'fee_recipient_address' }) + public feeRecipientAddress!: string; + @Column({ name: 'sender_address' }) + public senderAddress!: string; + @Column({ name: 'maker_asset_filled_amount' }) + public makerAssetFilledAmount!: string; + @Column({ name: 'taker_asset_filled_amount' }) + public takerAssetFilledAmount!: string; + @Column({ name: 'maker_fee_paid' }) + public makerFeePaid!: string; + @Column({ name: 'taker_fee_paid' }) + public takerFeePaid!: string; + @Column({ name: 'order_hash' }) + public orderHash!: string; - @Column() public rawMakerAssetData!: string; - @Column() public makerAssetType!: AssetType; - @Column() public makerAssetProxyId!: string; - @Column() public makerTokenAddress!: string; - @Column({ nullable: true, type: String }) + @Column({ name: 'raw_maker_asset_data' }) + public rawMakerAssetData!: string; + @Column({ name: 'maker_asset_type' }) + public makerAssetType!: AssetType; + @Column({ name: 'maker_asset_proxy_id' }) + public makerAssetProxyId!: string; + @Column({ name: 'maker_token_address' }) + public makerTokenAddress!: string; + @Column({ nullable: true, type: String, name: 'maker_token_id' }) public makerTokenId!: string | null; - @Column() public rawTakerAssetData!: string; - @Column() public takerAssetType!: AssetType; - @Column() public takerAssetProxyId!: string; - @Column() public takerTokenAddress!: string; - @Column({ nullable: true, type: String }) + @Column({ name: 'raw_taker_asset_data' }) + public rawTakerAssetData!: string; + @Column({ name: 'taker_asset_type' }) + public takerAssetType!: AssetType; + @Column({ name: 'taker_asset_proxy_id' }) + public takerAssetProxyId!: string; + @Column({ name: 'taker_token_address' }) + public takerTokenAddress!: string; + @Column({ nullable: true, type: String, name: 'taker_token_id' }) public takerTokenId!: string | null; // TODO(albrow): Include topics? diff --git a/packages/pipeline/src/entities/sra_order.ts b/packages/pipeline/src/entities/sra_order.ts index a22f7c4e5..f3f79852f 100644 --- a/packages/pipeline/src/entities/sra_order.ts +++ b/packages/pipeline/src/entities/sra_order.ts @@ -2,40 +2,65 @@ import { Column, Entity, PrimaryColumn } from 'typeorm'; import { AssetType } from '../types'; -@Entity() +@Entity({ name: 'sra_orders' }) export class SraOrder { - @PrimaryColumn() public exchangeAddress!: string; - @PrimaryColumn() public orderHashHex!: string; + @PrimaryColumn({ name: 'exchange_address' }) + public exchangeAddress!: string; + @PrimaryColumn({ name: 'order_hash_hex' }) + public orderHashHex!: string; - @Column() public sourceUrl!: string; - @Column() public lastUpdatedTimestamp!: number; - @Column() public firstSeenTimestamp!: number; + @Column({ name: 'source_url' }) + public sourceUrl!: string; + @Column({ name: 'last_updated_timestamp' }) + public lastUpdatedTimestamp!: number; + @Column({ name: 'first_seen_timestamp' }) + public firstSeenTimestamp!: number; - @Column() public makerAddress!: string; - @Column() public takerAddress!: string; - @Column() public feeRecipientAddress!: string; - @Column() public senderAddress!: string; - @Column() public makerAssetAmount!: string; - @Column() public takerAssetAmount!: string; - @Column() public makerFee!: string; - @Column() public takerFee!: string; - @Column() public expirationTimeSeconds!: string; - @Column() public salt!: string; - @Column() public signature!: string; + @Column({ name: 'maker_address' }) + public makerAddress!: string; + @Column({ name: 'taker_address' }) + public takerAddress!: string; + @Column({ name: 'fee_recipient_address' }) + public feeRecipientAddress!: string; + @Column({ name: 'sender_address' }) + public senderAddress!: string; + @Column({ name: 'maker_asset_amount' }) + public makerAssetAmount!: string; + @Column({ name: 'taker_asset_amount' }) + public takerAssetAmount!: string; + @Column({ name: 'maker_fee' }) + public makerFee!: string; + @Column({ name: 'taker_fee' }) + public takerFee!: string; + @Column({ name: 'expiration_time_seconds' }) + public expirationTimeSeconds!: string; + @Column({ name: 'salt' }) + public salt!: string; + @Column({ name: 'signature' }) + public signature!: string; - @Column() public rawMakerAssetData!: string; - @Column() public makerAssetType!: AssetType; - @Column() public makerAssetProxyId!: string; - @Column() public makerTokenAddress!: string; - @Column({ nullable: true, type: String }) + @Column({ name: 'raw_maker_asset_data' }) + public rawMakerAssetData!: string; + @Column({ name: 'maker_asset_type' }) + public makerAssetType!: AssetType; + @Column({ name: 'maker_asset_proxy_id' }) + public makerAssetProxyId!: string; + @Column({ name: 'maker_token_address' }) + public makerTokenAddress!: string; + @Column({ nullable: true, type: String, name: 'maker_token_id' }) public makerTokenId!: string | null; - @Column() public rawTakerAssetData!: string; - @Column() public takerAssetType!: AssetType; - @Column() public takerAssetProxyId!: string; - @Column() public takerTokenAddress!: string; - @Column({ nullable: true, type: String }) + @Column({ name: 'raw_taker_asset_data' }) + public rawTakerAssetData!: string; + @Column({ name: 'taker_asset_type' }) + public takerAssetType!: AssetType; + @Column({ name: 'taker_asset_proxy_id' }) + public takerAssetProxyId!: string; + @Column({ name: 'taker_token_address' }) + public takerTokenAddress!: string; + @Column({ nullable: true, type: String, name: 'taker_token_id' }) public takerTokenId!: string | null; // TODO(albrow): Make this optional? - @Column() public metaDataJson!: string; + @Column({ name: 'metadata_json' }) + public metadataJson!: string; } diff --git a/packages/pipeline/src/parsers/events/index.ts b/packages/pipeline/src/parsers/events/index.ts index 7211eed76..7a0ead0d4 100644 --- a/packages/pipeline/src/parsers/events/index.ts +++ b/packages/pipeline/src/parsers/events/index.ts @@ -79,7 +79,7 @@ export function _convertToExchangeCancelEvent( exchangeCancelEvent.makerAddress = eventLog.args.makerAddress.toString(); exchangeCancelEvent.takerAddress = eventLog.args.takerAddress == null ? null : eventLog.args.takerAddress.toString(); - exchangeCancelEvent.feeRecepientAddress = eventLog.args.feeRecipientAddress; + exchangeCancelEvent.feeRecipientAddress = eventLog.args.feeRecipientAddress; exchangeCancelEvent.senderAddress = eventLog.args.senderAddress; exchangeCancelEvent.orderHash = eventLog.args.orderHash; exchangeCancelEvent.rawMakerAssetData = eventLog.args.makerAssetData; diff --git a/packages/pipeline/src/parsers/sra_orders/index.ts b/packages/pipeline/src/parsers/sra_orders/index.ts index 5cd19b08a..4e4bf12ff 100644 --- a/packages/pipeline/src/parsers/sra_orders/index.ts +++ b/packages/pipeline/src/parsers/sra_orders/index.ts @@ -48,7 +48,7 @@ export function _convertToEntity(apiOrder: APIOrder): SraOrder { sraOrder.takerTokenAddress = takerAssetData.tokenAddress; sraOrder.takerTokenId = bigNumbertoStringOrNull((takerAssetData as ERC721AssetData).tokenId); - sraOrder.metaDataJson = JSON.stringify(apiOrder.metaData); + sraOrder.metadataJson = JSON.stringify(apiOrder.metaData); return sraOrder; } diff --git a/packages/pipeline/test/parsers/sra_orders/index_test.ts b/packages/pipeline/test/parsers/sra_orders/index_test.ts index fee32a0a7..4b2e7c2d0 100644 --- a/packages/pipeline/test/parsers/sra_orders/index_test.ts +++ b/packages/pipeline/test/parsers/sra_orders/index_test.ts @@ -61,7 +61,7 @@ describe('sra_orders', () => { expected.takerAssetProxyId = '0xf47261b0'; expected.takerTokenAddress = '0x42d6622dece394b54999fbd73d108123806f6a18'; expected.takerTokenId = null; - expected.metaDataJson = '{"isThisArbitraryData":true,"powerLevel":9001}'; + expected.metadataJson = '{"isThisArbitraryData":true,"powerLevel":9001}'; const actual = _convertToEntity(input); expect(actual).deep.equal(expected); -- cgit v1.2.3 From 410a9244952b663fa8e56cc778b712ae228bbd82 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Thu, 8 Nov 2018 11:38:37 -0800 Subject: Add better error handling for immediately invoked async functions --- packages/pipeline/src/scripts/merge_v2_events.ts | 81 ---------------------- .../pipeline/src/scripts/pull_missing_events.ts | 7 +- .../pipeline/src/scripts/update_relayer_info.ts | 3 +- packages/pipeline/src/utils/index.ts | 14 ++++ 4 files changed, 20 insertions(+), 85 deletions(-) delete mode 100644 packages/pipeline/src/scripts/merge_v2_events.ts diff --git a/packages/pipeline/src/scripts/merge_v2_events.ts b/packages/pipeline/src/scripts/merge_v2_events.ts deleted file mode 100644 index 99a76aa61..000000000 --- a/packages/pipeline/src/scripts/merge_v2_events.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { web3Factory } from '@0x/dev-utils'; -import 'reflect-metadata'; -import { Connection, createConnection } from 'typeorm'; - -import { ExchangeEventsSource } from '../data_sources/contract-wrappers/exchange_events'; -import { ExchangeFillEvent } from '../entities'; -import { deployConfig } from '../ormconfig'; -import { parseExchangeEvents } from '../parsers/events'; - -let connection: Connection; - -(async () => { - connection = await createConnection(deployConfig); - await getExchangeEventsAsync(); - await mergeExchangeEventsAsync(); - console.log('Exiting process'); - process.exit(0); -})(); - -// TODO(albrow): Separately: Errors do not appear to be handled correctly. If you use the -// wrong rpcUrl it just returns early with no error. -async function getExchangeEventsAsync(): Promise { - console.log('Getting event logs...'); - const provider = web3Factory.getRpcProvider({ - rpcUrl: 'https://mainnet.infura.io', - }); - const eventsRepository = connection.getRepository(ExchangeFillEvent); - const exchangeEvents = new ExchangeEventsSource(provider, 1); - const eventLogs = await exchangeEvents.getFillEventsAsync(); - console.log('Parsing events...'); - const events = parseExchangeEvents(eventLogs); - console.log(`Retrieved and parsed ${events.length} total events.`); - console.log('Saving events...'); - for (const event of events) { - await eventsRepository.save(event); - } - await eventsRepository.save(events); - console.log('Saved events.'); -} - -const insertEventsRawQuery = `INSERT INTO events_raw ( - event_type, - error_id, - order_hash, - maker, - maker_amount, - maker_fee, - maker_token, - taker, - taker_amount, - taker_fee, - taker_token, - txn_hash, - fee_recipient, - block_number, - log_index -) -( - SELECT - 'LogFill', - null, - "orderHash", - "makerAddress", - "makerAssetFilledAmount"::numeric(78), - "makerFeePaid"::numeric(78), - "makerTokenAddress", - "takerAddress", - "takerAssetFilledAmount"::numeric(78), - "takerFeePaid"::numeric(78), - "takerTokenAddress", - "transactionHash", - "feeRecipientAddress", - "blockNumber", - "logIndex" - FROM exchange_fill_event -) ON CONFLICT (order_hash, txn_hash, log_index) DO NOTHING`; - -async function mergeExchangeEventsAsync(): Promise { - console.log('Merging results into events_raw...'); - await connection.query(insertEventsRawQuery); -} diff --git a/packages/pipeline/src/scripts/pull_missing_events.ts b/packages/pipeline/src/scripts/pull_missing_events.ts index a108f012f..cca0d9cfe 100644 --- a/packages/pipeline/src/scripts/pull_missing_events.ts +++ b/packages/pipeline/src/scripts/pull_missing_events.ts @@ -8,6 +8,7 @@ import { ExchangeEventsSource } from '../data_sources/contract-wrappers/exchange import { ExchangeFillEvent } from '../entities'; import { deployConfig } from '../ormconfig'; import { parseExchangeEvents } from '../parsers/events'; +import { handleError } from '../utils'; const EXCHANGE_START_BLOCK = 6271590; // Block number when the Exchange contract was deployed to mainnet. const START_BLOCK_OFFSET = 1000; // Number of blocks before the last known block to consider when updating fill events. @@ -22,7 +23,7 @@ let connection: Connection; }); await getExchangeEventsAsync(provider); process.exit(0); -})(); +})().catch(handleError); async function getExchangeEventsAsync(provider: Web3ProviderEngine): Promise { console.log('Checking existing event logs...'); @@ -53,8 +54,8 @@ async function getStartBlockAsync(eventsRepository: Repository { console.log('Getting latest relayer info...'); diff --git a/packages/pipeline/src/utils/index.ts b/packages/pipeline/src/utils/index.ts index ebc1f8e0f..4242e0437 100644 --- a/packages/pipeline/src/utils/index.ts +++ b/packages/pipeline/src/utils/index.ts @@ -6,3 +6,17 @@ export function bigNumbertoStringOrNull(n: BigNumber): string | null { } return n.toString(); } + +export function handleError(e: any): void { + if (e.message != null) { + console.error(e.message); + } else { + console.error('Unknown error'); + } + if (e.stack != null) { + console.error(e.stack); + } else { + console.error('(No stack trace)'); + } + process.exit(1); +} -- cgit v1.2.3 From 6fb333f2003a67277a7605332f146adb2758cdd0 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Thu, 8 Nov 2018 12:35:59 -0800 Subject: Rename Transaction.ts to transaction.ts Rename Relayer.ts to relayer.ts Rename Block.ts to block.ts --- packages/pipeline/src/entities/Block.ts | 10 ---------- packages/pipeline/src/entities/Relayer.ts | 23 ----------------------- packages/pipeline/src/entities/Transaction.ts | 16 ---------------- packages/pipeline/src/entities/block.ts | 10 ++++++++++ packages/pipeline/src/entities/relayer.ts | 23 +++++++++++++++++++++++ packages/pipeline/src/entities/transaction.ts | 16 ++++++++++++++++ 6 files changed, 49 insertions(+), 49 deletions(-) delete mode 100644 packages/pipeline/src/entities/Block.ts delete mode 100644 packages/pipeline/src/entities/Relayer.ts delete mode 100644 packages/pipeline/src/entities/Transaction.ts create mode 100644 packages/pipeline/src/entities/block.ts create mode 100644 packages/pipeline/src/entities/relayer.ts create mode 100644 packages/pipeline/src/entities/transaction.ts diff --git a/packages/pipeline/src/entities/Block.ts b/packages/pipeline/src/entities/Block.ts deleted file mode 100644 index 5bd51f3d2..000000000 --- a/packages/pipeline/src/entities/Block.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Column, Entity, PrimaryColumn } from 'typeorm'; - -@Entity({ name: 'blocks' }) -export class Block { - @PrimaryColumn() public hash!: string; - @PrimaryColumn() public number!: number; - - @Column({ name: 'unix_timestamp_seconds' }) - public unixTimestampSeconds!: number; -} diff --git a/packages/pipeline/src/entities/Relayer.ts b/packages/pipeline/src/entities/Relayer.ts deleted file mode 100644 index 94b5232a8..000000000 --- a/packages/pipeline/src/entities/Relayer.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Column, Entity, PrimaryColumn } from 'typeorm'; - -@Entity({ name: 'relayers' }) -export class Relayer { - @PrimaryColumn() public uuid!: string; - - @Column() public name!: string; - @Column() public url!: string; - @Column({ nullable: true, type: String, name: 'sra_http_endpoint' }) - public sraHttpEndpoint!: string | null; - @Column({ nullable: true, type: String, name: 'sra_ws_endpoint' }) - public sraWsEndpoint!: string | null; - @Column({ nullable: true, type: String, name: 'app_url' }) - public appUrl!: string | null; - - // TODO(albrow): Add exchange contract or protocol version? - // TODO(albrow): Add network ids for addresses? - - @Column({ type: 'varchar', array: true, name: 'fee_recipient_addresses' }) - public feeRecipientAddresses!: string[]; - @Column({ type: 'varchar', array: true, name: 'taker_addresses' }) - public takerAddresses!: string[]; -} diff --git a/packages/pipeline/src/entities/Transaction.ts b/packages/pipeline/src/entities/Transaction.ts deleted file mode 100644 index eb2883fda..000000000 --- a/packages/pipeline/src/entities/Transaction.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Column, Entity, PrimaryColumn } from 'typeorm'; - -@Entity({ name: 'transactions' }) -export class Transaction { - @PrimaryColumn({ name: 'transaction_hash' }) - public transactionHash!: string; - @PrimaryColumn({ name: 'block_hash' }) - public blockHash!: string; - @PrimaryColumn({ name: 'block_number' }) - public blockNumber!: number; - - @Column({ type: 'bigint', name: 'gas_used' }) - public gasUsed!: number; - @Column({ type: 'bigint', name: 'gas_price' }) - public gasPrice!: number; -} diff --git a/packages/pipeline/src/entities/block.ts b/packages/pipeline/src/entities/block.ts new file mode 100644 index 000000000..5bd51f3d2 --- /dev/null +++ b/packages/pipeline/src/entities/block.ts @@ -0,0 +1,10 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +@Entity({ name: 'blocks' }) +export class Block { + @PrimaryColumn() public hash!: string; + @PrimaryColumn() public number!: number; + + @Column({ name: 'unix_timestamp_seconds' }) + public unixTimestampSeconds!: number; +} diff --git a/packages/pipeline/src/entities/relayer.ts b/packages/pipeline/src/entities/relayer.ts new file mode 100644 index 000000000..94b5232a8 --- /dev/null +++ b/packages/pipeline/src/entities/relayer.ts @@ -0,0 +1,23 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +@Entity({ name: 'relayers' }) +export class Relayer { + @PrimaryColumn() public uuid!: string; + + @Column() public name!: string; + @Column() public url!: string; + @Column({ nullable: true, type: String, name: 'sra_http_endpoint' }) + public sraHttpEndpoint!: string | null; + @Column({ nullable: true, type: String, name: 'sra_ws_endpoint' }) + public sraWsEndpoint!: string | null; + @Column({ nullable: true, type: String, name: 'app_url' }) + public appUrl!: string | null; + + // TODO(albrow): Add exchange contract or protocol version? + // TODO(albrow): Add network ids for addresses? + + @Column({ type: 'varchar', array: true, name: 'fee_recipient_addresses' }) + public feeRecipientAddresses!: string[]; + @Column({ type: 'varchar', array: true, name: 'taker_addresses' }) + public takerAddresses!: string[]; +} diff --git a/packages/pipeline/src/entities/transaction.ts b/packages/pipeline/src/entities/transaction.ts new file mode 100644 index 000000000..eb2883fda --- /dev/null +++ b/packages/pipeline/src/entities/transaction.ts @@ -0,0 +1,16 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +@Entity({ name: 'transactions' }) +export class Transaction { + @PrimaryColumn({ name: 'transaction_hash' }) + public transactionHash!: string; + @PrimaryColumn({ name: 'block_hash' }) + public blockHash!: string; + @PrimaryColumn({ name: 'block_number' }) + public blockNumber!: number; + + @Column({ type: 'bigint', name: 'gas_used' }) + public gasUsed!: number; + @Column({ type: 'bigint', name: 'gas_price' }) + public gasPrice!: number; +} -- cgit v1.2.3 From 329c68f610843ebded9ca31fc9cd6f3eed744a8e Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Mon, 12 Nov 2018 16:40:20 -0800 Subject: Configure TypeORM for migrations. Add new package.json scripts. --- packages/pipeline/package.json | 5 ++++- packages/pipeline/src/ormconfig.ts | 25 +++++----------------- .../pipeline/src/scripts/pull_missing_events.ts | 6 +++--- .../pipeline/src/scripts/update_relayer_info.ts | 6 +++--- packages/pipeline/tsconfig.json | 2 +- packages/pipeline/typedoc-tsconfig.json | 7 ++++-- 6 files changed, 21 insertions(+), 30 deletions(-) diff --git a/packages/pipeline/package.json b/packages/pipeline/package.json index 3380a970a..367296e05 100644 --- a/packages/pipeline/package.json +++ b/packages/pipeline/package.json @@ -13,7 +13,10 @@ "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", "coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info", "clean": "shx rm -rf lib", - "lint": "tslint --project ." + "lint": "tslint --project .", + "migrate:run": "yarn typeorm migration:run --config ./lib/src/ormconfig", + "migrate:revert": "yarn typeorm migration:revert --config ./lib/src/ormconfig", + "migrate:create": "yarn typeorm migration:create --config ./lib/src/ormconfig --dir migrations" }, "config": { "postpublish": { diff --git a/packages/pipeline/src/ormconfig.ts b/packages/pipeline/src/ormconfig.ts index 39b496ace..95c27eeba 100644 --- a/packages/pipeline/src/ormconfig.ts +++ b/packages/pipeline/src/ormconfig.ts @@ -20,28 +20,13 @@ const entities = [ Transaction, ]; -export const testConfig: ConnectionOptions = { - type: 'sqlite', - database: 'database.sqlite', - synchronize: true, - logging: false, - entities, - migrations: ['./lib/src/migrations/**/*.js'], - cli: { - entitiesDir: 'lib/src/entities', - migrationsDir: 'lib/src/migrations', - }, -}; - -export const deployConfig: ConnectionOptions = { +const config: ConnectionOptions = { type: 'postgres', url: process.env.ZEROEX_DATA_PIPELINE_DB_URL, - synchronize: true, + synchronize: false, logging: false, entities, - migrations: ['./lib/src/migrations/**/*.js'], - cli: { - entitiesDir: 'lib/src/entities', - migrationsDir: 'lib/src/migrations', - }, + migrations: ['./lib/migrations/**/*.js'], }; + +module.exports = config as ConnectionOptions; diff --git a/packages/pipeline/src/scripts/pull_missing_events.ts b/packages/pipeline/src/scripts/pull_missing_events.ts index cca0d9cfe..e2b312280 100644 --- a/packages/pipeline/src/scripts/pull_missing_events.ts +++ b/packages/pipeline/src/scripts/pull_missing_events.ts @@ -2,11 +2,11 @@ import { web3Factory } from '@0x/dev-utils'; import { Web3ProviderEngine } from '@0x/subproviders'; import R = require('ramda'); import 'reflect-metadata'; -import { Connection, createConnection, Repository } from 'typeorm'; +import { Connection, ConnectionOptions, createConnection, Repository } from 'typeorm'; import { ExchangeEventsSource } from '../data_sources/contract-wrappers/exchange_events'; import { ExchangeFillEvent } from '../entities'; -import { deployConfig } from '../ormconfig'; +import * as ormConfig from '../ormconfig'; import { parseExchangeEvents } from '../parsers/events'; import { handleError } from '../utils'; @@ -17,7 +17,7 @@ const BATCH_SAVE_SIZE = 1000; // Number of events to save at once. let connection: Connection; (async () => { - connection = await createConnection(deployConfig); + connection = await createConnection(ormConfig as ConnectionOptions); const provider = web3Factory.getRpcProvider({ rpcUrl: 'https://mainnet.infura.io', }); diff --git a/packages/pipeline/src/scripts/update_relayer_info.ts b/packages/pipeline/src/scripts/update_relayer_info.ts index 051289992..af9dd726e 100644 --- a/packages/pipeline/src/scripts/update_relayer_info.ts +++ b/packages/pipeline/src/scripts/update_relayer_info.ts @@ -1,9 +1,9 @@ import 'reflect-metadata'; -import { Connection, createConnection } from 'typeorm'; +import { Connection, ConnectionOptions, createConnection } from 'typeorm'; import { RelayerRegistrySource } from '../data_sources/relayer-registry'; import { Relayer } from '../entities'; -import { deployConfig } from '../ormconfig'; +import * as ormConfig from '../ormconfig'; import { parseRelayers } from '../parsers/relayer_registry'; import { handleError } from '../utils'; @@ -15,7 +15,7 @@ const RELAYER_REGISTRY_URL = let connection: Connection; (async () => { - connection = await createConnection(deployConfig); + connection = await createConnection(ormConfig as ConnectionOptions); await getRelayers(); process.exit(0); })().catch(handleError); diff --git a/packages/pipeline/tsconfig.json b/packages/pipeline/tsconfig.json index 918d469ed..6f138f260 100644 --- a/packages/pipeline/tsconfig.json +++ b/packages/pipeline/tsconfig.json @@ -6,5 +6,5 @@ "emitDecoratorMetadata": true, "experimentalDecorators": true }, - "include": ["./src/**/*", "./test/**/*"] + "include": ["./src/**/*", "./test/**/*", "./migrations/**/*"] } diff --git a/packages/pipeline/typedoc-tsconfig.json b/packages/pipeline/typedoc-tsconfig.json index b9c6b36f3..8b0ff51c1 100644 --- a/packages/pipeline/typedoc-tsconfig.json +++ b/packages/pipeline/typedoc-tsconfig.json @@ -1,7 +1,10 @@ { "extends": "../../typedoc-tsconfig", "compilerOptions": { - "outDir": "lib" + "outDir": "lib", + "rootDir": ".", + "emitDecoratorMetadata": true, + "experimentalDecorators": true }, - "include": ["src/**/*", "test/**/*"] + "include": ["./src/**/*", "./test/**/*", "./migrations/**/*"] } -- cgit v1.2.3 From 688d277b30b287f66f0dbd49f2a23cab8b256219 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Mon, 12 Nov 2018 17:36:33 -0800 Subject: Configure linter with --format stylish and fix linter errors --- packages/pipeline/package.json | 2 +- .../contract-wrappers/exchange_events.ts | 4 +-- .../src/data_sources/relayer-registry/index.ts | 2 +- packages/pipeline/src/data_sources/web3/index.ts | 4 +-- packages/pipeline/src/ormconfig.ts | 2 +- packages/pipeline/src/parsers/events/index.ts | 34 ++++++++++++++++++++++ .../pipeline/src/parsers/relayer_registry/index.ts | 4 +++ packages/pipeline/src/parsers/sra_orders/index.ts | 12 ++++++++ packages/pipeline/src/parsers/web3/index.ts | 8 +++++ .../pipeline/src/scripts/pull_missing_events.ts | 1 + .../pipeline/src/scripts/update_relayer_info.ts | 1 + packages/pipeline/src/utils/index.ts | 14 +++++++++ 12 files changed, 81 insertions(+), 7 deletions(-) diff --git a/packages/pipeline/package.json b/packages/pipeline/package.json index 367296e05..59cf24dc6 100644 --- a/packages/pipeline/package.json +++ b/packages/pipeline/package.json @@ -13,7 +13,7 @@ "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", "coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info", "clean": "shx rm -rf lib", - "lint": "tslint --project .", + "lint": "tslint --project . --format stylish --exclude ./migrations", "migrate:run": "yarn typeorm migration:run --config ./lib/src/ormconfig", "migrate:revert": "yarn typeorm migration:revert --config ./lib/src/ormconfig", "migrate:create": "yarn typeorm migration:create --config ./lib/src/ormconfig --dir migrations" diff --git a/packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts b/packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts index 5ce8381cd..e25c6a731 100644 --- a/packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts +++ b/packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts @@ -8,8 +8,8 @@ const NUM_BLOCKS_PER_QUERY = 100000; // Number of blocks to query for events at const EXCHANGE_START_BLOCK = 6271590; // Block number when the Exchange contract was deployed to mainnet. export class ExchangeEventsSource { - private _exchangeWrapper: ExchangeWrapper; - private _web3Wrapper: Web3Wrapper; + private readonly _exchangeWrapper: ExchangeWrapper; + private readonly _web3Wrapper: Web3Wrapper; constructor(provider: Web3ProviderEngine, networkId: number) { this._web3Wrapper = new Web3Wrapper(provider); const contractWrappers = new ContractWrappers(provider, { networkId }); diff --git a/packages/pipeline/src/data_sources/relayer-registry/index.ts b/packages/pipeline/src/data_sources/relayer-registry/index.ts index 8199dae14..8133f5eae 100644 --- a/packages/pipeline/src/data_sources/relayer-registry/index.ts +++ b/packages/pipeline/src/data_sources/relayer-registry/index.ts @@ -20,7 +20,7 @@ export interface RelayerResponseNetwork { } export class RelayerRegistrySource { - private _url: string; + private readonly _url: string; constructor(url: string) { this._url = url; diff --git a/packages/pipeline/src/data_sources/web3/index.ts b/packages/pipeline/src/data_sources/web3/index.ts index 64e909939..45a9ea161 100644 --- a/packages/pipeline/src/data_sources/web3/index.ts +++ b/packages/pipeline/src/data_sources/web3/index.ts @@ -3,7 +3,7 @@ import { Web3Wrapper } from '@0x/web3-wrapper'; import { BlockWithoutTransactionData, Transaction } from 'ethereum-types'; export class Web3Source { - private _web3Wrapper: Web3Wrapper; + private readonly _web3Wrapper: Web3Wrapper; constructor(provider: Web3ProviderEngine) { this._web3Wrapper = new Web3Wrapper(provider); } @@ -11,7 +11,7 @@ export class Web3Source { public async getBlockInfoAsync(blockNumber: number): Promise { const block = await this._web3Wrapper.getBlockIfExistsAsync(blockNumber); if (block == null) { - return Promise.reject(new Error('Could not find block for given block number: ' + blockNumber)); + return Promise.reject(new Error(`Could not find block for given block number: ${blockNumber}`)); } return block; } diff --git a/packages/pipeline/src/ormconfig.ts b/packages/pipeline/src/ormconfig.ts index 95c27eeba..2fb6b3d3c 100644 --- a/packages/pipeline/src/ormconfig.ts +++ b/packages/pipeline/src/ormconfig.ts @@ -29,4 +29,4 @@ const config: ConnectionOptions = { migrations: ['./lib/migrations/**/*.js'], }; -module.exports = config as ConnectionOptions; +module.exports = config; diff --git a/packages/pipeline/src/parsers/events/index.ts b/packages/pipeline/src/parsers/events/index.ts index 7a0ead0d4..d56dc7080 100644 --- a/packages/pipeline/src/parsers/events/index.ts +++ b/packages/pipeline/src/parsers/events/index.ts @@ -18,19 +18,38 @@ export const parseExchangeEvents: ( eventLogs: Array>, ) => ExchangeEventEntity[] = R.map(_convertToEntity); +/** + * Converts a raw event log to an Entity. Automatically detects the type of + * event and returns the appropriate entity type. Throws for unknown event + * types. + * @param eventLog Raw event log (e.g. returned from contract-wrappers). + */ export function _convertToEntity(eventLog: LogWithDecodedArgs): ExchangeEventEntity { switch (eventLog.event) { case 'Fill': + // tslint has a false positive here. We need to type assert in order + // to change the type argument to the more specific + // ExchangeFillEventArgs. + // tslint:disable-next-line:no-unnecessary-type-assertion return _convertToExchangeFillEvent(eventLog as LogWithDecodedArgs); case 'Cancel': + // tslint:disable-next-line:no-unnecessary-type-assertion return _convertToExchangeCancelEvent(eventLog as LogWithDecodedArgs); case 'CancelUpTo': + // tslint:disable-next-line:no-unnecessary-type-assertion return _convertToExchangeCancelUpToEvent(eventLog as LogWithDecodedArgs); default: + // Another false positive here. We are adding two strings, but + // tslint seems confused about the types. + // tslint:disable-next-line:restrict-plus-operands throw new Error('unexpected eventLog.event type: ' + eventLog.event); } } +/** + * Converts a raw event log for a fill event into an ExchangeFillEvent entity. + * @param eventLog Raw event log (e.g. returned from contract-wrappers). + */ export function _convertToExchangeFillEvent(eventLog: LogWithDecodedArgs): ExchangeFillEvent { const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.makerAssetData); const makerAssetType = makerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; @@ -55,15 +74,23 @@ export function _convertToExchangeFillEvent(eventLog: LogWithDecodedArgs, ): ExchangeCancelEvent { @@ -86,15 +113,22 @@ export function _convertToExchangeCancelEvent( exchangeCancelEvent.makerAssetType = makerAssetType; exchangeCancelEvent.makerAssetProxyId = makerAssetData.assetProxyId; exchangeCancelEvent.makerTokenAddress = makerAssetData.tokenAddress; + // tslint:disable-next-line:no-unnecessary-type-assertion exchangeCancelEvent.makerTokenId = bigNumbertoStringOrNull((makerAssetData as ERC721AssetData).tokenId); exchangeCancelEvent.rawTakerAssetData = eventLog.args.takerAssetData; exchangeCancelEvent.takerAssetType = takerAssetType; exchangeCancelEvent.takerAssetProxyId = takerAssetData.assetProxyId; exchangeCancelEvent.takerTokenAddress = takerAssetData.tokenAddress; + // tslint:disable-next-line:no-unnecessary-type-assertion exchangeCancelEvent.takerTokenId = bigNumbertoStringOrNull((takerAssetData as ERC721AssetData).tokenId); return exchangeCancelEvent; } +/** + * Converts a raw event log for a cancelUpTo event into an + * ExchangeCancelUpToEvent entity. + * @param eventLog Raw event log (e.g. returned from contract-wrappers). + */ export function _convertToExchangeCancelUpToEvent( eventLog: LogWithDecodedArgs, ): ExchangeCancelUpToEvent { diff --git a/packages/pipeline/src/parsers/relayer_registry/index.ts b/packages/pipeline/src/parsers/relayer_registry/index.ts index a61f4e62a..346f570bd 100644 --- a/packages/pipeline/src/parsers/relayer_registry/index.ts +++ b/packages/pipeline/src/parsers/relayer_registry/index.ts @@ -3,6 +3,10 @@ import * as R from 'ramda'; import { RelayerResponse, RelayerResponseNetwork } from '../../data_sources/relayer-registry'; import { Relayer } from '../../entities'; +/** + * Parses a raw relayer registry response into an array of Relayer entities. + * @param rawResp raw response from the relayer-registry json file. + */ export function parseRelayers(rawResp: Map): Relayer[] { const parsedAsObject = R.mapObjIndexed(parseRelayer, rawResp); return R.values(parsedAsObject); diff --git a/packages/pipeline/src/parsers/sra_orders/index.ts b/packages/pipeline/src/parsers/sra_orders/index.ts index 4e4bf12ff..3d7f73fca 100644 --- a/packages/pipeline/src/parsers/sra_orders/index.ts +++ b/packages/pipeline/src/parsers/sra_orders/index.ts @@ -6,10 +6,19 @@ import * as R from 'ramda'; import { SraOrder } from '../../entities'; import { bigNumbertoStringOrNull } from '../../utils'; +/** + * Parses a raw order response from an SRA endpoint and returns an array of + * SraOrder entities. + * @param rawOrdersResponse A raw order response from an SRA endpoint. + */ export function parseSraOrders(rawOrdersResponse: OrdersResponse): SraOrder[] { return R.map(_convertToEntity, rawOrdersResponse.records); } +/** + * Converts a single APIOrder into an SraOrder entity. + * @param apiOrder A single order from the response from an SRA endpoint. + */ export function _convertToEntity(apiOrder: APIOrder): SraOrder { // TODO(albrow): refactor out common asset data decoding code. const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(apiOrder.order.makerAssetData); @@ -41,11 +50,14 @@ export function _convertToEntity(apiOrder: APIOrder): SraOrder { sraOrder.makerAssetType = makerAssetType; sraOrder.makerAssetProxyId = makerAssetData.assetProxyId; sraOrder.makerTokenAddress = makerAssetData.tokenAddress; + // tslint has a false positive here. Type assertion is required. + // tslint:disable-next-line:no-unnecessary-type-assertion sraOrder.makerTokenId = bigNumbertoStringOrNull((makerAssetData as ERC721AssetData).tokenId); sraOrder.rawTakerAssetData = apiOrder.order.takerAssetData; sraOrder.takerAssetType = takerAssetType; sraOrder.takerAssetProxyId = takerAssetData.assetProxyId; sraOrder.takerTokenAddress = takerAssetData.tokenAddress; + // tslint:disable-next-line:no-unnecessary-type-assertion sraOrder.takerTokenId = bigNumbertoStringOrNull((takerAssetData as ERC721AssetData).tokenId); sraOrder.metadataJson = JSON.stringify(apiOrder.metaData); diff --git a/packages/pipeline/src/parsers/web3/index.ts b/packages/pipeline/src/parsers/web3/index.ts index 11571278b..2ead4c0e4 100644 --- a/packages/pipeline/src/parsers/web3/index.ts +++ b/packages/pipeline/src/parsers/web3/index.ts @@ -2,6 +2,10 @@ import { BlockWithoutTransactionData, Transaction as EthTransaction } from 'ethe import { Block, Transaction } from '../../entities'; +/** + * Parses a raw block and returns a Block entity. + * @param rawBlock a raw block (e.g. returned from web3-wrapper). + */ export function parseBlock(rawBlock: BlockWithoutTransactionData): Block { if (rawBlock.hash == null) { throw new Error('Tried to parse raw block but hash was null'); @@ -17,6 +21,10 @@ export function parseBlock(rawBlock: BlockWithoutTransactionData): Block { return block; } +/** + * Parses a raw transaction and returns a Transaction entity. + * @param rawBlock a raw transaction (e.g. returned from web3-wrapper). + */ export function parseTransaction(rawTransaction: EthTransaction): Transaction { if (rawTransaction.blockHash == null) { throw new Error('Tried to parse raw transaction but blockHash was null'); diff --git a/packages/pipeline/src/scripts/pull_missing_events.ts b/packages/pipeline/src/scripts/pull_missing_events.ts index e2b312280..bc0eac853 100644 --- a/packages/pipeline/src/scripts/pull_missing_events.ts +++ b/packages/pipeline/src/scripts/pull_missing_events.ts @@ -1,3 +1,4 @@ +// tslint:disable:no-console import { web3Factory } from '@0x/dev-utils'; import { Web3ProviderEngine } from '@0x/subproviders'; import R = require('ramda'); diff --git a/packages/pipeline/src/scripts/update_relayer_info.ts b/packages/pipeline/src/scripts/update_relayer_info.ts index af9dd726e..f8918728d 100644 --- a/packages/pipeline/src/scripts/update_relayer_info.ts +++ b/packages/pipeline/src/scripts/update_relayer_info.ts @@ -1,3 +1,4 @@ +// tslint:disable:no-console import 'reflect-metadata'; import { Connection, ConnectionOptions, createConnection } from 'typeorm'; diff --git a/packages/pipeline/src/utils/index.ts b/packages/pipeline/src/utils/index.ts index 4242e0437..a083cd8c9 100644 --- a/packages/pipeline/src/utils/index.ts +++ b/packages/pipeline/src/utils/index.ts @@ -1,5 +1,10 @@ import { BigNumber } from '@0x/utils'; +/** + * If the given BigNumber is not null, returns the string representation of that + * number. Otherwise, returns null. + * @param n The number to convert. + */ export function bigNumbertoStringOrNull(n: BigNumber): string | null { if (n == null) { return null; @@ -7,15 +12,24 @@ export function bigNumbertoStringOrNull(n: BigNumber): string | null { return n.toString(); } +/** + * Logs an error by intelligently checking for `message` and `stack` properties. + * Intended for use with top-level immediately invoked asynchronous functions. + * @param e the error to log. + */ export function handleError(e: any): void { if (e.message != null) { + // tslint:disable-next-line:no-console console.error(e.message); } else { + // tslint:disable-next-line:no-console console.error('Unknown error'); } if (e.stack != null) { + // tslint:disable-next-line:no-console console.error(e.stack); } else { + // tslint:disable-next-line:no-console console.error('(No stack trace)'); } process.exit(1); -- cgit v1.2.3 From 599a3f9b9614be6db1c9979514f3acca3f48bd05 Mon Sep 17 00:00:00 2001 From: Jake Ellowitz Date: Tue, 13 Nov 2018 09:38:23 -0800 Subject: Mapping dev schema to initial migration --- .../migrations/1542070840010-InitialSchema.ts | 190 +++++++++++++++++++++ .../src/entities/exchange_cancel_up_to_event.ts | 2 +- packages/pipeline/src/entities/index.ts | 1 + .../src/entities/token_on_chain_metadata.ts | 16 ++ 4 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 packages/pipeline/migrations/1542070840010-InitialSchema.ts create mode 100644 packages/pipeline/src/entities/token_on_chain_metadata.ts diff --git a/packages/pipeline/migrations/1542070840010-InitialSchema.ts b/packages/pipeline/migrations/1542070840010-InitialSchema.ts new file mode 100644 index 000000000..cf240a1ed --- /dev/null +++ b/packages/pipeline/migrations/1542070840010-InitialSchema.ts @@ -0,0 +1,190 @@ +import {MigrationInterface, QueryRunner, Table} from "typeorm"; + +const blocks = new Table({ + name: 'raw.blocks', + columns: [ + {name: 'number', type: 'bigint', isPrimary: true}, + {name: 'hash', type: 'varchar', isPrimary: true}, + {name: 'timestamp', type: 'bigint'} + ] +}); + +const exchange_cancel_events = new Table({ + name: 'raw.exchange_cancel_events', + columns: [ + {name: 'contract_address', type: 'char(42)', isPrimary: true}, + {name: 'log_index', type: 'integer', isPrimary: true}, + {name: 'block_number', type: 'bigint', isPrimary: true}, + + {name: 'raw_data', type: 'varchar'}, + + {name: 'transaction_hash', type: 'varchar'}, + {name: 'maker_address', type: 'char(42)'}, + {name: 'taker_address', type: 'char(42)'}, + {name: 'fee_recipient_address', type: 'char(42)'}, + {name: 'sender_address', type: 'char(42)'}, + {name: 'order_hash', type: 'varchar'}, + + {name: 'raw_maker_asset_data', type: 'varchar'}, + {name: 'maker_asset_type', type: 'varchar'}, + {name: 'maker_asset_proxy_id', type: 'varchar'}, + {name: 'maker_token_address', type: 'char(42)'}, + {name: 'maker_token_id', type: 'varchar', isNullable: true}, + {name: 'raw_taker_asset_data', type: 'varchar'}, + {name: 'taker_asset_type', type: 'varchar'}, + {name: 'taker_asset_proxy_id', type: 'varchar'}, + {name: 'taker_token_address', type: 'char(42)'}, + {name: 'taker_token_id', type: 'varchar', isNullable: true} + ] +}); + +const exchange_cancel_up_to_events = new Table({ + name: 'raw.exchange_cancel_up_to_events', + columns: [ + {name: 'contract_address', type: 'char(42)', isPrimary: true}, + {name: 'log_index', type: 'integer', isPrimary: true}, + {name: 'block_number', type: 'bigint', isPrimary: true}, + + {name: 'raw_data', type: 'varchar'}, + + {name: 'transaction_hash', type: 'varchar'}, + {name: 'maker_address', type: 'char(42)'}, + {name: 'sender_address', type: 'char(42)'}, + {name: 'order_epoch', type: 'varchar'} + ] +}); + +const exchange_fill_events = new Table({ + name: 'raw.exchange_fill_events', + columns: [ + {name: 'contract_address', type: 'char(42)', isPrimary: true}, + {name: 'log_index', type: 'integer', isPrimary: true}, + {name: 'block_number', type: 'bigint', isPrimary: true}, + + {name: 'raw_data', type: 'varchar'}, + + {name: 'transaction_hash', type: 'varchar'}, + {name: 'maker_address', type: 'char(42)'}, + {name: 'taker_address', type: 'char(42)'}, + {name: 'fee_recipient_address', type: 'char(42)'}, + {name: 'sender_address', type: 'char(42)'}, + {name: 'maker_asset_filled_amount', type: 'varchar'}, + {name: 'taker_asset_filled_amount', type: 'varchar'}, + {name: 'maker_fee_paid', type: 'varchar'}, + {name: 'taker_fee_paid', type: 'varchar'}, + {name: 'order_hash', type: 'varchar'}, + + {name: 'raw_maker_asset_data', type: 'varchar'}, + {name: 'maker_asset_type', type: 'varchar'}, + {name: 'maker_asset_proxy_id', type: 'varchar'}, + {name: 'maker_token_address', type: 'char(42)'}, + {name: 'maker_token_id', type: 'varchar', isNullable: true}, + {name: 'raw_taker_asset_data', type: 'varchar'}, + {name: 'taker_asset_type', type: 'varchar'}, + {name: 'taker_asset_proxy_id', type: 'varchar'}, + {name: 'taker_token_address', type: 'char(42)'}, + {name: 'taker_token_id', type: 'varchar', isNullable: true} + ] +}); + +const relayers = new Table({ + name: 'raw.relayers', + columns: [ + {name: 'uuid', type: 'varchar', isPrimary: true}, + {name: 'name', type: 'varchar'}, + {name: 'sra_http_endpoint', type: 'varchar', isNullable: true}, + {name: 'sra_ws_endpoint', type: 'varchar', isNullable: true}, + {name: 'app_url', type: 'varchar', isNullable: true}, + {name: 'fee_recipient_addresses', type: 'char(42)', isArray: true}, + {name: 'taker_addresses', type: 'char(42)', isArray: true} + ] +}); + +const sra_orders = new Table({ + name: 'raw.sra_orders', + columns: [ + {name: 'exchange_address', type: 'char(42)', isPrimary: true}, + {name: 'order_hash_hex', type: 'varchar', isPrimary: true}, + + {name: 'source_url', type: 'varchar'}, + {name: 'last_updated_timestamp', type: 'bigint'}, + {name: 'first_seen_timestamp', type: 'bigint'}, + + {name: 'maker_address', type: 'char(42)'}, + {name: 'taker_address', type: 'char(42)'}, + {name: 'fee_recipient_address', type: 'char(42)'}, + {name: 'sender_address', type: 'char(42)'}, + {name: 'maker_asset_filled_amount', type: 'varchar'}, + {name: 'taker_asset_filled_amount', type: 'varchar'}, + {name: 'maker_fee', type: 'varchar'}, + {name: 'taker_fee', type: 'varchar'}, + {name: 'expiration_time_seconds', type: 'int'}, + {name: 'salt', type: 'varchar'}, + {name: 'signature', type: 'varchar'}, + + {name: 'raw_maker_asset_data', type: 'varchar'}, + {name: 'maker_asset_type', type: 'varchar'}, + {name: 'maker_asset_proxy_id', type: 'varchar'}, + {name: 'maker_token_address', type: 'char(42)'}, + {name: 'maker_token_id', type: 'varchar', isNullable: true}, + {name: 'raw_taker_asset_data', type: 'varchar'}, + {name: 'taker_asset_type', type: 'varchar'}, + {name: 'taker_asset_proxy_id', type: 'varchar'}, + {name: 'taker_token_address', type: 'char(42)'}, + {name: 'taker_token_id', type: 'varchar', isNullable: true}, + + {name: 'metadata_json', type: 'varchar'} + ] +}); + +const token_on_chain_metadata = new Table({ + name: 'raw.token_on_chain_metadata', + columns: [ + {name: 'address', type: 'char(42)', isPrimary: true}, + {name: 'decimals', type: 'integer'}, + {name: 'symbol', type: 'varchar'}, + {name: 'name', type: 'varchar'} + ] +}); + +const transactions = new Table({ + name: 'raw.transactions', + columns: [ + {name: 'block_number', type: 'bigint', isPrimary: true}, + {name: 'block_hash', type: 'varchar', isPrimary: true}, + {name: 'transaction_hash', type: 'varchar', isPrimary: true}, + {name: 'gas_used', type: 'bigint'}, + {name: 'gas_price', type: 'bigint'} + ] +}); + +export class InitialSchema1542070840010 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + + await queryRunner.createSchema('raw'); + + await queryRunner.createTable(blocks); + await queryRunner.createTable(exchange_cancel_events); + await queryRunner.createTable(exchange_cancel_up_to_events); + await queryRunner.createTable(exchange_fill_events); + await queryRunner.createTable(relayers); + await queryRunner.createTable(sra_orders); + await queryRunner.createTable(token_on_chain_metadata); + await queryRunner.createTable(transactions); + } + + public async down(queryRunner: QueryRunner): Promise { + + await queryRunner.dropTable(blocks.name); + await queryRunner.dropTable(exchange_cancel_events.name); + await queryRunner.dropTable(exchange_cancel_up_to_events.name); + await queryRunner.dropTable(exchange_fill_events.name); + await queryRunner.dropTable(relayers.name); + await queryRunner.dropTable(sra_orders.name); + await queryRunner.dropTable(token_on_chain_metadata.name); + await queryRunner.dropTable(transactions.name); + + await queryRunner.dropSchema('raw'); + } +} diff --git a/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts b/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts index 891550050..bc3bab6bb 100644 --- a/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts +++ b/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts @@ -1,6 +1,6 @@ import { Column, Entity, PrimaryColumn } from 'typeorm'; -@Entity({ name: 'exchange_cancel_up_to_event' }) +@Entity({ name: 'exchange_cancel_up_to_events' }) export class ExchangeCancelUpToEvent { @PrimaryColumn({ name: 'contract_address' }) public contractAddress!: string; diff --git a/packages/pipeline/src/entities/index.ts b/packages/pipeline/src/entities/index.ts index 4cd8d9da2..6e024f280 100644 --- a/packages/pipeline/src/entities/index.ts +++ b/packages/pipeline/src/entities/index.ts @@ -5,3 +5,4 @@ export { ExchangeFillEvent } from './exchange_fill_event'; export { Relayer } from './relayer'; export { SraOrder } from './sra_order'; export { Transaction } from './transaction'; +export { TokenOnChainMetadata } from './token_on_chain_metadata'; diff --git a/packages/pipeline/src/entities/token_on_chain_metadata.ts b/packages/pipeline/src/entities/token_on_chain_metadata.ts new file mode 100644 index 000000000..fc21d0aed --- /dev/null +++ b/packages/pipeline/src/entities/token_on_chain_metadata.ts @@ -0,0 +1,16 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +@Entity({ name: 'token_on_chain_metadata' }) +export class TokenOnChainMetadata { + @PrimaryColumn({ type: 'nvarchar', nullable: false }) + public address!: string; + + @Column({ type: 'integer', nullable: false }) + public decimals!: number; + + @Column({ type: 'nvarchar', nullable: false }) + public symbol!: string; + + @Column({ type: 'nvarchar', nullable: false }) + public name!: string; +} \ No newline at end of file -- cgit v1.2.3 From 96134003e1aa1903bdf051e39e7e244cc8684043 Mon Sep 17 00:00:00 2001 From: Jake Ellowitz Date: Tue, 13 Nov 2018 09:57:02 -0800 Subject: Pointing entities to raw schema Fix linter issues --- .../migrations/1542070840010-InitialSchema.ts | 241 ++++++++++----------- packages/pipeline/package.json | 2 +- packages/pipeline/src/entities/block.ts | 2 +- .../pipeline/src/entities/exchange_cancel_event.ts | 2 +- .../src/entities/exchange_cancel_up_to_event.ts | 2 +- .../pipeline/src/entities/exchange_fill_event.ts | 2 +- packages/pipeline/src/entities/relayer.ts | 2 +- packages/pipeline/src/entities/sra_order.ts | 2 +- .../src/entities/token_on_chain_metadata.ts | 4 +- packages/pipeline/src/entities/transaction.ts | 2 +- 10 files changed, 129 insertions(+), 132 deletions(-) diff --git a/packages/pipeline/migrations/1542070840010-InitialSchema.ts b/packages/pipeline/migrations/1542070840010-InitialSchema.ts index cf240a1ed..895f9e6c9 100644 --- a/packages/pipeline/migrations/1542070840010-InitialSchema.ts +++ b/packages/pipeline/migrations/1542070840010-InitialSchema.ts @@ -1,167 +1,165 @@ -import {MigrationInterface, QueryRunner, Table} from "typeorm"; +import { MigrationInterface, QueryRunner, Table } from 'typeorm'; const blocks = new Table({ name: 'raw.blocks', columns: [ - {name: 'number', type: 'bigint', isPrimary: true}, - {name: 'hash', type: 'varchar', isPrimary: true}, - {name: 'timestamp', type: 'bigint'} - ] + { name: 'number', type: 'bigint', isPrimary: true }, + { name: 'hash', type: 'varchar', isPrimary: true }, + { name: 'timestamp', type: 'bigint' }, + ], }); const exchange_cancel_events = new Table({ name: 'raw.exchange_cancel_events', columns: [ - {name: 'contract_address', type: 'char(42)', isPrimary: true}, - {name: 'log_index', type: 'integer', isPrimary: true}, - {name: 'block_number', type: 'bigint', isPrimary: true}, - - {name: 'raw_data', type: 'varchar'}, - - {name: 'transaction_hash', type: 'varchar'}, - {name: 'maker_address', type: 'char(42)'}, - {name: 'taker_address', type: 'char(42)'}, - {name: 'fee_recipient_address', type: 'char(42)'}, - {name: 'sender_address', type: 'char(42)'}, - {name: 'order_hash', type: 'varchar'}, - - {name: 'raw_maker_asset_data', type: 'varchar'}, - {name: 'maker_asset_type', type: 'varchar'}, - {name: 'maker_asset_proxy_id', type: 'varchar'}, - {name: 'maker_token_address', type: 'char(42)'}, - {name: 'maker_token_id', type: 'varchar', isNullable: true}, - {name: 'raw_taker_asset_data', type: 'varchar'}, - {name: 'taker_asset_type', type: 'varchar'}, - {name: 'taker_asset_proxy_id', type: 'varchar'}, - {name: 'taker_token_address', type: 'char(42)'}, - {name: 'taker_token_id', type: 'varchar', isNullable: true} - ] + { name: 'contract_address', type: 'char(42)', isPrimary: true }, + { name: 'log_index', type: 'integer', isPrimary: true }, + { name: 'block_number', type: 'bigint', isPrimary: true }, + + { name: 'raw_data', type: 'varchar' }, + + { name: 'transaction_hash', type: 'varchar' }, + { name: 'maker_address', type: 'char(42)' }, + { name: 'taker_address', type: 'char(42)' }, + { name: 'fee_recipient_address', type: 'char(42)' }, + { name: 'sender_address', type: 'char(42)' }, + { name: 'order_hash', type: 'varchar' }, + + { name: 'raw_maker_asset_data', type: 'varchar' }, + { name: 'maker_asset_type', type: 'varchar' }, + { name: 'maker_asset_proxy_id', type: 'varchar' }, + { name: 'maker_token_address', type: 'char(42)' }, + { name: 'maker_token_id', type: 'varchar', isNullable: true }, + { name: 'raw_taker_asset_data', type: 'varchar' }, + { name: 'taker_asset_type', type: 'varchar' }, + { name: 'taker_asset_proxy_id', type: 'varchar' }, + { name: 'taker_token_address', type: 'char(42)' }, + { name: 'taker_token_id', type: 'varchar', isNullable: true }, + ], }); const exchange_cancel_up_to_events = new Table({ name: 'raw.exchange_cancel_up_to_events', columns: [ - {name: 'contract_address', type: 'char(42)', isPrimary: true}, - {name: 'log_index', type: 'integer', isPrimary: true}, - {name: 'block_number', type: 'bigint', isPrimary: true}, - - {name: 'raw_data', type: 'varchar'}, - - {name: 'transaction_hash', type: 'varchar'}, - {name: 'maker_address', type: 'char(42)'}, - {name: 'sender_address', type: 'char(42)'}, - {name: 'order_epoch', type: 'varchar'} - ] + { name: 'contract_address', type: 'char(42)', isPrimary: true }, + { name: 'log_index', type: 'integer', isPrimary: true }, + { name: 'block_number', type: 'bigint', isPrimary: true }, + + { name: 'raw_data', type: 'varchar' }, + + { name: 'transaction_hash', type: 'varchar' }, + { name: 'maker_address', type: 'char(42)' }, + { name: 'sender_address', type: 'char(42)' }, + { name: 'order_epoch', type: 'varchar' }, + ], }); const exchange_fill_events = new Table({ name: 'raw.exchange_fill_events', columns: [ - {name: 'contract_address', type: 'char(42)', isPrimary: true}, - {name: 'log_index', type: 'integer', isPrimary: true}, - {name: 'block_number', type: 'bigint', isPrimary: true}, - - {name: 'raw_data', type: 'varchar'}, - - {name: 'transaction_hash', type: 'varchar'}, - {name: 'maker_address', type: 'char(42)'}, - {name: 'taker_address', type: 'char(42)'}, - {name: 'fee_recipient_address', type: 'char(42)'}, - {name: 'sender_address', type: 'char(42)'}, - {name: 'maker_asset_filled_amount', type: 'varchar'}, - {name: 'taker_asset_filled_amount', type: 'varchar'}, - {name: 'maker_fee_paid', type: 'varchar'}, - {name: 'taker_fee_paid', type: 'varchar'}, - {name: 'order_hash', type: 'varchar'}, - - {name: 'raw_maker_asset_data', type: 'varchar'}, - {name: 'maker_asset_type', type: 'varchar'}, - {name: 'maker_asset_proxy_id', type: 'varchar'}, - {name: 'maker_token_address', type: 'char(42)'}, - {name: 'maker_token_id', type: 'varchar', isNullable: true}, - {name: 'raw_taker_asset_data', type: 'varchar'}, - {name: 'taker_asset_type', type: 'varchar'}, - {name: 'taker_asset_proxy_id', type: 'varchar'}, - {name: 'taker_token_address', type: 'char(42)'}, - {name: 'taker_token_id', type: 'varchar', isNullable: true} - ] + { name: 'contract_address', type: 'char(42)', isPrimary: true }, + { name: 'log_index', type: 'integer', isPrimary: true }, + { name: 'block_number', type: 'bigint', isPrimary: true }, + + { name: 'raw_data', type: 'varchar' }, + + { name: 'transaction_hash', type: 'varchar' }, + { name: 'maker_address', type: 'char(42)' }, + { name: 'taker_address', type: 'char(42)' }, + { name: 'fee_recipient_address', type: 'char(42)' }, + { name: 'sender_address', type: 'char(42)' }, + { name: 'maker_asset_filled_amount', type: 'varchar' }, + { name: 'taker_asset_filled_amount', type: 'varchar' }, + { name: 'maker_fee_paid', type: 'varchar' }, + { name: 'taker_fee_paid', type: 'varchar' }, + { name: 'order_hash', type: 'varchar' }, + + { name: 'raw_maker_asset_data', type: 'varchar' }, + { name: 'maker_asset_type', type: 'varchar' }, + { name: 'maker_asset_proxy_id', type: 'varchar' }, + { name: 'maker_token_address', type: 'char(42)' }, + { name: 'maker_token_id', type: 'varchar', isNullable: true }, + { name: 'raw_taker_asset_data', type: 'varchar' }, + { name: 'taker_asset_type', type: 'varchar' }, + { name: 'taker_asset_proxy_id', type: 'varchar' }, + { name: 'taker_token_address', type: 'char(42)' }, + { name: 'taker_token_id', type: 'varchar', isNullable: true }, + ], }); const relayers = new Table({ name: 'raw.relayers', columns: [ - {name: 'uuid', type: 'varchar', isPrimary: true}, - {name: 'name', type: 'varchar'}, - {name: 'sra_http_endpoint', type: 'varchar', isNullable: true}, - {name: 'sra_ws_endpoint', type: 'varchar', isNullable: true}, - {name: 'app_url', type: 'varchar', isNullable: true}, - {name: 'fee_recipient_addresses', type: 'char(42)', isArray: true}, - {name: 'taker_addresses', type: 'char(42)', isArray: true} - ] + { name: 'uuid', type: 'varchar', isPrimary: true }, + { name: 'name', type: 'varchar' }, + { name: 'sra_http_endpoint', type: 'varchar', isNullable: true }, + { name: 'sra_ws_endpoint', type: 'varchar', isNullable: true }, + { name: 'app_url', type: 'varchar', isNullable: true }, + { name: 'fee_recipient_addresses', type: 'char(42)', isArray: true }, + { name: 'taker_addresses', type: 'char(42)', isArray: true }, + ], }); const sra_orders = new Table({ name: 'raw.sra_orders', columns: [ - {name: 'exchange_address', type: 'char(42)', isPrimary: true}, - {name: 'order_hash_hex', type: 'varchar', isPrimary: true}, - - {name: 'source_url', type: 'varchar'}, - {name: 'last_updated_timestamp', type: 'bigint'}, - {name: 'first_seen_timestamp', type: 'bigint'}, - - {name: 'maker_address', type: 'char(42)'}, - {name: 'taker_address', type: 'char(42)'}, - {name: 'fee_recipient_address', type: 'char(42)'}, - {name: 'sender_address', type: 'char(42)'}, - {name: 'maker_asset_filled_amount', type: 'varchar'}, - {name: 'taker_asset_filled_amount', type: 'varchar'}, - {name: 'maker_fee', type: 'varchar'}, - {name: 'taker_fee', type: 'varchar'}, - {name: 'expiration_time_seconds', type: 'int'}, - {name: 'salt', type: 'varchar'}, - {name: 'signature', type: 'varchar'}, - - {name: 'raw_maker_asset_data', type: 'varchar'}, - {name: 'maker_asset_type', type: 'varchar'}, - {name: 'maker_asset_proxy_id', type: 'varchar'}, - {name: 'maker_token_address', type: 'char(42)'}, - {name: 'maker_token_id', type: 'varchar', isNullable: true}, - {name: 'raw_taker_asset_data', type: 'varchar'}, - {name: 'taker_asset_type', type: 'varchar'}, - {name: 'taker_asset_proxy_id', type: 'varchar'}, - {name: 'taker_token_address', type: 'char(42)'}, - {name: 'taker_token_id', type: 'varchar', isNullable: true}, - - {name: 'metadata_json', type: 'varchar'} - ] + { name: 'exchange_address', type: 'char(42)', isPrimary: true }, + { name: 'order_hash_hex', type: 'varchar', isPrimary: true }, + + { name: 'source_url', type: 'varchar' }, + { name: 'last_updated_timestamp', type: 'bigint' }, + { name: 'first_seen_timestamp', type: 'bigint' }, + + { name: 'maker_address', type: 'char(42)' }, + { name: 'taker_address', type: 'char(42)' }, + { name: 'fee_recipient_address', type: 'char(42)' }, + { name: 'sender_address', type: 'char(42)' }, + { name: 'maker_asset_filled_amount', type: 'varchar' }, + { name: 'taker_asset_filled_amount', type: 'varchar' }, + { name: 'maker_fee', type: 'varchar' }, + { name: 'taker_fee', type: 'varchar' }, + { name: 'expiration_time_seconds', type: 'int' }, + { name: 'salt', type: 'varchar' }, + { name: 'signature', type: 'varchar' }, + + { name: 'raw_maker_asset_data', type: 'varchar' }, + { name: 'maker_asset_type', type: 'varchar' }, + { name: 'maker_asset_proxy_id', type: 'varchar' }, + { name: 'maker_token_address', type: 'char(42)' }, + { name: 'maker_token_id', type: 'varchar', isNullable: true }, + { name: 'raw_taker_asset_data', type: 'varchar' }, + { name: 'taker_asset_type', type: 'varchar' }, + { name: 'taker_asset_proxy_id', type: 'varchar' }, + { name: 'taker_token_address', type: 'char(42)' }, + { name: 'taker_token_id', type: 'varchar', isNullable: true }, + + { name: 'metadata_json', type: 'varchar' }, + ], }); const token_on_chain_metadata = new Table({ name: 'raw.token_on_chain_metadata', columns: [ - {name: 'address', type: 'char(42)', isPrimary: true}, - {name: 'decimals', type: 'integer'}, - {name: 'symbol', type: 'varchar'}, - {name: 'name', type: 'varchar'} - ] + { name: 'address', type: 'char(42)', isPrimary: true }, + { name: 'decimals', type: 'integer' }, + { name: 'symbol', type: 'varchar' }, + { name: 'name', type: 'varchar' }, + ], }); const transactions = new Table({ name: 'raw.transactions', columns: [ - {name: 'block_number', type: 'bigint', isPrimary: true}, - {name: 'block_hash', type: 'varchar', isPrimary: true}, - {name: 'transaction_hash', type: 'varchar', isPrimary: true}, - {name: 'gas_used', type: 'bigint'}, - {name: 'gas_price', type: 'bigint'} - ] + { name: 'block_number', type: 'bigint', isPrimary: true }, + { name: 'block_hash', type: 'varchar', isPrimary: true }, + { name: 'transaction_hash', type: 'varchar', isPrimary: true }, + { name: 'gas_used', type: 'bigint' }, + { name: 'gas_price', type: 'bigint' }, + ], }); export class InitialSchema1542070840010 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.createSchema('raw'); await queryRunner.createTable(blocks); @@ -175,7 +173,6 @@ export class InitialSchema1542070840010 implements MigrationInterface { } public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropTable(blocks.name); await queryRunner.dropTable(exchange_cancel_events.name); await queryRunner.dropTable(exchange_cancel_up_to_events.name); diff --git a/packages/pipeline/package.json b/packages/pipeline/package.json index 59cf24dc6..dcd19d150 100644 --- a/packages/pipeline/package.json +++ b/packages/pipeline/package.json @@ -13,7 +13,7 @@ "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", "coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info", "clean": "shx rm -rf lib", - "lint": "tslint --project . --format stylish --exclude ./migrations", + "lint": "tslint --project . --format stylish --exclude ./migrations/**/*", "migrate:run": "yarn typeorm migration:run --config ./lib/src/ormconfig", "migrate:revert": "yarn typeorm migration:revert --config ./lib/src/ormconfig", "migrate:create": "yarn typeorm migration:create --config ./lib/src/ormconfig --dir migrations" diff --git a/packages/pipeline/src/entities/block.ts b/packages/pipeline/src/entities/block.ts index 5bd51f3d2..51be37703 100644 --- a/packages/pipeline/src/entities/block.ts +++ b/packages/pipeline/src/entities/block.ts @@ -1,6 +1,6 @@ import { Column, Entity, PrimaryColumn } from 'typeorm'; -@Entity({ name: 'blocks' }) +@Entity({ name: 'blocks', schema: 'raw' }) export class Block { @PrimaryColumn() public hash!: string; @PrimaryColumn() public number!: number; diff --git a/packages/pipeline/src/entities/exchange_cancel_event.ts b/packages/pipeline/src/entities/exchange_cancel_event.ts index 6d0c3bc32..781e1c4bb 100644 --- a/packages/pipeline/src/entities/exchange_cancel_event.ts +++ b/packages/pipeline/src/entities/exchange_cancel_event.ts @@ -2,7 +2,7 @@ import { Column, Entity, PrimaryColumn } from 'typeorm'; import { AssetType } from '../types'; -@Entity({ name: 'exchange_cancel_events' }) +@Entity({ name: 'exchange_cancel_events', schema: 'raw' }) export class ExchangeCancelEvent { @PrimaryColumn({ name: 'contract_address' }) public contractAddress!: string; diff --git a/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts b/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts index bc3bab6bb..7306a1a87 100644 --- a/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts +++ b/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts @@ -1,6 +1,6 @@ import { Column, Entity, PrimaryColumn } from 'typeorm'; -@Entity({ name: 'exchange_cancel_up_to_events' }) +@Entity({ name: 'exchange_cancel_up_to_events', schema: 'raw' }) export class ExchangeCancelUpToEvent { @PrimaryColumn({ name: 'contract_address' }) public contractAddress!: string; diff --git a/packages/pipeline/src/entities/exchange_fill_event.ts b/packages/pipeline/src/entities/exchange_fill_event.ts index 0c5546d7e..6202e558b 100644 --- a/packages/pipeline/src/entities/exchange_fill_event.ts +++ b/packages/pipeline/src/entities/exchange_fill_event.ts @@ -2,7 +2,7 @@ import { Column, Entity, PrimaryColumn } from 'typeorm'; import { AssetType } from '../types'; -@Entity({ name: 'exchange_fill_events' }) +@Entity({ name: 'exchange_fill_events', schema: 'raw' }) export class ExchangeFillEvent { @PrimaryColumn({ name: 'contract_address' }) public contractAddress!: string; diff --git a/packages/pipeline/src/entities/relayer.ts b/packages/pipeline/src/entities/relayer.ts index 94b5232a8..ef1f9cf2a 100644 --- a/packages/pipeline/src/entities/relayer.ts +++ b/packages/pipeline/src/entities/relayer.ts @@ -1,6 +1,6 @@ import { Column, Entity, PrimaryColumn } from 'typeorm'; -@Entity({ name: 'relayers' }) +@Entity({ name: 'relayers', schema: 'raw' }) export class Relayer { @PrimaryColumn() public uuid!: string; diff --git a/packages/pipeline/src/entities/sra_order.ts b/packages/pipeline/src/entities/sra_order.ts index f3f79852f..e59161c81 100644 --- a/packages/pipeline/src/entities/sra_order.ts +++ b/packages/pipeline/src/entities/sra_order.ts @@ -2,7 +2,7 @@ import { Column, Entity, PrimaryColumn } from 'typeorm'; import { AssetType } from '../types'; -@Entity({ name: 'sra_orders' }) +@Entity({ name: 'sra_orders', schema: 'raw' }) export class SraOrder { @PrimaryColumn({ name: 'exchange_address' }) public exchangeAddress!: string; diff --git a/packages/pipeline/src/entities/token_on_chain_metadata.ts b/packages/pipeline/src/entities/token_on_chain_metadata.ts index fc21d0aed..325d28e91 100644 --- a/packages/pipeline/src/entities/token_on_chain_metadata.ts +++ b/packages/pipeline/src/entities/token_on_chain_metadata.ts @@ -1,6 +1,6 @@ import { Column, Entity, PrimaryColumn } from 'typeorm'; -@Entity({ name: 'token_on_chain_metadata' }) +@Entity({ name: 'token_on_chain_metadata', schema: 'raw' }) export class TokenOnChainMetadata { @PrimaryColumn({ type: 'nvarchar', nullable: false }) public address!: string; @@ -13,4 +13,4 @@ export class TokenOnChainMetadata { @Column({ type: 'nvarchar', nullable: false }) public name!: string; -} \ No newline at end of file +} diff --git a/packages/pipeline/src/entities/transaction.ts b/packages/pipeline/src/entities/transaction.ts index eb2883fda..dd2143df5 100644 --- a/packages/pipeline/src/entities/transaction.ts +++ b/packages/pipeline/src/entities/transaction.ts @@ -1,6 +1,6 @@ import { Column, Entity, PrimaryColumn } from 'typeorm'; -@Entity({ name: 'transactions' }) +@Entity({ name: 'transactions', schema: 'raw' }) export class Transaction { @PrimaryColumn({ name: 'transaction_hash' }) public transactionHash!: string; -- cgit v1.2.3 From 2cbb82eb0498c539ea16afe4012bd69f154ad027 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Tue, 13 Nov 2018 14:57:58 -0800 Subject: Update schema for sra_orders --- .../1542147915364-NewSraOrderTimestampFormat.ts | 48 ++++++++++++++++++++++ packages/pipeline/src/entities/sra_order.ts | 7 +--- 2 files changed, 49 insertions(+), 6 deletions(-) create mode 100644 packages/pipeline/migrations/1542147915364-NewSraOrderTimestampFormat.ts diff --git a/packages/pipeline/migrations/1542147915364-NewSraOrderTimestampFormat.ts b/packages/pipeline/migrations/1542147915364-NewSraOrderTimestampFormat.ts new file mode 100644 index 000000000..5a8f3fec8 --- /dev/null +++ b/packages/pipeline/migrations/1542147915364-NewSraOrderTimestampFormat.ts @@ -0,0 +1,48 @@ +import { MigrationInterface, QueryRunner, Table } from 'typeorm'; + +export class NewSraOrderTimestampFormat1542147915364 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE raw.sra_orders + DROP CONSTRAINT "PK_09bfb9980715329563bd53d667e", + ADD PRIMARY KEY (order_hash_hex, exchange_address, source_url); + `, + ); + + await queryRunner.query( + `CREATE TABLE raw.sra_orders_observed_timestamps ( + order_hash_hex varchar NOT NULL, + exchange_address varchar NOT NULL, + source_url varchar NOT NULL, + observed_timestamp bigint NOT NULL, + FOREIGN KEY + (order_hash_hex, exchange_address, source_url) + REFERENCES raw.sra_orders (order_hash_hex, exchange_address, source_url), + PRIMARY KEY (order_hash_hex, exchange_address, source_url, observed_timestamp) + );`, + ); + + await queryRunner.query( + `ALTER TABLE raw.sra_orders + DROP COLUMN last_updated_timestamp, + DROP COLUMN first_seen_timestamp;`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropTable('raw.sra_orders_observed_timestamps'); + + await queryRunner.query( + `ALTER TABLE raw.sra_orders + ADD COLUMN last_updated_timestamp bigint NOT NULL DEFAULT 0, + ADD COLUMN first_seen_timestamp bigint NOT NULL DEFAULT 0;`, + ); + + await queryRunner.query( + `ALTER TABLE raw.sra_orders + DROP CONSTRAINT sra_orders_pkey, + ADD CONSTRAINT "PK_09bfb9980715329563bd53d667e" PRIMARY KEY ("exchange_address", "order_hash_hex"); + `, + ); + } +} diff --git a/packages/pipeline/src/entities/sra_order.ts b/packages/pipeline/src/entities/sra_order.ts index e59161c81..4b7f652d3 100644 --- a/packages/pipeline/src/entities/sra_order.ts +++ b/packages/pipeline/src/entities/sra_order.ts @@ -8,13 +8,8 @@ export class SraOrder { public exchangeAddress!: string; @PrimaryColumn({ name: 'order_hash_hex' }) public orderHashHex!: string; - - @Column({ name: 'source_url' }) + @PrimaryColumn({ name: 'source_url' }) public sourceUrl!: string; - @Column({ name: 'last_updated_timestamp' }) - public lastUpdatedTimestamp!: number; - @Column({ name: 'first_seen_timestamp' }) - public firstSeenTimestamp!: number; @Column({ name: 'maker_address' }) public makerAddress!: string; -- cgit v1.2.3 From 55bbe1954b35ff0a6367f1ff820d32a32b48eff3 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Tue, 13 Nov 2018 14:05:49 -0800 Subject: Preliminary work for adding RR order book scraping --- .../src/scripts/pull_radar_relay_orders.ts | 52 ++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 packages/pipeline/src/scripts/pull_radar_relay_orders.ts diff --git a/packages/pipeline/src/scripts/pull_radar_relay_orders.ts b/packages/pipeline/src/scripts/pull_radar_relay_orders.ts new file mode 100644 index 000000000..c4d3f7095 --- /dev/null +++ b/packages/pipeline/src/scripts/pull_radar_relay_orders.ts @@ -0,0 +1,52 @@ +// tslint:disable:no-console +import { HttpClient } from '@0x/connect'; +import * as R from 'ramda'; +import 'reflect-metadata'; +import { Connection, ConnectionOptions, createConnection } from 'typeorm'; + +import { SraOrder } from '../entities'; +import * as ormConfig from '../ormconfig'; +import { parseSraOrders } from '../parsers/sra_orders'; +import { handleError } from '../utils'; + +const RADAR_RELAY_URL = 'https://api.radarrelay.com/0x/v2'; +const BATCH_SAVE_SIZE = 1000; // Number of orders to save at once. +const ORDERS_PER_PAGE = 10000; // Number of orders to get per request. + +let connection: Connection; + +(async () => { + connection = await createConnection(ormConfig as ConnectionOptions); + await getOrderbook(); + process.exit(0); +})().catch(handleError); + +async function getOrderbook(): Promise { + console.log('Getting all orders...'); + const connectClient = new HttpClient(RADAR_RELAY_URL); + const rawOrders = await connectClient.getOrdersAsync({ + perPage: ORDERS_PER_PAGE, + }); + console.log(`Got ${rawOrders.records.length} orders.`); + console.log('Parsing orders...'); + const orders = R.pipe(parseSraOrders, R.map(setSourceUrl(RADAR_RELAY_URL)))(rawOrders); + const ordersRepository = connection.getRepository(SraOrder); + // TODO(albrow): Move batch saving to a utility function to reduce + // duplicated code. + for (const ordersBatch of R.splitEvery(BATCH_SAVE_SIZE, orders)) { + await ordersRepository.save(ordersBatch); + } +} + +const sourceUrlProp = R.lensProp('sourceUrl'); + +const setSourceUrl = R.curry((sourceURL: string, order: SraOrder): SraOrder => { + return R.set(sourceUrlProp, sourceURL, order); +}); + +const firstSeenProp = R.lensProp('firstSeenTimestamp'); +const lastUpdatedProp = R.lensProp('lastUpdatedTimestamp'); + +const setFirstSeen = R.curry((sourceURL: string, order: SraOrder): SraOrder => { + return R.set(firstSeenTimestampProp, sourceURL, order); +}); -- cgit v1.2.3 From 26280e4aba147ad6000b9df309e64db84b6932fc Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Tue, 13 Nov 2018 15:33:43 -0800 Subject: Implement scraping sra orders from radar relay --- packages/pipeline/src/entities/index.ts | 1 + packages/pipeline/src/entities/sra_order.ts | 4 +-- .../src/entities/sra_order_observed_timestamp.ts | 30 ++++++++++++++++++++ packages/pipeline/src/ormconfig.ts | 2 ++ packages/pipeline/src/parsers/sra_orders/index.ts | 4 --- .../src/scripts/pull_radar_relay_orders.ts | 33 +++++++++++----------- .../pipeline/test/parsers/sra_orders/index_test.ts | 2 -- 7 files changed, 52 insertions(+), 24 deletions(-) create mode 100644 packages/pipeline/src/entities/sra_order_observed_timestamp.ts diff --git a/packages/pipeline/src/entities/index.ts b/packages/pipeline/src/entities/index.ts index 6e024f280..442eec9cd 100644 --- a/packages/pipeline/src/entities/index.ts +++ b/packages/pipeline/src/entities/index.ts @@ -6,3 +6,4 @@ export { Relayer } from './relayer'; export { SraOrder } from './sra_order'; export { Transaction } from './transaction'; export { TokenOnChainMetadata } from './token_on_chain_metadata'; +export { SraOrdersObservedTimeStamp, createObservedTimestampForOrder } from './sra_order_observed_timestamp'; diff --git a/packages/pipeline/src/entities/sra_order.ts b/packages/pipeline/src/entities/sra_order.ts index 4b7f652d3..2bdb1ba2e 100644 --- a/packages/pipeline/src/entities/sra_order.ts +++ b/packages/pipeline/src/entities/sra_order.ts @@ -19,9 +19,9 @@ export class SraOrder { public feeRecipientAddress!: string; @Column({ name: 'sender_address' }) public senderAddress!: string; - @Column({ name: 'maker_asset_amount' }) + @Column({ name: 'maker_asset_filled_amount' }) public makerAssetAmount!: string; - @Column({ name: 'taker_asset_amount' }) + @Column({ name: 'taker_asset_filled_amount' }) public takerAssetAmount!: string; @Column({ name: 'maker_fee' }) public makerFee!: string; diff --git a/packages/pipeline/src/entities/sra_order_observed_timestamp.ts b/packages/pipeline/src/entities/sra_order_observed_timestamp.ts new file mode 100644 index 000000000..bdb6cd36b --- /dev/null +++ b/packages/pipeline/src/entities/sra_order_observed_timestamp.ts @@ -0,0 +1,30 @@ +import { Entity, PrimaryColumn } from 'typeorm'; + +import { SraOrder } from './sra_order'; + +@Entity({ name: 'sra_orders_observed_timestamps', schema: 'raw' }) +export class SraOrdersObservedTimeStamp { + @PrimaryColumn({ name: 'exchange_address' }) + public exchangeAddress!: string; + @PrimaryColumn({ name: 'order_hash_hex' }) + public orderHashHex!: string; + @PrimaryColumn({ name: 'source_url' }) + public sourceUrl!: string; + + @PrimaryColumn({ name: 'observed_timestamp' }) + public observedTimestamp!: number; +} + +/** + * Returns a new SraOrdersObservedTimeStamp for the given order based on the + * current time. + * @param order The order to generate a timestamp for. + */ +export function createObservedTimestampForOrder(order: SraOrder): SraOrdersObservedTimeStamp { + const observed = new SraOrdersObservedTimeStamp(); + observed.exchangeAddress = order.exchangeAddress; + observed.orderHashHex = order.orderHashHex; + observed.sourceUrl = order.sourceUrl; + observed.observedTimestamp = Date.now(); + return observed; +} diff --git a/packages/pipeline/src/ormconfig.ts b/packages/pipeline/src/ormconfig.ts index 2fb6b3d3c..2f5f7df33 100644 --- a/packages/pipeline/src/ormconfig.ts +++ b/packages/pipeline/src/ormconfig.ts @@ -7,6 +7,7 @@ import { ExchangeFillEvent, Relayer, SraOrder, + SraOrdersObservedTimeStamp, Transaction, } from './entities'; @@ -17,6 +18,7 @@ const entities = [ ExchangeFillEvent, Relayer, SraOrder, + SraOrdersObservedTimeStamp, Transaction, ]; diff --git a/packages/pipeline/src/parsers/sra_orders/index.ts b/packages/pipeline/src/parsers/sra_orders/index.ts index 3d7f73fca..800521955 100644 --- a/packages/pipeline/src/parsers/sra_orders/index.ts +++ b/packages/pipeline/src/parsers/sra_orders/index.ts @@ -30,10 +30,6 @@ export function _convertToEntity(apiOrder: APIOrder): SraOrder { sraOrder.exchangeAddress = apiOrder.order.exchangeAddress; sraOrder.orderHashHex = orderHashUtils.getOrderHashHex(apiOrder.order); - // TODO(albrow): Set these fields to the correct values upstack. - sraOrder.lastUpdatedTimestamp = 0; - sraOrder.firstSeenTimestamp = 0; - sraOrder.makerAddress = apiOrder.order.makerAddress; sraOrder.takerAddress = apiOrder.order.takerAddress; sraOrder.feeRecipientAddress = apiOrder.order.feeRecipientAddress; diff --git a/packages/pipeline/src/scripts/pull_radar_relay_orders.ts b/packages/pipeline/src/scripts/pull_radar_relay_orders.ts index c4d3f7095..b3a4d887e 100644 --- a/packages/pipeline/src/scripts/pull_radar_relay_orders.ts +++ b/packages/pipeline/src/scripts/pull_radar_relay_orders.ts @@ -2,15 +2,14 @@ import { HttpClient } from '@0x/connect'; import * as R from 'ramda'; import 'reflect-metadata'; -import { Connection, ConnectionOptions, createConnection } from 'typeorm'; +import { Connection, ConnectionOptions, createConnection, EntityManager } from 'typeorm'; -import { SraOrder } from '../entities'; +import { createObservedTimestampForOrder, SraOrder } from '../entities'; import * as ormConfig from '../ormconfig'; import { parseSraOrders } from '../parsers/sra_orders'; import { handleError } from '../utils'; const RADAR_RELAY_URL = 'https://api.radarrelay.com/0x/v2'; -const BATCH_SAVE_SIZE = 1000; // Number of orders to save at once. const ORDERS_PER_PAGE = 10000; // Number of orders to get per request. let connection: Connection; @@ -29,24 +28,26 @@ async function getOrderbook(): Promise { }); console.log(`Got ${rawOrders.records.length} orders.`); console.log('Parsing orders...'); + // Parse the sra orders, then add source url to each. const orders = R.pipe(parseSraOrders, R.map(setSourceUrl(RADAR_RELAY_URL)))(rawOrders); - const ordersRepository = connection.getRepository(SraOrder); - // TODO(albrow): Move batch saving to a utility function to reduce - // duplicated code. - for (const ordersBatch of R.splitEvery(BATCH_SAVE_SIZE, orders)) { - await ordersRepository.save(ordersBatch); - } + // Save all the orders and update the observed time stamps in a single + // transaction. + console.log('Saving orders and updating timestamps...'); + await connection.transaction(async (manager: EntityManager): Promise => { + for (const order of orders) { + await manager.save(SraOrder, order); + const observedTimestamp = createObservedTimestampForOrder(order); + await manager.save(observedTimestamp); + } + }); } const sourceUrlProp = R.lensProp('sourceUrl'); +/** + * Sets the source url for a single order. Returns a new order instead of + * mutating the given one. + */ const setSourceUrl = R.curry((sourceURL: string, order: SraOrder): SraOrder => { return R.set(sourceUrlProp, sourceURL, order); }); - -const firstSeenProp = R.lensProp('firstSeenTimestamp'); -const lastUpdatedProp = R.lensProp('lastUpdatedTimestamp'); - -const setFirstSeen = R.curry((sourceURL: string, order: SraOrder): SraOrder => { - return R.set(firstSeenTimestampProp, sourceURL, order); -}); diff --git a/packages/pipeline/test/parsers/sra_orders/index_test.ts b/packages/pipeline/test/parsers/sra_orders/index_test.ts index 4b2e7c2d0..534d84ab3 100644 --- a/packages/pipeline/test/parsers/sra_orders/index_test.ts +++ b/packages/pipeline/test/parsers/sra_orders/index_test.ts @@ -37,8 +37,6 @@ describe('sra_orders', () => { const expected = new SraOrder(); expected.exchangeAddress = '0x4f833a24e1f95d70f028921e27040ca56e09ab0b'; expected.orderHashHex = '0x1bdbeb0d088a33da28b9ee6d94e8771452f90f4a69107da2fa75195d61b9a1c9'; - expected.lastUpdatedTimestamp = 0; - expected.firstSeenTimestamp = 0; expected.makerAddress = '0xb45df06e38540a675fdb5b598abf2c0dbe9d6b81'; expected.takerAddress = '0x0000000000000000000000000000000000000000'; expected.feeRecipientAddress = '0xa258b39954cef5cb142fd567a46cddb31a670124'; -- cgit v1.2.3 From a948305e7a488ad01404fa04c95dacc98b4b0061 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Tue, 13 Nov 2018 15:44:53 -0800 Subject: Rename taker and maker amounts in sra_orders --- .../1542152278484-RenameSraOrdersFilledAmounts.ts | 13 +++++++++++++ packages/pipeline/src/entities/sra_order.ts | 4 ++-- 2 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 packages/pipeline/migrations/1542152278484-RenameSraOrdersFilledAmounts.ts diff --git a/packages/pipeline/migrations/1542152278484-RenameSraOrdersFilledAmounts.ts b/packages/pipeline/migrations/1542152278484-RenameSraOrdersFilledAmounts.ts new file mode 100644 index 000000000..a13e3efa5 --- /dev/null +++ b/packages/pipeline/migrations/1542152278484-RenameSraOrdersFilledAmounts.ts @@ -0,0 +1,13 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RenameSraOrdersFilledAmounts1542152278484 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.renameColumn('raw.sra_orders', 'maker_asset_filled_amount', 'maker_asset_amount'); + await queryRunner.renameColumn('raw.sra_orders', 'taker_asset_filled_amount', 'taker_asset_amount'); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.renameColumn('raw.sra_orders', 'maker_asset_amount', 'maker_asset_filled_amount'); + await queryRunner.renameColumn('raw.sra_orders', 'taker_asset_amount', 'taker_asset_filled_amount'); + } +} diff --git a/packages/pipeline/src/entities/sra_order.ts b/packages/pipeline/src/entities/sra_order.ts index 2bdb1ba2e..4b7f652d3 100644 --- a/packages/pipeline/src/entities/sra_order.ts +++ b/packages/pipeline/src/entities/sra_order.ts @@ -19,9 +19,9 @@ export class SraOrder { public feeRecipientAddress!: string; @Column({ name: 'sender_address' }) public senderAddress!: string; - @Column({ name: 'maker_asset_filled_amount' }) + @Column({ name: 'maker_asset_amount' }) public makerAssetAmount!: string; - @Column({ name: 'taker_asset_filled_amount' }) + @Column({ name: 'taker_asset_amount' }) public takerAssetAmount!: string; @Column({ name: 'maker_fee' }) public makerFee!: string; -- cgit v1.2.3 From 10e93bb01ffcf029821430781ef582a24901a461 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Wed, 14 Nov 2018 14:39:34 -0800 Subject: Add raw schema prefix to query in pull_missing_events --- packages/pipeline/src/scripts/pull_missing_events.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pipeline/src/scripts/pull_missing_events.ts b/packages/pipeline/src/scripts/pull_missing_events.ts index bc0eac853..0af999a77 100644 --- a/packages/pipeline/src/scripts/pull_missing_events.ts +++ b/packages/pipeline/src/scripts/pull_missing_events.ts @@ -55,7 +55,7 @@ async function getStartBlockAsync(eventsRepository: Repository Date: Wed, 14 Nov 2018 15:58:36 -0800 Subject: Change some column types from varchar to numeric --- .../1542234704666-ConvertBigNumberToNumeric.ts | 53 ++++++++++++++++++++++ .../src/entities/exchange_cancel_up_to_event.ts | 7 ++- .../pipeline/src/entities/exchange_fill_event.ts | 18 ++++---- packages/pipeline/src/entities/sra_order.ts | 26 ++++++----- packages/pipeline/src/ormconfig.ts | 2 +- packages/pipeline/src/parsers/events/index.ts | 10 ++-- packages/pipeline/src/parsers/sra_orders/index.ts | 12 ++--- .../pipeline/src/scripts/pull_missing_events.ts | 2 +- packages/pipeline/src/utils/index.ts | 15 ++++++ .../pipeline/test/parsers/events/index_test.ts | 10 ++-- .../pipeline/test/parsers/sra_orders/index_test.ts | 12 ++--- 11 files changed, 121 insertions(+), 46 deletions(-) create mode 100644 packages/pipeline/migrations/1542234704666-ConvertBigNumberToNumeric.ts diff --git a/packages/pipeline/migrations/1542234704666-ConvertBigNumberToNumeric.ts b/packages/pipeline/migrations/1542234704666-ConvertBigNumberToNumeric.ts new file mode 100644 index 000000000..5200ef7cc --- /dev/null +++ b/packages/pipeline/migrations/1542234704666-ConvertBigNumberToNumeric.ts @@ -0,0 +1,53 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ConvertBigNumberToNumeric1542234704666 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE raw.exchange_fill_events + ALTER COLUMN maker_asset_filled_amount TYPE numeric USING maker_asset_filled_amount::numeric, + ALTER COLUMN taker_asset_filled_amount TYPE numeric USING taker_asset_filled_amount::numeric, + ALTER COLUMN maker_fee_paid TYPE numeric USING maker_fee_paid::numeric, + ALTER COLUMN taker_fee_paid TYPE numeric USING taker_fee_paid::numeric;`, + ); + + await queryRunner.query( + `ALTER TABLE raw.exchange_cancel_up_to_events + ALTER COLUMN order_epoch TYPE numeric USING order_epoch::numeric;`, + ); + + await queryRunner.query( + `ALTER TABLE raw.sra_orders + ALTER COLUMN maker_asset_amount TYPE numeric USING maker_asset_amount::numeric, + ALTER COLUMN taker_asset_amount TYPE numeric USING taker_asset_amount::numeric, + ALTER COLUMN maker_fee TYPE numeric USING maker_fee::numeric, + ALTER COLUMN taker_fee TYPE numeric USING taker_fee::numeric, + ALTER COLUMN expiration_time_seconds TYPE numeric USING expiration_time_seconds::numeric, + ALTER COLUMN salt TYPE numeric USING salt::numeric;`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE raw.sra_orders + ALTER COLUMN maker_asset_amount TYPE varchar USING maker_asset_amount::varchar, + ALTER COLUMN taker_asset_amount TYPE varchar USING taker_asset_amount::varchar, + ALTER COLUMN maker_fee TYPE varchar USING maker_fee::varchar, + ALTER COLUMN taker_fee TYPE varchar USING taker_fee::varchar, + ALTER COLUMN expiration_time_seconds TYPE varchar USING expiration_time_seconds::varchar, + ALTER COLUMN salt TYPE varchar USING salt::varchar;`, + ); + + await queryRunner.query( + `ALTER TABLE raw.exchange_cancel_up_to_events + ALTER COLUMN order_epoch TYPE varchar USING order_epoch::varchar;`, + ); + + await queryRunner.query( + `ALTER TABLE raw.exchange_fill_events + ALTER COLUMN maker_asset_filled_amount TYPE varchar USING maker_asset_filled_amount::varchar, + ALTER COLUMN taker_asset_filled_amount TYPE varchar USING taker_asset_filled_amount::varchar, + ALTER COLUMN maker_fee_paid TYPE varchar USING maker_fee_paid::varchar, + ALTER COLUMN taker_fee_paid TYPE varchar USING taker_fee_paid::varchar;`, + ); + } +} diff --git a/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts b/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts index 7306a1a87..752631b85 100644 --- a/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts +++ b/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts @@ -1,5 +1,8 @@ +import { BigNumber } from '@0x/utils'; import { Column, Entity, PrimaryColumn } from 'typeorm'; +import { bigNumberTransformer } from '../utils'; + @Entity({ name: 'exchange_cancel_up_to_events', schema: 'raw' }) export class ExchangeCancelUpToEvent { @PrimaryColumn({ name: 'contract_address' }) @@ -17,7 +20,7 @@ export class ExchangeCancelUpToEvent { public makerAddress!: string; @Column({ name: 'sender_address' }) public senderAddress!: string; - @Column({ name: 'order_epoch' }) - public orderEpoch!: string; + @Column({ name: 'order_epoch', type: 'numeric', transformer: bigNumberTransformer }) + public orderEpoch!: BigNumber; // TODO(albrow): Include topics? } diff --git a/packages/pipeline/src/entities/exchange_fill_event.ts b/packages/pipeline/src/entities/exchange_fill_event.ts index 6202e558b..aa082436b 100644 --- a/packages/pipeline/src/entities/exchange_fill_event.ts +++ b/packages/pipeline/src/entities/exchange_fill_event.ts @@ -1,6 +1,8 @@ +import { BigNumber } from '@0x/utils'; import { Column, Entity, PrimaryColumn } from 'typeorm'; import { AssetType } from '../types'; +import { bigNumberTransformer } from '../utils'; @Entity({ name: 'exchange_fill_events', schema: 'raw' }) export class ExchangeFillEvent { @@ -24,14 +26,14 @@ export class ExchangeFillEvent { public feeRecipientAddress!: string; @Column({ name: 'sender_address' }) public senderAddress!: string; - @Column({ name: 'maker_asset_filled_amount' }) - public makerAssetFilledAmount!: string; - @Column({ name: 'taker_asset_filled_amount' }) - public takerAssetFilledAmount!: string; - @Column({ name: 'maker_fee_paid' }) - public makerFeePaid!: string; - @Column({ name: 'taker_fee_paid' }) - public takerFeePaid!: string; + @Column({ name: 'maker_asset_filled_amount', type: 'numeric', transformer: bigNumberTransformer }) + public makerAssetFilledAmount!: BigNumber; + @Column({ name: 'taker_asset_filled_amount', type: 'numeric', transformer: bigNumberTransformer }) + public takerAssetFilledAmount!: BigNumber; + @Column({ name: 'maker_fee_paid', type: 'numeric', transformer: bigNumberTransformer }) + public makerFeePaid!: BigNumber; + @Column({ name: 'taker_fee_paid', type: 'numeric', transformer: bigNumberTransformer }) + public takerFeePaid!: BigNumber; @Column({ name: 'order_hash' }) public orderHash!: string; diff --git a/packages/pipeline/src/entities/sra_order.ts b/packages/pipeline/src/entities/sra_order.ts index 4b7f652d3..9c730a0bb 100644 --- a/packages/pipeline/src/entities/sra_order.ts +++ b/packages/pipeline/src/entities/sra_order.ts @@ -1,6 +1,8 @@ +import { BigNumber } from '@0x/utils'; import { Column, Entity, PrimaryColumn } from 'typeorm'; import { AssetType } from '../types'; +import { bigNumberTransformer } from '../utils'; @Entity({ name: 'sra_orders', schema: 'raw' }) export class SraOrder { @@ -19,18 +21,18 @@ export class SraOrder { public feeRecipientAddress!: string; @Column({ name: 'sender_address' }) public senderAddress!: string; - @Column({ name: 'maker_asset_amount' }) - public makerAssetAmount!: string; - @Column({ name: 'taker_asset_amount' }) - public takerAssetAmount!: string; - @Column({ name: 'maker_fee' }) - public makerFee!: string; - @Column({ name: 'taker_fee' }) - public takerFee!: string; - @Column({ name: 'expiration_time_seconds' }) - public expirationTimeSeconds!: string; - @Column({ name: 'salt' }) - public salt!: string; + @Column({ name: 'maker_asset_amount', type: 'numeric', transformer: bigNumberTransformer }) + public makerAssetAmount!: BigNumber; + @Column({ name: 'taker_asset_amount', type: 'numeric', transformer: bigNumberTransformer }) + public takerAssetAmount!: BigNumber; + @Column({ name: 'maker_fee', type: 'numeric', transformer: bigNumberTransformer }) + public makerFee!: BigNumber; + @Column({ name: 'taker_fee', type: 'numeric', transformer: bigNumberTransformer }) + public takerFee!: BigNumber; + @Column({ name: 'expiration_time_seconds', type: 'numeric', transformer: bigNumberTransformer }) + public expirationTimeSeconds!: BigNumber; + @Column({ name: 'salt', type: 'numeric', transformer: bigNumberTransformer }) + public salt!: BigNumber; @Column({ name: 'signature' }) public signature!: string; diff --git a/packages/pipeline/src/ormconfig.ts b/packages/pipeline/src/ormconfig.ts index 2f5f7df33..0e489e560 100644 --- a/packages/pipeline/src/ormconfig.ts +++ b/packages/pipeline/src/ormconfig.ts @@ -26,7 +26,7 @@ const config: ConnectionOptions = { type: 'postgres', url: process.env.ZEROEX_DATA_PIPELINE_DB_URL, synchronize: false, - logging: false, + logging: ['error', 'warn'], entities, migrations: ['./lib/migrations/**/*.js'], }; diff --git a/packages/pipeline/src/parsers/events/index.ts b/packages/pipeline/src/parsers/events/index.ts index d56dc7080..27e192d7b 100644 --- a/packages/pipeline/src/parsers/events/index.ts +++ b/packages/pipeline/src/parsers/events/index.ts @@ -65,10 +65,10 @@ export function _convertToExchangeFillEvent(eventLog: LogWithDecodedArgs { makerAssetFilledAmount: new BigNumber('10000000000000000'), takerAssetFilledAmount: new BigNumber('100000000000000000'), makerFeePaid: new BigNumber('0'), - takerFeePaid: new BigNumber('0'), + takerFeePaid: new BigNumber('12345'), orderHash: '0xab12ed2cbaa5615ab690b9da75a46e53ddfcf3f1a68655b5fe0d94c75a1aac4a', makerAssetData: '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', takerAssetData: '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498', @@ -56,10 +56,10 @@ describe('exchange_events', () => { expected.takerAddress = '0xf6da68519f78b0d0bc93c701e86affcb75c92428'; expected.feeRecipientAddress = '0xc370d2a5920344aa6b7d8d11250e3e861434cbdd'; expected.senderAddress = '0xf6da68519f78b0d0bc93c701e86affcb75c92428'; - expected.makerAssetFilledAmount = '10000000000000000'; - expected.takerAssetFilledAmount = '100000000000000000'; - expected.makerFeePaid = '0'; - expected.takerFeePaid = '0'; + expected.makerAssetFilledAmount = new BigNumber('10000000000000000'); + expected.takerAssetFilledAmount = new BigNumber('100000000000000000'); + expected.makerFeePaid = new BigNumber('0'); + expected.takerFeePaid = new BigNumber('12345'); expected.orderHash = '0xab12ed2cbaa5615ab690b9da75a46e53ddfcf3f1a68655b5fe0d94c75a1aac4a'; expected.rawMakerAssetData = '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; expected.makerAssetType = 'erc20'; diff --git a/packages/pipeline/test/parsers/sra_orders/index_test.ts b/packages/pipeline/test/parsers/sra_orders/index_test.ts index 534d84ab3..ee2842ef3 100644 --- a/packages/pipeline/test/parsers/sra_orders/index_test.ts +++ b/packages/pipeline/test/parsers/sra_orders/index_test.ts @@ -41,12 +41,12 @@ describe('sra_orders', () => { expected.takerAddress = '0x0000000000000000000000000000000000000000'; expected.feeRecipientAddress = '0xa258b39954cef5cb142fd567a46cddb31a670124'; expected.senderAddress = '0x0000000000000000000000000000000000000000'; - expected.makerAssetAmount = '1619310371000000000'; - expected.takerAssetAmount = '8178335207070707070707'; - expected.makerFee = '0'; - expected.takerFee = '0'; - expected.expirationTimeSeconds = '1538529488'; - expected.salt = '1537924688891'; + expected.makerAssetAmount = new BigNumber('1619310371000000000'); + expected.takerAssetAmount = new BigNumber('8178335207070707070707'); + expected.makerFee = new BigNumber('0'); + expected.takerFee = new BigNumber('0'); + expected.expirationTimeSeconds = new BigNumber('1538529488'); + expected.salt = new BigNumber('1537924688891'); expected.signature = '0x1b5a5d672b0d647b5797387ccbb89d822d5d2e873346b014f4ff816ff0783f2a7a0d2824d2d7042ec8ea375bc7f870963e1cb8248f1db03ddf125e27b5963aa11f03'; expected.rawMakerAssetData = '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; -- cgit v1.2.3 From b0a2c10e11fa41e2800d4fe67d8008b5828128a3 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Wed, 14 Nov 2018 16:20:07 -0800 Subject: Use built-in chunk feature of TypeORM save method --- packages/pipeline/src/scripts/pull_missing_events.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/pipeline/src/scripts/pull_missing_events.ts b/packages/pipeline/src/scripts/pull_missing_events.ts index 1693bb59a..b1b8665dd 100644 --- a/packages/pipeline/src/scripts/pull_missing_events.ts +++ b/packages/pipeline/src/scripts/pull_missing_events.ts @@ -29,6 +29,7 @@ let connection: Connection; async function getExchangeEventsAsync(provider: Web3ProviderEngine): Promise { console.log('Checking existing event logs...'); const eventsRepository = connection.getRepository(ExchangeFillEvent); + const manager = connection.createEntityManager(); const startBlock = await getStartBlockAsync(eventsRepository); console.log(`Getting event logs starting at ${startBlock}...`); const exchangeEvents = new ExchangeEventsSource(provider, 1); @@ -37,13 +38,7 @@ async function getExchangeEventsAsync(provider: Web3ProviderEngine): Promise Date: Wed, 14 Nov 2018 17:40:19 -0800 Subject: Fix chunk size in pull_missing_events --- packages/pipeline/src/scripts/pull_missing_events.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pipeline/src/scripts/pull_missing_events.ts b/packages/pipeline/src/scripts/pull_missing_events.ts index b1b8665dd..f53bc12bd 100644 --- a/packages/pipeline/src/scripts/pull_missing_events.ts +++ b/packages/pipeline/src/scripts/pull_missing_events.ts @@ -38,7 +38,7 @@ async function getExchangeEventsAsync(provider: Web3ProviderEngine): Promise Date: Wed, 14 Nov 2018 18:39:06 -0800 Subject: Add workaround for broken save method --- packages/pipeline/src/ormconfig.ts | 2 +- .../pipeline/src/scripts/pull_missing_events.ts | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/packages/pipeline/src/ormconfig.ts b/packages/pipeline/src/ormconfig.ts index 0e489e560..64b5b1827 100644 --- a/packages/pipeline/src/ormconfig.ts +++ b/packages/pipeline/src/ormconfig.ts @@ -26,7 +26,7 @@ const config: ConnectionOptions = { type: 'postgres', url: process.env.ZEROEX_DATA_PIPELINE_DB_URL, synchronize: false, - logging: ['error', 'warn'], + // logging: ['error'], entities, migrations: ['./lib/migrations/**/*.js'], }; diff --git a/packages/pipeline/src/scripts/pull_missing_events.ts b/packages/pipeline/src/scripts/pull_missing_events.ts index f53bc12bd..2dc81f205 100644 --- a/packages/pipeline/src/scripts/pull_missing_events.ts +++ b/packages/pipeline/src/scripts/pull_missing_events.ts @@ -38,7 +38,27 @@ async function getExchangeEventsAsync(provider: Web3ProviderEngine): Promise Date: Wed, 14 Nov 2018 18:47:22 -0800 Subject: Add homepage_url to relayers --- .../migrations/1542249766882-AddHomepageUrlToRelayers.ts | 14 ++++++++++++++ packages/pipeline/src/entities/relayer.ts | 13 +++++++------ packages/pipeline/src/parsers/relayer_registry/index.ts | 2 +- 3 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 packages/pipeline/migrations/1542249766882-AddHomepageUrlToRelayers.ts diff --git a/packages/pipeline/migrations/1542249766882-AddHomepageUrlToRelayers.ts b/packages/pipeline/migrations/1542249766882-AddHomepageUrlToRelayers.ts new file mode 100644 index 000000000..9a4811ad5 --- /dev/null +++ b/packages/pipeline/migrations/1542249766882-AddHomepageUrlToRelayers.ts @@ -0,0 +1,14 @@ +import { MigrationInterface, QueryRunner, TableColumn } from 'typeorm'; + +export class AddHomepageUrlToRelayers1542249766882 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.addColumn( + 'raw.relayers', + new TableColumn({ name: 'homepage_url', type: 'varchar', default: `'unknown'` }), + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropColumn('raw.relayers', 'homepage_url'); + } +} diff --git a/packages/pipeline/src/entities/relayer.ts b/packages/pipeline/src/entities/relayer.ts index ef1f9cf2a..b3a856fd8 100644 --- a/packages/pipeline/src/entities/relayer.ts +++ b/packages/pipeline/src/entities/relayer.ts @@ -5,19 +5,20 @@ export class Relayer { @PrimaryColumn() public uuid!: string; @Column() public name!: string; - @Column() public url!: string; - @Column({ nullable: true, type: String, name: 'sra_http_endpoint' }) + @Column({ name: 'homepage_url', type: 'varchar' }) + public homepageUrl!: string; + @Column({ name: 'sra_http_endpoint', type: 'varchar', nullable: true }) public sraHttpEndpoint!: string | null; - @Column({ nullable: true, type: String, name: 'sra_ws_endpoint' }) + @Column({ name: 'sra_ws_endpoint', type: 'varchar', nullable: true }) public sraWsEndpoint!: string | null; - @Column({ nullable: true, type: String, name: 'app_url' }) + @Column({ name: 'app_url', type: 'varchar', nullable: true }) public appUrl!: string | null; // TODO(albrow): Add exchange contract or protocol version? // TODO(albrow): Add network ids for addresses? - @Column({ type: 'varchar', array: true, name: 'fee_recipient_addresses' }) + @Column({ name: 'fee_recipient_addresses', type: 'varchar', array: true }) public feeRecipientAddresses!: string[]; - @Column({ type: 'varchar', array: true, name: 'taker_addresses' }) + @Column({ name: 'taker_addresses', type: 'varchar', array: true }) public takerAddresses!: string[]; } diff --git a/packages/pipeline/src/parsers/relayer_registry/index.ts b/packages/pipeline/src/parsers/relayer_registry/index.ts index 346f570bd..7b1235005 100644 --- a/packages/pipeline/src/parsers/relayer_registry/index.ts +++ b/packages/pipeline/src/parsers/relayer_registry/index.ts @@ -16,7 +16,7 @@ function parseRelayer(relayerResp: RelayerResponse, uuid: string): Relayer { const relayer = new Relayer(); relayer.uuid = uuid; relayer.name = relayerResp.name; - relayer.url = relayerResp.homepage_url; + relayer.homepageUrl = relayerResp.homepage_url; relayer.appUrl = relayerResp.app_url; const mainnet = getMainNetwork(relayerResp); if (mainnet !== undefined) { -- cgit v1.2.3 From 1a2586208617efca8d26f9c7ac31a46d16a52aba Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Thu, 15 Nov 2018 12:10:27 -0800 Subject: Optimize database operations in pull_missing_events script --- .../pipeline/src/scripts/pull_missing_events.ts | 35 ++++++++++++++++------ 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/packages/pipeline/src/scripts/pull_missing_events.ts b/packages/pipeline/src/scripts/pull_missing_events.ts index 2dc81f205..bceed299c 100644 --- a/packages/pipeline/src/scripts/pull_missing_events.ts +++ b/packages/pipeline/src/scripts/pull_missing_events.ts @@ -8,12 +8,12 @@ import { Connection, ConnectionOptions, createConnection, Repository } from 'typ import { ExchangeEventsSource } from '../data_sources/contract-wrappers/exchange_events'; import { ExchangeFillEvent } from '../entities'; import * as ormConfig from '../ormconfig'; -import { parseExchangeEvents } from '../parsers/events'; +import { ExchangeEventEntity, parseExchangeEvents } from '../parsers/events'; import { handleError } from '../utils'; const EXCHANGE_START_BLOCK = 6271590; // Block number when the Exchange contract was deployed to mainnet. -const START_BLOCK_OFFSET = 1000; // Number of blocks before the last known block to consider when updating fill events. -const BATCH_SAVE_SIZE = 10000; // Number of events to save at once. +const START_BLOCK_OFFSET = 100; // Number of blocks before the last known block to consider when updating fill events. +const BATCH_SAVE_SIZE = 1000; // Number of events to save at once. let connection: Connection; @@ -38,17 +38,36 @@ async function getExchangeEventsAsync(provider: Web3ProviderEngine): Promise, + events: ExchangeEventEntity[], +): Promise { // Note(albrow): This is a temporary hack because `save` is not working as // documented and is causing a foreign key constraint violation. Hopefully // can remove later because this "poor man's upsert" implementation operates // on one event at a time and is therefore much slower. - // await eventsRepository.save(events, { chunk: Math.ceil(events.length / BATCH_SAVE_SIZE) }); for (const event of events) { try { - await eventsRepository.save(event); + // First try and insert. + await eventsRepository.insert(event); } catch { - // Assume this is a foreign key constraint error and try doing an - // update instead. + // If it fails, assume it was a foreign key constraint error and try + // doing an update instead. await eventsRepository.update( { contractAddress: event.contractAddress, @@ -59,8 +78,6 @@ async function getExchangeEventsAsync(provider: Web3ProviderEngine): Promise): Promise { -- cgit v1.2.3 From 0397ff8b2206ab4e9308a524d635ec37ec9dda99 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Thu, 15 Nov 2018 13:09:44 -0800 Subject: Add transactionHash to CancelUpToEvent and CancelEvent entities --- packages/pipeline/src/entities/exchange_cancel_event.ts | 3 ++- packages/pipeline/src/entities/exchange_cancel_up_to_event.ts | 2 ++ packages/pipeline/src/parsers/events/index.ts | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/pipeline/src/entities/exchange_cancel_event.ts b/packages/pipeline/src/entities/exchange_cancel_event.ts index 781e1c4bb..5a40ba799 100644 --- a/packages/pipeline/src/entities/exchange_cancel_event.ts +++ b/packages/pipeline/src/entities/exchange_cancel_event.ts @@ -14,7 +14,8 @@ export class ExchangeCancelEvent { @Column({ name: 'raw_data' }) public rawData!: string; - // TODO(albrow): Include transaction hash + @Column({ name: 'transaction_hash' }) + public transactionHash!: string; @Column({ name: 'maker_address' }) public makerAddress!: string; @Column({ nullable: true, type: String, name: 'taker_address' }) diff --git a/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts b/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts index 752631b85..9b1c6174a 100644 --- a/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts +++ b/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts @@ -16,6 +16,8 @@ export class ExchangeCancelUpToEvent { @Column({ name: 'raw_data' }) public rawData!: string; + @Column({ name: 'transaction_hash' }) + public transactionHash!: string; @Column({ name: 'maker_address' }) public makerAddress!: string; @Column({ name: 'sender_address' }) diff --git a/packages/pipeline/src/parsers/events/index.ts b/packages/pipeline/src/parsers/events/index.ts index 27e192d7b..407883078 100644 --- a/packages/pipeline/src/parsers/events/index.ts +++ b/packages/pipeline/src/parsers/events/index.ts @@ -103,6 +103,7 @@ export function _convertToExchangeCancelEvent( exchangeCancelEvent.blockNumber = eventLog.blockNumber as number; exchangeCancelEvent.logIndex = eventLog.logIndex as number; exchangeCancelEvent.rawData = eventLog.data as string; + exchangeCancelEvent.transactionHash = eventLog.transactionHash; exchangeCancelEvent.makerAddress = eventLog.args.makerAddress.toString(); exchangeCancelEvent.takerAddress = eventLog.args.takerAddress == null ? null : eventLog.args.takerAddress.toString(); @@ -137,6 +138,7 @@ export function _convertToExchangeCancelUpToEvent( exchangeCancelUpToEvent.blockNumber = eventLog.blockNumber as number; exchangeCancelUpToEvent.logIndex = eventLog.logIndex as number; exchangeCancelUpToEvent.rawData = eventLog.data as string; + exchangeCancelUpToEvent.transactionHash = eventLog.transactionHash; exchangeCancelUpToEvent.makerAddress = eventLog.args.makerAddress.toString(); exchangeCancelUpToEvent.senderAddress = eventLog.args.senderAddress.toString(); exchangeCancelUpToEvent.orderEpoch = eventLog.args.orderEpoch; -- cgit v1.2.3 From 24fd2d9730d58a58929f401674175ad8a5a7fbc1 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Fri, 16 Nov 2018 12:55:54 -0800 Subject: Add support for pulling Cancel and CancelUpTo events --- .../1542401122477-MakeTakerAddressNullable.ts | 17 +++ .../contract-wrappers/exchange_events.ts | 50 +++++++-- packages/pipeline/src/entities/index.ts | 6 + packages/pipeline/src/parsers/events/index.ts | 58 ++++------ .../pipeline/src/scripts/pull_missing_events.ts | 123 ++++++++++++++------- .../pipeline/test/parsers/events/index_test.ts | 6 +- 6 files changed, 170 insertions(+), 90 deletions(-) create mode 100644 packages/pipeline/migrations/1542401122477-MakeTakerAddressNullable.ts diff --git a/packages/pipeline/migrations/1542401122477-MakeTakerAddressNullable.ts b/packages/pipeline/migrations/1542401122477-MakeTakerAddressNullable.ts new file mode 100644 index 000000000..957c85a36 --- /dev/null +++ b/packages/pipeline/migrations/1542401122477-MakeTakerAddressNullable.ts @@ -0,0 +1,17 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class MakeTakerAddressNullable1542401122477 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE raw.exchange_cancel_events + ALTER COLUMN taker_address DROP NOT NULL;`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE raw.exchange_cancel_events + ALTER COLUMN taker_address SET NOT NULL;`, + ); + } +} diff --git a/packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts b/packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts index e25c6a731..d09f59535 100644 --- a/packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts +++ b/packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts @@ -1,4 +1,12 @@ -import { ContractWrappers, ExchangeEvents, ExchangeFillEventArgs, ExchangeWrapper } from '@0x/contract-wrappers'; +import { + ContractWrappers, + ExchangeCancelEventArgs, + ExchangeCancelUpToEventArgs, + ExchangeEventArgs, + ExchangeEvents, + ExchangeFillEventArgs, + ExchangeWrapper, +} from '@0x/contract-wrappers'; import { Web3ProviderEngine } from '@0x/subproviders'; import { Web3Wrapper } from '@0x/web3-wrapper'; import { LogWithDecodedArgs } from 'ethereum-types'; @@ -16,20 +24,41 @@ export class ExchangeEventsSource { this._exchangeWrapper = contractWrappers.exchange; } - // TODO(albrow): Get Cancel and CancelUpTo events. - public async getFillEventsAsync( - fromBlock: number = EXCHANGE_START_BLOCK, + fromBlock?: number, toBlock?: number, ): Promise>> { + return this._getEventsAsync(ExchangeEvents.Fill, fromBlock, toBlock); + } + + public async getCancelEventsAsync( + fromBlock?: number, + toBlock?: number, + ): Promise>> { + return this._getEventsAsync(ExchangeEvents.Cancel, fromBlock, toBlock); + } + + public async getCancelUpToEventsAsync( + fromBlock?: number, + toBlock?: number, + ): Promise>> { + return this._getEventsAsync(ExchangeEvents.CancelUpTo, fromBlock, toBlock); + } + + private async _getEventsAsync( + eventName: ExchangeEvents, + fromBlock: number = EXCHANGE_START_BLOCK, + toBlock?: number, + ): Promise>> { const calculatedToBlock = toBlock === undefined ? (await this._web3Wrapper.getBlockNumberAsync()) - BLOCK_FINALITY_THRESHOLD : toBlock; - let events: Array> = []; + let events: Array> = []; for (let currFromBlock = fromBlock; currFromBlock <= calculatedToBlock; currFromBlock += NUM_BLOCKS_PER_QUERY) { events = events.concat( - await this._getFillEventsForRangeAsync( + await this._getEventsForRangeAsync( + eventName, currFromBlock, Math.min(currFromBlock + NUM_BLOCKS_PER_QUERY - 1, calculatedToBlock), ), @@ -38,12 +67,13 @@ export class ExchangeEventsSource { return events; } - private async _getFillEventsForRangeAsync( + private async _getEventsForRangeAsync( + eventName: ExchangeEvents, fromBlock: number, toBlock: number, - ): Promise>> { - return this._exchangeWrapper.getLogsAsync( - ExchangeEvents.Fill, + ): Promise>> { + return this._exchangeWrapper.getLogsAsync( + eventName, { fromBlock, toBlock, diff --git a/packages/pipeline/src/entities/index.ts b/packages/pipeline/src/entities/index.ts index 442eec9cd..d3c61bcb9 100644 --- a/packages/pipeline/src/entities/index.ts +++ b/packages/pipeline/src/entities/index.ts @@ -1,3 +1,7 @@ +import { ExchangeCancelEvent } from './exchange_cancel_event'; +import { ExchangeCancelUpToEvent } from './exchange_cancel_up_to_event'; +import { ExchangeFillEvent } from './exchange_fill_event'; + export { Block } from './block'; export { ExchangeCancelEvent } from './exchange_cancel_event'; export { ExchangeCancelUpToEvent } from './exchange_cancel_up_to_event'; @@ -7,3 +11,5 @@ export { SraOrder } from './sra_order'; export { Transaction } from './transaction'; export { TokenOnChainMetadata } from './token_on_chain_metadata'; export { SraOrdersObservedTimeStamp, createObservedTimestampForOrder } from './sra_order_observed_timestamp'; + +export type ExchangeEvent = ExchangeFillEvent | ExchangeCancelEvent | ExchangeCancelUpToEvent; diff --git a/packages/pipeline/src/parsers/events/index.ts b/packages/pipeline/src/parsers/events/index.ts index 407883078..d42d1c57a 100644 --- a/packages/pipeline/src/parsers/events/index.ts +++ b/packages/pipeline/src/parsers/events/index.ts @@ -1,9 +1,4 @@ -import { - ExchangeCancelEventArgs, - ExchangeCancelUpToEventArgs, - ExchangeEventArgs, - ExchangeFillEventArgs, -} from '@0x/contract-wrappers'; +import { ExchangeCancelEventArgs, ExchangeCancelUpToEventArgs, ExchangeFillEventArgs } from '@0x/contract-wrappers'; import { assetDataUtils } from '@0x/order-utils'; import { AssetProxyId, ERC721AssetData } from '@0x/types'; import { LogWithDecodedArgs } from 'ethereum-types'; @@ -12,39 +7,32 @@ import * as R from 'ramda'; import { ExchangeCancelEvent, ExchangeCancelUpToEvent, ExchangeFillEvent } from '../../entities'; import { bigNumbertoStringOrNull } from '../../utils'; -export type ExchangeEventEntity = ExchangeFillEvent | ExchangeCancelEvent | ExchangeCancelUpToEvent; +/** + * Parses raw event logs for a fill event and returns an array of + * ExchangeFillEvent entities. + * @param eventLogs Raw event logs (e.g. returned from contract-wrappers). + */ +export const parseExchangeFillEvents: ( + eventLogs: Array>, +) => ExchangeFillEvent[] = R.map(_convertToExchangeFillEvent); -export const parseExchangeEvents: ( - eventLogs: Array>, -) => ExchangeEventEntity[] = R.map(_convertToEntity); +/** + * Parses raw event logs for a cancel event and returns an array of + * ExchangeCancelEvent entities. + * @param eventLogs Raw event logs (e.g. returned from contract-wrappers). + */ +export const parseExchangeCancelEvents: ( + eventLogs: Array>, +) => ExchangeCancelEvent[] = R.map(_convertToExchangeCancelEvent); /** - * Converts a raw event log to an Entity. Automatically detects the type of - * event and returns the appropriate entity type. Throws for unknown event - * types. - * @param eventLog Raw event log (e.g. returned from contract-wrappers). + * Parses raw event logs for a CancelUpTo event and returns an array of + * ExchangeCancelUpToEvent entities. + * @param eventLogs Raw event logs (e.g. returned from contract-wrappers). */ -export function _convertToEntity(eventLog: LogWithDecodedArgs): ExchangeEventEntity { - switch (eventLog.event) { - case 'Fill': - // tslint has a false positive here. We need to type assert in order - // to change the type argument to the more specific - // ExchangeFillEventArgs. - // tslint:disable-next-line:no-unnecessary-type-assertion - return _convertToExchangeFillEvent(eventLog as LogWithDecodedArgs); - case 'Cancel': - // tslint:disable-next-line:no-unnecessary-type-assertion - return _convertToExchangeCancelEvent(eventLog as LogWithDecodedArgs); - case 'CancelUpTo': - // tslint:disable-next-line:no-unnecessary-type-assertion - return _convertToExchangeCancelUpToEvent(eventLog as LogWithDecodedArgs); - default: - // Another false positive here. We are adding two strings, but - // tslint seems confused about the types. - // tslint:disable-next-line:restrict-plus-operands - throw new Error('unexpected eventLog.event type: ' + eventLog.event); - } -} +export const parseExchangeCancelUpToEvents: ( + eventLogs: Array>, +) => ExchangeCancelUpToEvent[] = R.map(_convertToExchangeCancelUpToEvent); /** * Converts a raw event log for a fill event into an ExchangeFillEvent entity. diff --git a/packages/pipeline/src/scripts/pull_missing_events.ts b/packages/pipeline/src/scripts/pull_missing_events.ts index bceed299c..b2a99e3c0 100644 --- a/packages/pipeline/src/scripts/pull_missing_events.ts +++ b/packages/pipeline/src/scripts/pull_missing_events.ts @@ -1,14 +1,13 @@ // tslint:disable:no-console import { web3Factory } from '@0x/dev-utils'; -import { Web3ProviderEngine } from '@0x/subproviders'; import R = require('ramda'); import 'reflect-metadata'; import { Connection, ConnectionOptions, createConnection, Repository } from 'typeorm'; import { ExchangeEventsSource } from '../data_sources/contract-wrappers/exchange_events'; -import { ExchangeFillEvent } from '../entities'; +import { ExchangeCancelEvent, ExchangeCancelUpToEvent, ExchangeEvent, ExchangeFillEvent } from '../entities'; import * as ormConfig from '../ormconfig'; -import { ExchangeEventEntity, parseExchangeEvents } from '../parsers/events'; +import { parseExchangeCancelEvents, parseExchangeCancelUpToEvents, parseExchangeFillEvents } from '../parsers/events'; import { handleError } from '../utils'; const EXCHANGE_START_BLOCK = 6271590; // Block number when the Exchange contract was deployed to mainnet. @@ -22,40 +21,88 @@ let connection: Connection; const provider = web3Factory.getRpcProvider({ rpcUrl: 'https://mainnet.infura.io', }); - await getExchangeEventsAsync(provider); + const eventsSource = new ExchangeEventsSource(provider, 1); + await getFillEventsAsync(eventsSource); + await getCancelEventsAsync(eventsSource); + await getCancelUpToEventsAsync(eventsSource); process.exit(0); })().catch(handleError); -async function getExchangeEventsAsync(provider: Web3ProviderEngine): Promise { - console.log('Checking existing event logs...'); - const eventsRepository = connection.getRepository(ExchangeFillEvent); - const manager = connection.createEntityManager(); - const startBlock = await getStartBlockAsync(eventsRepository); - console.log(`Getting event logs starting at ${startBlock}...`); - const exchangeEvents = new ExchangeEventsSource(provider, 1); - const eventLogs = await exchangeEvents.getFillEventsAsync(startBlock); - console.log('Parsing events...'); - const events = parseExchangeEvents(eventLogs); - console.log(`Retrieved and parsed ${events.length} total events.`); - console.log('Saving events...'); - if (startBlock === EXCHANGE_START_BLOCK) { +async function getFillEventsAsync(eventsSource: ExchangeEventsSource): Promise { + console.log('Checking existing fill events...'); + const repository = connection.getRepository(ExchangeFillEvent); + const startBlock = await getStartBlockAsync(repository); + console.log(`Getting fill events starting at ${startBlock}...`); + const eventLogs = await eventsSource.getFillEventsAsync(startBlock); + console.log('Parsing fill events...'); + const events = parseExchangeFillEvents(eventLogs); + console.log(`Retrieved and parsed ${events.length} total fill events.`); + await saveEventsAsync(startBlock === EXCHANGE_START_BLOCK, repository, events); +} + +async function getCancelEventsAsync(eventsSource: ExchangeEventsSource): Promise { + console.log('Checking existing cancel events...'); + const repository = connection.getRepository(ExchangeCancelEvent); + const startBlock = await getStartBlockAsync(repository); + console.log(`Getting cancel events starting at ${startBlock}...`); + const eventLogs = await eventsSource.getCancelEventsAsync(startBlock); + console.log('Parsing cancel events...'); + const events = parseExchangeCancelEvents(eventLogs); + console.log(`Retrieved and parsed ${events.length} total cancel events.`); + await saveEventsAsync(startBlock === EXCHANGE_START_BLOCK, repository, events); +} + +async function getCancelUpToEventsAsync(eventsSource: ExchangeEventsSource): Promise { + console.log('Checking existing CancelUpTo events...'); + const repository = connection.getRepository(ExchangeCancelUpToEvent); + const startBlock = await getStartBlockAsync(repository); + console.log(`Getting CancelUpTo events starting at ${startBlock}...`); + const eventLogs = await eventsSource.getCancelUpToEventsAsync(startBlock); + console.log('Parsing CancelUpTo events...'); + const events = parseExchangeCancelUpToEvents(eventLogs); + console.log(`Retrieved and parsed ${events.length} total CancelUpTo events.`); + await saveEventsAsync(startBlock === EXCHANGE_START_BLOCK, repository, events); +} + +async function getStartBlockAsync(repository: Repository): Promise { + const fillEventCount = await repository.count(); + if (fillEventCount === 0) { + console.log(`No existing ${repository.metadata.name}s found.`); + return EXCHANGE_START_BLOCK; + } + const queryResult = await connection.query( + // TODO(albrow): Would prefer to use a prepared statement here to reduce + // surface area for SQL injections, but it doesn't appear to be working. + `SELECT block_number FROM raw.${repository.metadata.tableName} ORDER BY block_number DESC LIMIT 1`, + ); + const lastKnownBlock = queryResult[0].block_number; + return lastKnownBlock - START_BLOCK_OFFSET; +} + +async function saveEventsAsync( + isInitialPull: boolean, + repository: Repository, + events: T[], +): Promise { + console.log(`Saving ${repository.metadata.name}s...`); + if (isInitialPull) { // Split data into numChunks pieces of maximum size BATCH_SAVE_SIZE // each. for (const eventsBatch of R.splitEvery(BATCH_SAVE_SIZE, events)) { - await eventsRepository.insert(eventsBatch); + await repository.insert(eventsBatch); } } else { // If we possibly have some overlap where we need to update some // existing events, we need to use our workaround/fallback. - await saveIndividuallyWithFallbackAsync(eventsRepository, events); + await saveIndividuallyWithFallbackAsync(repository, events); } - const totalEvents = await eventsRepository.count(); - console.log(`Done saving events. There are now ${totalEvents} total events.`); + const totalEvents = await repository.count(); + console.log(`Done saving events. There are now ${totalEvents} total ${repository.metadata.name}s.`); } -async function saveIndividuallyWithFallbackAsync( - eventsRepository: Repository, - events: ExchangeEventEntity[], +async function saveIndividuallyWithFallbackAsync( + repository: Repository, + events: T[], ): Promise { // Note(albrow): This is a temporary hack because `save` is not working as // documented and is causing a foreign key constraint violation. Hopefully @@ -63,32 +110,24 @@ async function saveIndividuallyWithFallbackAsync( // on one event at a time and is therefore much slower. for (const event of events) { try { - // First try and insert. - await eventsRepository.insert(event); + // First try an insert. + await repository.insert(event); } catch { // If it fails, assume it was a foreign key constraint error and try // doing an update instead. - await eventsRepository.update( + // Note(albrow): Unfortunately the `as any` hack here seems + // required. I can't figure out how to convince the type-checker + // that the criteria and the entity itself are the correct type for + // the given repository. If we can remove the `save` hack then this + // will probably no longer be necessary. + await repository.update( { contractAddress: event.contractAddress, blockNumber: event.blockNumber, logIndex: event.logIndex, - }, - event, + } as any, + event as any, ); } } } - -async function getStartBlockAsync(eventsRepository: Repository): Promise { - const fillEventCount = await eventsRepository.count(); - if (fillEventCount === 0) { - console.log('No existing fill events found.'); - return EXCHANGE_START_BLOCK; - } - const queryResult = await connection.query( - 'SELECT block_number FROM raw.exchange_fill_events ORDER BY block_number DESC LIMIT 1', - ); - const lastKnownBlock = queryResult[0].block_number; - return lastKnownBlock - START_BLOCK_OFFSET; -} diff --git a/packages/pipeline/test/parsers/events/index_test.ts b/packages/pipeline/test/parsers/events/index_test.ts index 63e080edc..7e439ce39 100644 --- a/packages/pipeline/test/parsers/events/index_test.ts +++ b/packages/pipeline/test/parsers/events/index_test.ts @@ -5,7 +5,7 @@ import { LogWithDecodedArgs } from 'ethereum-types'; import 'mocha'; import { ExchangeFillEvent } from '../../../src/entities'; -import { _convertToEntity } from '../../../src/parsers/events'; +import { _convertToExchangeFillEvent } from '../../../src/parsers/events'; import { chaiSetup } from '../../utils/chai_setup'; chaiSetup.configure(); @@ -13,7 +13,7 @@ const expect = chai.expect; // tslint:disable:custom-no-magic-numbers describe('exchange_events', () => { - describe('_convertToEntity', () => { + describe('_convertToExchangeFillEvent', () => { it('converts LogWithDecodedArgs to ExchangeFillEvent entity', () => { const input: LogWithDecodedArgs = { logIndex: 102, @@ -71,7 +71,7 @@ describe('exchange_events', () => { expected.takerAssetProxyId = '0xf47261b0'; expected.takerTokenAddress = '0xe41d2489571d322189246dafa5ebde1f4699f498'; expected.takerTokenId = null; - const actual = _convertToEntity(input); + const actual = _convertToExchangeFillEvent(input); expect(actual).deep.equal(expected); }); }); -- cgit v1.2.3 From 5cad2ad1744ab1c1e24ed52fc0a26ec5acf5c898 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Fri, 16 Nov 2018 13:16:17 -0800 Subject: Check for special characters in table name in pull_missing_events --- packages/pipeline/src/scripts/pull_missing_events.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/pipeline/src/scripts/pull_missing_events.ts b/packages/pipeline/src/scripts/pull_missing_events.ts index b2a99e3c0..0b7f6287f 100644 --- a/packages/pipeline/src/scripts/pull_missing_events.ts +++ b/packages/pipeline/src/scripts/pull_missing_events.ts @@ -64,16 +64,20 @@ async function getCancelUpToEventsAsync(eventsSource: ExchangeEventsSource): Pro await saveEventsAsync(startBlock === EXCHANGE_START_BLOCK, repository, events); } +const tabelNameRegex = /^[a-zA-Z_]*$/; + async function getStartBlockAsync(repository: Repository): Promise { const fillEventCount = await repository.count(); if (fillEventCount === 0) { console.log(`No existing ${repository.metadata.name}s found.`); return EXCHANGE_START_BLOCK; } + const tableName = repository.metadata.tableName; + if (!tabelNameRegex.test(tableName)) { + throw new Error('Unexpected special character in table name: ' + tableName); + } const queryResult = await connection.query( - // TODO(albrow): Would prefer to use a prepared statement here to reduce - // surface area for SQL injections, but it doesn't appear to be working. - `SELECT block_number FROM raw.${repository.metadata.tableName} ORDER BY block_number DESC LIMIT 1`, + `SELECT block_number FROM raw.${tableName} ORDER BY block_number DESC LIMIT 1`, ); const lastKnownBlock = queryResult[0].block_number; return lastKnownBlock - START_BLOCK_OFFSET; -- cgit v1.2.3 From 9986717671fe8e14c2168f7479bdaffe406bedc0 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Mon, 19 Nov 2018 18:38:11 -0800 Subject: Add script for pulling missing block data --- packages/pipeline/package.json | 3 +- packages/pipeline/src/entities/block.ts | 4 +- packages/pipeline/src/parsers/web3/index.ts | 5 +- .../pipeline/src/scripts/pull_missing_blocks.ts | 83 ++++++++++++++++++++++ yarn.lock | 4 ++ 5 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 packages/pipeline/src/scripts/pull_missing_blocks.ts diff --git a/packages/pipeline/package.json b/packages/pipeline/package.json index dcd19d150..a57fbf5bc 100644 --- a/packages/pipeline/package.json +++ b/packages/pipeline/package.json @@ -30,8 +30,8 @@ "license": "Apache-2.0", "devDependencies": { "@0x/tslint-config": "^1.0.9", - "@types/ramda": "^0.25.38", "@types/axios": "^0.14.0", + "@types/ramda": "^0.25.38", "chai": "^4.1.2", "chai-as-promised": "^7.1.1", "chai-bignumber": "^2.0.2", @@ -50,6 +50,7 @@ "@0x/types": "^1.2.0", "@0x/utils": "^2.0.3", "@0x/web3-wrapper": "^3.1.0", + "async-parallel": "^1.2.3", "axios": "^0.18.0", "ethereum-types": "^1.0.6", "pg": "^7.5.0", diff --git a/packages/pipeline/src/entities/block.ts b/packages/pipeline/src/entities/block.ts index 51be37703..f2efc6390 100644 --- a/packages/pipeline/src/entities/block.ts +++ b/packages/pipeline/src/entities/block.ts @@ -5,6 +5,6 @@ export class Block { @PrimaryColumn() public hash!: string; @PrimaryColumn() public number!: number; - @Column({ name: 'unix_timestamp_seconds' }) - public unixTimestampSeconds!: number; + @Column({ name: 'timestamp' }) + public timestamp!: number; } diff --git a/packages/pipeline/src/parsers/web3/index.ts b/packages/pipeline/src/parsers/web3/index.ts index 2ead4c0e4..9b5b3b55d 100644 --- a/packages/pipeline/src/parsers/web3/index.ts +++ b/packages/pipeline/src/parsers/web3/index.ts @@ -2,6 +2,8 @@ import { BlockWithoutTransactionData, Transaction as EthTransaction } from 'ethe import { Block, Transaction } from '../../entities'; +const MILLISECONDS_PER_SECOND = 1000; + /** * Parses a raw block and returns a Block entity. * @param rawBlock a raw block (e.g. returned from web3-wrapper). @@ -17,7 +19,8 @@ export function parseBlock(rawBlock: BlockWithoutTransactionData): Block { const block = new Block(); block.hash = rawBlock.hash; block.number = rawBlock.number; - block.unixTimestampSeconds = rawBlock.timestamp; + // Block timestamps are in seconds, but we use milliseconds everywhere else. + block.timestamp = rawBlock.timestamp * MILLISECONDS_PER_SECOND; return block; } diff --git a/packages/pipeline/src/scripts/pull_missing_blocks.ts b/packages/pipeline/src/scripts/pull_missing_blocks.ts new file mode 100644 index 000000000..4a1483ab9 --- /dev/null +++ b/packages/pipeline/src/scripts/pull_missing_blocks.ts @@ -0,0 +1,83 @@ +// tslint:disable:no-console +import { web3Factory } from '@0x/dev-utils'; +import * as Parallel from 'async-parallel'; +import R = require('ramda'); +import 'reflect-metadata'; +import { Connection, ConnectionOptions, createConnection, Repository } from 'typeorm'; + +import { Web3Source } from '../data_sources/web3'; +import { Block } from '../entities'; +import * as ormConfig from '../ormconfig'; +import { parseBlock } from '../parsers/web3'; +import { handleError } from '../utils'; + +// Number of blocks to save at once. +const BATCH_SAVE_SIZE = 1000; +// Maximum number of requests to send at once. +const MAX_CONCURRENCY = 10; +// Maximum number of blocks to query for at once. This is also the maximum +// number of blocks we will hold in memory prior to being saved to the database. +const MAX_BLOCKS_PER_QUERY = 1000; +// Block number when the Exchange contract was deployed to mainnet. +// TODO(albrow): De-dupe this constant. +const EXCHANGE_START_BLOCK = 6271590; + +let connection: Connection; + +(async () => { + connection = await createConnection(ormConfig as ConnectionOptions); + const provider = web3Factory.getRpcProvider({ + rpcUrl: `https://mainnet.infura.io/${process.env.INFURA_API_KEY}`, + }); + const web3Source = new Web3Source(provider); + await getAllMissingBlocks(web3Source); + process.exit(0); +})().catch(handleError); + +interface MissingBlocksResponse { + block_number: string; +} + +async function getAllMissingBlocks(web3Source: Web3Source): Promise { + const blocksRepository = connection.getRepository(Block); + let fromBlock = EXCHANGE_START_BLOCK; + while (true) { + const blockNumbers = await getMissingBlockNumbers(fromBlock); + if (blockNumbers.length === 0) { + // There are no more missing blocks. We're done. + break; + } + await getAndSaveBlocks(web3Source, blocksRepository, blockNumbers); + fromBlock = Math.max(...blockNumbers) + 1; + } + const totalBlocks = await blocksRepository.count(); + console.log(`Done saving blocks. There are now ${totalBlocks} total blocks.`); +} + +async function getMissingBlockNumbers(fromBlock: number): Promise { + console.log(`Checking for missing blocks starting at ${fromBlock}...`); + const response = (await connection.query( + 'SELECT DISTINCT(block_number) FROM raw.exchange_fill_events WHERE block_number NOT IN (SELECT number FROM raw.blocks) AND block_number >= $1 ORDER BY block_number ASC LIMIT $2', + [fromBlock, MAX_BLOCKS_PER_QUERY], + )) as MissingBlocksResponse[]; + const blockNumberStrings = R.pluck('block_number', response); + const blockNumbers = R.map(parseInt, blockNumberStrings); + console.log(`Found ${blockNumbers.length} missing blocks in the given range.`); + return blockNumbers; +} + +async function getAndSaveBlocks( + web3Source: Web3Source, + blocksRepository: Repository, + blockNumbers: number[], +): Promise { + console.log(`Getting block data for ${blockNumbers.length} blocks...`); + Parallel.setConcurrency(MAX_CONCURRENCY); + const rawBlocks = await Parallel.map(blockNumbers, async (blockNumber: number) => + web3Source.getBlockInfoAsync(blockNumber), + ); + console.log(`Parsing ${rawBlocks.length} blocks...`); + const blocks = R.map(parseBlock, rawBlocks); + console.log(`Saving ${blocks.length} blocks...`); + await blocksRepository.save(blocks, { chunk: Math.ceil(blocks.length / BATCH_SAVE_SIZE) }); +} diff --git a/yarn.lock b/yarn.lock index b1de13e68..88747eee8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2325,6 +2325,10 @@ async-limiter@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" +async-parallel@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/async-parallel/-/async-parallel-1.2.3.tgz#0b90550aeffb7a365d8cee881eb0618f656a3450" + async@1.x, async@^1.4.0, async@^1.4.2, async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" -- cgit v1.2.3 From c6af5131b0b06433d6294260274e187ad61f4ef7 Mon Sep 17 00:00:00 2001 From: Jake Ellowitz Date: Mon, 19 Nov 2018 16:24:07 -0800 Subject: Pull token metadata re trusted tokens --- .../1542655823221-NewMetadataAndOHLCVTables.ts | 46 +++++++++++++++++++ .../src/data_sources/trusted_tokens/index.ts | 29 ++++++++++++ packages/pipeline/src/entities/index.ts | 2 + packages/pipeline/src/entities/ohlcv_external.ts | 20 +++++++++ .../src/entities/token_on_chain_metadata.ts | 6 +++ packages/pipeline/src/entities/trusted_tokens.ts | 7 +++ packages/pipeline/src/ormconfig.ts | 2 + .../pipeline/src/parsers/ohlcv_external/index.ts | 0 .../pipeline/src/parsers/trusted_tokens/index.ts | 37 ++++++++++++++++ .../pipeline/src/scripts/pull_missing_events.ts | 4 +- .../pipeline/src/scripts/pull_trusted_tokens.ts | 51 ++++++++++++++++++++++ packages/pipeline/src/utils/index.ts | 6 +++ 12 files changed, 208 insertions(+), 2 deletions(-) create mode 100644 packages/pipeline/migrations/1542655823221-NewMetadataAndOHLCVTables.ts create mode 100644 packages/pipeline/src/data_sources/trusted_tokens/index.ts create mode 100644 packages/pipeline/src/entities/ohlcv_external.ts create mode 100644 packages/pipeline/src/entities/trusted_tokens.ts create mode 100644 packages/pipeline/src/parsers/ohlcv_external/index.ts create mode 100644 packages/pipeline/src/parsers/trusted_tokens/index.ts create mode 100644 packages/pipeline/src/scripts/pull_trusted_tokens.ts diff --git a/packages/pipeline/migrations/1542655823221-NewMetadataAndOHLCVTables.ts b/packages/pipeline/migrations/1542655823221-NewMetadataAndOHLCVTables.ts new file mode 100644 index 000000000..5c7ec6de7 --- /dev/null +++ b/packages/pipeline/migrations/1542655823221-NewMetadataAndOHLCVTables.ts @@ -0,0 +1,46 @@ +import {MigrationInterface, QueryRunner} from 'typeorm'; + +export class NewMetadataAndOHLCVTables1542655823221 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + + await queryRunner.query(` + CREATE TABLE raw.trusted_tokens ( + address VARCHAR NOT NULL, + authority VARCHAR NOT NULL, + + PRIMARY KEY (address, authority) + ); + `); + + await queryRunner.query(` + CREATE TABLE raw.ohlcv_external ( + exchange VARCHAR NOT NULL, + from_symbol VARCHAR NOT NULL, + to_symbol VARCHAR NOT NULL, + start_time BIGINT NOT NULL, + end_time BIGINT NOT NULL, + + open DOUBLE PRECISION NOT NULL, + close DOUBLE PRECISION NOT NULL, + low DOUBLE PRECISION NOT NULL, + high DOUBLE PRECISION NOT NULL, + volume_from DOUBLE PRECISION NOT NULL, + volume_to DOUBLE PRECISION NOT NULL, + + source VARCHAR NOT NULL, + observed_timestamp BIGINT NOT NULL, + + PRIMARY KEY (exchange, from_symbol, to_symbol, start_time, end_time, source, observed_timestamp) + ); + `); + } + + public async down(queryRunner: QueryRunner): Promise { + + await queryRunner.dropTable('raw.trusted_tokens'); + + await queryRunner.dropTable('raw.ohlcv_external'); + } + +} diff --git a/packages/pipeline/src/data_sources/trusted_tokens/index.ts b/packages/pipeline/src/data_sources/trusted_tokens/index.ts new file mode 100644 index 000000000..552739fb9 --- /dev/null +++ b/packages/pipeline/src/data_sources/trusted_tokens/index.ts @@ -0,0 +1,29 @@ +import axios from 'axios'; + +export interface ZeroExTrustedTokenMeta { + address: string; + name: string; + symbol: string; + decimals: number; +} + +export interface MetamaskTrustedTokenMeta { + address: string; + name: string; + erc20: boolean; + symbol: string; + decimals: number; +} + +export class TrustedTokenSource { + private readonly _url: string; + + constructor(url: string) { + this._url = url; + } + + public async getTrustedTokenMetaAsync(): Promise { + const resp = await axios.get(this._url); + return resp.data; + } +} diff --git a/packages/pipeline/src/entities/index.ts b/packages/pipeline/src/entities/index.ts index d3c61bcb9..769efb092 100644 --- a/packages/pipeline/src/entities/index.ts +++ b/packages/pipeline/src/entities/index.ts @@ -10,6 +10,8 @@ export { Relayer } from './relayer'; export { SraOrder } from './sra_order'; export { Transaction } from './transaction'; export { TokenOnChainMetadata } from './token_on_chain_metadata'; +export { TrustedToken } from './trusted_tokens'; export { SraOrdersObservedTimeStamp, createObservedTimestampForOrder } from './sra_order_observed_timestamp'; +export { OHLCVExternal } from './ohlcv_external'; export type ExchangeEvent = ExchangeFillEvent | ExchangeCancelEvent | ExchangeCancelUpToEvent; diff --git a/packages/pipeline/src/entities/ohlcv_external.ts b/packages/pipeline/src/entities/ohlcv_external.ts new file mode 100644 index 000000000..3fbb4c499 --- /dev/null +++ b/packages/pipeline/src/entities/ohlcv_external.ts @@ -0,0 +1,20 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +@Entity({ name: 'ohlcv_external', schema: 'raw' }) +export class OHLCVExternal { + @PrimaryColumn() public exchange!: string; + @PrimaryColumn() public from_symbol!: string; + @PrimaryColumn() public to_symbol!: string; + @PrimaryColumn() public start_time!: number; + @PrimaryColumn() public end_time!: number; + + @Column() public open!: number; + @Column() public close!: number; + @Column() public low!: number; + @Column() public high!: number; + @Column() public volume_from!: number; + @Column() public volume_to!: number; + + @PrimaryColumn() public source!: string; + @PrimaryColumn() public observed_timestamp!: number; +} \ No newline at end of file diff --git a/packages/pipeline/src/entities/token_on_chain_metadata.ts b/packages/pipeline/src/entities/token_on_chain_metadata.ts index 325d28e91..ca1f1827b 100644 --- a/packages/pipeline/src/entities/token_on_chain_metadata.ts +++ b/packages/pipeline/src/entities/token_on_chain_metadata.ts @@ -14,3 +14,9 @@ export class TokenOnChainMetadata { @Column({ type: 'nvarchar', nullable: false }) public name!: string; } + +@Entity({ name: 'trusted_tokens', schema: 'raw' }) +export class TrustedTokens { + @PrimaryColumn() public address!: string; + @PrimaryColumn() public authority!: string; +} diff --git a/packages/pipeline/src/entities/trusted_tokens.ts b/packages/pipeline/src/entities/trusted_tokens.ts new file mode 100644 index 000000000..6ec27c963 --- /dev/null +++ b/packages/pipeline/src/entities/trusted_tokens.ts @@ -0,0 +1,7 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +@Entity({ name: 'trusted_tokens', schema: 'raw' }) +export class TrustedToken { + @PrimaryColumn() public address!: string; + @PrimaryColumn() public authority!: string; +} diff --git a/packages/pipeline/src/ormconfig.ts b/packages/pipeline/src/ormconfig.ts index 64b5b1827..1bd337866 100644 --- a/packages/pipeline/src/ormconfig.ts +++ b/packages/pipeline/src/ormconfig.ts @@ -9,6 +9,7 @@ import { SraOrder, SraOrdersObservedTimeStamp, Transaction, + TrustedToken, } from './entities'; const entities = [ @@ -20,6 +21,7 @@ const entities = [ SraOrder, SraOrdersObservedTimeStamp, Transaction, + TrustedToken, ]; const config: ConnectionOptions = { diff --git a/packages/pipeline/src/parsers/ohlcv_external/index.ts b/packages/pipeline/src/parsers/ohlcv_external/index.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/pipeline/src/parsers/trusted_tokens/index.ts b/packages/pipeline/src/parsers/trusted_tokens/index.ts new file mode 100644 index 000000000..a6d32de35 --- /dev/null +++ b/packages/pipeline/src/parsers/trusted_tokens/index.ts @@ -0,0 +1,37 @@ +import * as R from 'ramda'; + +import { MetamaskTrustedTokenMeta, ZeroExTrustedTokenMeta } from '../../data_sources/trusted_tokens'; +import { TrustedToken } from '../../entities'; + +/** + * Parses Metamask's trusted tokens list. + * @param rawResp raw response from the metamask json file. + */ +export function parseMetamaskTrustedTokens(rawResp: Map): TrustedToken[] { + const parsedAsObject = R.mapObjIndexed(parseMetamaskTrustedToken, rawResp); + return R.values(parsedAsObject); +} + +export function parseZeroExTrustedTokens(rawResp: ZeroExTrustedTokenMeta[]): TrustedToken[] { + return R.map(parseZeroExTrustedToken, rawResp); +} + +function parseMetamaskTrustedToken(resp: MetamaskTrustedTokenMeta, address: string): TrustedToken { + + const trustedToken = new TrustedToken(); + + trustedToken.address = address; + trustedToken.authority = 'metamask'; + + return trustedToken; +} + +function parseZeroExTrustedToken(resp: ZeroExTrustedTokenMeta): TrustedToken { + + const trustedToken = new TrustedToken(); + + trustedToken.address = resp.address; + trustedToken.authority = '0x'; + + return trustedToken; +} diff --git a/packages/pipeline/src/scripts/pull_missing_events.ts b/packages/pipeline/src/scripts/pull_missing_events.ts index 0b7f6287f..68cabe3de 100644 --- a/packages/pipeline/src/scripts/pull_missing_events.ts +++ b/packages/pipeline/src/scripts/pull_missing_events.ts @@ -64,7 +64,7 @@ async function getCancelUpToEventsAsync(eventsSource: ExchangeEventsSource): Pro await saveEventsAsync(startBlock === EXCHANGE_START_BLOCK, repository, events); } -const tabelNameRegex = /^[a-zA-Z_]*$/; +const tableNameRegex = /^[a-zA-Z_]*$/; async function getStartBlockAsync(repository: Repository): Promise { const fillEventCount = await repository.count(); @@ -73,7 +73,7 @@ async function getStartBlockAsync(repository: Repositor return EXCHANGE_START_BLOCK; } const tableName = repository.metadata.tableName; - if (!tabelNameRegex.test(tableName)) { + if (!tableNameRegex.test(tableName)) { throw new Error('Unexpected special character in table name: ' + tableName); } const queryResult = await connection.query( diff --git a/packages/pipeline/src/scripts/pull_trusted_tokens.ts b/packages/pipeline/src/scripts/pull_trusted_tokens.ts new file mode 100644 index 000000000..67d9e08d1 --- /dev/null +++ b/packages/pipeline/src/scripts/pull_trusted_tokens.ts @@ -0,0 +1,51 @@ +import 'reflect-metadata'; +import { Connection, ConnectionOptions, createConnection } from 'typeorm'; + +import { MetamaskTrustedTokenMeta, TrustedTokenSource, ZeroExTrustedTokenMeta } from '../data_sources/trusted_tokens'; +import { TrustedToken } from '../entities'; +import * as ormConfig from '../ormconfig'; +import { parseMetamaskTrustedTokens, parseZeroExTrustedTokens } from '../parsers/trusted_tokens'; +import { handleError } from '../utils'; + +const METAMASK_TRUSTED_TOKENS_URL = + 'https://raw.githubusercontent.com/MetaMask/eth-contract-metadata/master/contract-map.json'; + +const ZEROEX_TRUSTED_TOKENS_URL = + 'https://website-api.0xproject.com/tokens'; + +let connection: Connection; + +(async () => { + connection = await createConnection(ormConfig as ConnectionOptions); + await getMetamaskTrustedTokens(); + await getZeroExTrustedTokens(); + process.exit(0); +})().catch(handleError); + +async function getMetamaskTrustedTokens(): Promise { + // tslint:disable-next-line + console.log('Getting latest metamask trusted tokens list ...'); + const trustedTokensRepository = connection.getRepository(TrustedToken); + const trustedTokensSource = new TrustedTokenSource>(METAMASK_TRUSTED_TOKENS_URL); + const resp = await trustedTokensSource.getTrustedTokenMetaAsync(); + const trustedTokens = parseMetamaskTrustedTokens(resp); + // tslint:disable-next-line + console.log('Saving metamask trusted tokens list'); + await trustedTokensRepository.save(trustedTokens); + // tslint:disable-next-line + console.log('Done saving metamask trusted tokens.') +} + +async function getZeroExTrustedTokens(): Promise { + // tslint:disable-next-line + console.log('Getting latest 0x trusted tokens list ...'); + const trustedTokensRepository = connection.getRepository(TrustedToken); + const trustedTokensSource = new TrustedTokenSource(ZEROEX_TRUSTED_TOKENS_URL); + const resp = await trustedTokensSource.getTrustedTokenMetaAsync(); + const trustedTokens = parseZeroExTrustedTokens(resp); + // tslint:disable-next-line + console.log('Saving metamask trusted tokens list'); + await trustedTokensRepository.save(trustedTokens); + // tslint:disable-next-line + console.log('Done saving metamask trusted tokens.'); +} diff --git a/packages/pipeline/src/utils/index.ts b/packages/pipeline/src/utils/index.ts index 78aa89374..b66ab40ab 100644 --- a/packages/pipeline/src/utils/index.ts +++ b/packages/pipeline/src/utils/index.ts @@ -49,3 +49,9 @@ class BigNumberTransformer implements ValueTransformer { } export const bigNumberTransformer = new BigNumberTransformer(); + +export function getHourInUnixTime(): number { + const currentTime: number = Date.now(); + // tslint:disable-next-line + return currentTime - (currentTime % (3600 * 1000)); +} -- cgit v1.2.3 From dea89c4e221d5b22de97b27573719cd27ce250c7 Mon Sep 17 00:00:00 2001 From: Jake Ellowitz Date: Mon, 19 Nov 2018 19:11:51 -0800 Subject: metadata and trusted sources in same raw table --- .../1542655823221-NewMetadataAndOHLCVTables.ts | 22 +++++++++- packages/pipeline/src/entities/index.ts | 3 +- packages/pipeline/src/entities/ohlcv_external.ts | 18 ++++----- packages/pipeline/src/entities/token_metadata.ts | 19 +++++++++ .../src/entities/token_on_chain_metadata.ts | 22 ---------- packages/pipeline/src/entities/trusted_tokens.ts | 7 ---- packages/pipeline/src/ormconfig.ts | 4 +- .../pipeline/src/parsers/token_metadata/index.ts | 47 ++++++++++++++++++++++ .../pipeline/src/parsers/trusted_tokens/index.ts | 37 ----------------- .../pipeline/src/scripts/pull_trusted_tokens.ts | 8 ++-- packages/pipeline/src/utils/index.ts | 3 ++ 11 files changed, 105 insertions(+), 85 deletions(-) create mode 100644 packages/pipeline/src/entities/token_metadata.ts delete mode 100644 packages/pipeline/src/entities/token_on_chain_metadata.ts delete mode 100644 packages/pipeline/src/entities/trusted_tokens.ts create mode 100644 packages/pipeline/src/parsers/token_metadata/index.ts delete mode 100644 packages/pipeline/src/parsers/trusted_tokens/index.ts diff --git a/packages/pipeline/migrations/1542655823221-NewMetadataAndOHLCVTables.ts b/packages/pipeline/migrations/1542655823221-NewMetadataAndOHLCVTables.ts index 5c7ec6de7..8d131271b 100644 --- a/packages/pipeline/migrations/1542655823221-NewMetadataAndOHLCVTables.ts +++ b/packages/pipeline/migrations/1542655823221-NewMetadataAndOHLCVTables.ts @@ -2,17 +2,23 @@ import {MigrationInterface, QueryRunner} from 'typeorm'; export class NewMetadataAndOHLCVTables1542655823221 implements MigrationInterface { + // tslint:disable-next-line public async up(queryRunner: QueryRunner): Promise { await queryRunner.query(` - CREATE TABLE raw.trusted_tokens ( + CREATE TABLE raw.token_metadata ( address VARCHAR NOT NULL, authority VARCHAR NOT NULL, + decimals INT NULL, + symbol VARCHAR NULL, + name VARCHAR NULL, PRIMARY KEY (address, authority) ); `); + await queryRunner.dropTable('raw.token_on_chain_metadata'); + await queryRunner.query(` CREATE TABLE raw.ohlcv_external ( exchange VARCHAR NOT NULL, @@ -36,9 +42,21 @@ export class NewMetadataAndOHLCVTables1542655823221 implements MigrationInterfac `); } + // tslint:disable-next-line public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropTable('raw.trusted_tokens'); + await queryRunner.query(` + CREATE TABLE raw.token_on_chain_metadata ( + address VARCHAR NOT NULL, + decimals INT NULL, + symbol VARCHAR NULL, + name VARCHAR NULL, + + PRIMARY KEY (address) + ); + `); + + await queryRunner.dropTable('raw.token_metadata'); await queryRunner.dropTable('raw.ohlcv_external'); } diff --git a/packages/pipeline/src/entities/index.ts b/packages/pipeline/src/entities/index.ts index 769efb092..a8f159a2a 100644 --- a/packages/pipeline/src/entities/index.ts +++ b/packages/pipeline/src/entities/index.ts @@ -9,8 +9,7 @@ export { ExchangeFillEvent } from './exchange_fill_event'; export { Relayer } from './relayer'; export { SraOrder } from './sra_order'; export { Transaction } from './transaction'; -export { TokenOnChainMetadata } from './token_on_chain_metadata'; -export { TrustedToken } from './trusted_tokens'; +export { TokenMetadata } from './token_metadata'; export { SraOrdersObservedTimeStamp, createObservedTimestampForOrder } from './sra_order_observed_timestamp'; export { OHLCVExternal } from './ohlcv_external'; diff --git a/packages/pipeline/src/entities/ohlcv_external.ts b/packages/pipeline/src/entities/ohlcv_external.ts index 3fbb4c499..95cd4f2f5 100644 --- a/packages/pipeline/src/entities/ohlcv_external.ts +++ b/packages/pipeline/src/entities/ohlcv_external.ts @@ -3,18 +3,18 @@ import { Column, Entity, PrimaryColumn } from 'typeorm'; @Entity({ name: 'ohlcv_external', schema: 'raw' }) export class OHLCVExternal { @PrimaryColumn() public exchange!: string; - @PrimaryColumn() public from_symbol!: string; - @PrimaryColumn() public to_symbol!: string; - @PrimaryColumn() public start_time!: number; - @PrimaryColumn() public end_time!: number; - + @PrimaryColumn() public fromSymbol!: string; + @PrimaryColumn() public toSymbol!: string; + @PrimaryColumn() public startTime!: number; + @PrimaryColumn() public endTime!: number; + @Column() public open!: number; @Column() public close!: number; @Column() public low!: number; @Column() public high!: number; - @Column() public volume_from!: number; - @Column() public volume_to!: number; + @Column() public volumeFrom!: number; + @Column() public volumeTo!: number; @PrimaryColumn() public source!: string; - @PrimaryColumn() public observed_timestamp!: number; -} \ No newline at end of file + @PrimaryColumn() public observedTimestamp!: number; +} diff --git a/packages/pipeline/src/entities/token_metadata.ts b/packages/pipeline/src/entities/token_metadata.ts new file mode 100644 index 000000000..4a0bceefe --- /dev/null +++ b/packages/pipeline/src/entities/token_metadata.ts @@ -0,0 +1,19 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +@Entity({ name: 'token_metadata', schema: 'raw' }) +export class TokenMetadata { + @PrimaryColumn({ type: 'varchar', nullable: false }) + public address!: string; + + @PrimaryColumn({ type: 'varchar', nullable: false }) + public authority!: string; + + @Column({ type: 'integer', nullable: true }) + public decimals!: number; + + @Column({ type: 'varchar', nullable: true }) + public symbol!: string; + + @Column({ type: 'varchar', nullable: true }) + public name!: string; +} diff --git a/packages/pipeline/src/entities/token_on_chain_metadata.ts b/packages/pipeline/src/entities/token_on_chain_metadata.ts deleted file mode 100644 index ca1f1827b..000000000 --- a/packages/pipeline/src/entities/token_on_chain_metadata.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Column, Entity, PrimaryColumn } from 'typeorm'; - -@Entity({ name: 'token_on_chain_metadata', schema: 'raw' }) -export class TokenOnChainMetadata { - @PrimaryColumn({ type: 'nvarchar', nullable: false }) - public address!: string; - - @Column({ type: 'integer', nullable: false }) - public decimals!: number; - - @Column({ type: 'nvarchar', nullable: false }) - public symbol!: string; - - @Column({ type: 'nvarchar', nullable: false }) - public name!: string; -} - -@Entity({ name: 'trusted_tokens', schema: 'raw' }) -export class TrustedTokens { - @PrimaryColumn() public address!: string; - @PrimaryColumn() public authority!: string; -} diff --git a/packages/pipeline/src/entities/trusted_tokens.ts b/packages/pipeline/src/entities/trusted_tokens.ts deleted file mode 100644 index 6ec27c963..000000000 --- a/packages/pipeline/src/entities/trusted_tokens.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Column, Entity, PrimaryColumn } from 'typeorm'; - -@Entity({ name: 'trusted_tokens', schema: 'raw' }) -export class TrustedToken { - @PrimaryColumn() public address!: string; - @PrimaryColumn() public authority!: string; -} diff --git a/packages/pipeline/src/ormconfig.ts b/packages/pipeline/src/ormconfig.ts index 1bd337866..e8277a439 100644 --- a/packages/pipeline/src/ormconfig.ts +++ b/packages/pipeline/src/ormconfig.ts @@ -8,8 +8,8 @@ import { Relayer, SraOrder, SraOrdersObservedTimeStamp, + TokenMetadata, Transaction, - TrustedToken, } from './entities'; const entities = [ @@ -20,8 +20,8 @@ const entities = [ Relayer, SraOrder, SraOrdersObservedTimeStamp, + TokenMetadata, Transaction, - TrustedToken, ]; const config: ConnectionOptions = { diff --git a/packages/pipeline/src/parsers/token_metadata/index.ts b/packages/pipeline/src/parsers/token_metadata/index.ts new file mode 100644 index 000000000..d09def0db --- /dev/null +++ b/packages/pipeline/src/parsers/token_metadata/index.ts @@ -0,0 +1,47 @@ +import * as R from 'ramda'; + +import { MetamaskTrustedTokenMeta, ZeroExTrustedTokenMeta } from '../../data_sources/trusted_tokens'; +import { TokenMetadata } from '../../entities'; + +/** + * Parses Metamask's trusted tokens list. + * @param rawResp raw response from the metamask json file. + */ +export function parseMetamaskTrustedTokens(rawResp: Map): TokenMetadata[] { + const parsedAsObject = R.mapObjIndexed(parseMetamaskTrustedToken, rawResp); + return R.values(parsedAsObject); +} + +/** + * Parses 0x's trusted tokens list. + * @param rawResp raw response from the 0x json file. + */ +export function parseZeroExTrustedTokens(rawResp: ZeroExTrustedTokenMeta[]): TokenMetadata[] { + return R.map(parseZeroExTrustedToken, rawResp); +} + +function parseMetamaskTrustedToken(resp: MetamaskTrustedTokenMeta, address: string): TokenMetadata { + + const trustedToken = new TokenMetadata(); + + trustedToken.address = address; + trustedToken.decimals = resp.decimals; + trustedToken.symbol = resp.symbol; + trustedToken.name = resp.name; + trustedToken.authority = 'metamask'; + + return trustedToken; +} + +function parseZeroExTrustedToken(resp: ZeroExTrustedTokenMeta): TokenMetadata { + + const trustedToken = new TokenMetadata(); + + trustedToken.address = resp.address; + trustedToken.decimals = resp.decimals; + trustedToken.symbol = resp.symbol; + trustedToken.name = resp.name; + trustedToken.authority = '0x'; + + return trustedToken; +} diff --git a/packages/pipeline/src/parsers/trusted_tokens/index.ts b/packages/pipeline/src/parsers/trusted_tokens/index.ts deleted file mode 100644 index a6d32de35..000000000 --- a/packages/pipeline/src/parsers/trusted_tokens/index.ts +++ /dev/null @@ -1,37 +0,0 @@ -import * as R from 'ramda'; - -import { MetamaskTrustedTokenMeta, ZeroExTrustedTokenMeta } from '../../data_sources/trusted_tokens'; -import { TrustedToken } from '../../entities'; - -/** - * Parses Metamask's trusted tokens list. - * @param rawResp raw response from the metamask json file. - */ -export function parseMetamaskTrustedTokens(rawResp: Map): TrustedToken[] { - const parsedAsObject = R.mapObjIndexed(parseMetamaskTrustedToken, rawResp); - return R.values(parsedAsObject); -} - -export function parseZeroExTrustedTokens(rawResp: ZeroExTrustedTokenMeta[]): TrustedToken[] { - return R.map(parseZeroExTrustedToken, rawResp); -} - -function parseMetamaskTrustedToken(resp: MetamaskTrustedTokenMeta, address: string): TrustedToken { - - const trustedToken = new TrustedToken(); - - trustedToken.address = address; - trustedToken.authority = 'metamask'; - - return trustedToken; -} - -function parseZeroExTrustedToken(resp: ZeroExTrustedTokenMeta): TrustedToken { - - const trustedToken = new TrustedToken(); - - trustedToken.address = resp.address; - trustedToken.authority = '0x'; - - return trustedToken; -} diff --git a/packages/pipeline/src/scripts/pull_trusted_tokens.ts b/packages/pipeline/src/scripts/pull_trusted_tokens.ts index 67d9e08d1..48e20bea7 100644 --- a/packages/pipeline/src/scripts/pull_trusted_tokens.ts +++ b/packages/pipeline/src/scripts/pull_trusted_tokens.ts @@ -2,9 +2,9 @@ import 'reflect-metadata'; import { Connection, ConnectionOptions, createConnection } from 'typeorm'; import { MetamaskTrustedTokenMeta, TrustedTokenSource, ZeroExTrustedTokenMeta } from '../data_sources/trusted_tokens'; -import { TrustedToken } from '../entities'; +import { TokenMetadata } from '../entities'; import * as ormConfig from '../ormconfig'; -import { parseMetamaskTrustedTokens, parseZeroExTrustedTokens } from '../parsers/trusted_tokens'; +import { parseMetamaskTrustedTokens, parseZeroExTrustedTokens } from '../parsers/token_metadata'; import { handleError } from '../utils'; const METAMASK_TRUSTED_TOKENS_URL = @@ -25,7 +25,7 @@ let connection: Connection; async function getMetamaskTrustedTokens(): Promise { // tslint:disable-next-line console.log('Getting latest metamask trusted tokens list ...'); - const trustedTokensRepository = connection.getRepository(TrustedToken); + const trustedTokensRepository = connection.getRepository(TokenMetadata); const trustedTokensSource = new TrustedTokenSource>(METAMASK_TRUSTED_TOKENS_URL); const resp = await trustedTokensSource.getTrustedTokenMetaAsync(); const trustedTokens = parseMetamaskTrustedTokens(resp); @@ -39,7 +39,7 @@ async function getMetamaskTrustedTokens(): Promise { async function getZeroExTrustedTokens(): Promise { // tslint:disable-next-line console.log('Getting latest 0x trusted tokens list ...'); - const trustedTokensRepository = connection.getRepository(TrustedToken); + const trustedTokensRepository = connection.getRepository(TokenMetadata); const trustedTokensSource = new TrustedTokenSource(ZEROEX_TRUSTED_TOKENS_URL); const resp = await trustedTokensSource.getTrustedTokenMetaAsync(); const trustedTokens = parseZeroExTrustedTokens(resp); diff --git a/packages/pipeline/src/utils/index.ts b/packages/pipeline/src/utils/index.ts index b66ab40ab..732c259d7 100644 --- a/packages/pipeline/src/utils/index.ts +++ b/packages/pipeline/src/utils/index.ts @@ -50,6 +50,9 @@ class BigNumberTransformer implements ValueTransformer { export const bigNumberTransformer = new BigNumberTransformer(); +/** + * Returns the unix timestamp of the current hour + */ export function getHourInUnixTime(): number { const currentTime: number = Date.now(); // tslint:disable-next-line -- cgit v1.2.3 From 1aa3f9d69f7a6570c6fa62c542063fa92fc0bf5a Mon Sep 17 00:00:00 2001 From: Jake Ellowitz Date: Mon, 26 Nov 2018 14:55:45 -0800 Subject: updating comment for 0x trusted tokens --- .../migrations/1542655823221-NewMetadataAndOHLCVTables.ts | 6 +----- packages/pipeline/src/parsers/token_metadata/index.ts | 4 +--- packages/pipeline/src/scripts/pull_trusted_tokens.ts | 9 +++++---- packages/pipeline/src/utils/index.ts | 2 +- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/packages/pipeline/migrations/1542655823221-NewMetadataAndOHLCVTables.ts b/packages/pipeline/migrations/1542655823221-NewMetadataAndOHLCVTables.ts index 8d131271b..838f5ba9c 100644 --- a/packages/pipeline/migrations/1542655823221-NewMetadataAndOHLCVTables.ts +++ b/packages/pipeline/migrations/1542655823221-NewMetadataAndOHLCVTables.ts @@ -1,10 +1,8 @@ -import {MigrationInterface, QueryRunner} from 'typeorm'; +import { MigrationInterface, QueryRunner } from 'typeorm'; export class NewMetadataAndOHLCVTables1542655823221 implements MigrationInterface { - // tslint:disable-next-line public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` CREATE TABLE raw.token_metadata ( address VARCHAR NOT NULL, @@ -44,7 +42,6 @@ export class NewMetadataAndOHLCVTables1542655823221 implements MigrationInterfac // tslint:disable-next-line public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` CREATE TABLE raw.token_on_chain_metadata ( address VARCHAR NOT NULL, @@ -60,5 +57,4 @@ export class NewMetadataAndOHLCVTables1542655823221 implements MigrationInterfac await queryRunner.dropTable('raw.ohlcv_external'); } - } diff --git a/packages/pipeline/src/parsers/token_metadata/index.ts b/packages/pipeline/src/parsers/token_metadata/index.ts index d09def0db..916448cb3 100644 --- a/packages/pipeline/src/parsers/token_metadata/index.ts +++ b/packages/pipeline/src/parsers/token_metadata/index.ts @@ -14,14 +14,13 @@ export function parseMetamaskTrustedTokens(rawResp: Map { // tslint:disable-next-line console.log('Getting latest metamask trusted tokens list ...'); const trustedTokensRepository = connection.getRepository(TokenMetadata); - const trustedTokensSource = new TrustedTokenSource>(METAMASK_TRUSTED_TOKENS_URL); + const trustedTokensSource = new TrustedTokenSource>( + METAMASK_TRUSTED_TOKENS_URL, + ); const resp = await trustedTokensSource.getTrustedTokenMetaAsync(); const trustedTokens = parseMetamaskTrustedTokens(resp); // tslint:disable-next-line console.log('Saving metamask trusted tokens list'); await trustedTokensRepository.save(trustedTokens); // tslint:disable-next-line - console.log('Done saving metamask trusted tokens.') + console.log('Done saving metamask trusted tokens.'); } async function getZeroExTrustedTokens(): Promise { diff --git a/packages/pipeline/src/utils/index.ts b/packages/pipeline/src/utils/index.ts index 732c259d7..b6d84a565 100644 --- a/packages/pipeline/src/utils/index.ts +++ b/packages/pipeline/src/utils/index.ts @@ -56,5 +56,5 @@ export const bigNumberTransformer = new BigNumberTransformer(); export function getHourInUnixTime(): number { const currentTime: number = Date.now(); // tslint:disable-next-line - return currentTime - (currentTime % (3600 * 1000)); + return currentTime - currentTime % (3600 * 1000); } -- cgit v1.2.3 From 96fdb9b76603ac5e4be654e52c52128f0490b3d8 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Tue, 27 Nov 2018 12:38:44 -0800 Subject: Reduce number of blocks per query in pull_missing_events --- packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts b/packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts index d09f59535..5721c0ea3 100644 --- a/packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts +++ b/packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts @@ -12,7 +12,7 @@ import { Web3Wrapper } from '@0x/web3-wrapper'; import { LogWithDecodedArgs } from 'ethereum-types'; const BLOCK_FINALITY_THRESHOLD = 10; // When to consider blocks as final. Used to compute default toBlock. -const NUM_BLOCKS_PER_QUERY = 100000; // Number of blocks to query for events at a time. +const NUM_BLOCKS_PER_QUERY = 20000; // Number of blocks to query for events at a time. const EXCHANGE_START_BLOCK = 6271590; // Block number when the Exchange contract was deployed to mainnet. export class ExchangeEventsSource { -- cgit v1.2.3 From 4061731245a8513e8d990f3af87e182fb674838b Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Tue, 27 Nov 2018 16:43:00 -0800 Subject: [pipeline] Add additional documentation to the README (#1328) --- packages/pipeline/README.md | 113 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/packages/pipeline/README.md b/packages/pipeline/README.md index df54de21c..c647950a2 100644 --- a/packages/pipeline/README.md +++ b/packages/pipeline/README.md @@ -31,3 +31,116 @@ yarn clean ```bash yarn lint ``` + +### Migrations + +Create a new migration: `yarn migrate:create --name MigrationNameInCamelCase` +Run migrations: `yarn migrate:run` +Revert the most recent migration (CAUTION: may result in data loss!): `yarn migrate:revert` + +## Connecting to PostgreSQL + +Across the pipeline package, any code which accesses the database uses the +environment variable `ZEROEX_DATA_PIPELINE_DB_URL` which should be a properly +formatted +[PostgreSQL connection url](https://stackoverflow.com/questions/3582552/postgresql-connection-url). + +## Test environment + +The easiest way to start Postgres is via Docker. Depending on your +platform, you may need to prepend `sudo` to the following command: + +``` +docker run --rm -d -p 5432:5432 --name pipeline_postgres postgres:11-alpine +``` + +This will start a Postgres server with the default username and database name. +You should set the environment variable as follows: + +``` +export ZEROEX_DATA_PIPELINE_DB_URL=postgresql://postgres@localhost/postgres +``` + +First thing you will need to do is run the migrations: + +``` +yarn migrate:run +``` + +Now you can run scripts locally: + +``` +node packages/pipeline/lib/src/scripts/pull_radar_relay_orders.js +``` + +To stop the Postgres server (you may need to add `sudo`): + +``` +docker stop pipeline_postgres +``` + +This will remove all data from the database. + +If you prefer, you can also install Postgres with e.g., +[Homebrew](https://wiki.postgresql.org/wiki/Homebrew) or +[Postgress.app](https://postgresapp.com/). As long as you set the +`ZEROEX_DATA_PIPELINE_DB_URL` environment variable appropriately, any Postgres +server will work. + +## Directory structure + +``` +. +├── lib: Code generated by the TypeScript compiler. Don't edit this directly. +├── migrations: Code for creating and updating database schemas. +├── node_modules: +├── src: All TypeScript source code. +│   ├── data_sources: Code responsible for getting raw data, typically from a third-party source. +│   ├── entities: TypeORM entities which closely mirror our database schemas. Some other ORMs call these "models". +│   ├── parsers: Code for converting raw data into entities. +│   ├── scripts: Executable scripts which put all the pieces together. +│   └── utils: Various utils used across packages/files. +├── test: All tests go here and are organized in the same way as the folder/file that they test. +``` + +## Adding new data to the pipeline + +1. Create an entity in the _entities_ directory. Entities directly mirror our + database schemas. We follow the practice of having "dumb" entities, so + entity classes should typically not have any methods. +2. Create a migration using the `yarn migrate:create` command. Create/update + tables as needed. Remember to fill in both the `up` and `down` methods. Try + to avoid data loss as much as possible in your migrations. +3. Create a class or function in the _data_sources_ directory for getting raw + data. This code should abstract away pagination and rate-limiting as much as + possible. +4. Create a class or function in the _parsers_ directory for converting the raw + data into an entity. Also add tests in the _tests_ directory to test the + parser. +5. Create an executable script in the _scripts_ directory for putting + everything together. Your script can accept environment variables for things + like API keys. It should pull the data, parse it, and save it to the + database. Scripts should be idempotent and atomic (when possible). What this + means is that your script may be responsible for determining **which** data + needs to be updated. For example, you may need to query the database to find + the most recent block number that we have already pulled, then pull new data + starting from that block number. +6. Run the migrations and then run your new script locally and verify it works + as expected. + +#### Additional guidelines and tips: + +* Table names should be plural and separated by underscores (e.g., + `exchange_fill_events`). +* Any table which contains data which comes directly from a third-party source + should be namespaced in the `raw` PostgreSQL schema. +* Column names in the database should be separated by underscores (e.g., + `maker_asset_type`). +* Field names in entity classes (like any other fields in TypeScript) should + be camel-cased (e.g., `makerAssetType`). +* All timestamps should be stored as milliseconds since the Unix Epoch. +* Use the `BigNumber` type for TypeScript code which deals with 256-bit + numbers from smart contracts or for any case where we are dealing with large + floating point numbers. +* [TypeORM documentation](http://typeorm.io/#/) is pretty robust and can be a + helpful resource. -- cgit v1.2.3 From 3d211c415b58a67f84332ff512bf9372cac5a3ac Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Wed, 28 Nov 2018 13:21:04 -0800 Subject: Introduce framework for running basic tests for entities (#1344) * Introduce framework for running basic tests for entities * Add pipeline tests to CircleCI config * Make pipeline tests more configurable and fix CircleCI config * Add coverage dir to pipeline package * Add basic tests for all exchange event entities * Add tests for remaining entities * Create separate test scripts in package.json and add new info to README * Update db_setup.ts to revert migrations even if you are using docker * Automatically pull the postgres image if needed * Add comment about why NumberToBigIntTransformer is needed --- .circleci/config.yml | 17 ++ packages/pipeline/README.md | 59 ++++--- packages/pipeline/coverage/.gitkeep | 0 packages/pipeline/package.json | 11 +- packages/pipeline/src/entities/block.ts | 7 +- .../pipeline/src/entities/exchange_cancel_event.ts | 3 +- .../src/entities/exchange_cancel_up_to_event.ts | 4 +- .../pipeline/src/entities/exchange_fill_event.ts | 4 +- .../src/entities/sra_order_observed_timestamp.ts | 4 +- packages/pipeline/src/entities/token_metadata.ts | 8 +- packages/pipeline/src/entities/transaction.ts | 8 +- packages/pipeline/src/utils/index.ts | 16 +- .../pipeline/src/utils/transformers/big_number.ts | 16 ++ packages/pipeline/src/utils/transformers/index.ts | 2 + .../src/utils/transformers/number_to_bigint.ts | 27 ++++ packages/pipeline/test/db_global_hooks.ts | 9 ++ packages/pipeline/test/db_setup.ts | 174 +++++++++++++++++++++ packages/pipeline/test/entities/block_test.ts | 23 +++ .../test/entities/exchange_cancel_event_test.ts | 57 +++++++ .../entities/exchange_cancel_up_to_event_test.ts | 29 ++++ .../test/entities/exchange_fill_event_test.ts | 62 ++++++++ packages/pipeline/test/entities/relayer_test.ts | 55 +++++++ packages/pipeline/test/entities/sra_order_test.ts | 84 ++++++++++ .../pipeline/test/entities/token_metadata_test.ts | 38 +++++ .../pipeline/test/entities/transaction_test.ts | 25 +++ packages/pipeline/test/entities/util.ts | 25 +++ yarn.lock | 53 ++++++- 27 files changed, 766 insertions(+), 54 deletions(-) create mode 100644 packages/pipeline/coverage/.gitkeep create mode 100644 packages/pipeline/src/utils/transformers/big_number.ts create mode 100644 packages/pipeline/src/utils/transformers/index.ts create mode 100644 packages/pipeline/src/utils/transformers/number_to_bigint.ts create mode 100644 packages/pipeline/test/db_global_hooks.ts create mode 100644 packages/pipeline/test/db_setup.ts create mode 100644 packages/pipeline/test/entities/block_test.ts create mode 100644 packages/pipeline/test/entities/exchange_cancel_event_test.ts create mode 100644 packages/pipeline/test/entities/exchange_cancel_up_to_event_test.ts create mode 100644 packages/pipeline/test/entities/exchange_fill_event_test.ts create mode 100644 packages/pipeline/test/entities/relayer_test.ts create mode 100644 packages/pipeline/test/entities/sra_order_test.ts create mode 100644 packages/pipeline/test/entities/token_metadata_test.ts create mode 100644 packages/pipeline/test/entities/transaction_test.ts create mode 100644 packages/pipeline/test/entities/util.ts diff --git a/.circleci/config.yml b/.circleci/config.yml index fe81054ac..eab912934 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -77,6 +77,20 @@ jobs: keys: - repo-{{ .Environment.CIRCLE_SHA1 }} - run: yarn test:generate_docs:circleci + test-pipeline: + docker: + - image: circleci/node:9 + - image: postgres:11-alpine + working_directory: ~/repo + steps: + - restore_cache: + keys: + - repo-{{ .Environment.CIRCLE_SHA1 }} + - run: ZEROEX_DATA_PIPELINE_TEST_DB_URL='postgresql://postgres@localhost/postgres' yarn wsrun test:circleci @0x/pipeline + - save_cache: + key: coverage-pipeline-{{ .Environment.CIRCLE_SHA1 }} + paths: + - ~/repo/packages/pipeline/coverage/lcov.info test-rest: docker: - image: circleci/node:9 @@ -336,6 +350,9 @@ workflows: - test-contracts-geth: requires: - build + - test-pipeline: + requires: + - build - test-rest: requires: - build diff --git a/packages/pipeline/README.md b/packages/pipeline/README.md index c647950a2..fb563b14c 100644 --- a/packages/pipeline/README.md +++ b/packages/pipeline/README.md @@ -38,17 +38,34 @@ Create a new migration: `yarn migrate:create --name MigrationNameInCamelCase` Run migrations: `yarn migrate:run` Revert the most recent migration (CAUTION: may result in data loss!): `yarn migrate:revert` -## Connecting to PostgreSQL +## Testing -Across the pipeline package, any code which accesses the database uses the -environment variable `ZEROEX_DATA_PIPELINE_DB_URL` which should be a properly -formatted -[PostgreSQL connection url](https://stackoverflow.com/questions/3582552/postgresql-connection-url). +There are several test scripts in **package.json**. You can run all the tests +with `yarn test:all` or run certain tests seprately by following the +instructions below. Some tests may not work out of the box on certain platforms. -## Test environment +### Unit tests -The easiest way to start Postgres is via Docker. Depending on your -platform, you may need to prepend `sudo` to the following command: +The unit tests can be run with `yarn test`. These tests don't depend on any +services or databases and will run in any environment that can run Node. + +### Database tests + +Database integration tests can be run with `yarn test:db`. These tests will +attempt to automatically spin up a Postgres database via Docker. If this doesn't +work you have two other options: + +1. Set the `DOCKER_SOCKET` environment variable to a valid socket path to use + for communicating with Docker. +2. Start Postgres manually and set the `ZEROEX_DATA_PIPELINE_TEST_DB_URL` + environment variable. If this is set, the tests will use your existing + Postgres database instead of trying to create one with Docker. + +## Running locally + +`pipeline` requires access to a PostgreSQL database. The easiest way to start +Postgres is via Docker. Depending on your platform, you may need to prepend +`sudo` to the following command: ``` docker run --rm -d -p 5432:5432 --name pipeline_postgres postgres:11-alpine @@ -83,9 +100,9 @@ This will remove all data from the database. If you prefer, you can also install Postgres with e.g., [Homebrew](https://wiki.postgresql.org/wiki/Homebrew) or -[Postgress.app](https://postgresapp.com/). As long as you set the -`ZEROEX_DATA_PIPELINE_DB_URL` environment variable appropriately, any Postgres -server will work. +[Postgress.app](https://postgresapp.com/). Keep in mind that you will need to +set the`ZEROEX_DATA_PIPELINE_DB_URL` environment variable to a valid +[PostgreSQL connection url](https://stackoverflow.com/questions/3582552/postgresql-connection-url) ## Directory structure @@ -111,21 +128,23 @@ server will work. 2. Create a migration using the `yarn migrate:create` command. Create/update tables as needed. Remember to fill in both the `up` and `down` methods. Try to avoid data loss as much as possible in your migrations. -3. Create a class or function in the _data_sources_ directory for getting raw - data. This code should abstract away pagination and rate-limiting as much as - possible. -4. Create a class or function in the _parsers_ directory for converting the raw - data into an entity. Also add tests in the _tests_ directory to test the - parser. -5. Create an executable script in the _scripts_ directory for putting +3. Add basic tests for your entity and migrations to the **test/entities/** + directory. +4. Create a class or function in the **data_sources/** directory for getting + raw data. This code should abstract away pagination and rate-limiting as + much as possible. +5. Create a class or function in the **parsers/** directory for converting the + raw data into an entity. Also add tests in the **tests/** directory to test + the parser. +6. Create an executable script in the **scripts/** directory for putting everything together. Your script can accept environment variables for things like API keys. It should pull the data, parse it, and save it to the database. Scripts should be idempotent and atomic (when possible). What this - means is that your script may be responsible for determining **which** data + means is that your script may be responsible for determining _which_ data needs to be updated. For example, you may need to query the database to find the most recent block number that we have already pulled, then pull new data starting from that block number. -6. Run the migrations and then run your new script locally and verify it works +7. Run the migrations and then run your new script locally and verify it works as expected. #### Additional guidelines and tips: diff --git a/packages/pipeline/coverage/.gitkeep b/packages/pipeline/coverage/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/packages/pipeline/package.json b/packages/pipeline/package.json index a57fbf5bc..9831517fb 100644 --- a/packages/pipeline/package.json +++ b/packages/pipeline/package.json @@ -7,10 +7,13 @@ "build": "yarn tsc -b", "build:ci": "yarn build", "test": "yarn run_mocha", - "rebuild_and_test": "run-s build test", + "rebuild_and_test": "run-s build test:all", + "test:db": "yarn run_mocha:db", + "test:all": "run-s test test:db", "test:circleci": "yarn test:coverage", - "run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js --bail --exit", - "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", + "run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/!(entities)/**/*_test.js' --bail --exit", + "run_mocha:db": "mocha --require source-map-support/register --require make-promises-safe lib/test/db_global_hooks.js 'lib/test/entities/*_test.js' --bail --exit --timeout 60000", + "test:coverage": "nyc npm run test:all --all && yarn coverage:report:lcov", "coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info", "clean": "shx rm -rf lib", "lint": "tslint --project . --format stylish --exclude ./migrations/**/*", @@ -50,8 +53,10 @@ "@0x/types": "^1.2.0", "@0x/utils": "^2.0.3", "@0x/web3-wrapper": "^3.1.0", + "@types/dockerode": "^2.5.9", "async-parallel": "^1.2.3", "axios": "^0.18.0", + "dockerode": "^2.5.7", "ethereum-types": "^1.0.6", "pg": "^7.5.0", "ramda": "^0.25.0", diff --git a/packages/pipeline/src/entities/block.ts b/packages/pipeline/src/entities/block.ts index f2efc6390..398946622 100644 --- a/packages/pipeline/src/entities/block.ts +++ b/packages/pipeline/src/entities/block.ts @@ -1,10 +1,13 @@ import { Column, Entity, PrimaryColumn } from 'typeorm'; +import { numberToBigIntTransformer } from '../utils'; + @Entity({ name: 'blocks', schema: 'raw' }) export class Block { @PrimaryColumn() public hash!: string; - @PrimaryColumn() public number!: number; + @PrimaryColumn({ transformer: numberToBigIntTransformer }) + public number!: number; - @Column({ name: 'timestamp' }) + @Column({ name: 'timestamp', transformer: numberToBigIntTransformer }) public timestamp!: number; } diff --git a/packages/pipeline/src/entities/exchange_cancel_event.ts b/packages/pipeline/src/entities/exchange_cancel_event.ts index 5a40ba799..2fcc17df6 100644 --- a/packages/pipeline/src/entities/exchange_cancel_event.ts +++ b/packages/pipeline/src/entities/exchange_cancel_event.ts @@ -1,6 +1,7 @@ import { Column, Entity, PrimaryColumn } from 'typeorm'; import { AssetType } from '../types'; +import { numberToBigIntTransformer } from '../utils'; @Entity({ name: 'exchange_cancel_events', schema: 'raw' }) export class ExchangeCancelEvent { @@ -8,7 +9,7 @@ export class ExchangeCancelEvent { public contractAddress!: string; @PrimaryColumn({ name: 'log_index' }) public logIndex!: number; - @PrimaryColumn({ name: 'block_number' }) + @PrimaryColumn({ name: 'block_number', transformer: numberToBigIntTransformer }) public blockNumber!: number; @Column({ name: 'raw_data' }) diff --git a/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts b/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts index 9b1c6174a..60ead324f 100644 --- a/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts +++ b/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts @@ -1,7 +1,7 @@ import { BigNumber } from '@0x/utils'; import { Column, Entity, PrimaryColumn } from 'typeorm'; -import { bigNumberTransformer } from '../utils'; +import { bigNumberTransformer, numberToBigIntTransformer } from '../utils'; @Entity({ name: 'exchange_cancel_up_to_events', schema: 'raw' }) export class ExchangeCancelUpToEvent { @@ -9,7 +9,7 @@ export class ExchangeCancelUpToEvent { public contractAddress!: string; @PrimaryColumn({ name: 'log_index' }) public logIndex!: number; - @PrimaryColumn({ name: 'block_number' }) + @PrimaryColumn({ name: 'block_number', transformer: numberToBigIntTransformer }) public blockNumber!: number; // TODO(albrow): Include transaction hash diff --git a/packages/pipeline/src/entities/exchange_fill_event.ts b/packages/pipeline/src/entities/exchange_fill_event.ts index aa082436b..bbf0abf58 100644 --- a/packages/pipeline/src/entities/exchange_fill_event.ts +++ b/packages/pipeline/src/entities/exchange_fill_event.ts @@ -2,7 +2,7 @@ import { BigNumber } from '@0x/utils'; import { Column, Entity, PrimaryColumn } from 'typeorm'; import { AssetType } from '../types'; -import { bigNumberTransformer } from '../utils'; +import { bigNumberTransformer, numberToBigIntTransformer } from '../utils'; @Entity({ name: 'exchange_fill_events', schema: 'raw' }) export class ExchangeFillEvent { @@ -10,7 +10,7 @@ export class ExchangeFillEvent { public contractAddress!: string; @PrimaryColumn({ name: 'log_index' }) public logIndex!: number; - @PrimaryColumn({ name: 'block_number' }) + @PrimaryColumn({ name: 'block_number', transformer: numberToBigIntTransformer }) public blockNumber!: number; @Column({ name: 'raw_data' }) diff --git a/packages/pipeline/src/entities/sra_order_observed_timestamp.ts b/packages/pipeline/src/entities/sra_order_observed_timestamp.ts index bdb6cd36b..cd2d41397 100644 --- a/packages/pipeline/src/entities/sra_order_observed_timestamp.ts +++ b/packages/pipeline/src/entities/sra_order_observed_timestamp.ts @@ -1,5 +1,7 @@ import { Entity, PrimaryColumn } from 'typeorm'; +import { numberToBigIntTransformer } from '../utils'; + import { SraOrder } from './sra_order'; @Entity({ name: 'sra_orders_observed_timestamps', schema: 'raw' }) @@ -11,7 +13,7 @@ export class SraOrdersObservedTimeStamp { @PrimaryColumn({ name: 'source_url' }) public sourceUrl!: string; - @PrimaryColumn({ name: 'observed_timestamp' }) + @PrimaryColumn({ name: 'observed_timestamp', transformer: numberToBigIntTransformer }) public observedTimestamp!: number; } diff --git a/packages/pipeline/src/entities/token_metadata.ts b/packages/pipeline/src/entities/token_metadata.ts index 4a0bceefe..ca1e57937 100644 --- a/packages/pipeline/src/entities/token_metadata.ts +++ b/packages/pipeline/src/entities/token_metadata.ts @@ -8,12 +8,14 @@ export class TokenMetadata { @PrimaryColumn({ type: 'varchar', nullable: false }) public authority!: string; + // TODO(albrow): Convert decimals field to type BigNumber/numeric because it + // comes from a 256-bit integer in a smart contract. @Column({ type: 'integer', nullable: true }) - public decimals!: number; + public decimals!: number | null; @Column({ type: 'varchar', nullable: true }) - public symbol!: string; + public symbol!: string | null; @Column({ type: 'varchar', nullable: true }) - public name!: string; + public name!: string | null; } diff --git a/packages/pipeline/src/entities/transaction.ts b/packages/pipeline/src/entities/transaction.ts index dd2143df5..91e4ecb5d 100644 --- a/packages/pipeline/src/entities/transaction.ts +++ b/packages/pipeline/src/entities/transaction.ts @@ -1,16 +1,18 @@ import { Column, Entity, PrimaryColumn } from 'typeorm'; +import { numberToBigIntTransformer } from '../utils'; + @Entity({ name: 'transactions', schema: 'raw' }) export class Transaction { @PrimaryColumn({ name: 'transaction_hash' }) public transactionHash!: string; @PrimaryColumn({ name: 'block_hash' }) public blockHash!: string; - @PrimaryColumn({ name: 'block_number' }) + @PrimaryColumn({ name: 'block_number', transformer: numberToBigIntTransformer }) public blockNumber!: number; - @Column({ type: 'bigint', name: 'gas_used' }) + @Column({ type: 'bigint', name: 'gas_used', transformer: numberToBigIntTransformer }) public gasUsed!: number; - @Column({ type: 'bigint', name: 'gas_price' }) + @Column({ type: 'bigint', name: 'gas_price', transformer: numberToBigIntTransformer }) public gasPrice!: number; } diff --git a/packages/pipeline/src/utils/index.ts b/packages/pipeline/src/utils/index.ts index b6d84a565..918cfc695 100644 --- a/packages/pipeline/src/utils/index.ts +++ b/packages/pipeline/src/utils/index.ts @@ -1,5 +1,5 @@ import { BigNumber } from '@0x/utils'; -import { ValueTransformer } from 'typeorm/decorator/options/ValueTransformer'; +export * from './transformers'; /** * If the given BigNumber is not null, returns the string representation of that @@ -36,20 +36,6 @@ export function handleError(e: any): void { process.exit(1); } -class BigNumberTransformer implements ValueTransformer { - // tslint:disable-next-line:prefer-function-over-method - public to(value: BigNumber): string { - return value.toString(); - } - - // tslint:disable-next-line:prefer-function-over-method - public from(value: string): BigNumber { - return new BigNumber(value); - } -} - -export const bigNumberTransformer = new BigNumberTransformer(); - /** * Returns the unix timestamp of the current hour */ diff --git a/packages/pipeline/src/utils/transformers/big_number.ts b/packages/pipeline/src/utils/transformers/big_number.ts new file mode 100644 index 000000000..a0471a5e3 --- /dev/null +++ b/packages/pipeline/src/utils/transformers/big_number.ts @@ -0,0 +1,16 @@ +import { BigNumber } from '@0x/utils'; +import { ValueTransformer } from 'typeorm/decorator/options/ValueTransformer'; + +export class BigNumberTransformer implements ValueTransformer { + // tslint:disable-next-line:prefer-function-over-method + public to(value: BigNumber): string { + return value.toString(); + } + + // tslint:disable-next-line:prefer-function-over-method + public from(value: string): BigNumber { + return new BigNumber(value); + } +} + +export const bigNumberTransformer = new BigNumberTransformer(); diff --git a/packages/pipeline/src/utils/transformers/index.ts b/packages/pipeline/src/utils/transformers/index.ts new file mode 100644 index 000000000..232c1c5de --- /dev/null +++ b/packages/pipeline/src/utils/transformers/index.ts @@ -0,0 +1,2 @@ +export * from './big_number'; +export * from './number_to_bigint'; diff --git a/packages/pipeline/src/utils/transformers/number_to_bigint.ts b/packages/pipeline/src/utils/transformers/number_to_bigint.ts new file mode 100644 index 000000000..85560c1f0 --- /dev/null +++ b/packages/pipeline/src/utils/transformers/number_to_bigint.ts @@ -0,0 +1,27 @@ +import { BigNumber } from '@0x/utils'; +import { ValueTransformer } from 'typeorm/decorator/options/ValueTransformer'; + +const decimalRadix = 10; + +// Can be used to convert a JavaScript number type to a Postgres bigint type and +// vice versa. By default TypeORM will silently convert number types to string +// if the corresponding Postgres type is bigint. See +// https://github.com/typeorm/typeorm/issues/2400 for more information. +export class NumberToBigIntTransformer implements ValueTransformer { + // tslint:disable-next-line:prefer-function-over-method + public to(value: number): string { + return value.toString(); + } + + // tslint:disable-next-line:prefer-function-over-method + public from(value: string): number { + if (new BigNumber(value).greaterThan(Number.MAX_SAFE_INTEGER)) { + throw new Error( + `Attempted to convert PostgreSQL bigint value (${value}) to JavaScript number type but it is too big to safely convert`, + ); + } + return Number.parseInt(value, decimalRadix); + } +} + +export const numberToBigIntTransformer = new NumberToBigIntTransformer(); diff --git a/packages/pipeline/test/db_global_hooks.ts b/packages/pipeline/test/db_global_hooks.ts new file mode 100644 index 000000000..dfee02c45 --- /dev/null +++ b/packages/pipeline/test/db_global_hooks.ts @@ -0,0 +1,9 @@ +import { setUpDbAsync, tearDownDbAsync } from './db_setup'; + +before('set up database', async () => { + await setUpDbAsync(); +}); + +after('tear down database', async () => { + await tearDownDbAsync(); +}); diff --git a/packages/pipeline/test/db_setup.ts b/packages/pipeline/test/db_setup.ts new file mode 100644 index 000000000..bf31d15b6 --- /dev/null +++ b/packages/pipeline/test/db_setup.ts @@ -0,0 +1,174 @@ +import * as Docker from 'dockerode'; +import * as fs from 'fs'; +import * as R from 'ramda'; +import { Connection, ConnectionOptions, createConnection } from 'typeorm'; + +import * as ormConfig from '../src/ormconfig'; + +// The name of the image to pull and use for the container. This also affects +// which version of Postgres we use. +const DOCKER_IMAGE_NAME = 'postgres:11-alpine'; +// The name to use for the Docker container which will run Postgres. +const DOCKER_CONTAINER_NAME = '0x_pipeline_postgres_test'; +// The port which will be exposed on the Docker container. +const POSTGRES_HOST_PORT = '15432'; +// Number of milliseconds to wait for postgres to finish initializing after +// starting the docker container. +const POSTGRES_SETUP_DELAY_MS = 5000; + +/** + * Sets up the database for testing purposes. If the + * ZEROEX_DATA_PIPELINE_TEST_DB_URL env var is specified, it will create a + * connection using that url. Otherwise it will spin up a new Docker container + * with a Postgres database and then create a connection to that database. + */ +export async function setUpDbAsync(): Promise { + const connection = await createDbConnectionOnceAsync(); + await connection.runMigrations({ transaction: true }); +} + +/** + * Tears down the database used for testing. This completely destroys any data. + * If a docker container was created, it destroys that container too. + */ +export async function tearDownDbAsync(): Promise { + const connection = await createDbConnectionOnceAsync(); + for (const _ of connection.migrations) { + await connection.undoLastMigration({ transaction: true }); + } + if (needsDocker()) { + const docker = initDockerOnce(); + const postgresContainer = docker.getContainer(DOCKER_CONTAINER_NAME); + await postgresContainer.kill(); + await postgresContainer.remove(); + } +} + +let savedConnection: Connection; + +/** + * The first time this is run, it creates and returns a new TypeORM connection. + * Each subsequent time, it returns the existing connection. This is helpful + * because only one TypeORM connection can be active at a time. + */ +export async function createDbConnectionOnceAsync(): Promise { + if (savedConnection !== undefined) { + return savedConnection; + } + + if (needsDocker()) { + await initContainerAsync(); + } + const testDbUrl = + process.env.ZEROEX_DATA_PIPELINE_TEST_DB_URL || + `postgresql://postgres@localhost:${POSTGRES_HOST_PORT}/postgres`; + const testOrmConfig = R.merge(ormConfig, { url: testDbUrl }) as ConnectionOptions; + + savedConnection = await createConnection(testOrmConfig); + return savedConnection; +} + +async function sleepAsync(ms: number): Promise<{}> { + return new Promise<{}>(resolve => setTimeout(resolve, ms)); +} + +let savedDocker: Docker; + +function initDockerOnce(): Docker { + if (savedDocker !== undefined) { + return savedDocker; + } + + // Note(albrow): Code for determining the right socket path is partially + // based on https://github.com/apocas/dockerode/blob/8f3aa85311fab64d58eca08fef49aa1da5b5f60b/test/spec_helper.js + const isWin = require('os').type() === 'Windows_NT'; + const socketPath = process.env.DOCKER_SOCKET || (isWin ? '//./pipe/docker_engine' : '/var/run/docker.sock'); + const isSocket = fs.existsSync(socketPath) ? fs.statSync(socketPath).isSocket() : false; + if (!isSocket) { + throw new Error(`Failed to connect to Docker using socket path: "${socketPath}". + +The database integration tests need to be able to connect to a Postgres database. Make sure that Docker is running and accessible at the expected socket path. If Docker isn't working you have two options: + + 1) Set the DOCKER_SOCKET environment variable to a socket path that can be used to connect to Docker or + 2) Set the ZEROEX_DATA_PIPELINE_TEST_DB_URL environment variable to connect directly to an existing Postgres database instead of trying to start Postgres via Docker +`); + } + savedDocker = new Docker({ + socketPath, + }); + return savedDocker; +} + +// Creates the container, waits for it to initialize, and returns it. +async function initContainerAsync(): Promise { + const docker = initDockerOnce(); + + // Tear down any existing containers with the same name. + await tearDownExistingContainerIfAnyAsync(); + + // Pull the image we need. + await pullImageAsync(docker, DOCKER_IMAGE_NAME); + + // Create the container. + const postgresContainer = await docker.createContainer({ + name: DOCKER_CONTAINER_NAME, + Image: DOCKER_IMAGE_NAME, + ExposedPorts: { + '5432': {}, + }, + HostConfig: { + PortBindings: { + '5432': [ + { + HostPort: POSTGRES_HOST_PORT, + }, + ], + }, + }, + }); + await postgresContainer.start(); + await sleepAsync(POSTGRES_SETUP_DELAY_MS); + return postgresContainer; +} + +async function tearDownExistingContainerIfAnyAsync(): Promise { + const docker = initDockerOnce(); + + // Check if a container with the desired name already exists. If so, this + // probably means we didn't clean up properly on the last test run. + const existingContainer = docker.getContainer(DOCKER_CONTAINER_NAME); + if (existingContainer != null) { + try { + await existingContainer.kill(); + } catch { + // If this fails, it's fine. The container was probably already + // killed. + } + try { + await existingContainer.remove(); + } catch { + // If this fails, it's fine. The container was probably already + // removed. + } + } +} + +function needsDocker(): boolean { + return process.env.ZEROEX_DATA_PIPELINE_TEST_DB_URL === undefined; +} + +// Note(albrow): This is partially based on +// https://stackoverflow.com/questions/38258263/how-do-i-wait-for-a-pull +async function pullImageAsync(docker: Docker, imageName: string): Promise { + return new Promise((resolve, reject) => { + docker.pull(imageName, {}, (err, stream) => { + if (err != null) { + reject(err); + return; + } + docker.modem.followProgress(stream, () => { + resolve(); + }); + }); + }); +} diff --git a/packages/pipeline/test/entities/block_test.ts b/packages/pipeline/test/entities/block_test.ts new file mode 100644 index 000000000..503f284f0 --- /dev/null +++ b/packages/pipeline/test/entities/block_test.ts @@ -0,0 +1,23 @@ +import 'mocha'; +import 'reflect-metadata'; + +import { Block } from '../../src/entities'; +import { createDbConnectionOnceAsync } from '../db_setup'; +import { chaiSetup } from '../utils/chai_setup'; + +import { testSaveAndFindEntityAsync } from './util'; + +chaiSetup.configure(); + +// tslint:disable:custom-no-magic-numbers +describe('Block entity', () => { + it('save/find', async () => { + const connection = await createDbConnectionOnceAsync(); + const block = new Block(); + block.hash = '0x12345'; + block.number = 1234567; + block.timestamp = 5432154321; + const blocksRepository = connection.getRepository(Block); + await testSaveAndFindEntityAsync(blocksRepository, block); + }); +}); diff --git a/packages/pipeline/test/entities/exchange_cancel_event_test.ts b/packages/pipeline/test/entities/exchange_cancel_event_test.ts new file mode 100644 index 000000000..f3b306d69 --- /dev/null +++ b/packages/pipeline/test/entities/exchange_cancel_event_test.ts @@ -0,0 +1,57 @@ +import 'mocha'; +import * as R from 'ramda'; +import 'reflect-metadata'; + +import { ExchangeCancelEvent } from '../../src/entities'; +import { AssetType } from '../../src/types'; +import { createDbConnectionOnceAsync } from '../db_setup'; +import { chaiSetup } from '../utils/chai_setup'; + +import { testSaveAndFindEntityAsync } from './util'; + +chaiSetup.configure(); + +const baseCancelEvent = { + contractAddress: '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', + logIndex: 1234, + blockNumber: 6276262, + rawData: '0x000000000000000000000000f6da68519f78b0d0bc93c701e86affcb75c92428', + transactionHash: '0x6dd106d002873746072fc5e496dd0fb2541b68c77bcf9184ae19a42fd33657fe', + makerAddress: '0xf6da68519f78b0d0bc93c701e86affcb75c92428', + takerAddress: '0xf6da68519f78b0d0bc93c701e86affcb75c92428', + feeRecipientAddress: '0xc370d2a5920344aa6b7d8d11250e3e861434cbdd', + senderAddress: '0xf6da68519f78b0d0bc93c701e86affcb75c92428', + orderHash: '0xab12ed2cbaa5615ab690b9da75a46e53ddfcf3f1a68655b5fe0d94c75a1aac4a', + rawMakerAssetData: '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + makerAssetProxyId: '0xf47261b0', + makerTokenAddress: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + rawTakerAssetData: '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498', + takerAssetProxyId: '0xf47261b0', + takerTokenAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498', +}; + +const erc20CancelEvent = R.merge(baseCancelEvent, { + makerAssetType: 'erc20' as AssetType, + makerTokenId: null, + takerAssetType: 'erc20' as AssetType, + takerTokenId: null, +}); + +const erc721CancelEvent = R.merge(baseCancelEvent, { + makerAssetType: 'erc721' as AssetType, + makerTokenId: '19378573', + takerAssetType: 'erc721' as AssetType, + takerTokenId: '63885673888', +}); + +// tslint:disable:custom-no-magic-numbers +describe('ExchangeCancelEvent entity', () => { + it('save/find', async () => { + const connection = await createDbConnectionOnceAsync(); + const events = [erc20CancelEvent, erc721CancelEvent]; + const cancelEventRepository = connection.getRepository(ExchangeCancelEvent); + for (const event of events) { + await testSaveAndFindEntityAsync(cancelEventRepository, event); + } + }); +}); diff --git a/packages/pipeline/test/entities/exchange_cancel_up_to_event_test.ts b/packages/pipeline/test/entities/exchange_cancel_up_to_event_test.ts new file mode 100644 index 000000000..aa34f8c1c --- /dev/null +++ b/packages/pipeline/test/entities/exchange_cancel_up_to_event_test.ts @@ -0,0 +1,29 @@ +import { BigNumber } from '@0x/utils'; +import 'mocha'; +import 'reflect-metadata'; + +import { ExchangeCancelUpToEvent } from '../../src/entities'; +import { createDbConnectionOnceAsync } from '../db_setup'; +import { chaiSetup } from '../utils/chai_setup'; + +import { testSaveAndFindEntityAsync } from './util'; + +chaiSetup.configure(); + +// tslint:disable:custom-no-magic-numbers +describe('ExchangeCancelUpToEvent entity', () => { + it('save/find', async () => { + const connection = await createDbConnectionOnceAsync(); + const cancelUpToEventRepository = connection.getRepository(ExchangeCancelUpToEvent); + const cancelUpToEvent = new ExchangeCancelUpToEvent(); + cancelUpToEvent.blockNumber = 6276262; + cancelUpToEvent.contractAddress = '0x4f833a24e1f95d70f028921e27040ca56e09ab0b'; + cancelUpToEvent.logIndex = 42; + cancelUpToEvent.makerAddress = '0xf6da68519f78b0d0bc93c701e86affcb75c92428'; + cancelUpToEvent.orderEpoch = new BigNumber('123456789123456789'); + cancelUpToEvent.rawData = '0x000000000000000000000000f6da68519f78b0d0bc93c701e86affcb75c92428'; + cancelUpToEvent.senderAddress = '0xf6da68519f78b0d0bc93c701e86affcb75c92428'; + cancelUpToEvent.transactionHash = '0x6dd106d002873746072fc5e496dd0fb2541b68c77bcf9184ae19a42fd33657fe'; + await testSaveAndFindEntityAsync(cancelUpToEventRepository, cancelUpToEvent); + }); +}); diff --git a/packages/pipeline/test/entities/exchange_fill_event_test.ts b/packages/pipeline/test/entities/exchange_fill_event_test.ts new file mode 100644 index 000000000..b2cb8c5e0 --- /dev/null +++ b/packages/pipeline/test/entities/exchange_fill_event_test.ts @@ -0,0 +1,62 @@ +import { BigNumber } from '@0x/utils'; +import 'mocha'; +import * as R from 'ramda'; +import 'reflect-metadata'; + +import { ExchangeFillEvent } from '../../src/entities'; +import { AssetType } from '../../src/types'; +import { createDbConnectionOnceAsync } from '../db_setup'; +import { chaiSetup } from '../utils/chai_setup'; + +import { testSaveAndFindEntityAsync } from './util'; + +chaiSetup.configure(); + +const baseFillEvent = { + contractAddress: '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', + blockNumber: 6276262, + logIndex: 102, + rawData: '0x000000000000000000000000f6da68519f78b0d0bc93c701e86affcb75c92428', + transactionHash: '0x6dd106d002873746072fc5e496dd0fb2541b68c77bcf9184ae19a42fd33657fe', + makerAddress: '0xf6da68519f78b0d0bc93c701e86affcb75c92428', + takerAddress: '0xf6da68519f78b0d0bc93c701e86affcb75c92428', + feeRecipientAddress: '0xc370d2a5920344aa6b7d8d11250e3e861434cbdd', + senderAddress: '0xf6da68519f78b0d0bc93c701e86affcb75c92428', + makerAssetFilledAmount: new BigNumber('10000000000000000'), + takerAssetFilledAmount: new BigNumber('100000000000000000'), + makerFeePaid: new BigNumber('0'), + takerFeePaid: new BigNumber('12345'), + orderHash: '0xab12ed2cbaa5615ab690b9da75a46e53ddfcf3f1a68655b5fe0d94c75a1aac4a', + rawMakerAssetData: '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + makerAssetProxyId: '0xf47261b0', + makerTokenAddress: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + rawTakerAssetData: '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498', + takerAssetProxyId: '0xf47261b0', + takerTokenAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498', +}; + +const erc20FillEvent = R.merge(baseFillEvent, { + makerAssetType: 'erc20' as AssetType, + makerTokenId: null, + takerAssetType: 'erc20' as AssetType, + takerTokenId: null, +}); + +const erc721FillEvent = R.merge(baseFillEvent, { + makerAssetType: 'erc721' as AssetType, + makerTokenId: '19378573', + takerAssetType: 'erc721' as AssetType, + takerTokenId: '63885673888', +}); + +// tslint:disable:custom-no-magic-numbers +describe('ExchangeFillEvent entity', () => { + it('save/find', async () => { + const connection = await createDbConnectionOnceAsync(); + const events = [erc20FillEvent, erc721FillEvent]; + const fillEventsRepository = connection.getRepository(ExchangeFillEvent); + for (const event of events) { + await testSaveAndFindEntityAsync(fillEventsRepository, event); + } + }); +}); diff --git a/packages/pipeline/test/entities/relayer_test.ts b/packages/pipeline/test/entities/relayer_test.ts new file mode 100644 index 000000000..760ffb6f9 --- /dev/null +++ b/packages/pipeline/test/entities/relayer_test.ts @@ -0,0 +1,55 @@ +import 'mocha'; +import * as R from 'ramda'; +import 'reflect-metadata'; + +import { Relayer } from '../../src/entities'; +import { createDbConnectionOnceAsync } from '../db_setup'; +import { chaiSetup } from '../utils/chai_setup'; + +import { testSaveAndFindEntityAsync } from './util'; + +chaiSetup.configure(); + +const baseRelayer = { + uuid: 'e8d27d8d-ddf6-48b1-9663-60b0a3ddc716', + name: 'Radar Relay', + homepageUrl: 'https://radarrelay.com', + appUrl: null, + sraHttpEndpoint: null, + sraWsEndpoint: null, + feeRecipientAddresses: [], + takerAddresses: [], +}; + +const relayerWithUrls = R.merge(baseRelayer, { + uuid: 'e8d27d8d-ddf6-48b1-9663-60b0a3ddc717', + appUrl: 'https://app.radarrelay.com', + sraHttpEndpoint: 'https://api.radarrelay.com/0x/v2/', + sraWsEndpoint: 'wss://ws.radarrelay.com/0x/v2', +}); + +const relayerWithAddresses = R.merge(baseRelayer, { + uuid: 'e8d27d8d-ddf6-48b1-9663-60b0a3ddc718', + feeRecipientAddresses: [ + '0xa258b39954cef5cb142fd567a46cddb31a670124', + '0xa258b39954cef5cb142fd567a46cddb31a670125', + '0xa258b39954cef5cb142fd567a46cddb31a670126', + ], + takerAddresses: [ + '0xa258b39954cef5cb142fd567a46cddb31a670127', + '0xa258b39954cef5cb142fd567a46cddb31a670128', + '0xa258b39954cef5cb142fd567a46cddb31a670129', + ], +}); + +// tslint:disable:custom-no-magic-numbers +describe('Relayer entity', () => { + it('save/find', async () => { + const connection = await createDbConnectionOnceAsync(); + const relayers = [baseRelayer, relayerWithUrls, relayerWithAddresses]; + const relayerRepository = connection.getRepository(Relayer); + for (const relayer of relayers) { + await testSaveAndFindEntityAsync(relayerRepository, relayer); + } + }); +}); diff --git a/packages/pipeline/test/entities/sra_order_test.ts b/packages/pipeline/test/entities/sra_order_test.ts new file mode 100644 index 000000000..c43de8ce8 --- /dev/null +++ b/packages/pipeline/test/entities/sra_order_test.ts @@ -0,0 +1,84 @@ +import { BigNumber } from '@0x/utils'; +import 'mocha'; +import * as R from 'ramda'; +import 'reflect-metadata'; +import { Repository } from 'typeorm'; + +import { SraOrder, SraOrdersObservedTimeStamp } from '../../src/entities'; +import { AssetType } from '../../src/types'; +import { createDbConnectionOnceAsync } from '../db_setup'; +import { chaiSetup } from '../utils/chai_setup'; + +import { testSaveAndFindEntityAsync } from './util'; + +chaiSetup.configure(); + +const baseOrder = { + sourceUrl: 'https://api.radarrelay.com/0x/v2', + exchangeAddress: '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', + makerAddress: '0xb45df06e38540a675fdb5b598abf2c0dbe9d6b81', + takerAddress: '0x0000000000000000000000000000000000000000', + feeRecipientAddress: '0xa258b39954cef5cb142fd567a46cddb31a670124', + senderAddress: '0x0000000000000000000000000000000000000000', + makerAssetAmount: new BigNumber('1619310371000000000'), + takerAssetAmount: new BigNumber('8178335207070707070707'), + makerFee: new BigNumber('100'), + takerFee: new BigNumber('200'), + expirationTimeSeconds: new BigNumber('1538529488'), + salt: new BigNumber('1537924688891'), + signature: '0x1b5a5d672b0d647b5797387ccbb89d8', + rawMakerAssetData: '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + makerAssetProxyId: '0xf47261b0', + makerTokenAddress: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + rawTakerAssetData: '0xf47261b000000000000000000000000042d6622dece394b54999fbd73d108123806f6a18', + takerAssetProxyId: '0xf47261b0', + takerTokenAddress: '0x42d6622dece394b54999fbd73d108123806f6a18', + metadataJson: '{"isThisArbitraryData":true,"powerLevel":9001}', +}; + +const erc20Order = R.merge(baseOrder, { + orderHashHex: '0x1bdbeb0d088a33da28b9ee6d94e8771452f90f4a69107da2fa75195d61b9a1c9', + makerAssetType: 'erc20' as AssetType, + makerTokenId: null, + takerAssetType: 'erc20' as AssetType, + takerTokenId: null, +}); + +const erc721Order = R.merge(baseOrder, { + orderHashHex: '0x1bdbeb0d088a33da28b9ee6d94e8771452f90f4a69107da2fa75195d61b9a1d0', + makerAssetType: 'erc721' as AssetType, + makerTokenId: '19378573', + takerAssetType: 'erc721' as AssetType, + takerTokenId: '63885673888', +}); + +// tslint:disable:custom-no-magic-numbers +describe('SraOrder and SraOrdersObservedTimeStamp entities', () => { + // Note(albrow): SraOrder and SraOrdersObservedTimeStamp are tightly coupled + // and timestamps have a foreign key constraint such that they have to point + // to an existing SraOrder. For these reasons, we are testing them together + // in the same test. + it('save/find', async () => { + const connection = await createDbConnectionOnceAsync(); + const orderRepository = connection.getRepository(SraOrder); + const timestampRepository = connection.getRepository(SraOrdersObservedTimeStamp); + const orders = [erc20Order, erc721Order]; + for (const order of orders) { + await testOrderWithTimestampAsync(orderRepository, timestampRepository, order); + } + }); +}); + +async function testOrderWithTimestampAsync( + orderRepository: Repository, + timestampRepository: Repository, + order: SraOrder, +): Promise { + await testSaveAndFindEntityAsync(orderRepository, order); + const timestamp = new SraOrdersObservedTimeStamp(); + timestamp.exchangeAddress = order.exchangeAddress; + timestamp.orderHashHex = order.orderHashHex; + timestamp.sourceUrl = order.sourceUrl; + timestamp.observedTimestamp = 1543377376153; + await testSaveAndFindEntityAsync(timestampRepository, timestamp); +} diff --git a/packages/pipeline/test/entities/token_metadata_test.ts b/packages/pipeline/test/entities/token_metadata_test.ts new file mode 100644 index 000000000..805b4b0f7 --- /dev/null +++ b/packages/pipeline/test/entities/token_metadata_test.ts @@ -0,0 +1,38 @@ +import 'mocha'; +import 'reflect-metadata'; + +import { TokenMetadata } from '../../src/entities'; +import { createDbConnectionOnceAsync } from '../db_setup'; +import { chaiSetup } from '../utils/chai_setup'; + +import { testSaveAndFindEntityAsync } from './util'; + +chaiSetup.configure(); + +const metadataWithoutNullFields = { + address: '0xe41d2489571d322189246dafa5ebde1f4699f498', + authority: 'https://website-api.0xproject.com/tokens', + decimals: 18, + symbol: 'ZRX', + name: '0x', +}; + +const metadataWithNullFields = { + address: '0xe41d2489571d322189246dafa5ebde1f4699f499', + authority: 'https://website-api.0xproject.com/tokens', + decimals: null, + symbol: null, + name: null, +}; + +// tslint:disable:custom-no-magic-numbers +describe('TokenMetadata entity', () => { + it('save/find', async () => { + const connection = await createDbConnectionOnceAsync(); + const tokenMetadata = [metadataWithoutNullFields, metadataWithNullFields]; + const tokenMetadataRepository = connection.getRepository(TokenMetadata); + for (const tokenMetadatum of tokenMetadata) { + await testSaveAndFindEntityAsync(tokenMetadataRepository, tokenMetadatum); + } + }); +}); diff --git a/packages/pipeline/test/entities/transaction_test.ts b/packages/pipeline/test/entities/transaction_test.ts new file mode 100644 index 000000000..027de7d32 --- /dev/null +++ b/packages/pipeline/test/entities/transaction_test.ts @@ -0,0 +1,25 @@ +import 'mocha'; +import 'reflect-metadata'; + +import { Transaction } from '../../src/entities'; +import { createDbConnectionOnceAsync } from '../db_setup'; +import { chaiSetup } from '../utils/chai_setup'; + +import { testSaveAndFindEntityAsync } from './util'; + +chaiSetup.configure(); + +// tslint:disable:custom-no-magic-numbers +describe('Transaction entity', () => { + it('save/find', async () => { + const connection = await createDbConnectionOnceAsync(); + const transactionRepository = connection.getRepository(Transaction); + const transaction = new Transaction(); + transaction.blockHash = '0x6ff106d00b6c3746072fc06bae140fb2549036ba7bcf9184ae19a42fd33657fd'; + transaction.blockNumber = 6276262; + transaction.gasPrice = 3000000; + transaction.gasUsed = 125000; + transaction.transactionHash = '0x6dd106d002873746072fc5e496dd0fb2541b68c77bcf9184ae19a42fd33657fe'; + await testSaveAndFindEntityAsync(transactionRepository, transaction); + }); +}); diff --git a/packages/pipeline/test/entities/util.ts b/packages/pipeline/test/entities/util.ts new file mode 100644 index 000000000..043a3b15d --- /dev/null +++ b/packages/pipeline/test/entities/util.ts @@ -0,0 +1,25 @@ +import * as chai from 'chai'; +import 'mocha'; + +import { Repository } from 'typeorm'; + +const expect = chai.expect; + +/** + * First saves the given entity to the database, then finds it and makes sure + * that the found entity is exactly equal to the original one. This is a bare + * minimum basic test to make sure that the entity type definition and our + * database schema are aligned and that it is possible to save and find the + * entity. + * @param repository A TypeORM repository corresponding with the type of the entity. + * @param entity An instance of a TypeORM entity which will be saved/retrieved from the database. + */ +export async function testSaveAndFindEntityAsync(repository: Repository, entity: T): Promise { + // Note(albrow): We are forced to use an 'as any' hack here because + // TypeScript complains about stack depth when checking the types. + await repository.save(entity as any); + const gotEntity = await repository.findOneOrFail({ + where: entity, + }); + expect(gotEntity).deep.equal(entity); +} diff --git a/yarn.lock b/yarn.lock index 88747eee8..400c48a51 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1284,6 +1284,12 @@ version "2.0.0" resolved "https://registry.npmjs.org/@types/detect-node/-/detect-node-2.0.0.tgz#696e024ddd105c72bbc6a2e3f97902a2886f2c3f" +"@types/dockerode@^2.5.9": + version "2.5.9" + resolved "https://registry.yarnpkg.com/@types/dockerode/-/dockerode-2.5.9.tgz#de702ef63a0db7d025211e2e8af7bc90f617b7d8" + dependencies: + "@types/node" "*" + "@types/enzyme-adapter-react-16@^1.0.3": version "1.0.3" resolved "https://registry.npmjs.org/@types/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.0.3.tgz#0cf7025b036694ca8d596fe38f24162e7117acf1" @@ -1850,6 +1856,13 @@ version "4.2.1" resolved "https://registry.npmjs.org/@xtuc/long/-/long-4.2.1.tgz#5c85d662f76fa1d34575766c5dcd6615abcd30d8" +JSONStream@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.2.tgz#c102371b6ec3a7cf3b847ca00c20bb0fce4c6dea" + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + JSONStream@^1.0.4: version "1.3.3" resolved "http://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.3.tgz#27b4b8fbbfeab4e71bcf551e7f27be8d952239bf" @@ -4237,7 +4250,7 @@ concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" -concat-stream@^1.4.6, concat-stream@^1.5.1, concat-stream@^1.6.0: +concat-stream@^1.4.6, concat-stream@^1.5.1, concat-stream@^1.6.0, concat-stream@~1.6.2: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" dependencies: @@ -4903,6 +4916,12 @@ debug@3.1.0, debug@=3.1.0, debug@^3.1.0: dependencies: ms "2.0.0" +debug@^3.2.5: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + dependencies: + ms "^2.1.1" + debug@^4.0.1: version "4.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.0.tgz#373687bffa678b38b1cd91f861b63850035ddc87" @@ -5267,6 +5286,23 @@ dns-txt@^2.0.2: dependencies: buffer-indexof "^1.0.0" +docker-modem@1.0.x: + version "1.0.7" + resolved "https://registry.yarnpkg.com/docker-modem/-/docker-modem-1.0.7.tgz#69702a95c060eeb6775f79ccdcc734e5946972a4" + dependencies: + JSONStream "1.3.2" + debug "^3.2.5" + readable-stream "~1.0.26-4" + split-ca "^1.0.0" + +dockerode@^2.5.7: + version "2.5.7" + resolved "https://registry.yarnpkg.com/dockerode/-/dockerode-2.5.7.tgz#13dc9ec0f7f353ac0e512249e77f32d1aaa1199e" + dependencies: + concat-stream "~1.6.2" + docker-modem "1.0.x" + tar-fs "~1.16.3" + doctrine@0.7.2: version "0.7.2" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-0.7.2.tgz#7cb860359ba3be90e040b26b729ce4bfa654c523" @@ -12940,7 +12976,7 @@ read@1, read@1.0.x, read@~1.0.1, read@~1.0.5: string_decoder "~1.1.1" util-deprecate "~1.0.1" -"readable-stream@>=1.0.33-1 <1.1.0-0", readable-stream@~1.0.15, readable-stream@~1.0.26, readable-stream@~1.0.31: +"readable-stream@>=1.0.33-1 <1.1.0-0", readable-stream@~1.0.15, readable-stream@~1.0.26, readable-stream@~1.0.26-4, readable-stream@~1.0.31: version "1.0.34" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" dependencies: @@ -14403,6 +14439,10 @@ speedometer@~0.1.2: version "0.1.4" resolved "https://registry.yarnpkg.com/speedometer/-/speedometer-0.1.4.tgz#9876dbd2a169d3115402d48e6ea6329c8816a50d" +split-ca@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/split-ca/-/split-ca-1.0.1.tgz#6c83aff3692fa61256e0cd197e05e9de157691a6" + split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" @@ -14927,6 +14967,15 @@ tar-fs@^1.13.0: pump "^1.0.0" tar-stream "^1.1.2" +tar-fs@~1.16.3: + version "1.16.3" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-1.16.3.tgz#966a628841da2c4010406a82167cbd5e0c72d509" + dependencies: + chownr "^1.0.1" + mkdirp "^0.5.1" + pump "^1.0.0" + tar-stream "^1.1.2" + tar-pack@^3.4.0: version "3.4.1" resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.1.tgz#e1dbc03a9b9d3ba07e896ad027317eb679a10a1f" -- cgit v1.2.3 From 7198b441e0d85785eec7244dd60bcd92269d954e Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Thu, 29 Nov 2018 11:40:09 -0800 Subject: Add script for parsing competing dex trades from Bloxy (#1355) --- .../migrations/1543446690436-CreateDexTrades.ts | 41 +++++++ packages/pipeline/src/data_sources/bloxy/index.ts | 133 +++++++++++++++++++++ packages/pipeline/src/entities/dex_trade.ts | 54 +++++++++ packages/pipeline/src/entities/index.ts | 7 +- packages/pipeline/src/ormconfig.ts | 4 +- packages/pipeline/src/parsers/bloxy/index.ts | 53 ++++++++ .../src/scripts/pull_competing_dex_trades.ts | 51 ++++++++ packages/pipeline/test/entities/dex_trades_test.ts | 60 ++++++++++ packages/pipeline/test/parsers/bloxy/index_test.ts | 99 +++++++++++++++ 9 files changed, 498 insertions(+), 4 deletions(-) create mode 100644 packages/pipeline/migrations/1543446690436-CreateDexTrades.ts create mode 100644 packages/pipeline/src/data_sources/bloxy/index.ts create mode 100644 packages/pipeline/src/entities/dex_trade.ts create mode 100644 packages/pipeline/src/parsers/bloxy/index.ts create mode 100644 packages/pipeline/src/scripts/pull_competing_dex_trades.ts create mode 100644 packages/pipeline/test/entities/dex_trades_test.ts create mode 100644 packages/pipeline/test/parsers/bloxy/index_test.ts diff --git a/packages/pipeline/migrations/1543446690436-CreateDexTrades.ts b/packages/pipeline/migrations/1543446690436-CreateDexTrades.ts new file mode 100644 index 000000000..267cf144b --- /dev/null +++ b/packages/pipeline/migrations/1543446690436-CreateDexTrades.ts @@ -0,0 +1,41 @@ +import { MigrationInterface, QueryRunner, Table } from 'typeorm'; + +const dexTrades = new Table({ + name: 'raw.dex_trades', + columns: [ + { name: 'source_url', type: 'varchar', isPrimary: true }, + { name: 'tx_hash', type: 'varchar', isPrimary: true }, + + { name: 'tx_timestamp', type: 'bigint' }, + { name: 'tx_date', type: 'varchar' }, + { name: 'tx_sender', type: 'varchar(42)' }, + { name: 'smart_contract_id', type: 'bigint' }, + { name: 'smart_contract_address', type: 'varchar(42)' }, + { name: 'contract_type', type: 'varchar' }, + { name: 'maker', type: 'varchar(42)' }, + { name: 'taker', type: 'varchar(42)' }, + { name: 'amount_buy', type: 'numeric' }, + { name: 'maker_fee_amount', type: 'numeric' }, + { name: 'buy_currency_id', type: 'bigint' }, + { name: 'buy_symbol', type: 'varchar' }, + { name: 'amount_sell', type: 'numeric' }, + { name: 'taker_fee_amount', type: 'numeric' }, + { name: 'sell_currency_id', type: 'bigint' }, + { name: 'sell_symbol', type: 'varchar' }, + { name: 'maker_annotation', type: 'varchar' }, + { name: 'taker_annotation', type: 'varchar' }, + { name: 'protocol', type: 'varchar' }, + { name: 'buy_address', type: 'varchar(42)', isNullable: true }, + { name: 'sell_address', type: 'varchar(42)', isNullable: true }, + ], +}); + +export class CreateDexTrades1543446690436 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.createTable(dexTrades); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropTable(dexTrades); + } +} diff --git a/packages/pipeline/src/data_sources/bloxy/index.ts b/packages/pipeline/src/data_sources/bloxy/index.ts new file mode 100644 index 000000000..31cd5bfd6 --- /dev/null +++ b/packages/pipeline/src/data_sources/bloxy/index.ts @@ -0,0 +1,133 @@ +import axios from 'axios'; +import * as R from 'ramda'; + +// URL to use for getting dex trades from Bloxy. +export const BLOXY_DEX_TRADES_URL = 'https://bloxy.info/api/dex/trades'; +// Number of trades to get at once. Must be less than or equal to MAX_OFFSET. +const TRADES_PER_QUERY = 10000; +// Maximum offset supported by the Bloxy API. +const MAX_OFFSET = 100000; +// Buffer to subtract from offset. This means we will request some trades twice +// but we have less chance on missing out on any data. +const OFFSET_BUFFER = 1000; +// Maximum number of days supported by the Bloxy API. +const MAX_DAYS = 30; +// Buffer used for comparing the last seen timestamp to the last returned +// timestamp. Increasing this reduces chances of data loss but also creates more +// redundancy and can impact performance. +// tslint:disable-next-line:custom-no-magic-numbers +const LAST_SEEN_TIMESTAMP_BUFFER_MS = 1000 * 60 * 30; // 30 minutes + +// tslint:disable-next-line:custom-no-magic-numbers +const millisecondsPerDay = 1000 * 60 * 60 * 24; // ms/d = ms/s * s/m * m/h * h/d + +export interface BloxyTrade { + tx_hash: string; + tx_time: string; + tx_date: string; + tx_sender: string; + smart_contract_id: number; + smart_contract_address: string; + contract_type: string; + maker: string; + taker: string; + amountBuy: number; + makerFee: number; + buyCurrencyId: number; + buySymbol: string; + amountSell: number; + takerFee: number; + sellCurrencyId: number; + sellSymbol: string; + maker_annotation: string; + taker_annotation: string; + protocol: string; + buyAddress: string | null; + sellAddress: string | null; +} + +interface BloxyError { + error: string; +} + +type BloxyResponse = T | BloxyError; +type BloxyTradeResponse = BloxyResponse; + +function isError(response: BloxyResponse): response is BloxyError { + return (response as BloxyError).error !== undefined; +} + +export class BloxySource { + private readonly _apiKey: string; + + constructor(apiKey: string) { + this._apiKey = apiKey; + } + + /** + * Gets all latest trades between the lastSeenTimestamp (minus some buffer) + * and the current time. Note that because the Bloxy API has some hard + * limits it might not always be possible to get *all* the trades in the + * desired time range. + * @param lastSeenTimestamp The latest timestamp for trades that have + * already been seen. + */ + public async getDexTradesAsync(lastSeenTimestamp: number): Promise { + let allTrades: BloxyTrade[] = []; + + // Clamp numberOfDays so that it is always between 1 and MAX_DAYS (inclusive) + const numberOfDays = R.clamp(1, MAX_DAYS, getDaysSinceTimestamp(lastSeenTimestamp)); + + // Keep getting trades until we hit one of the following conditions: + // + // 1. Offset hits MAX_OFFSET (we can't go back any further). + // 2. There are no more trades in the response. + // 3. We see a tx_time equal to or earlier than lastSeenTimestamp (plus + // some buffer). + // + for (let offset = 0; offset <= MAX_OFFSET; offset += TRADES_PER_QUERY - OFFSET_BUFFER) { + const trades = await this._getTradesWithOffsetAsync(numberOfDays, offset); + if (trades.length === 0) { + // There are no more trades left for the days we are querying. + // This means we are done. + return filterDuplicateTrades(allTrades); + } + const sortedTrades = R.reverse(R.sortBy(trade => trade.tx_time, trades)); + allTrades = allTrades.concat(sortedTrades); + + // Check if lastReturnedTimestamp < lastSeenTimestamp + const lastReturnedTimestamp = new Date(sortedTrades[0].tx_time).getTime(); + if (lastReturnedTimestamp < lastSeenTimestamp - LAST_SEEN_TIMESTAMP_BUFFER_MS) { + // We are at the point where we have already seen trades for the + // timestamp range that is being returned. We're done. + return filterDuplicateTrades(allTrades); + } + } + return filterDuplicateTrades(allTrades); + } + + private async _getTradesWithOffsetAsync(numberOfDays: number, offset: number): Promise { + const resp = await axios.get(BLOXY_DEX_TRADES_URL, { + params: { + key: this._apiKey, + days: numberOfDays, + limit: TRADES_PER_QUERY, + offset, + }, + }); + if (isError(resp.data)) { + throw new Error('Error in Bloxy API response: ' + resp.data.error); + } + return resp.data; + } +} + +// Computes the number of days between the given timestamp and the current +// timestamp (rounded up). +function getDaysSinceTimestamp(timestamp: number): number { + const msSinceTimestamp = Date.now() - timestamp; + const daysSinceTimestamp = msSinceTimestamp / millisecondsPerDay; + return Math.ceil(daysSinceTimestamp); +} + +const filterDuplicateTrades = R.uniqBy((trade: BloxyTrade) => trade.tx_hash); diff --git a/packages/pipeline/src/entities/dex_trade.ts b/packages/pipeline/src/entities/dex_trade.ts new file mode 100644 index 000000000..9d288cb51 --- /dev/null +++ b/packages/pipeline/src/entities/dex_trade.ts @@ -0,0 +1,54 @@ +import { BigNumber } from '@0x/utils'; +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { bigNumberTransformer, numberToBigIntTransformer } from '../utils'; + +@Entity({ name: 'dex_trades', schema: 'raw' }) +export class DexTrade { + @PrimaryColumn({ name: 'source_url' }) + public sourceUrl!: string; + @PrimaryColumn({ name: 'tx_hash' }) + public txHash!: string; + + @Column({ name: 'tx_timestamp', type: 'bigint', transformer: numberToBigIntTransformer }) + public txTimestamp!: number; + @Column({ name: 'tx_date' }) + public txDate!: string; + @Column({ name: 'tx_sender' }) + public txSender!: string; + @Column({ name: 'smart_contract_id', type: 'bigint', transformer: numberToBigIntTransformer }) + public smartContractId!: number; + @Column({ name: 'smart_contract_address' }) + public smartContractAddress!: string; + @Column({ name: 'contract_type' }) + public contractType!: string; + @Column({ type: 'varchar' }) + public maker!: string; + @Column({ type: 'varchar' }) + public taker!: string; + @Column({ name: 'amount_buy', type: 'numeric', transformer: bigNumberTransformer }) + public amountBuy!: BigNumber; + @Column({ name: 'maker_fee_amount', type: 'numeric', transformer: bigNumberTransformer }) + public makerFeeAmount!: BigNumber; + @Column({ name: 'buy_currency_id', type: 'bigint', transformer: numberToBigIntTransformer }) + public buyCurrencyId!: number; + @Column({ name: 'buy_symbol' }) + public buySymbol!: string; + @Column({ name: 'amount_sell', type: 'numeric', transformer: bigNumberTransformer }) + public amountSell!: BigNumber; + @Column({ name: 'taker_fee_amount', type: 'numeric', transformer: bigNumberTransformer }) + public takerFeeAmount!: BigNumber; + @Column({ name: 'sell_currency_id', type: 'bigint', transformer: numberToBigIntTransformer }) + public sellCurrencyId!: number; + @Column({ name: 'sell_symbol' }) + public sellSymbol!: string; + @Column({ name: 'maker_annotation' }) + public makerAnnotation!: string; + @Column({ name: 'taker_annotation' }) + public takerAnnotation!: string; + @Column() public protocol!: string; + @Column({ name: 'buy_address', type: 'varchar', nullable: true }) + public buyAddress!: string | null; + @Column({ name: 'sell_address', type: 'varchar', nullable: true }) + public sellAddress!: string | null; +} diff --git a/packages/pipeline/src/entities/index.ts b/packages/pipeline/src/entities/index.ts index a8f159a2a..d3056a477 100644 --- a/packages/pipeline/src/entities/index.ts +++ b/packages/pipeline/src/entities/index.ts @@ -3,14 +3,15 @@ import { ExchangeCancelUpToEvent } from './exchange_cancel_up_to_event'; import { ExchangeFillEvent } from './exchange_fill_event'; export { Block } from './block'; +export { DexTrade } from './dex_trade'; export { ExchangeCancelEvent } from './exchange_cancel_event'; export { ExchangeCancelUpToEvent } from './exchange_cancel_up_to_event'; export { ExchangeFillEvent } from './exchange_fill_event'; +export { OHLCVExternal } from './ohlcv_external'; export { Relayer } from './relayer'; export { SraOrder } from './sra_order'; -export { Transaction } from './transaction'; -export { TokenMetadata } from './token_metadata'; export { SraOrdersObservedTimeStamp, createObservedTimestampForOrder } from './sra_order_observed_timestamp'; -export { OHLCVExternal } from './ohlcv_external'; +export { TokenMetadata } from './token_metadata'; +export { Transaction } from './transaction'; export type ExchangeEvent = ExchangeFillEvent | ExchangeCancelEvent | ExchangeCancelUpToEvent; diff --git a/packages/pipeline/src/ormconfig.ts b/packages/pipeline/src/ormconfig.ts index e8277a439..fd6c7c39b 100644 --- a/packages/pipeline/src/ormconfig.ts +++ b/packages/pipeline/src/ormconfig.ts @@ -2,6 +2,7 @@ import { ConnectionOptions } from 'typeorm'; import { Block, + DexTrade, ExchangeCancelEvent, ExchangeCancelUpToEvent, ExchangeFillEvent, @@ -14,6 +15,7 @@ import { const entities = [ Block, + DexTrade, ExchangeCancelEvent, ExchangeCancelUpToEvent, ExchangeFillEvent, @@ -28,7 +30,7 @@ const config: ConnectionOptions = { type: 'postgres', url: process.env.ZEROEX_DATA_PIPELINE_DB_URL, synchronize: false, - // logging: ['error'], + logging: ['error'], entities, migrations: ['./lib/migrations/**/*.js'], }; diff --git a/packages/pipeline/src/parsers/bloxy/index.ts b/packages/pipeline/src/parsers/bloxy/index.ts new file mode 100644 index 000000000..af07c7507 --- /dev/null +++ b/packages/pipeline/src/parsers/bloxy/index.ts @@ -0,0 +1,53 @@ +import { BigNumber } from '@0x/utils'; +import * as R from 'ramda'; + +import { BLOXY_DEX_TRADES_URL, BloxyTrade } from '../../data_sources/bloxy'; +import { DexTrade } from '../../entities'; + +/** + * Parses a raw trades response from the Bloxy Dex API and returns an array of + * DexTrade entities. + * @param rawTrades A raw order response from an SRA endpoint. + */ +export function parseBloxyTrades(rawTrades: BloxyTrade[]): DexTrade[] { + return R.map(_parseBloxyTrade, rawTrades); +} + +/** + * Converts a single Bloxy trade into a DexTrade entity. + * @param rawTrade A single trade from the response from the Bloxy API. + */ +export function _parseBloxyTrade(rawTrade: BloxyTrade): DexTrade { + const dexTrade = new DexTrade(); + dexTrade.sourceUrl = BLOXY_DEX_TRADES_URL; + dexTrade.txHash = rawTrade.tx_hash; + dexTrade.txTimestamp = new Date(rawTrade.tx_time).getTime(); + dexTrade.txDate = rawTrade.tx_date; + dexTrade.txSender = rawTrade.tx_sender; + dexTrade.smartContractId = rawTrade.smart_contract_id; + dexTrade.smartContractAddress = rawTrade.smart_contract_address; + dexTrade.contractType = rawTrade.contract_type; + dexTrade.maker = rawTrade.maker; + dexTrade.taker = rawTrade.taker; + // TODO(albrow): The Bloxy API returns amounts and fees as a `number` type + // but some of their values have too many significant digits to be + // represented that way. Ideally they will switch to using strings and then + // we can update this code. + dexTrade.amountBuy = new BigNumber(rawTrade.amountBuy.toString()); + dexTrade.makerFeeAmount = new BigNumber(rawTrade.makerFee.toString()); + dexTrade.buyCurrencyId = rawTrade.buyCurrencyId; + dexTrade.buySymbol = filterNullCharacters(rawTrade.buySymbol); + dexTrade.amountSell = new BigNumber(rawTrade.amountSell.toString()); + dexTrade.takerFeeAmount = new BigNumber(rawTrade.takerFee.toString()); + dexTrade.sellCurrencyId = rawTrade.sellCurrencyId; + dexTrade.sellSymbol = filterNullCharacters(rawTrade.sellSymbol); + dexTrade.makerAnnotation = rawTrade.maker_annotation; + dexTrade.takerAnnotation = rawTrade.taker_annotation; + dexTrade.protocol = rawTrade.protocol; + dexTrade.buyAddress = rawTrade.buyAddress; + dexTrade.sellAddress = rawTrade.sellAddress; + return dexTrade; +} + +// Works with any form of escaoed null character (e.g., '\0' and '\u0000'). +const filterNullCharacters = R.replace(/\0/g, ''); diff --git a/packages/pipeline/src/scripts/pull_competing_dex_trades.ts b/packages/pipeline/src/scripts/pull_competing_dex_trades.ts new file mode 100644 index 000000000..4e4c12dd0 --- /dev/null +++ b/packages/pipeline/src/scripts/pull_competing_dex_trades.ts @@ -0,0 +1,51 @@ +// tslint:disable:no-console +import 'reflect-metadata'; +import { Connection, ConnectionOptions, createConnection, Repository } from 'typeorm'; + +import { BloxySource } from '../data_sources/bloxy'; +import { DexTrade } from '../entities'; +import * as ormConfig from '../ormconfig'; +import { parseBloxyTrades } from '../parsers/bloxy'; +import { handleError } from '../utils'; + +// Number of trades to save at once. +const BATCH_SAVE_SIZE = 1000; + +let connection: Connection; + +(async () => { + connection = await createConnection(ormConfig as ConnectionOptions); + await getAndSaveTrades(); + process.exit(0); +})().catch(handleError); + +async function getAndSaveTrades(): Promise { + const apiKey = process.env.BLOXY_API_KEY; + if (apiKey === undefined) { + throw new Error('Missing required env var: BLOXY_API_KEY'); + } + const bloxySource = new BloxySource(apiKey); + const tradesRepository = connection.getRepository(DexTrade); + const lastSeenTimestamp = await getLastSeenTimestampAsync(tradesRepository); + console.log(`Last seen timestamp: ${lastSeenTimestamp === 0 ? 'none' : lastSeenTimestamp}`); + console.log('Getting latest dex trades...'); + const rawTrades = await bloxySource.getDexTradesAsync(lastSeenTimestamp); + console.log(`Parsing ${rawTrades.length} trades...`); + const trades = parseBloxyTrades(rawTrades); + console.log(`Saving ${trades.length} trades...`); + await tradesRepository.save(trades, { chunk: Math.ceil(trades.length / BATCH_SAVE_SIZE) }); + console.log('Done saving trades.'); +} + +async function getLastSeenTimestampAsync(tradesRepository: Repository): Promise { + if ((await tradesRepository.count()) === 0) { + return 0; + } + const response = (await connection.query( + 'SELECT tx_timestamp FROM raw.dex_trades ORDER BY tx_timestamp DESC LIMIT 1', + )) as Array<{ tx_timestamp: number }>; + if (response.length === 0) { + return 0; + } + return response[0].tx_timestamp; +} diff --git a/packages/pipeline/test/entities/dex_trades_test.ts b/packages/pipeline/test/entities/dex_trades_test.ts new file mode 100644 index 000000000..83aaeec8f --- /dev/null +++ b/packages/pipeline/test/entities/dex_trades_test.ts @@ -0,0 +1,60 @@ +import { BigNumber } from '@0x/utils'; +import 'mocha'; +import * as R from 'ramda'; +import 'reflect-metadata'; + +import { DexTrade } from '../../src/entities'; +import { createDbConnectionOnceAsync } from '../db_setup'; +import { chaiSetup } from '../utils/chai_setup'; + +import { testSaveAndFindEntityAsync } from './util'; + +chaiSetup.configure(); + +const baseTrade = { + sourceUrl: 'https://bloxy.info/api/dex/trades', + txTimestamp: 1543447585938, + txDate: '2018-11-21', + txSender: '0x00923b9a074762b93650716333b3e1473a15048e', + smartContractId: 7091917, + smartContractAddress: '0x818e6fecd516ecc3849daf6845e3ec868087b755', + contractType: 'DEX/Kyber Network Proxy', + maker: '0xbf2179859fc6d5bee9bf9158632dc51678a4100c', + taker: '0xbf2179859fc6d5bee9bf9158632dc51678a4100d', + amountBuy: new BigNumber('1.011943163078103'), + makerFeeAmount: new BigNumber(0), + buyCurrencyId: 1, + buySymbol: 'ETH', + amountSell: new BigNumber('941.4997928436911'), + takerFeeAmount: new BigNumber(0), + sellCurrencyId: 16610, + sellSymbol: 'ELF', + makerAnnotation: '', + takerAnnotation: '', + protocol: 'Kyber Network Proxy', + sellAddress: '0xbf2179859fc6d5bee9bf9158632dc51678a4100e', +}; + +const tradeWithNullAddresses: DexTrade = R.merge(baseTrade, { + txHash: '0xb93a7faf92efbbb5405c9a73cd4efd99702fe27c03ff22baee1f1b1e37b3a0bf', + buyAddress: '0xbf2179859fc6d5bee9bf9158632dc51678a4100e', + sellAddress: '0xbf2179859fc6d5bee9bf9158632dc51678a4100f', +}); + +const tradeWithNonNullAddresses: DexTrade = R.merge(baseTrade, { + txHash: '0xb93a7faf92efbbb5405c9a73cd4efd99702fe27c03ff22baee1f1b1e37b3a0be', + buyAddress: null, + sellAddress: null, +}); + +// tslint:disable:custom-no-magic-numbers +describe('DexTrade entity', () => { + it('save/find', async () => { + const connection = await createDbConnectionOnceAsync(); + const trades = [tradeWithNullAddresses, tradeWithNonNullAddresses]; + const tradesRepository = connection.getRepository(DexTrade); + for (const trade of trades) { + await testSaveAndFindEntityAsync(tradesRepository, trade); + } + }); +}); diff --git a/packages/pipeline/test/parsers/bloxy/index_test.ts b/packages/pipeline/test/parsers/bloxy/index_test.ts new file mode 100644 index 000000000..2b8d68f98 --- /dev/null +++ b/packages/pipeline/test/parsers/bloxy/index_test.ts @@ -0,0 +1,99 @@ +// tslint:disable:custom-no-magic-numbers +import { BigNumber } from '@0x/utils'; +import * as chai from 'chai'; +import 'mocha'; +import * as R from 'ramda'; + +import { BLOXY_DEX_TRADES_URL, BloxyTrade } from '../../../src/data_sources/bloxy'; +import { DexTrade } from '../../../src/entities'; +import { _parseBloxyTrade } from '../../../src/parsers/bloxy'; +import { _convertToExchangeFillEvent } from '../../../src/parsers/events'; +import { chaiSetup } from '../../utils/chai_setup'; + +chaiSetup.configure(); +const expect = chai.expect; + +const baseInput: BloxyTrade = { + tx_hash: '0xb93a7faf92efbbb5405c9a73cd4efd99702fe27c03ff22baee1f1b1e37b3a0bf', + tx_time: '2018-11-21T09:06:28.000+00:00', + tx_date: '2018-11-21', + tx_sender: '0x00923b9a074762b93650716333b3e1473a15048e', + smart_contract_id: 7091917, + smart_contract_address: '0x818e6fecd516ecc3849daf6845e3ec868087b755', + contract_type: 'DEX/Kyber Network Proxy', + maker: '0x0000000000000000000000000000000000000001', + taker: '0x0000000000000000000000000000000000000002', + amountBuy: 1.011943163078103, + makerFee: 38.912083, + buyCurrencyId: 1, + buySymbol: 'ETH', + amountSell: 941.4997928436911, + takerFee: 100.39, + sellCurrencyId: 16610, + sellSymbol: 'ELF', + maker_annotation: 'random annotation', + taker_annotation: 'random other annotation', + protocol: 'Kyber Network Proxy', + buyAddress: '0xbf2179859fc6d5bee9bf9158632dc51678a4100d', + sellAddress: '0xbf2179859fc6d5bee9bf9158632dc51678a4100e', +}; + +const baseExpected: DexTrade = { + sourceUrl: BLOXY_DEX_TRADES_URL, + txHash: '0xb93a7faf92efbbb5405c9a73cd4efd99702fe27c03ff22baee1f1b1e37b3a0bf', + txTimestamp: 1542791188000, + txDate: '2018-11-21', + txSender: '0x00923b9a074762b93650716333b3e1473a15048e', + smartContractId: 7091917, + smartContractAddress: '0x818e6fecd516ecc3849daf6845e3ec868087b755', + contractType: 'DEX/Kyber Network Proxy', + maker: '0x0000000000000000000000000000000000000001', + taker: '0x0000000000000000000000000000000000000002', + amountBuy: new BigNumber('1.011943163078103'), + makerFeeAmount: new BigNumber('38.912083'), + buyCurrencyId: 1, + buySymbol: 'ETH', + amountSell: new BigNumber('941.4997928436911'), + takerFeeAmount: new BigNumber('100.39'), + sellCurrencyId: 16610, + sellSymbol: 'ELF', + makerAnnotation: 'random annotation', + takerAnnotation: 'random other annotation', + protocol: 'Kyber Network Proxy', + buyAddress: '0xbf2179859fc6d5bee9bf9158632dc51678a4100d', + sellAddress: '0xbf2179859fc6d5bee9bf9158632dc51678a4100e', +}; + +interface TestCase { + input: BloxyTrade; + expected: DexTrade; +} + +const testCases: TestCase[] = [ + { + input: baseInput, + expected: baseExpected, + }, + { + input: R.merge(baseInput, { buyAddress: null, sellAddress: null }), + expected: R.merge(baseExpected, { buyAddress: null, sellAddress: null }), + }, + { + input: R.merge(baseInput, { + buySymbol: + 'RING\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000', + }), + expected: R.merge(baseExpected, { buySymbol: 'RING' }), + }, +]; + +describe('bloxy', () => { + describe('_parseBloxyTrade', () => { + for (const [i, testCase] of testCases.entries()) { + it(`converts BloxyTrade to DexTrade entity (${i + 1}/${testCases.length})`, () => { + const actual = _parseBloxyTrade(testCase.input); + expect(actual).deep.equal(testCase.expected); + }); + } + }); +}); -- cgit v1.2.3 From 87ffa5d7ab19d2288bf68131a7e7ec77578c564c Mon Sep 17 00:00:00 2001 From: zkao Date: Tue, 4 Dec 2018 13:21:46 -0800 Subject: Token_orderbook_snapshots for Ddex and Paradex(#1354) * Implements the TokenOrderbookSnapshot Table * Scripts, Data Sources and Entities to pull Ddex and Paradex API data. --- .../1543434472116-TokenOrderbookSnapshots.ts | 30 +++++++ packages/pipeline/src/data_sources/ddex/index.ts | 78 ++++++++++++++++++ .../pipeline/src/data_sources/paradex/index.ts | 92 ++++++++++++++++++++++ packages/pipeline/src/entities/index.ts | 1 + packages/pipeline/src/entities/token_order.ts | 29 +++++++ packages/pipeline/src/ormconfig.ts | 2 + packages/pipeline/src/parsers/ddex_orders/index.ts | 77 ++++++++++++++++++ .../pipeline/src/parsers/paradex_orders/index.ts | 66 ++++++++++++++++ .../src/scripts/pull_ddex_orderbook_snapshots.ts | 55 +++++++++++++ .../scripts/pull_paradex_orderbook_snapshots.ts | 87 ++++++++++++++++++++ packages/pipeline/src/types.ts | 1 + .../pipeline/test/entities/token_order_test.ts | 31 ++++++++ .../test/parsers/ddex_orders/index_test.ts | 66 ++++++++++++++++ .../test/parsers/paradex_orders/index_test.ts | 54 +++++++++++++ 14 files changed, 669 insertions(+) create mode 100644 packages/pipeline/migrations/1543434472116-TokenOrderbookSnapshots.ts create mode 100644 packages/pipeline/src/data_sources/ddex/index.ts create mode 100644 packages/pipeline/src/data_sources/paradex/index.ts create mode 100644 packages/pipeline/src/entities/token_order.ts create mode 100644 packages/pipeline/src/parsers/ddex_orders/index.ts create mode 100644 packages/pipeline/src/parsers/paradex_orders/index.ts create mode 100644 packages/pipeline/src/scripts/pull_ddex_orderbook_snapshots.ts create mode 100644 packages/pipeline/src/scripts/pull_paradex_orderbook_snapshots.ts create mode 100644 packages/pipeline/test/entities/token_order_test.ts create mode 100644 packages/pipeline/test/parsers/ddex_orders/index_test.ts create mode 100644 packages/pipeline/test/parsers/paradex_orders/index_test.ts diff --git a/packages/pipeline/migrations/1543434472116-TokenOrderbookSnapshots.ts b/packages/pipeline/migrations/1543434472116-TokenOrderbookSnapshots.ts new file mode 100644 index 000000000..a7117c753 --- /dev/null +++ b/packages/pipeline/migrations/1543434472116-TokenOrderbookSnapshots.ts @@ -0,0 +1,30 @@ +import { MigrationInterface, QueryRunner, Table } from 'typeorm'; + +const tokenOrderbookSnapshots = new Table({ + name: 'raw.token_orderbook_snapshots', + columns: [ + { name: 'observed_timestamp', type: 'bigint', isPrimary: true }, + { name: 'source', type: 'varchar', isPrimary: true }, + { name: 'order_type', type: 'order_t' }, + { name: 'price', type: 'numeric', isPrimary: true }, + + { name: 'base_asset_symbol', type: 'varchar', isPrimary: true }, + { name: 'base_asset_address', type: 'char(42)' }, + { name: 'base_volume', type: 'numeric' }, + + { name: 'quote_asset_symbol', type: 'varchar', isPrimary: true }, + { name: 'quote_asset_address', type: 'char(42)' }, + { name: 'quote_volume', type: 'numeric' }, + ], +}); + +export class TokenOrderbookSnapshots1543434472116 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE TYPE order_t AS enum('bid', 'ask');`); + await queryRunner.createTable(tokenOrderbookSnapshots); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropTable(tokenOrderbookSnapshots.name); + } +} diff --git a/packages/pipeline/src/data_sources/ddex/index.ts b/packages/pipeline/src/data_sources/ddex/index.ts new file mode 100644 index 000000000..2bbd8c29b --- /dev/null +++ b/packages/pipeline/src/data_sources/ddex/index.ts @@ -0,0 +1,78 @@ +import { fetchAsync, logUtils } from '@0x/utils'; + +const DDEX_BASE_URL = 'https://api.ddex.io/v2'; +const ACTIVE_MARKETS_URL = `${DDEX_BASE_URL}/markets`; +const NO_AGGREGATION_LEVEL = 3; // See https://docs.ddex.io/#get-orderbook +const ORDERBOOK_ENDPOINT = `/orderbook?level=${NO_AGGREGATION_LEVEL}`; +export const DDEX_SOURCE = 'ddex'; + +export interface DdexActiveMarketsResponse { + status: number; + desc: string; + data: { + markets: DdexMarket[]; + }; +} + +export interface DdexMarket { + id: string; + quoteToken: string; + quoteTokenDecimals: number; + quoteTokenAddress: string; + baseToken: string; + baseTokenDecimals: number; + baseTokenAddress: string; + minOrderSize: string; + maxOrderSize: string; + pricePrecision: number; + priceDecimals: number; + amountDecimals: number; +} + +export interface DdexOrderbookResponse { + status: number; + desc: string; + data: { + orderBook: DdexOrderbook; + }; +} + +export interface DdexOrderbook { + marketId: string; + bids: DdexOrder[]; + asks: DdexOrder[]; +} + +export interface DdexOrder { + price: string; + amount: string; + orderId: string; +} + +// tslint:disable:prefer-function-over-method +// ^ Keep consistency with other sources and help logical organization +export class DdexSource { + /** + * Call Ddex API to find out which markets they are maintaining orderbooks for. + */ + public async getActiveMarketsAsync(): Promise { + logUtils.log('Getting all active DDEX markets'); + const resp = await fetchAsync(ACTIVE_MARKETS_URL); + const respJson: DdexActiveMarketsResponse = await resp.json(); + const markets = respJson.data.markets; + logUtils.log(`Got ${markets.length} markets.`); + return markets; + } + + /** + * Retrieve orderbook from Ddex API for a given market. + * @param marketId String identifying the market we want data for. Eg. 'REP/AUG' + */ + public async getMarketOrderbookAsync(marketId: string): Promise { + logUtils.log(`${marketId}: Retrieving orderbook.`); + const marketOrderbookUrl = `${ACTIVE_MARKETS_URL}/${marketId}${ORDERBOOK_ENDPOINT}`; + const resp = await fetchAsync(marketOrderbookUrl); + const respJson: DdexOrderbookResponse = await resp.json(); + return respJson.data.orderBook; + } +} diff --git a/packages/pipeline/src/data_sources/paradex/index.ts b/packages/pipeline/src/data_sources/paradex/index.ts new file mode 100644 index 000000000..69a03d553 --- /dev/null +++ b/packages/pipeline/src/data_sources/paradex/index.ts @@ -0,0 +1,92 @@ +import { fetchAsync, logUtils } from '@0x/utils'; + +const PARADEX_BASE_URL = 'https://api.paradex.io/consumer/v0'; +const ACTIVE_MARKETS_URL = PARADEX_BASE_URL + '/markets'; +const ORDERBOOK_ENDPOINT = PARADEX_BASE_URL + '/orderbook'; +const TOKEN_INFO_ENDPOINT = PARADEX_BASE_URL + '/tokens'; +export const PARADEX_SOURCE = 'paradex'; + +export type ParadexActiveMarketsResponse = ParadexMarket[]; + +export interface ParadexMarket { + id: string; + symbol: string; + baseToken: string; + quoteToken: string; + minOrderSize: string; + maxOrderSize: string; + priceMaxDecimals: number; + amountMaxDecimals: number; + // These are not native to the Paradex API response. We tag them on later + // by calling the token endpoint and joining on symbol. + baseTokenAddress?: string; + quoteTokenAddress?: string; +} + +export interface ParadexOrderbookResponse { + marketId: number; + marketSymbol: string; + bids: ParadexOrder[]; + asks: ParadexOrder[]; +} + +export interface ParadexOrder { + amount: string; + price: string; +} + +export type ParadexTokenInfoResponse = ParadexTokenInfo[]; + +export interface ParadexTokenInfo { + name: string; + symbol: string; + address: string; +} + +export class ParadexSource { + private readonly _apiKey: string; + + constructor(apiKey: string) { + this._apiKey = apiKey; + } + + /** + * Call Paradex API to find out which markets they are maintaining orderbooks for. + */ + public async getActiveMarketsAsync(): Promise { + logUtils.log('Getting all active Paradex markets.'); + const resp = await fetchAsync(ACTIVE_MARKETS_URL, { + headers: { 'API-KEY': this._apiKey }, + }); + const markets: ParadexActiveMarketsResponse = await resp.json(); + logUtils.log(`Got ${markets.length} markets.`); + return markets; + } + + /** + * Call Paradex API to find out their token information. + */ + public async getTokenInfoAsync(): Promise { + logUtils.log('Getting token information from Paradex.'); + const resp = await fetchAsync(TOKEN_INFO_ENDPOINT, { + headers: { 'API-KEY': this._apiKey }, + }); + const tokens: ParadexTokenInfoResponse = await resp.json(); + logUtils.log(`Got information for ${tokens.length} tokens.`); + return tokens; + } + + /** + * Retrieve orderbook from Paradex API for a given market. + * @param marketSymbol String representing the market we want data for. + */ + public async getMarketOrderbookAsync(marketSymbol: string): Promise { + logUtils.log(`${marketSymbol}: Retrieving orderbook.`); + const marketOrderbookUrl = `${ORDERBOOK_ENDPOINT}?market=${marketSymbol}`; + const resp = await fetchAsync(marketOrderbookUrl, { + headers: { 'API-KEY': this._apiKey }, + }); + const orderbookResponse: ParadexOrderbookResponse = await resp.json(); + return orderbookResponse; + } +} diff --git a/packages/pipeline/src/entities/index.ts b/packages/pipeline/src/entities/index.ts index d3056a477..db0814e38 100644 --- a/packages/pipeline/src/entities/index.ts +++ b/packages/pipeline/src/entities/index.ts @@ -12,6 +12,7 @@ export { Relayer } from './relayer'; export { SraOrder } from './sra_order'; export { SraOrdersObservedTimeStamp, createObservedTimestampForOrder } from './sra_order_observed_timestamp'; export { TokenMetadata } from './token_metadata'; +export { TokenOrderbookSnapshot } from './token_order'; export { Transaction } from './transaction'; export type ExchangeEvent = ExchangeFillEvent | ExchangeCancelEvent | ExchangeCancelUpToEvent; diff --git a/packages/pipeline/src/entities/token_order.ts b/packages/pipeline/src/entities/token_order.ts new file mode 100644 index 000000000..557705767 --- /dev/null +++ b/packages/pipeline/src/entities/token_order.ts @@ -0,0 +1,29 @@ +import { BigNumber } from '@0x/utils'; +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { OrderType } from '../types'; +import { bigNumberTransformer, numberToBigIntTransformer } from '../utils'; + +@Entity({ name: 'token_orderbook_snapshots', schema: 'raw' }) +export class TokenOrderbookSnapshot { + @PrimaryColumn({ name: 'observed_timestamp', type: 'bigint', transformer: numberToBigIntTransformer }) + public observedTimestamp!: number; + @PrimaryColumn({ name: 'source' }) + public source!: string; + @Column({ name: 'order_type' }) + public orderType!: OrderType; + @PrimaryColumn({ name: 'price', type: 'numeric', transformer: bigNumberTransformer }) + public price!: BigNumber; + @PrimaryColumn({ name: 'base_asset_symbol' }) + public baseAssetSymbol!: string; + @Column({ name: 'base_asset_address' }) + public baseAssetAddress!: string; + @Column({ name: 'base_volume', type: 'numeric', transformer: bigNumberTransformer }) + public baseVolume!: BigNumber; + @PrimaryColumn({ name: 'quote_asset_symbol' }) + public quoteAssetSymbol!: string; + @Column({ name: 'quote_asset_address' }) + public quoteAssetAddress!: string; + @Column({ name: 'quote_volume', type: 'numeric', transformer: bigNumberTransformer }) + public quoteVolume!: BigNumber; +} diff --git a/packages/pipeline/src/ormconfig.ts b/packages/pipeline/src/ormconfig.ts index fd6c7c39b..c135c399b 100644 --- a/packages/pipeline/src/ormconfig.ts +++ b/packages/pipeline/src/ormconfig.ts @@ -10,6 +10,7 @@ import { SraOrder, SraOrdersObservedTimeStamp, TokenMetadata, + TokenOrderbookSnapshot, Transaction, } from './entities'; @@ -23,6 +24,7 @@ const entities = [ SraOrder, SraOrdersObservedTimeStamp, TokenMetadata, + TokenOrderbookSnapshot, Transaction, ]; diff --git a/packages/pipeline/src/parsers/ddex_orders/index.ts b/packages/pipeline/src/parsers/ddex_orders/index.ts new file mode 100644 index 000000000..81132e8f0 --- /dev/null +++ b/packages/pipeline/src/parsers/ddex_orders/index.ts @@ -0,0 +1,77 @@ +import { BigNumber } from '@0x/utils'; +import * as R from 'ramda'; + +import { DdexMarket, DdexOrder, DdexOrderbook } from '../../data_sources/ddex'; +import { TokenOrderbookSnapshot as TokenOrder } from '../../entities'; +import { OrderType } from '../../types'; + +/** + * Marque function of this file. + * 1) Takes in orders from an orderbook, + * other information attached. + * @param ddexOrderbook A raw orderbook that we pull from the Ddex API. + * @param ddexMarket An object containing market data also directly from the API. + * @param observedTimestamp Time at which the orders for the market were pulled. + * @param source The exchange where these orders are placed. In this case 'ddex'. + */ +export function parseDdexOrders( + ddexOrderbook: DdexOrderbook, + ddexMarket: DdexMarket, + observedTimestamp: number, + source: string, +): TokenOrder[] { + const aggregatedBids = aggregateOrders(ddexOrderbook.bids); + const aggregatedAsks = aggregateOrders(ddexOrderbook.asks); + const parsedBids = aggregatedBids.map(order => parseDdexOrder(ddexMarket, observedTimestamp, 'bid', source, order)); + const parsedAsks = aggregatedAsks.map(order => parseDdexOrder(ddexMarket, observedTimestamp, 'ask', source, order)); + return parsedBids.concat(parsedAsks); +} + +/** + * Aggregates orders by price point for consistency with other exchanges. + * Querying the Ddex API at level 3 setting returns a breakdown of + * individual orders at each price point. Other exchanges only give total amount + * at each price point. Returns an array of tuples. + * @param ddexOrders A list of Ddex orders awaiting aggregation. + */ +export function aggregateOrders(ddexOrders: DdexOrder[]): Array<[string, BigNumber]> { + const sumAmount = (acc: BigNumber, order: DdexOrder): BigNumber => acc.plus(order.amount); + const aggregatedPricePoints = R.reduceBy(sumAmount, new BigNumber(0), R.prop('price'), ddexOrders); + return Object.entries(aggregatedPricePoints); +} + +/** + * Parse a single aggregated Ddex order in order to form a tokenOrder entity + * which can be saved into the database. + * @param ddexMarket An object containing information about the market where these + * trades have been placed. + * @param observedTimestamp The time when the API response returned back to us. + * @param orderType 'bid' or 'ask' enum. + * @param source Exchange where these orders were placed. + * @param ddexOrder A tuple which we will convert to volume-basis. + */ +export function parseDdexOrder( + ddexMarket: DdexMarket, + observedTimestamp: number, + orderType: OrderType, + source: string, + ddexOrder: [string, BigNumber], +): TokenOrder { + const tokenOrder = new TokenOrder(); + const price = new BigNumber(ddexOrder[0]); + const amount = ddexOrder[1]; + + tokenOrder.source = source; + tokenOrder.observedTimestamp = observedTimestamp; + tokenOrder.orderType = orderType; + tokenOrder.price = price; + + tokenOrder.baseAssetSymbol = ddexMarket.baseToken; + tokenOrder.baseAssetAddress = ddexMarket.baseTokenAddress; + tokenOrder.baseVolume = price.times(amount); + + tokenOrder.quoteAssetSymbol = ddexMarket.quoteToken; + tokenOrder.quoteAssetAddress = ddexMarket.quoteTokenAddress; + tokenOrder.quoteVolume = amount; + return tokenOrder; +} diff --git a/packages/pipeline/src/parsers/paradex_orders/index.ts b/packages/pipeline/src/parsers/paradex_orders/index.ts new file mode 100644 index 000000000..7966658a7 --- /dev/null +++ b/packages/pipeline/src/parsers/paradex_orders/index.ts @@ -0,0 +1,66 @@ +import { BigNumber } from '@0x/utils'; + +import { ParadexMarket, ParadexOrder, ParadexOrderbookResponse } from '../../data_sources/paradex'; +import { TokenOrderbookSnapshot as TokenOrder } from '../../entities'; +import { OrderType } from '../../types'; + +/** + * Marque function of this file. + * 1) Takes in orders from an orderbook (orders are already aggregated by price point), + * 2) For each aggregated order, forms a TokenOrder entity with market data and + * other information attached. + * @param paradexOrderbookResponse An orderbook response from the Paradex API. + * @param paradexMarket An object containing market data also directly from the API. + * @param observedTimestamp Time at which the orders for the market were pulled. + * @param source The exchange where these orders are placed. In this case 'paradex'. + */ +export function parseParadexOrders( + paradexOrderbookResponse: ParadexOrderbookResponse, + paradexMarket: ParadexMarket, + observedTimestamp: number, + source: string, +): TokenOrder[] { + const parsedBids = paradexOrderbookResponse.bids.map(order => + parseParadexOrder(paradexMarket, observedTimestamp, 'bid', source, order), + ); + const parsedAsks = paradexOrderbookResponse.asks.map(order => + parseParadexOrder(paradexMarket, observedTimestamp, 'ask', source, order), + ); + return parsedBids.concat(parsedAsks); +} + +/** + * Parse a single aggregated Ddex order in order to form a tokenOrder entity + * which can be saved into the database. + * @param paradexMarket An object containing information about the market where these + * orders have been placed. + * @param observedTimestamp The time when the API response returned back to us. + * @param orderType 'bid' or 'ask' enum. + * @param source Exchange where these orders were placed. + * @param paradexOrder A ParadexOrder object; basically price, amount tuple. + */ +export function parseParadexOrder( + paradexMarket: ParadexMarket, + observedTimestamp: number, + orderType: OrderType, + source: string, + paradexOrder: ParadexOrder, +): TokenOrder { + const tokenOrder = new TokenOrder(); + const price = new BigNumber(paradexOrder.price); + const amount = new BigNumber(paradexOrder.amount); + + tokenOrder.source = source; + tokenOrder.observedTimestamp = observedTimestamp; + tokenOrder.orderType = orderType; + tokenOrder.price = price; + + tokenOrder.baseAssetSymbol = paradexMarket.baseToken; + tokenOrder.baseAssetAddress = paradexMarket.baseTokenAddress as string; + tokenOrder.baseVolume = price.times(amount); + + tokenOrder.quoteAssetSymbol = paradexMarket.quoteToken; + tokenOrder.quoteAssetAddress = paradexMarket.quoteTokenAddress as string; + tokenOrder.quoteVolume = amount; + return tokenOrder; +} diff --git a/packages/pipeline/src/scripts/pull_ddex_orderbook_snapshots.ts b/packages/pipeline/src/scripts/pull_ddex_orderbook_snapshots.ts new file mode 100644 index 000000000..b02468e9b --- /dev/null +++ b/packages/pipeline/src/scripts/pull_ddex_orderbook_snapshots.ts @@ -0,0 +1,55 @@ +import { logUtils } from '@0x/utils'; +import * as R from 'ramda'; +import { Connection, ConnectionOptions, createConnection } from 'typeorm'; + +import { DDEX_SOURCE, DdexMarket, DdexSource } from '../data_sources/ddex'; +import { TokenOrderbookSnapshot as TokenOrder } from '../entities'; +import * as ormConfig from '../ormconfig'; +import { parseDdexOrders } from '../parsers/ddex_orders'; +import { handleError } from '../utils'; + +// Number of orders to save at once. +const BATCH_SAVE_SIZE = 1000; + +// Number of markets to retrieve orderbooks for at once. +const MARKET_ORDERBOOK_REQUEST_BATCH_SIZE = 50; + +// Delay between market orderbook requests. +const MILLISEC_MARKET_ORDERBOOK_REQUEST_DELAY = 5000; + +let connection: Connection; + +(async () => { + connection = await createConnection(ormConfig as ConnectionOptions); + const ddexSource = new DdexSource(); + const markets = await ddexSource.getActiveMarketsAsync(); + for (const marketsChunk of R.splitEvery(MARKET_ORDERBOOK_REQUEST_BATCH_SIZE, markets)) { + await Promise.all( + marketsChunk.map(async (market: DdexMarket) => getAndSaveMarketOrderbook(ddexSource, market)), + ); + await new Promise(resolve => setTimeout(resolve, MILLISEC_MARKET_ORDERBOOK_REQUEST_DELAY)); + } + process.exit(0); +})().catch(handleError); + +/** + * Retrieve orderbook from Ddex API for a given market. Parse orders and insert + * them into our database. + * @param ddexSource Data source which can query Ddex API. + * @param market Object from Ddex API containing market data. + */ +async function getAndSaveMarketOrderbook(ddexSource: DdexSource, market: DdexMarket): Promise { + const orderBook = await ddexSource.getMarketOrderbookAsync(market.id); + const observedTimestamp = Date.now(); + + logUtils.log(`${market.id}: Parsing orders.`); + const orders = parseDdexOrders(orderBook, market, observedTimestamp, DDEX_SOURCE); + + if (orders.length > 0) { + logUtils.log(`${market.id}: Saving ${orders.length} orders.`); + const TokenOrderRepository = connection.getRepository(TokenOrder); + await TokenOrderRepository.save(orders, { chunk: Math.ceil(orders.length / BATCH_SAVE_SIZE) }); + } else { + logUtils.log(`${market.id}: 0 orders to save.`); + } +} diff --git a/packages/pipeline/src/scripts/pull_paradex_orderbook_snapshots.ts b/packages/pipeline/src/scripts/pull_paradex_orderbook_snapshots.ts new file mode 100644 index 000000000..bae1fbede --- /dev/null +++ b/packages/pipeline/src/scripts/pull_paradex_orderbook_snapshots.ts @@ -0,0 +1,87 @@ +import { logUtils } from '@0x/utils'; +import { Connection, ConnectionOptions, createConnection } from 'typeorm'; + +import { + PARADEX_SOURCE, + ParadexActiveMarketsResponse, + ParadexMarket, + ParadexSource, + ParadexTokenInfoResponse, +} from '../data_sources/paradex'; +import { TokenOrderbookSnapshot as TokenOrder } from '../entities'; +import * as ormConfig from '../ormconfig'; +import { parseParadexOrders } from '../parsers/paradex_orders'; +import { handleError } from '../utils'; + +// Number of orders to save at once. +const BATCH_SAVE_SIZE = 1000; + +let connection: Connection; + +(async () => { + connection = await createConnection(ormConfig as ConnectionOptions); + const apiKey = process.env.PARADEX_DATA_PIPELINE_API_KEY; + if (apiKey === undefined) { + throw new Error('Missing required env var: PARADEX_DATA_PIPELINE_API_KEY'); + } + const paradexSource = new ParadexSource(apiKey); + const markets = await paradexSource.getActiveMarketsAsync(); + const tokenInfoResponse = await paradexSource.getTokenInfoAsync(); + const extendedMarkets = addTokenAddresses(markets, tokenInfoResponse); + await Promise.all( + extendedMarkets.map(async (market: ParadexMarket) => getAndSaveMarketOrderbook(paradexSource, market)), + ); + process.exit(0); +})().catch(handleError); + +/** + * Extend the default ParadexMarket objects with token addresses. + * @param markets An array of ParadexMarket objects. + * @param tokenInfoResponse An array of ParadexTokenInfo containing the addresses. + */ +function addTokenAddresses( + markets: ParadexActiveMarketsResponse, + tokenInfoResponse: ParadexTokenInfoResponse, +): ParadexMarket[] { + const symbolAddressMapping = new Map(); + tokenInfoResponse.forEach(tokenInfo => symbolAddressMapping.set(tokenInfo.symbol, tokenInfo.address)); + + markets.forEach((market: ParadexMarket) => { + if (symbolAddressMapping.has(market.baseToken)) { + market.baseTokenAddress = symbolAddressMapping.get(market.baseToken); + } else { + market.quoteTokenAddress = ''; + logUtils.warn(`${market.baseToken}: No address found.`); + } + + if (symbolAddressMapping.has(market.quoteToken)) { + market.quoteTokenAddress = symbolAddressMapping.get(market.quoteToken); + } else { + market.quoteTokenAddress = ''; + logUtils.warn(`${market.quoteToken}: No address found.`); + } + }); + return markets; +} + +/** + * Retrieve orderbook from Paradex API for a given market. Parse orders and insert + * them into our database. + * @param paradexSource Data source which can query the Paradex API. + * @param market Object from the Paradex API with information about the market in question. + */ +async function getAndSaveMarketOrderbook(paradexSource: ParadexSource, market: ParadexMarket): Promise { + const paradexOrderbookResponse = await paradexSource.getMarketOrderbookAsync(market.symbol); + const observedTimestamp = Date.now(); + + logUtils.log(`${market.symbol}: Parsing orders.`); + const orders = parseParadexOrders(paradexOrderbookResponse, market, observedTimestamp, PARADEX_SOURCE); + + if (orders.length > 0) { + logUtils.log(`${market.symbol}: Saving ${orders.length} orders.`); + const tokenOrderRepository = connection.getRepository(TokenOrder); + await tokenOrderRepository.save(orders, { chunk: Math.ceil(orders.length / BATCH_SAVE_SIZE) }); + } else { + logUtils.log(`${market.symbol}: 0 orders to save.`); + } +} diff --git a/packages/pipeline/src/types.ts b/packages/pipeline/src/types.ts index a12d37895..e02b42a40 100644 --- a/packages/pipeline/src/types.ts +++ b/packages/pipeline/src/types.ts @@ -1 +1,2 @@ export type AssetType = 'erc20' | 'erc721'; +export type OrderType = 'bid' | 'ask'; diff --git a/packages/pipeline/test/entities/token_order_test.ts b/packages/pipeline/test/entities/token_order_test.ts new file mode 100644 index 000000000..c6057f5aa --- /dev/null +++ b/packages/pipeline/test/entities/token_order_test.ts @@ -0,0 +1,31 @@ +import { BigNumber } from '@0x/utils'; +import 'mocha'; + +import { TokenOrderbookSnapshot } from '../../src/entities'; +import { createDbConnectionOnceAsync } from '../db_setup'; +import { chaiSetup } from '../utils/chai_setup'; + +import { testSaveAndFindEntityAsync } from './util'; + +chaiSetup.configure(); + +const tokenOrderbookSnapshot: TokenOrderbookSnapshot = { + source: 'ddextest', + observedTimestamp: Date.now(), + orderType: 'bid', + price: new BigNumber(10.1), + baseAssetSymbol: 'ETH', + baseAssetAddress: '0x818e6fecd516ecc3849daf6845e3ec868087b755', + baseVolume: new BigNumber(143), + quoteAssetSymbol: 'ABC', + quoteAssetAddress: '0x00923b9a074762b93650716333b3e1473a15048e', + quoteVolume: new BigNumber(12.3234234), +}; + +describe('TokenOrderbookSnapshot entity', () => { + it('save/find', async () => { + const connection = await createDbConnectionOnceAsync(); + const tokenOrderbookSnapshotRepository = connection.getRepository(TokenOrderbookSnapshot); + await testSaveAndFindEntityAsync(tokenOrderbookSnapshotRepository, tokenOrderbookSnapshot); + }); +}); diff --git a/packages/pipeline/test/parsers/ddex_orders/index_test.ts b/packages/pipeline/test/parsers/ddex_orders/index_test.ts new file mode 100644 index 000000000..213100f44 --- /dev/null +++ b/packages/pipeline/test/parsers/ddex_orders/index_test.ts @@ -0,0 +1,66 @@ +import { BigNumber } from '@0x/utils'; +import * as chai from 'chai'; +import 'mocha'; + +import { DdexMarket } from '../../../src/data_sources/ddex'; +import { TokenOrderbookSnapshot as TokenOrder } from '../../../src/entities'; +import { aggregateOrders, parseDdexOrder } from '../../../src/parsers/ddex_orders'; +import { OrderType } from '../../../src/types'; +import { chaiSetup } from '../../utils/chai_setup'; + +chaiSetup.configure(); +const expect = chai.expect; + +// tslint:disable:custom-no-magic-numbers +describe('ddex_orders', () => { + describe('aggregateOrders', () => { + it('aggregates orders by price point', () => { + const input = [ + { price: '1', amount: '20', orderId: 'testtest' }, + { price: '1', amount: '30', orderId: 'testone' }, + { price: '2', amount: '100', orderId: 'testtwo' }, + ]; + const expected = [['1', new BigNumber(50)], ['2', new BigNumber(100)]]; + const actual = aggregateOrders(input); + expect(actual).deep.equal(expected); + }); + }); + + describe('parseDdexOrder', () => { + it('converts ddexOrder to TokenOrder entity', () => { + const ddexOrder: [string, BigNumber] = ['0.5', new BigNumber(10)]; + const ddexMarket: DdexMarket = { + id: 'ABC-DEF', + quoteToken: 'ABC', + quoteTokenDecimals: 5, + quoteTokenAddress: '0x0000000000000000000000000000000000000000', + baseToken: 'DEF', + baseTokenDecimals: 2, + baseTokenAddress: '0xb45df06e38540a675fdb5b598abf2c0dbe9d6b81', + minOrderSize: '0.1', + maxOrderSize: '1000', + pricePrecision: 1, + priceDecimals: 1, + amountDecimals: 0, + }; + const observedTimestamp: number = Date.now(); + const orderType: OrderType = 'bid'; + const source: string = 'ddex'; + + const expected = new TokenOrder(); + expected.source = 'ddex'; + expected.observedTimestamp = observedTimestamp; + expected.orderType = 'bid'; + expected.price = new BigNumber(0.5); + expected.baseAssetSymbol = 'DEF'; + expected.baseAssetAddress = '0xb45df06e38540a675fdb5b598abf2c0dbe9d6b81'; + expected.baseVolume = new BigNumber(5); + expected.quoteAssetSymbol = 'ABC'; + expected.quoteAssetAddress = '0x0000000000000000000000000000000000000000'; + expected.quoteVolume = new BigNumber(10); + + const actual = parseDdexOrder(ddexMarket, observedTimestamp, orderType, source, ddexOrder); + expect(actual).deep.equal(expected); + }); + }); +}); diff --git a/packages/pipeline/test/parsers/paradex_orders/index_test.ts b/packages/pipeline/test/parsers/paradex_orders/index_test.ts new file mode 100644 index 000000000..1522806bf --- /dev/null +++ b/packages/pipeline/test/parsers/paradex_orders/index_test.ts @@ -0,0 +1,54 @@ +import { BigNumber } from '@0x/utils'; +import * as chai from 'chai'; +import 'mocha'; + +import { ParadexMarket, ParadexOrder } from '../../../src/data_sources/paradex'; +import { TokenOrderbookSnapshot as TokenOrder } from '../../../src/entities'; +import { parseParadexOrder } from '../../../src/parsers/paradex_orders'; +import { OrderType } from '../../../src/types'; +import { chaiSetup } from '../../utils/chai_setup'; + +chaiSetup.configure(); +const expect = chai.expect; + +// tslint:disable:custom-no-magic-numbers +describe('paradex_orders', () => { + describe('parseParadexOrder', () => { + it('converts ParadexOrder to TokenOrder entity', () => { + const paradexOrder: ParadexOrder = { + amount: '412', + price: '0.1245', + }; + const paradexMarket: ParadexMarket = { + id: '2', + symbol: 'ABC/DEF', + baseToken: 'DEF', + quoteToken: 'ABC', + minOrderSize: '0.1', + maxOrderSize: '1000', + priceMaxDecimals: 5, + amountMaxDecimals: 5, + baseTokenAddress: '0xb45df06e38540a675fdb5b598abf2c0dbe9d6b81', + quoteTokenAddress: '0x0000000000000000000000000000000000000000', + }; + const observedTimestamp: number = Date.now(); + const orderType: OrderType = 'bid'; + const source: string = 'paradex'; + + const expected = new TokenOrder(); + expected.source = 'paradex'; + expected.observedTimestamp = observedTimestamp; + expected.orderType = 'bid'; + expected.price = new BigNumber(0.1245); + expected.baseAssetSymbol = 'DEF'; + expected.baseAssetAddress = '0xb45df06e38540a675fdb5b598abf2c0dbe9d6b81'; + expected.baseVolume = new BigNumber(412 * 0.1245); + expected.quoteAssetSymbol = 'ABC'; + expected.quoteAssetAddress = '0x0000000000000000000000000000000000000000'; + expected.quoteVolume = new BigNumber(412); + + const actual = parseParadexOrder(paradexMarket, observedTimestamp, orderType, source, paradexOrder); + expect(actual).deep.equal(expected); + }); + }); +}); -- cgit v1.2.3 From 8c21a700bae0c751f7f9ca47f9a47628a4478911 Mon Sep 17 00:00:00 2001 From: Xianny <8582774+xianny@users.noreply.github.com> Date: Tue, 4 Dec 2018 13:36:18 -0800 Subject: pull OHLCV records from Crypto Compare (#1349) * [WIP] pull OHLCV records from Crypto Compare * lint * refactor to pull logic out of script and into modules * add entity test for ohlcv_external entity * implement rate limit and chronological backfill for ohlcv * add unit tests; cleanup variable names * Fetch OHLCV pairs params from events table * better method names * fix outdated test * lint * Clean up after review * oops * fix failing test * better filtering of most recent records * fix bug when generating pairs * fix default earliest backfill date * fix bug with retrieving backfill time * prettier --- packages/pipeline/package.json | 3 + .../data_sources/ohlcv_external/crypto_compare.ts | 94 +++++++++++++++++++ packages/pipeline/src/entities/ohlcv_external.ts | 24 +++-- packages/pipeline/src/ormconfig.ts | 2 + .../src/parsers/ohlcv_external/crypto_compare.ts | 38 ++++++++ .../pipeline/src/parsers/ohlcv_external/index.ts | 0 .../src/scripts/pull_ohlcv_cryptocompare.ts | 101 +++++++++++++++++++++ .../pipeline/src/utils/get_ohlcv_trading_pairs.ts | 92 +++++++++++++++++++ packages/pipeline/src/utils/index.ts | 9 -- .../ohlcv_external/crypto_compare_test.ts | 47 ++++++++++ .../pipeline/test/entities/ohlcv_external_test.ts | 35 +++++++ .../parsers/ohlcv_external/crypto_compare_test.ts | 62 +++++++++++++ yarn.lock | 10 +- 13 files changed, 500 insertions(+), 17 deletions(-) create mode 100644 packages/pipeline/src/data_sources/ohlcv_external/crypto_compare.ts create mode 100644 packages/pipeline/src/parsers/ohlcv_external/crypto_compare.ts delete mode 100644 packages/pipeline/src/parsers/ohlcv_external/index.ts create mode 100644 packages/pipeline/src/scripts/pull_ohlcv_cryptocompare.ts create mode 100644 packages/pipeline/src/utils/get_ohlcv_trading_pairs.ts create mode 100644 packages/pipeline/test/data_sources/ohlcv_external/crypto_compare_test.ts create mode 100644 packages/pipeline/test/entities/ohlcv_external_test.ts create mode 100644 packages/pipeline/test/parsers/ohlcv_external/crypto_compare_test.ts diff --git a/packages/pipeline/package.json b/packages/pipeline/package.json index 9831517fb..6af81c048 100644 --- a/packages/pipeline/package.json +++ b/packages/pipeline/package.json @@ -54,11 +54,14 @@ "@0x/utils": "^2.0.3", "@0x/web3-wrapper": "^3.1.0", "@types/dockerode": "^2.5.9", + "@types/p-limit": "^2.0.0", "async-parallel": "^1.2.3", "axios": "^0.18.0", "dockerode": "^2.5.7", "ethereum-types": "^1.0.6", + "p-limit": "^2.0.0", "pg": "^7.5.0", + "prettier": "^1.15.3", "ramda": "^0.25.0", "reflect-metadata": "^0.1.12", "sqlite3": "^4.0.2", diff --git a/packages/pipeline/src/data_sources/ohlcv_external/crypto_compare.ts b/packages/pipeline/src/data_sources/ohlcv_external/crypto_compare.ts new file mode 100644 index 000000000..6b10c29c5 --- /dev/null +++ b/packages/pipeline/src/data_sources/ohlcv_external/crypto_compare.ts @@ -0,0 +1,94 @@ +// tslint:disable:no-duplicate-imports +import { fetchAsync } from '@0x/utils'; +import promiseLimit = require('p-limit'); +import { stringify } from 'querystring'; +import * as R from 'ramda'; + +import { TradingPair } from '../../utils/get_ohlcv_trading_pairs'; + +export interface CryptoCompareOHLCVResponse { + Data: Map; + Response: string; + Message: string; +} + +export interface CryptoCompareOHLCVRecord { + time: number; // in seconds, not milliseconds + close: number; + high: number; + low: number; + open: number; + volumefrom: number; + volumeto: number; +} + +export interface CryptoCompareOHLCVParams { + fsym: string; + tsym: string; + e?: string; + aggregate?: string; + aggregatePredictableTimePeriods?: boolean; + limit?: number; + toTs?: number; +} + +const ONE_WEEK = 7 * 24 * 60 * 60 * 1000; // tslint:disable-line:custom-no-magic-numbers +const ONE_HOUR = 60 * 60 * 1000; // tslint:disable-line:custom-no-magic-numbers +const ONE_SECOND = 1000; +const HTTP_OK_STATUS = 200; + +export class CryptoCompareOHLCVSource { + public readonly interval = ONE_WEEK; // the hourly API returns data for one week at a time + public readonly default_exchange = 'CCCAGG'; + public readonly intervalBetweenRecords = ONE_HOUR; + private readonly _url: string = 'https://min-api.cryptocompare.com/data/histohour?'; + + // rate-limit for all API calls through this class instance + private readonly _promiseLimit: (fetchFn: () => Promise) => Promise; + constructor(maxConcurrentRequests: number = 50) { + this._promiseLimit = promiseLimit(maxConcurrentRequests); + } + + // gets OHLCV records starting from pair.latest + public async getHourlyOHLCVAsync(pair: TradingPair): Promise { + const params = { + e: this.default_exchange, + fsym: pair.fromSymbol, + tsym: pair.toSymbol, + toTs: Math.floor((pair.latestSavedTime + this.interval) / ONE_SECOND), // CryptoCompare uses timestamp in seconds. not ms + }; + const url = this._url + stringify(params); + + // go through the instance-wide rate-limit + const fetchPromise: Promise = this._promiseLimit(() => { + // tslint:disable-next-line:no-console + console.log(`Scraping Crypto Compare at ${url}`); + return fetchAsync(url); + }); + + const response = await Promise.resolve(fetchPromise); + if (response.status !== HTTP_OK_STATUS) { + // tslint:disable-next-line:no-console + console.log(`Error scraping ${url}`); + return []; + } + const json: CryptoCompareOHLCVResponse = await response.json(); + if (json.Response === 'Error' || Object.keys(json.Data).length === 0) { + // tslint:disable-next-line:no-console + console.log(`Error scraping ${url}: ${json.Message}`); + return []; + } + return Object.values(json.Data).filter(rec => rec.time * ONE_SECOND >= pair.latestSavedTime); + } + public generateBackfillIntervals(pair: TradingPair): TradingPair[] { + const now = new Date().getTime(); + const f = (p: TradingPair): false | [TradingPair, TradingPair] => { + if (p.latestSavedTime > now) { + return false; + } else { + return [p, R.merge(p, { latestSavedTime: p.latestSavedTime + this.interval })]; + } + }; + return R.unfold(f, pair); + } +} diff --git a/packages/pipeline/src/entities/ohlcv_external.ts b/packages/pipeline/src/entities/ohlcv_external.ts index 95cd4f2f5..4f55dd930 100644 --- a/packages/pipeline/src/entities/ohlcv_external.ts +++ b/packages/pipeline/src/entities/ohlcv_external.ts @@ -1,20 +1,30 @@ import { Column, Entity, PrimaryColumn } from 'typeorm'; +import { numberToBigIntTransformer } from '../utils'; + @Entity({ name: 'ohlcv_external', schema: 'raw' }) export class OHLCVExternal { @PrimaryColumn() public exchange!: string; - @PrimaryColumn() public fromSymbol!: string; - @PrimaryColumn() public toSymbol!: string; - @PrimaryColumn() public startTime!: number; - @PrimaryColumn() public endTime!: number; + + @PrimaryColumn({ name: 'from_symbol', type: 'varchar' }) + public fromSymbol!: string; + @PrimaryColumn({ name: 'to_symbol', type: 'varchar' }) + public toSymbol!: string; + @PrimaryColumn({ name: 'start_time', transformer: numberToBigIntTransformer }) + public startTime!: number; + @PrimaryColumn({ name: 'end_time', transformer: numberToBigIntTransformer }) + public endTime!: number; @Column() public open!: number; @Column() public close!: number; @Column() public low!: number; @Column() public high!: number; - @Column() public volumeFrom!: number; - @Column() public volumeTo!: number; + @Column({ name: 'volume_from' }) + public volumeFrom!: number; + @Column({ name: 'volume_to' }) + public volumeTo!: number; @PrimaryColumn() public source!: string; - @PrimaryColumn() public observedTimestamp!: number; + @PrimaryColumn({ name: 'observed_timestamp', transformer: numberToBigIntTransformer }) + public observedTimestamp!: number; } diff --git a/packages/pipeline/src/ormconfig.ts b/packages/pipeline/src/ormconfig.ts index c135c399b..9f7815b4e 100644 --- a/packages/pipeline/src/ormconfig.ts +++ b/packages/pipeline/src/ormconfig.ts @@ -6,6 +6,7 @@ import { ExchangeCancelEvent, ExchangeCancelUpToEvent, ExchangeFillEvent, + OHLCVExternal, Relayer, SraOrder, SraOrdersObservedTimeStamp, @@ -20,6 +21,7 @@ const entities = [ ExchangeCancelEvent, ExchangeCancelUpToEvent, ExchangeFillEvent, + OHLCVExternal, Relayer, SraOrder, SraOrdersObservedTimeStamp, diff --git a/packages/pipeline/src/parsers/ohlcv_external/crypto_compare.ts b/packages/pipeline/src/parsers/ohlcv_external/crypto_compare.ts new file mode 100644 index 000000000..3efb90384 --- /dev/null +++ b/packages/pipeline/src/parsers/ohlcv_external/crypto_compare.ts @@ -0,0 +1,38 @@ +import { CryptoCompareOHLCVRecord } from '../../data_sources/ohlcv_external/crypto_compare'; +import { OHLCVExternal } from '../../entities'; + +const ONE_SECOND = 1000; // Crypto Compare uses timestamps in seconds instead of milliseconds + +export interface OHLCVMetadata { + exchange: string; + fromSymbol: string; + toSymbol: string; + source: string; + observedTimestamp: number; + interval: number; +} +/** + * Parses OHLCV records from Crypto Compare into an array of OHLCVExternal entities + * @param rawRecords an array of OHLCV records from Crypto Compare (not the full response) + */ +export function parseRecords(rawRecords: CryptoCompareOHLCVRecord[], metadata: OHLCVMetadata): OHLCVExternal[] { + return rawRecords.map(rec => { + const ohlcvRecord = new OHLCVExternal(); + ohlcvRecord.exchange = metadata.exchange; + ohlcvRecord.fromSymbol = metadata.fromSymbol; + ohlcvRecord.toSymbol = metadata.toSymbol; + ohlcvRecord.startTime = rec.time * ONE_SECOND - metadata.interval; + ohlcvRecord.endTime = rec.time * ONE_SECOND; + + ohlcvRecord.open = rec.open; + ohlcvRecord.close = rec.close; + ohlcvRecord.low = rec.low; + ohlcvRecord.high = rec.high; + ohlcvRecord.volumeFrom = rec.volumefrom; + ohlcvRecord.volumeTo = rec.volumeto; + + ohlcvRecord.source = metadata.source; + ohlcvRecord.observedTimestamp = metadata.observedTimestamp; + return ohlcvRecord; + }); +} diff --git a/packages/pipeline/src/parsers/ohlcv_external/index.ts b/packages/pipeline/src/parsers/ohlcv_external/index.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/pipeline/src/scripts/pull_ohlcv_cryptocompare.ts b/packages/pipeline/src/scripts/pull_ohlcv_cryptocompare.ts new file mode 100644 index 000000000..6979cd10e --- /dev/null +++ b/packages/pipeline/src/scripts/pull_ohlcv_cryptocompare.ts @@ -0,0 +1,101 @@ +// tslint:disable:no-console +import { Connection, ConnectionOptions, createConnection, Repository } from 'typeorm'; + +import { CryptoCompareOHLCVSource } from '../data_sources/ohlcv_external/crypto_compare'; +import { OHLCVExternal } from '../entities'; +import * as ormConfig from '../ormconfig'; +import { OHLCVMetadata, parseRecords } from '../parsers/ohlcv_external/crypto_compare'; +import { handleError } from '../utils'; +import { fetchOHLCVTradingPairsAsync, TradingPair } from '../utils/get_ohlcv_trading_pairs'; + +const SOURCE_NAME = 'CryptoCompare'; +const TWO_HOURS_AGO = new Date().getTime() - 2 * 60 * 60 * 1000; // tslint:disable-line:custom-no-magic-numbers +const ONE_HOUR_AGO = new Date().getTime() - 60 * 60 * 1000; // tslint:disable-line:custom-no-magic-numbers +const ONE_SECOND = 1000; + +const MAX_CONCURRENT_REQUESTS = parseInt(process.env.CRYPTOCOMPARE_MAX_CONCURRENT_REQUESTS || '14', 10); // tslint:disable-line:custom-no-magic-numbers +const EARLIEST_BACKFILL_DATE = process.env.OHLCV_EARLIEST_BACKFILL_DATE || '2010-09-01'; // the time when BTC/USD info starts appearing on Crypto Compare +const EARLIEST_BACKFILL_TIME = new Date(EARLIEST_BACKFILL_DATE).getTime(); + +let connection: Connection; + +(async () => { + connection = await createConnection(ormConfig as ConnectionOptions); + const repository = connection.getRepository(OHLCVExternal); + const source = new CryptoCompareOHLCVSource(MAX_CONCURRENT_REQUESTS); + + const jobTime = new Date().getTime(); + const tradingPairs = await fetchOHLCVTradingPairsAsync(connection, SOURCE_NAME, EARLIEST_BACKFILL_TIME); + console.log(`Starting ${tradingPairs.length} job(s) to scrape Crypto Compare for OHLCV records...`); + + const fetchAndSavePromises = tradingPairs.map(async pair => { + const pairs = source.generateBackfillIntervals(pair); + return fetchAndSaveAsync(source, repository, jobTime, pairs); + }); + await Promise.all(fetchAndSavePromises); + console.log(`Finished scraping OHLCV records from Crypto Compare, exiting...`); + process.exit(0); +})().catch(handleError); + +async function fetchAndSaveAsync( + source: CryptoCompareOHLCVSource, + repository: Repository, + jobTime: number, + pairs: TradingPair[], +): Promise { + const sortAscTimestamp = (a: TradingPair, b: TradingPair): number => { + if (a.latestSavedTime < b.latestSavedTime) { + return -1; + } else if (a.latestSavedTime > b.latestSavedTime) { + return 1; + } else { + return 0; + } + }; + pairs.sort(sortAscTimestamp); + + let i = 0; + while (i < pairs.length) { + const pair = pairs[i]; + if (pair.latestSavedTime > TWO_HOURS_AGO) { + break; + } + const rawRecords = await source.getHourlyOHLCVAsync(pair); + const records = rawRecords.filter(rec => { + return rec.time * ONE_SECOND < ONE_HOUR_AGO && rec.time * ONE_SECOND > pair.latestSavedTime; + }); // Crypto Compare can take ~30mins to finalise records + if (records.length === 0) { + console.log(`No more records, stopping task for ${JSON.stringify(pair)}`); + break; + } + const metadata: OHLCVMetadata = { + exchange: source.default_exchange, + fromSymbol: pair.fromSymbol, + toSymbol: pair.toSymbol, + source: SOURCE_NAME, + observedTimestamp: jobTime, + interval: source.intervalBetweenRecords, + }; + const parsedRecords = parseRecords(records, metadata); + try { + await saveRecordsAsync(repository, parsedRecords); + i++; + } catch (err) { + console.log(`Error saving OHLCVRecords, stopping task for ${JSON.stringify(pair)} [${err}]`); + break; + } + } + return Promise.resolve(); +} + +async function saveRecordsAsync(repository: Repository, records: OHLCVExternal[]): Promise { + const metadata = [ + records[0].fromSymbol, + records[0].toSymbol, + new Date(records[0].startTime), + new Date(records[records.length - 1].endTime), + ]; + + console.log(`Saving ${records.length} records to ${repository.metadata.name}... ${JSON.stringify(metadata)}`); + await repository.save(records); +} diff --git a/packages/pipeline/src/utils/get_ohlcv_trading_pairs.ts b/packages/pipeline/src/utils/get_ohlcv_trading_pairs.ts new file mode 100644 index 000000000..9d3ef2fba --- /dev/null +++ b/packages/pipeline/src/utils/get_ohlcv_trading_pairs.ts @@ -0,0 +1,92 @@ +import { fetchAsync } from '@0x/utils'; +import * as R from 'ramda'; +import { Connection } from 'typeorm'; + +export interface TradingPair { + fromSymbol: string; + toSymbol: string; + latestSavedTime: number; +} + +const COINLIST_API = 'https://min-api.cryptocompare.com/data/all/coinlist?BuiltOn=7605'; + +interface CryptoCompareCoinListResp { + Data: Map; +} + +interface CryptoCompareCoin { + Symbol: string; + BuiltOn: string; + SmartContractAddress: string; +} + +const TO_CURRENCIES = ['USD', 'EUR', 'ETH', 'USDT']; +const ETHEREUM_IDENTIFIER = '7605'; +const HTTP_OK_STATUS = 200; +/** + * Get trading pairs with latest scraped time for OHLCV records + * @param conn a typeorm Connection to postgres + */ +export async function fetchOHLCVTradingPairsAsync( + conn: Connection, + source: string, + earliestBackfillTime: number, +): Promise { + // fetch existing ohlcv records + const latestTradingPairs: Array<{ + from_symbol: string; + to_symbol: string; + latest: string; + }> = await conn.query(`SELECT + MAX(end_time) as latest, + from_symbol, + to_symbol + FROM raw.ohlcv_external + GROUP BY from_symbol, to_symbol;`); + + const latestTradingPairsIndex: { [fromSym: string]: { [toSym: string]: number } } = {}; + latestTradingPairs.forEach(pair => { + const latestIndex: { [toSym: string]: number } = latestTradingPairsIndex[pair.from_symbol] || {}; + latestIndex[pair.to_symbol] = parseInt(pair.latest, 10); // tslint:disable-line:custom-no-magic-numbers + latestTradingPairsIndex[pair.from_symbol] = latestIndex; + }); + + // get token symbols used by Crypto Compare + const allCoinsResp = await fetchAsync(COINLIST_API); + if (allCoinsResp.status !== HTTP_OK_STATUS) { + return []; + } + const allCoins: CryptoCompareCoinListResp = await allCoinsResp.json(); + const erc20CoinsIndex: Map = new Map(); + Object.entries(allCoins.Data).forEach(pair => { + const [symbol, coinData] = pair; + if (coinData.BuiltOn === ETHEREUM_IDENTIFIER && coinData.SmartContractAddress !== 'N/A') { + erc20CoinsIndex.set(coinData.SmartContractAddress.toLowerCase(), symbol); + } + }); + + // fetch all tokens that are traded on 0x + const rawTokenAddresses: Array<{ tokenaddress: string }> = await conn.query( + `SELECT DISTINCT(maker_token_address) as tokenaddress FROM raw.exchange_fill_events UNION + SELECT DISTINCT(taker_token_address) as tokenaddress FROM raw.exchange_fill_events`, + ); + const tokenAddresses = R.pluck('tokenaddress', rawTokenAddresses); + + // join token addresses with CC symbols + const allTokenSymbols: string[] = tokenAddresses + .map(tokenAddress => erc20CoinsIndex.get(tokenAddress.toLowerCase()) || '') + .filter(x => x); + + // generate list of all tokens with time of latest existing record OR default earliest time + const allTradingPairCombinations: TradingPair[] = R.chain(sym => { + return TO_CURRENCIES.map(fiat => { + return { + fromSymbol: sym, + toSymbol: fiat, + latestSavedTime: R.path([sym, fiat], latestTradingPairsIndex) || earliestBackfillTime, + }; + }); + }, allTokenSymbols); + + return allTradingPairCombinations; +} diff --git a/packages/pipeline/src/utils/index.ts b/packages/pipeline/src/utils/index.ts index 918cfc695..718ea6133 100644 --- a/packages/pipeline/src/utils/index.ts +++ b/packages/pipeline/src/utils/index.ts @@ -35,12 +35,3 @@ export function handleError(e: any): void { } process.exit(1); } - -/** - * Returns the unix timestamp of the current hour - */ -export function getHourInUnixTime(): number { - const currentTime: number = Date.now(); - // tslint:disable-next-line - return currentTime - currentTime % (3600 * 1000); -} diff --git a/packages/pipeline/test/data_sources/ohlcv_external/crypto_compare_test.ts b/packages/pipeline/test/data_sources/ohlcv_external/crypto_compare_test.ts new file mode 100644 index 000000000..cb374bbb1 --- /dev/null +++ b/packages/pipeline/test/data_sources/ohlcv_external/crypto_compare_test.ts @@ -0,0 +1,47 @@ +import * as chai from 'chai'; +import 'mocha'; +import * as R from 'ramda'; + +import { CryptoCompareOHLCVSource } from '../../../src/data_sources/ohlcv_external/crypto_compare'; +import { TradingPair } from '../../../src/utils/get_ohlcv_trading_pairs'; +import { chaiSetup } from '../../utils/chai_setup'; + +chaiSetup.configure(); +const expect = chai.expect; + +// tslint:disable:custom-no-magic-numbers +describe('ohlcv_external data source (Crypto Compare)', () => { + describe('generateBackfillIntervals', () => { + it('generates pairs with intervals to query', () => { + const source = new CryptoCompareOHLCVSource(); + const pair: TradingPair = { + fromSymbol: 'ETH', + toSymbol: 'ZRX', + latestSavedTime: new Date().getTime() - source.interval * 2, + }; + + const expected = [ + pair, + R.merge(pair, { latestSavedTime: pair.latestSavedTime + source.interval }), + R.merge(pair, { latestSavedTime: pair.latestSavedTime + source.interval * 2 }), + ]; + + const actual = source.generateBackfillIntervals(pair); + expect(actual).deep.equal(expected); + }); + + it('returns single pair if no backfill is needed', () => { + const source = new CryptoCompareOHLCVSource(); + const pair: TradingPair = { + fromSymbol: 'ETH', + toSymbol: 'ZRX', + latestSavedTime: new Date().getTime() - source.interval + 5000, + }; + + const expected = [pair]; + + const actual = source.generateBackfillIntervals(pair); + expect(actual).deep.equal(expected); + }); + }); +}); diff --git a/packages/pipeline/test/entities/ohlcv_external_test.ts b/packages/pipeline/test/entities/ohlcv_external_test.ts new file mode 100644 index 000000000..8b995db50 --- /dev/null +++ b/packages/pipeline/test/entities/ohlcv_external_test.ts @@ -0,0 +1,35 @@ +import 'mocha'; +import 'reflect-metadata'; + +import { OHLCVExternal } from '../../src/entities'; +import { createDbConnectionOnceAsync } from '../db_setup'; +import { chaiSetup } from '../utils/chai_setup'; + +import { testSaveAndFindEntityAsync } from './util'; + +chaiSetup.configure(); + +const ohlcvExternal: OHLCVExternal = { + exchange: 'CCCAGG', + fromSymbol: 'ETH', + toSymbol: 'ZRX', + startTime: 1543352400000, + endTime: 1543356000000, + open: 307.41, + close: 310.08, + low: 304.6, + high: 310.27, + volumeFrom: 904.6, + volumeTo: 278238.5, + source: 'Crypto Compare', + observedTimestamp: 1543442338074, +}; + +// tslint:disable:custom-no-magic-numbers +describe('OHLCVExternal entity', () => { + it('save/find', async () => { + const connection = await createDbConnectionOnceAsync(); + const repository = connection.getRepository(OHLCVExternal); + await testSaveAndFindEntityAsync(repository, ohlcvExternal); + }); +}); diff --git a/packages/pipeline/test/parsers/ohlcv_external/crypto_compare_test.ts b/packages/pipeline/test/parsers/ohlcv_external/crypto_compare_test.ts new file mode 100644 index 000000000..118cafc5e --- /dev/null +++ b/packages/pipeline/test/parsers/ohlcv_external/crypto_compare_test.ts @@ -0,0 +1,62 @@ +import * as chai from 'chai'; +import 'mocha'; +import * as R from 'ramda'; + +import { CryptoCompareOHLCVRecord } from '../../../src/data_sources/ohlcv_external/crypto_compare'; +import { OHLCVExternal } from '../../../src/entities'; +import { OHLCVMetadata, parseRecords } from '../../../src/parsers/ohlcv_external/crypto_compare'; +import { chaiSetup } from '../../utils/chai_setup'; + +chaiSetup.configure(); +const expect = chai.expect; + +// tslint:disable:custom-no-magic-numbers +describe('ohlcv_external parser (Crypto Compare)', () => { + describe('parseRecords', () => { + const record: CryptoCompareOHLCVRecord = { + time: 200, + close: 100, + high: 101, + low: 99, + open: 98, + volumefrom: 1234, + volumeto: 4321, + }; + + const metadata: OHLCVMetadata = { + fromSymbol: 'ETH', + toSymbol: 'ZRX', + exchange: 'CCCAGG', + source: 'CryptoCompare', + observedTimestamp: new Date().getTime(), + interval: 100000, + }; + + const entity = new OHLCVExternal(); + entity.exchange = metadata.exchange; + entity.fromSymbol = metadata.fromSymbol; + entity.toSymbol = metadata.toSymbol; + entity.startTime = 100000; + entity.endTime = 200000; + entity.open = record.open; + entity.close = record.close; + entity.low = record.low; + entity.high = record.high; + entity.volumeFrom = record.volumefrom; + entity.volumeTo = record.volumeto; + entity.source = metadata.source; + entity.observedTimestamp = metadata.observedTimestamp; + + it('converts Crypto Compare OHLCV records to OHLCVExternal entity', () => { + const input = [record, R.merge(record, { time: 300 }), R.merge(record, { time: 400 })]; + const expected = [ + entity, + R.merge(entity, { startTime: 200000, endTime: 300000 }), + R.merge(entity, { startTime: 300000, endTime: 400000 }), + ]; + + const actual = parseRecords(input, metadata); + expect(actual).deep.equal(expected); + }); + }); +}); diff --git a/yarn.lock b/yarn.lock index 400c48a51..7a5091a5d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1515,6 +1515,10 @@ dependencies: "@types/node" "*" +"@types/p-limit@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/p-limit/-/p-limit-2.0.0.tgz#c076b7daa9163108a35899ea6a9d927526943ea2" + "@types/prop-types@*": version "15.5.5" resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.5.5.tgz#17038dd322c2325f5da650a94d5f9974943625e3" @@ -11458,7 +11462,7 @@ p-limit@^1.1.0: p-limit@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz#e624ed54ee8c460a778b3c9f3670496ff8a57aec" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.0.0.tgz#e624ed54ee8c460a778b3c9f3670496ff8a57aec" dependencies: p-try "^2.0.0" @@ -12235,6 +12239,10 @@ prettier@^1.14.2, prettier@^1.14.3: version "1.15.2" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.15.2.tgz#d31abe22afa4351efa14c7f8b94b58bb7452205e" +prettier@^1.15.3: + version "1.15.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.15.3.tgz#1feaac5bdd181237b54dbe65d874e02a1472786a" + pretty-bytes@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-1.0.4.tgz#0a22e8210609ad35542f8c8d5d2159aff0751c84" -- cgit v1.2.3 From ce013489ecf86ac7ad51e2f2cd435b18d6054993 Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 4 Dec 2018 14:30:31 -0800 Subject: feat: add copy code feature --- packages/website/ts/components/ui/container.tsx | 1 + packages/website/ts/pages/instant/code_demo.tsx | 20 +++++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/website/ts/components/ui/container.tsx b/packages/website/ts/components/ui/container.tsx index c2ae9ad9c..ae00851e5 100644 --- a/packages/website/ts/components/ui/container.tsx +++ b/packages/website/ts/components/ui/container.tsx @@ -44,6 +44,7 @@ export interface ContainerProps { right?: string; bottom?: string; zIndex?: number; + float?: 'right' | 'left'; Tag?: ContainerTag; cursor?: string; id?: string; diff --git a/packages/website/ts/pages/instant/code_demo.tsx b/packages/website/ts/pages/instant/code_demo.tsx index 4fe1928f5..9a04fceed 100644 --- a/packages/website/ts/pages/instant/code_demo.tsx +++ b/packages/website/ts/pages/instant/code_demo.tsx @@ -1,5 +1,10 @@ import * as React from 'react'; +import * as CopyToClipboard from 'react-copy-to-clipboard'; import SyntaxHighlighter from 'react-syntax-highlighter'; + +import { Button } from 'ts/components/ui/button'; +import { Container } from 'ts/components/ui/container'; +import { Text } from 'ts/components/ui/text'; import { colors } from 'ts/style/colors'; import { styled } from 'ts/style/theme'; @@ -142,7 +147,16 @@ export interface CodeDemoProps { } export const CodeDemo: React.StatelessComponent = props => ( - - {props.children} - + + + + + + + + {props.children} + + ); -- cgit v1.2.3 From 8721d4ed7a7a38f77847b45619f15315044c375c Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Tue, 4 Dec 2018 15:23:15 -0800 Subject: Fix linter --- packages/pipeline/src/scripts/pull_ddex_orderbook_snapshots.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pipeline/src/scripts/pull_ddex_orderbook_snapshots.ts b/packages/pipeline/src/scripts/pull_ddex_orderbook_snapshots.ts index b02468e9b..7868e9c5a 100644 --- a/packages/pipeline/src/scripts/pull_ddex_orderbook_snapshots.ts +++ b/packages/pipeline/src/scripts/pull_ddex_orderbook_snapshots.ts @@ -27,7 +27,7 @@ let connection: Connection; await Promise.all( marketsChunk.map(async (market: DdexMarket) => getAndSaveMarketOrderbook(ddexSource, market)), ); - await new Promise(resolve => setTimeout(resolve, MILLISEC_MARKET_ORDERBOOK_REQUEST_DELAY)); + await new Promise(resolve => setTimeout(resolve, MILLISEC_MARKET_ORDERBOOK_REQUEST_DELAY)); } process.exit(0); })().catch(handleError); -- cgit v1.2.3 From 5c29b918df4ac8b0f7914e8da10fa1ae530ff4e8 Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 4 Dec 2018 15:45:23 -0800 Subject: chore: run linter --- packages/website/ts/pages/instant/code_demo.tsx | 1 - packages/website/ts/pages/instant/fee_percentage_slider.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/website/ts/pages/instant/code_demo.tsx b/packages/website/ts/pages/instant/code_demo.tsx index 9a04fceed..c5e565d61 100644 --- a/packages/website/ts/pages/instant/code_demo.tsx +++ b/packages/website/ts/pages/instant/code_demo.tsx @@ -4,7 +4,6 @@ import SyntaxHighlighter from 'react-syntax-highlighter'; import { Button } from 'ts/components/ui/button'; import { Container } from 'ts/components/ui/container'; -import { Text } from 'ts/components/ui/text'; import { colors } from 'ts/style/colors'; import { styled } from 'ts/style/theme'; diff --git a/packages/website/ts/pages/instant/fee_percentage_slider.tsx b/packages/website/ts/pages/instant/fee_percentage_slider.tsx index 6256c6cb3..4c92883cb 100644 --- a/packages/website/ts/pages/instant/fee_percentage_slider.tsx +++ b/packages/website/ts/pages/instant/fee_percentage_slider.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import { Text } from 'ts/components/ui/text'; import { colors } from 'ts/style/colors'; -import { injectGlobal, styled } from 'ts/style/theme'; +import { injectGlobal } from 'ts/style/theme'; const SliderWithTooltip = (Slider as any).createSliderWithTooltip(Slider); // tslint:disable-next-line:no-unused-expression -- cgit v1.2.3 From ebdc539da7f5714ddff74c5ee3f34667d7115d6e Mon Sep 17 00:00:00 2001 From: Fabio B Date: Tue, 4 Dec 2018 19:31:19 -0800 Subject: Fix typo in packages/pipeline/src/parsers/bloxy/index.ts Co-Authored-By: albrow --- packages/pipeline/src/parsers/bloxy/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pipeline/src/parsers/bloxy/index.ts b/packages/pipeline/src/parsers/bloxy/index.ts index af07c7507..caa55d289 100644 --- a/packages/pipeline/src/parsers/bloxy/index.ts +++ b/packages/pipeline/src/parsers/bloxy/index.ts @@ -49,5 +49,5 @@ export function _parseBloxyTrade(rawTrade: BloxyTrade): DexTrade { return dexTrade; } -// Works with any form of escaoed null character (e.g., '\0' and '\u0000'). +// Works with any form of escaped null character (e.g., '\0' and '\u0000'). const filterNullCharacters = R.replace(/\0/g, ''); -- cgit v1.2.3 From 549f5e4655f246062dd6451065ec01eb789dbd8f Mon Sep 17 00:00:00 2001 From: Fabio B Date: Tue, 4 Dec 2018 19:56:23 -0800 Subject: Use a string template in packages/pipeline/src/scripts/pull_missing_events.ts Co-Authored-By: albrow --- packages/pipeline/src/scripts/pull_missing_events.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pipeline/src/scripts/pull_missing_events.ts b/packages/pipeline/src/scripts/pull_missing_events.ts index 68cabe3de..c7e07c540 100644 --- a/packages/pipeline/src/scripts/pull_missing_events.ts +++ b/packages/pipeline/src/scripts/pull_missing_events.ts @@ -74,7 +74,7 @@ async function getStartBlockAsync(repository: Repositor } const tableName = repository.metadata.tableName; if (!tableNameRegex.test(tableName)) { - throw new Error('Unexpected special character in table name: ' + tableName); + throw new Error(`Unexpected special character in table name: ${tableName}`); } const queryResult = await connection.query( `SELECT block_number FROM raw.${tableName} ORDER BY block_number DESC LIMIT 1`, -- cgit v1.2.3 From 00f86ca0f7871639d2b0be496f6f8c5e0d8d7ffe Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Tue, 4 Dec 2018 20:04:08 -0800 Subject: Address PR feedback --- packages/pipeline/README.md | 21 +++++++------- ...9179-ConvertTokenMetadataDecimalsToBigNumber.ts | 17 +++++++++++ packages/pipeline/package.json | 7 +---- .../contract-wrappers/exchange_events.ts | 3 +- .../pipeline/src/entities/exchange_cancel_event.ts | 2 -- .../src/entities/exchange_cancel_up_to_event.ts | 2 -- .../pipeline/src/entities/exchange_fill_event.ts | 2 -- packages/pipeline/src/entities/relayer.ts | 3 -- packages/pipeline/src/entities/token_metadata.ts | 9 +++--- packages/pipeline/src/parsers/events/index.ts | 13 ++++----- .../pipeline/src/parsers/relayer_registry/index.ts | 13 +++++---- .../pipeline/src/parsers/token_metadata/index.ts | 6 ++-- packages/pipeline/src/parsers/web3/index.ts | 1 - .../pipeline/src/scripts/pull_missing_blocks.ts | 7 ++--- .../pipeline/src/scripts/pull_missing_events.ts | 5 ++-- .../src/scripts/pull_radar_relay_orders.ts | 33 +++++++++++++--------- .../pipeline/src/scripts/pull_trusted_tokens.ts | 14 ++++----- packages/pipeline/src/utils/constants.ts | 3 ++ packages/pipeline/src/utils/index.ts | 1 + .../pipeline/src/utils/transformers/big_number.ts | 8 +++--- .../pipeline/test/entities/token_metadata_test.ts | 7 +++-- 21 files changed, 96 insertions(+), 81 deletions(-) create mode 100644 packages/pipeline/migrations/1543980079179-ConvertTokenMetadataDecimalsToBigNumber.ts create mode 100644 packages/pipeline/src/utils/constants.ts diff --git a/packages/pipeline/README.md b/packages/pipeline/README.md index fb563b14c..0f4abd935 100644 --- a/packages/pipeline/README.md +++ b/packages/pipeline/README.md @@ -42,7 +42,8 @@ Revert the most recent migration (CAUTION: may result in data loss!): `yarn migr There are several test scripts in **package.json**. You can run all the tests with `yarn test:all` or run certain tests seprately by following the -instructions below. Some tests may not work out of the box on certain platforms. +instructions below. Some tests may not work out of the box on certain platforms +or operating systems (see the "Database tests" section below). ### Unit tests @@ -71,8 +72,8 @@ Postgres is via Docker. Depending on your platform, you may need to prepend docker run --rm -d -p 5432:5432 --name pipeline_postgres postgres:11-alpine ``` -This will start a Postgres server with the default username and database name. -You should set the environment variable as follows: +This will start a Postgres server with the default username and database name +(`postgres` and `postgres`). You should set the environment variable as follows: ``` export ZEROEX_DATA_PIPELINE_DB_URL=postgresql://postgres@localhost/postgres @@ -149,17 +150,17 @@ set the`ZEROEX_DATA_PIPELINE_DB_URL` environment variable to a valid #### Additional guidelines and tips: -* Table names should be plural and separated by underscores (e.g., +- Table names should be plural and separated by underscores (e.g., `exchange_fill_events`). -* Any table which contains data which comes directly from a third-party source +- Any table which contains data which comes directly from a third-party source should be namespaced in the `raw` PostgreSQL schema. -* Column names in the database should be separated by underscores (e.g., +- Column names in the database should be separated by underscores (e.g., `maker_asset_type`). -* Field names in entity classes (like any other fields in TypeScript) should +- Field names in entity classes (like any other fields in TypeScript) should be camel-cased (e.g., `makerAssetType`). -* All timestamps should be stored as milliseconds since the Unix Epoch. -* Use the `BigNumber` type for TypeScript code which deals with 256-bit +- All timestamps should be stored as milliseconds since the Unix Epoch. +- Use the `BigNumber` type for TypeScript code which deals with 256-bit numbers from smart contracts or for any case where we are dealing with large floating point numbers. -* [TypeORM documentation](http://typeorm.io/#/) is pretty robust and can be a +- [TypeORM documentation](http://typeorm.io/#/) is pretty robust and can be a helpful resource. diff --git a/packages/pipeline/migrations/1543980079179-ConvertTokenMetadataDecimalsToBigNumber.ts b/packages/pipeline/migrations/1543980079179-ConvertTokenMetadataDecimalsToBigNumber.ts new file mode 100644 index 000000000..351bc7eb8 --- /dev/null +++ b/packages/pipeline/migrations/1543980079179-ConvertTokenMetadataDecimalsToBigNumber.ts @@ -0,0 +1,17 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ConvertTokenMetadataDecimalsToBigNumber1543980079179 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE raw.token_metadata + ALTER COLUMN decimals TYPE numeric USING decimals::numeric;`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE raw.token_metadata + ALTER COLUMN decimals TYPE numeric USING decimals::integer;`, + ); + } +} diff --git a/packages/pipeline/package.json b/packages/pipeline/package.json index 6af81c048..0539618d4 100644 --- a/packages/pipeline/package.json +++ b/packages/pipeline/package.json @@ -1,6 +1,6 @@ { "name": "@0x/pipeline", - "version": "0.0.1", + "version": "1.0.0", "private": true, "description": "Data pipeline for offline analysis", "scripts": { @@ -21,11 +21,6 @@ "migrate:revert": "yarn typeorm migration:revert --config ./lib/src/ormconfig", "migrate:create": "yarn typeorm migration:create --config ./lib/src/ormconfig --dir migrations" }, - "config": { - "postpublish": { - "assets": [] - } - }, "repository": { "type": "git", "url": "https://github.com/0xProject/0x-monorepo" diff --git a/packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts b/packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts index 5721c0ea3..1717eb8b3 100644 --- a/packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts +++ b/packages/pipeline/src/data_sources/contract-wrappers/exchange_events.ts @@ -11,9 +11,10 @@ import { Web3ProviderEngine } from '@0x/subproviders'; import { Web3Wrapper } from '@0x/web3-wrapper'; import { LogWithDecodedArgs } from 'ethereum-types'; +import { EXCHANGE_START_BLOCK } from '../../utils'; + const BLOCK_FINALITY_THRESHOLD = 10; // When to consider blocks as final. Used to compute default toBlock. const NUM_BLOCKS_PER_QUERY = 20000; // Number of blocks to query for events at a time. -const EXCHANGE_START_BLOCK = 6271590; // Block number when the Exchange contract was deployed to mainnet. export class ExchangeEventsSource { private readonly _exchangeWrapper: ExchangeWrapper; diff --git a/packages/pipeline/src/entities/exchange_cancel_event.ts b/packages/pipeline/src/entities/exchange_cancel_event.ts index 2fcc17df6..38f99c903 100644 --- a/packages/pipeline/src/entities/exchange_cancel_event.ts +++ b/packages/pipeline/src/entities/exchange_cancel_event.ts @@ -48,6 +48,4 @@ export class ExchangeCancelEvent { public takerTokenAddress!: string; @Column({ nullable: true, type: String, name: 'taker_token_id' }) public takerTokenId!: string | null; - - // TODO(albrow): Include topics? } diff --git a/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts b/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts index 60ead324f..27580305e 100644 --- a/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts +++ b/packages/pipeline/src/entities/exchange_cancel_up_to_event.ts @@ -12,7 +12,6 @@ export class ExchangeCancelUpToEvent { @PrimaryColumn({ name: 'block_number', transformer: numberToBigIntTransformer }) public blockNumber!: number; - // TODO(albrow): Include transaction hash @Column({ name: 'raw_data' }) public rawData!: string; @@ -24,5 +23,4 @@ export class ExchangeCancelUpToEvent { public senderAddress!: string; @Column({ name: 'order_epoch', type: 'numeric', transformer: bigNumberTransformer }) public orderEpoch!: BigNumber; - // TODO(albrow): Include topics? } diff --git a/packages/pipeline/src/entities/exchange_fill_event.ts b/packages/pipeline/src/entities/exchange_fill_event.ts index bbf0abf58..9b7727615 100644 --- a/packages/pipeline/src/entities/exchange_fill_event.ts +++ b/packages/pipeline/src/entities/exchange_fill_event.ts @@ -57,6 +57,4 @@ export class ExchangeFillEvent { public takerTokenAddress!: string; @Column({ nullable: true, type: String, name: 'taker_token_id' }) public takerTokenId!: string | null; - - // TODO(albrow): Include topics? } diff --git a/packages/pipeline/src/entities/relayer.ts b/packages/pipeline/src/entities/relayer.ts index b3a856fd8..5af8578b4 100644 --- a/packages/pipeline/src/entities/relayer.ts +++ b/packages/pipeline/src/entities/relayer.ts @@ -14,9 +14,6 @@ export class Relayer { @Column({ name: 'app_url', type: 'varchar', nullable: true }) public appUrl!: string | null; - // TODO(albrow): Add exchange contract or protocol version? - // TODO(albrow): Add network ids for addresses? - @Column({ name: 'fee_recipient_addresses', type: 'varchar', array: true }) public feeRecipientAddresses!: string[]; @Column({ name: 'taker_addresses', type: 'varchar', array: true }) diff --git a/packages/pipeline/src/entities/token_metadata.ts b/packages/pipeline/src/entities/token_metadata.ts index ca1e57937..911b53972 100644 --- a/packages/pipeline/src/entities/token_metadata.ts +++ b/packages/pipeline/src/entities/token_metadata.ts @@ -1,5 +1,8 @@ +import { BigNumber } from '@0x/utils'; import { Column, Entity, PrimaryColumn } from 'typeorm'; +import { bigNumberTransformer } from '../utils/transformers'; + @Entity({ name: 'token_metadata', schema: 'raw' }) export class TokenMetadata { @PrimaryColumn({ type: 'varchar', nullable: false }) @@ -8,10 +11,8 @@ export class TokenMetadata { @PrimaryColumn({ type: 'varchar', nullable: false }) public authority!: string; - // TODO(albrow): Convert decimals field to type BigNumber/numeric because it - // comes from a 256-bit integer in a smart contract. - @Column({ type: 'integer', nullable: true }) - public decimals!: number | null; + @Column({ type: 'numeric', transformer: bigNumberTransformer, nullable: true }) + public decimals!: BigNumber | null; @Column({ type: 'varchar', nullable: true }) public symbol!: string | null; diff --git a/packages/pipeline/src/parsers/events/index.ts b/packages/pipeline/src/parsers/events/index.ts index d42d1c57a..e18106c75 100644 --- a/packages/pipeline/src/parsers/events/index.ts +++ b/packages/pipeline/src/parsers/events/index.ts @@ -49,8 +49,8 @@ export function _convertToExchangeFillEvent(eventLog: LogWithDecodedArgs { connection = await createConnection(ormConfig as ConnectionOptions); const provider = web3Factory.getRpcProvider({ - rpcUrl: `https://mainnet.infura.io/${process.env.INFURA_API_KEY}`, + rpcUrl: `${INFURA_ROOT_URL}/${process.env.INFURA_API_KEY}`, }); const web3Source = new Web3Source(provider); await getAllMissingBlocks(web3Source); diff --git a/packages/pipeline/src/scripts/pull_missing_events.ts b/packages/pipeline/src/scripts/pull_missing_events.ts index c7e07c540..80abbb8b0 100644 --- a/packages/pipeline/src/scripts/pull_missing_events.ts +++ b/packages/pipeline/src/scripts/pull_missing_events.ts @@ -8,9 +8,8 @@ import { ExchangeEventsSource } from '../data_sources/contract-wrappers/exchange import { ExchangeCancelEvent, ExchangeCancelUpToEvent, ExchangeEvent, ExchangeFillEvent } from '../entities'; import * as ormConfig from '../ormconfig'; import { parseExchangeCancelEvents, parseExchangeCancelUpToEvents, parseExchangeFillEvents } from '../parsers/events'; -import { handleError } from '../utils'; +import { EXCHANGE_START_BLOCK, handleError, INFURA_ROOT_URL } from '../utils'; -const EXCHANGE_START_BLOCK = 6271590; // Block number when the Exchange contract was deployed to mainnet. const START_BLOCK_OFFSET = 100; // Number of blocks before the last known block to consider when updating fill events. const BATCH_SAVE_SIZE = 1000; // Number of events to save at once. @@ -19,7 +18,7 @@ let connection: Connection; (async () => { connection = await createConnection(ormConfig as ConnectionOptions); const provider = web3Factory.getRpcProvider({ - rpcUrl: 'https://mainnet.infura.io', + rpcUrl: INFURA_ROOT_URL, }); const eventsSource = new ExchangeEventsSource(provider, 1); await getFillEventsAsync(eventsSource); diff --git a/packages/pipeline/src/scripts/pull_radar_relay_orders.ts b/packages/pipeline/src/scripts/pull_radar_relay_orders.ts index b3a4d887e..bbbef9b47 100644 --- a/packages/pipeline/src/scripts/pull_radar_relay_orders.ts +++ b/packages/pipeline/src/scripts/pull_radar_relay_orders.ts @@ -16,11 +16,11 @@ let connection: Connection; (async () => { connection = await createConnection(ormConfig as ConnectionOptions); - await getOrderbook(); + await getOrderbookAsync(); process.exit(0); })().catch(handleError); -async function getOrderbook(): Promise { +async function getOrderbookAsync(): Promise { console.log('Getting all orders...'); const connectClient = new HttpClient(RADAR_RELAY_URL); const rawOrders = await connectClient.getOrdersAsync({ @@ -29,17 +29,22 @@ async function getOrderbook(): Promise { console.log(`Got ${rawOrders.records.length} orders.`); console.log('Parsing orders...'); // Parse the sra orders, then add source url to each. - const orders = R.pipe(parseSraOrders, R.map(setSourceUrl(RADAR_RELAY_URL)))(rawOrders); + const orders = R.pipe( + parseSraOrders, + R.map(setSourceUrl(RADAR_RELAY_URL)), + )(rawOrders); // Save all the orders and update the observed time stamps in a single // transaction. console.log('Saving orders and updating timestamps...'); - await connection.transaction(async (manager: EntityManager): Promise => { - for (const order of orders) { - await manager.save(SraOrder, order); - const observedTimestamp = createObservedTimestampForOrder(order); - await manager.save(observedTimestamp); - } - }); + await connection.transaction( + async (manager: EntityManager): Promise => { + for (const order of orders) { + await manager.save(SraOrder, order); + const observedTimestamp = createObservedTimestampForOrder(order); + await manager.save(observedTimestamp); + } + }, + ); } const sourceUrlProp = R.lensProp('sourceUrl'); @@ -48,6 +53,8 @@ const sourceUrlProp = R.lensProp('sourceUrl'); * Sets the source url for a single order. Returns a new order instead of * mutating the given one. */ -const setSourceUrl = R.curry((sourceURL: string, order: SraOrder): SraOrder => { - return R.set(sourceUrlProp, sourceURL, order); -}); +const setSourceUrl = R.curry( + (sourceURL: string, order: SraOrder): SraOrder => { + return R.set(sourceUrlProp, sourceURL, order); + }, +); diff --git a/packages/pipeline/src/scripts/pull_trusted_tokens.ts b/packages/pipeline/src/scripts/pull_trusted_tokens.ts index b3a4466bb..1befc4437 100644 --- a/packages/pipeline/src/scripts/pull_trusted_tokens.ts +++ b/packages/pipeline/src/scripts/pull_trusted_tokens.ts @@ -8,7 +8,7 @@ import { parseMetamaskTrustedTokens, parseZeroExTrustedTokens } from '../parsers import { handleError } from '../utils'; const METAMASK_TRUSTED_TOKENS_URL = - 'https://raw.githubusercontent.com/MetaMask/eth-contract-metadata/master/contract-map.json'; + 'https://raw.githubusercontent.com/MetaMask/eth-contract-metadata/d45916c533116510cc8e9e048a8b5fc3732a6b6d/contract-map.json'; const ZEROEX_TRUSTED_TOKENS_URL = 'https://website-api.0xproject.com/tokens'; @@ -22,7 +22,7 @@ let connection: Connection; })().catch(handleError); async function getMetamaskTrustedTokens(): Promise { - // tslint:disable-next-line + // tslint:disable-next-line:no-console console.log('Getting latest metamask trusted tokens list ...'); const trustedTokensRepository = connection.getRepository(TokenMetadata); const trustedTokensSource = new TrustedTokenSource>( @@ -30,23 +30,23 @@ async function getMetamaskTrustedTokens(): Promise { ); const resp = await trustedTokensSource.getTrustedTokenMetaAsync(); const trustedTokens = parseMetamaskTrustedTokens(resp); - // tslint:disable-next-line + // tslint:disable-next-line:no-console console.log('Saving metamask trusted tokens list'); await trustedTokensRepository.save(trustedTokens); - // tslint:disable-next-line + // tslint:disable-next-line:no-console console.log('Done saving metamask trusted tokens.'); } async function getZeroExTrustedTokens(): Promise { - // tslint:disable-next-line + // tslint:disable-next-line:no-console console.log('Getting latest 0x trusted tokens list ...'); const trustedTokensRepository = connection.getRepository(TokenMetadata); const trustedTokensSource = new TrustedTokenSource(ZEROEX_TRUSTED_TOKENS_URL); const resp = await trustedTokensSource.getTrustedTokenMetaAsync(); const trustedTokens = parseZeroExTrustedTokens(resp); - // tslint:disable-next-line + // tslint:disable-next-line:no-console console.log('Saving metamask trusted tokens list'); await trustedTokensRepository.save(trustedTokens); - // tslint:disable-next-line + // tslint:disable-next-line:no-console console.log('Done saving metamask trusted tokens.'); } diff --git a/packages/pipeline/src/utils/constants.ts b/packages/pipeline/src/utils/constants.ts new file mode 100644 index 000000000..56f3e82d8 --- /dev/null +++ b/packages/pipeline/src/utils/constants.ts @@ -0,0 +1,3 @@ +// Block number when the Exchange contract was deployed to mainnet. +export const EXCHANGE_START_BLOCK = 6271590; +export const INFURA_ROOT_URL = 'https://mainnet.infura.io'; diff --git a/packages/pipeline/src/utils/index.ts b/packages/pipeline/src/utils/index.ts index 718ea6133..2096a0a39 100644 --- a/packages/pipeline/src/utils/index.ts +++ b/packages/pipeline/src/utils/index.ts @@ -1,5 +1,6 @@ import { BigNumber } from '@0x/utils'; export * from './transformers'; +export * from './constants'; /** * If the given BigNumber is not null, returns the string representation of that diff --git a/packages/pipeline/src/utils/transformers/big_number.ts b/packages/pipeline/src/utils/transformers/big_number.ts index a0471a5e3..5f2e4d565 100644 --- a/packages/pipeline/src/utils/transformers/big_number.ts +++ b/packages/pipeline/src/utils/transformers/big_number.ts @@ -3,13 +3,13 @@ import { ValueTransformer } from 'typeorm/decorator/options/ValueTransformer'; export class BigNumberTransformer implements ValueTransformer { // tslint:disable-next-line:prefer-function-over-method - public to(value: BigNumber): string { - return value.toString(); + public to(value: BigNumber | null): string | null { + return value === null ? null : value.toString(); } // tslint:disable-next-line:prefer-function-over-method - public from(value: string): BigNumber { - return new BigNumber(value); + public from(value: string | null): BigNumber | null { + return value === null ? null : new BigNumber(value); } } diff --git a/packages/pipeline/test/entities/token_metadata_test.ts b/packages/pipeline/test/entities/token_metadata_test.ts index 805b4b0f7..48e656644 100644 --- a/packages/pipeline/test/entities/token_metadata_test.ts +++ b/packages/pipeline/test/entities/token_metadata_test.ts @@ -1,3 +1,4 @@ +import { BigNumber } from '@0x/utils'; import 'mocha'; import 'reflect-metadata'; @@ -9,15 +10,15 @@ import { testSaveAndFindEntityAsync } from './util'; chaiSetup.configure(); -const metadataWithoutNullFields = { +const metadataWithoutNullFields: TokenMetadata = { address: '0xe41d2489571d322189246dafa5ebde1f4699f498', authority: 'https://website-api.0xproject.com/tokens', - decimals: 18, + decimals: new BigNumber(18), symbol: 'ZRX', name: '0x', }; -const metadataWithNullFields = { +const metadataWithNullFields: TokenMetadata = { address: '0xe41d2489571d322189246dafa5ebde1f4699f499', authority: 'https://website-api.0xproject.com/tokens', decimals: null, -- cgit v1.2.3 From 2e704ac01a077b0c73288aaa53c9cf66c73e27f1 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Tue, 4 Dec 2018 20:08:32 -0800 Subject: Fix prettier --- packages/pipeline/README.md | 14 +++++------ .../src/scripts/pull_radar_relay_orders.ts | 29 ++++++++-------------- 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/packages/pipeline/README.md b/packages/pipeline/README.md index 0f4abd935..794488cac 100644 --- a/packages/pipeline/README.md +++ b/packages/pipeline/README.md @@ -150,17 +150,17 @@ set the`ZEROEX_DATA_PIPELINE_DB_URL` environment variable to a valid #### Additional guidelines and tips: -- Table names should be plural and separated by underscores (e.g., +* Table names should be plural and separated by underscores (e.g., `exchange_fill_events`). -- Any table which contains data which comes directly from a third-party source +* Any table which contains data which comes directly from a third-party source should be namespaced in the `raw` PostgreSQL schema. -- Column names in the database should be separated by underscores (e.g., +* Column names in the database should be separated by underscores (e.g., `maker_asset_type`). -- Field names in entity classes (like any other fields in TypeScript) should +* Field names in entity classes (like any other fields in TypeScript) should be camel-cased (e.g., `makerAssetType`). -- All timestamps should be stored as milliseconds since the Unix Epoch. -- Use the `BigNumber` type for TypeScript code which deals with 256-bit +* All timestamps should be stored as milliseconds since the Unix Epoch. +* Use the `BigNumber` type for TypeScript code which deals with 256-bit numbers from smart contracts or for any case where we are dealing with large floating point numbers. -- [TypeORM documentation](http://typeorm.io/#/) is pretty robust and can be a +* [TypeORM documentation](http://typeorm.io/#/) is pretty robust and can be a helpful resource. diff --git a/packages/pipeline/src/scripts/pull_radar_relay_orders.ts b/packages/pipeline/src/scripts/pull_radar_relay_orders.ts index bbbef9b47..6c18bcaef 100644 --- a/packages/pipeline/src/scripts/pull_radar_relay_orders.ts +++ b/packages/pipeline/src/scripts/pull_radar_relay_orders.ts @@ -29,22 +29,17 @@ async function getOrderbookAsync(): Promise { console.log(`Got ${rawOrders.records.length} orders.`); console.log('Parsing orders...'); // Parse the sra orders, then add source url to each. - const orders = R.pipe( - parseSraOrders, - R.map(setSourceUrl(RADAR_RELAY_URL)), - )(rawOrders); + const orders = R.pipe(parseSraOrders, R.map(setSourceUrl(RADAR_RELAY_URL)))(rawOrders); // Save all the orders and update the observed time stamps in a single // transaction. console.log('Saving orders and updating timestamps...'); - await connection.transaction( - async (manager: EntityManager): Promise => { - for (const order of orders) { - await manager.save(SraOrder, order); - const observedTimestamp = createObservedTimestampForOrder(order); - await manager.save(observedTimestamp); - } - }, - ); + await connection.transaction(async (manager: EntityManager): Promise => { + for (const order of orders) { + await manager.save(SraOrder, order); + const observedTimestamp = createObservedTimestampForOrder(order); + await manager.save(observedTimestamp); + } + }); } const sourceUrlProp = R.lensProp('sourceUrl'); @@ -53,8 +48,6 @@ const sourceUrlProp = R.lensProp('sourceUrl'); * Sets the source url for a single order. Returns a new order instead of * mutating the given one. */ -const setSourceUrl = R.curry( - (sourceURL: string, order: SraOrder): SraOrder => { - return R.set(sourceUrlProp, sourceURL, order); - }, -); +const setSourceUrl = R.curry((sourceURL: string, order: SraOrder): SraOrder => { + return R.set(sourceUrlProp, sourceURL, order); +}); -- cgit v1.2.3 From e0348f9c044b4909260e4864398b4f50232da620 Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Tue, 4 Dec 2018 20:20:49 -0800 Subject: Change type of transactions.gas_used and gas_price to BigNumber/numeric --- ...983324954-ConvertTransactionGasPriceToBigNumber.ts | 19 +++++++++++++++++++ packages/pipeline/src/entities/transaction.ts | 11 ++++++----- packages/pipeline/src/parsers/web3/index.ts | 5 +++-- packages/pipeline/test/entities/transaction_test.ts | 5 +++-- 4 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 packages/pipeline/migrations/1543983324954-ConvertTransactionGasPriceToBigNumber.ts diff --git a/packages/pipeline/migrations/1543983324954-ConvertTransactionGasPriceToBigNumber.ts b/packages/pipeline/migrations/1543983324954-ConvertTransactionGasPriceToBigNumber.ts new file mode 100644 index 000000000..dcb0fd727 --- /dev/null +++ b/packages/pipeline/migrations/1543983324954-ConvertTransactionGasPriceToBigNumber.ts @@ -0,0 +1,19 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ConvertTransactionGasPriceToBigNumber1543983324954 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE raw.transactions + ALTER COLUMN gas_price TYPE numeric USING gas_price::numeric, + ALTER COLUMN gas_used TYPE numeric USING gas_used::numeric;`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE raw.transactions + ALTER COLUMN gas_price TYPE numeric USING gas_price::bigint, + ALTER COLUMN gas_used TYPE numeric USING gas_used::bigint;`, + ); + } +} diff --git a/packages/pipeline/src/entities/transaction.ts b/packages/pipeline/src/entities/transaction.ts index 91e4ecb5d..742050177 100644 --- a/packages/pipeline/src/entities/transaction.ts +++ b/packages/pipeline/src/entities/transaction.ts @@ -1,6 +1,7 @@ +import { BigNumber } from '@0x/utils'; import { Column, Entity, PrimaryColumn } from 'typeorm'; -import { numberToBigIntTransformer } from '../utils'; +import { bigNumberTransformer, numberToBigIntTransformer } from '../utils'; @Entity({ name: 'transactions', schema: 'raw' }) export class Transaction { @@ -11,8 +12,8 @@ export class Transaction { @PrimaryColumn({ name: 'block_number', transformer: numberToBigIntTransformer }) public blockNumber!: number; - @Column({ type: 'bigint', name: 'gas_used', transformer: numberToBigIntTransformer }) - public gasUsed!: number; - @Column({ type: 'bigint', name: 'gas_price', transformer: numberToBigIntTransformer }) - public gasPrice!: number; + @Column({ type: 'numeric', name: 'gas_used', transformer: bigNumberTransformer }) + public gasUsed!: BigNumber; + @Column({ type: 'numeric', name: 'gas_price', transformer: bigNumberTransformer }) + public gasPrice!: BigNumber; } diff --git a/packages/pipeline/src/parsers/web3/index.ts b/packages/pipeline/src/parsers/web3/index.ts index 86f924151..f986efc59 100644 --- a/packages/pipeline/src/parsers/web3/index.ts +++ b/packages/pipeline/src/parsers/web3/index.ts @@ -1,3 +1,4 @@ +import { BigNumber } from '@0x/utils'; import { BlockWithoutTransactionData, Transaction as EthTransaction } from 'ethereum-types'; import { Block, Transaction } from '../../entities'; @@ -41,8 +42,8 @@ export function parseTransaction(rawTransaction: EthTransaction): Transaction { tx.blockHash = rawTransaction.blockHash; tx.blockNumber = rawTransaction.blockNumber; - tx.gasUsed = rawTransaction.gas; - tx.gasPrice = rawTransaction.gasPrice.toNumber(); + tx.gasUsed = new BigNumber(rawTransaction.gas); + tx.gasPrice = rawTransaction.gasPrice; return tx; } diff --git a/packages/pipeline/test/entities/transaction_test.ts b/packages/pipeline/test/entities/transaction_test.ts index 027de7d32..634844544 100644 --- a/packages/pipeline/test/entities/transaction_test.ts +++ b/packages/pipeline/test/entities/transaction_test.ts @@ -1,3 +1,4 @@ +import { BigNumber } from '@0x/utils'; import 'mocha'; import 'reflect-metadata'; @@ -17,8 +18,8 @@ describe('Transaction entity', () => { const transaction = new Transaction(); transaction.blockHash = '0x6ff106d00b6c3746072fc06bae140fb2549036ba7bcf9184ae19a42fd33657fd'; transaction.blockNumber = 6276262; - transaction.gasPrice = 3000000; - transaction.gasUsed = 125000; + transaction.gasPrice = new BigNumber(3000000); + transaction.gasUsed = new BigNumber(125000); transaction.transactionHash = '0x6dd106d002873746072fc5e496dd0fb2541b68c77bcf9184ae19a42fd33657fe'; await testSaveAndFindEntityAsync(transactionRepository, transaction); }); -- cgit v1.2.3 From d0b2ee7fc139de2429066946cdb4517e5d6362f3 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 5 Dec 2018 11:35:38 +0000 Subject: Fix mixed up chinese translations --- packages/website/translations/chinese.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/website/translations/chinese.json b/packages/website/translations/chinese.json index eb88b43d0..89b7ed485 100644 --- a/packages/website/translations/chinese.json +++ b/packages/website/translations/chinese.json @@ -76,9 +76,9 @@ "STANDARD_RELAYER_API": "中继方标准API", "PORTAL_DAPP": "去中心化应用门户", "WEBSITE": "网站", - "DEVELOPERS": "首页", - "HOME": "Rocket.chat", - "ROCKETCHAT": "开发人员", + "DEVELOPERS": "开发人员", + "HOME": "首页", + "ROCKETCHAT": "聊", "BUILD_A_RELAYER": "build a relayer", "BUILD_A_RELAYER_DESCRIPTION": "Learn how to build your own 0x relayer from scratch", "DEVELOP_ON_ETHEREUM": "develop on Ethereum", -- cgit v1.2.3 From 0a618f6f4dc61ff329ee720cbadd41d7068fec92 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 5 Dec 2018 11:46:28 +0000 Subject: Replace Rocket.chat with Discord --- packages/website/translations/chinese.json | 2 +- packages/website/translations/english.json | 2 +- packages/website/translations/korean.json | 2 +- packages/website/translations/russian.json | 6 +++--- packages/website/translations/spanish.json | 6 +++--- packages/website/ts/components/footer.tsx | 4 ++-- packages/website/ts/pages/faq/faq.tsx | 2 +- packages/website/ts/types.ts | 2 +- packages/website/ts/utils/constants.ts | 2 +- 9 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/website/translations/chinese.json b/packages/website/translations/chinese.json index 89b7ed485..2b1f2a3f5 100644 --- a/packages/website/translations/chinese.json +++ b/packages/website/translations/chinese.json @@ -78,7 +78,7 @@ "WEBSITE": "网站", "DEVELOPERS": "开发人员", "HOME": "首页", - "ROCKETCHAT": "聊", + "DISCORD": "discord chat", "BUILD_A_RELAYER": "build a relayer", "BUILD_A_RELAYER_DESCRIPTION": "Learn how to build your own 0x relayer from scratch", "DEVELOP_ON_ETHEREUM": "develop on Ethereum", diff --git a/packages/website/translations/english.json b/packages/website/translations/english.json index 5fba7a0ff..b2799e264 100644 --- a/packages/website/translations/english.json +++ b/packages/website/translations/english.json @@ -81,7 +81,7 @@ "WEBSITE": "website", "DEVELOPERS": "developers", "HOME": "home", - "ROCKETCHAT": "rocket.chat", + "DISCORD": "discord chat", "TRADE_CALL_TO_ACTION": "trade on 0x", "BUILD_A_RELAYER": "build a relayer", "BUILD_A_RELAYER_DESCRIPTION": "Learn how to build your own 0x relayer from scratch", diff --git a/packages/website/translations/korean.json b/packages/website/translations/korean.json index e3ce74676..61c431a5a 100644 --- a/packages/website/translations/korean.json +++ b/packages/website/translations/korean.json @@ -77,7 +77,7 @@ "PORTAL_DAPP": "포털 dApp", "WEBSITE": "Website", "HOME": "홈", - "ROCKETCHAT": "Rocket.chat", + "DISCORD": "discord chat", "DEVELOPERS": "개발자", "BUILD_A_RELAYER": "build a relayer", "BUILD_A_RELAYER_DESCRIPTION": "Learn how to build your own 0x relayer from scratch", diff --git a/packages/website/translations/russian.json b/packages/website/translations/russian.json index c74fb5e32..3180d4a8e 100644 --- a/packages/website/translations/russian.json +++ b/packages/website/translations/russian.json @@ -76,9 +76,9 @@ "STANDARD_RELAYER_API": "standard relayer API", "PORTAL_DAPP": "DApp-портал", "WEBSITE": "Веб-сайт", - "DEVELOPERS": "Домашняя страница", - "HOME": "Rocket.chat", - "ROCKETCHAT": "Для разработчиков", + "DEVELOPERS": "Для разработчиков", + "HOME": "Домашняя страница", + "DISCORD": "discord chat", "BUILD_A_RELAYER": "build a relayer", "BUILD_A_RELAYER_DESCRIPTION": "Learn how to build your own 0x relayer from scratch", "DEVELOP_ON_ETHEREUM": "develop on Ethereum", diff --git a/packages/website/translations/spanish.json b/packages/website/translations/spanish.json index e29db711b..c23bd609b 100644 --- a/packages/website/translations/spanish.json +++ b/packages/website/translations/spanish.json @@ -77,9 +77,9 @@ "STANDARD_RELAYER_API": "API de transmisión estándar", "PORTAL_DAPP": "portal dApp", "WEBSITE": "website", - "DEVELOPERS": "inicio", - "HOME": "rocket.chat", - "ROCKETCHAT": "desarrolladores", + "DEVELOPERS": "desarrolladores", + "HOME": "inicio", + "DISCORD": "discord chat", "BUILD_A_RELAYER": "build a relayer", "BUILD_A_RELAYER_DESCRIPTION": "Learn how to build your own 0x relayer from scratch", "DEVELOP_ON_ETHEREUM": "develop on Ethereum", diff --git a/packages/website/ts/components/footer.tsx b/packages/website/ts/components/footer.tsx index e10005a0a..57071ee29 100644 --- a/packages/website/ts/components/footer.tsx +++ b/packages/website/ts/components/footer.tsx @@ -71,7 +71,7 @@ export class Footer extends React.Component { ], [Key.Community]: [ { - title: this.props.translate.get(Key.RocketChat, Deco.Cap), + title: this.props.translate.get(Key.Discord, Deco.Cap), to: constants.URL_ZEROEX_CHAT, shouldOpenInNewTab: true, }, @@ -177,7 +177,7 @@ export class Footer extends React.Component { } private _renderMenuItem(link: ALink): React.ReactNode { const titleToIcon: { [title: string]: string } = { - [this.props.translate.get(Key.RocketChat, Deco.Cap)]: 'rocketchat.png', + [this.props.translate.get(Key.Discord, Deco.Cap)]: 'rocketchat.png', [this.props.translate.get(Key.Blog, Deco.Cap)]: 'medium.png', Twitter: 'twitter.png', Reddit: 'reddit.png', diff --git a/packages/website/ts/pages/faq/faq.tsx b/packages/website/ts/pages/faq/faq.tsx index 10d91bae8..c4965e61c 100644 --- a/packages/website/ts/pages/faq/faq.tsx +++ b/packages/website/ts/pages/faq/faq.tsx @@ -379,7 +379,7 @@ const sections: FAQSection[] = [
Join our{' '} - Rocket.chat + Discord ! As an open source project, 0x will rely on a worldwide community of passionate developers to contribute proposals, ideas and code.
diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 9c4b8a018..d2914dbfe 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -463,7 +463,7 @@ export enum Key { Website = 'WEBSITE', Developers = 'DEVELOPERS', Home = 'HOME', - RocketChat = 'ROCKETCHAT', + Discord = 'DISCORD', TradeCallToAction = 'TRADE_CALL_TO_ACTION', OurMissionAndValues = 'OUR_MISSION_AND_VALUES', BuildARelayer = 'BUILD_A_RELAYER', diff --git a/packages/website/ts/utils/constants.ts b/packages/website/ts/utils/constants.ts index e9afc8763..6597d51b4 100644 --- a/packages/website/ts/utils/constants.ts +++ b/packages/website/ts/utils/constants.ts @@ -3,7 +3,7 @@ import { BigNumber } from '@0x/utils'; import { Key, WebsitePaths } from 'ts/types'; const URL_FORUM = 'https://forum.0xproject.com'; -const URL_ZEROEX_CHAT = 'https://chat.0xproject.com'; +const URL_ZEROEX_CHAT = 'https://discord.gg/d3FTX3M'; export const constants = { DECIMAL_PLACES_ETH: 18, -- cgit v1.2.3 From 48021a227f7034c2238c64146b2eb05eb8bfb687 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Wed, 5 Dec 2018 11:19:10 +0100 Subject: Refactor our protocol interfaces into @0x/contracts-interfaces --- .gitignore | 2 + .prettierignore | 2 + contracts/core/compiler.json | 4 - .../examples/ExchangeWrapper/ExchangeWrapper.sol | 2 +- .../contracts/examples/Validator/Validator.sol | 2 +- .../core/contracts/examples/Wallet/Wallet.sol | 2 +- .../contracts/examples/Whitelist/Whitelist.sol | 2 +- .../extensions/DutchAuction/DutchAuction.sol | 2 +- .../extensions/Forwarder/libs/LibConstants.sol | 2 +- .../extensions/OrderValidator/OrderValidator.sol | 2 +- .../protocol/AssetProxy/interfaces/IAssetData.sol | 44 ------ .../protocol/AssetProxy/interfaces/IAssetProxy.sol | 46 ------ .../AssetProxy/interfaces/IAuthorizable.sol | 52 ------- .../protocol/AssetProxy/mixins/MAuthorizable.sol | 2 +- .../Exchange/MixinAssetProxyDispatcher.sol | 2 +- .../protocol/Exchange/MixinSignatureValidator.sol | 4 +- .../Exchange/interfaces/IAssetProxyDispatcher.sol | 37 ----- .../protocol/Exchange/interfaces/IExchange.sol | 38 ----- .../protocol/Exchange/interfaces/IExchangeCore.sol | 60 -------- .../protocol/Exchange/interfaces/IMatchOrders.sol | 44 ------ .../Exchange/interfaces/ISignatureValidator.sol | 57 -------- .../protocol/Exchange/interfaces/ITransactions.sol | 35 ----- .../protocol/Exchange/interfaces/IValidator.sol | 37 ----- .../protocol/Exchange/interfaces/IWallet.sol | 35 ----- .../Exchange/interfaces/IWrapperFunctions.sol | 160 --------------------- .../Exchange/mixins/MAssetProxyDispatcher.sol | 2 +- .../protocol/Exchange/mixins/MExchangeCore.sol | 2 +- .../protocol/Exchange/mixins/MMatchOrders.sol | 2 +- .../Exchange/mixins/MSignatureValidator.sol | 2 +- .../protocol/Exchange/mixins/MTransactions.sol | 2 +- .../protocol/Exchange/mixins/MWrapperFunctions.sol | 2 +- .../ReentrantERC20Token/ReentrantERC20Token.sol | 2 +- contracts/core/package.json | 3 +- contracts/core/src/artifacts/index.ts | 8 -- contracts/core/src/wrappers/index.ts | 2 - contracts/core/test/asset_proxy/proxies.ts | 7 +- contracts/core/test/exchange/core.ts | 4 +- contracts/core/tsconfig.json | 6 +- contracts/interfaces/.solhint.json | 20 +++ contracts/interfaces/README.md | 72 ++++++++++ contracts/interfaces/compiler.json | 35 +++++ .../contracts/protocol/AssetProxy/IAssetData.sol | 44 ++++++ .../contracts/protocol/AssetProxy/IAssetProxy.sol | 46 ++++++ .../protocol/AssetProxy/IAuthorizable.sol | 52 +++++++ .../protocol/Exchange/IAssetProxyDispatcher.sol | 37 +++++ .../contracts/protocol/Exchange/IExchange.sol | 38 +++++ .../contracts/protocol/Exchange/IExchangeCore.sol | 60 ++++++++ .../contracts/protocol/Exchange/IMatchOrders.sol | 44 ++++++ .../protocol/Exchange/ISignatureValidator.sol | 57 ++++++++ .../contracts/protocol/Exchange/ITransactions.sol | 35 +++++ .../contracts/protocol/Exchange/IValidator.sol | 37 +++++ .../contracts/protocol/Exchange/IWallet.sol | 35 +++++ .../protocol/Exchange/IWrapperFunctions.sol | 160 +++++++++++++++++++++ contracts/interfaces/package.json | 57 ++++++++ contracts/interfaces/src/artifacts/index.ts | 29 ++++ contracts/interfaces/src/index.ts | 2 + contracts/interfaces/src/wrappers/index.ts | 12 ++ contracts/interfaces/tsconfig.json | 24 ++++ contracts/interfaces/tslint.json | 6 + 59 files changed, 932 insertions(+), 689 deletions(-) delete mode 100644 contracts/core/contracts/protocol/AssetProxy/interfaces/IAssetData.sol delete mode 100644 contracts/core/contracts/protocol/AssetProxy/interfaces/IAssetProxy.sol delete mode 100644 contracts/core/contracts/protocol/AssetProxy/interfaces/IAuthorizable.sol delete mode 100644 contracts/core/contracts/protocol/Exchange/interfaces/IAssetProxyDispatcher.sol delete mode 100644 contracts/core/contracts/protocol/Exchange/interfaces/IExchange.sol delete mode 100644 contracts/core/contracts/protocol/Exchange/interfaces/IExchangeCore.sol delete mode 100644 contracts/core/contracts/protocol/Exchange/interfaces/IMatchOrders.sol delete mode 100644 contracts/core/contracts/protocol/Exchange/interfaces/ISignatureValidator.sol delete mode 100644 contracts/core/contracts/protocol/Exchange/interfaces/ITransactions.sol delete mode 100644 contracts/core/contracts/protocol/Exchange/interfaces/IValidator.sol delete mode 100644 contracts/core/contracts/protocol/Exchange/interfaces/IWallet.sol delete mode 100644 contracts/core/contracts/protocol/Exchange/interfaces/IWrapperFunctions.sol create mode 100644 contracts/interfaces/.solhint.json create mode 100644 contracts/interfaces/README.md create mode 100644 contracts/interfaces/compiler.json create mode 100644 contracts/interfaces/contracts/protocol/AssetProxy/IAssetData.sol create mode 100644 contracts/interfaces/contracts/protocol/AssetProxy/IAssetProxy.sol create mode 100644 contracts/interfaces/contracts/protocol/AssetProxy/IAuthorizable.sol create mode 100644 contracts/interfaces/contracts/protocol/Exchange/IAssetProxyDispatcher.sol create mode 100644 contracts/interfaces/contracts/protocol/Exchange/IExchange.sol create mode 100644 contracts/interfaces/contracts/protocol/Exchange/IExchangeCore.sol create mode 100644 contracts/interfaces/contracts/protocol/Exchange/IMatchOrders.sol create mode 100644 contracts/interfaces/contracts/protocol/Exchange/ISignatureValidator.sol create mode 100644 contracts/interfaces/contracts/protocol/Exchange/ITransactions.sol create mode 100644 contracts/interfaces/contracts/protocol/Exchange/IValidator.sol create mode 100644 contracts/interfaces/contracts/protocol/Exchange/IWallet.sol create mode 100644 contracts/interfaces/contracts/protocol/Exchange/IWrapperFunctions.sol create mode 100644 contracts/interfaces/package.json create mode 100644 contracts/interfaces/src/artifacts/index.ts create mode 100644 contracts/interfaces/src/index.ts create mode 100644 contracts/interfaces/src/wrappers/index.ts create mode 100644 contracts/interfaces/tsconfig.json create mode 100644 contracts/interfaces/tslint.json diff --git a/.gitignore b/.gitignore index fab8f389e..22db8cf31 100644 --- a/.gitignore +++ b/.gitignore @@ -83,6 +83,7 @@ contracts/core/generated-artifacts/ contracts/multisig/generated-artifacts/ contracts/utils/generated-artifacts/ contracts/libs/generated-artifacts/ +contracts/interfaces/generated-artifacts/ packages/sol-cov/test/fixtures/artifacts/ packages/metacoin/artifacts/ @@ -92,6 +93,7 @@ contracts/core/generated-wrappers/ contracts/multisig/generated-wrappers/ contracts/utils/generated-wrappers/ contracts/libs/generated-wrappers/ +contracts/interfaces/generated-wrappers/ packages/metacoin/src/contract_wrappers # solc-bin in sol-compiler diff --git a/.prettierignore b/.prettierignore index 43c8015fd..0be17624b 100644 --- a/.prettierignore +++ b/.prettierignore @@ -8,6 +8,8 @@ lib /contracts/utils/generated-artifacts /contracts/libs/generated-wrappers /contracts/libs/generated-artifacts +/contracts/interfaces/generated-wrappers +/contracts/interfaces/generated-artifacts /packages/abi-gen-wrappers/src/generated-wrappers /packages/contract-artifacts/artifacts /python-packages/order_utils/src/zero_ex/contract_artifacts/artifacts diff --git a/contracts/core/compiler.json b/contracts/core/compiler.json index 7e527130a..239c25919 100644 --- a/contracts/core/compiler.json +++ b/contracts/core/compiler.json @@ -33,11 +33,7 @@ "Exchange", "ExchangeWrapper", "Forwarder", - "IAssetData", - "IAssetProxy", "InvalidERC721Receiver", - "IValidator", - "IWallet", "MixinAuthorizable", "MultiAssetProxy", "OrderValidator", diff --git a/contracts/core/contracts/examples/ExchangeWrapper/ExchangeWrapper.sol b/contracts/core/contracts/examples/ExchangeWrapper/ExchangeWrapper.sol index ca5a64a26..d98136922 100644 --- a/contracts/core/contracts/examples/ExchangeWrapper/ExchangeWrapper.sol +++ b/contracts/core/contracts/examples/ExchangeWrapper/ExchangeWrapper.sol @@ -19,7 +19,7 @@ pragma solidity 0.4.24; pragma experimental ABIEncoderV2; -import "../../protocol/Exchange/interfaces/IExchange.sol"; +import "@0x/contracts-interfaces/contracts/protocol/Exchange/IExchange.sol"; import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; diff --git a/contracts/core/contracts/examples/Validator/Validator.sol b/contracts/core/contracts/examples/Validator/Validator.sol index 72ed528ba..e488a9ca7 100644 --- a/contracts/core/contracts/examples/Validator/Validator.sol +++ b/contracts/core/contracts/examples/Validator/Validator.sol @@ -18,7 +18,7 @@ pragma solidity 0.4.24; -import "../../protocol/Exchange/interfaces/IValidator.sol"; +import "@0x/contracts-interfaces/contracts/protocol/Exchange/IValidator.sol"; contract Validator is diff --git a/contracts/core/contracts/examples/Wallet/Wallet.sol b/contracts/core/contracts/examples/Wallet/Wallet.sol index 3738be841..8edc74eb3 100644 --- a/contracts/core/contracts/examples/Wallet/Wallet.sol +++ b/contracts/core/contracts/examples/Wallet/Wallet.sol @@ -18,7 +18,7 @@ pragma solidity 0.4.24; -import "../../protocol/Exchange/interfaces/IWallet.sol"; +import "@0x/contracts-interfaces/contracts/protocol/Exchange/IWallet.sol"; import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol"; diff --git a/contracts/core/contracts/examples/Whitelist/Whitelist.sol b/contracts/core/contracts/examples/Whitelist/Whitelist.sol index cfcddddd3..2a3d33738 100644 --- a/contracts/core/contracts/examples/Whitelist/Whitelist.sol +++ b/contracts/core/contracts/examples/Whitelist/Whitelist.sol @@ -19,7 +19,7 @@ pragma solidity 0.4.24; pragma experimental ABIEncoderV2; -import "../../protocol/Exchange/interfaces/IExchange.sol"; +import "@0x/contracts-interfaces/contracts/protocol/Exchange/IExchange.sol"; import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; import "@0x/contracts-utils/contracts/utils/Ownable/Ownable.sol"; diff --git a/contracts/core/contracts/extensions/DutchAuction/DutchAuction.sol b/contracts/core/contracts/extensions/DutchAuction/DutchAuction.sol index a40991ae7..90b6f4648 100644 --- a/contracts/core/contracts/extensions/DutchAuction/DutchAuction.sol +++ b/contracts/core/contracts/extensions/DutchAuction/DutchAuction.sol @@ -19,7 +19,7 @@ pragma solidity 0.4.24; pragma experimental ABIEncoderV2; -import "../../protocol/Exchange/interfaces/IExchange.sol"; +import "@0x/contracts-interfaces/contracts/protocol/Exchange/IExchange.sol"; import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; import "../../tokens/ERC20Token/IERC20Token.sol"; import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol"; diff --git a/contracts/core/contracts/extensions/Forwarder/libs/LibConstants.sol b/contracts/core/contracts/extensions/Forwarder/libs/LibConstants.sol index 0f98ae595..00be0f1bd 100644 --- a/contracts/core/contracts/extensions/Forwarder/libs/LibConstants.sol +++ b/contracts/core/contracts/extensions/Forwarder/libs/LibConstants.sol @@ -19,7 +19,7 @@ pragma solidity 0.4.24; import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol"; -import "../../../protocol/Exchange/interfaces/IExchange.sol"; +import "@0x/contracts-interfaces/contracts/protocol/Exchange/IExchange.sol"; import "../../../tokens/EtherToken/IEtherToken.sol"; import "../../../tokens/ERC20Token/IERC20Token.sol"; diff --git a/contracts/core/contracts/extensions/OrderValidator/OrderValidator.sol b/contracts/core/contracts/extensions/OrderValidator/OrderValidator.sol index 9e9e63e9b..78c31c8fc 100644 --- a/contracts/core/contracts/extensions/OrderValidator/OrderValidator.sol +++ b/contracts/core/contracts/extensions/OrderValidator/OrderValidator.sol @@ -19,7 +19,7 @@ pragma solidity 0.4.24; pragma experimental ABIEncoderV2; -import "../../protocol/Exchange/interfaces/IExchange.sol"; +import "@0x/contracts-interfaces/contracts/protocol/Exchange/IExchange.sol"; import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; import "../../tokens/ERC20Token/IERC20Token.sol"; import "../../tokens/ERC721Token/IERC721Token.sol"; diff --git a/contracts/core/contracts/protocol/AssetProxy/interfaces/IAssetData.sol b/contracts/core/contracts/protocol/AssetProxy/interfaces/IAssetData.sol deleted file mode 100644 index e2da68919..000000000 --- a/contracts/core/contracts/protocol/AssetProxy/interfaces/IAssetData.sol +++ /dev/null @@ -1,44 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -// solhint-disable -pragma solidity 0.4.24; -pragma experimental ABIEncoderV2; - - -// @dev Interface of the asset proxy's assetData. -// The asset proxies take an ABI encoded `bytes assetData` as argument. -// This argument is ABI encoded as one of the methods of this interface. -interface IAssetData { - - function ERC20Token(address tokenContract) - external; - - function ERC721Token( - address tokenContract, - uint256 tokenId - ) - external; - - function MultiAsset( - uint256[] amounts, - bytes[] nestedAssetData - ) - external; - -} diff --git a/contracts/core/contracts/protocol/AssetProxy/interfaces/IAssetProxy.sol b/contracts/core/contracts/protocol/AssetProxy/interfaces/IAssetProxy.sol deleted file mode 100644 index b25d2d75a..000000000 --- a/contracts/core/contracts/protocol/AssetProxy/interfaces/IAssetProxy.sol +++ /dev/null @@ -1,46 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity 0.4.24; - -import "./IAuthorizable.sol"; - - -contract IAssetProxy is - IAuthorizable -{ - /// @dev Transfers assets. Either succeeds or throws. - /// @param assetData Byte array encoded for the respective asset proxy. - /// @param from Address to transfer asset from. - /// @param to Address to transfer asset to. - /// @param amount Amount of asset to transfer. - function transferFrom( - bytes assetData, - address from, - address to, - uint256 amount - ) - external; - - /// @dev Gets the proxy id associated with the proxy address. - /// @return Proxy id. - function getProxyId() - external - pure - returns (bytes4); -} diff --git a/contracts/core/contracts/protocol/AssetProxy/interfaces/IAuthorizable.sol b/contracts/core/contracts/protocol/AssetProxy/interfaces/IAuthorizable.sol deleted file mode 100644 index 96ee05dee..000000000 --- a/contracts/core/contracts/protocol/AssetProxy/interfaces/IAuthorizable.sol +++ /dev/null @@ -1,52 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity 0.4.24; - -import "@0x/contracts-utils/contracts/utils/Ownable/IOwnable.sol"; - - -contract IAuthorizable is - IOwnable -{ - /// @dev Authorizes an address. - /// @param target Address to authorize. - function addAuthorizedAddress(address target) - external; - - /// @dev Removes authorizion of an address. - /// @param target Address to remove authorization from. - function removeAuthorizedAddress(address target) - external; - - /// @dev Removes authorizion of an address. - /// @param target Address to remove authorization from. - /// @param index Index of target in authorities array. - function removeAuthorizedAddressAtIndex( - address target, - uint256 index - ) - external; - - /// @dev Gets all authorized addresses. - /// @return Array of authorized addresses. - function getAuthorizedAddresses() - external - view - returns (address[] memory); -} diff --git a/contracts/core/contracts/protocol/AssetProxy/mixins/MAuthorizable.sol b/contracts/core/contracts/protocol/AssetProxy/mixins/MAuthorizable.sol index d63fb7f6d..fe77048ce 100644 --- a/contracts/core/contracts/protocol/AssetProxy/mixins/MAuthorizable.sol +++ b/contracts/core/contracts/protocol/AssetProxy/mixins/MAuthorizable.sol @@ -18,7 +18,7 @@ pragma solidity 0.4.24; -import "../interfaces/IAuthorizable.sol"; +import "@0x/contracts-interfaces/contracts/protocol/AssetProxy/IAuthorizable.sol"; contract MAuthorizable is diff --git a/contracts/core/contracts/protocol/Exchange/MixinAssetProxyDispatcher.sol b/contracts/core/contracts/protocol/Exchange/MixinAssetProxyDispatcher.sol index 02aeb4a13..36ab39b45 100644 --- a/contracts/core/contracts/protocol/Exchange/MixinAssetProxyDispatcher.sol +++ b/contracts/core/contracts/protocol/Exchange/MixinAssetProxyDispatcher.sol @@ -20,7 +20,7 @@ pragma solidity 0.4.24; import "@0x/contracts-utils/contracts/utils/Ownable/Ownable.sol"; import "./mixins/MAssetProxyDispatcher.sol"; -import "../AssetProxy/interfaces/IAssetProxy.sol"; +import "@0x/contracts-interfaces/contracts/protocol/AssetProxy/IAssetProxy.sol"; contract MixinAssetProxyDispatcher is diff --git a/contracts/core/contracts/protocol/Exchange/MixinSignatureValidator.sol b/contracts/core/contracts/protocol/Exchange/MixinSignatureValidator.sol index 711535aa8..80b4c0755 100644 --- a/contracts/core/contracts/protocol/Exchange/MixinSignatureValidator.sol +++ b/contracts/core/contracts/protocol/Exchange/MixinSignatureValidator.sol @@ -22,8 +22,8 @@ import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol"; import "@0x/contracts-utils/contracts/utils/ReentrancyGuard/ReentrancyGuard.sol"; import "./mixins/MSignatureValidator.sol"; import "./mixins/MTransactions.sol"; -import "./interfaces/IWallet.sol"; -import "./interfaces/IValidator.sol"; +import "@0x/contracts-interfaces/contracts/protocol/Exchange/IWallet.sol"; +import "@0x/contracts-interfaces/contracts/protocol/Exchange/IValidator.sol"; contract MixinSignatureValidator is diff --git a/contracts/core/contracts/protocol/Exchange/interfaces/IAssetProxyDispatcher.sol b/contracts/core/contracts/protocol/Exchange/interfaces/IAssetProxyDispatcher.sol deleted file mode 100644 index 8db8d6f6c..000000000 --- a/contracts/core/contracts/protocol/Exchange/interfaces/IAssetProxyDispatcher.sol +++ /dev/null @@ -1,37 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity 0.4.24; - - -contract IAssetProxyDispatcher { - - /// @dev Registers an asset proxy to its asset proxy id. - /// Once an asset proxy is registered, it cannot be unregistered. - /// @param assetProxy Address of new asset proxy to register. - function registerAssetProxy(address assetProxy) - external; - - /// @dev Gets an asset proxy. - /// @param assetProxyId Id of the asset proxy. - /// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered. - function getAssetProxy(bytes4 assetProxyId) - external - view - returns (address); -} diff --git a/contracts/core/contracts/protocol/Exchange/interfaces/IExchange.sol b/contracts/core/contracts/protocol/Exchange/interfaces/IExchange.sol deleted file mode 100644 index b92abba04..000000000 --- a/contracts/core/contracts/protocol/Exchange/interfaces/IExchange.sol +++ /dev/null @@ -1,38 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity 0.4.24; -pragma experimental ABIEncoderV2; - -import "./IExchangeCore.sol"; -import "./IMatchOrders.sol"; -import "./ISignatureValidator.sol"; -import "./ITransactions.sol"; -import "./IAssetProxyDispatcher.sol"; -import "./IWrapperFunctions.sol"; - - -// solhint-disable no-empty-blocks -contract IExchange is - IExchangeCore, - IMatchOrders, - ISignatureValidator, - ITransactions, - IAssetProxyDispatcher, - IWrapperFunctions -{} diff --git a/contracts/core/contracts/protocol/Exchange/interfaces/IExchangeCore.sol b/contracts/core/contracts/protocol/Exchange/interfaces/IExchangeCore.sol deleted file mode 100644 index 0da73529c..000000000 --- a/contracts/core/contracts/protocol/Exchange/interfaces/IExchangeCore.sol +++ /dev/null @@ -1,60 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity 0.4.24; -pragma experimental ABIEncoderV2; - -import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; -import "@0x/contracts-libs/contracts/libs/LibFillResults.sol"; - - -contract IExchangeCore { - - /// @dev Cancels all orders created by makerAddress with a salt less than or equal to the targetOrderEpoch - /// and senderAddress equal to msg.sender (or null address if msg.sender == makerAddress). - /// @param targetOrderEpoch Orders created with a salt less or equal to this value will be cancelled. - function cancelOrdersUpTo(uint256 targetOrderEpoch) - external; - - /// @dev Fills the input order. - /// @param order Order struct containing order specifications. - /// @param takerAssetFillAmount Desired amount of takerAsset to sell. - /// @param signature Proof that order has been created by maker. - /// @return Amounts filled and fees paid by maker and taker. - function fillOrder( - LibOrder.Order memory order, - uint256 takerAssetFillAmount, - bytes memory signature - ) - public - returns (LibFillResults.FillResults memory fillResults); - - /// @dev After calling, the order can not be filled anymore. - /// @param order Order struct containing order specifications. - function cancelOrder(LibOrder.Order memory order) - public; - - /// @dev Gets information about an order: status, hash, and amount filled. - /// @param order Order to gather information on. - /// @return OrderInfo Information about the order and its state. - /// See LibOrder.OrderInfo for a complete description. - function getOrderInfo(LibOrder.Order memory order) - public - view - returns (LibOrder.OrderInfo memory orderInfo); -} diff --git a/contracts/core/contracts/protocol/Exchange/interfaces/IMatchOrders.sol b/contracts/core/contracts/protocol/Exchange/interfaces/IMatchOrders.sol deleted file mode 100644 index b88e158c3..000000000 --- a/contracts/core/contracts/protocol/Exchange/interfaces/IMatchOrders.sol +++ /dev/null @@ -1,44 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ -pragma solidity 0.4.24; -pragma experimental ABIEncoderV2; - -import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; -import "@0x/contracts-libs/contracts/libs/LibFillResults.sol"; - - -contract IMatchOrders { - - /// @dev Match two complementary orders that have a profitable spread. - /// Each order is filled at their respective price point. However, the calculations are - /// carried out as though the orders are both being filled at the right order's price point. - /// The profit made by the left order goes to the taker (who matched the two orders). - /// @param leftOrder First order to match. - /// @param rightOrder Second order to match. - /// @param leftSignature Proof that order was created by the left maker. - /// @param rightSignature Proof that order was created by the right maker. - /// @return matchedFillResults Amounts filled and fees paid by maker and taker of matched orders. - function matchOrders( - LibOrder.Order memory leftOrder, - LibOrder.Order memory rightOrder, - bytes memory leftSignature, - bytes memory rightSignature - ) - public - returns (LibFillResults.MatchedFillResults memory matchedFillResults); -} diff --git a/contracts/core/contracts/protocol/Exchange/interfaces/ISignatureValidator.sol b/contracts/core/contracts/protocol/Exchange/interfaces/ISignatureValidator.sol deleted file mode 100644 index 1fd0eccf0..000000000 --- a/contracts/core/contracts/protocol/Exchange/interfaces/ISignatureValidator.sol +++ /dev/null @@ -1,57 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity 0.4.24; - - -contract ISignatureValidator { - - /// @dev Approves a hash on-chain using any valid signature type. - /// After presigning a hash, the preSign signature type will become valid for that hash and signer. - /// @param signerAddress Address that should have signed the given hash. - /// @param signature Proof that the hash has been signed by signer. - function preSign( - bytes32 hash, - address signerAddress, - bytes signature - ) - external; - - /// @dev Approves/unnapproves a Validator contract to verify signatures on signer's behalf. - /// @param validatorAddress Address of Validator contract. - /// @param approval Approval or disapproval of Validator contract. - function setSignatureValidatorApproval( - address validatorAddress, - bool approval - ) - external; - - /// @dev Verifies that a signature is valid. - /// @param hash Message hash that is signed. - /// @param signerAddress Address of signer. - /// @param signature Proof of signing. - /// @return Validity of order signature. - function isValidSignature( - bytes32 hash, - address signerAddress, - bytes memory signature - ) - public - view - returns (bool isValid); -} diff --git a/contracts/core/contracts/protocol/Exchange/interfaces/ITransactions.sol b/contracts/core/contracts/protocol/Exchange/interfaces/ITransactions.sol deleted file mode 100644 index 4446c55ce..000000000 --- a/contracts/core/contracts/protocol/Exchange/interfaces/ITransactions.sol +++ /dev/null @@ -1,35 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ -pragma solidity 0.4.24; - - -contract ITransactions { - - /// @dev Executes an exchange method call in the context of signer. - /// @param salt Arbitrary number to ensure uniqueness of transaction hash. - /// @param signerAddress Address of transaction signer. - /// @param data AbiV2 encoded calldata. - /// @param signature Proof of signer transaction by signer. - function executeTransaction( - uint256 salt, - address signerAddress, - bytes data, - bytes signature - ) - external; -} diff --git a/contracts/core/contracts/protocol/Exchange/interfaces/IValidator.sol b/contracts/core/contracts/protocol/Exchange/interfaces/IValidator.sol deleted file mode 100644 index 2dd69100c..000000000 --- a/contracts/core/contracts/protocol/Exchange/interfaces/IValidator.sol +++ /dev/null @@ -1,37 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity 0.4.24; - - -contract IValidator { - - /// @dev Verifies that a signature is valid. - /// @param hash Message hash that is signed. - /// @param signerAddress Address that should have signed the given hash. - /// @param signature Proof of signing. - /// @return Validity of order signature. - function isValidSignature( - bytes32 hash, - address signerAddress, - bytes signature - ) - external - view - returns (bool isValid); -} diff --git a/contracts/core/contracts/protocol/Exchange/interfaces/IWallet.sol b/contracts/core/contracts/protocol/Exchange/interfaces/IWallet.sol deleted file mode 100644 index c97161ca6..000000000 --- a/contracts/core/contracts/protocol/Exchange/interfaces/IWallet.sol +++ /dev/null @@ -1,35 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity 0.4.24; - - -contract IWallet { - - /// @dev Verifies that a signature is valid. - /// @param hash Message hash that is signed. - /// @param signature Proof of signing. - /// @return Validity of order signature. - function isValidSignature( - bytes32 hash, - bytes signature - ) - external - view - returns (bool isValid); -} diff --git a/contracts/core/contracts/protocol/Exchange/interfaces/IWrapperFunctions.sol b/contracts/core/contracts/protocol/Exchange/interfaces/IWrapperFunctions.sol deleted file mode 100644 index 833bb7e88..000000000 --- a/contracts/core/contracts/protocol/Exchange/interfaces/IWrapperFunctions.sol +++ /dev/null @@ -1,160 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity 0.4.24; -pragma experimental ABIEncoderV2; - -import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; -import "@0x/contracts-libs/contracts/libs/LibFillResults.sol"; - - -contract IWrapperFunctions { - - /// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled. - /// @param order LibOrder.Order struct containing order specifications. - /// @param takerAssetFillAmount Desired amount of takerAsset to sell. - /// @param signature Proof that order has been created by maker. - function fillOrKillOrder( - LibOrder.Order memory order, - uint256 takerAssetFillAmount, - bytes memory signature - ) - public - returns (LibFillResults.FillResults memory fillResults); - - /// @dev Fills an order with specified parameters and ECDSA signature. - /// Returns false if the transaction would otherwise revert. - /// @param order LibOrder.Order struct containing order specifications. - /// @param takerAssetFillAmount Desired amount of takerAsset to sell. - /// @param signature Proof that order has been created by maker. - /// @return Amounts filled and fees paid by maker and taker. - function fillOrderNoThrow( - LibOrder.Order memory order, - uint256 takerAssetFillAmount, - bytes memory signature - ) - public - returns (LibFillResults.FillResults memory fillResults); - - /// @dev Synchronously executes multiple calls of fillOrder. - /// @param orders Array of order specifications. - /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders. - /// @param signatures Proofs that orders have been created by makers. - /// @return Amounts filled and fees paid by makers and taker. - function batchFillOrders( - LibOrder.Order[] memory orders, - uint256[] memory takerAssetFillAmounts, - bytes[] memory signatures - ) - public - returns (LibFillResults.FillResults memory totalFillResults); - - /// @dev Synchronously executes multiple calls of fillOrKill. - /// @param orders Array of order specifications. - /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders. - /// @param signatures Proofs that orders have been created by makers. - /// @return Amounts filled and fees paid by makers and taker. - function batchFillOrKillOrders( - LibOrder.Order[] memory orders, - uint256[] memory takerAssetFillAmounts, - bytes[] memory signatures - ) - public - returns (LibFillResults.FillResults memory totalFillResults); - - /// @dev Fills an order with specified parameters and ECDSA signature. - /// Returns false if the transaction would otherwise revert. - /// @param orders Array of order specifications. - /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders. - /// @param signatures Proofs that orders have been created by makers. - /// @return Amounts filled and fees paid by makers and taker. - function batchFillOrdersNoThrow( - LibOrder.Order[] memory orders, - uint256[] memory takerAssetFillAmounts, - bytes[] memory signatures - ) - public - returns (LibFillResults.FillResults memory totalFillResults); - - /// @dev Synchronously executes multiple calls of fillOrder until total amount of takerAsset is sold by taker. - /// @param orders Array of order specifications. - /// @param takerAssetFillAmount Desired amount of takerAsset to sell. - /// @param signatures Proofs that orders have been created by makers. - /// @return Amounts filled and fees paid by makers and taker. - function marketSellOrders( - LibOrder.Order[] memory orders, - uint256 takerAssetFillAmount, - bytes[] memory signatures - ) - public - returns (LibFillResults.FillResults memory totalFillResults); - - /// @dev Synchronously executes multiple calls of fillOrder until total amount of takerAsset is sold by taker. - /// Returns false if the transaction would otherwise revert. - /// @param orders Array of order specifications. - /// @param takerAssetFillAmount Desired amount of takerAsset to sell. - /// @param signatures Proofs that orders have been signed by makers. - /// @return Amounts filled and fees paid by makers and taker. - function marketSellOrdersNoThrow( - LibOrder.Order[] memory orders, - uint256 takerAssetFillAmount, - bytes[] memory signatures - ) - public - returns (LibFillResults.FillResults memory totalFillResults); - - /// @dev Synchronously executes multiple calls of fillOrder until total amount of makerAsset is bought by taker. - /// @param orders Array of order specifications. - /// @param makerAssetFillAmount Desired amount of makerAsset to buy. - /// @param signatures Proofs that orders have been signed by makers. - /// @return Amounts filled and fees paid by makers and taker. - function marketBuyOrders( - LibOrder.Order[] memory orders, - uint256 makerAssetFillAmount, - bytes[] memory signatures - ) - public - returns (LibFillResults.FillResults memory totalFillResults); - - /// @dev Synchronously executes multiple fill orders in a single transaction until total amount is bought by taker. - /// Returns false if the transaction would otherwise revert. - /// @param orders Array of order specifications. - /// @param makerAssetFillAmount Desired amount of makerAsset to buy. - /// @param signatures Proofs that orders have been signed by makers. - /// @return Amounts filled and fees paid by makers and taker. - function marketBuyOrdersNoThrow( - LibOrder.Order[] memory orders, - uint256 makerAssetFillAmount, - bytes[] memory signatures - ) - public - returns (LibFillResults.FillResults memory totalFillResults); - - /// @dev Synchronously cancels multiple orders in a single transaction. - /// @param orders Array of order specifications. - function batchCancelOrders(LibOrder.Order[] memory orders) - public; - - /// @dev Fetches information for all passed in orders - /// @param orders Array of order specifications. - /// @return Array of OrderInfo instances that correspond to each order. - function getOrdersInfo(LibOrder.Order[] memory orders) - public - view - returns (LibOrder.OrderInfo[] memory); -} diff --git a/contracts/core/contracts/protocol/Exchange/mixins/MAssetProxyDispatcher.sol b/contracts/core/contracts/protocol/Exchange/mixins/MAssetProxyDispatcher.sol index 0ddfca270..fe3d03326 100644 --- a/contracts/core/contracts/protocol/Exchange/mixins/MAssetProxyDispatcher.sol +++ b/contracts/core/contracts/protocol/Exchange/mixins/MAssetProxyDispatcher.sol @@ -18,7 +18,7 @@ pragma solidity 0.4.24; -import "../interfaces/IAssetProxyDispatcher.sol"; +import "@0x/contracts-interfaces/contracts/protocol/Exchange/IAssetProxyDispatcher.sol"; contract MAssetProxyDispatcher is diff --git a/contracts/core/contracts/protocol/Exchange/mixins/MExchangeCore.sol b/contracts/core/contracts/protocol/Exchange/mixins/MExchangeCore.sol index 099bdcc33..215284900 100644 --- a/contracts/core/contracts/protocol/Exchange/mixins/MExchangeCore.sol +++ b/contracts/core/contracts/protocol/Exchange/mixins/MExchangeCore.sol @@ -21,7 +21,7 @@ pragma experimental ABIEncoderV2; import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; import "@0x/contracts-libs/contracts/libs/LibFillResults.sol"; -import "../interfaces/IExchangeCore.sol"; +import "@0x/contracts-interfaces/contracts/protocol/Exchange/IExchangeCore.sol"; contract MExchangeCore is diff --git a/contracts/core/contracts/protocol/Exchange/mixins/MMatchOrders.sol b/contracts/core/contracts/protocol/Exchange/mixins/MMatchOrders.sol index bb285de03..1eb4be329 100644 --- a/contracts/core/contracts/protocol/Exchange/mixins/MMatchOrders.sol +++ b/contracts/core/contracts/protocol/Exchange/mixins/MMatchOrders.sol @@ -20,7 +20,7 @@ pragma experimental ABIEncoderV2; import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; import "@0x/contracts-libs/contracts/libs/LibFillResults.sol"; -import "../interfaces/IMatchOrders.sol"; +import "@0x/contracts-interfaces/contracts/protocol/Exchange/IMatchOrders.sol"; contract MMatchOrders is diff --git a/contracts/core/contracts/protocol/Exchange/mixins/MSignatureValidator.sol b/contracts/core/contracts/protocol/Exchange/mixins/MSignatureValidator.sol index 1fe88b908..a933976d1 100644 --- a/contracts/core/contracts/protocol/Exchange/mixins/MSignatureValidator.sol +++ b/contracts/core/contracts/protocol/Exchange/mixins/MSignatureValidator.sol @@ -18,7 +18,7 @@ pragma solidity 0.4.24; -import "../interfaces/ISignatureValidator.sol"; +import "@0x/contracts-interfaces/contracts/protocol/Exchange/ISignatureValidator.sol"; contract MSignatureValidator is diff --git a/contracts/core/contracts/protocol/Exchange/mixins/MTransactions.sol b/contracts/core/contracts/protocol/Exchange/mixins/MTransactions.sol index 4f61a4945..a6b0fdc85 100644 --- a/contracts/core/contracts/protocol/Exchange/mixins/MTransactions.sol +++ b/contracts/core/contracts/protocol/Exchange/mixins/MTransactions.sol @@ -17,7 +17,7 @@ */ pragma solidity 0.4.24; -import "../interfaces/ITransactions.sol"; +import "@0x/contracts-interfaces/contracts/protocol/Exchange/ITransactions.sol"; contract MTransactions is diff --git a/contracts/core/contracts/protocol/Exchange/mixins/MWrapperFunctions.sol b/contracts/core/contracts/protocol/Exchange/mixins/MWrapperFunctions.sol index 2d21bf057..101e7cb82 100644 --- a/contracts/core/contracts/protocol/Exchange/mixins/MWrapperFunctions.sol +++ b/contracts/core/contracts/protocol/Exchange/mixins/MWrapperFunctions.sol @@ -21,7 +21,7 @@ pragma experimental ABIEncoderV2; import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; import "@0x/contracts-libs/contracts/libs/LibFillResults.sol"; -import "../interfaces/IWrapperFunctions.sol"; +import "@0x/contracts-interfaces/contracts/protocol/Exchange/IWrapperFunctions.sol"; contract MWrapperFunctions is diff --git a/contracts/core/contracts/test/ReentrantERC20Token/ReentrantERC20Token.sol b/contracts/core/contracts/test/ReentrantERC20Token/ReentrantERC20Token.sol index 8e077e3e8..d8c65929d 100644 --- a/contracts/core/contracts/test/ReentrantERC20Token/ReentrantERC20Token.sol +++ b/contracts/core/contracts/test/ReentrantERC20Token/ReentrantERC20Token.sol @@ -21,7 +21,7 @@ pragma experimental ABIEncoderV2; import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol"; import "../../tokens/ERC20Token/ERC20Token.sol"; -import "../../protocol/Exchange/interfaces/IExchange.sol"; +import "@0x/contracts-interfaces/contracts/protocol/Exchange/IExchange.sol"; import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; diff --git a/contracts/core/package.json b/contracts/core/package.json index 43fa9370e..be2214177 100644 --- a/contracts/core/package.json +++ b/contracts/core/package.json @@ -33,7 +33,7 @@ "lint-contracts": "solhint contracts/**/**/**/**/*.sol" }, "config": { - "abis": "generated-artifacts/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyMultipleReturnERC20Token|DummyNoReturnERC20Token|DutchAuction|ERC20Token|ERC20Proxy|ERC721Token|ERC721Proxy|Forwarder|Exchange|ExchangeWrapper|IAssetData|IAssetProxy|InvalidERC721Receiver|MixinAuthorizable|MultiAssetProxy|OrderValidator|ReentrantERC20Token|TestAssetProxyOwner|TestAssetProxyDispatcher|TestConstants|TestExchangeInternals|TestLibBytes|TestSignatureValidator|TestStaticCallReceiver|Validator|Wallet|Whitelist|WETH9|ZRXToken).json" + "abis": "generated-artifacts/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyMultipleReturnERC20Token|DummyNoReturnERC20Token|DutchAuction|ERC20Token|ERC20Proxy|ERC721Token|ERC721Proxy|Forwarder|Exchange|ExchangeWrapper|InvalidERC721Receiver|MixinAuthorizable|MultiAssetProxy|OrderValidator|ReentrantERC20Token|TestAssetProxyOwner|TestAssetProxyDispatcher|TestConstants|TestExchangeInternals|TestLibBytes|TestSignatureValidator|TestStaticCallReceiver|Validator|Wallet|Whitelist|WETH9|ZRXToken).json" }, "repository": { "type": "git", @@ -77,6 +77,7 @@ "@0x/contracts-multisig": "^1.0.0", "@0x/contracts-utils": "^1.0.0", "@0x/contracts-libs": "^1.0.0", + "@0x/contracts-interfaces": "^1.0.0", "@0x/types": "^1.3.0", "@0x/typescript-typings": "^3.0.4", "@0x/utils": "^2.0.6", diff --git a/contracts/core/src/artifacts/index.ts b/contracts/core/src/artifacts/index.ts index d578c36fe..1d868bae2 100644 --- a/contracts/core/src/artifacts/index.ts +++ b/contracts/core/src/artifacts/index.ts @@ -14,11 +14,7 @@ import * as ERC721Token from '../../generated-artifacts/ERC721Token.json'; import * as Exchange from '../../generated-artifacts/Exchange.json'; import * as ExchangeWrapper from '../../generated-artifacts/ExchangeWrapper.json'; import * as Forwarder from '../../generated-artifacts/Forwarder.json'; -import * as IAssetData from '../../generated-artifacts/IAssetData.json'; -import * as IAssetProxy from '../../generated-artifacts/IAssetProxy.json'; import * as InvalidERC721Receiver from '../../generated-artifacts/InvalidERC721Receiver.json'; -import * as IValidator from '../../generated-artifacts/IValidator.json'; -import * as IWallet from '../../generated-artifacts/IWallet.json'; import * as MixinAuthorizable from '../../generated-artifacts/MixinAuthorizable.json'; import * as MultiAssetProxy from '../../generated-artifacts/MultiAssetProxy.json'; import * as OrderValidator from '../../generated-artifacts/OrderValidator.json'; @@ -49,10 +45,6 @@ export const artifacts = { Exchange: Exchange as ContractArtifact, ExchangeWrapper: ExchangeWrapper as ContractArtifact, Forwarder: Forwarder as ContractArtifact, - IAssetData: IAssetData as ContractArtifact, - IAssetProxy: IAssetProxy as ContractArtifact, - IValidator: IValidator as ContractArtifact, - IWallet: IWallet as ContractArtifact, InvalidERC721Receiver: InvalidERC721Receiver as ContractArtifact, MixinAuthorizable: MixinAuthorizable as ContractArtifact, MultiAssetProxy: MultiAssetProxy as ContractArtifact, diff --git a/contracts/core/src/wrappers/index.ts b/contracts/core/src/wrappers/index.ts index ed9d8ef47..92e2f6b9f 100644 --- a/contracts/core/src/wrappers/index.ts +++ b/contracts/core/src/wrappers/index.ts @@ -12,8 +12,6 @@ export * from '../../generated-wrappers/erc721_token'; export * from '../../generated-wrappers/exchange'; export * from '../../generated-wrappers/exchange_wrapper'; export * from '../../generated-wrappers/forwarder'; -export * from '../../generated-wrappers/i_asset_data'; -export * from '../../generated-wrappers/i_asset_proxy'; export * from '../../generated-wrappers/invalid_erc721_receiver'; export * from '../../generated-wrappers/mixin_authorizable'; export * from '../../generated-wrappers/order_validator'; diff --git a/contracts/core/test/asset_proxy/proxies.ts b/contracts/core/test/asset_proxy/proxies.ts index 2527b0fbf..faa643924 100644 --- a/contracts/core/test/asset_proxy/proxies.ts +++ b/contracts/core/test/asset_proxy/proxies.ts @@ -1,3 +1,4 @@ +import { artifacts as interfacesArtifacts, IAssetDataContract, IAssetProxyContract } from '@0x/contracts-interfaces'; import { chaiSetup, constants, @@ -22,8 +23,6 @@ import { DummyMultipleReturnERC20TokenContract } from '../../generated-wrappers/ import { DummyNoReturnERC20TokenContract } from '../../generated-wrappers/dummy_no_return_erc20_token'; import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy'; import { ERC721ProxyContract } from '../../generated-wrappers/erc721_proxy'; -import { IAssetDataContract } from '../../generated-wrappers/i_asset_data'; -import { IAssetProxyContract } from '../../generated-wrappers/i_asset_proxy'; import { MultiAssetProxyContract } from '../../generated-wrappers/multi_asset_proxy'; import { artifacts } from '../../src/artifacts'; import { ERC20Wrapper } from '../utils/erc20_wrapper'; @@ -33,12 +32,12 @@ chaiSetup.configure(); const expect = chai.expect; const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); const assetProxyInterface = new IAssetProxyContract( - artifacts.IAssetProxy.compilerOutput.abi, + interfacesArtifacts.IAssetProxy.compilerOutput.abi, constants.NULL_ADDRESS, provider, ); const assetDataInterface = new IAssetDataContract( - artifacts.IAssetData.compilerOutput.abi, + interfacesArtifacts.IAssetData.compilerOutput.abi, constants.NULL_ADDRESS, provider, ); diff --git a/contracts/core/test/exchange/core.ts b/contracts/core/test/exchange/core.ts index fd6b9ee6b..45eae0f22 100644 --- a/contracts/core/test/exchange/core.ts +++ b/contracts/core/test/exchange/core.ts @@ -1,3 +1,4 @@ +import { artifacts as interfacesArtifacts, IAssetDataContract } from '@0x/contracts-interfaces'; import { chaiSetup, constants, @@ -27,7 +28,6 @@ import { DummyNoReturnERC20TokenContract } from '../../generated-wrappers/dummy_ import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy'; import { ERC721ProxyContract } from '../../generated-wrappers/erc721_proxy'; import { ExchangeCancelEventArgs, ExchangeContract } from '../../generated-wrappers/exchange'; -import { IAssetDataContract } from '../../generated-wrappers/i_asset_data'; import { MultiAssetProxyContract } from '../../generated-wrappers/multi_asset_proxy'; import { ReentrantERC20TokenContract } from '../../generated-wrappers/reentrant_erc20_token'; import { TestStaticCallReceiverContract } from '../../generated-wrappers/test_static_call_receiver'; @@ -40,7 +40,7 @@ chaiSetup.configure(); const expect = chai.expect; const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); const assetDataInterface = new IAssetDataContract( - artifacts.IAssetData.compilerOutput.abi, + interfacesArtifacts.IAssetData.compilerOutput.abi, constants.NULL_ADDRESS, provider, ); diff --git a/contracts/core/tsconfig.json b/contracts/core/tsconfig.json index f2f3c4e97..ca3e6c15f 100644 --- a/contracts/core/tsconfig.json +++ b/contracts/core/tsconfig.json @@ -21,11 +21,6 @@ "./generated-artifacts/Exchange.json", "./generated-artifacts/ExchangeWrapper.json", "./generated-artifacts/Forwarder.json", - "./generated-artifacts/IAssetData.json", - "./generated-artifacts/IAssetProxy.json", - "./generated-artifacts/IValidator.json", - "./generated-artifacts/IWallet.json", - "./generated-artifacts/InvalidERC721Receiver.json", "./generated-artifacts/MixinAuthorizable.json", "./generated-artifacts/MultiAssetProxy.json", "./generated-artifacts/OrderValidator.json", @@ -39,6 +34,7 @@ "./generated-artifacts/WETH9.json", "./generated-artifacts/Wallet.json", "./generated-artifacts/Whitelist.json", + "./generated-artifacts/InvalidERC721Receiver.json", "./generated-artifacts/ZRXToken.json" ], "exclude": ["./deploy/solc/solc_bin"] diff --git a/contracts/interfaces/.solhint.json b/contracts/interfaces/.solhint.json new file mode 100644 index 000000000..076afe9f3 --- /dev/null +++ b/contracts/interfaces/.solhint.json @@ -0,0 +1,20 @@ +{ + "extends": "default", + "rules": { + "avoid-low-level-calls": false, + "avoid-tx-origin": "warn", + "bracket-align": false, + "code-complexity": false, + "const-name-snakecase": "error", + "expression-indent": "error", + "function-max-lines": false, + "func-order": "error", + "indent": ["error", 4], + "max-line-length": ["warn", 160], + "no-inline-assembly": false, + "quotes": ["error", "double"], + "separate-by-one-line-in-contract": "error", + "space-after-comma": "error", + "statement-indent": "error" + } +} diff --git a/contracts/interfaces/README.md b/contracts/interfaces/README.md new file mode 100644 index 000000000..7e1c42c7b --- /dev/null +++ b/contracts/interfaces/README.md @@ -0,0 +1,72 @@ +## Contracts interfaces + +Smart contract interfaces of the 0x protocol. + +## Usage + +Contracts that make up and interact with version 2.0.0 of the protocol can be found in the [contracts](./contracts) directory. The contents of this directory are broken down into the following subdirectories: + +* [protocol](./contracts/protocol) + * This directory contains the contract interfaces that make up version 2.0.0. A full specification can be found [here](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). + +## Bug bounty + +A bug bounty for the 2.0.0 contracts is ongoing! Instructions can be found [here](https://0xproject.com/wiki#Bug-Bounty). + +## Contributing + +We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository. + +For proposals regarding the 0x protocol's smart contract architecture, message format, or additional functionality, go to the [0x Improvement Proposals (ZEIPs)](https://github.com/0xProject/ZEIPs) repository and follow the contribution guidelines provided therein. + +Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. + +### Install Dependencies + +If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them: + +```bash +yarn config set workspaces-experimental true +``` + +Then install dependencies + +```bash +yarn install +``` + +### Build + +To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory: + +```bash +PKG=@0x/contracts-interfaces yarn build +``` + +Or continuously rebuild on change: + +```bash +PKG=@0x/contracts-interfaces yarn watch +``` + +### Clean + +```bash +yarn clean +``` + +### Lint + +```bash +yarn lint +``` + +### Run Tests + +```bash +yarn test +``` + +#### Testing options + +Contracts testing options like coverage, profiling, revert traces or backing node choosing - are described [here](../TESTING.md). diff --git a/contracts/interfaces/compiler.json b/contracts/interfaces/compiler.json new file mode 100644 index 000000000..38a232541 --- /dev/null +++ b/contracts/interfaces/compiler.json @@ -0,0 +1,35 @@ +{ + "artifactsDir": "./generated-artifacts", + "contractsDir": "./contracts", + "compilerSettings": { + "optimizer": { + "enabled": true, + "runs": 1000000 + }, + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode.object", + "evm.bytecode.sourceMap", + "evm.deployedBytecode.object", + "evm.deployedBytecode.sourceMap" + ] + } + } + }, + "contracts": [ + "IAssetData", + "IAssetProxy", + "IAuthorizable", + "IAssetProxyDispatcher", + "IExchange", + "IExchangeCore", + "IMatchOrders", + "ISignatureValidator", + "ITransactions", + "IValidator", + "IWallet", + "IWrapperFunctions" + ] +} diff --git a/contracts/interfaces/contracts/protocol/AssetProxy/IAssetData.sol b/contracts/interfaces/contracts/protocol/AssetProxy/IAssetData.sol new file mode 100644 index 000000000..e2da68919 --- /dev/null +++ b/contracts/interfaces/contracts/protocol/AssetProxy/IAssetData.sol @@ -0,0 +1,44 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +// solhint-disable +pragma solidity 0.4.24; +pragma experimental ABIEncoderV2; + + +// @dev Interface of the asset proxy's assetData. +// The asset proxies take an ABI encoded `bytes assetData` as argument. +// This argument is ABI encoded as one of the methods of this interface. +interface IAssetData { + + function ERC20Token(address tokenContract) + external; + + function ERC721Token( + address tokenContract, + uint256 tokenId + ) + external; + + function MultiAsset( + uint256[] amounts, + bytes[] nestedAssetData + ) + external; + +} diff --git a/contracts/interfaces/contracts/protocol/AssetProxy/IAssetProxy.sol b/contracts/interfaces/contracts/protocol/AssetProxy/IAssetProxy.sol new file mode 100644 index 000000000..b25d2d75a --- /dev/null +++ b/contracts/interfaces/contracts/protocol/AssetProxy/IAssetProxy.sol @@ -0,0 +1,46 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; + +import "./IAuthorizable.sol"; + + +contract IAssetProxy is + IAuthorizable +{ + /// @dev Transfers assets. Either succeeds or throws. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param from Address to transfer asset from. + /// @param to Address to transfer asset to. + /// @param amount Amount of asset to transfer. + function transferFrom( + bytes assetData, + address from, + address to, + uint256 amount + ) + external; + + /// @dev Gets the proxy id associated with the proxy address. + /// @return Proxy id. + function getProxyId() + external + pure + returns (bytes4); +} diff --git a/contracts/interfaces/contracts/protocol/AssetProxy/IAuthorizable.sol b/contracts/interfaces/contracts/protocol/AssetProxy/IAuthorizable.sol new file mode 100644 index 000000000..96ee05dee --- /dev/null +++ b/contracts/interfaces/contracts/protocol/AssetProxy/IAuthorizable.sol @@ -0,0 +1,52 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; + +import "@0x/contracts-utils/contracts/utils/Ownable/IOwnable.sol"; + + +contract IAuthorizable is + IOwnable +{ + /// @dev Authorizes an address. + /// @param target Address to authorize. + function addAuthorizedAddress(address target) + external; + + /// @dev Removes authorizion of an address. + /// @param target Address to remove authorization from. + function removeAuthorizedAddress(address target) + external; + + /// @dev Removes authorizion of an address. + /// @param target Address to remove authorization from. + /// @param index Index of target in authorities array. + function removeAuthorizedAddressAtIndex( + address target, + uint256 index + ) + external; + + /// @dev Gets all authorized addresses. + /// @return Array of authorized addresses. + function getAuthorizedAddresses() + external + view + returns (address[] memory); +} diff --git a/contracts/interfaces/contracts/protocol/Exchange/IAssetProxyDispatcher.sol b/contracts/interfaces/contracts/protocol/Exchange/IAssetProxyDispatcher.sol new file mode 100644 index 000000000..8db8d6f6c --- /dev/null +++ b/contracts/interfaces/contracts/protocol/Exchange/IAssetProxyDispatcher.sol @@ -0,0 +1,37 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; + + +contract IAssetProxyDispatcher { + + /// @dev Registers an asset proxy to its asset proxy id. + /// Once an asset proxy is registered, it cannot be unregistered. + /// @param assetProxy Address of new asset proxy to register. + function registerAssetProxy(address assetProxy) + external; + + /// @dev Gets an asset proxy. + /// @param assetProxyId Id of the asset proxy. + /// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered. + function getAssetProxy(bytes4 assetProxyId) + external + view + returns (address); +} diff --git a/contracts/interfaces/contracts/protocol/Exchange/IExchange.sol b/contracts/interfaces/contracts/protocol/Exchange/IExchange.sol new file mode 100644 index 000000000..b92abba04 --- /dev/null +++ b/contracts/interfaces/contracts/protocol/Exchange/IExchange.sol @@ -0,0 +1,38 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; +pragma experimental ABIEncoderV2; + +import "./IExchangeCore.sol"; +import "./IMatchOrders.sol"; +import "./ISignatureValidator.sol"; +import "./ITransactions.sol"; +import "./IAssetProxyDispatcher.sol"; +import "./IWrapperFunctions.sol"; + + +// solhint-disable no-empty-blocks +contract IExchange is + IExchangeCore, + IMatchOrders, + ISignatureValidator, + ITransactions, + IAssetProxyDispatcher, + IWrapperFunctions +{} diff --git a/contracts/interfaces/contracts/protocol/Exchange/IExchangeCore.sol b/contracts/interfaces/contracts/protocol/Exchange/IExchangeCore.sol new file mode 100644 index 000000000..0da73529c --- /dev/null +++ b/contracts/interfaces/contracts/protocol/Exchange/IExchangeCore.sol @@ -0,0 +1,60 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; +pragma experimental ABIEncoderV2; + +import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; +import "@0x/contracts-libs/contracts/libs/LibFillResults.sol"; + + +contract IExchangeCore { + + /// @dev Cancels all orders created by makerAddress with a salt less than or equal to the targetOrderEpoch + /// and senderAddress equal to msg.sender (or null address if msg.sender == makerAddress). + /// @param targetOrderEpoch Orders created with a salt less or equal to this value will be cancelled. + function cancelOrdersUpTo(uint256 targetOrderEpoch) + external; + + /// @dev Fills the input order. + /// @param order Order struct containing order specifications. + /// @param takerAssetFillAmount Desired amount of takerAsset to sell. + /// @param signature Proof that order has been created by maker. + /// @return Amounts filled and fees paid by maker and taker. + function fillOrder( + LibOrder.Order memory order, + uint256 takerAssetFillAmount, + bytes memory signature + ) + public + returns (LibFillResults.FillResults memory fillResults); + + /// @dev After calling, the order can not be filled anymore. + /// @param order Order struct containing order specifications. + function cancelOrder(LibOrder.Order memory order) + public; + + /// @dev Gets information about an order: status, hash, and amount filled. + /// @param order Order to gather information on. + /// @return OrderInfo Information about the order and its state. + /// See LibOrder.OrderInfo for a complete description. + function getOrderInfo(LibOrder.Order memory order) + public + view + returns (LibOrder.OrderInfo memory orderInfo); +} diff --git a/contracts/interfaces/contracts/protocol/Exchange/IMatchOrders.sol b/contracts/interfaces/contracts/protocol/Exchange/IMatchOrders.sol new file mode 100644 index 000000000..b88e158c3 --- /dev/null +++ b/contracts/interfaces/contracts/protocol/Exchange/IMatchOrders.sol @@ -0,0 +1,44 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +pragma solidity 0.4.24; +pragma experimental ABIEncoderV2; + +import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; +import "@0x/contracts-libs/contracts/libs/LibFillResults.sol"; + + +contract IMatchOrders { + + /// @dev Match two complementary orders that have a profitable spread. + /// Each order is filled at their respective price point. However, the calculations are + /// carried out as though the orders are both being filled at the right order's price point. + /// The profit made by the left order goes to the taker (who matched the two orders). + /// @param leftOrder First order to match. + /// @param rightOrder Second order to match. + /// @param leftSignature Proof that order was created by the left maker. + /// @param rightSignature Proof that order was created by the right maker. + /// @return matchedFillResults Amounts filled and fees paid by maker and taker of matched orders. + function matchOrders( + LibOrder.Order memory leftOrder, + LibOrder.Order memory rightOrder, + bytes memory leftSignature, + bytes memory rightSignature + ) + public + returns (LibFillResults.MatchedFillResults memory matchedFillResults); +} diff --git a/contracts/interfaces/contracts/protocol/Exchange/ISignatureValidator.sol b/contracts/interfaces/contracts/protocol/Exchange/ISignatureValidator.sol new file mode 100644 index 000000000..1fd0eccf0 --- /dev/null +++ b/contracts/interfaces/contracts/protocol/Exchange/ISignatureValidator.sol @@ -0,0 +1,57 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; + + +contract ISignatureValidator { + + /// @dev Approves a hash on-chain using any valid signature type. + /// After presigning a hash, the preSign signature type will become valid for that hash and signer. + /// @param signerAddress Address that should have signed the given hash. + /// @param signature Proof that the hash has been signed by signer. + function preSign( + bytes32 hash, + address signerAddress, + bytes signature + ) + external; + + /// @dev Approves/unnapproves a Validator contract to verify signatures on signer's behalf. + /// @param validatorAddress Address of Validator contract. + /// @param approval Approval or disapproval of Validator contract. + function setSignatureValidatorApproval( + address validatorAddress, + bool approval + ) + external; + + /// @dev Verifies that a signature is valid. + /// @param hash Message hash that is signed. + /// @param signerAddress Address of signer. + /// @param signature Proof of signing. + /// @return Validity of order signature. + function isValidSignature( + bytes32 hash, + address signerAddress, + bytes memory signature + ) + public + view + returns (bool isValid); +} diff --git a/contracts/interfaces/contracts/protocol/Exchange/ITransactions.sol b/contracts/interfaces/contracts/protocol/Exchange/ITransactions.sol new file mode 100644 index 000000000..4446c55ce --- /dev/null +++ b/contracts/interfaces/contracts/protocol/Exchange/ITransactions.sol @@ -0,0 +1,35 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +pragma solidity 0.4.24; + + +contract ITransactions { + + /// @dev Executes an exchange method call in the context of signer. + /// @param salt Arbitrary number to ensure uniqueness of transaction hash. + /// @param signerAddress Address of transaction signer. + /// @param data AbiV2 encoded calldata. + /// @param signature Proof of signer transaction by signer. + function executeTransaction( + uint256 salt, + address signerAddress, + bytes data, + bytes signature + ) + external; +} diff --git a/contracts/interfaces/contracts/protocol/Exchange/IValidator.sol b/contracts/interfaces/contracts/protocol/Exchange/IValidator.sol new file mode 100644 index 000000000..2dd69100c --- /dev/null +++ b/contracts/interfaces/contracts/protocol/Exchange/IValidator.sol @@ -0,0 +1,37 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; + + +contract IValidator { + + /// @dev Verifies that a signature is valid. + /// @param hash Message hash that is signed. + /// @param signerAddress Address that should have signed the given hash. + /// @param signature Proof of signing. + /// @return Validity of order signature. + function isValidSignature( + bytes32 hash, + address signerAddress, + bytes signature + ) + external + view + returns (bool isValid); +} diff --git a/contracts/interfaces/contracts/protocol/Exchange/IWallet.sol b/contracts/interfaces/contracts/protocol/Exchange/IWallet.sol new file mode 100644 index 000000000..c97161ca6 --- /dev/null +++ b/contracts/interfaces/contracts/protocol/Exchange/IWallet.sol @@ -0,0 +1,35 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; + + +contract IWallet { + + /// @dev Verifies that a signature is valid. + /// @param hash Message hash that is signed. + /// @param signature Proof of signing. + /// @return Validity of order signature. + function isValidSignature( + bytes32 hash, + bytes signature + ) + external + view + returns (bool isValid); +} diff --git a/contracts/interfaces/contracts/protocol/Exchange/IWrapperFunctions.sol b/contracts/interfaces/contracts/protocol/Exchange/IWrapperFunctions.sol new file mode 100644 index 000000000..833bb7e88 --- /dev/null +++ b/contracts/interfaces/contracts/protocol/Exchange/IWrapperFunctions.sol @@ -0,0 +1,160 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; +pragma experimental ABIEncoderV2; + +import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; +import "@0x/contracts-libs/contracts/libs/LibFillResults.sol"; + + +contract IWrapperFunctions { + + /// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled. + /// @param order LibOrder.Order struct containing order specifications. + /// @param takerAssetFillAmount Desired amount of takerAsset to sell. + /// @param signature Proof that order has been created by maker. + function fillOrKillOrder( + LibOrder.Order memory order, + uint256 takerAssetFillAmount, + bytes memory signature + ) + public + returns (LibFillResults.FillResults memory fillResults); + + /// @dev Fills an order with specified parameters and ECDSA signature. + /// Returns false if the transaction would otherwise revert. + /// @param order LibOrder.Order struct containing order specifications. + /// @param takerAssetFillAmount Desired amount of takerAsset to sell. + /// @param signature Proof that order has been created by maker. + /// @return Amounts filled and fees paid by maker and taker. + function fillOrderNoThrow( + LibOrder.Order memory order, + uint256 takerAssetFillAmount, + bytes memory signature + ) + public + returns (LibFillResults.FillResults memory fillResults); + + /// @dev Synchronously executes multiple calls of fillOrder. + /// @param orders Array of order specifications. + /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders. + /// @param signatures Proofs that orders have been created by makers. + /// @return Amounts filled and fees paid by makers and taker. + function batchFillOrders( + LibOrder.Order[] memory orders, + uint256[] memory takerAssetFillAmounts, + bytes[] memory signatures + ) + public + returns (LibFillResults.FillResults memory totalFillResults); + + /// @dev Synchronously executes multiple calls of fillOrKill. + /// @param orders Array of order specifications. + /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders. + /// @param signatures Proofs that orders have been created by makers. + /// @return Amounts filled and fees paid by makers and taker. + function batchFillOrKillOrders( + LibOrder.Order[] memory orders, + uint256[] memory takerAssetFillAmounts, + bytes[] memory signatures + ) + public + returns (LibFillResults.FillResults memory totalFillResults); + + /// @dev Fills an order with specified parameters and ECDSA signature. + /// Returns false if the transaction would otherwise revert. + /// @param orders Array of order specifications. + /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders. + /// @param signatures Proofs that orders have been created by makers. + /// @return Amounts filled and fees paid by makers and taker. + function batchFillOrdersNoThrow( + LibOrder.Order[] memory orders, + uint256[] memory takerAssetFillAmounts, + bytes[] memory signatures + ) + public + returns (LibFillResults.FillResults memory totalFillResults); + + /// @dev Synchronously executes multiple calls of fillOrder until total amount of takerAsset is sold by taker. + /// @param orders Array of order specifications. + /// @param takerAssetFillAmount Desired amount of takerAsset to sell. + /// @param signatures Proofs that orders have been created by makers. + /// @return Amounts filled and fees paid by makers and taker. + function marketSellOrders( + LibOrder.Order[] memory orders, + uint256 takerAssetFillAmount, + bytes[] memory signatures + ) + public + returns (LibFillResults.FillResults memory totalFillResults); + + /// @dev Synchronously executes multiple calls of fillOrder until total amount of takerAsset is sold by taker. + /// Returns false if the transaction would otherwise revert. + /// @param orders Array of order specifications. + /// @param takerAssetFillAmount Desired amount of takerAsset to sell. + /// @param signatures Proofs that orders have been signed by makers. + /// @return Amounts filled and fees paid by makers and taker. + function marketSellOrdersNoThrow( + LibOrder.Order[] memory orders, + uint256 takerAssetFillAmount, + bytes[] memory signatures + ) + public + returns (LibFillResults.FillResults memory totalFillResults); + + /// @dev Synchronously executes multiple calls of fillOrder until total amount of makerAsset is bought by taker. + /// @param orders Array of order specifications. + /// @param makerAssetFillAmount Desired amount of makerAsset to buy. + /// @param signatures Proofs that orders have been signed by makers. + /// @return Amounts filled and fees paid by makers and taker. + function marketBuyOrders( + LibOrder.Order[] memory orders, + uint256 makerAssetFillAmount, + bytes[] memory signatures + ) + public + returns (LibFillResults.FillResults memory totalFillResults); + + /// @dev Synchronously executes multiple fill orders in a single transaction until total amount is bought by taker. + /// Returns false if the transaction would otherwise revert. + /// @param orders Array of order specifications. + /// @param makerAssetFillAmount Desired amount of makerAsset to buy. + /// @param signatures Proofs that orders have been signed by makers. + /// @return Amounts filled and fees paid by makers and taker. + function marketBuyOrdersNoThrow( + LibOrder.Order[] memory orders, + uint256 makerAssetFillAmount, + bytes[] memory signatures + ) + public + returns (LibFillResults.FillResults memory totalFillResults); + + /// @dev Synchronously cancels multiple orders in a single transaction. + /// @param orders Array of order specifications. + function batchCancelOrders(LibOrder.Order[] memory orders) + public; + + /// @dev Fetches information for all passed in orders + /// @param orders Array of order specifications. + /// @return Array of OrderInfo instances that correspond to each order. + function getOrdersInfo(LibOrder.Order[] memory orders) + public + view + returns (LibOrder.OrderInfo[] memory); +} diff --git a/contracts/interfaces/package.json b/contracts/interfaces/package.json new file mode 100644 index 000000000..b79ae20ff --- /dev/null +++ b/contracts/interfaces/package.json @@ -0,0 +1,57 @@ +{ + "private": true, + "name": "@0x/contracts-interfaces", + "version": "1.0.0", + "engines": { + "node": ">=6.12" + }, + "description": "Smart contract interfaces of 0x protocol", + "main": "lib/src/index.js", + "scripts": { + "build": "yarn pre_build && tsc -b", + "build:ci": "yarn build", + "pre_build": "run-s compile generate_contract_wrappers", + "compile": "sol-compiler --contracts-dir contracts", + "clean": "shx rm -rf lib generated-artifacts generated-wrappers", + "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers", + "lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts", + "lint-contracts": "solhint contracts/**/**/**/**/*.sol" + }, + "config": { + "abis": "generated-artifacts/@(IAssetData|IAssetProxy|IAuthorizable|IAssetProxyDispatcher|IExchange|IExchangeCore|IMatchOrders|ISignatureValidator|ITransactions|IValidator|IWallet|IWrapperFunctions).json" + }, + "repository": { + "type": "git", + "url": "https://github.com/0xProject/0x-monorepo.git" + }, + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/0xProject/0x-monorepo/issues" + }, + "homepage": "https://github.com/0xProject/0x-monorepo/contracts/interfaces/README.md", + "devDependencies": { + "@0x/abi-gen": "^1.0.17", + "@0x/sol-compiler": "^1.1.14", + "@0x/tslint-config": "^1.0.10", + "npm-run-all": "^4.1.2", + "shx": "^0.2.2", + "solhint": "^1.2.1", + "tslint": "5.11.0", + "typescript": "3.0.1", + "yargs": "^10.0.3" + }, + "dependencies": { + "@0x/base-contract": "^3.0.8", + "@0x/contracts-utils": "^1.0.0", + "@0x/contracts-libs": "^1.0.0", + "@0x/types": "^1.3.0", + "@0x/typescript-typings": "^3.0.4", + "@0x/utils": "^2.0.6", + "@0x/web3-wrapper": "^3.1.6", + "ethereum-types": "^1.1.2", + "lodash": "^4.17.5" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/contracts/interfaces/src/artifacts/index.ts b/contracts/interfaces/src/artifacts/index.ts new file mode 100644 index 000000000..5a2a37d8a --- /dev/null +++ b/contracts/interfaces/src/artifacts/index.ts @@ -0,0 +1,29 @@ +import { ContractArtifact } from 'ethereum-types'; + +import * as IAssetData from '../../generated-artifacts/IAssetData.json'; +import * as IAssetProxy from '../../generated-artifacts/IAssetProxy.json'; +import * as IAssetProxyDispatcher from '../../generated-artifacts/IAssetProxyDispatcher.json'; +import * as IAuthorizable from '../../generated-artifacts/IAuthorizable.json'; +import * as IExchange from '../../generated-artifacts/IExchange.json'; +import * as IExchangeCore from '../../generated-artifacts/IExchangeCore.json'; +import * as IMatchOrders from '../../generated-artifacts/IMatchOrders.json'; +import * as ISignatureValidator from '../../generated-artifacts/ISignatureValidator.json'; +import * as ITransactions from '../../generated-artifacts/ITransactions.json'; +import * as IValidator from '../../generated-artifacts/IValidator.json'; +import * as IWallet from '../../generated-artifacts/IWallet.json'; +import * as IWrapperFunctions from '../../generated-artifacts/IWrapperFunctions.json'; + +export const artifacts = { + IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact, + IAuthorizable: IAuthorizable as ContractArtifact, + IExchange: IExchange as ContractArtifact, + IExchangeCore: IExchangeCore as ContractArtifact, + IMatchOrders: IMatchOrders as ContractArtifact, + ISignatureValidator: ISignatureValidator as ContractArtifact, + ITransactions: ITransactions as ContractArtifact, + IWrapperFunctions: IWrapperFunctions as ContractArtifact, + IAssetData: IAssetData as ContractArtifact, + IAssetProxy: IAssetProxy as ContractArtifact, + IValidator: IValidator as ContractArtifact, + IWallet: IWallet as ContractArtifact, +}; diff --git a/contracts/interfaces/src/index.ts b/contracts/interfaces/src/index.ts new file mode 100644 index 000000000..d55f08ea2 --- /dev/null +++ b/contracts/interfaces/src/index.ts @@ -0,0 +1,2 @@ +export * from './artifacts'; +export * from './wrappers'; diff --git a/contracts/interfaces/src/wrappers/index.ts b/contracts/interfaces/src/wrappers/index.ts new file mode 100644 index 000000000..f7f72fada --- /dev/null +++ b/contracts/interfaces/src/wrappers/index.ts @@ -0,0 +1,12 @@ +export * from '../../generated-wrappers/i_asset_data'; +export * from '../../generated-wrappers/i_asset_proxy'; +export * from '../../generated-wrappers/i_asset_proxy_dispatcher'; +export * from '../../generated-wrappers/i_exchange'; +export * from '../../generated-wrappers/i_exchange_core'; +export * from '../../generated-wrappers/i_match_orders'; +export * from '../../generated-wrappers/i_signature_validator'; +export * from '../../generated-wrappers/i_transactions'; +export * from '../../generated-wrappers/i_authorizable'; +export * from '../../generated-wrappers/i_wrapper_functions'; +export * from '../../generated-wrappers/i_validator'; +export * from '../../generated-wrappers/i_wallet'; diff --git a/contracts/interfaces/tsconfig.json b/contracts/interfaces/tsconfig.json new file mode 100644 index 000000000..90b7d41bb --- /dev/null +++ b/contracts/interfaces/tsconfig.json @@ -0,0 +1,24 @@ +{ + "extends": "../../tsconfig", + "compilerOptions": { + "outDir": "lib", + "rootDir": ".", + "resolveJsonModule": true + }, + "include": ["./src/**/*", "./generated-wrappers/**/*"], + "files": [ + "./generated-artifacts/IAssetData.json", + "./generated-artifacts/IAssetProxy.json", + "./generated-artifacts/IAuthorizable.json", + "./generated-artifacts/IAssetProxyDispatcher.json", + "./generated-artifacts/IExchange.json", + "./generated-artifacts/IExchangeCore.json", + "./generated-artifacts/IMatchOrders.json", + "./generated-artifacts/ISignatureValidator.json", + "./generated-artifacts/ITransactions.json", + "./generated-artifacts/IValidator.json", + "./generated-artifacts/IWallet.json", + "./generated-artifacts/IWrapperFunctions.json" + ], + "exclude": ["./deploy/solc/solc_bin"] +} diff --git a/contracts/interfaces/tslint.json b/contracts/interfaces/tslint.json new file mode 100644 index 000000000..1bb3ac2a2 --- /dev/null +++ b/contracts/interfaces/tslint.json @@ -0,0 +1,6 @@ +{ + "extends": ["@0x/tslint-config"], + "rules": { + "custom-no-magic-numbers": false + } +} -- cgit v1.2.3 From a365ab11d24ff8365b7d69a948b1b5211b772f5e Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Wed, 5 Dec 2018 14:23:35 +0100 Subject: Refactor out tokens from core contracts package --- .circleci/config.yml | 2 + .gitignore | 2 + .prettierignore | 2 + contracts/core/.solhintignore | 3 - contracts/core/CHANGELOG.json | 14 - contracts/core/README.md | 4 - contracts/core/compiler.json | 13 +- .../extensions/DutchAuction/DutchAuction.sol | 2 +- .../contracts/extensions/Forwarder/MixinAssets.sol | 4 +- .../extensions/Forwarder/libs/LibConstants.sol | 4 +- .../extensions/OrderValidator/OrderValidator.sol | 4 +- .../test/DummyERC20Token/DummyERC20Token.sol | 77 --- .../DummyMultipleReturnERC20Token.sol | 69 -- .../DummyERC20Token/DummyNoReturnERC20Token.sol | 115 ---- .../DummyERC721Receiver/DummyERC721Receiver.sol | 67 -- .../DummyERC721Receiver/InvalidERC721Receiver.sol | 66 -- .../test/DummyERC721Token/DummyERC721Token.sol | 63 -- .../ReentrantERC20Token/ReentrantERC20Token.sol | 188 ----- .../TestStaticCallReceiver.sol | 2 +- .../contracts/tokens/ERC20Token/ERC20Token.sol | 148 ---- .../contracts/tokens/ERC20Token/IERC20Token.sol | 87 --- .../tokens/ERC20Token/MintableERC20Token.sol | 60 -- .../ERC20Token/UnlimitedAllowanceERC20Token.sol | 70 -- .../contracts/tokens/ERC721Token/ERC721Token.sol | 277 -------- .../tokens/ERC721Token/IERC721Receiver.sol | 44 -- .../contracts/tokens/ERC721Token/IERC721Token.sol | 158 ----- .../tokens/ERC721Token/MintableERC721Token.sol | 82 --- .../contracts/tokens/EtherToken/IEtherToken.sol | 33 - .../core/contracts/tokens/EtherToken/WETH9.sol | 758 --------------------- .../contracts/tokens/ZRXToken/ERC20Token_v1.sol | 44 -- .../core/contracts/tokens/ZRXToken/Token_v1.sol | 39 -- .../tokens/ZRXToken/UnlimitedAllowanceToken_v1.sol | 52 -- .../core/contracts/tokens/ZRXToken/ZRXToken.sol | 41 -- contracts/core/package.json | 3 +- contracts/core/src/artifacts/index.ts | 24 - contracts/core/src/index.ts | 2 + contracts/core/src/wrappers/index.ts | 11 - contracts/core/test/asset_proxy/proxies.ts | 21 +- contracts/core/test/exchange/core.ts | 16 +- contracts/core/test/exchange/dispatcher.ts | 2 +- contracts/core/test/exchange/match_orders.ts | 11 +- .../core/test/exchange/signature_validator.ts | 13 +- contracts/core/test/exchange/transactions.ts | 2 +- contracts/core/test/exchange/wrapper.ts | 11 +- contracts/core/test/extensions/dutch_auction.ts | 11 +- contracts/core/test/extensions/forwarder.ts | 11 +- contracts/core/test/extensions/order_validator.ts | 3 +- contracts/core/test/tokens/erc721_token.ts | 284 -------- .../core/test/tokens/unlimited_allowance_token.ts | 195 ------ contracts/core/test/tokens/weth9.ts | 143 ---- contracts/core/test/tokens/zrx_token.ts | 204 ------ .../core/test/utils/asset_proxy_owner_wrapper.ts | 3 +- contracts/core/test/utils/erc20_wrapper.ts | 4 +- contracts/core/test/utils/erc721_wrapper.ts | 4 +- contracts/core/test/utils/exchange_wrapper.ts | 3 +- contracts/core/test/utils/forwarder_wrapper.ts | 3 +- .../core/test/utils/order_factory_from_scenario.ts | 3 +- contracts/core/tsconfig.json | 13 +- contracts/tokens/.solhint.json | 20 + contracts/tokens/.solhintignore | 3 + contracts/tokens/CHANGELOG.json | 16 + contracts/tokens/README.md | 74 ++ contracts/tokens/compiler.json | 44 ++ .../test/DummyERC20Token/DummyERC20Token.sol | 77 +++ .../DummyMultipleReturnERC20Token.sol | 69 ++ .../DummyERC20Token/DummyNoReturnERC20Token.sol | 115 ++++ .../DummyERC721Receiver/DummyERC721Receiver.sol | 67 ++ .../DummyERC721Receiver/InvalidERC721Receiver.sol | 66 ++ .../test/DummyERC721Token/DummyERC721Token.sol | 63 ++ .../ReentrantERC20Token/ReentrantERC20Token.sol | 188 +++++ .../contracts/tokens/ERC20Token/ERC20Token.sol | 148 ++++ .../contracts/tokens/ERC20Token/IERC20Token.sol | 87 +++ .../tokens/ERC20Token/MintableERC20Token.sol | 60 ++ .../ERC20Token/UnlimitedAllowanceERC20Token.sol | 70 ++ .../contracts/tokens/ERC721Token/ERC721Token.sol | 277 ++++++++ .../tokens/ERC721Token/IERC721Receiver.sol | 44 ++ .../contracts/tokens/ERC721Token/IERC721Token.sol | 158 +++++ .../tokens/ERC721Token/MintableERC721Token.sol | 82 +++ .../contracts/tokens/EtherToken/IEtherToken.sol | 33 + .../tokens/contracts/tokens/EtherToken/WETH9.sol | 758 +++++++++++++++++++++ .../contracts/tokens/ZRXToken/ERC20Token_v1.sol | 44 ++ .../tokens/contracts/tokens/ZRXToken/Token_v1.sol | 39 ++ .../tokens/ZRXToken/UnlimitedAllowanceToken_v1.sol | 52 ++ .../tokens/contracts/tokens/ZRXToken/ZRXToken.sol | 41 ++ contracts/tokens/package.json | 94 +++ contracts/tokens/src/artifacts/index.ts | 49 ++ contracts/tokens/src/index.ts | 2 + contracts/tokens/src/wrappers/index.ts | 21 + contracts/tokens/test/erc721_token.ts | 282 ++++++++ contracts/tokens/test/global_hooks.ts | 17 + contracts/tokens/test/unlimited_allowance_token.ts | 194 ++++++ contracts/tokens/test/weth9.ts | 142 ++++ contracts/tokens/test/zrx_token.ts | 203 ++++++ contracts/tokens/tsconfig.json | 33 + contracts/tokens/tslint.json | 6 + 95 files changed, 3829 insertions(+), 3503 deletions(-) delete mode 100644 contracts/core/.solhintignore delete mode 100644 contracts/core/contracts/test/DummyERC20Token/DummyERC20Token.sol delete mode 100644 contracts/core/contracts/test/DummyERC20Token/DummyMultipleReturnERC20Token.sol delete mode 100644 contracts/core/contracts/test/DummyERC20Token/DummyNoReturnERC20Token.sol delete mode 100644 contracts/core/contracts/test/DummyERC721Receiver/DummyERC721Receiver.sol delete mode 100644 contracts/core/contracts/test/DummyERC721Receiver/InvalidERC721Receiver.sol delete mode 100644 contracts/core/contracts/test/DummyERC721Token/DummyERC721Token.sol delete mode 100644 contracts/core/contracts/test/ReentrantERC20Token/ReentrantERC20Token.sol delete mode 100644 contracts/core/contracts/tokens/ERC20Token/ERC20Token.sol delete mode 100644 contracts/core/contracts/tokens/ERC20Token/IERC20Token.sol delete mode 100644 contracts/core/contracts/tokens/ERC20Token/MintableERC20Token.sol delete mode 100644 contracts/core/contracts/tokens/ERC20Token/UnlimitedAllowanceERC20Token.sol delete mode 100644 contracts/core/contracts/tokens/ERC721Token/ERC721Token.sol delete mode 100644 contracts/core/contracts/tokens/ERC721Token/IERC721Receiver.sol delete mode 100644 contracts/core/contracts/tokens/ERC721Token/IERC721Token.sol delete mode 100644 contracts/core/contracts/tokens/ERC721Token/MintableERC721Token.sol delete mode 100644 contracts/core/contracts/tokens/EtherToken/IEtherToken.sol delete mode 100644 contracts/core/contracts/tokens/EtherToken/WETH9.sol delete mode 100644 contracts/core/contracts/tokens/ZRXToken/ERC20Token_v1.sol delete mode 100644 contracts/core/contracts/tokens/ZRXToken/Token_v1.sol delete mode 100644 contracts/core/contracts/tokens/ZRXToken/UnlimitedAllowanceToken_v1.sol delete mode 100644 contracts/core/contracts/tokens/ZRXToken/ZRXToken.sol create mode 100644 contracts/core/src/index.ts delete mode 100644 contracts/core/test/tokens/erc721_token.ts delete mode 100644 contracts/core/test/tokens/unlimited_allowance_token.ts delete mode 100644 contracts/core/test/tokens/weth9.ts delete mode 100644 contracts/core/test/tokens/zrx_token.ts create mode 100644 contracts/tokens/.solhint.json create mode 100644 contracts/tokens/.solhintignore create mode 100644 contracts/tokens/CHANGELOG.json create mode 100644 contracts/tokens/README.md create mode 100644 contracts/tokens/compiler.json create mode 100644 contracts/tokens/contracts/test/DummyERC20Token/DummyERC20Token.sol create mode 100644 contracts/tokens/contracts/test/DummyERC20Token/DummyMultipleReturnERC20Token.sol create mode 100644 contracts/tokens/contracts/test/DummyERC20Token/DummyNoReturnERC20Token.sol create mode 100644 contracts/tokens/contracts/test/DummyERC721Receiver/DummyERC721Receiver.sol create mode 100644 contracts/tokens/contracts/test/DummyERC721Receiver/InvalidERC721Receiver.sol create mode 100644 contracts/tokens/contracts/test/DummyERC721Token/DummyERC721Token.sol create mode 100644 contracts/tokens/contracts/test/ReentrantERC20Token/ReentrantERC20Token.sol create mode 100644 contracts/tokens/contracts/tokens/ERC20Token/ERC20Token.sol create mode 100644 contracts/tokens/contracts/tokens/ERC20Token/IERC20Token.sol create mode 100644 contracts/tokens/contracts/tokens/ERC20Token/MintableERC20Token.sol create mode 100644 contracts/tokens/contracts/tokens/ERC20Token/UnlimitedAllowanceERC20Token.sol create mode 100644 contracts/tokens/contracts/tokens/ERC721Token/ERC721Token.sol create mode 100644 contracts/tokens/contracts/tokens/ERC721Token/IERC721Receiver.sol create mode 100644 contracts/tokens/contracts/tokens/ERC721Token/IERC721Token.sol create mode 100644 contracts/tokens/contracts/tokens/ERC721Token/MintableERC721Token.sol create mode 100644 contracts/tokens/contracts/tokens/EtherToken/IEtherToken.sol create mode 100644 contracts/tokens/contracts/tokens/EtherToken/WETH9.sol create mode 100644 contracts/tokens/contracts/tokens/ZRXToken/ERC20Token_v1.sol create mode 100644 contracts/tokens/contracts/tokens/ZRXToken/Token_v1.sol create mode 100644 contracts/tokens/contracts/tokens/ZRXToken/UnlimitedAllowanceToken_v1.sol create mode 100644 contracts/tokens/contracts/tokens/ZRXToken/ZRXToken.sol create mode 100644 contracts/tokens/package.json create mode 100644 contracts/tokens/src/artifacts/index.ts create mode 100644 contracts/tokens/src/index.ts create mode 100644 contracts/tokens/src/wrappers/index.ts create mode 100644 contracts/tokens/test/erc721_token.ts create mode 100644 contracts/tokens/test/global_hooks.ts create mode 100644 contracts/tokens/test/unlimited_allowance_token.ts create mode 100644 contracts/tokens/test/weth9.ts create mode 100644 contracts/tokens/test/zrx_token.ts create mode 100644 contracts/tokens/tsconfig.json create mode 100644 contracts/tokens/tslint.json diff --git a/.circleci/config.yml b/.circleci/config.yml index 1ea5aa280..8de8b02c9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -43,6 +43,7 @@ jobs: - run: yarn wsrun test:circleci @0x/contracts-multisig - run: yarn wsrun test:circleci @0x/contracts-utils - run: yarn wsrun test:circleci @0x/contracts-libs + - run: yarn wsrun test:circleci @0x/contracts-tokens - run: yarn wsrun test:circleci @0x/contracts-core test-contracts-geth: docker: @@ -58,6 +59,7 @@ jobs: - run: sleep 10 && TEST_PROVIDER=geth yarn wsrun test @0x/contracts-multisig - run: TEST_PROVIDER=geth yarn wsrun test @0x/contracts-utils - run: TEST_PROVIDER=geth yarn wsrun test @0x/contracts-libs + - run: TEST_PROVIDER=geth yarn wsrun test @0x/contracts-tokens - run: TEST_PROVIDER=geth yarn wsrun test @0x/contracts-core test-publish: resource_class: medium+ diff --git a/.gitignore b/.gitignore index 22db8cf31..a49f2837b 100644 --- a/.gitignore +++ b/.gitignore @@ -84,6 +84,7 @@ contracts/multisig/generated-artifacts/ contracts/utils/generated-artifacts/ contracts/libs/generated-artifacts/ contracts/interfaces/generated-artifacts/ +contracts/tokens/generated-artifacts/ packages/sol-cov/test/fixtures/artifacts/ packages/metacoin/artifacts/ @@ -94,6 +95,7 @@ contracts/multisig/generated-wrappers/ contracts/utils/generated-wrappers/ contracts/libs/generated-wrappers/ contracts/interfaces/generated-wrappers/ +contracts/tokens/generated-wrappers/ packages/metacoin/src/contract_wrappers # solc-bin in sol-compiler diff --git a/.prettierignore b/.prettierignore index 0be17624b..4da37ae6e 100644 --- a/.prettierignore +++ b/.prettierignore @@ -10,6 +10,8 @@ lib /contracts/libs/generated-artifacts /contracts/interfaces/generated-wrappers /contracts/interfaces/generated-artifacts +/contracts/tokens/generated-wrappers +/contracts/tokens/generated-artifacts /packages/abi-gen-wrappers/src/generated-wrappers /packages/contract-artifacts/artifacts /python-packages/order_utils/src/zero_ex/contract_artifacts/artifacts diff --git a/contracts/core/.solhintignore b/contracts/core/.solhintignore deleted file mode 100644 index 1e33ec53b..000000000 --- a/contracts/core/.solhintignore +++ /dev/null @@ -1,3 +0,0 @@ -contracts/tokens/ZRXToken/ERC20Token_v1.sol -contracts/tokens/ZRXToken/Token_v1.sol -contracts/tokens/ZRXToken/UnlimitedAllowanceToken_v1.sol diff --git a/contracts/core/CHANGELOG.json b/contracts/core/CHANGELOG.json index 7dfa06990..371f18cd4 100644 --- a/contracts/core/CHANGELOG.json +++ b/contracts/core/CHANGELOG.json @@ -117,19 +117,5 @@ } } ] - }, - { - "name": "ZRXToken", - "version": "1.0.0", - "changes": [ - { - "note": "protocol v1 deploy", - "networks": { - "1": "0xe41d2489571d322189246dafa5ebde1f4699f498", - "3": "0xff67881f8d12f372d91baae9752eb3631ff0ed00", - "42": "0x2002d3812f58e35f0ea1ffbf80a75a38c32175fa" - } - } - ] } ] diff --git a/contracts/core/README.md b/contracts/core/README.md index 0004925c1..8371d8041 100644 --- a/contracts/core/README.md +++ b/contracts/core/README.md @@ -12,10 +12,6 @@ Contracts that make up and interact with version 2.0.0 of the protocol can be fo * This directory contains contracts that interact with the 2.0.0 contracts and will be used in production, such as the [Forwarder](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/forwarder-specification.md) contract. * [examples](./contracts/examples) * This directory contains example implementations of contracts that interact with the protocol but are _not_ intended for use in production. Examples include [filter](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#filter-contracts) contracts, a [Wallet](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#wallet) contract, and a [Validator](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#validator) contract, among others. -* [tokens](./contracts/tokens) - * This directory contains implementations of different tokens and token standards, including [wETH](https://weth.io/), ZRX, [ERC20](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md), and [ERC721](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md). -* [utils](./contracts/utils) - * This directory contains libraries and utils that are shared across all of the other directories. * [test](./contracts/test) * This directory contains mocks and other contracts that are used solely for testing contracts within the other directories. diff --git a/contracts/core/compiler.json b/contracts/core/compiler.json index 239c25919..606208e49 100644 --- a/contracts/core/compiler.json +++ b/contracts/core/compiler.json @@ -20,24 +20,15 @@ }, "contracts": [ "AssetProxyOwner", - "DummyERC20Token", - "DummyERC721Receiver", - "DummyERC721Token", - "DummyMultipleReturnERC20Token", - "DummyNoReturnERC20Token", "DutchAuction", "ERC20Proxy", - "ERC20Token", - "ERC721Token", "ERC721Proxy", "Exchange", "ExchangeWrapper", "Forwarder", - "InvalidERC721Receiver", "MixinAuthorizable", "MultiAssetProxy", "OrderValidator", - "ReentrantERC20Token", "TestAssetProxyOwner", "TestAssetProxyDispatcher", "TestExchangeInternals", @@ -45,8 +36,6 @@ "TestStaticCallReceiver", "Validator", "Wallet", - "WETH9", - "Whitelist", - "ZRXToken" + "Whitelist" ] } diff --git a/contracts/core/contracts/extensions/DutchAuction/DutchAuction.sol b/contracts/core/contracts/extensions/DutchAuction/DutchAuction.sol index 90b6f4648..9c9f3990a 100644 --- a/contracts/core/contracts/extensions/DutchAuction/DutchAuction.sol +++ b/contracts/core/contracts/extensions/DutchAuction/DutchAuction.sol @@ -21,7 +21,7 @@ pragma experimental ABIEncoderV2; import "@0x/contracts-interfaces/contracts/protocol/Exchange/IExchange.sol"; import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; -import "../../tokens/ERC20Token/IERC20Token.sol"; +import "@0x/contracts-tokens/contracts/tokens/ERC20Token/IERC20Token.sol"; import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol"; import "@0x/contracts-utils/contracts/utils/SafeMath/SafeMath.sol"; diff --git a/contracts/core/contracts/extensions/Forwarder/MixinAssets.sol b/contracts/core/contracts/extensions/Forwarder/MixinAssets.sol index 5f5f3456d..3ebf75161 100644 --- a/contracts/core/contracts/extensions/Forwarder/MixinAssets.sol +++ b/contracts/core/contracts/extensions/Forwarder/MixinAssets.sol @@ -20,8 +20,8 @@ pragma solidity 0.4.24; import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol"; import "@0x/contracts-utils/contracts/utils/Ownable/Ownable.sol"; -import "../../tokens/ERC20Token/IERC20Token.sol"; -import "../../tokens/ERC721Token/IERC721Token.sol"; +import "@0x/contracts-tokens/contracts/tokens/ERC20Token/IERC20Token.sol"; +import "@0x/contracts-tokens/contracts/tokens/ERC721Token/IERC721Token.sol"; import "./libs/LibConstants.sol"; import "./mixins/MAssets.sol"; diff --git a/contracts/core/contracts/extensions/Forwarder/libs/LibConstants.sol b/contracts/core/contracts/extensions/Forwarder/libs/LibConstants.sol index 00be0f1bd..4a81abf76 100644 --- a/contracts/core/contracts/extensions/Forwarder/libs/LibConstants.sol +++ b/contracts/core/contracts/extensions/Forwarder/libs/LibConstants.sol @@ -20,8 +20,8 @@ pragma solidity 0.4.24; import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol"; import "@0x/contracts-interfaces/contracts/protocol/Exchange/IExchange.sol"; -import "../../../tokens/EtherToken/IEtherToken.sol"; -import "../../../tokens/ERC20Token/IERC20Token.sol"; +import "@0x/contracts-tokens/contracts/tokens/EtherToken/IEtherToken.sol"; +import "@0x/contracts-tokens/contracts/tokens/ERC20Token/IERC20Token.sol"; contract LibConstants { diff --git a/contracts/core/contracts/extensions/OrderValidator/OrderValidator.sol b/contracts/core/contracts/extensions/OrderValidator/OrderValidator.sol index 78c31c8fc..33dd1326c 100644 --- a/contracts/core/contracts/extensions/OrderValidator/OrderValidator.sol +++ b/contracts/core/contracts/extensions/OrderValidator/OrderValidator.sol @@ -21,8 +21,8 @@ pragma experimental ABIEncoderV2; import "@0x/contracts-interfaces/contracts/protocol/Exchange/IExchange.sol"; import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; -import "../../tokens/ERC20Token/IERC20Token.sol"; -import "../../tokens/ERC721Token/IERC721Token.sol"; +import "@0x/contracts-tokens/contracts/tokens/ERC20Token/IERC20Token.sol"; +import "@0x/contracts-tokens/contracts/tokens/ERC721Token/IERC721Token.sol"; import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol"; diff --git a/contracts/core/contracts/test/DummyERC20Token/DummyERC20Token.sol b/contracts/core/contracts/test/DummyERC20Token/DummyERC20Token.sol deleted file mode 100644 index 33028db0c..000000000 --- a/contracts/core/contracts/test/DummyERC20Token/DummyERC20Token.sol +++ /dev/null @@ -1,77 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity 0.4.24; - -import "@0x/contracts-utils/contracts/utils/Ownable/Ownable.sol"; -import "../../tokens/ERC20Token/MintableERC20Token.sol"; - - -contract DummyERC20Token is - Ownable, - MintableERC20Token -{ - string public name; - string public symbol; - uint256 public decimals; - uint256 public constant MAX_MINT_AMOUNT = 10000000000000000000000; - - constructor ( - string _name, - string _symbol, - uint256 _decimals, - uint256 _totalSupply - ) - public - { - name = _name; - symbol = _symbol; - decimals = _decimals; - _totalSupply = _totalSupply; - balances[msg.sender] = _totalSupply; - } - - /// @dev Sets the balance of target address - /// @param _target Address or which balance will be updated - /// @param _value New balance of target address - function setBalance(address _target, uint256 _value) - external - onlyOwner - { - uint256 currBalance = balances[_target]; - if (_value < currBalance) { - _totalSupply = safeSub(_totalSupply, safeSub(currBalance, _value)); - } else { - _totalSupply = safeAdd(_totalSupply, safeSub(_value, currBalance)); - } - balances[_target] = _value; - } - - /// @dev Mints new tokens for sender - /// @param _value Amount of tokens to mint - function mint(uint256 _value) - external - { - require( - _value <= MAX_MINT_AMOUNT, - "VALUE_TOO_LARGE" - ); - - _mint(msg.sender, _value); - } -} diff --git a/contracts/core/contracts/test/DummyERC20Token/DummyMultipleReturnERC20Token.sol b/contracts/core/contracts/test/DummyERC20Token/DummyMultipleReturnERC20Token.sol deleted file mode 100644 index 733d4437e..000000000 --- a/contracts/core/contracts/test/DummyERC20Token/DummyMultipleReturnERC20Token.sol +++ /dev/null @@ -1,69 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity 0.4.24; - -import "./DummyERC20Token.sol"; - - -// solhint-disable no-empty-blocks -contract DummyMultipleReturnERC20Token is - DummyERC20Token -{ - constructor ( - string _name, - string _symbol, - uint256 _decimals, - uint256 _totalSupply - ) - public - DummyERC20Token( - _name, - _symbol, - _decimals, - _totalSupply - ) - {} - - /// @dev send `value` token to `to` from `from` on the condition it is approved by `from` - /// @param _from The address of the sender - /// @param _to The address of the recipient - /// @param _value The amount of token to be transferred - function transferFrom( - address _from, - address _to, - uint256 _value - ) - external - returns (bool) - { - emit Transfer( - _from, - _to, - _value - ); - - // HACK: This contract will not compile if we remove `returns (bool)`, so we manually return 64 bytes (equiavalent to true, true) - assembly { - mstore(0, 1) - mstore(32, 1) - return(0, 64) - } - } -} - diff --git a/contracts/core/contracts/test/DummyERC20Token/DummyNoReturnERC20Token.sol b/contracts/core/contracts/test/DummyERC20Token/DummyNoReturnERC20Token.sol deleted file mode 100644 index e16825a16..000000000 --- a/contracts/core/contracts/test/DummyERC20Token/DummyNoReturnERC20Token.sol +++ /dev/null @@ -1,115 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity 0.4.24; - -import "./DummyERC20Token.sol"; - - -// solhint-disable no-empty-blocks -contract DummyNoReturnERC20Token is - DummyERC20Token -{ - constructor ( - string _name, - string _symbol, - uint256 _decimals, - uint256 _totalSupply - ) - public - DummyERC20Token( - _name, - _symbol, - _decimals, - _totalSupply - ) - {} - - /// @dev send `value` token to `to` from `msg.sender` - /// @param _to The address of the recipient - /// @param _value The amount of token to be transferred - function transfer(address _to, uint256 _value) - external - returns (bool) - { - require( - balances[msg.sender] >= _value, - "ERC20_INSUFFICIENT_BALANCE" - ); - require( - balances[_to] + _value >= balances[_to], - "UINT256_OVERFLOW" - ); - - balances[msg.sender] -= _value; - balances[_to] += _value; - - emit Transfer( - msg.sender, - _to, - _value - ); - - // HACK: This contract will not compile if we remove `returns (bool)`, so we manually return no data - assembly { - return(0, 0) - } - } - - /// @dev send `value` token to `to` from `from` on the condition it is approved by `from` - /// @param _from The address of the sender - /// @param _to The address of the recipient - /// @param _value The amount of token to be transferred - function transferFrom( - address _from, - address _to, - uint256 _value - ) - external - returns (bool) - { - require( - balances[_from] >= _value, - "ERC20_INSUFFICIENT_BALANCE" - ); - require( - allowed[_from][msg.sender] >= _value, - "ERC20_INSUFFICIENT_ALLOWANCE" - ); - require( - balances[_to] + _value >= balances[_to], - "UINT256_OVERFLOW" - ); - - balances[_to] += _value; - balances[_from] -= _value; - allowed[_from][msg.sender] -= _value; - - emit Transfer( - _from, - _to, - _value - ); - - // HACK: This contract will not compile if we remove `returns (bool)`, so we manually return no data - assembly { - return(0, 0) - } - } -} - diff --git a/contracts/core/contracts/test/DummyERC721Receiver/DummyERC721Receiver.sol b/contracts/core/contracts/test/DummyERC721Receiver/DummyERC721Receiver.sol deleted file mode 100644 index 6c8371559..000000000 --- a/contracts/core/contracts/test/DummyERC721Receiver/DummyERC721Receiver.sol +++ /dev/null @@ -1,67 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity 0.4.24; - -import "../../tokens/ERC721Token/IERC721Receiver.sol"; - - -contract DummyERC721Receiver is - IERC721Receiver -{ - // Function selector for ERC721Receiver.onERC721Received - // 0x150b7a02 - bytes4 constant internal ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)")); - - event TokenReceived( - address operator, - address from, - uint256 tokenId, - bytes data - ); - - /// @notice Handle the receipt of an NFT - /// @dev The ERC721 smart contract calls this function on the recipient - /// after a `transfer`. This function MAY throw to revert and reject the - /// transfer. Return of other than the magic value MUST result in the - /// transaction being reverted. - /// Note: the contract address is always the message sender. - /// @param _operator The address which called `safeTransferFrom` function - /// @param _from The address which previously owned the token - /// @param _tokenId The NFT identifier which is being transferred - /// @param _data Additional data with no specified format - /// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` - /// unless throwing - function onERC721Received( - address _operator, - address _from, - uint256 _tokenId, - bytes _data - ) - external - returns (bytes4) - { - emit TokenReceived( - _operator, - _from, - _tokenId, - _data - ); - return ERC721_RECEIVED; - } -} diff --git a/contracts/core/contracts/test/DummyERC721Receiver/InvalidERC721Receiver.sol b/contracts/core/contracts/test/DummyERC721Receiver/InvalidERC721Receiver.sol deleted file mode 100644 index 309633bf5..000000000 --- a/contracts/core/contracts/test/DummyERC721Receiver/InvalidERC721Receiver.sol +++ /dev/null @@ -1,66 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity 0.4.24; - -import "../../tokens/ERC721Token/IERC721Receiver.sol"; - - -contract InvalidERC721Receiver is - IERC721Receiver -{ - // Actual function signature is `onERC721Received(address,address,uint256,bytes)` - bytes4 constant internal INVALID_ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,uint256,bytes)")); - - event TokenReceived( - address operator, - address from, - uint256 tokenId, - bytes data - ); - - /// @notice Handle the receipt of an NFT - /// @dev The ERC721 smart contract calls this function on the recipient - /// after a `transfer`. This function MAY throw to revert and reject the - /// transfer. Return of other than the magic value MUST result in the - /// transaction being reverted. - /// Note: the contract address is always the message sender. - /// @param _operator The address which called `safeTransferFrom` function - /// @param _from The address which previously owned the token - /// @param _tokenId The NFT identifier which is being transferred - /// @param _data Additional data with no specified format - /// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` - /// unless throwing - function onERC721Received( - address _operator, - address _from, - uint256 _tokenId, - bytes _data - ) - external - returns (bytes4) - { - emit TokenReceived( - _operator, - _from, - _tokenId, - _data - ); - return INVALID_ERC721_RECEIVED; - } -} diff --git a/contracts/core/contracts/test/DummyERC721Token/DummyERC721Token.sol b/contracts/core/contracts/test/DummyERC721Token/DummyERC721Token.sol deleted file mode 100644 index 4c978b2df..000000000 --- a/contracts/core/contracts/test/DummyERC721Token/DummyERC721Token.sol +++ /dev/null @@ -1,63 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity 0.4.24; - -import "../../tokens/ERC721Token/MintableERC721Token.sol"; -import "@0x/contracts-utils/contracts/utils/Ownable/Ownable.sol"; - - -// solhint-disable no-empty-blocks -contract DummyERC721Token is - Ownable, - MintableERC721Token -{ - string public name; - string public symbol; - - constructor ( - string _name, - string _symbol - ) - public - { - name = _name; - symbol = _symbol; - } - - /// @dev Function to mint a new token - /// Reverts if the given token ID already exists - /// @param _to Address of the beneficiary that will own the minted token - /// @param _tokenId ID of the token to be minted by the msg.sender - function mint(address _to, uint256 _tokenId) - external - { - _mint(_to, _tokenId); - } - - /// @dev Function to burn a token - /// Reverts if the given token ID doesn't exist or not called by contract owner - /// @param _owner Owner of token with given token ID - /// @param _tokenId ID of the token to be burned by the msg.sender - function burn(address _owner, uint256 _tokenId) - external - onlyOwner - { - _burn(_owner, _tokenId); - } -} diff --git a/contracts/core/contracts/test/ReentrantERC20Token/ReentrantERC20Token.sol b/contracts/core/contracts/test/ReentrantERC20Token/ReentrantERC20Token.sol deleted file mode 100644 index d8c65929d..000000000 --- a/contracts/core/contracts/test/ReentrantERC20Token/ReentrantERC20Token.sol +++ /dev/null @@ -1,188 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity 0.4.24; -pragma experimental ABIEncoderV2; - -import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol"; -import "../../tokens/ERC20Token/ERC20Token.sol"; -import "@0x/contracts-interfaces/contracts/protocol/Exchange/IExchange.sol"; -import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; - - -// solhint-disable no-unused-vars -contract ReentrantERC20Token is - ERC20Token -{ - using LibBytes for bytes; - - // solhint-disable-next-line var-name-mixedcase - IExchange internal EXCHANGE; - - bytes internal constant REENTRANCY_ILLEGAL_REVERT_REASON = abi.encodeWithSelector( - bytes4(keccak256("Error(string)")), - "REENTRANCY_ILLEGAL" - ); - - // All of these functions are potentially vulnerable to reentrancy - // We do not test any "noThrow" functions because `fillOrderNoThrow` makes a delegatecall to `fillOrder` - enum ExchangeFunction { - FILL_ORDER, - FILL_OR_KILL_ORDER, - BATCH_FILL_ORDERS, - BATCH_FILL_OR_KILL_ORDERS, - MARKET_BUY_ORDERS, - MARKET_SELL_ORDERS, - MATCH_ORDERS, - CANCEL_ORDER, - BATCH_CANCEL_ORDERS, - CANCEL_ORDERS_UP_TO, - SET_SIGNATURE_VALIDATOR_APPROVAL - } - - uint8 internal currentFunctionId = 0; - - constructor (address _exchange) - public - { - EXCHANGE = IExchange(_exchange); - } - - /// @dev Set the current function that will be called when `transferFrom` is called. - /// @param _currentFunctionId Id that corresponds to function name. - function setCurrentFunction(uint8 _currentFunctionId) - external - { - currentFunctionId = _currentFunctionId; - } - - /// @dev A version of `transferFrom` that attempts to reenter the Exchange contract. - /// @param _from The address of the sender - /// @param _to The address of the recipient - /// @param _value The amount of token to be transferred - function transferFrom( - address _from, - address _to, - uint256 _value - ) - external - returns (bool) - { - // This order would normally be invalid, but it will be used strictly for testing reentrnacy. - // Any reentrancy checks will happen before any other checks that invalidate the order. - LibOrder.Order memory order; - - // Initialize remaining null parameters - bytes memory signature; - LibOrder.Order[] memory orders; - uint256[] memory takerAssetFillAmounts; - bytes[] memory signatures; - bytes memory callData; - - // Create callData for function that corresponds to currentFunctionId - if (currentFunctionId == uint8(ExchangeFunction.FILL_ORDER)) { - callData = abi.encodeWithSelector( - EXCHANGE.fillOrder.selector, - order, - 0, - signature - ); - } else if (currentFunctionId == uint8(ExchangeFunction.FILL_OR_KILL_ORDER)) { - callData = abi.encodeWithSelector( - EXCHANGE.fillOrKillOrder.selector, - order, - 0, - signature - ); - } else if (currentFunctionId == uint8(ExchangeFunction.BATCH_FILL_ORDERS)) { - callData = abi.encodeWithSelector( - EXCHANGE.batchFillOrders.selector, - orders, - takerAssetFillAmounts, - signatures - ); - } else if (currentFunctionId == uint8(ExchangeFunction.BATCH_FILL_OR_KILL_ORDERS)) { - callData = abi.encodeWithSelector( - EXCHANGE.batchFillOrKillOrders.selector, - orders, - takerAssetFillAmounts, - signatures - ); - } else if (currentFunctionId == uint8(ExchangeFunction.MARKET_BUY_ORDERS)) { - callData = abi.encodeWithSelector( - EXCHANGE.marketBuyOrders.selector, - orders, - 0, - signatures - ); - } else if (currentFunctionId == uint8(ExchangeFunction.MARKET_SELL_ORDERS)) { - callData = abi.encodeWithSelector( - EXCHANGE.marketSellOrders.selector, - orders, - 0, - signatures - ); - } else if (currentFunctionId == uint8(ExchangeFunction.MATCH_ORDERS)) { - callData = abi.encodeWithSelector( - EXCHANGE.matchOrders.selector, - order, - order, - signature, - signature - ); - } else if (currentFunctionId == uint8(ExchangeFunction.CANCEL_ORDER)) { - callData = abi.encodeWithSelector( - EXCHANGE.cancelOrder.selector, - order - ); - } else if (currentFunctionId == uint8(ExchangeFunction.BATCH_CANCEL_ORDERS)) { - callData = abi.encodeWithSelector( - EXCHANGE.batchCancelOrders.selector, - orders - ); - } else if (currentFunctionId == uint8(ExchangeFunction.CANCEL_ORDERS_UP_TO)) { - callData = abi.encodeWithSelector( - EXCHANGE.cancelOrdersUpTo.selector, - 0 - ); - } else if (currentFunctionId == uint8(ExchangeFunction.SET_SIGNATURE_VALIDATOR_APPROVAL)) { - callData = abi.encodeWithSelector( - EXCHANGE.setSignatureValidatorApproval.selector, - address(0), - false - ); - } - - // Call Exchange function, swallow error - address(EXCHANGE).call(callData); - - // Revert reason is 100 bytes - bytes memory returnData = new bytes(100); - - // Copy return data - assembly { - returndatacopy(add(returnData, 32), 0, 100) - } - - // Revert if function reverted with REENTRANCY_ILLEGAL error - require(!REENTRANCY_ILLEGAL_REVERT_REASON.equals(returnData)); - - // Transfer will return true if function failed for any other reason - return true; - } -} \ No newline at end of file diff --git a/contracts/core/contracts/test/TestStaticCallReceiver/TestStaticCallReceiver.sol b/contracts/core/contracts/test/TestStaticCallReceiver/TestStaticCallReceiver.sol index 41aab01c8..d08da7303 100644 --- a/contracts/core/contracts/test/TestStaticCallReceiver/TestStaticCallReceiver.sol +++ b/contracts/core/contracts/test/TestStaticCallReceiver/TestStaticCallReceiver.sol @@ -18,7 +18,7 @@ pragma solidity 0.4.24; -import "../../tokens/ERC20Token/IERC20Token.sol"; +import "@0x/contracts-tokens/contracts/tokens/ERC20Token/IERC20Token.sol"; // solhint-disable no-unused-vars diff --git a/contracts/core/contracts/tokens/ERC20Token/ERC20Token.sol b/contracts/core/contracts/tokens/ERC20Token/ERC20Token.sol deleted file mode 100644 index 725d304df..000000000 --- a/contracts/core/contracts/tokens/ERC20Token/ERC20Token.sol +++ /dev/null @@ -1,148 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity 0.4.24; - -import "./IERC20Token.sol"; - - -contract ERC20Token is - IERC20Token -{ - mapping (address => uint256) internal balances; - mapping (address => mapping (address => uint256)) internal allowed; - - uint256 internal _totalSupply; - - /// @dev send `value` token to `to` from `msg.sender` - /// @param _to The address of the recipient - /// @param _value The amount of token to be transferred - /// @return True if transfer was successful - function transfer(address _to, uint256 _value) - external - returns (bool) - { - require( - balances[msg.sender] >= _value, - "ERC20_INSUFFICIENT_BALANCE" - ); - require( - balances[_to] + _value >= balances[_to], - "UINT256_OVERFLOW" - ); - - balances[msg.sender] -= _value; - balances[_to] += _value; - - emit Transfer( - msg.sender, - _to, - _value - ); - - return true; - } - - /// @dev send `value` token to `to` from `from` on the condition it is approved by `from` - /// @param _from The address of the sender - /// @param _to The address of the recipient - /// @param _value The amount of token to be transferred - /// @return True if transfer was successful - function transferFrom( - address _from, - address _to, - uint256 _value - ) - external - returns (bool) - { - require( - balances[_from] >= _value, - "ERC20_INSUFFICIENT_BALANCE" - ); - require( - allowed[_from][msg.sender] >= _value, - "ERC20_INSUFFICIENT_ALLOWANCE" - ); - require( - balances[_to] + _value >= balances[_to], - "UINT256_OVERFLOW" - ); - - balances[_to] += _value; - balances[_from] -= _value; - allowed[_from][msg.sender] -= _value; - - emit Transfer( - _from, - _to, - _value - ); - - return true; - } - - /// @dev `msg.sender` approves `_spender` to spend `_value` tokens - /// @param _spender The address of the account able to transfer the tokens - /// @param _value The amount of wei to be approved for transfer - /// @return Always true if the call has enough gas to complete execution - function approve(address _spender, uint256 _value) - external - returns (bool) - { - allowed[msg.sender][_spender] = _value; - emit Approval( - msg.sender, - _spender, - _value - ); - return true; - } - - /// @dev Query total supply of token - /// @return Total supply of token - function totalSupply() - external - view - returns (uint256) - { - return _totalSupply; - } - - /// @dev Query the balance of owner - /// @param _owner The address from which the balance will be retrieved - /// @return Balance of owner - function balanceOf(address _owner) - external - view - returns (uint256) - { - return balances[_owner]; - } - - /// @param _owner The address of the account owning tokens - /// @param _spender The address of the account able to transfer the tokens - /// @return Amount of remaining tokens allowed to spent - function allowance(address _owner, address _spender) - external - view - returns (uint256) - { - return allowed[_owner][_spender]; - } -} diff --git a/contracts/core/contracts/tokens/ERC20Token/IERC20Token.sol b/contracts/core/contracts/tokens/ERC20Token/IERC20Token.sol deleted file mode 100644 index 258d47393..000000000 --- a/contracts/core/contracts/tokens/ERC20Token/IERC20Token.sol +++ /dev/null @@ -1,87 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity 0.4.24; - - -contract IERC20Token { - - // solhint-disable no-simple-event-func-name - event Transfer( - address indexed _from, - address indexed _to, - uint256 _value - ); - - event Approval( - address indexed _owner, - address indexed _spender, - uint256 _value - ); - - /// @dev send `value` token to `to` from `msg.sender` - /// @param _to The address of the recipient - /// @param _value The amount of token to be transferred - /// @return True if transfer was successful - function transfer(address _to, uint256 _value) - external - returns (bool); - - /// @dev send `value` token to `to` from `from` on the condition it is approved by `from` - /// @param _from The address of the sender - /// @param _to The address of the recipient - /// @param _value The amount of token to be transferred - /// @return True if transfer was successful - function transferFrom( - address _from, - address _to, - uint256 _value - ) - external - returns (bool); - - /// @dev `msg.sender` approves `_spender` to spend `_value` tokens - /// @param _spender The address of the account able to transfer the tokens - /// @param _value The amount of wei to be approved for transfer - /// @return Always true if the call has enough gas to complete execution - function approve(address _spender, uint256 _value) - external - returns (bool); - - /// @dev Query total supply of token - /// @return Total supply of token - function totalSupply() - external - view - returns (uint256); - - /// @param _owner The address from which the balance will be retrieved - /// @return Balance of owner - function balanceOf(address _owner) - external - view - returns (uint256); - - /// @param _owner The address of the account owning tokens - /// @param _spender The address of the account able to transfer the tokens - /// @return Amount of remaining tokens allowed to spent - function allowance(address _owner, address _spender) - external - view - returns (uint256); -} diff --git a/contracts/core/contracts/tokens/ERC20Token/MintableERC20Token.sol b/contracts/core/contracts/tokens/ERC20Token/MintableERC20Token.sol deleted file mode 100644 index 58bccb5a1..000000000 --- a/contracts/core/contracts/tokens/ERC20Token/MintableERC20Token.sol +++ /dev/null @@ -1,60 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity 0.4.24; - -import "@0x/contracts-utils/contracts/utils/SafeMath/SafeMath.sol"; -import "./UnlimitedAllowanceERC20Token.sol"; - - -contract MintableERC20Token is - SafeMath, - UnlimitedAllowanceERC20Token -{ - /// @dev Mints new tokens - /// @param _to Address of the beneficiary that will own the minted token - /// @param _value Amount of tokens to mint - function _mint(address _to, uint256 _value) - internal - { - balances[_to] = safeAdd(_value, balances[_to]); - _totalSupply = safeAdd(_totalSupply, _value); - - emit Transfer( - address(0), - _to, - _value - ); - } - - /// @dev Mints new tokens - /// @param _owner Owner of tokens that will be burned - /// @param _value Amount of tokens to burn - function _burn(address _owner, uint256 _value) - internal - { - balances[_owner] = safeSub(balances[_owner], _value); - _totalSupply = safeSub(_totalSupply, _value); - - emit Transfer( - _owner, - address(0), - _value - ); - } -} diff --git a/contracts/core/contracts/tokens/ERC20Token/UnlimitedAllowanceERC20Token.sol b/contracts/core/contracts/tokens/ERC20Token/UnlimitedAllowanceERC20Token.sol deleted file mode 100644 index 2e5bd4348..000000000 --- a/contracts/core/contracts/tokens/ERC20Token/UnlimitedAllowanceERC20Token.sol +++ /dev/null @@ -1,70 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity 0.4.24; - -import "../ERC20Token/ERC20Token.sol"; - - -contract UnlimitedAllowanceERC20Token is - ERC20Token -{ - uint256 constant internal MAX_UINT = 2**256 - 1; - - /// @dev ERC20 transferFrom, modified such that an allowance of MAX_UINT represents an unlimited allowance. See https://github.com/ethereum/EIPs/issues/717 - /// @param _from Address to transfer from. - /// @param _to Address to transfer to. - /// @param _value Amount to transfer. - /// @return Success of transfer. - function transferFrom( - address _from, - address _to, - uint256 _value - ) - external - returns (bool) - { - uint256 allowance = allowed[_from][msg.sender]; - require( - balances[_from] >= _value, - "ERC20_INSUFFICIENT_BALANCE" - ); - require( - allowance >= _value, - "ERC20_INSUFFICIENT_ALLOWANCE" - ); - require( - balances[_to] + _value >= balances[_to], - "UINT256_OVERFLOW" - ); - - balances[_to] += _value; - balances[_from] -= _value; - if (allowance < MAX_UINT) { - allowed[_from][msg.sender] -= _value; - } - - emit Transfer( - _from, - _to, - _value - ); - - return true; - } -} diff --git a/contracts/core/contracts/tokens/ERC721Token/ERC721Token.sol b/contracts/core/contracts/tokens/ERC721Token/ERC721Token.sol deleted file mode 100644 index 600cee1ab..000000000 --- a/contracts/core/contracts/tokens/ERC721Token/ERC721Token.sol +++ /dev/null @@ -1,277 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity 0.4.24; - -import "./IERC721Token.sol"; -import "./IERC721Receiver.sol"; -import "@0x/contracts-utils/contracts/utils/SafeMath/SafeMath.sol"; - - -contract ERC721Token is - IERC721Token, - SafeMath -{ - // Function selector for ERC721Receiver.onERC721Received - // 0x150b7a02 - bytes4 constant internal ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)")); - - // Mapping of tokenId => owner - mapping (uint256 => address) internal owners; - - // Mapping of tokenId => approved address - mapping (uint256 => address) internal approvals; - - // Mapping of owner => number of tokens owned - mapping (address => uint256) internal balances; - - // Mapping of owner => operator => approved - mapping (address => mapping (address => bool)) internal operatorApprovals; - - /// @notice Transfers the ownership of an NFT from one address to another address - /// @dev Throws unless `msg.sender` is the current owner, an authorized - /// operator, or the approved address for this NFT. Throws if `_from` is - /// not the current owner. Throws if `_to` is the zero address. Throws if - /// `_tokenId` is not a valid NFT. When transfer is complete, this function - /// checks if `_to` is a smart contract (code size > 0). If so, it calls - /// `onERC721Received` on `_to` and throws if the return value is not - /// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`. - /// @param _from The current owner of the NFT - /// @param _to The new owner - /// @param _tokenId The NFT to transfer - /// @param _data Additional data with no specified format, sent in call to `_to` - function safeTransferFrom( - address _from, - address _to, - uint256 _tokenId, - bytes _data - ) - external - { - transferFrom( - _from, - _to, - _tokenId - ); - - uint256 receiverCodeSize; - assembly { - receiverCodeSize := extcodesize(_to) - } - if (receiverCodeSize > 0) { - bytes4 selector = IERC721Receiver(_to).onERC721Received( - msg.sender, - _from, - _tokenId, - _data - ); - require( - selector == ERC721_RECEIVED, - "ERC721_INVALID_SELECTOR" - ); - } - } - - /// @notice Transfers the ownership of an NFT from one address to another address - /// @dev This works identically to the other function with an extra data parameter, - /// except this function just sets data to "". - /// @param _from The current owner of the NFT - /// @param _to The new owner - /// @param _tokenId The NFT to transfer - function safeTransferFrom( - address _from, - address _to, - uint256 _tokenId - ) - external - { - transferFrom( - _from, - _to, - _tokenId - ); - - uint256 receiverCodeSize; - assembly { - receiverCodeSize := extcodesize(_to) - } - if (receiverCodeSize > 0) { - bytes4 selector = IERC721Receiver(_to).onERC721Received( - msg.sender, - _from, - _tokenId, - "" - ); - require( - selector == ERC721_RECEIVED, - "ERC721_INVALID_SELECTOR" - ); - } - } - - /// @notice Change or reaffirm the approved address for an NFT - /// @dev The zero address indicates there is no approved address. - /// Throws unless `msg.sender` is the current NFT owner, or an authorized - /// operator of the current owner. - /// @param _approved The new approved NFT controller - /// @param _tokenId The NFT to approve - function approve(address _approved, uint256 _tokenId) - external - { - address owner = ownerOf(_tokenId); - require( - msg.sender == owner || isApprovedForAll(owner, msg.sender), - "ERC721_INVALID_SENDER" - ); - - approvals[_tokenId] = _approved; - emit Approval( - owner, - _approved, - _tokenId - ); - } - - /// @notice Enable or disable approval for a third party ("operator") to manage - /// all of `msg.sender`'s assets - /// @dev Emits the ApprovalForAll event. The contract MUST allow - /// multiple operators per owner. - /// @param _operator Address to add to the set of authorized operators - /// @param _approved True if the operator is approved, false to revoke approval - function setApprovalForAll(address _operator, bool _approved) - external - { - operatorApprovals[msg.sender][_operator] = _approved; - emit ApprovalForAll( - msg.sender, - _operator, - _approved - ); - } - - /// @notice Count all NFTs assigned to an owner - /// @dev NFTs assigned to the zero address are considered invalid, and this - /// function throws for queries about the zero address. - /// @param _owner An address for whom to query the balance - /// @return The number of NFTs owned by `_owner`, possibly zero - function balanceOf(address _owner) - external - view - returns (uint256) - { - require( - _owner != address(0), - "ERC721_ZERO_OWNER" - ); - return balances[_owner]; - } - - /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE - /// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE - /// THEY MAY BE PERMANENTLY LOST - /// @dev Throws unless `msg.sender` is the current owner, an authorized - /// operator, or the approved address for this NFT. Throws if `_from` is - /// not the current owner. Throws if `_to` is the zero address. Throws if - /// `_tokenId` is not a valid NFT. - /// @param _from The current owner of the NFT - /// @param _to The new owner - /// @param _tokenId The NFT to transfer - function transferFrom( - address _from, - address _to, - uint256 _tokenId - ) - public - { - require( - _to != address(0), - "ERC721_ZERO_TO_ADDRESS" - ); - - address owner = ownerOf(_tokenId); - require( - _from == owner, - "ERC721_OWNER_MISMATCH" - ); - - address spender = msg.sender; - address approvedAddress = getApproved(_tokenId); - require( - spender == owner || - isApprovedForAll(owner, spender) || - approvedAddress == spender, - "ERC721_INVALID_SPENDER" - ); - - if (approvedAddress != address(0)) { - approvals[_tokenId] = address(0); - } - - owners[_tokenId] = _to; - balances[_from] = safeSub(balances[_from], 1); - balances[_to] = safeAdd(balances[_to], 1); - - emit Transfer( - _from, - _to, - _tokenId - ); - } - - /// @notice Find the owner of an NFT - /// @dev NFTs assigned to zero address are considered invalid, and queries - /// about them do throw. - /// @param _tokenId The identifier for an NFT - /// @return The address of the owner of the NFT - function ownerOf(uint256 _tokenId) - public - view - returns (address) - { - address owner = owners[_tokenId]; - require( - owner != address(0), - "ERC721_ZERO_OWNER" - ); - return owner; - } - - /// @notice Get the approved address for a single NFT - /// @dev Throws if `_tokenId` is not a valid NFT. - /// @param _tokenId The NFT to find the approved address for - /// @return The approved address for this NFT, or the zero address if there is none - function getApproved(uint256 _tokenId) - public - view - returns (address) - { - return approvals[_tokenId]; - } - - /// @notice Query if an address is an authorized operator for another address - /// @param _owner The address that owns the NFTs - /// @param _operator The address that acts on behalf of the owner - /// @return True if `_operator` is an approved operator for `_owner`, false otherwise - function isApprovedForAll(address _owner, address _operator) - public - view - returns (bool) - { - return operatorApprovals[_owner][_operator]; - } -} diff --git a/contracts/core/contracts/tokens/ERC721Token/IERC721Receiver.sol b/contracts/core/contracts/tokens/ERC721Token/IERC721Receiver.sol deleted file mode 100644 index 8e0e32ab2..000000000 --- a/contracts/core/contracts/tokens/ERC721Token/IERC721Receiver.sol +++ /dev/null @@ -1,44 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity 0.4.24; - - -contract IERC721Receiver { - - /// @notice Handle the receipt of an NFT - /// @dev The ERC721 smart contract calls this function on the recipient - /// after a `transfer`. This function MAY throw to revert and reject the - /// transfer. Return of other than the magic value MUST result in the - /// transaction being reverted. - /// Note: the contract address is always the message sender. - /// @param _operator The address which called `safeTransferFrom` function - /// @param _from The address which previously owned the token - /// @param _tokenId The NFT identifier which is being transferred - /// @param _data Additional data with no specified format - /// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` - /// unless throwing - function onERC721Received( - address _operator, - address _from, - uint256 _tokenId, - bytes _data - ) - external - returns (bytes4); -} diff --git a/contracts/core/contracts/tokens/ERC721Token/IERC721Token.sol b/contracts/core/contracts/tokens/ERC721Token/IERC721Token.sol deleted file mode 100644 index ac992c80d..000000000 --- a/contracts/core/contracts/tokens/ERC721Token/IERC721Token.sol +++ /dev/null @@ -1,158 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity 0.4.24; - - -contract IERC721Token { - - /// @dev This emits when ownership of any NFT changes by any mechanism. - /// This event emits when NFTs are created (`from` == 0) and destroyed - /// (`to` == 0). Exception: during contract creation, any number of NFTs - /// may be created and assigned without emitting Transfer. At the time of - /// any transfer, the approved address for that NFT (if any) is reset to none. - event Transfer( - address indexed _from, - address indexed _to, - uint256 indexed _tokenId - ); - - /// @dev This emits when the approved address for an NFT is changed or - /// reaffirmed. The zero address indicates there is no approved address. - /// When a Transfer event emits, this also indicates that the approved - /// address for that NFT (if any) is reset to none. - event Approval( - address indexed _owner, - address indexed _approved, - uint256 indexed _tokenId - ); - - /// @dev This emits when an operator is enabled or disabled for an owner. - /// The operator can manage all NFTs of the owner. - event ApprovalForAll( - address indexed _owner, - address indexed _operator, - bool _approved - ); - - /// @notice Transfers the ownership of an NFT from one address to another address - /// @dev Throws unless `msg.sender` is the current owner, an authorized - /// perator, or the approved address for this NFT. Throws if `_from` is - /// not the current owner. Throws if `_to` is the zero address. Throws if - /// `_tokenId` is not a valid NFT. When transfer is complete, this function - /// checks if `_to` is a smart contract (code size > 0). If so, it calls - /// `onERC721Received` on `_to` and throws if the return value is not - /// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`. - /// @param _from The current owner of the NFT - /// @param _to The new owner - /// @param _tokenId The NFT to transfer - /// @param _data Additional data with no specified format, sent in call to `_to` - function safeTransferFrom( - address _from, - address _to, - uint256 _tokenId, - bytes _data - ) - external; - - /// @notice Transfers the ownership of an NFT from one address to another address - /// @dev This works identically to the other function with an extra data parameter, - /// except this function just sets data to "". - /// @param _from The current owner of the NFT - /// @param _to The new owner - /// @param _tokenId The NFT to transfer - function safeTransferFrom( - address _from, - address _to, - uint256 _tokenId - ) - external; - - /// @notice Change or reaffirm the approved address for an NFT - /// @dev The zero address indicates there is no approved address. - /// Throws unless `msg.sender` is the current NFT owner, or an authorized - /// operator of the current owner. - /// @param _approved The new approved NFT controller - /// @param _tokenId The NFT to approve - function approve(address _approved, uint256 _tokenId) - external; - - /// @notice Enable or disable approval for a third party ("operator") to manage - /// all of `msg.sender`'s assets - /// @dev Emits the ApprovalForAll event. The contract MUST allow - /// multiple operators per owner. - /// @param _operator Address to add to the set of authorized operators - /// @param _approved True if the operator is approved, false to revoke approval - function setApprovalForAll(address _operator, bool _approved) - external; - - /// @notice Count all NFTs assigned to an owner - /// @dev NFTs assigned to the zero address are considered invalid, and this - /// function throws for queries about the zero address. - /// @param _owner An address for whom to query the balance - /// @return The number of NFTs owned by `_owner`, possibly zero - function balanceOf(address _owner) - external - view - returns (uint256); - - /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE - /// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE - /// THEY MAY BE PERMANENTLY LOST - /// @dev Throws unless `msg.sender` is the current owner, an authorized - /// operator, or the approved address for this NFT. Throws if `_from` is - /// not the current owner. Throws if `_to` is the zero address. Throws if - /// `_tokenId` is not a valid NFT. - /// @param _from The current owner of the NFT - /// @param _to The new owner - /// @param _tokenId The NFT to transfer - function transferFrom( - address _from, - address _to, - uint256 _tokenId - ) - public; - - /// @notice Find the owner of an NFT - /// @dev NFTs assigned to zero address are considered invalid, and queries - /// about them do throw. - /// @param _tokenId The identifier for an NFT - /// @return The address of the owner of the NFT - function ownerOf(uint256 _tokenId) - public - view - returns (address); - - /// @notice Get the approved address for a single NFT - /// @dev Throws if `_tokenId` is not a valid NFT. - /// @param _tokenId The NFT to find the approved address for - /// @return The approved address for this NFT, or the zero address if there is none - function getApproved(uint256 _tokenId) - public - view - returns (address); - - /// @notice Query if an address is an authorized operator for another address - /// @param _owner The address that owns the NFTs - /// @param _operator The address that acts on behalf of the owner - /// @return True if `_operator` is an approved operator for `_owner`, false otherwise - function isApprovedForAll(address _owner, address _operator) - public - view - returns (bool); -} diff --git a/contracts/core/contracts/tokens/ERC721Token/MintableERC721Token.sol b/contracts/core/contracts/tokens/ERC721Token/MintableERC721Token.sol deleted file mode 100644 index bc5cd2cc2..000000000 --- a/contracts/core/contracts/tokens/ERC721Token/MintableERC721Token.sol +++ /dev/null @@ -1,82 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity 0.4.24; - -import "./ERC721Token.sol"; - - -contract MintableERC721Token is - ERC721Token -{ - /// @dev Function to mint a new token - /// Reverts if the given token ID already exists - /// @param _to Address of the beneficiary that will own the minted token - /// @param _tokenId ID of the token to be minted by the msg.sender - function _mint(address _to, uint256 _tokenId) - internal - { - require( - _to != address(0), - "ERC721_ZERO_TO_ADDRESS" - ); - - address owner = owners[_tokenId]; - require( - owner == address(0), - "ERC721_OWNER_ALREADY_EXISTS" - ); - - owners[_tokenId] = _to; - balances[_to] = safeAdd(balances[_to], 1); - - emit Transfer( - address(0), - _to, - _tokenId - ); - } - - /// @dev Function to burn a token - /// Reverts if the given token ID doesn't exist - /// @param _owner Owner of token with given token ID - /// @param _tokenId ID of the token to be burned by the msg.sender - function _burn(address _owner, uint256 _tokenId) - internal - { - require( - _owner != address(0), - "ERC721_ZERO_OWNER_ADDRESS" - ); - - address owner = owners[_tokenId]; - require( - owner == _owner, - "ERC721_OWNER_MISMATCH" - ); - - owners[_tokenId] = address(0); - balances[_owner] = safeSub(balances[_owner], 1); - - emit Transfer( - _owner, - address(0), - _tokenId - ); - } -} diff --git a/contracts/core/contracts/tokens/EtherToken/IEtherToken.sol b/contracts/core/contracts/tokens/EtherToken/IEtherToken.sol deleted file mode 100644 index 9e2e68766..000000000 --- a/contracts/core/contracts/tokens/EtherToken/IEtherToken.sol +++ /dev/null @@ -1,33 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity 0.4.24; - -import "../ERC20Token/IERC20Token.sol"; - - -contract IEtherToken is - IERC20Token -{ - function deposit() - public - payable; - - function withdraw(uint256 amount) - public; -} diff --git a/contracts/core/contracts/tokens/EtherToken/WETH9.sol b/contracts/core/contracts/tokens/EtherToken/WETH9.sol deleted file mode 100644 index 17876b86d..000000000 --- a/contracts/core/contracts/tokens/EtherToken/WETH9.sol +++ /dev/null @@ -1,758 +0,0 @@ -// Copyright (C) 2015, 2016, 2017 Dapphub - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// solhint-disable -pragma solidity ^0.4.18; - - -contract WETH9 { - string public name = "Wrapped Ether"; - string public symbol = "WETH"; - uint8 public decimals = 18; - - event Approval(address indexed _owner, address indexed _spender, uint _value); - event Transfer(address indexed _from, address indexed _to, uint _value); - event Deposit(address indexed _owner, uint _value); - event Withdrawal(address indexed _owner, uint _value); - - mapping (address => uint) public balanceOf; - mapping (address => mapping (address => uint)) public allowance; - - function() public payable { - deposit(); - } - function deposit() public payable { - balanceOf[msg.sender] += msg.value; - Deposit(msg.sender, msg.value); - } - function withdraw(uint wad) public { - require(balanceOf[msg.sender] >= wad); - balanceOf[msg.sender] -= wad; - msg.sender.transfer(wad); - Withdrawal(msg.sender, wad); - } - - function totalSupply() public view returns (uint) { - return this.balance; - } - - function approve(address guy, uint wad) public returns (bool) { - allowance[msg.sender][guy] = wad; - Approval(msg.sender, guy, wad); - return true; - } - - function transfer(address dst, uint wad) public returns (bool) { - return transferFrom(msg.sender, dst, wad); - } - - function transferFrom(address src, address dst, uint wad) - public - returns (bool) - { - require(balanceOf[src] >= wad); - - if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) { - require(allowance[src][msg.sender] >= wad); - allowance[src][msg.sender] -= wad; - } - - balanceOf[src] -= wad; - balanceOf[dst] += wad; - - Transfer(src, dst, wad); - - return true; - } -} - - -/* - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. - -*/ diff --git a/contracts/core/contracts/tokens/ZRXToken/ERC20Token_v1.sol b/contracts/core/contracts/tokens/ZRXToken/ERC20Token_v1.sol deleted file mode 100644 index 4920c4aac..000000000 --- a/contracts/core/contracts/tokens/ZRXToken/ERC20Token_v1.sol +++ /dev/null @@ -1,44 +0,0 @@ -pragma solidity ^0.4.11; - -import { Token_v1 as Token } from "./Token_v1.sol"; - -contract ERC20Token_v1 is Token { - - function transfer(address _to, uint _value) returns (bool) { - //Default assumes totalSupply can't be over max (2^256 - 1). - if (balances[msg.sender] >= _value && balances[_to] + _value >= balances[_to]) { - balances[msg.sender] -= _value; - balances[_to] += _value; - Transfer(msg.sender, _to, _value); - return true; - } else { return false; } - } - - function transferFrom(address _from, address _to, uint _value) returns (bool) { - if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value >= balances[_to]) { - balances[_to] += _value; - balances[_from] -= _value; - allowed[_from][msg.sender] -= _value; - Transfer(_from, _to, _value); - return true; - } else { return false; } - } - - function balanceOf(address _owner) constant returns (uint) { - return balances[_owner]; - } - - function approve(address _spender, uint _value) returns (bool) { - allowed[msg.sender][_spender] = _value; - Approval(msg.sender, _spender, _value); - return true; - } - - function allowance(address _owner, address _spender) constant returns (uint) { - return allowed[_owner][_spender]; - } - - mapping (address => uint) balances; - mapping (address => mapping (address => uint)) allowed; - uint public totalSupply; -} diff --git a/contracts/core/contracts/tokens/ZRXToken/Token_v1.sol b/contracts/core/contracts/tokens/ZRXToken/Token_v1.sol deleted file mode 100644 index de619fb7e..000000000 --- a/contracts/core/contracts/tokens/ZRXToken/Token_v1.sol +++ /dev/null @@ -1,39 +0,0 @@ -pragma solidity ^0.4.11; - -contract Token_v1 { - - /// @return total amount of tokens - function totalSupply() constant returns (uint supply) {} - - /// @param _owner The address from which the balance will be retrieved - /// @return The balance - function balanceOf(address _owner) constant returns (uint balance) {} - - /// @notice send `_value` token to `_to` from `msg.sender` - /// @param _to The address of the recipient - /// @param _value The amount of token to be transferred - /// @return Whether the transfer was successful or not - function transfer(address _to, uint _value) returns (bool success) {} - - /// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from` - /// @param _from The address of the sender - /// @param _to The address of the recipient - /// @param _value The amount of token to be transferred - /// @return Whether the transfer was successful or not - function transferFrom(address _from, address _to, uint _value) returns (bool success) {} - - /// @notice `msg.sender` approves `_addr` to spend `_value` tokens - /// @param _spender The address of the account able to transfer the tokens - /// @param _value The amount of wei to be approved for transfer - /// @return Whether the approval was successful or not - function approve(address _spender, uint _value) returns (bool success) {} - - /// @param _owner The address of the account owning tokens - /// @param _spender The address of the account able to transfer the tokens - /// @return Amount of remaining tokens allowed to spent - function allowance(address _owner, address _spender) constant returns (uint remaining) {} - - event Transfer(address indexed _from, address indexed _to, uint _value); - event Approval(address indexed _owner, address indexed _spender, uint _value); -} - diff --git a/contracts/core/contracts/tokens/ZRXToken/UnlimitedAllowanceToken_v1.sol b/contracts/core/contracts/tokens/ZRXToken/UnlimitedAllowanceToken_v1.sol deleted file mode 100644 index bf1b0335a..000000000 --- a/contracts/core/contracts/tokens/ZRXToken/UnlimitedAllowanceToken_v1.sol +++ /dev/null @@ -1,52 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.4.11; - -import { ERC20Token_v1 as ERC20Token } from "./ERC20Token_v1.sol"; - -contract UnlimitedAllowanceToken_v1 is ERC20Token { - - uint constant MAX_UINT = 2**256 - 1; - - /// @dev ERC20 transferFrom, modified such that an allowance of MAX_UINT represents an unlimited allowance. - /// @param _from Address to transfer from. - /// @param _to Address to transfer to. - /// @param _value Amount to transfer. - /// @return Success of transfer. - function transferFrom(address _from, address _to, uint _value) - public - returns (bool) - { - uint allowance = allowed[_from][msg.sender]; - if (balances[_from] >= _value - && allowance >= _value - && balances[_to] + _value >= balances[_to] - ) { - balances[_to] += _value; - balances[_from] -= _value; - if (allowance < MAX_UINT) { - allowed[_from][msg.sender] -= _value; - } - Transfer(_from, _to, _value); - return true; - } else { - return false; - } - } -} diff --git a/contracts/core/contracts/tokens/ZRXToken/ZRXToken.sol b/contracts/core/contracts/tokens/ZRXToken/ZRXToken.sol deleted file mode 100644 index 831e1822c..000000000 --- a/contracts/core/contracts/tokens/ZRXToken/ZRXToken.sol +++ /dev/null @@ -1,41 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity 0.4.11; - -// solhint-disable-next-line max-line-length -import { UnlimitedAllowanceToken_v1 as UnlimitedAllowanceToken } from "./UnlimitedAllowanceToken_v1.sol"; - - -contract ZRXToken is - UnlimitedAllowanceToken -{ - - // solhint-disable const-name-snakecase - uint8 constant public decimals = 18; - uint256 public totalSupply = 10**27; // 1 billion tokens, 18 decimal places - string constant public name = "0x Protocol Token"; - string constant public symbol = "ZRX"; - // solhint-enableconst-name-snakecase - - function ZRXToken() - public - { - balances[msg.sender] = totalSupply; - } -} diff --git a/contracts/core/package.json b/contracts/core/package.json index be2214177..fd844ae16 100644 --- a/contracts/core/package.json +++ b/contracts/core/package.json @@ -33,7 +33,7 @@ "lint-contracts": "solhint contracts/**/**/**/**/*.sol" }, "config": { - "abis": "generated-artifacts/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyMultipleReturnERC20Token|DummyNoReturnERC20Token|DutchAuction|ERC20Token|ERC20Proxy|ERC721Token|ERC721Proxy|Forwarder|Exchange|ExchangeWrapper|InvalidERC721Receiver|MixinAuthorizable|MultiAssetProxy|OrderValidator|ReentrantERC20Token|TestAssetProxyOwner|TestAssetProxyDispatcher|TestConstants|TestExchangeInternals|TestLibBytes|TestSignatureValidator|TestStaticCallReceiver|Validator|Wallet|Whitelist|WETH9|ZRXToken).json" + "abis": "generated-artifacts/@(AssetProxyOwner|DutchAuction|ERC20Proxy|ERC721Proxy|Forwarder|Exchange|ExchangeWrapper|MixinAuthorizable|MultiAssetProxy|OrderValidator|TestAssetProxyOwner|TestAssetProxyDispatcher|TestExchangeInternals|TestSignatureValidator|TestStaticCallReceiver|Validator|Wallet|Whitelist).json" }, "repository": { "type": "git", @@ -76,6 +76,7 @@ "@0x/order-utils": "^3.0.4", "@0x/contracts-multisig": "^1.0.0", "@0x/contracts-utils": "^1.0.0", + "@0x/contracts-tokens": "^1.0.0", "@0x/contracts-libs": "^1.0.0", "@0x/contracts-interfaces": "^1.0.0", "@0x/types": "^1.3.0", diff --git a/contracts/core/src/artifacts/index.ts b/contracts/core/src/artifacts/index.ts index 1d868bae2..ac334c173 100644 --- a/contracts/core/src/artifacts/index.ts +++ b/contracts/core/src/artifacts/index.ts @@ -1,24 +1,15 @@ import { ContractArtifact } from 'ethereum-types'; import * as AssetProxyOwner from '../../generated-artifacts/AssetProxyOwner.json'; -import * as DummyERC20Token from '../../generated-artifacts/DummyERC20Token.json'; -import * as DummyERC721Receiver from '../../generated-artifacts/DummyERC721Receiver.json'; -import * as DummyERC721Token from '../../generated-artifacts/DummyERC721Token.json'; -import * as DummyMultipleReturnERC20Token from '../../generated-artifacts/DummyMultipleReturnERC20Token.json'; -import * as DummyNoReturnERC20Token from '../../generated-artifacts/DummyNoReturnERC20Token.json'; import * as DutchAuction from '../../generated-artifacts/DutchAuction.json'; import * as ERC20Proxy from '../../generated-artifacts/ERC20Proxy.json'; -import * as ERC20Token from '../../generated-artifacts/ERC20Token.json'; import * as ERC721Proxy from '../../generated-artifacts/ERC721Proxy.json'; -import * as ERC721Token from '../../generated-artifacts/ERC721Token.json'; import * as Exchange from '../../generated-artifacts/Exchange.json'; import * as ExchangeWrapper from '../../generated-artifacts/ExchangeWrapper.json'; import * as Forwarder from '../../generated-artifacts/Forwarder.json'; -import * as InvalidERC721Receiver from '../../generated-artifacts/InvalidERC721Receiver.json'; import * as MixinAuthorizable from '../../generated-artifacts/MixinAuthorizable.json'; import * as MultiAssetProxy from '../../generated-artifacts/MultiAssetProxy.json'; import * as OrderValidator from '../../generated-artifacts/OrderValidator.json'; -import * as ReentrantERC20Token from '../../generated-artifacts/ReentrantERC20Token.json'; import * as TestAssetProxyDispatcher from '../../generated-artifacts/TestAssetProxyDispatcher.json'; import * as TestAssetProxyOwner from '../../generated-artifacts/TestAssetProxyOwner.json'; import * as TestExchangeInternals from '../../generated-artifacts/TestExchangeInternals.json'; @@ -26,40 +17,25 @@ import * as TestSignatureValidator from '../../generated-artifacts/TestSignature import * as TestStaticCallReceiver from '../../generated-artifacts/TestStaticCallReceiver.json'; import * as Validator from '../../generated-artifacts/Validator.json'; import * as Wallet from '../../generated-artifacts/Wallet.json'; -import * as WETH9 from '../../generated-artifacts/WETH9.json'; import * as Whitelist from '../../generated-artifacts/Whitelist.json'; -import * as ZRXToken from '../../generated-artifacts/ZRXToken.json'; export const artifacts = { AssetProxyOwner: AssetProxyOwner as ContractArtifact, - DummyERC20Token: DummyERC20Token as ContractArtifact, - DummyERC721Receiver: DummyERC721Receiver as ContractArtifact, - DummyERC721Token: DummyERC721Token as ContractArtifact, - DummyMultipleReturnERC20Token: DummyMultipleReturnERC20Token as ContractArtifact, - DummyNoReturnERC20Token: DummyNoReturnERC20Token as ContractArtifact, DutchAuction: DutchAuction as ContractArtifact, ERC20Proxy: ERC20Proxy as ContractArtifact, - ERC20Token: ERC20Token as ContractArtifact, ERC721Proxy: ERC721Proxy as ContractArtifact, - ERC721Token: ERC721Token as ContractArtifact, Exchange: Exchange as ContractArtifact, ExchangeWrapper: ExchangeWrapper as ContractArtifact, Forwarder: Forwarder as ContractArtifact, - InvalidERC721Receiver: InvalidERC721Receiver as ContractArtifact, MixinAuthorizable: MixinAuthorizable as ContractArtifact, MultiAssetProxy: MultiAssetProxy as ContractArtifact, OrderValidator: OrderValidator as ContractArtifact, - ReentrantERC20Token: ReentrantERC20Token as ContractArtifact, TestAssetProxyDispatcher: TestAssetProxyDispatcher as ContractArtifact, TestAssetProxyOwner: TestAssetProxyOwner as ContractArtifact, TestExchangeInternals: TestExchangeInternals as ContractArtifact, TestSignatureValidator: TestSignatureValidator as ContractArtifact, TestStaticCallReceiver: TestStaticCallReceiver as ContractArtifact, Validator: Validator as ContractArtifact, - WETH9: WETH9 as ContractArtifact, Wallet: Wallet as ContractArtifact, Whitelist: Whitelist as ContractArtifact, - // Note(albrow): "as any" hack still required here because ZRXToken does not - // conform to the v2 artifact type. - ZRXToken: (ZRXToken as any) as ContractArtifact, }; diff --git a/contracts/core/src/index.ts b/contracts/core/src/index.ts new file mode 100644 index 000000000..d55f08ea2 --- /dev/null +++ b/contracts/core/src/index.ts @@ -0,0 +1,2 @@ +export * from './artifacts'; +export * from './wrappers'; diff --git a/contracts/core/src/wrappers/index.ts b/contracts/core/src/wrappers/index.ts index 92e2f6b9f..ff26a171e 100644 --- a/contracts/core/src/wrappers/index.ts +++ b/contracts/core/src/wrappers/index.ts @@ -1,21 +1,12 @@ export * from '../../generated-wrappers/asset_proxy_owner'; -export * from '../../generated-wrappers/dummy_erc20_token'; -export * from '../../generated-wrappers/dummy_erc721_receiver'; -export * from '../../generated-wrappers/dummy_erc721_token'; -export * from '../../generated-wrappers/dummy_multiple_return_erc20_token'; -export * from '../../generated-wrappers/dummy_no_return_erc20_token'; export * from '../../generated-wrappers/dutch_auction'; export * from '../../generated-wrappers/erc20_proxy'; export * from '../../generated-wrappers/erc721_proxy'; -export * from '../../generated-wrappers/erc20_token'; -export * from '../../generated-wrappers/erc721_token'; export * from '../../generated-wrappers/exchange'; export * from '../../generated-wrappers/exchange_wrapper'; export * from '../../generated-wrappers/forwarder'; -export * from '../../generated-wrappers/invalid_erc721_receiver'; export * from '../../generated-wrappers/mixin_authorizable'; export * from '../../generated-wrappers/order_validator'; -export * from '../../generated-wrappers/reentrant_erc20_token'; export * from '../../generated-wrappers/test_asset_proxy_dispatcher'; export * from '../../generated-wrappers/test_asset_proxy_owner'; export * from '../../generated-wrappers/test_exchange_internals'; @@ -23,6 +14,4 @@ export * from '../../generated-wrappers/test_signature_validator'; export * from '../../generated-wrappers/test_static_call_receiver'; export * from '../../generated-wrappers/validator'; export * from '../../generated-wrappers/wallet'; -export * from '../../generated-wrappers/weth9'; export * from '../../generated-wrappers/whitelist'; -export * from '../../generated-wrappers/zrx_token'; diff --git a/contracts/core/test/asset_proxy/proxies.ts b/contracts/core/test/asset_proxy/proxies.ts index faa643924..c4bd95905 100644 --- a/contracts/core/test/asset_proxy/proxies.ts +++ b/contracts/core/test/asset_proxy/proxies.ts @@ -9,6 +9,14 @@ import { txDefaults, web3Wrapper, } from '@0x/contracts-test-utils'; +import { + artifacts as tokensArtifacts, + DummyERC20TokenContract, + DummyERC721ReceiverContract, + DummyERC721TokenContract, + DummyMultipleReturnERC20TokenContract, + DummyNoReturnERC20TokenContract, +} from '@0x/contracts-tokens'; import { BlockchainLifecycle } from '@0x/dev-utils'; import { assetDataUtils } from '@0x/order-utils'; import { RevertReason } from '@0x/types'; @@ -16,11 +24,6 @@ import { BigNumber } from '@0x/utils'; import * as chai from 'chai'; import * as _ from 'lodash'; -import { DummyERC20TokenContract } from '../../generated-wrappers/dummy_erc20_token'; -import { DummyERC721ReceiverContract } from '../../generated-wrappers/dummy_erc721_receiver'; -import { DummyERC721TokenContract } from '../../generated-wrappers/dummy_erc721_token'; -import { DummyMultipleReturnERC20TokenContract } from '../../generated-wrappers/dummy_multiple_return_erc20_token'; -import { DummyNoReturnERC20TokenContract } from '../../generated-wrappers/dummy_no_return_erc20_token'; import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy'; import { ERC721ProxyContract } from '../../generated-wrappers/erc721_proxy'; import { MultiAssetProxyContract } from '../../generated-wrappers/multi_asset_proxy'; @@ -143,7 +146,7 @@ describe('Asset Transfer Proxies', () => { constants.DUMMY_TOKEN_DECIMALS, ); noReturnErc20Token = await DummyNoReturnERC20TokenContract.deployFrom0xArtifactAsync( - artifacts.DummyNoReturnERC20Token, + tokensArtifacts.DummyNoReturnERC20Token, provider, txDefaults, constants.DUMMY_TOKEN_NAME, @@ -152,7 +155,7 @@ describe('Asset Transfer Proxies', () => { constants.DUMMY_TOKEN_TOTAL_SUPPLY, ); multipleReturnErc20Token = await DummyMultipleReturnERC20TokenContract.deployFrom0xArtifactAsync( - artifacts.DummyMultipleReturnERC20Token, + tokensArtifacts.DummyMultipleReturnERC20Token, provider, txDefaults, constants.DUMMY_TOKEN_NAME, @@ -193,7 +196,7 @@ describe('Asset Transfer Proxies', () => { // Deploy and configure ERC721 tokens and receiver [erc721TokenA, erc721TokenB] = await erc721Wrapper.deployDummyTokensAsync(); erc721Receiver = await DummyERC721ReceiverContract.deployFrom0xArtifactAsync( - artifacts.DummyERC721Receiver, + tokensArtifacts.DummyERC721Receiver, provider, txDefaults, ); @@ -557,7 +560,7 @@ describe('Asset Transfer Proxies', () => { erc721Receiver.address, amount, ); - const logDecoder = new LogDecoder(web3Wrapper, artifacts); + const logDecoder = new LogDecoder(web3Wrapper, { ...artifacts, ...tokensArtifacts }); const tx = await logDecoder.getTxWithDecodedLogsAsync( await web3Wrapper.sendTransactionAsync({ to: erc721Proxy.address, diff --git a/contracts/core/test/exchange/core.ts b/contracts/core/test/exchange/core.ts index 45eae0f22..9b948f991 100644 --- a/contracts/core/test/exchange/core.ts +++ b/contracts/core/test/exchange/core.ts @@ -12,6 +12,14 @@ import { txDefaults, web3Wrapper, } from '@0x/contracts-test-utils'; +import { + artifacts as tokensArtifacts, + DummyERC20TokenContract, + DummyERC20TokenTransferEventArgs, + DummyERC721TokenContract, + DummyNoReturnERC20TokenContract, + ReentrantERC20TokenContract, +} from '@0x/contracts-tokens'; import { BlockchainLifecycle } from '@0x/dev-utils'; import { assetDataUtils, orderHashUtils } from '@0x/order-utils'; import { RevertReason, SignatureType, SignedOrder } from '@0x/types'; @@ -22,14 +30,10 @@ import { LogWithDecodedArgs } from 'ethereum-types'; import ethUtil = require('ethereumjs-util'); import * as _ from 'lodash'; -import { DummyERC20TokenContract, DummyERC20TokenTransferEventArgs } from '../../generated-wrappers/dummy_erc20_token'; -import { DummyERC721TokenContract } from '../../generated-wrappers/dummy_erc721_token'; -import { DummyNoReturnERC20TokenContract } from '../../generated-wrappers/dummy_no_return_erc20_token'; import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy'; import { ERC721ProxyContract } from '../../generated-wrappers/erc721_proxy'; import { ExchangeCancelEventArgs, ExchangeContract } from '../../generated-wrappers/exchange'; import { MultiAssetProxyContract } from '../../generated-wrappers/multi_asset_proxy'; -import { ReentrantERC20TokenContract } from '../../generated-wrappers/reentrant_erc20_token'; import { TestStaticCallReceiverContract } from '../../generated-wrappers/test_static_call_receiver'; import { artifacts } from '../../src/artifacts'; import { ERC20Wrapper } from '../utils/erc20_wrapper'; @@ -116,7 +120,7 @@ describe('Exchange core', () => { txDefaults, ); reentrantErc20Token = await ReentrantERC20TokenContract.deployFrom0xArtifactAsync( - artifacts.ReentrantERC20Token, + tokensArtifacts.ReentrantERC20Token, provider, txDefaults, exchange.address, @@ -341,7 +345,7 @@ describe('Exchange core', () => { describe('Testing exchange of ERC20 tokens with no return values', () => { before(async () => { noReturnErc20Token = await DummyNoReturnERC20TokenContract.deployFrom0xArtifactAsync( - artifacts.DummyNoReturnERC20Token, + tokensArtifacts.DummyNoReturnERC20Token, provider, txDefaults, constants.DUMMY_TOKEN_NAME, diff --git a/contracts/core/test/exchange/dispatcher.ts b/contracts/core/test/exchange/dispatcher.ts index 9bc5cbcce..5b1ac1e20 100644 --- a/contracts/core/test/exchange/dispatcher.ts +++ b/contracts/core/test/exchange/dispatcher.ts @@ -7,6 +7,7 @@ import { txDefaults, web3Wrapper, } from '@0x/contracts-test-utils'; +import { DummyERC20TokenContract } from '@0x/contracts-tokens'; import { BlockchainLifecycle } from '@0x/dev-utils'; import { assetDataUtils } from '@0x/order-utils'; import { AssetProxyId, RevertReason } from '@0x/types'; @@ -15,7 +16,6 @@ import * as chai from 'chai'; import { LogWithDecodedArgs } from 'ethereum-types'; import * as _ from 'lodash'; -import { DummyERC20TokenContract } from '../../generated-wrappers/dummy_erc20_token'; import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy'; import { ERC721ProxyContract } from '../../generated-wrappers/erc721_proxy'; import { diff --git a/contracts/core/test/exchange/match_orders.ts b/contracts/core/test/exchange/match_orders.ts index 0e841b359..8e943c237 100644 --- a/contracts/core/test/exchange/match_orders.ts +++ b/contracts/core/test/exchange/match_orders.ts @@ -9,6 +9,12 @@ import { txDefaults, web3Wrapper, } from '@0x/contracts-test-utils'; +import { + artifacts as tokensArtifacts, + DummyERC20TokenContract, + DummyERC721TokenContract, + ReentrantERC20TokenContract, +} from '@0x/contracts-tokens'; import { BlockchainLifecycle } from '@0x/dev-utils'; import { assetDataUtils } from '@0x/order-utils'; import { RevertReason } from '@0x/types'; @@ -17,12 +23,9 @@ import { Web3Wrapper } from '@0x/web3-wrapper'; import * as chai from 'chai'; import * as _ from 'lodash'; -import { DummyERC20TokenContract } from '../../generated-wrappers/dummy_erc20_token'; -import { DummyERC721TokenContract } from '../../generated-wrappers/dummy_erc721_token'; import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy'; import { ERC721ProxyContract } from '../../generated-wrappers/erc721_proxy'; import { ExchangeContract } from '../../generated-wrappers/exchange'; -import { ReentrantERC20TokenContract } from '../../generated-wrappers/reentrant_erc20_token'; import { TestExchangeInternalsContract } from '../../generated-wrappers/test_exchange_internals'; import { artifacts } from '../../src/artifacts'; import { ERC20Wrapper } from '../utils/erc20_wrapper'; @@ -139,7 +142,7 @@ describe('matchOrders', () => { ); reentrantErc20Token = await ReentrantERC20TokenContract.deployFrom0xArtifactAsync( - artifacts.ReentrantERC20Token, + tokensArtifacts.ReentrantERC20Token, provider, txDefaults, exchange.address, diff --git a/contracts/core/test/exchange/signature_validator.ts b/contracts/core/test/exchange/signature_validator.ts index b84a488a1..ae372d677 100644 --- a/contracts/core/test/exchange/signature_validator.ts +++ b/contracts/core/test/exchange/signature_validator.ts @@ -9,6 +9,7 @@ import { txDefaults, web3Wrapper, } from '@0x/contracts-test-utils'; +import { artifacts as tokensArtifacts } from '@0x/contracts-tokens'; import { BlockchainLifecycle } from '@0x/dev-utils'; import { assetDataUtils, orderHashUtils, signatureUtils } from '@0x/order-utils'; import { RevertReason, SignatureType, SignedOrder } from '@0x/types'; @@ -17,13 +18,13 @@ import { LogWithDecodedArgs } from 'ethereum-types'; import ethUtil = require('ethereumjs-util'); import { + artifacts, TestSignatureValidatorContract, TestSignatureValidatorSignatureValidatorApprovalEventArgs, -} from '../../generated-wrappers/test_signature_validator'; -import { TestStaticCallReceiverContract } from '../../generated-wrappers/test_static_call_receiver'; -import { ValidatorContract } from '../../generated-wrappers/validator'; -import { WalletContract } from '../../generated-wrappers/wallet'; -import { artifacts } from '../../src/artifacts'; + TestStaticCallReceiverContract, + ValidatorContract, + WalletContract, +} from '../../src'; chaiSetup.configure(); const expect = chai.expect; @@ -77,7 +78,7 @@ describe('MixinSignatureValidator', () => { provider, txDefaults, ); - signatureValidatorLogDecoder = new LogDecoder(web3Wrapper, artifacts); + signatureValidatorLogDecoder = new LogDecoder(web3Wrapper, { ...artifacts, ...tokensArtifacts }); await web3Wrapper.awaitTransactionSuccessAsync( await signatureValidator.setSignatureValidatorApproval.sendTransactionAsync(testValidator.address, true, { from: signerAddress, diff --git a/contracts/core/test/exchange/transactions.ts b/contracts/core/test/exchange/transactions.ts index c4086d9be..ad0ca41c2 100644 --- a/contracts/core/test/exchange/transactions.ts +++ b/contracts/core/test/exchange/transactions.ts @@ -11,6 +11,7 @@ import { txDefaults, web3Wrapper, } from '@0x/contracts-test-utils'; +import { DummyERC20TokenContract } from '@0x/contracts-tokens'; import { BlockchainLifecycle } from '@0x/dev-utils'; import { assetDataUtils, generatePseudoRandomSalt } from '@0x/order-utils'; import { OrderWithoutExchangeAddress, RevertReason, SignedOrder } from '@0x/types'; @@ -18,7 +19,6 @@ import { BigNumber } from '@0x/utils'; import * as chai from 'chai'; import * as _ from 'lodash'; -import { DummyERC20TokenContract } from '../../generated-wrappers/dummy_erc20_token'; import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy'; import { ExchangeContract } from '../../generated-wrappers/exchange'; import { ExchangeWrapperContract } from '../../generated-wrappers/exchange_wrapper'; diff --git a/contracts/core/test/exchange/wrapper.ts b/contracts/core/test/exchange/wrapper.ts index 17cb7a3bb..c9efb0c9d 100644 --- a/contracts/core/test/exchange/wrapper.ts +++ b/contracts/core/test/exchange/wrapper.ts @@ -11,6 +11,12 @@ import { txDefaults, web3Wrapper, } from '@0x/contracts-test-utils'; +import { + artifacts as tokensArtifacts, + DummyERC20TokenContract, + DummyERC721TokenContract, + ReentrantERC20TokenContract, +} from '@0x/contracts-tokens'; import { BlockchainLifecycle } from '@0x/dev-utils'; import { assetDataUtils, orderHashUtils } from '@0x/order-utils'; import { RevertReason, SignedOrder } from '@0x/types'; @@ -19,12 +25,9 @@ import { Web3Wrapper } from '@0x/web3-wrapper'; import * as chai from 'chai'; import * as _ from 'lodash'; -import { DummyERC20TokenContract } from '../../generated-wrappers/dummy_erc20_token'; -import { DummyERC721TokenContract } from '../../generated-wrappers/dummy_erc721_token'; import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy'; import { ERC721ProxyContract } from '../../generated-wrappers/erc721_proxy'; import { ExchangeContract } from '../../generated-wrappers/exchange'; -import { ReentrantERC20TokenContract } from '../../generated-wrappers/reentrant_erc20_token'; import { artifacts } from '../../src/artifacts'; import { ERC20Wrapper } from '../utils/erc20_wrapper'; import { ERC721Wrapper } from '../utils/erc721_wrapper'; @@ -113,7 +116,7 @@ describe('Exchange wrappers', () => { ); reentrantErc20Token = await ReentrantERC20TokenContract.deployFrom0xArtifactAsync( - artifacts.ReentrantERC20Token, + tokensArtifacts.ReentrantERC20Token, provider, txDefaults, exchange.address, diff --git a/contracts/core/test/extensions/dutch_auction.ts b/contracts/core/test/extensions/dutch_auction.ts index 54e6092d7..b396d4206 100644 --- a/contracts/core/test/extensions/dutch_auction.ts +++ b/contracts/core/test/extensions/dutch_auction.ts @@ -10,6 +10,12 @@ import { txDefaults, web3Wrapper, } from '@0x/contracts-test-utils'; +import { + artifacts as tokensArtifacts, + DummyERC20TokenContract, + DummyERC721TokenContract, + WETH9Contract, +} from '@0x/contracts-tokens'; import { BlockchainLifecycle } from '@0x/dev-utils'; import { assetDataUtils, generatePseudoRandomSalt } from '@0x/order-utils'; import { RevertReason, SignedOrder } from '@0x/types'; @@ -20,11 +26,8 @@ import ethAbi = require('ethereumjs-abi'); import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; -import { DummyERC20TokenContract } from '../../generated-wrappers/dummy_erc20_token'; -import { DummyERC721TokenContract } from '../../generated-wrappers/dummy_erc721_token'; import { DutchAuctionContract } from '../../generated-wrappers/dutch_auction'; import { ExchangeContract } from '../../generated-wrappers/exchange'; -import { WETH9Contract } from '../../generated-wrappers/weth9'; import { artifacts } from '../../src/artifacts'; import { ERC20Wrapper } from '../utils/erc20_wrapper'; import { ERC721Wrapper } from '../utils/erc721_wrapper'; @@ -98,7 +101,7 @@ describe(ContractName.DutchAuction, () => { const erc721Balances = await erc721Wrapper.getBalancesAsync(); erc721MakerAssetIds = erc721Balances[makerAddress][erc721Token.address]; - wethContract = await WETH9Contract.deployFrom0xArtifactAsync(artifacts.WETH9, provider, txDefaults); + wethContract = await WETH9Contract.deployFrom0xArtifactAsync(tokensArtifacts.WETH9, provider, txDefaults); erc20Wrapper.addDummyTokenContract(wethContract as any); const zrxAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); diff --git a/contracts/core/test/extensions/forwarder.ts b/contracts/core/test/extensions/forwarder.ts index 44ad4d6ff..ffa015dd2 100644 --- a/contracts/core/test/extensions/forwarder.ts +++ b/contracts/core/test/extensions/forwarder.ts @@ -11,6 +11,12 @@ import { txDefaults, web3Wrapper, } from '@0x/contracts-test-utils'; +import { + artifacts as tokenArtifacts, + DummyERC20TokenContract, + DummyERC721TokenContract, + WETH9Contract, +} from '@0x/contracts-tokens'; import { BlockchainLifecycle } from '@0x/dev-utils'; import { assetDataUtils } from '@0x/order-utils'; import { RevertReason, SignedOrder } from '@0x/types'; @@ -19,11 +25,8 @@ import { Web3Wrapper } from '@0x/web3-wrapper'; import * as chai from 'chai'; import { TransactionReceiptWithDecodedLogs } from 'ethereum-types'; -import { DummyERC20TokenContract } from '../../generated-wrappers/dummy_erc20_token'; -import { DummyERC721TokenContract } from '../../generated-wrappers/dummy_erc721_token'; import { ExchangeContract } from '../../generated-wrappers/exchange'; import { ForwarderContract } from '../../generated-wrappers/forwarder'; -import { WETH9Contract } from '../../generated-wrappers/weth9'; import { artifacts } from '../../src/artifacts'; import { ERC20Wrapper } from '../utils/erc20_wrapper'; import { ERC721Wrapper } from '../utils/erc721_wrapper'; @@ -94,7 +97,7 @@ describe(ContractName.Forwarder, () => { const erc721Balances = await erc721Wrapper.getBalancesAsync(); erc721MakerAssetIds = erc721Balances[makerAddress][erc721Token.address]; - wethContract = await WETH9Contract.deployFrom0xArtifactAsync(artifacts.WETH9, provider, txDefaults); + wethContract = await WETH9Contract.deployFrom0xArtifactAsync(tokenArtifacts.WETH9, provider, txDefaults); weth = new DummyERC20TokenContract(wethContract.abi, wethContract.address, provider); erc20Wrapper.addDummyTokenContract(weth); diff --git a/contracts/core/test/extensions/order_validator.ts b/contracts/core/test/extensions/order_validator.ts index 3dbe76f6e..622710c98 100644 --- a/contracts/core/test/extensions/order_validator.ts +++ b/contracts/core/test/extensions/order_validator.ts @@ -7,6 +7,7 @@ import { txDefaults, web3Wrapper, } from '@0x/contracts-test-utils'; +import { DummyERC20TokenContract, DummyERC721TokenContract } from '@0x/contracts-tokens'; import { BlockchainLifecycle } from '@0x/dev-utils'; import { assetDataUtils, orderHashUtils } from '@0x/order-utils'; import { SignedOrder } from '@0x/types'; @@ -14,8 +15,6 @@ import { BigNumber } from '@0x/utils'; import * as chai from 'chai'; import * as _ from 'lodash'; -import { DummyERC20TokenContract } from '../../generated-wrappers/dummy_erc20_token'; -import { DummyERC721TokenContract } from '../../generated-wrappers/dummy_erc721_token'; import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy'; import { ERC721ProxyContract } from '../../generated-wrappers/erc721_proxy'; import { ExchangeContract } from '../../generated-wrappers/exchange'; diff --git a/contracts/core/test/tokens/erc721_token.ts b/contracts/core/test/tokens/erc721_token.ts deleted file mode 100644 index 3b0a5f001..000000000 --- a/contracts/core/test/tokens/erc721_token.ts +++ /dev/null @@ -1,284 +0,0 @@ -import { - chaiSetup, - constants, - expectTransactionFailedAsync, - expectTransactionFailedWithoutReasonAsync, - LogDecoder, - provider, - txDefaults, - web3Wrapper, -} from '@0x/contracts-test-utils'; -import { BlockchainLifecycle } from '@0x/dev-utils'; -import { RevertReason } from '@0x/types'; -import { BigNumber } from '@0x/utils'; -import * as chai from 'chai'; -import { LogWithDecodedArgs } from 'ethereum-types'; - -import { - DummyERC721ReceiverContract, - DummyERC721ReceiverTokenReceivedEventArgs, -} from '../../generated-wrappers/dummy_erc721_receiver'; -import { - DummyERC721TokenContract, - DummyERC721TokenTransferEventArgs, -} from '../../generated-wrappers/dummy_erc721_token'; -import { InvalidERC721ReceiverContract } from '../../generated-wrappers/invalid_erc721_receiver'; -import { artifacts } from '../../src/artifacts'; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); -// tslint:disable:no-unnecessary-type-assertion -describe('ERC721Token', () => { - let owner: string; - let spender: string; - let token: DummyERC721TokenContract; - let erc721Receiver: DummyERC721ReceiverContract; - let logDecoder: LogDecoder; - const tokenId = new BigNumber(1); - before(async () => { - await blockchainLifecycle.startAsync(); - }); - after(async () => { - await blockchainLifecycle.revertAsync(); - }); - before(async () => { - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - owner = accounts[0]; - spender = accounts[1]; - token = await DummyERC721TokenContract.deployFrom0xArtifactAsync( - artifacts.DummyERC721Token, - provider, - txDefaults, - constants.DUMMY_TOKEN_NAME, - constants.DUMMY_TOKEN_SYMBOL, - ); - erc721Receiver = await DummyERC721ReceiverContract.deployFrom0xArtifactAsync( - artifacts.DummyERC721Receiver, - provider, - txDefaults, - ); - logDecoder = new LogDecoder(web3Wrapper, artifacts); - await web3Wrapper.awaitTransactionSuccessAsync( - await token.mint.sendTransactionAsync(owner, tokenId, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - - describe('transferFrom', () => { - it('should revert if the tokenId is not owner', async () => { - const from = owner; - const to = erc721Receiver.address; - const unownedTokenId = new BigNumber(2); - await expectTransactionFailedAsync( - token.transferFrom.sendTransactionAsync(from, to, unownedTokenId), - RevertReason.Erc721ZeroOwner, - ); - }); - it('should revert if transferring to a null address', async () => { - const from = owner; - const to = constants.NULL_ADDRESS; - await expectTransactionFailedAsync( - token.transferFrom.sendTransactionAsync(from, to, tokenId), - RevertReason.Erc721ZeroToAddress, - ); - }); - it('should revert if the from address does not own the token', async () => { - const from = spender; - const to = erc721Receiver.address; - await expectTransactionFailedAsync( - token.transferFrom.sendTransactionAsync(from, to, tokenId), - RevertReason.Erc721OwnerMismatch, - ); - }); - it('should revert if spender does not own the token, is not approved, and is not approved for all', async () => { - const from = owner; - const to = erc721Receiver.address; - await expectTransactionFailedAsync( - token.transferFrom.sendTransactionAsync(from, to, tokenId, { from: spender }), - RevertReason.Erc721InvalidSpender, - ); - }); - it('should transfer the token if called by owner', async () => { - const from = owner; - const to = erc721Receiver.address; - const txReceipt = await logDecoder.getTxWithDecodedLogsAsync( - await token.transferFrom.sendTransactionAsync(from, to, tokenId), - ); - const newOwner = await token.ownerOf.callAsync(tokenId); - expect(newOwner).to.be.equal(to); - const log = txReceipt.logs[0] as LogWithDecodedArgs; - expect(log.args._from).to.be.equal(from); - expect(log.args._to).to.be.equal(to); - expect(log.args._tokenId).to.be.bignumber.equal(tokenId); - }); - it('should transfer the token if spender is approved for all', async () => { - const isApproved = true; - await web3Wrapper.awaitTransactionSuccessAsync( - await token.setApprovalForAll.sendTransactionAsync(spender, isApproved), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - const from = owner; - const to = erc721Receiver.address; - const txReceipt = await logDecoder.getTxWithDecodedLogsAsync( - await token.transferFrom.sendTransactionAsync(from, to, tokenId), - ); - const newOwner = await token.ownerOf.callAsync(tokenId); - expect(newOwner).to.be.equal(to); - const log = txReceipt.logs[0] as LogWithDecodedArgs; - expect(log.args._from).to.be.equal(from); - expect(log.args._to).to.be.equal(to); - expect(log.args._tokenId).to.be.bignumber.equal(tokenId); - }); - it('should transfer the token if spender is individually approved', async () => { - await web3Wrapper.awaitTransactionSuccessAsync( - await token.approve.sendTransactionAsync(spender, tokenId), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - const from = owner; - const to = erc721Receiver.address; - const txReceipt = await logDecoder.getTxWithDecodedLogsAsync( - await token.transferFrom.sendTransactionAsync(from, to, tokenId), - ); - const newOwner = await token.ownerOf.callAsync(tokenId); - expect(newOwner).to.be.equal(to); - - const approvedAddress = await token.getApproved.callAsync(tokenId); - expect(approvedAddress).to.be.equal(constants.NULL_ADDRESS); - const log = txReceipt.logs[0] as LogWithDecodedArgs; - expect(log.args._from).to.be.equal(from); - expect(log.args._to).to.be.equal(to); - expect(log.args._tokenId).to.be.bignumber.equal(tokenId); - }); - }); - describe('safeTransferFrom without data', () => { - it('should transfer token to a non-contract address if called by owner', async () => { - const from = owner; - const to = spender; - const txReceipt = await logDecoder.getTxWithDecodedLogsAsync( - await token.safeTransferFrom1.sendTransactionAsync(from, to, tokenId), - ); - const newOwner = await token.ownerOf.callAsync(tokenId); - expect(newOwner).to.be.equal(to); - const log = txReceipt.logs[0] as LogWithDecodedArgs; - expect(log.args._from).to.be.equal(from); - expect(log.args._to).to.be.equal(to); - expect(log.args._tokenId).to.be.bignumber.equal(tokenId); - }); - it('should revert if transferring to a contract address without onERC721Received', async () => { - const contract = await DummyERC721TokenContract.deployFrom0xArtifactAsync( - artifacts.DummyERC721Token, - provider, - txDefaults, - constants.DUMMY_TOKEN_NAME, - constants.DUMMY_TOKEN_SYMBOL, - ); - const from = owner; - const to = contract.address; - await expectTransactionFailedWithoutReasonAsync( - token.safeTransferFrom1.sendTransactionAsync(from, to, tokenId), - ); - }); - it('should revert if onERC721Received does not return the correct value', async () => { - const invalidErc721Receiver = await InvalidERC721ReceiverContract.deployFrom0xArtifactAsync( - artifacts.InvalidERC721Receiver, - provider, - txDefaults, - ); - const from = owner; - const to = invalidErc721Receiver.address; - await expectTransactionFailedAsync( - token.safeTransferFrom1.sendTransactionAsync(from, to, tokenId), - RevertReason.Erc721InvalidSelector, - ); - }); - it('should transfer to contract and call onERC721Received with correct return value', async () => { - const from = owner; - const to = erc721Receiver.address; - const txReceipt = await logDecoder.getTxWithDecodedLogsAsync( - await token.safeTransferFrom1.sendTransactionAsync(from, to, tokenId), - ); - const newOwner = await token.ownerOf.callAsync(tokenId); - expect(newOwner).to.be.equal(to); - const transferLog = txReceipt.logs[0] as LogWithDecodedArgs; - const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs; - expect(transferLog.args._from).to.be.equal(from); - expect(transferLog.args._to).to.be.equal(to); - expect(transferLog.args._tokenId).to.be.bignumber.equal(tokenId); - expect(receiverLog.args.operator).to.be.equal(owner); - expect(receiverLog.args.from).to.be.equal(from); - expect(receiverLog.args.tokenId).to.be.bignumber.equal(tokenId); - expect(receiverLog.args.data).to.be.equal(constants.NULL_BYTES); - }); - }); - describe('safeTransferFrom with data', () => { - const data = '0x0102030405060708090a0b0c0d0e0f'; - it('should transfer token to a non-contract address if called by owner', async () => { - const from = owner; - const to = spender; - const txReceipt = await logDecoder.getTxWithDecodedLogsAsync( - await token.safeTransferFrom2.sendTransactionAsync(from, to, tokenId, data), - ); - const newOwner = await token.ownerOf.callAsync(tokenId); - expect(newOwner).to.be.equal(to); - const log = txReceipt.logs[0] as LogWithDecodedArgs; - expect(log.args._from).to.be.equal(from); - expect(log.args._to).to.be.equal(to); - expect(log.args._tokenId).to.be.bignumber.equal(tokenId); - }); - it('should revert if transferring to a contract address without onERC721Received', async () => { - const contract = await DummyERC721TokenContract.deployFrom0xArtifactAsync( - artifacts.DummyERC721Token, - provider, - txDefaults, - constants.DUMMY_TOKEN_NAME, - constants.DUMMY_TOKEN_SYMBOL, - ); - const from = owner; - const to = contract.address; - await expectTransactionFailedWithoutReasonAsync( - token.safeTransferFrom2.sendTransactionAsync(from, to, tokenId, data), - ); - }); - it('should revert if onERC721Received does not return the correct value', async () => { - const invalidErc721Receiver = await InvalidERC721ReceiverContract.deployFrom0xArtifactAsync( - artifacts.InvalidERC721Receiver, - provider, - txDefaults, - ); - const from = owner; - const to = invalidErc721Receiver.address; - await expectTransactionFailedAsync( - token.safeTransferFrom2.sendTransactionAsync(from, to, tokenId, data), - RevertReason.Erc721InvalidSelector, - ); - }); - it('should transfer to contract and call onERC721Received with correct return value', async () => { - const from = owner; - const to = erc721Receiver.address; - const txReceipt = await logDecoder.getTxWithDecodedLogsAsync( - await token.safeTransferFrom2.sendTransactionAsync(from, to, tokenId, data), - ); - const newOwner = await token.ownerOf.callAsync(tokenId); - expect(newOwner).to.be.equal(to); - const transferLog = txReceipt.logs[0] as LogWithDecodedArgs; - const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs; - expect(transferLog.args._from).to.be.equal(from); - expect(transferLog.args._to).to.be.equal(to); - expect(transferLog.args._tokenId).to.be.bignumber.equal(tokenId); - expect(receiverLog.args.operator).to.be.equal(owner); - expect(receiverLog.args.from).to.be.equal(from); - expect(receiverLog.args.tokenId).to.be.bignumber.equal(tokenId); - expect(receiverLog.args.data).to.be.equal(data); - }); - }); -}); -// tslint:enable:no-unnecessary-type-assertion diff --git a/contracts/core/test/tokens/unlimited_allowance_token.ts b/contracts/core/test/tokens/unlimited_allowance_token.ts deleted file mode 100644 index c3e4072c5..000000000 --- a/contracts/core/test/tokens/unlimited_allowance_token.ts +++ /dev/null @@ -1,195 +0,0 @@ -import { - chaiSetup, - constants, - expectContractCallFailedAsync, - provider, - txDefaults, - web3Wrapper, -} from '@0x/contracts-test-utils'; -import { BlockchainLifecycle } from '@0x/dev-utils'; -import { RevertReason } from '@0x/types'; -import { BigNumber } from '@0x/utils'; -import * as chai from 'chai'; - -import { DummyERC20TokenContract } from '../../generated-wrappers/dummy_erc20_token'; -import { artifacts } from '../../src/artifacts'; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); - -describe('UnlimitedAllowanceToken', () => { - let owner: string; - let spender: string; - const MAX_MINT_VALUE = new BigNumber(10000000000000000000000); - let token: DummyERC20TokenContract; - - before(async () => { - await blockchainLifecycle.startAsync(); - }); - after(async () => { - await blockchainLifecycle.revertAsync(); - }); - before(async () => { - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - owner = accounts[0]; - spender = accounts[1]; - token = await DummyERC20TokenContract.deployFrom0xArtifactAsync( - artifacts.DummyERC20Token, - provider, - txDefaults, - constants.DUMMY_TOKEN_NAME, - constants.DUMMY_TOKEN_SYMBOL, - constants.DUMMY_TOKEN_DECIMALS, - constants.DUMMY_TOKEN_TOTAL_SUPPLY, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await token.mint.sendTransactionAsync(MAX_MINT_VALUE, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - describe('transfer', () => { - it('should throw if owner has insufficient balance', async () => { - const ownerBalance = await token.balanceOf.callAsync(owner); - const amountToTransfer = ownerBalance.plus(1); - return expectContractCallFailedAsync( - token.transfer.callAsync(spender, amountToTransfer, { from: owner }), - RevertReason.Erc20InsufficientBalance, - ); - }); - - it('should transfer balance from sender to receiver', async () => { - const receiver = spender; - const initOwnerBalance = await token.balanceOf.callAsync(owner); - const amountToTransfer = new BigNumber(1); - await web3Wrapper.awaitTransactionSuccessAsync( - await token.transfer.sendTransactionAsync(receiver, amountToTransfer, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const finalOwnerBalance = await token.balanceOf.callAsync(owner); - const finalReceiverBalance = await token.balanceOf.callAsync(receiver); - - const expectedFinalOwnerBalance = initOwnerBalance.minus(amountToTransfer); - const expectedFinalReceiverBalance = amountToTransfer; - expect(finalOwnerBalance).to.be.bignumber.equal(expectedFinalOwnerBalance); - expect(finalReceiverBalance).to.be.bignumber.equal(expectedFinalReceiverBalance); - }); - - it('should return true on a 0 value transfer', async () => { - const didReturnTrue = await token.transfer.callAsync(spender, new BigNumber(0), { - from: owner, - }); - expect(didReturnTrue).to.be.true(); - }); - }); - - describe('transferFrom', () => { - it('should throw if owner has insufficient balance', async () => { - const ownerBalance = await token.balanceOf.callAsync(owner); - const amountToTransfer = ownerBalance.plus(1); - await web3Wrapper.awaitTransactionSuccessAsync( - await token.approve.sendTransactionAsync(spender, amountToTransfer, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - return expectContractCallFailedAsync( - token.transferFrom.callAsync(owner, spender, amountToTransfer, { - from: spender, - }), - RevertReason.Erc20InsufficientBalance, - ); - }); - - it('should throw if spender has insufficient allowance', async () => { - const ownerBalance = await token.balanceOf.callAsync(owner); - const amountToTransfer = ownerBalance; - - const spenderAllowance = await token.allowance.callAsync(owner, spender); - const isSpenderAllowanceInsufficient = spenderAllowance.cmp(amountToTransfer) < 0; - expect(isSpenderAllowanceInsufficient).to.be.true(); - - return expectContractCallFailedAsync( - token.transferFrom.callAsync(owner, spender, amountToTransfer, { - from: spender, - }), - RevertReason.Erc20InsufficientAllowance, - ); - }); - - it('should return true on a 0 value transfer', async () => { - const amountToTransfer = new BigNumber(0); - const didReturnTrue = await token.transferFrom.callAsync(owner, spender, amountToTransfer, { - from: spender, - }); - expect(didReturnTrue).to.be.true(); - }); - - it('should not modify spender allowance if spender allowance is 2^256 - 1', async () => { - const initOwnerBalance = await token.balanceOf.callAsync(owner); - const amountToTransfer = initOwnerBalance; - const initSpenderAllowance = constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; - await web3Wrapper.awaitTransactionSuccessAsync( - await token.approve.sendTransactionAsync(spender, initSpenderAllowance, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await token.transferFrom.sendTransactionAsync(owner, spender, amountToTransfer, { - from: spender, - gas: constants.MAX_TOKEN_TRANSFERFROM_GAS, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - const newSpenderAllowance = await token.allowance.callAsync(owner, spender); - expect(initSpenderAllowance).to.be.bignumber.equal(newSpenderAllowance); - }); - - it('should transfer the correct balances if spender has sufficient allowance', async () => { - const initOwnerBalance = await token.balanceOf.callAsync(owner); - const amountToTransfer = initOwnerBalance; - const initSpenderAllowance = initOwnerBalance; - await web3Wrapper.awaitTransactionSuccessAsync( - await token.approve.sendTransactionAsync(spender, initSpenderAllowance, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await token.transferFrom.sendTransactionAsync(owner, spender, amountToTransfer, { - from: spender, - gas: constants.MAX_TOKEN_TRANSFERFROM_GAS, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - const newOwnerBalance = await token.balanceOf.callAsync(owner); - const newSpenderBalance = await token.balanceOf.callAsync(spender); - - expect(newOwnerBalance).to.be.bignumber.equal(0); - expect(newSpenderBalance).to.be.bignumber.equal(initOwnerBalance); - }); - - it('should modify allowance if spender has sufficient allowance less than 2^256 - 1', async () => { - const initOwnerBalance = await token.balanceOf.callAsync(owner); - const amountToTransfer = initOwnerBalance; - const initSpenderAllowance = initOwnerBalance; - await web3Wrapper.awaitTransactionSuccessAsync( - await token.approve.sendTransactionAsync(spender, initSpenderAllowance, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await token.transferFrom.sendTransactionAsync(owner, spender, amountToTransfer, { - from: spender, - gas: constants.MAX_TOKEN_TRANSFERFROM_GAS, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - const newSpenderAllowance = await token.allowance.callAsync(owner, spender); - expect(newSpenderAllowance).to.be.bignumber.equal(0); - }); - }); -}); diff --git a/contracts/core/test/tokens/weth9.ts b/contracts/core/test/tokens/weth9.ts deleted file mode 100644 index 225481904..000000000 --- a/contracts/core/test/tokens/weth9.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { - chaiSetup, - constants, - expectInsufficientFundsAsync, - expectTransactionFailedWithoutReasonAsync, - provider, - txDefaults, - web3Wrapper, -} from '@0x/contracts-test-utils'; -import { BlockchainLifecycle } from '@0x/dev-utils'; -import { BigNumber } from '@0x/utils'; -import { Web3Wrapper } from '@0x/web3-wrapper'; -import * as chai from 'chai'; - -import { WETH9Contract } from '../../generated-wrappers/weth9'; -import { artifacts } from '../../src/artifacts'; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); - -describe('EtherToken', () => { - let account: string; - const gasPrice = Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 9); - let etherToken: WETH9Contract; - - before(async () => { - await blockchainLifecycle.startAsync(); - }); - after(async () => { - await blockchainLifecycle.revertAsync(); - }); - before(async () => { - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - account = accounts[0]; - - etherToken = await WETH9Contract.deployFrom0xArtifactAsync(artifacts.WETH9, provider, { - gasPrice, - ...txDefaults, - }); - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - describe('deposit', () => { - it('should throw if caller attempts to deposit more Ether than caller balance', async () => { - const initEthBalance = await web3Wrapper.getBalanceInWeiAsync(account); - const ethToDeposit = initEthBalance.plus(1); - - return expectInsufficientFundsAsync(etherToken.deposit.sendTransactionAsync({ value: ethToDeposit })); - }); - - it('should convert deposited Ether to wrapped Ether tokens', async () => { - const initEthBalance = await web3Wrapper.getBalanceInWeiAsync(account); - const initEthTokenBalance = await etherToken.balanceOf.callAsync(account); - - const ethToDeposit = new BigNumber(Web3Wrapper.toWei(new BigNumber(1))); - - const txHash = await etherToken.deposit.sendTransactionAsync({ value: ethToDeposit }); - const receipt = await web3Wrapper.awaitTransactionSuccessAsync( - txHash, - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - const ethSpentOnGas = gasPrice.times(receipt.gasUsed); - const finalEthBalance = await web3Wrapper.getBalanceInWeiAsync(account); - const finalEthTokenBalance = await etherToken.balanceOf.callAsync(account); - - expect(finalEthBalance).to.be.bignumber.equal(initEthBalance.minus(ethToDeposit.plus(ethSpentOnGas))); - expect(finalEthTokenBalance).to.be.bignumber.equal(initEthTokenBalance.plus(ethToDeposit)); - }); - }); - - describe('withdraw', () => { - it('should throw if caller attempts to withdraw greater than caller balance', async () => { - const initEthTokenBalance = await etherToken.balanceOf.callAsync(account); - const ethTokensToWithdraw = initEthTokenBalance.plus(1); - - return expectTransactionFailedWithoutReasonAsync( - etherToken.withdraw.sendTransactionAsync(ethTokensToWithdraw), - ); - }); - - it('should convert ether tokens to ether with sufficient balance', async () => { - const ethToDeposit = new BigNumber(Web3Wrapper.toWei(new BigNumber(1))); - await web3Wrapper.awaitTransactionSuccessAsync( - await etherToken.deposit.sendTransactionAsync({ value: ethToDeposit }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const initEthTokenBalance = await etherToken.balanceOf.callAsync(account); - const initEthBalance = await web3Wrapper.getBalanceInWeiAsync(account); - const ethTokensToWithdraw = initEthTokenBalance; - expect(ethTokensToWithdraw).to.not.be.bignumber.equal(0); - const txHash = await etherToken.withdraw.sendTransactionAsync(ethTokensToWithdraw, { - gas: constants.MAX_ETHERTOKEN_WITHDRAW_GAS, - }); - const receipt = await web3Wrapper.awaitTransactionSuccessAsync( - txHash, - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - const ethSpentOnGas = gasPrice.times(receipt.gasUsed); - const finalEthBalance = await web3Wrapper.getBalanceInWeiAsync(account); - const finalEthTokenBalance = await etherToken.balanceOf.callAsync(account); - - expect(finalEthBalance).to.be.bignumber.equal( - initEthBalance.plus(ethTokensToWithdraw.minus(ethSpentOnGas)), - ); - expect(finalEthTokenBalance).to.be.bignumber.equal(initEthTokenBalance.minus(ethTokensToWithdraw)); - }); - }); - - describe('fallback', () => { - it('should convert sent ether to ether tokens', async () => { - const initEthBalance = await web3Wrapper.getBalanceInWeiAsync(account); - const initEthTokenBalance = await etherToken.balanceOf.callAsync(account); - - const ethToDeposit = Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18); - - const txHash = await web3Wrapper.sendTransactionAsync({ - from: account, - to: etherToken.address, - value: ethToDeposit, - gasPrice, - }); - - const receipt = await web3Wrapper.awaitTransactionSuccessAsync( - txHash, - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - const ethSpentOnGas = gasPrice.times(receipt.gasUsed); - const finalEthBalance = await web3Wrapper.getBalanceInWeiAsync(account); - const finalEthTokenBalance = await etherToken.balanceOf.callAsync(account); - - expect(finalEthBalance).to.be.bignumber.equal(initEthBalance.minus(ethToDeposit.plus(ethSpentOnGas))); - expect(finalEthTokenBalance).to.be.bignumber.equal(initEthTokenBalance.plus(ethToDeposit)); - }); - }); -}); diff --git a/contracts/core/test/tokens/zrx_token.ts b/contracts/core/test/tokens/zrx_token.ts deleted file mode 100644 index 6bc5e164c..000000000 --- a/contracts/core/test/tokens/zrx_token.ts +++ /dev/null @@ -1,204 +0,0 @@ -import { chaiSetup, constants, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils'; -import { BlockchainLifecycle } from '@0x/dev-utils'; -import { BigNumber } from '@0x/utils'; -import { Web3Wrapper } from '@0x/web3-wrapper'; -import * as chai from 'chai'; - -import { ZRXTokenContract } from '../../generated-wrappers/zrx_token'; -import { artifacts } from '../../src/artifacts'; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); - -describe('ZRXToken', () => { - let owner: string; - let spender: string; - let MAX_UINT: BigNumber; - let zrxToken: ZRXTokenContract; - - before(async () => { - await blockchainLifecycle.startAsync(); - }); - after(async () => { - await blockchainLifecycle.revertAsync(); - }); - before(async () => { - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - owner = accounts[0]; - spender = accounts[1]; - zrxToken = await ZRXTokenContract.deployFrom0xArtifactAsync(artifacts.ZRXToken, provider, txDefaults); - MAX_UINT = constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - describe('constants', () => { - it('should have 18 decimals', async () => { - const decimals = new BigNumber(await zrxToken.decimals.callAsync()); - const expectedDecimals = 18; - expect(decimals).to.be.bignumber.equal(expectedDecimals); - }); - - it('should have a total supply of 1 billion tokens', async () => { - const totalSupply = new BigNumber(await zrxToken.totalSupply.callAsync()); - const expectedTotalSupply = 1000000000; - expect(Web3Wrapper.toUnitAmount(totalSupply, 18)).to.be.bignumber.equal(expectedTotalSupply); - }); - - it('should be named 0x Protocol Token', async () => { - const name = await zrxToken.name.callAsync(); - const expectedName = '0x Protocol Token'; - expect(name).to.be.equal(expectedName); - }); - - it('should have the symbol ZRX', async () => { - const symbol = await zrxToken.symbol.callAsync(); - const expectedSymbol = 'ZRX'; - expect(symbol).to.be.equal(expectedSymbol); - }); - }); - - describe('constructor', () => { - it('should initialize owner balance to totalSupply', async () => { - const ownerBalance = await zrxToken.balanceOf.callAsync(owner); - const totalSupply = new BigNumber(await zrxToken.totalSupply.callAsync()); - expect(totalSupply).to.be.bignumber.equal(ownerBalance); - }); - }); - - describe('transfer', () => { - it('should transfer balance from sender to receiver', async () => { - const receiver = spender; - const initOwnerBalance = await zrxToken.balanceOf.callAsync(owner); - const amountToTransfer = new BigNumber(1); - await web3Wrapper.awaitTransactionSuccessAsync( - await zrxToken.transfer.sendTransactionAsync(receiver, amountToTransfer, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const finalOwnerBalance = await zrxToken.balanceOf.callAsync(owner); - const finalReceiverBalance = await zrxToken.balanceOf.callAsync(receiver); - - const expectedFinalOwnerBalance = initOwnerBalance.minus(amountToTransfer); - const expectedFinalReceiverBalance = amountToTransfer; - expect(finalOwnerBalance).to.be.bignumber.equal(expectedFinalOwnerBalance); - expect(finalReceiverBalance).to.be.bignumber.equal(expectedFinalReceiverBalance); - }); - - it('should return true on a 0 value transfer', async () => { - const didReturnTrue = await zrxToken.transfer.callAsync(spender, new BigNumber(0), { - from: owner, - }); - expect(didReturnTrue).to.be.true(); - }); - }); - - describe('transferFrom', () => { - it('should return false if owner has insufficient balance', async () => { - const ownerBalance = await zrxToken.balanceOf.callAsync(owner); - const amountToTransfer = ownerBalance.plus(1); - await web3Wrapper.awaitTransactionSuccessAsync( - await zrxToken.approve.sendTransactionAsync(spender, amountToTransfer, { - from: owner, - gas: constants.MAX_TOKEN_APPROVE_GAS, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const didReturnTrue = await zrxToken.transferFrom.callAsync(owner, spender, amountToTransfer, { - from: spender, - }); - expect(didReturnTrue).to.be.false(); - }); - - it('should return false if spender has insufficient allowance', async () => { - const ownerBalance = await zrxToken.balanceOf.callAsync(owner); - const amountToTransfer = ownerBalance; - - const spenderAllowance = await zrxToken.allowance.callAsync(owner, spender); - const isSpenderAllowanceInsufficient = spenderAllowance.cmp(amountToTransfer) < 0; - expect(isSpenderAllowanceInsufficient).to.be.true(); - - const didReturnTrue = await zrxToken.transferFrom.callAsync(owner, spender, amountToTransfer, { - from: spender, - }); - expect(didReturnTrue).to.be.false(); - }); - - it('should return true on a 0 value transfer', async () => { - const amountToTransfer = new BigNumber(0); - const didReturnTrue = await zrxToken.transferFrom.callAsync(owner, spender, amountToTransfer, { - from: spender, - }); - expect(didReturnTrue).to.be.true(); - }); - - it('should not modify spender allowance if spender allowance is 2^256 - 1', async () => { - const initOwnerBalance = await zrxToken.balanceOf.callAsync(owner); - const amountToTransfer = initOwnerBalance; - const initSpenderAllowance = MAX_UINT; - await web3Wrapper.awaitTransactionSuccessAsync( - await zrxToken.approve.sendTransactionAsync(spender, initSpenderAllowance, { - from: owner, - gas: constants.MAX_TOKEN_APPROVE_GAS, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await zrxToken.transferFrom.sendTransactionAsync(owner, spender, amountToTransfer, { - from: spender, - gas: constants.MAX_TOKEN_TRANSFERFROM_GAS, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - const newSpenderAllowance = await zrxToken.allowance.callAsync(owner, spender); - expect(initSpenderAllowance).to.be.bignumber.equal(newSpenderAllowance); - }); - - it('should transfer the correct balances if spender has sufficient allowance', async () => { - const initOwnerBalance = await zrxToken.balanceOf.callAsync(owner); - const initSpenderBalance = await zrxToken.balanceOf.callAsync(spender); - const amountToTransfer = initOwnerBalance; - const initSpenderAllowance = initOwnerBalance; - await web3Wrapper.awaitTransactionSuccessAsync( - await zrxToken.approve.sendTransactionAsync(spender, initSpenderAllowance), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await zrxToken.transferFrom.sendTransactionAsync(owner, spender, amountToTransfer, { - from: spender, - gas: constants.MAX_TOKEN_TRANSFERFROM_GAS, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - const newOwnerBalance = await zrxToken.balanceOf.callAsync(owner); - const newSpenderBalance = await zrxToken.balanceOf.callAsync(spender); - - expect(newOwnerBalance).to.be.bignumber.equal(0); - expect(newSpenderBalance).to.be.bignumber.equal(initSpenderBalance.plus(initOwnerBalance)); - }); - - it('should modify allowance if spender has sufficient allowance less than 2^256 - 1', async () => { - const initOwnerBalance = await zrxToken.balanceOf.callAsync(owner); - const amountToTransfer = initOwnerBalance; - await web3Wrapper.awaitTransactionSuccessAsync( - await zrxToken.approve.sendTransactionAsync(spender, amountToTransfer), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await zrxToken.transferFrom.sendTransactionAsync(owner, spender, amountToTransfer, { - from: spender, - gas: constants.MAX_TOKEN_TRANSFERFROM_GAS, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - const newSpenderAllowance = await zrxToken.allowance.callAsync(owner, spender); - expect(newSpenderAllowance).to.be.bignumber.equal(0); - }); - }); -}); diff --git a/contracts/core/test/utils/asset_proxy_owner_wrapper.ts b/contracts/core/test/utils/asset_proxy_owner_wrapper.ts index d5aaaf519..df23658d8 100644 --- a/contracts/core/test/utils/asset_proxy_owner_wrapper.ts +++ b/contracts/core/test/utils/asset_proxy_owner_wrapper.ts @@ -1,4 +1,5 @@ import { LogDecoder } from '@0x/contracts-test-utils'; +import { artifacts as tokensArtifacts } from '@0x/contracts-tokens'; import { BigNumber } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types'; @@ -14,7 +15,7 @@ export class AssetProxyOwnerWrapper { constructor(assetproxyOwnerContract: AssetProxyOwnerContract, provider: Provider) { this._assetProxyOwner = assetproxyOwnerContract; this._web3Wrapper = new Web3Wrapper(provider); - this._logDecoder = new LogDecoder(this._web3Wrapper, artifacts); + this._logDecoder = new LogDecoder(this._web3Wrapper, { ...artifacts, ...tokensArtifacts }); } public async submitTransactionAsync( destination: string, diff --git a/contracts/core/test/utils/erc20_wrapper.ts b/contracts/core/test/utils/erc20_wrapper.ts index d6210646c..e5ab0e36c 100644 --- a/contracts/core/test/utils/erc20_wrapper.ts +++ b/contracts/core/test/utils/erc20_wrapper.ts @@ -1,11 +1,11 @@ import { constants, ERC20BalancesByOwner, txDefaults } from '@0x/contracts-test-utils'; +import { artifacts as tokensArtifacts, DummyERC20TokenContract } from '@0x/contracts-tokens'; import { assetDataUtils } from '@0x/order-utils'; import { BigNumber } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; import { Provider } from 'ethereum-types'; import * as _ from 'lodash'; -import { DummyERC20TokenContract } from '../../generated-wrappers/dummy_erc20_token'; import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy'; import { artifacts } from '../../src/artifacts'; @@ -38,7 +38,7 @@ export class ERC20Wrapper { for (let i = 0; i < numberToDeploy; i++) { this._dummyTokenContracts.push( await DummyERC20TokenContract.deployFrom0xArtifactAsync( - artifacts.DummyERC20Token, + tokensArtifacts.DummyERC20Token, this._provider, txDefaults, constants.DUMMY_TOKEN_NAME, diff --git a/contracts/core/test/utils/erc721_wrapper.ts b/contracts/core/test/utils/erc721_wrapper.ts index b5ae34e60..1f658ce86 100644 --- a/contracts/core/test/utils/erc721_wrapper.ts +++ b/contracts/core/test/utils/erc721_wrapper.ts @@ -1,11 +1,11 @@ import { constants, ERC721TokenIdsByOwner, txDefaults } from '@0x/contracts-test-utils'; +import { artifacts as tokensArtifacts, DummyERC721TokenContract } from '@0x/contracts-tokens'; import { generatePseudoRandomSalt } from '@0x/order-utils'; import { BigNumber } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; import { Provider } from 'ethereum-types'; import * as _ from 'lodash'; -import { DummyERC721TokenContract } from '../../generated-wrappers/dummy_erc721_token'; import { ERC721ProxyContract } from '../../generated-wrappers/erc721_proxy'; import { artifacts } from '../../src/artifacts'; @@ -30,7 +30,7 @@ export class ERC721Wrapper { for (const i of _.times(constants.NUM_DUMMY_ERC721_TO_DEPLOY)) { this._dummyTokenContracts.push( await DummyERC721TokenContract.deployFrom0xArtifactAsync( - artifacts.DummyERC721Token, + tokensArtifacts.DummyERC721Token, this._provider, txDefaults, constants.DUMMY_TOKEN_NAME, diff --git a/contracts/core/test/utils/exchange_wrapper.ts b/contracts/core/test/utils/exchange_wrapper.ts index 2a24b880f..075d2cb96 100644 --- a/contracts/core/test/utils/exchange_wrapper.ts +++ b/contracts/core/test/utils/exchange_wrapper.ts @@ -6,6 +6,7 @@ import { orderUtils, SignedTransaction, } from '@0x/contracts-test-utils'; +import { artifacts as tokensArtifacts } from '@0x/contracts-tokens'; import { SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; @@ -21,7 +22,7 @@ export class ExchangeWrapper { constructor(exchangeContract: ExchangeContract, provider: Provider) { this._exchange = exchangeContract; this._web3Wrapper = new Web3Wrapper(provider); - this._logDecoder = new LogDecoder(this._web3Wrapper, artifacts); + this._logDecoder = new LogDecoder(this._web3Wrapper, { ...artifacts, ...tokensArtifacts }); } public async fillOrderAsync( signedOrder: SignedOrder, diff --git a/contracts/core/test/utils/forwarder_wrapper.ts b/contracts/core/test/utils/forwarder_wrapper.ts index 9c5560381..4c78ecd79 100644 --- a/contracts/core/test/utils/forwarder_wrapper.ts +++ b/contracts/core/test/utils/forwarder_wrapper.ts @@ -1,4 +1,5 @@ import { constants, formatters, LogDecoder, MarketSellOrders } from '@0x/contracts-test-utils'; +import { artifacts as tokensArtifacts } from '@0x/contracts-tokens'; import { SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; @@ -58,7 +59,7 @@ export class ForwarderWrapper { constructor(contractInstance: ForwarderContract, provider: Provider) { this._forwarderContract = contractInstance; this._web3Wrapper = new Web3Wrapper(provider); - this._logDecoder = new LogDecoder(this._web3Wrapper, artifacts); + this._logDecoder = new LogDecoder(this._web3Wrapper, { ...artifacts, ...tokensArtifacts }); } public async marketSellOrdersWithEthAsync( orders: SignedOrder[], diff --git a/contracts/core/test/utils/order_factory_from_scenario.ts b/contracts/core/test/utils/order_factory_from_scenario.ts index 1cc962020..ecb4b2e28 100644 --- a/contracts/core/test/utils/order_factory_from_scenario.ts +++ b/contracts/core/test/utils/order_factory_from_scenario.ts @@ -8,12 +8,11 @@ import { OrderScenario, TakerScenario, } from '@0x/contracts-test-utils'; +import { DummyERC721TokenContract } from '@0x/contracts-tokens'; import { assetDataUtils, generatePseudoRandomSalt } from '@0x/order-utils'; import { Order } from '@0x/types'; import { BigNumber, errorUtils } from '@0x/utils'; -import { DummyERC721TokenContract } from '../../generated-wrappers/dummy_erc721_token'; - const TEN_UNITS_EIGHTEEN_DECIMALS = new BigNumber(10_000_000_000_000_000_000); const FIVE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(5_000_000_000_000_000_000); const POINT_ONE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(100_000_000_000_000_000); diff --git a/contracts/core/tsconfig.json b/contracts/core/tsconfig.json index ca3e6c15f..ea493cbb8 100644 --- a/contracts/core/tsconfig.json +++ b/contracts/core/tsconfig.json @@ -8,34 +8,23 @@ "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"], "files": [ "./generated-artifacts/AssetProxyOwner.json", - "./generated-artifacts/DummyERC20Token.json", - "./generated-artifacts/DummyERC721Receiver.json", - "./generated-artifacts/DummyERC721Token.json", - "./generated-artifacts/DummyMultipleReturnERC20Token.json", - "./generated-artifacts/DummyNoReturnERC20Token.json", "./generated-artifacts/DutchAuction.json", "./generated-artifacts/ERC20Proxy.json", - "./generated-artifacts/ERC20Token.json", "./generated-artifacts/ERC721Proxy.json", - "./generated-artifacts/ERC721Token.json", "./generated-artifacts/Exchange.json", "./generated-artifacts/ExchangeWrapper.json", "./generated-artifacts/Forwarder.json", "./generated-artifacts/MixinAuthorizable.json", "./generated-artifacts/MultiAssetProxy.json", "./generated-artifacts/OrderValidator.json", - "./generated-artifacts/ReentrantERC20Token.json", "./generated-artifacts/TestAssetProxyDispatcher.json", "./generated-artifacts/TestAssetProxyOwner.json", "./generated-artifacts/TestExchangeInternals.json", "./generated-artifacts/TestSignatureValidator.json", "./generated-artifacts/TestStaticCallReceiver.json", "./generated-artifacts/Validator.json", - "./generated-artifacts/WETH9.json", "./generated-artifacts/Wallet.json", - "./generated-artifacts/Whitelist.json", - "./generated-artifacts/InvalidERC721Receiver.json", - "./generated-artifacts/ZRXToken.json" + "./generated-artifacts/Whitelist.json" ], "exclude": ["./deploy/solc/solc_bin"] } diff --git a/contracts/tokens/.solhint.json b/contracts/tokens/.solhint.json new file mode 100644 index 000000000..076afe9f3 --- /dev/null +++ b/contracts/tokens/.solhint.json @@ -0,0 +1,20 @@ +{ + "extends": "default", + "rules": { + "avoid-low-level-calls": false, + "avoid-tx-origin": "warn", + "bracket-align": false, + "code-complexity": false, + "const-name-snakecase": "error", + "expression-indent": "error", + "function-max-lines": false, + "func-order": "error", + "indent": ["error", 4], + "max-line-length": ["warn", 160], + "no-inline-assembly": false, + "quotes": ["error", "double"], + "separate-by-one-line-in-contract": "error", + "space-after-comma": "error", + "statement-indent": "error" + } +} diff --git a/contracts/tokens/.solhintignore b/contracts/tokens/.solhintignore new file mode 100644 index 000000000..1e33ec53b --- /dev/null +++ b/contracts/tokens/.solhintignore @@ -0,0 +1,3 @@ +contracts/tokens/ZRXToken/ERC20Token_v1.sol +contracts/tokens/ZRXToken/Token_v1.sol +contracts/tokens/ZRXToken/UnlimitedAllowanceToken_v1.sol diff --git a/contracts/tokens/CHANGELOG.json b/contracts/tokens/CHANGELOG.json new file mode 100644 index 000000000..5ff58c035 --- /dev/null +++ b/contracts/tokens/CHANGELOG.json @@ -0,0 +1,16 @@ +[ + { + "name": "ZRXToken", + "version": "1.0.0", + "changes": [ + { + "note": "protocol v1 deploy", + "networks": { + "1": "0xe41d2489571d322189246dafa5ebde1f4699f498", + "3": "0xff67881f8d12f372d91baae9752eb3631ff0ed00", + "42": "0x2002d3812f58e35f0ea1ffbf80a75a38c32175fa" + } + } + ] + } +] diff --git a/contracts/tokens/README.md b/contracts/tokens/README.md new file mode 100644 index 000000000..9c4106d41 --- /dev/null +++ b/contracts/tokens/README.md @@ -0,0 +1,74 @@ +## Token contracts + +Token smart contracts that are used in the 0x protocol. Addresses of the deployed contracts can be found in the 0x [wiki](https://0xproject.com/wiki#Deployed-Addresses) or the [CHANGELOG](./CHANGELOG.json) of this package. + +## Usage + +Token contracts that make up and interact with version 2.0.0 of the protocol can be found in the [contracts](./contracts) directory. The contents of this directory are broken down into the following subdirectories: + +* [tokens](./contracts/tokens) + * This directory contains implementations of different tokens and token standards, including [wETH](https://weth.io/), ZRX, [ERC20](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md), and [ERC721](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md). +* [test](./contracts/test) + * This directory contains mocks and other contracts that are used solely for testing contracts within the other directories. + +## Bug bounty + +A bug bounty for the 2.0.0 contracts is ongoing! Instructions can be found [here](https://0xproject.com/wiki#Bug-Bounty). + +## Contributing + +We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository. + +For proposals regarding the 0x protocol's smart contract architecture, message format, or additional functionality, go to the [0x Improvement Proposals (ZEIPs)](https://github.com/0xProject/ZEIPs) repository and follow the contribution guidelines provided therein. + +Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. + +### Install Dependencies + +If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them: + +```bash +yarn config set workspaces-experimental true +``` + +Then install dependencies + +```bash +yarn install +``` + +### Build + +To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory: + +```bash +PKG=@0x/contracts-tokens yarn build +``` + +Or continuously rebuild on change: + +```bash +PKG=@0x/contracts-tokens yarn watch +``` + +### Clean + +```bash +yarn clean +``` + +### Lint + +```bash +yarn lint +``` + +### Run Tests + +```bash +yarn test +``` + +#### Testing options + +Contracts testing options like coverage, profiling, revert traces or backing node choosing - are described [here](../TESTING.md). diff --git a/contracts/tokens/compiler.json b/contracts/tokens/compiler.json new file mode 100644 index 000000000..498c5d826 --- /dev/null +++ b/contracts/tokens/compiler.json @@ -0,0 +1,44 @@ +{ + "artifactsDir": "./generated-artifacts", + "contractsDir": "./contracts", + "compilerSettings": { + "optimizer": { + "enabled": true, + "runs": 1000000 + }, + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode.object", + "evm.bytecode.sourceMap", + "evm.deployedBytecode.object", + "evm.deployedBytecode.sourceMap" + ] + } + } + }, + "contracts": [ + "DummyERC20Token", + "DummyMultipleReturnERC20Token", + "DummyNoReturnERC20Token", + "DummyERC721Receiver", + "InvalidERC721Receiver", + "DummyERC721Token", + "ReentrantERC20Token", + "ERC20Token", + "IERC20Token", + "MintableERC20Token", + "UnlimitedAllowanceERC20Token", + "ERC721Token", + "IERC721Receiver", + "IERC721Token", + "MintableERC721Token", + "IEtherToken", + "WETH9", + "ERC20Token_v1", + "Token_v1", + "UnlimitedAllowanceToken_v1", + "ZRXToken" + ] +} diff --git a/contracts/tokens/contracts/test/DummyERC20Token/DummyERC20Token.sol b/contracts/tokens/contracts/test/DummyERC20Token/DummyERC20Token.sol new file mode 100644 index 000000000..33028db0c --- /dev/null +++ b/contracts/tokens/contracts/test/DummyERC20Token/DummyERC20Token.sol @@ -0,0 +1,77 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; + +import "@0x/contracts-utils/contracts/utils/Ownable/Ownable.sol"; +import "../../tokens/ERC20Token/MintableERC20Token.sol"; + + +contract DummyERC20Token is + Ownable, + MintableERC20Token +{ + string public name; + string public symbol; + uint256 public decimals; + uint256 public constant MAX_MINT_AMOUNT = 10000000000000000000000; + + constructor ( + string _name, + string _symbol, + uint256 _decimals, + uint256 _totalSupply + ) + public + { + name = _name; + symbol = _symbol; + decimals = _decimals; + _totalSupply = _totalSupply; + balances[msg.sender] = _totalSupply; + } + + /// @dev Sets the balance of target address + /// @param _target Address or which balance will be updated + /// @param _value New balance of target address + function setBalance(address _target, uint256 _value) + external + onlyOwner + { + uint256 currBalance = balances[_target]; + if (_value < currBalance) { + _totalSupply = safeSub(_totalSupply, safeSub(currBalance, _value)); + } else { + _totalSupply = safeAdd(_totalSupply, safeSub(_value, currBalance)); + } + balances[_target] = _value; + } + + /// @dev Mints new tokens for sender + /// @param _value Amount of tokens to mint + function mint(uint256 _value) + external + { + require( + _value <= MAX_MINT_AMOUNT, + "VALUE_TOO_LARGE" + ); + + _mint(msg.sender, _value); + } +} diff --git a/contracts/tokens/contracts/test/DummyERC20Token/DummyMultipleReturnERC20Token.sol b/contracts/tokens/contracts/test/DummyERC20Token/DummyMultipleReturnERC20Token.sol new file mode 100644 index 000000000..733d4437e --- /dev/null +++ b/contracts/tokens/contracts/test/DummyERC20Token/DummyMultipleReturnERC20Token.sol @@ -0,0 +1,69 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; + +import "./DummyERC20Token.sol"; + + +// solhint-disable no-empty-blocks +contract DummyMultipleReturnERC20Token is + DummyERC20Token +{ + constructor ( + string _name, + string _symbol, + uint256 _decimals, + uint256 _totalSupply + ) + public + DummyERC20Token( + _name, + _symbol, + _decimals, + _totalSupply + ) + {} + + /// @dev send `value` token to `to` from `from` on the condition it is approved by `from` + /// @param _from The address of the sender + /// @param _to The address of the recipient + /// @param _value The amount of token to be transferred + function transferFrom( + address _from, + address _to, + uint256 _value + ) + external + returns (bool) + { + emit Transfer( + _from, + _to, + _value + ); + + // HACK: This contract will not compile if we remove `returns (bool)`, so we manually return 64 bytes (equiavalent to true, true) + assembly { + mstore(0, 1) + mstore(32, 1) + return(0, 64) + } + } +} + diff --git a/contracts/tokens/contracts/test/DummyERC20Token/DummyNoReturnERC20Token.sol b/contracts/tokens/contracts/test/DummyERC20Token/DummyNoReturnERC20Token.sol new file mode 100644 index 000000000..e16825a16 --- /dev/null +++ b/contracts/tokens/contracts/test/DummyERC20Token/DummyNoReturnERC20Token.sol @@ -0,0 +1,115 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; + +import "./DummyERC20Token.sol"; + + +// solhint-disable no-empty-blocks +contract DummyNoReturnERC20Token is + DummyERC20Token +{ + constructor ( + string _name, + string _symbol, + uint256 _decimals, + uint256 _totalSupply + ) + public + DummyERC20Token( + _name, + _symbol, + _decimals, + _totalSupply + ) + {} + + /// @dev send `value` token to `to` from `msg.sender` + /// @param _to The address of the recipient + /// @param _value The amount of token to be transferred + function transfer(address _to, uint256 _value) + external + returns (bool) + { + require( + balances[msg.sender] >= _value, + "ERC20_INSUFFICIENT_BALANCE" + ); + require( + balances[_to] + _value >= balances[_to], + "UINT256_OVERFLOW" + ); + + balances[msg.sender] -= _value; + balances[_to] += _value; + + emit Transfer( + msg.sender, + _to, + _value + ); + + // HACK: This contract will not compile if we remove `returns (bool)`, so we manually return no data + assembly { + return(0, 0) + } + } + + /// @dev send `value` token to `to` from `from` on the condition it is approved by `from` + /// @param _from The address of the sender + /// @param _to The address of the recipient + /// @param _value The amount of token to be transferred + function transferFrom( + address _from, + address _to, + uint256 _value + ) + external + returns (bool) + { + require( + balances[_from] >= _value, + "ERC20_INSUFFICIENT_BALANCE" + ); + require( + allowed[_from][msg.sender] >= _value, + "ERC20_INSUFFICIENT_ALLOWANCE" + ); + require( + balances[_to] + _value >= balances[_to], + "UINT256_OVERFLOW" + ); + + balances[_to] += _value; + balances[_from] -= _value; + allowed[_from][msg.sender] -= _value; + + emit Transfer( + _from, + _to, + _value + ); + + // HACK: This contract will not compile if we remove `returns (bool)`, so we manually return no data + assembly { + return(0, 0) + } + } +} + diff --git a/contracts/tokens/contracts/test/DummyERC721Receiver/DummyERC721Receiver.sol b/contracts/tokens/contracts/test/DummyERC721Receiver/DummyERC721Receiver.sol new file mode 100644 index 000000000..6c8371559 --- /dev/null +++ b/contracts/tokens/contracts/test/DummyERC721Receiver/DummyERC721Receiver.sol @@ -0,0 +1,67 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; + +import "../../tokens/ERC721Token/IERC721Receiver.sol"; + + +contract DummyERC721Receiver is + IERC721Receiver +{ + // Function selector for ERC721Receiver.onERC721Received + // 0x150b7a02 + bytes4 constant internal ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)")); + + event TokenReceived( + address operator, + address from, + uint256 tokenId, + bytes data + ); + + /// @notice Handle the receipt of an NFT + /// @dev The ERC721 smart contract calls this function on the recipient + /// after a `transfer`. This function MAY throw to revert and reject the + /// transfer. Return of other than the magic value MUST result in the + /// transaction being reverted. + /// Note: the contract address is always the message sender. + /// @param _operator The address which called `safeTransferFrom` function + /// @param _from The address which previously owned the token + /// @param _tokenId The NFT identifier which is being transferred + /// @param _data Additional data with no specified format + /// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` + /// unless throwing + function onERC721Received( + address _operator, + address _from, + uint256 _tokenId, + bytes _data + ) + external + returns (bytes4) + { + emit TokenReceived( + _operator, + _from, + _tokenId, + _data + ); + return ERC721_RECEIVED; + } +} diff --git a/contracts/tokens/contracts/test/DummyERC721Receiver/InvalidERC721Receiver.sol b/contracts/tokens/contracts/test/DummyERC721Receiver/InvalidERC721Receiver.sol new file mode 100644 index 000000000..309633bf5 --- /dev/null +++ b/contracts/tokens/contracts/test/DummyERC721Receiver/InvalidERC721Receiver.sol @@ -0,0 +1,66 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; + +import "../../tokens/ERC721Token/IERC721Receiver.sol"; + + +contract InvalidERC721Receiver is + IERC721Receiver +{ + // Actual function signature is `onERC721Received(address,address,uint256,bytes)` + bytes4 constant internal INVALID_ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,uint256,bytes)")); + + event TokenReceived( + address operator, + address from, + uint256 tokenId, + bytes data + ); + + /// @notice Handle the receipt of an NFT + /// @dev The ERC721 smart contract calls this function on the recipient + /// after a `transfer`. This function MAY throw to revert and reject the + /// transfer. Return of other than the magic value MUST result in the + /// transaction being reverted. + /// Note: the contract address is always the message sender. + /// @param _operator The address which called `safeTransferFrom` function + /// @param _from The address which previously owned the token + /// @param _tokenId The NFT identifier which is being transferred + /// @param _data Additional data with no specified format + /// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` + /// unless throwing + function onERC721Received( + address _operator, + address _from, + uint256 _tokenId, + bytes _data + ) + external + returns (bytes4) + { + emit TokenReceived( + _operator, + _from, + _tokenId, + _data + ); + return INVALID_ERC721_RECEIVED; + } +} diff --git a/contracts/tokens/contracts/test/DummyERC721Token/DummyERC721Token.sol b/contracts/tokens/contracts/test/DummyERC721Token/DummyERC721Token.sol new file mode 100644 index 000000000..4c978b2df --- /dev/null +++ b/contracts/tokens/contracts/test/DummyERC721Token/DummyERC721Token.sol @@ -0,0 +1,63 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; + +import "../../tokens/ERC721Token/MintableERC721Token.sol"; +import "@0x/contracts-utils/contracts/utils/Ownable/Ownable.sol"; + + +// solhint-disable no-empty-blocks +contract DummyERC721Token is + Ownable, + MintableERC721Token +{ + string public name; + string public symbol; + + constructor ( + string _name, + string _symbol + ) + public + { + name = _name; + symbol = _symbol; + } + + /// @dev Function to mint a new token + /// Reverts if the given token ID already exists + /// @param _to Address of the beneficiary that will own the minted token + /// @param _tokenId ID of the token to be minted by the msg.sender + function mint(address _to, uint256 _tokenId) + external + { + _mint(_to, _tokenId); + } + + /// @dev Function to burn a token + /// Reverts if the given token ID doesn't exist or not called by contract owner + /// @param _owner Owner of token with given token ID + /// @param _tokenId ID of the token to be burned by the msg.sender + function burn(address _owner, uint256 _tokenId) + external + onlyOwner + { + _burn(_owner, _tokenId); + } +} diff --git a/contracts/tokens/contracts/test/ReentrantERC20Token/ReentrantERC20Token.sol b/contracts/tokens/contracts/test/ReentrantERC20Token/ReentrantERC20Token.sol new file mode 100644 index 000000000..d8c65929d --- /dev/null +++ b/contracts/tokens/contracts/test/ReentrantERC20Token/ReentrantERC20Token.sol @@ -0,0 +1,188 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; +pragma experimental ABIEncoderV2; + +import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol"; +import "../../tokens/ERC20Token/ERC20Token.sol"; +import "@0x/contracts-interfaces/contracts/protocol/Exchange/IExchange.sol"; +import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; + + +// solhint-disable no-unused-vars +contract ReentrantERC20Token is + ERC20Token +{ + using LibBytes for bytes; + + // solhint-disable-next-line var-name-mixedcase + IExchange internal EXCHANGE; + + bytes internal constant REENTRANCY_ILLEGAL_REVERT_REASON = abi.encodeWithSelector( + bytes4(keccak256("Error(string)")), + "REENTRANCY_ILLEGAL" + ); + + // All of these functions are potentially vulnerable to reentrancy + // We do not test any "noThrow" functions because `fillOrderNoThrow` makes a delegatecall to `fillOrder` + enum ExchangeFunction { + FILL_ORDER, + FILL_OR_KILL_ORDER, + BATCH_FILL_ORDERS, + BATCH_FILL_OR_KILL_ORDERS, + MARKET_BUY_ORDERS, + MARKET_SELL_ORDERS, + MATCH_ORDERS, + CANCEL_ORDER, + BATCH_CANCEL_ORDERS, + CANCEL_ORDERS_UP_TO, + SET_SIGNATURE_VALIDATOR_APPROVAL + } + + uint8 internal currentFunctionId = 0; + + constructor (address _exchange) + public + { + EXCHANGE = IExchange(_exchange); + } + + /// @dev Set the current function that will be called when `transferFrom` is called. + /// @param _currentFunctionId Id that corresponds to function name. + function setCurrentFunction(uint8 _currentFunctionId) + external + { + currentFunctionId = _currentFunctionId; + } + + /// @dev A version of `transferFrom` that attempts to reenter the Exchange contract. + /// @param _from The address of the sender + /// @param _to The address of the recipient + /// @param _value The amount of token to be transferred + function transferFrom( + address _from, + address _to, + uint256 _value + ) + external + returns (bool) + { + // This order would normally be invalid, but it will be used strictly for testing reentrnacy. + // Any reentrancy checks will happen before any other checks that invalidate the order. + LibOrder.Order memory order; + + // Initialize remaining null parameters + bytes memory signature; + LibOrder.Order[] memory orders; + uint256[] memory takerAssetFillAmounts; + bytes[] memory signatures; + bytes memory callData; + + // Create callData for function that corresponds to currentFunctionId + if (currentFunctionId == uint8(ExchangeFunction.FILL_ORDER)) { + callData = abi.encodeWithSelector( + EXCHANGE.fillOrder.selector, + order, + 0, + signature + ); + } else if (currentFunctionId == uint8(ExchangeFunction.FILL_OR_KILL_ORDER)) { + callData = abi.encodeWithSelector( + EXCHANGE.fillOrKillOrder.selector, + order, + 0, + signature + ); + } else if (currentFunctionId == uint8(ExchangeFunction.BATCH_FILL_ORDERS)) { + callData = abi.encodeWithSelector( + EXCHANGE.batchFillOrders.selector, + orders, + takerAssetFillAmounts, + signatures + ); + } else if (currentFunctionId == uint8(ExchangeFunction.BATCH_FILL_OR_KILL_ORDERS)) { + callData = abi.encodeWithSelector( + EXCHANGE.batchFillOrKillOrders.selector, + orders, + takerAssetFillAmounts, + signatures + ); + } else if (currentFunctionId == uint8(ExchangeFunction.MARKET_BUY_ORDERS)) { + callData = abi.encodeWithSelector( + EXCHANGE.marketBuyOrders.selector, + orders, + 0, + signatures + ); + } else if (currentFunctionId == uint8(ExchangeFunction.MARKET_SELL_ORDERS)) { + callData = abi.encodeWithSelector( + EXCHANGE.marketSellOrders.selector, + orders, + 0, + signatures + ); + } else if (currentFunctionId == uint8(ExchangeFunction.MATCH_ORDERS)) { + callData = abi.encodeWithSelector( + EXCHANGE.matchOrders.selector, + order, + order, + signature, + signature + ); + } else if (currentFunctionId == uint8(ExchangeFunction.CANCEL_ORDER)) { + callData = abi.encodeWithSelector( + EXCHANGE.cancelOrder.selector, + order + ); + } else if (currentFunctionId == uint8(ExchangeFunction.BATCH_CANCEL_ORDERS)) { + callData = abi.encodeWithSelector( + EXCHANGE.batchCancelOrders.selector, + orders + ); + } else if (currentFunctionId == uint8(ExchangeFunction.CANCEL_ORDERS_UP_TO)) { + callData = abi.encodeWithSelector( + EXCHANGE.cancelOrdersUpTo.selector, + 0 + ); + } else if (currentFunctionId == uint8(ExchangeFunction.SET_SIGNATURE_VALIDATOR_APPROVAL)) { + callData = abi.encodeWithSelector( + EXCHANGE.setSignatureValidatorApproval.selector, + address(0), + false + ); + } + + // Call Exchange function, swallow error + address(EXCHANGE).call(callData); + + // Revert reason is 100 bytes + bytes memory returnData = new bytes(100); + + // Copy return data + assembly { + returndatacopy(add(returnData, 32), 0, 100) + } + + // Revert if function reverted with REENTRANCY_ILLEGAL error + require(!REENTRANCY_ILLEGAL_REVERT_REASON.equals(returnData)); + + // Transfer will return true if function failed for any other reason + return true; + } +} \ No newline at end of file diff --git a/contracts/tokens/contracts/tokens/ERC20Token/ERC20Token.sol b/contracts/tokens/contracts/tokens/ERC20Token/ERC20Token.sol new file mode 100644 index 000000000..725d304df --- /dev/null +++ b/contracts/tokens/contracts/tokens/ERC20Token/ERC20Token.sol @@ -0,0 +1,148 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; + +import "./IERC20Token.sol"; + + +contract ERC20Token is + IERC20Token +{ + mapping (address => uint256) internal balances; + mapping (address => mapping (address => uint256)) internal allowed; + + uint256 internal _totalSupply; + + /// @dev send `value` token to `to` from `msg.sender` + /// @param _to The address of the recipient + /// @param _value The amount of token to be transferred + /// @return True if transfer was successful + function transfer(address _to, uint256 _value) + external + returns (bool) + { + require( + balances[msg.sender] >= _value, + "ERC20_INSUFFICIENT_BALANCE" + ); + require( + balances[_to] + _value >= balances[_to], + "UINT256_OVERFLOW" + ); + + balances[msg.sender] -= _value; + balances[_to] += _value; + + emit Transfer( + msg.sender, + _to, + _value + ); + + return true; + } + + /// @dev send `value` token to `to` from `from` on the condition it is approved by `from` + /// @param _from The address of the sender + /// @param _to The address of the recipient + /// @param _value The amount of token to be transferred + /// @return True if transfer was successful + function transferFrom( + address _from, + address _to, + uint256 _value + ) + external + returns (bool) + { + require( + balances[_from] >= _value, + "ERC20_INSUFFICIENT_BALANCE" + ); + require( + allowed[_from][msg.sender] >= _value, + "ERC20_INSUFFICIENT_ALLOWANCE" + ); + require( + balances[_to] + _value >= balances[_to], + "UINT256_OVERFLOW" + ); + + balances[_to] += _value; + balances[_from] -= _value; + allowed[_from][msg.sender] -= _value; + + emit Transfer( + _from, + _to, + _value + ); + + return true; + } + + /// @dev `msg.sender` approves `_spender` to spend `_value` tokens + /// @param _spender The address of the account able to transfer the tokens + /// @param _value The amount of wei to be approved for transfer + /// @return Always true if the call has enough gas to complete execution + function approve(address _spender, uint256 _value) + external + returns (bool) + { + allowed[msg.sender][_spender] = _value; + emit Approval( + msg.sender, + _spender, + _value + ); + return true; + } + + /// @dev Query total supply of token + /// @return Total supply of token + function totalSupply() + external + view + returns (uint256) + { + return _totalSupply; + } + + /// @dev Query the balance of owner + /// @param _owner The address from which the balance will be retrieved + /// @return Balance of owner + function balanceOf(address _owner) + external + view + returns (uint256) + { + return balances[_owner]; + } + + /// @param _owner The address of the account owning tokens + /// @param _spender The address of the account able to transfer the tokens + /// @return Amount of remaining tokens allowed to spent + function allowance(address _owner, address _spender) + external + view + returns (uint256) + { + return allowed[_owner][_spender]; + } +} diff --git a/contracts/tokens/contracts/tokens/ERC20Token/IERC20Token.sol b/contracts/tokens/contracts/tokens/ERC20Token/IERC20Token.sol new file mode 100644 index 000000000..258d47393 --- /dev/null +++ b/contracts/tokens/contracts/tokens/ERC20Token/IERC20Token.sol @@ -0,0 +1,87 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; + + +contract IERC20Token { + + // solhint-disable no-simple-event-func-name + event Transfer( + address indexed _from, + address indexed _to, + uint256 _value + ); + + event Approval( + address indexed _owner, + address indexed _spender, + uint256 _value + ); + + /// @dev send `value` token to `to` from `msg.sender` + /// @param _to The address of the recipient + /// @param _value The amount of token to be transferred + /// @return True if transfer was successful + function transfer(address _to, uint256 _value) + external + returns (bool); + + /// @dev send `value` token to `to` from `from` on the condition it is approved by `from` + /// @param _from The address of the sender + /// @param _to The address of the recipient + /// @param _value The amount of token to be transferred + /// @return True if transfer was successful + function transferFrom( + address _from, + address _to, + uint256 _value + ) + external + returns (bool); + + /// @dev `msg.sender` approves `_spender` to spend `_value` tokens + /// @param _spender The address of the account able to transfer the tokens + /// @param _value The amount of wei to be approved for transfer + /// @return Always true if the call has enough gas to complete execution + function approve(address _spender, uint256 _value) + external + returns (bool); + + /// @dev Query total supply of token + /// @return Total supply of token + function totalSupply() + external + view + returns (uint256); + + /// @param _owner The address from which the balance will be retrieved + /// @return Balance of owner + function balanceOf(address _owner) + external + view + returns (uint256); + + /// @param _owner The address of the account owning tokens + /// @param _spender The address of the account able to transfer the tokens + /// @return Amount of remaining tokens allowed to spent + function allowance(address _owner, address _spender) + external + view + returns (uint256); +} diff --git a/contracts/tokens/contracts/tokens/ERC20Token/MintableERC20Token.sol b/contracts/tokens/contracts/tokens/ERC20Token/MintableERC20Token.sol new file mode 100644 index 000000000..58bccb5a1 --- /dev/null +++ b/contracts/tokens/contracts/tokens/ERC20Token/MintableERC20Token.sol @@ -0,0 +1,60 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; + +import "@0x/contracts-utils/contracts/utils/SafeMath/SafeMath.sol"; +import "./UnlimitedAllowanceERC20Token.sol"; + + +contract MintableERC20Token is + SafeMath, + UnlimitedAllowanceERC20Token +{ + /// @dev Mints new tokens + /// @param _to Address of the beneficiary that will own the minted token + /// @param _value Amount of tokens to mint + function _mint(address _to, uint256 _value) + internal + { + balances[_to] = safeAdd(_value, balances[_to]); + _totalSupply = safeAdd(_totalSupply, _value); + + emit Transfer( + address(0), + _to, + _value + ); + } + + /// @dev Mints new tokens + /// @param _owner Owner of tokens that will be burned + /// @param _value Amount of tokens to burn + function _burn(address _owner, uint256 _value) + internal + { + balances[_owner] = safeSub(balances[_owner], _value); + _totalSupply = safeSub(_totalSupply, _value); + + emit Transfer( + _owner, + address(0), + _value + ); + } +} diff --git a/contracts/tokens/contracts/tokens/ERC20Token/UnlimitedAllowanceERC20Token.sol b/contracts/tokens/contracts/tokens/ERC20Token/UnlimitedAllowanceERC20Token.sol new file mode 100644 index 000000000..2e5bd4348 --- /dev/null +++ b/contracts/tokens/contracts/tokens/ERC20Token/UnlimitedAllowanceERC20Token.sol @@ -0,0 +1,70 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; + +import "../ERC20Token/ERC20Token.sol"; + + +contract UnlimitedAllowanceERC20Token is + ERC20Token +{ + uint256 constant internal MAX_UINT = 2**256 - 1; + + /// @dev ERC20 transferFrom, modified such that an allowance of MAX_UINT represents an unlimited allowance. See https://github.com/ethereum/EIPs/issues/717 + /// @param _from Address to transfer from. + /// @param _to Address to transfer to. + /// @param _value Amount to transfer. + /// @return Success of transfer. + function transferFrom( + address _from, + address _to, + uint256 _value + ) + external + returns (bool) + { + uint256 allowance = allowed[_from][msg.sender]; + require( + balances[_from] >= _value, + "ERC20_INSUFFICIENT_BALANCE" + ); + require( + allowance >= _value, + "ERC20_INSUFFICIENT_ALLOWANCE" + ); + require( + balances[_to] + _value >= balances[_to], + "UINT256_OVERFLOW" + ); + + balances[_to] += _value; + balances[_from] -= _value; + if (allowance < MAX_UINT) { + allowed[_from][msg.sender] -= _value; + } + + emit Transfer( + _from, + _to, + _value + ); + + return true; + } +} diff --git a/contracts/tokens/contracts/tokens/ERC721Token/ERC721Token.sol b/contracts/tokens/contracts/tokens/ERC721Token/ERC721Token.sol new file mode 100644 index 000000000..600cee1ab --- /dev/null +++ b/contracts/tokens/contracts/tokens/ERC721Token/ERC721Token.sol @@ -0,0 +1,277 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; + +import "./IERC721Token.sol"; +import "./IERC721Receiver.sol"; +import "@0x/contracts-utils/contracts/utils/SafeMath/SafeMath.sol"; + + +contract ERC721Token is + IERC721Token, + SafeMath +{ + // Function selector for ERC721Receiver.onERC721Received + // 0x150b7a02 + bytes4 constant internal ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)")); + + // Mapping of tokenId => owner + mapping (uint256 => address) internal owners; + + // Mapping of tokenId => approved address + mapping (uint256 => address) internal approvals; + + // Mapping of owner => number of tokens owned + mapping (address => uint256) internal balances; + + // Mapping of owner => operator => approved + mapping (address => mapping (address => bool)) internal operatorApprovals; + + /// @notice Transfers the ownership of an NFT from one address to another address + /// @dev Throws unless `msg.sender` is the current owner, an authorized + /// operator, or the approved address for this NFT. Throws if `_from` is + /// not the current owner. Throws if `_to` is the zero address. Throws if + /// `_tokenId` is not a valid NFT. When transfer is complete, this function + /// checks if `_to` is a smart contract (code size > 0). If so, it calls + /// `onERC721Received` on `_to` and throws if the return value is not + /// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`. + /// @param _from The current owner of the NFT + /// @param _to The new owner + /// @param _tokenId The NFT to transfer + /// @param _data Additional data with no specified format, sent in call to `_to` + function safeTransferFrom( + address _from, + address _to, + uint256 _tokenId, + bytes _data + ) + external + { + transferFrom( + _from, + _to, + _tokenId + ); + + uint256 receiverCodeSize; + assembly { + receiverCodeSize := extcodesize(_to) + } + if (receiverCodeSize > 0) { + bytes4 selector = IERC721Receiver(_to).onERC721Received( + msg.sender, + _from, + _tokenId, + _data + ); + require( + selector == ERC721_RECEIVED, + "ERC721_INVALID_SELECTOR" + ); + } + } + + /// @notice Transfers the ownership of an NFT from one address to another address + /// @dev This works identically to the other function with an extra data parameter, + /// except this function just sets data to "". + /// @param _from The current owner of the NFT + /// @param _to The new owner + /// @param _tokenId The NFT to transfer + function safeTransferFrom( + address _from, + address _to, + uint256 _tokenId + ) + external + { + transferFrom( + _from, + _to, + _tokenId + ); + + uint256 receiverCodeSize; + assembly { + receiverCodeSize := extcodesize(_to) + } + if (receiverCodeSize > 0) { + bytes4 selector = IERC721Receiver(_to).onERC721Received( + msg.sender, + _from, + _tokenId, + "" + ); + require( + selector == ERC721_RECEIVED, + "ERC721_INVALID_SELECTOR" + ); + } + } + + /// @notice Change or reaffirm the approved address for an NFT + /// @dev The zero address indicates there is no approved address. + /// Throws unless `msg.sender` is the current NFT owner, or an authorized + /// operator of the current owner. + /// @param _approved The new approved NFT controller + /// @param _tokenId The NFT to approve + function approve(address _approved, uint256 _tokenId) + external + { + address owner = ownerOf(_tokenId); + require( + msg.sender == owner || isApprovedForAll(owner, msg.sender), + "ERC721_INVALID_SENDER" + ); + + approvals[_tokenId] = _approved; + emit Approval( + owner, + _approved, + _tokenId + ); + } + + /// @notice Enable or disable approval for a third party ("operator") to manage + /// all of `msg.sender`'s assets + /// @dev Emits the ApprovalForAll event. The contract MUST allow + /// multiple operators per owner. + /// @param _operator Address to add to the set of authorized operators + /// @param _approved True if the operator is approved, false to revoke approval + function setApprovalForAll(address _operator, bool _approved) + external + { + operatorApprovals[msg.sender][_operator] = _approved; + emit ApprovalForAll( + msg.sender, + _operator, + _approved + ); + } + + /// @notice Count all NFTs assigned to an owner + /// @dev NFTs assigned to the zero address are considered invalid, and this + /// function throws for queries about the zero address. + /// @param _owner An address for whom to query the balance + /// @return The number of NFTs owned by `_owner`, possibly zero + function balanceOf(address _owner) + external + view + returns (uint256) + { + require( + _owner != address(0), + "ERC721_ZERO_OWNER" + ); + return balances[_owner]; + } + + /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE + /// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE + /// THEY MAY BE PERMANENTLY LOST + /// @dev Throws unless `msg.sender` is the current owner, an authorized + /// operator, or the approved address for this NFT. Throws if `_from` is + /// not the current owner. Throws if `_to` is the zero address. Throws if + /// `_tokenId` is not a valid NFT. + /// @param _from The current owner of the NFT + /// @param _to The new owner + /// @param _tokenId The NFT to transfer + function transferFrom( + address _from, + address _to, + uint256 _tokenId + ) + public + { + require( + _to != address(0), + "ERC721_ZERO_TO_ADDRESS" + ); + + address owner = ownerOf(_tokenId); + require( + _from == owner, + "ERC721_OWNER_MISMATCH" + ); + + address spender = msg.sender; + address approvedAddress = getApproved(_tokenId); + require( + spender == owner || + isApprovedForAll(owner, spender) || + approvedAddress == spender, + "ERC721_INVALID_SPENDER" + ); + + if (approvedAddress != address(0)) { + approvals[_tokenId] = address(0); + } + + owners[_tokenId] = _to; + balances[_from] = safeSub(balances[_from], 1); + balances[_to] = safeAdd(balances[_to], 1); + + emit Transfer( + _from, + _to, + _tokenId + ); + } + + /// @notice Find the owner of an NFT + /// @dev NFTs assigned to zero address are considered invalid, and queries + /// about them do throw. + /// @param _tokenId The identifier for an NFT + /// @return The address of the owner of the NFT + function ownerOf(uint256 _tokenId) + public + view + returns (address) + { + address owner = owners[_tokenId]; + require( + owner != address(0), + "ERC721_ZERO_OWNER" + ); + return owner; + } + + /// @notice Get the approved address for a single NFT + /// @dev Throws if `_tokenId` is not a valid NFT. + /// @param _tokenId The NFT to find the approved address for + /// @return The approved address for this NFT, or the zero address if there is none + function getApproved(uint256 _tokenId) + public + view + returns (address) + { + return approvals[_tokenId]; + } + + /// @notice Query if an address is an authorized operator for another address + /// @param _owner The address that owns the NFTs + /// @param _operator The address that acts on behalf of the owner + /// @return True if `_operator` is an approved operator for `_owner`, false otherwise + function isApprovedForAll(address _owner, address _operator) + public + view + returns (bool) + { + return operatorApprovals[_owner][_operator]; + } +} diff --git a/contracts/tokens/contracts/tokens/ERC721Token/IERC721Receiver.sol b/contracts/tokens/contracts/tokens/ERC721Token/IERC721Receiver.sol new file mode 100644 index 000000000..8e0e32ab2 --- /dev/null +++ b/contracts/tokens/contracts/tokens/ERC721Token/IERC721Receiver.sol @@ -0,0 +1,44 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; + + +contract IERC721Receiver { + + /// @notice Handle the receipt of an NFT + /// @dev The ERC721 smart contract calls this function on the recipient + /// after a `transfer`. This function MAY throw to revert and reject the + /// transfer. Return of other than the magic value MUST result in the + /// transaction being reverted. + /// Note: the contract address is always the message sender. + /// @param _operator The address which called `safeTransferFrom` function + /// @param _from The address which previously owned the token + /// @param _tokenId The NFT identifier which is being transferred + /// @param _data Additional data with no specified format + /// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` + /// unless throwing + function onERC721Received( + address _operator, + address _from, + uint256 _tokenId, + bytes _data + ) + external + returns (bytes4); +} diff --git a/contracts/tokens/contracts/tokens/ERC721Token/IERC721Token.sol b/contracts/tokens/contracts/tokens/ERC721Token/IERC721Token.sol new file mode 100644 index 000000000..ac992c80d --- /dev/null +++ b/contracts/tokens/contracts/tokens/ERC721Token/IERC721Token.sol @@ -0,0 +1,158 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; + + +contract IERC721Token { + + /// @dev This emits when ownership of any NFT changes by any mechanism. + /// This event emits when NFTs are created (`from` == 0) and destroyed + /// (`to` == 0). Exception: during contract creation, any number of NFTs + /// may be created and assigned without emitting Transfer. At the time of + /// any transfer, the approved address for that NFT (if any) is reset to none. + event Transfer( + address indexed _from, + address indexed _to, + uint256 indexed _tokenId + ); + + /// @dev This emits when the approved address for an NFT is changed or + /// reaffirmed. The zero address indicates there is no approved address. + /// When a Transfer event emits, this also indicates that the approved + /// address for that NFT (if any) is reset to none. + event Approval( + address indexed _owner, + address indexed _approved, + uint256 indexed _tokenId + ); + + /// @dev This emits when an operator is enabled or disabled for an owner. + /// The operator can manage all NFTs of the owner. + event ApprovalForAll( + address indexed _owner, + address indexed _operator, + bool _approved + ); + + /// @notice Transfers the ownership of an NFT from one address to another address + /// @dev Throws unless `msg.sender` is the current owner, an authorized + /// perator, or the approved address for this NFT. Throws if `_from` is + /// not the current owner. Throws if `_to` is the zero address. Throws if + /// `_tokenId` is not a valid NFT. When transfer is complete, this function + /// checks if `_to` is a smart contract (code size > 0). If so, it calls + /// `onERC721Received` on `_to` and throws if the return value is not + /// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`. + /// @param _from The current owner of the NFT + /// @param _to The new owner + /// @param _tokenId The NFT to transfer + /// @param _data Additional data with no specified format, sent in call to `_to` + function safeTransferFrom( + address _from, + address _to, + uint256 _tokenId, + bytes _data + ) + external; + + /// @notice Transfers the ownership of an NFT from one address to another address + /// @dev This works identically to the other function with an extra data parameter, + /// except this function just sets data to "". + /// @param _from The current owner of the NFT + /// @param _to The new owner + /// @param _tokenId The NFT to transfer + function safeTransferFrom( + address _from, + address _to, + uint256 _tokenId + ) + external; + + /// @notice Change or reaffirm the approved address for an NFT + /// @dev The zero address indicates there is no approved address. + /// Throws unless `msg.sender` is the current NFT owner, or an authorized + /// operator of the current owner. + /// @param _approved The new approved NFT controller + /// @param _tokenId The NFT to approve + function approve(address _approved, uint256 _tokenId) + external; + + /// @notice Enable or disable approval for a third party ("operator") to manage + /// all of `msg.sender`'s assets + /// @dev Emits the ApprovalForAll event. The contract MUST allow + /// multiple operators per owner. + /// @param _operator Address to add to the set of authorized operators + /// @param _approved True if the operator is approved, false to revoke approval + function setApprovalForAll(address _operator, bool _approved) + external; + + /// @notice Count all NFTs assigned to an owner + /// @dev NFTs assigned to the zero address are considered invalid, and this + /// function throws for queries about the zero address. + /// @param _owner An address for whom to query the balance + /// @return The number of NFTs owned by `_owner`, possibly zero + function balanceOf(address _owner) + external + view + returns (uint256); + + /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE + /// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE + /// THEY MAY BE PERMANENTLY LOST + /// @dev Throws unless `msg.sender` is the current owner, an authorized + /// operator, or the approved address for this NFT. Throws if `_from` is + /// not the current owner. Throws if `_to` is the zero address. Throws if + /// `_tokenId` is not a valid NFT. + /// @param _from The current owner of the NFT + /// @param _to The new owner + /// @param _tokenId The NFT to transfer + function transferFrom( + address _from, + address _to, + uint256 _tokenId + ) + public; + + /// @notice Find the owner of an NFT + /// @dev NFTs assigned to zero address are considered invalid, and queries + /// about them do throw. + /// @param _tokenId The identifier for an NFT + /// @return The address of the owner of the NFT + function ownerOf(uint256 _tokenId) + public + view + returns (address); + + /// @notice Get the approved address for a single NFT + /// @dev Throws if `_tokenId` is not a valid NFT. + /// @param _tokenId The NFT to find the approved address for + /// @return The approved address for this NFT, or the zero address if there is none + function getApproved(uint256 _tokenId) + public + view + returns (address); + + /// @notice Query if an address is an authorized operator for another address + /// @param _owner The address that owns the NFTs + /// @param _operator The address that acts on behalf of the owner + /// @return True if `_operator` is an approved operator for `_owner`, false otherwise + function isApprovedForAll(address _owner, address _operator) + public + view + returns (bool); +} diff --git a/contracts/tokens/contracts/tokens/ERC721Token/MintableERC721Token.sol b/contracts/tokens/contracts/tokens/ERC721Token/MintableERC721Token.sol new file mode 100644 index 000000000..bc5cd2cc2 --- /dev/null +++ b/contracts/tokens/contracts/tokens/ERC721Token/MintableERC721Token.sol @@ -0,0 +1,82 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; + +import "./ERC721Token.sol"; + + +contract MintableERC721Token is + ERC721Token +{ + /// @dev Function to mint a new token + /// Reverts if the given token ID already exists + /// @param _to Address of the beneficiary that will own the minted token + /// @param _tokenId ID of the token to be minted by the msg.sender + function _mint(address _to, uint256 _tokenId) + internal + { + require( + _to != address(0), + "ERC721_ZERO_TO_ADDRESS" + ); + + address owner = owners[_tokenId]; + require( + owner == address(0), + "ERC721_OWNER_ALREADY_EXISTS" + ); + + owners[_tokenId] = _to; + balances[_to] = safeAdd(balances[_to], 1); + + emit Transfer( + address(0), + _to, + _tokenId + ); + } + + /// @dev Function to burn a token + /// Reverts if the given token ID doesn't exist + /// @param _owner Owner of token with given token ID + /// @param _tokenId ID of the token to be burned by the msg.sender + function _burn(address _owner, uint256 _tokenId) + internal + { + require( + _owner != address(0), + "ERC721_ZERO_OWNER_ADDRESS" + ); + + address owner = owners[_tokenId]; + require( + owner == _owner, + "ERC721_OWNER_MISMATCH" + ); + + owners[_tokenId] = address(0); + balances[_owner] = safeSub(balances[_owner], 1); + + emit Transfer( + _owner, + address(0), + _tokenId + ); + } +} diff --git a/contracts/tokens/contracts/tokens/EtherToken/IEtherToken.sol b/contracts/tokens/contracts/tokens/EtherToken/IEtherToken.sol new file mode 100644 index 000000000..9e2e68766 --- /dev/null +++ b/contracts/tokens/contracts/tokens/EtherToken/IEtherToken.sol @@ -0,0 +1,33 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; + +import "../ERC20Token/IERC20Token.sol"; + + +contract IEtherToken is + IERC20Token +{ + function deposit() + public + payable; + + function withdraw(uint256 amount) + public; +} diff --git a/contracts/tokens/contracts/tokens/EtherToken/WETH9.sol b/contracts/tokens/contracts/tokens/EtherToken/WETH9.sol new file mode 100644 index 000000000..17876b86d --- /dev/null +++ b/contracts/tokens/contracts/tokens/EtherToken/WETH9.sol @@ -0,0 +1,758 @@ +// Copyright (C) 2015, 2016, 2017 Dapphub + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// solhint-disable +pragma solidity ^0.4.18; + + +contract WETH9 { + string public name = "Wrapped Ether"; + string public symbol = "WETH"; + uint8 public decimals = 18; + + event Approval(address indexed _owner, address indexed _spender, uint _value); + event Transfer(address indexed _from, address indexed _to, uint _value); + event Deposit(address indexed _owner, uint _value); + event Withdrawal(address indexed _owner, uint _value); + + mapping (address => uint) public balanceOf; + mapping (address => mapping (address => uint)) public allowance; + + function() public payable { + deposit(); + } + function deposit() public payable { + balanceOf[msg.sender] += msg.value; + Deposit(msg.sender, msg.value); + } + function withdraw(uint wad) public { + require(balanceOf[msg.sender] >= wad); + balanceOf[msg.sender] -= wad; + msg.sender.transfer(wad); + Withdrawal(msg.sender, wad); + } + + function totalSupply() public view returns (uint) { + return this.balance; + } + + function approve(address guy, uint wad) public returns (bool) { + allowance[msg.sender][guy] = wad; + Approval(msg.sender, guy, wad); + return true; + } + + function transfer(address dst, uint wad) public returns (bool) { + return transferFrom(msg.sender, dst, wad); + } + + function transferFrom(address src, address dst, uint wad) + public + returns (bool) + { + require(balanceOf[src] >= wad); + + if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) { + require(allowance[src][msg.sender] >= wad); + allowance[src][msg.sender] -= wad; + } + + balanceOf[src] -= wad; + balanceOf[dst] += wad; + + Transfer(src, dst, wad); + + return true; + } +} + + +/* + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + +*/ diff --git a/contracts/tokens/contracts/tokens/ZRXToken/ERC20Token_v1.sol b/contracts/tokens/contracts/tokens/ZRXToken/ERC20Token_v1.sol new file mode 100644 index 000000000..4920c4aac --- /dev/null +++ b/contracts/tokens/contracts/tokens/ZRXToken/ERC20Token_v1.sol @@ -0,0 +1,44 @@ +pragma solidity ^0.4.11; + +import { Token_v1 as Token } from "./Token_v1.sol"; + +contract ERC20Token_v1 is Token { + + function transfer(address _to, uint _value) returns (bool) { + //Default assumes totalSupply can't be over max (2^256 - 1). + if (balances[msg.sender] >= _value && balances[_to] + _value >= balances[_to]) { + balances[msg.sender] -= _value; + balances[_to] += _value; + Transfer(msg.sender, _to, _value); + return true; + } else { return false; } + } + + function transferFrom(address _from, address _to, uint _value) returns (bool) { + if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value >= balances[_to]) { + balances[_to] += _value; + balances[_from] -= _value; + allowed[_from][msg.sender] -= _value; + Transfer(_from, _to, _value); + return true; + } else { return false; } + } + + function balanceOf(address _owner) constant returns (uint) { + return balances[_owner]; + } + + function approve(address _spender, uint _value) returns (bool) { + allowed[msg.sender][_spender] = _value; + Approval(msg.sender, _spender, _value); + return true; + } + + function allowance(address _owner, address _spender) constant returns (uint) { + return allowed[_owner][_spender]; + } + + mapping (address => uint) balances; + mapping (address => mapping (address => uint)) allowed; + uint public totalSupply; +} diff --git a/contracts/tokens/contracts/tokens/ZRXToken/Token_v1.sol b/contracts/tokens/contracts/tokens/ZRXToken/Token_v1.sol new file mode 100644 index 000000000..de619fb7e --- /dev/null +++ b/contracts/tokens/contracts/tokens/ZRXToken/Token_v1.sol @@ -0,0 +1,39 @@ +pragma solidity ^0.4.11; + +contract Token_v1 { + + /// @return total amount of tokens + function totalSupply() constant returns (uint supply) {} + + /// @param _owner The address from which the balance will be retrieved + /// @return The balance + function balanceOf(address _owner) constant returns (uint balance) {} + + /// @notice send `_value` token to `_to` from `msg.sender` + /// @param _to The address of the recipient + /// @param _value The amount of token to be transferred + /// @return Whether the transfer was successful or not + function transfer(address _to, uint _value) returns (bool success) {} + + /// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from` + /// @param _from The address of the sender + /// @param _to The address of the recipient + /// @param _value The amount of token to be transferred + /// @return Whether the transfer was successful or not + function transferFrom(address _from, address _to, uint _value) returns (bool success) {} + + /// @notice `msg.sender` approves `_addr` to spend `_value` tokens + /// @param _spender The address of the account able to transfer the tokens + /// @param _value The amount of wei to be approved for transfer + /// @return Whether the approval was successful or not + function approve(address _spender, uint _value) returns (bool success) {} + + /// @param _owner The address of the account owning tokens + /// @param _spender The address of the account able to transfer the tokens + /// @return Amount of remaining tokens allowed to spent + function allowance(address _owner, address _spender) constant returns (uint remaining) {} + + event Transfer(address indexed _from, address indexed _to, uint _value); + event Approval(address indexed _owner, address indexed _spender, uint _value); +} + diff --git a/contracts/tokens/contracts/tokens/ZRXToken/UnlimitedAllowanceToken_v1.sol b/contracts/tokens/contracts/tokens/ZRXToken/UnlimitedAllowanceToken_v1.sol new file mode 100644 index 000000000..bf1b0335a --- /dev/null +++ b/contracts/tokens/contracts/tokens/ZRXToken/UnlimitedAllowanceToken_v1.sol @@ -0,0 +1,52 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.4.11; + +import { ERC20Token_v1 as ERC20Token } from "./ERC20Token_v1.sol"; + +contract UnlimitedAllowanceToken_v1 is ERC20Token { + + uint constant MAX_UINT = 2**256 - 1; + + /// @dev ERC20 transferFrom, modified such that an allowance of MAX_UINT represents an unlimited allowance. + /// @param _from Address to transfer from. + /// @param _to Address to transfer to. + /// @param _value Amount to transfer. + /// @return Success of transfer. + function transferFrom(address _from, address _to, uint _value) + public + returns (bool) + { + uint allowance = allowed[_from][msg.sender]; + if (balances[_from] >= _value + && allowance >= _value + && balances[_to] + _value >= balances[_to] + ) { + balances[_to] += _value; + balances[_from] -= _value; + if (allowance < MAX_UINT) { + allowed[_from][msg.sender] -= _value; + } + Transfer(_from, _to, _value); + return true; + } else { + return false; + } + } +} diff --git a/contracts/tokens/contracts/tokens/ZRXToken/ZRXToken.sol b/contracts/tokens/contracts/tokens/ZRXToken/ZRXToken.sol new file mode 100644 index 000000000..831e1822c --- /dev/null +++ b/contracts/tokens/contracts/tokens/ZRXToken/ZRXToken.sol @@ -0,0 +1,41 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.11; + +// solhint-disable-next-line max-line-length +import { UnlimitedAllowanceToken_v1 as UnlimitedAllowanceToken } from "./UnlimitedAllowanceToken_v1.sol"; + + +contract ZRXToken is + UnlimitedAllowanceToken +{ + + // solhint-disable const-name-snakecase + uint8 constant public decimals = 18; + uint256 public totalSupply = 10**27; // 1 billion tokens, 18 decimal places + string constant public name = "0x Protocol Token"; + string constant public symbol = "ZRX"; + // solhint-enableconst-name-snakecase + + function ZRXToken() + public + { + balances[msg.sender] = totalSupply; + } +} diff --git a/contracts/tokens/package.json b/contracts/tokens/package.json new file mode 100644 index 000000000..0666ea89a --- /dev/null +++ b/contracts/tokens/package.json @@ -0,0 +1,94 @@ +{ + "private": true, + "name": "@0x/contracts-tokens", + "version": "1.0.0", + "engines": { + "node": ">=6.12" + }, + "description": "Token contracts used by 0x protocol", + "main": "lib/src/index.js", + "directories": { + "test": "test" + }, + "scripts": { + "build": "yarn pre_build && tsc -b", + "build:ci": "yarn build", + "pre_build": "run-s compile generate_contract_wrappers", + "test": "yarn run_mocha", + "rebuild_and_test": "run-s build test", + "test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov", + "test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html", + "test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha", + "run_mocha": + "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit", + "compile": "sol-compiler --contracts-dir contracts", + "clean": "shx rm -rf lib generated-artifacts generated-wrappers", + "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers", + "lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts", + "coverage:report:text": "istanbul report text", + "coverage:report:html": "istanbul report html && open coverage/index.html", + "profiler:report:html": "istanbul report html && open coverage/index.html", + "coverage:report:lcov": "istanbul report lcov", + "test:circleci": "yarn test", + "lint-contracts": "solhint contracts/**/**/**/**/*.sol" + }, + "config": { + "abis": "generated-artifacts/@(DummyERC20Token|DummyMultipleReturnERC20Token|DummyNoReturnERC20Token|DummyERC721Receiver|InvalidERC721Receiver|DummyERC721Token|ReentrantERC20Token|ERC20Token|IERC20Token|MintableERC20Token|UnlimitedAllowanceERC20Token|ERC721Token|IERC721Receiver|IERC721Token|MintableERC721Token|IEtherToken|WETH9|ERC20Token_v1|Token_v1|UnlimitedAllowanceToken_v1|ZRXToken).json" + }, + "repository": { + "type": "git", + "url": "https://github.com/0xProject/0x-monorepo.git" + }, + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/0xProject/0x-monorepo/issues" + }, + "homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md", + "devDependencies": { + "@0x/contracts-test-utils": "^1.0.0", + "@0x/abi-gen": "^1.0.17", + "@0x/dev-utils": "^1.0.19", + "@0x/sol-compiler": "^1.1.14", + "@0x/sol-cov": "^2.1.14", + "@0x/subproviders": "^2.1.6", + "@0x/tslint-config": "^1.0.10", + "@types/bn.js": "^4.11.0", + "@types/lodash": "4.14.104", + "@types/node": "*", + "@types/yargs": "^10.0.0", + "chai": "^4.0.1", + "chai-as-promised": "^7.1.0", + "chai-bignumber": "^2.0.1", + "dirty-chai": "^2.0.1", + "make-promises-safe": "^1.1.0", + "ethereumjs-abi": "0.6.5", + "mocha": "^4.1.0", + "npm-run-all": "^4.1.2", + "shx": "^0.2.2", + "solc": "^0.4.24", + "solhint": "^1.2.1", + "tslint": "5.11.0", + "typescript": "3.0.1", + "yargs": "^10.0.3" + }, + "dependencies": { + "@0x/base-contract": "^3.0.8", + "@0x/order-utils": "^3.0.4", + "@0x/contracts-multisig": "^1.0.0", + "@0x/contracts-utils": "^1.0.0", + "@0x/contracts-libs": "^1.0.0", + "@0x/contracts-interfaces": "^1.0.0", + "@0x/types": "^1.3.0", + "@0x/typescript-typings": "^3.0.4", + "@0x/utils": "^2.0.6", + "@0x/web3-wrapper": "^3.1.6", + "@types/js-combinatorics": "^0.5.29", + "bn.js": "^4.11.8", + "ethereum-types": "^1.1.2", + "ethereumjs-util": "^5.1.1", + "lodash": "^4.17.5" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/contracts/tokens/src/artifacts/index.ts b/contracts/tokens/src/artifacts/index.ts new file mode 100644 index 000000000..63b808748 --- /dev/null +++ b/contracts/tokens/src/artifacts/index.ts @@ -0,0 +1,49 @@ +import { ContractArtifact } from 'ethereum-types'; + +import * as DummyERC20Token from '../../generated-artifacts/DummyERC20Token.json'; +import * as DummyERC721Receiver from '../../generated-artifacts/DummyERC721Receiver.json'; +import * as DummyERC721Token from '../../generated-artifacts/DummyERC721Token.json'; +import * as DummyMultipleReturnERC20Token from '../../generated-artifacts/DummyMultipleReturnERC20Token.json'; +import * as DummyNoReturnERC20Token from '../../generated-artifacts/DummyNoReturnERC20Token.json'; +import * as ERC20Token from '../../generated-artifacts/ERC20Token.json'; +import * as ERC20Token_v1 from '../../generated-artifacts/ERC20Token_v1.json'; +import * as ERC721Token from '../../generated-artifacts/ERC721Token.json'; +import * as IERC20Token from '../../generated-artifacts/IERC20Token.json'; +import * as IERC721Receiver from '../../generated-artifacts/IERC721Receiver.json'; +import * as IERC721Token from '../../generated-artifacts/IERC721Token.json'; +import * as IEtherToken from '../../generated-artifacts/IEtherToken.json'; +import * as InvalidERC721Receiver from '../../generated-artifacts/InvalidERC721Receiver.json'; +import * as MintableERC20Token from '../../generated-artifacts/MintableERC20Token.json'; +import * as MintableERC721Token from '../../generated-artifacts/MintableERC721Token.json'; +import * as ReentrantERC20Token from '../../generated-artifacts/ReentrantERC20Token.json'; +import * as Token_v1 from '../../generated-artifacts/Token_v1.json'; +import * as UnlimitedAllowanceERC20Token from '../../generated-artifacts/UnlimitedAllowanceERC20Token.json'; +import * as UnlimitedAllowanceToken_v1 from '../../generated-artifacts/UnlimitedAllowanceToken_v1.json'; +import * as WETH9 from '../../generated-artifacts/WETH9.json'; +import * as ZRXToken from '../../generated-artifacts/ZRXToken.json'; + +export const artifacts = { + DummyERC20Token: DummyERC20Token as ContractArtifact, + DummyMultipleReturnERC20Token: DummyMultipleReturnERC20Token as ContractArtifact, + DummyNoReturnERC20Token: DummyNoReturnERC20Token as ContractArtifact, + DummyERC721Receiver: DummyERC721Receiver as ContractArtifact, + InvalidERC721Receiver: InvalidERC721Receiver as ContractArtifact, + DummyERC721Token: DummyERC721Token as ContractArtifact, + ReentrantERC20Token: ReentrantERC20Token as ContractArtifact, + ERC20Token: ERC20Token as ContractArtifact, + IERC20Token: IERC20Token as ContractArtifact, + MintableERC20Token: MintableERC20Token as ContractArtifact, + UnlimitedAllowanceERC20Token: UnlimitedAllowanceERC20Token as ContractArtifact, + ERC721Token: ERC721Token as ContractArtifact, + IERC721Receiver: IERC721Receiver as ContractArtifact, + IERC721Token: IERC721Token as ContractArtifact, + MintableERC721Token: MintableERC721Token as ContractArtifact, + IEtherToken: IEtherToken as ContractArtifact, + WETH9: WETH9 as ContractArtifact, + ERC20Token_v1: ERC20Token_v1 as ContractArtifact, + Token_v1: Token_v1 as ContractArtifact, + UnlimitedAllowanceToken_v1: UnlimitedAllowanceToken_v1 as ContractArtifact, + // Note(albrow): "as any" hack still required here because ZRXToken does not + // conform to the v2 artifact type. + ZRXToken: (ZRXToken as any) as ContractArtifact, +}; diff --git a/contracts/tokens/src/index.ts b/contracts/tokens/src/index.ts new file mode 100644 index 000000000..91dd7e0e3 --- /dev/null +++ b/contracts/tokens/src/index.ts @@ -0,0 +1,2 @@ +export * from './wrappers'; +export * from './artifacts'; diff --git a/contracts/tokens/src/wrappers/index.ts b/contracts/tokens/src/wrappers/index.ts new file mode 100644 index 000000000..98bf26657 --- /dev/null +++ b/contracts/tokens/src/wrappers/index.ts @@ -0,0 +1,21 @@ +export * from '../../generated-wrappers/zrx_token'; +export * from '../../generated-wrappers/weth9'; +export * from '../../generated-wrappers/unlimitedallowancetoken_v1'; +export * from '../../generated-wrappers/unlimited_allowance_erc20_token'; +export * from '../../generated-wrappers/token_v1'; +export * from '../../generated-wrappers/reentrant_erc20_token'; +export * from '../../generated-wrappers/mintable_erc721_token'; +export * from '../../generated-wrappers/mintable_erc20_token'; +export * from '../../generated-wrappers/invalid_erc721_receiver'; +export * from '../../generated-wrappers/i_ether_token'; +export * from '../../generated-wrappers/i_erc721_token'; +export * from '../../generated-wrappers/i_erc721_receiver'; +export * from '../../generated-wrappers/i_erc20_token'; +export * from '../../generated-wrappers/erc721_token'; +export * from '../../generated-wrappers/erc20token_v1'; +export * from '../../generated-wrappers/erc20_token'; +export * from '../../generated-wrappers/dummy_no_return_erc20_token'; +export * from '../../generated-wrappers/dummy_multiple_return_erc20_token'; +export * from '../../generated-wrappers/dummy_erc721_token'; +export * from '../../generated-wrappers/dummy_erc721_receiver'; +export * from '../../generated-wrappers/dummy_erc20_token'; diff --git a/contracts/tokens/test/erc721_token.ts b/contracts/tokens/test/erc721_token.ts new file mode 100644 index 000000000..13332cd35 --- /dev/null +++ b/contracts/tokens/test/erc721_token.ts @@ -0,0 +1,282 @@ +import { + chaiSetup, + constants, + expectTransactionFailedAsync, + expectTransactionFailedWithoutReasonAsync, + LogDecoder, + provider, + txDefaults, + web3Wrapper, +} from '@0x/contracts-test-utils'; +import { BlockchainLifecycle } from '@0x/dev-utils'; +import { RevertReason } from '@0x/types'; +import { BigNumber } from '@0x/utils'; +import * as chai from 'chai'; +import { LogWithDecodedArgs } from 'ethereum-types'; + +import { + artifacts, + DummyERC721ReceiverContract, + DummyERC721ReceiverTokenReceivedEventArgs, + DummyERC721TokenContract, + DummyERC721TokenTransferEventArgs, + InvalidERC721ReceiverContract, +} from '../src'; + +chaiSetup.configure(); +const expect = chai.expect; +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); +// tslint:disable:no-unnecessary-type-assertion +describe('ERC721Token', () => { + let owner: string; + let spender: string; + let token: DummyERC721TokenContract; + let erc721Receiver: DummyERC721ReceiverContract; + let logDecoder: LogDecoder; + const tokenId = new BigNumber(1); + before(async () => { + await blockchainLifecycle.startAsync(); + }); + after(async () => { + await blockchainLifecycle.revertAsync(); + }); + before(async () => { + const accounts = await web3Wrapper.getAvailableAddressesAsync(); + owner = accounts[0]; + spender = accounts[1]; + token = await DummyERC721TokenContract.deployFrom0xArtifactAsync( + artifacts.DummyERC721Token, + provider, + txDefaults, + constants.DUMMY_TOKEN_NAME, + constants.DUMMY_TOKEN_SYMBOL, + ); + erc721Receiver = await DummyERC721ReceiverContract.deployFrom0xArtifactAsync( + artifacts.DummyERC721Receiver, + provider, + txDefaults, + ); + logDecoder = new LogDecoder(web3Wrapper, artifacts); + await web3Wrapper.awaitTransactionSuccessAsync( + await token.mint.sendTransactionAsync(owner, tokenId, { from: owner }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + }); + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + + describe('transferFrom', () => { + it('should revert if the tokenId is not owner', async () => { + const from = owner; + const to = erc721Receiver.address; + const unownedTokenId = new BigNumber(2); + await expectTransactionFailedAsync( + token.transferFrom.sendTransactionAsync(from, to, unownedTokenId), + RevertReason.Erc721ZeroOwner, + ); + }); + it('should revert if transferring to a null address', async () => { + const from = owner; + const to = constants.NULL_ADDRESS; + await expectTransactionFailedAsync( + token.transferFrom.sendTransactionAsync(from, to, tokenId), + RevertReason.Erc721ZeroToAddress, + ); + }); + it('should revert if the from address does not own the token', async () => { + const from = spender; + const to = erc721Receiver.address; + await expectTransactionFailedAsync( + token.transferFrom.sendTransactionAsync(from, to, tokenId), + RevertReason.Erc721OwnerMismatch, + ); + }); + it('should revert if spender does not own the token, is not approved, and is not approved for all', async () => { + const from = owner; + const to = erc721Receiver.address; + await expectTransactionFailedAsync( + token.transferFrom.sendTransactionAsync(from, to, tokenId, { from: spender }), + RevertReason.Erc721InvalidSpender, + ); + }); + it('should transfer the token if called by owner', async () => { + const from = owner; + const to = erc721Receiver.address; + const txReceipt = await logDecoder.getTxWithDecodedLogsAsync( + await token.transferFrom.sendTransactionAsync(from, to, tokenId), + ); + const newOwner = await token.ownerOf.callAsync(tokenId); + expect(newOwner).to.be.equal(to); + const log = txReceipt.logs[0] as LogWithDecodedArgs; + expect(log.args._from).to.be.equal(from); + expect(log.args._to).to.be.equal(to); + expect(log.args._tokenId).to.be.bignumber.equal(tokenId); + }); + it('should transfer the token if spender is approved for all', async () => { + const isApproved = true; + await web3Wrapper.awaitTransactionSuccessAsync( + await token.setApprovalForAll.sendTransactionAsync(spender, isApproved), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + + const from = owner; + const to = erc721Receiver.address; + const txReceipt = await logDecoder.getTxWithDecodedLogsAsync( + await token.transferFrom.sendTransactionAsync(from, to, tokenId), + ); + const newOwner = await token.ownerOf.callAsync(tokenId); + expect(newOwner).to.be.equal(to); + const log = txReceipt.logs[0] as LogWithDecodedArgs; + expect(log.args._from).to.be.equal(from); + expect(log.args._to).to.be.equal(to); + expect(log.args._tokenId).to.be.bignumber.equal(tokenId); + }); + it('should transfer the token if spender is individually approved', async () => { + await web3Wrapper.awaitTransactionSuccessAsync( + await token.approve.sendTransactionAsync(spender, tokenId), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + + const from = owner; + const to = erc721Receiver.address; + const txReceipt = await logDecoder.getTxWithDecodedLogsAsync( + await token.transferFrom.sendTransactionAsync(from, to, tokenId), + ); + const newOwner = await token.ownerOf.callAsync(tokenId); + expect(newOwner).to.be.equal(to); + + const approvedAddress = await token.getApproved.callAsync(tokenId); + expect(approvedAddress).to.be.equal(constants.NULL_ADDRESS); + const log = txReceipt.logs[0] as LogWithDecodedArgs; + expect(log.args._from).to.be.equal(from); + expect(log.args._to).to.be.equal(to); + expect(log.args._tokenId).to.be.bignumber.equal(tokenId); + }); + }); + describe('safeTransferFrom without data', () => { + it('should transfer token to a non-contract address if called by owner', async () => { + const from = owner; + const to = spender; + const txReceipt = await logDecoder.getTxWithDecodedLogsAsync( + await token.safeTransferFrom1.sendTransactionAsync(from, to, tokenId), + ); + const newOwner = await token.ownerOf.callAsync(tokenId); + expect(newOwner).to.be.equal(to); + const log = txReceipt.logs[0] as LogWithDecodedArgs; + expect(log.args._from).to.be.equal(from); + expect(log.args._to).to.be.equal(to); + expect(log.args._tokenId).to.be.bignumber.equal(tokenId); + }); + it('should revert if transferring to a contract address without onERC721Received', async () => { + const contract = await DummyERC721TokenContract.deployFrom0xArtifactAsync( + artifacts.DummyERC721Token, + provider, + txDefaults, + constants.DUMMY_TOKEN_NAME, + constants.DUMMY_TOKEN_SYMBOL, + ); + const from = owner; + const to = contract.address; + await expectTransactionFailedWithoutReasonAsync( + token.safeTransferFrom1.sendTransactionAsync(from, to, tokenId), + ); + }); + it('should revert if onERC721Received does not return the correct value', async () => { + const invalidErc721Receiver = await InvalidERC721ReceiverContract.deployFrom0xArtifactAsync( + artifacts.InvalidERC721Receiver, + provider, + txDefaults, + ); + const from = owner; + const to = invalidErc721Receiver.address; + await expectTransactionFailedAsync( + token.safeTransferFrom1.sendTransactionAsync(from, to, tokenId), + RevertReason.Erc721InvalidSelector, + ); + }); + it('should transfer to contract and call onERC721Received with correct return value', async () => { + const from = owner; + const to = erc721Receiver.address; + const txReceipt = await logDecoder.getTxWithDecodedLogsAsync( + await token.safeTransferFrom1.sendTransactionAsync(from, to, tokenId), + ); + const newOwner = await token.ownerOf.callAsync(tokenId); + expect(newOwner).to.be.equal(to); + const transferLog = txReceipt.logs[0] as LogWithDecodedArgs; + const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs; + expect(transferLog.args._from).to.be.equal(from); + expect(transferLog.args._to).to.be.equal(to); + expect(transferLog.args._tokenId).to.be.bignumber.equal(tokenId); + expect(receiverLog.args.operator).to.be.equal(owner); + expect(receiverLog.args.from).to.be.equal(from); + expect(receiverLog.args.tokenId).to.be.bignumber.equal(tokenId); + expect(receiverLog.args.data).to.be.equal(constants.NULL_BYTES); + }); + }); + describe('safeTransferFrom with data', () => { + const data = '0x0102030405060708090a0b0c0d0e0f'; + it('should transfer token to a non-contract address if called by owner', async () => { + const from = owner; + const to = spender; + const txReceipt = await logDecoder.getTxWithDecodedLogsAsync( + await token.safeTransferFrom2.sendTransactionAsync(from, to, tokenId, data), + ); + const newOwner = await token.ownerOf.callAsync(tokenId); + expect(newOwner).to.be.equal(to); + const log = txReceipt.logs[0] as LogWithDecodedArgs; + expect(log.args._from).to.be.equal(from); + expect(log.args._to).to.be.equal(to); + expect(log.args._tokenId).to.be.bignumber.equal(tokenId); + }); + it('should revert if transferring to a contract address without onERC721Received', async () => { + const contract = await DummyERC721TokenContract.deployFrom0xArtifactAsync( + artifacts.DummyERC721Token, + provider, + txDefaults, + constants.DUMMY_TOKEN_NAME, + constants.DUMMY_TOKEN_SYMBOL, + ); + const from = owner; + const to = contract.address; + await expectTransactionFailedWithoutReasonAsync( + token.safeTransferFrom2.sendTransactionAsync(from, to, tokenId, data), + ); + }); + it('should revert if onERC721Received does not return the correct value', async () => { + const invalidErc721Receiver = await InvalidERC721ReceiverContract.deployFrom0xArtifactAsync( + artifacts.InvalidERC721Receiver, + provider, + txDefaults, + ); + const from = owner; + const to = invalidErc721Receiver.address; + await expectTransactionFailedAsync( + token.safeTransferFrom2.sendTransactionAsync(from, to, tokenId, data), + RevertReason.Erc721InvalidSelector, + ); + }); + it('should transfer to contract and call onERC721Received with correct return value', async () => { + const from = owner; + const to = erc721Receiver.address; + const txReceipt = await logDecoder.getTxWithDecodedLogsAsync( + await token.safeTransferFrom2.sendTransactionAsync(from, to, tokenId, data), + ); + const newOwner = await token.ownerOf.callAsync(tokenId); + expect(newOwner).to.be.equal(to); + const transferLog = txReceipt.logs[0] as LogWithDecodedArgs; + const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs; + expect(transferLog.args._from).to.be.equal(from); + expect(transferLog.args._to).to.be.equal(to); + expect(transferLog.args._tokenId).to.be.bignumber.equal(tokenId); + expect(receiverLog.args.operator).to.be.equal(owner); + expect(receiverLog.args.from).to.be.equal(from); + expect(receiverLog.args.tokenId).to.be.bignumber.equal(tokenId); + expect(receiverLog.args.data).to.be.equal(data); + }); + }); +}); +// tslint:enable:no-unnecessary-type-assertion diff --git a/contracts/tokens/test/global_hooks.ts b/contracts/tokens/test/global_hooks.ts new file mode 100644 index 000000000..f8ace376a --- /dev/null +++ b/contracts/tokens/test/global_hooks.ts @@ -0,0 +1,17 @@ +import { env, EnvVars } from '@0x/dev-utils'; + +import { coverage, profiler, provider } from '@0x/contracts-test-utils'; +before('start web3 provider', () => { + provider.start(); +}); +after('generate coverage report', async () => { + if (env.parseBoolean(EnvVars.SolidityCoverage)) { + const coverageSubprovider = coverage.getCoverageSubproviderSingleton(); + await coverageSubprovider.writeCoverageAsync(); + } + if (env.parseBoolean(EnvVars.SolidityProfiler)) { + const profilerSubprovider = profiler.getProfilerSubproviderSingleton(); + await profilerSubprovider.writeProfilerOutputAsync(); + } + provider.stop(); +}); diff --git a/contracts/tokens/test/unlimited_allowance_token.ts b/contracts/tokens/test/unlimited_allowance_token.ts new file mode 100644 index 000000000..6d5a29b23 --- /dev/null +++ b/contracts/tokens/test/unlimited_allowance_token.ts @@ -0,0 +1,194 @@ +import { + chaiSetup, + constants, + expectContractCallFailedAsync, + provider, + txDefaults, + web3Wrapper, +} from '@0x/contracts-test-utils'; +import { BlockchainLifecycle } from '@0x/dev-utils'; +import { RevertReason } from '@0x/types'; +import { BigNumber } from '@0x/utils'; +import * as chai from 'chai'; + +import { artifacts, DummyERC20TokenContract } from '../src'; + +chaiSetup.configure(); +const expect = chai.expect; +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); + +describe('UnlimitedAllowanceToken', () => { + let owner: string; + let spender: string; + const MAX_MINT_VALUE = new BigNumber(10000000000000000000000); + let token: DummyERC20TokenContract; + + before(async () => { + await blockchainLifecycle.startAsync(); + }); + after(async () => { + await blockchainLifecycle.revertAsync(); + }); + before(async () => { + const accounts = await web3Wrapper.getAvailableAddressesAsync(); + owner = accounts[0]; + spender = accounts[1]; + token = await DummyERC20TokenContract.deployFrom0xArtifactAsync( + artifacts.DummyERC20Token, + provider, + txDefaults, + constants.DUMMY_TOKEN_NAME, + constants.DUMMY_TOKEN_SYMBOL, + constants.DUMMY_TOKEN_DECIMALS, + constants.DUMMY_TOKEN_TOTAL_SUPPLY, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await token.mint.sendTransactionAsync(MAX_MINT_VALUE, { from: owner }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + }); + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + describe('transfer', () => { + it('should throw if owner has insufficient balance', async () => { + const ownerBalance = await token.balanceOf.callAsync(owner); + const amountToTransfer = ownerBalance.plus(1); + return expectContractCallFailedAsync( + token.transfer.callAsync(spender, amountToTransfer, { from: owner }), + RevertReason.Erc20InsufficientBalance, + ); + }); + + it('should transfer balance from sender to receiver', async () => { + const receiver = spender; + const initOwnerBalance = await token.balanceOf.callAsync(owner); + const amountToTransfer = new BigNumber(1); + await web3Wrapper.awaitTransactionSuccessAsync( + await token.transfer.sendTransactionAsync(receiver, amountToTransfer, { from: owner }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const finalOwnerBalance = await token.balanceOf.callAsync(owner); + const finalReceiverBalance = await token.balanceOf.callAsync(receiver); + + const expectedFinalOwnerBalance = initOwnerBalance.minus(amountToTransfer); + const expectedFinalReceiverBalance = amountToTransfer; + expect(finalOwnerBalance).to.be.bignumber.equal(expectedFinalOwnerBalance); + expect(finalReceiverBalance).to.be.bignumber.equal(expectedFinalReceiverBalance); + }); + + it('should return true on a 0 value transfer', async () => { + const didReturnTrue = await token.transfer.callAsync(spender, new BigNumber(0), { + from: owner, + }); + expect(didReturnTrue).to.be.true(); + }); + }); + + describe('transferFrom', () => { + it('should throw if owner has insufficient balance', async () => { + const ownerBalance = await token.balanceOf.callAsync(owner); + const amountToTransfer = ownerBalance.plus(1); + await web3Wrapper.awaitTransactionSuccessAsync( + await token.approve.sendTransactionAsync(spender, amountToTransfer, { from: owner }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + return expectContractCallFailedAsync( + token.transferFrom.callAsync(owner, spender, amountToTransfer, { + from: spender, + }), + RevertReason.Erc20InsufficientBalance, + ); + }); + + it('should throw if spender has insufficient allowance', async () => { + const ownerBalance = await token.balanceOf.callAsync(owner); + const amountToTransfer = ownerBalance; + + const spenderAllowance = await token.allowance.callAsync(owner, spender); + const isSpenderAllowanceInsufficient = spenderAllowance.cmp(amountToTransfer) < 0; + expect(isSpenderAllowanceInsufficient).to.be.true(); + + return expectContractCallFailedAsync( + token.transferFrom.callAsync(owner, spender, amountToTransfer, { + from: spender, + }), + RevertReason.Erc20InsufficientAllowance, + ); + }); + + it('should return true on a 0 value transfer', async () => { + const amountToTransfer = new BigNumber(0); + const didReturnTrue = await token.transferFrom.callAsync(owner, spender, amountToTransfer, { + from: spender, + }); + expect(didReturnTrue).to.be.true(); + }); + + it('should not modify spender allowance if spender allowance is 2^256 - 1', async () => { + const initOwnerBalance = await token.balanceOf.callAsync(owner); + const amountToTransfer = initOwnerBalance; + const initSpenderAllowance = constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; + await web3Wrapper.awaitTransactionSuccessAsync( + await token.approve.sendTransactionAsync(spender, initSpenderAllowance, { from: owner }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await token.transferFrom.sendTransactionAsync(owner, spender, amountToTransfer, { + from: spender, + gas: constants.MAX_TOKEN_TRANSFERFROM_GAS, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + + const newSpenderAllowance = await token.allowance.callAsync(owner, spender); + expect(initSpenderAllowance).to.be.bignumber.equal(newSpenderAllowance); + }); + + it('should transfer the correct balances if spender has sufficient allowance', async () => { + const initOwnerBalance = await token.balanceOf.callAsync(owner); + const amountToTransfer = initOwnerBalance; + const initSpenderAllowance = initOwnerBalance; + await web3Wrapper.awaitTransactionSuccessAsync( + await token.approve.sendTransactionAsync(spender, initSpenderAllowance, { from: owner }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await token.transferFrom.sendTransactionAsync(owner, spender, amountToTransfer, { + from: spender, + gas: constants.MAX_TOKEN_TRANSFERFROM_GAS, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + + const newOwnerBalance = await token.balanceOf.callAsync(owner); + const newSpenderBalance = await token.balanceOf.callAsync(spender); + + expect(newOwnerBalance).to.be.bignumber.equal(0); + expect(newSpenderBalance).to.be.bignumber.equal(initOwnerBalance); + }); + + it('should modify allowance if spender has sufficient allowance less than 2^256 - 1', async () => { + const initOwnerBalance = await token.balanceOf.callAsync(owner); + const amountToTransfer = initOwnerBalance; + const initSpenderAllowance = initOwnerBalance; + await web3Wrapper.awaitTransactionSuccessAsync( + await token.approve.sendTransactionAsync(spender, initSpenderAllowance, { from: owner }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await token.transferFrom.sendTransactionAsync(owner, spender, amountToTransfer, { + from: spender, + gas: constants.MAX_TOKEN_TRANSFERFROM_GAS, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + + const newSpenderAllowance = await token.allowance.callAsync(owner, spender); + expect(newSpenderAllowance).to.be.bignumber.equal(0); + }); + }); +}); diff --git a/contracts/tokens/test/weth9.ts b/contracts/tokens/test/weth9.ts new file mode 100644 index 000000000..6a3948e2c --- /dev/null +++ b/contracts/tokens/test/weth9.ts @@ -0,0 +1,142 @@ +import { + chaiSetup, + constants, + expectInsufficientFundsAsync, + expectTransactionFailedWithoutReasonAsync, + provider, + txDefaults, + web3Wrapper, +} from '@0x/contracts-test-utils'; +import { BlockchainLifecycle } from '@0x/dev-utils'; +import { BigNumber } from '@0x/utils'; +import { Web3Wrapper } from '@0x/web3-wrapper'; +import * as chai from 'chai'; + +import { artifacts, WETH9Contract } from '../src'; + +chaiSetup.configure(); +const expect = chai.expect; +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); + +describe('EtherToken', () => { + let account: string; + const gasPrice = Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 9); + let etherToken: WETH9Contract; + + before(async () => { + await blockchainLifecycle.startAsync(); + }); + after(async () => { + await blockchainLifecycle.revertAsync(); + }); + before(async () => { + const accounts = await web3Wrapper.getAvailableAddressesAsync(); + account = accounts[0]; + + etherToken = await WETH9Contract.deployFrom0xArtifactAsync(artifacts.WETH9, provider, { + gasPrice, + ...txDefaults, + }); + }); + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + describe('deposit', () => { + it('should throw if caller attempts to deposit more Ether than caller balance', async () => { + const initEthBalance = await web3Wrapper.getBalanceInWeiAsync(account); + const ethToDeposit = initEthBalance.plus(1); + + return expectInsufficientFundsAsync(etherToken.deposit.sendTransactionAsync({ value: ethToDeposit })); + }); + + it('should convert deposited Ether to wrapped Ether tokens', async () => { + const initEthBalance = await web3Wrapper.getBalanceInWeiAsync(account); + const initEthTokenBalance = await etherToken.balanceOf.callAsync(account); + + const ethToDeposit = new BigNumber(Web3Wrapper.toWei(new BigNumber(1))); + + const txHash = await etherToken.deposit.sendTransactionAsync({ value: ethToDeposit }); + const receipt = await web3Wrapper.awaitTransactionSuccessAsync( + txHash, + constants.AWAIT_TRANSACTION_MINED_MS, + ); + + const ethSpentOnGas = gasPrice.times(receipt.gasUsed); + const finalEthBalance = await web3Wrapper.getBalanceInWeiAsync(account); + const finalEthTokenBalance = await etherToken.balanceOf.callAsync(account); + + expect(finalEthBalance).to.be.bignumber.equal(initEthBalance.minus(ethToDeposit.plus(ethSpentOnGas))); + expect(finalEthTokenBalance).to.be.bignumber.equal(initEthTokenBalance.plus(ethToDeposit)); + }); + }); + + describe('withdraw', () => { + it('should throw if caller attempts to withdraw greater than caller balance', async () => { + const initEthTokenBalance = await etherToken.balanceOf.callAsync(account); + const ethTokensToWithdraw = initEthTokenBalance.plus(1); + + return expectTransactionFailedWithoutReasonAsync( + etherToken.withdraw.sendTransactionAsync(ethTokensToWithdraw), + ); + }); + + it('should convert ether tokens to ether with sufficient balance', async () => { + const ethToDeposit = new BigNumber(Web3Wrapper.toWei(new BigNumber(1))); + await web3Wrapper.awaitTransactionSuccessAsync( + await etherToken.deposit.sendTransactionAsync({ value: ethToDeposit }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const initEthTokenBalance = await etherToken.balanceOf.callAsync(account); + const initEthBalance = await web3Wrapper.getBalanceInWeiAsync(account); + const ethTokensToWithdraw = initEthTokenBalance; + expect(ethTokensToWithdraw).to.not.be.bignumber.equal(0); + const txHash = await etherToken.withdraw.sendTransactionAsync(ethTokensToWithdraw, { + gas: constants.MAX_ETHERTOKEN_WITHDRAW_GAS, + }); + const receipt = await web3Wrapper.awaitTransactionSuccessAsync( + txHash, + constants.AWAIT_TRANSACTION_MINED_MS, + ); + + const ethSpentOnGas = gasPrice.times(receipt.gasUsed); + const finalEthBalance = await web3Wrapper.getBalanceInWeiAsync(account); + const finalEthTokenBalance = await etherToken.balanceOf.callAsync(account); + + expect(finalEthBalance).to.be.bignumber.equal( + initEthBalance.plus(ethTokensToWithdraw.minus(ethSpentOnGas)), + ); + expect(finalEthTokenBalance).to.be.bignumber.equal(initEthTokenBalance.minus(ethTokensToWithdraw)); + }); + }); + + describe('fallback', () => { + it('should convert sent ether to ether tokens', async () => { + const initEthBalance = await web3Wrapper.getBalanceInWeiAsync(account); + const initEthTokenBalance = await etherToken.balanceOf.callAsync(account); + + const ethToDeposit = Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18); + + const txHash = await web3Wrapper.sendTransactionAsync({ + from: account, + to: etherToken.address, + value: ethToDeposit, + gasPrice, + }); + + const receipt = await web3Wrapper.awaitTransactionSuccessAsync( + txHash, + constants.AWAIT_TRANSACTION_MINED_MS, + ); + + const ethSpentOnGas = gasPrice.times(receipt.gasUsed); + const finalEthBalance = await web3Wrapper.getBalanceInWeiAsync(account); + const finalEthTokenBalance = await etherToken.balanceOf.callAsync(account); + + expect(finalEthBalance).to.be.bignumber.equal(initEthBalance.minus(ethToDeposit.plus(ethSpentOnGas))); + expect(finalEthTokenBalance).to.be.bignumber.equal(initEthTokenBalance.plus(ethToDeposit)); + }); + }); +}); diff --git a/contracts/tokens/test/zrx_token.ts b/contracts/tokens/test/zrx_token.ts new file mode 100644 index 000000000..5dc8447f6 --- /dev/null +++ b/contracts/tokens/test/zrx_token.ts @@ -0,0 +1,203 @@ +import { chaiSetup, constants, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils'; +import { BlockchainLifecycle } from '@0x/dev-utils'; +import { BigNumber } from '@0x/utils'; +import { Web3Wrapper } from '@0x/web3-wrapper'; +import * as chai from 'chai'; + +import { artifacts, ZRXTokenContract } from '../src'; + +chaiSetup.configure(); +const expect = chai.expect; +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); + +describe('ZRXToken', () => { + let owner: string; + let spender: string; + let MAX_UINT: BigNumber; + let zrxToken: ZRXTokenContract; + + before(async () => { + await blockchainLifecycle.startAsync(); + }); + after(async () => { + await blockchainLifecycle.revertAsync(); + }); + before(async () => { + const accounts = await web3Wrapper.getAvailableAddressesAsync(); + owner = accounts[0]; + spender = accounts[1]; + zrxToken = await ZRXTokenContract.deployFrom0xArtifactAsync(artifacts.ZRXToken, provider, txDefaults); + MAX_UINT = constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; + }); + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + describe('constants', () => { + it('should have 18 decimals', async () => { + const decimals = new BigNumber(await zrxToken.decimals.callAsync()); + const expectedDecimals = 18; + expect(decimals).to.be.bignumber.equal(expectedDecimals); + }); + + it('should have a total supply of 1 billion tokens', async () => { + const totalSupply = new BigNumber(await zrxToken.totalSupply.callAsync()); + const expectedTotalSupply = 1000000000; + expect(Web3Wrapper.toUnitAmount(totalSupply, 18)).to.be.bignumber.equal(expectedTotalSupply); + }); + + it('should be named 0x Protocol Token', async () => { + const name = await zrxToken.name.callAsync(); + const expectedName = '0x Protocol Token'; + expect(name).to.be.equal(expectedName); + }); + + it('should have the symbol ZRX', async () => { + const symbol = await zrxToken.symbol.callAsync(); + const expectedSymbol = 'ZRX'; + expect(symbol).to.be.equal(expectedSymbol); + }); + }); + + describe('constructor', () => { + it('should initialize owner balance to totalSupply', async () => { + const ownerBalance = await zrxToken.balanceOf.callAsync(owner); + const totalSupply = new BigNumber(await zrxToken.totalSupply.callAsync()); + expect(totalSupply).to.be.bignumber.equal(ownerBalance); + }); + }); + + describe('transfer', () => { + it('should transfer balance from sender to receiver', async () => { + const receiver = spender; + const initOwnerBalance = await zrxToken.balanceOf.callAsync(owner); + const amountToTransfer = new BigNumber(1); + await web3Wrapper.awaitTransactionSuccessAsync( + await zrxToken.transfer.sendTransactionAsync(receiver, amountToTransfer, { from: owner }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const finalOwnerBalance = await zrxToken.balanceOf.callAsync(owner); + const finalReceiverBalance = await zrxToken.balanceOf.callAsync(receiver); + + const expectedFinalOwnerBalance = initOwnerBalance.minus(amountToTransfer); + const expectedFinalReceiverBalance = amountToTransfer; + expect(finalOwnerBalance).to.be.bignumber.equal(expectedFinalOwnerBalance); + expect(finalReceiverBalance).to.be.bignumber.equal(expectedFinalReceiverBalance); + }); + + it('should return true on a 0 value transfer', async () => { + const didReturnTrue = await zrxToken.transfer.callAsync(spender, new BigNumber(0), { + from: owner, + }); + expect(didReturnTrue).to.be.true(); + }); + }); + + describe('transferFrom', () => { + it('should return false if owner has insufficient balance', async () => { + const ownerBalance = await zrxToken.balanceOf.callAsync(owner); + const amountToTransfer = ownerBalance.plus(1); + await web3Wrapper.awaitTransactionSuccessAsync( + await zrxToken.approve.sendTransactionAsync(spender, amountToTransfer, { + from: owner, + gas: constants.MAX_TOKEN_APPROVE_GAS, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const didReturnTrue = await zrxToken.transferFrom.callAsync(owner, spender, amountToTransfer, { + from: spender, + }); + expect(didReturnTrue).to.be.false(); + }); + + it('should return false if spender has insufficient allowance', async () => { + const ownerBalance = await zrxToken.balanceOf.callAsync(owner); + const amountToTransfer = ownerBalance; + + const spenderAllowance = await zrxToken.allowance.callAsync(owner, spender); + const isSpenderAllowanceInsufficient = spenderAllowance.cmp(amountToTransfer) < 0; + expect(isSpenderAllowanceInsufficient).to.be.true(); + + const didReturnTrue = await zrxToken.transferFrom.callAsync(owner, spender, amountToTransfer, { + from: spender, + }); + expect(didReturnTrue).to.be.false(); + }); + + it('should return true on a 0 value transfer', async () => { + const amountToTransfer = new BigNumber(0); + const didReturnTrue = await zrxToken.transferFrom.callAsync(owner, spender, amountToTransfer, { + from: spender, + }); + expect(didReturnTrue).to.be.true(); + }); + + it('should not modify spender allowance if spender allowance is 2^256 - 1', async () => { + const initOwnerBalance = await zrxToken.balanceOf.callAsync(owner); + const amountToTransfer = initOwnerBalance; + const initSpenderAllowance = MAX_UINT; + await web3Wrapper.awaitTransactionSuccessAsync( + await zrxToken.approve.sendTransactionAsync(spender, initSpenderAllowance, { + from: owner, + gas: constants.MAX_TOKEN_APPROVE_GAS, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await zrxToken.transferFrom.sendTransactionAsync(owner, spender, amountToTransfer, { + from: spender, + gas: constants.MAX_TOKEN_TRANSFERFROM_GAS, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + + const newSpenderAllowance = await zrxToken.allowance.callAsync(owner, spender); + expect(initSpenderAllowance).to.be.bignumber.equal(newSpenderAllowance); + }); + + it('should transfer the correct balances if spender has sufficient allowance', async () => { + const initOwnerBalance = await zrxToken.balanceOf.callAsync(owner); + const initSpenderBalance = await zrxToken.balanceOf.callAsync(spender); + const amountToTransfer = initOwnerBalance; + const initSpenderAllowance = initOwnerBalance; + await web3Wrapper.awaitTransactionSuccessAsync( + await zrxToken.approve.sendTransactionAsync(spender, initSpenderAllowance), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await zrxToken.transferFrom.sendTransactionAsync(owner, spender, amountToTransfer, { + from: spender, + gas: constants.MAX_TOKEN_TRANSFERFROM_GAS, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + + const newOwnerBalance = await zrxToken.balanceOf.callAsync(owner); + const newSpenderBalance = await zrxToken.balanceOf.callAsync(spender); + + expect(newOwnerBalance).to.be.bignumber.equal(0); + expect(newSpenderBalance).to.be.bignumber.equal(initSpenderBalance.plus(initOwnerBalance)); + }); + + it('should modify allowance if spender has sufficient allowance less than 2^256 - 1', async () => { + const initOwnerBalance = await zrxToken.balanceOf.callAsync(owner); + const amountToTransfer = initOwnerBalance; + await web3Wrapper.awaitTransactionSuccessAsync( + await zrxToken.approve.sendTransactionAsync(spender, amountToTransfer), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await zrxToken.transferFrom.sendTransactionAsync(owner, spender, amountToTransfer, { + from: spender, + gas: constants.MAX_TOKEN_TRANSFERFROM_GAS, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + + const newSpenderAllowance = await zrxToken.allowance.callAsync(owner, spender); + expect(newSpenderAllowance).to.be.bignumber.equal(0); + }); + }); +}); diff --git a/contracts/tokens/tsconfig.json b/contracts/tokens/tsconfig.json new file mode 100644 index 000000000..0d5f98cb8 --- /dev/null +++ b/contracts/tokens/tsconfig.json @@ -0,0 +1,33 @@ +{ + "extends": "../../tsconfig", + "compilerOptions": { + "outDir": "lib", + "rootDir": ".", + "resolveJsonModule": true + }, + "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"], + "files": [ + "./generated-artifacts/DummyERC20Token.json", + "./generated-artifacts/DummyMultipleReturnERC20Token.json", + "./generated-artifacts/DummyNoReturnERC20Token.json", + "./generated-artifacts/DummyERC721Receiver.json", + "./generated-artifacts/InvalidERC721Receiver.json", + "./generated-artifacts/DummyERC721Token.json", + "./generated-artifacts/ReentrantERC20Token.json", + "./generated-artifacts/ERC20Token.json", + "./generated-artifacts/IERC20Token.json", + "./generated-artifacts/MintableERC20Token.json", + "./generated-artifacts/UnlimitedAllowanceERC20Token.json", + "./generated-artifacts/ERC721Token.json", + "./generated-artifacts/IERC721Receiver.json", + "./generated-artifacts/IERC721Token.json", + "./generated-artifacts/MintableERC721Token.json", + "./generated-artifacts/IEtherToken.json", + "./generated-artifacts/WETH9.json", + "./generated-artifacts/ERC20Token_v1.json", + "./generated-artifacts/Token_v1.json", + "./generated-artifacts/UnlimitedAllowanceToken_v1.json", + "./generated-artifacts/ZRXToken.json" + ], + "exclude": ["./deploy/solc/solc_bin"] +} diff --git a/contracts/tokens/tslint.json b/contracts/tokens/tslint.json new file mode 100644 index 000000000..1bb3ac2a2 --- /dev/null +++ b/contracts/tokens/tslint.json @@ -0,0 +1,6 @@ +{ + "extends": ["@0x/tslint-config"], + "rules": { + "custom-no-magic-numbers": false + } +} -- cgit v1.2.3 From 37dbb4d06f959978a343d870bcf1714b0142c274 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Wed, 5 Dec 2018 09:45:23 -0800 Subject: Use dotenv in development --- packages/instant/package.json | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/instant/package.json b/packages/instant/package.json index 7d0bf6bec..9303276b4 100644 --- a/packages/instant/package.json +++ b/packages/instant/package.json @@ -10,7 +10,7 @@ "scripts": { "build": "webpack --mode production", "build:ci": "yarn build", - "dev": "webpack-dev-server --mode development", + "dev": "dotenv webpack-dev-server -- --mode development", "lint": "tslint --format stylish --project .", "test": "jest", "test:coverage": "jest --coverage", @@ -24,10 +24,7 @@ }, "config": { "postpublish": { - "assets": [ - "packages/instant/umd/instant.js", - "packages/instant/umd/instant.js.map" - ] + "assets": ["packages/instant/umd/instant.js", "packages/instant/umd/instant.js.map"] } }, "repository": { -- cgit v1.2.3 From 9eff85741617d9d64c674e41a344e42325153292 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Wed, 5 Dec 2018 09:45:34 -0800 Subject: Only include localhost in host domains if in development mode --- packages/instant/src/constants.ts | 7 +++---- packages/instant/src/util/analytics.ts | 4 ++-- packages/instant/src/util/error_reporter.ts | 24 +++++++++++++++++++++--- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/packages/instant/src/constants.ts b/packages/instant/src/constants.ts index 506348092..f83eb4ac7 100644 --- a/packages/instant/src/constants.ts +++ b/packages/instant/src/constants.ts @@ -15,6 +15,7 @@ export const GWEI_IN_WEI = new BigNumber(1000000000); export const ONE_SECOND_MS = 1000; export const ONE_MINUTE_MS = ONE_SECOND_MS * 60; export const GIT_SHA = process.env.GIT_SHA; +export const NODE_ENV = process.env.NODE_ENV; export const NPM_PACKAGE_VERSION = process.env.NPM_PACKAGE_VERSION; export const ACCOUNT_UPDATE_INTERVAL_TIME_MS = ONE_SECOND_MS * 5; export const BUY_QUOTE_UPDATE_INTERVAL_TIME_MS = ONE_SECOND_MS * 15; @@ -28,14 +29,12 @@ export const HEAP_ENABLED = process.env.HEAP_ENABLED; export const COINBASE_API_BASE_URL = 'https://api.coinbase.com/v2'; export const PROGRESS_STALL_AT_WIDTH = '95%'; export const PROGRESS_FINISH_ANIMATION_TIME_MS = 200; -export const HOST_DOMAINS = [ +export const HOST_DOMAINS_EXTERNAL = [ '0x-instant-staging.s3-website-us-east-1.amazonaws.com', '0x-instant-dogfood.s3-website-us-east-1.amazonaws.com', - 'localhost', - '127.0.0.1', - '0.0.0.0', 'instant.0xproject.com', ]; +export const HOST_DOMAINS_LOCAL = ['localhost', '127.0.0.1', '0.0.0.0']; export const ROLLBAR_CLIENT_TOKEN = process.env.ROLLBAR_CLIENT_TOKEN; export const ROLLBAR_ENABLED = process.env.ROLLBAR_ENABLED; export const INSTANT_DISCHARGE_TARGET = process.env.INSTANT_DISCHARGE_TARGET as diff --git a/packages/instant/src/util/analytics.ts b/packages/instant/src/util/analytics.ts index 6da37bedb..6da52db16 100644 --- a/packages/instant/src/util/analytics.ts +++ b/packages/instant/src/util/analytics.ts @@ -2,7 +2,7 @@ import { BuyQuote } from '@0x/asset-buyer'; import { BigNumber } from '@0x/utils'; import * as _ from 'lodash'; -import { GIT_SHA, HEAP_ENABLED, INSTANT_DISCHARGE_TARGET, NPM_PACKAGE_VERSION } from '../constants'; +import { GIT_SHA, HEAP_ENABLED, INSTANT_DISCHARGE_TARGET, NODE_ENV, NPM_PACKAGE_VERSION } from '../constants'; import { AffiliateInfo, Asset, @@ -156,7 +156,7 @@ export const analytics = { affiliateFeePercent, selectedAssetName: selectedAsset ? selectedAsset.metaData.name : 'none', selectedAssetData: selectedAsset ? selectedAsset.assetData : 'none', - instantEnvironment: INSTANT_DISCHARGE_TARGET || `Local ${process.env.NODE_ENV}`, + instantEnvironment: INSTANT_DISCHARGE_TARGET || `Local ${NODE_ENV}`, }; return eventOptions; }, diff --git a/packages/instant/src/util/error_reporter.ts b/packages/instant/src/util/error_reporter.ts index b1824eaf9..ec074c440 100644 --- a/packages/instant/src/util/error_reporter.ts +++ b/packages/instant/src/util/error_reporter.ts @@ -1,17 +1,35 @@ import { logUtils } from '@0x/utils'; import * as _ from 'lodash'; -import { GIT_SHA, HOST_DOMAINS, INSTANT_DISCHARGE_TARGET, ROLLBAR_CLIENT_TOKEN, ROLLBAR_ENABLED } from '../constants'; +import { + GIT_SHA, + HOST_DOMAINS_EXTERNAL, + HOST_DOMAINS_LOCAL, + INSTANT_DISCHARGE_TARGET, + NODE_ENV, + ROLLBAR_CLIENT_TOKEN, + ROLLBAR_ENABLED, +} from '../constants'; // Import version of Rollbar designed for embedded components // See https://docs.rollbar.com/docs/using-rollbarjs-inside-an-embedded-component // tslint:disable-next-line:no-var-requires const Rollbar = require('rollbar/dist/rollbar.noconflict.umd'); +const getRollbarHostDomains = (): string[] => { + if (NODE_ENV === 'development') { + return HOST_DOMAINS_EXTERNAL.concat(HOST_DOMAINS_LOCAL); + } else { + return HOST_DOMAINS_EXTERNAL; + } +}; + let rollbar: any; // Configures rollbar and sets up error catching export const setupRollbar = (): any => { if (_.isUndefined(rollbar) && ROLLBAR_CLIENT_TOKEN && ROLLBAR_ENABLED) { + const hostDomains = getRollbarHostDomains(); + console.log('hostDomains', hostDomains); rollbar = new Rollbar({ accessToken: ROLLBAR_CLIENT_TOKEN, captureUncaught: true, @@ -20,7 +38,7 @@ export const setupRollbar = (): any => { itemsPerMinute: 10, maxItems: 500, payload: { - environment: INSTANT_DISCHARGE_TARGET || `Local ${process.env.NODE_ENV}`, + environment: INSTANT_DISCHARGE_TARGET || `Local ${NODE_ENV}`, client: { javascript: { source_map_enabled: true, @@ -29,7 +47,7 @@ export const setupRollbar = (): any => { }, }, }, - hostWhiteList: HOST_DOMAINS, + hostWhiteList: hostDomains, uncaughtErrorLevel: 'error', ignoredMessages: [ // Errors from the third-party scripts -- cgit v1.2.3 From 69d4e330cf312af99cf547add9cd2106e492c563 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Wed, 5 Dec 2018 10:17:18 -0800 Subject: Takeout console.log --- packages/instant/src/util/error_reporter.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/instant/src/util/error_reporter.ts b/packages/instant/src/util/error_reporter.ts index ec074c440..8d7481684 100644 --- a/packages/instant/src/util/error_reporter.ts +++ b/packages/instant/src/util/error_reporter.ts @@ -29,7 +29,6 @@ let rollbar: any; export const setupRollbar = (): any => { if (_.isUndefined(rollbar) && ROLLBAR_CLIENT_TOKEN && ROLLBAR_ENABLED) { const hostDomains = getRollbarHostDomains(); - console.log('hostDomains', hostDomains); rollbar = new Rollbar({ accessToken: ROLLBAR_CLIENT_TOKEN, captureUncaught: true, -- cgit v1.2.3 From 3454bbfd65f69f219e210fe87bfad5be6993adde Mon Sep 17 00:00:00 2001 From: fragosti Date: Wed, 5 Dec 2018 11:39:10 -0800 Subject: fix: input placeholder css specificity increase --- packages/instant/src/components/ui/input.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/instant/src/components/ui/input.tsx b/packages/instant/src/components/ui/input.tsx index 863c970ef..62c70f9e1 100644 --- a/packages/instant/src/components/ui/input.tsx +++ b/packages/instant/src/components/ui/input.tsx @@ -29,8 +29,8 @@ export const Input = outline: none; border: none; &::placeholder { - color: ${props => props.theme[props.fontColor || 'white']}; - opacity: 0.5; + color: ${props => props.theme[props.fontColor || 'white']} !important; + opacity: 0.5 !important; } } `; -- cgit v1.2.3 From 92d4c1d09e05d82bec010f0241c6772ff03da503 Mon Sep 17 00:00:00 2001 From: fragosti Date: Wed, 5 Dec 2018 11:52:27 -0800 Subject: fix: call toLowerCase in asset creation logic --- packages/instant/src/util/asset.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/instant/src/util/asset.ts b/packages/instant/src/util/asset.ts index 08f3642e3..13f84ef74 100644 --- a/packages/instant/src/util/asset.ts +++ b/packages/instant/src/util/asset.ts @@ -26,7 +26,7 @@ export const assetUtils = { return; } return { - assetData, + assetData: assetData.toLowerCase(), metaData, }; }, @@ -36,7 +36,7 @@ export const assetUtils = { network: Network, ): Asset => { return { - assetData, + assetData: assetData.toLowerCase(), metaData: assetUtils.getMetaDataOrThrow(assetData, assetMetaDataMap, network), }; }, -- cgit v1.2.3 From 55ebf405a0cc7dd24607ff1d1db5b0edd3a61e6e Mon Sep 17 00:00:00 2001 From: fragosti Date: Wed, 5 Dec 2018 11:56:54 -0800 Subject: chore: add coingecko css example to externall.css --- packages/instant/public/external.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/instant/public/external.css b/packages/instant/public/external.css index cab11112a..21278577e 100644 --- a/packages/instant/public/external.css +++ b/packages/instant/public/external.css @@ -15,6 +15,10 @@ input { height: 100px; } +input::-webkit-input-placeholder { + color: #b4b4b4 !important; +} + div { padding: 3px; } -- cgit v1.2.3 From f2c5a8e2f1e517e89f45b03bd3cdf5bb59c450bf Mon Sep 17 00:00:00 2001 From: fragosti Date: Wed, 5 Dec 2018 12:16:55 -0800 Subject: feat: lowercase keys in additionalAssetMetaDataMap --- packages/instant/src/components/zero_ex_instant_provider.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx index dae9124c6..f06d551c7 100644 --- a/packages/instant/src/components/zero_ex_instant_provider.tsx +++ b/packages/instant/src/components/zero_ex_instant_provider.tsx @@ -60,7 +60,8 @@ export class ZeroExInstantProvider extends React.Component key.toLowerCase()), ...defaultState.assetMetaDataMap, }; // construct the final state -- cgit v1.2.3 From 08eb0b91b6d0f0dc90ae920a18ca5dd080bf235c Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Wed, 5 Dec 2018 12:27:32 -0800 Subject: Fix RadarRelay timestamps (#1391) * Fixing rr timestamps * Apply prettier --- packages/pipeline/src/entities/sra_order_observed_timestamp.ts | 7 +++++-- packages/pipeline/src/scripts/pull_radar_relay_orders.ts | 5 +++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/pipeline/src/entities/sra_order_observed_timestamp.ts b/packages/pipeline/src/entities/sra_order_observed_timestamp.ts index cd2d41397..cbec1c6d0 100644 --- a/packages/pipeline/src/entities/sra_order_observed_timestamp.ts +++ b/packages/pipeline/src/entities/sra_order_observed_timestamp.ts @@ -22,11 +22,14 @@ export class SraOrdersObservedTimeStamp { * current time. * @param order The order to generate a timestamp for. */ -export function createObservedTimestampForOrder(order: SraOrder): SraOrdersObservedTimeStamp { +export function createObservedTimestampForOrder( + order: SraOrder, + observedTimestamp: number, +): SraOrdersObservedTimeStamp { const observed = new SraOrdersObservedTimeStamp(); observed.exchangeAddress = order.exchangeAddress; observed.orderHashHex = order.orderHashHex; observed.sourceUrl = order.sourceUrl; - observed.observedTimestamp = Date.now(); + observed.observedTimestamp = observedTimestamp; return observed; } diff --git a/packages/pipeline/src/scripts/pull_radar_relay_orders.ts b/packages/pipeline/src/scripts/pull_radar_relay_orders.ts index 6c18bcaef..40bb6fc97 100644 --- a/packages/pipeline/src/scripts/pull_radar_relay_orders.ts +++ b/packages/pipeline/src/scripts/pull_radar_relay_orders.ts @@ -33,11 +33,12 @@ async function getOrderbookAsync(): Promise { // Save all the orders and update the observed time stamps in a single // transaction. console.log('Saving orders and updating timestamps...'); + const observedTimestamp = Date.now(); await connection.transaction(async (manager: EntityManager): Promise => { for (const order of orders) { await manager.save(SraOrder, order); - const observedTimestamp = createObservedTimestampForOrder(order); - await manager.save(observedTimestamp); + const orderObservation = createObservedTimestampForOrder(order, observedTimestamp); + await manager.save(orderObservation); } }); } -- cgit v1.2.3 From 1f8c09779d57ec0243e3c8d15e9e461c6d491018 Mon Sep 17 00:00:00 2001 From: fragosti Date: Wed, 5 Dec 2018 12:34:24 -0800 Subject: chore: make default fee percentage in configurator 0 --- packages/website/ts/pages/instant/configurator.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/ts/pages/instant/configurator.tsx b/packages/website/ts/pages/instant/configurator.tsx index d19a5b4fe..a6c792edf 100644 --- a/packages/website/ts/pages/instant/configurator.tsx +++ b/packages/website/ts/pages/instant/configurator.tsx @@ -26,7 +26,7 @@ export class Configurator extends React.Component { availableAssetDatas: undefined, affiliateInfo: { feeRecipient: '', - feePercentage: 0.01, + feePercentage: 0, }, }, }; -- cgit v1.2.3 From fb221e4ca9eec6a66130e3f8230457a1fcee20f8 Mon Sep 17 00:00:00 2001 From: fragosti Date: Wed, 5 Dec 2018 12:41:57 -0800 Subject: feat: notify user they've copied to clipboard on copy --- packages/website/ts/pages/instant/code_demo.tsx | 43 +++++++++++++++++-------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/packages/website/ts/pages/instant/code_demo.tsx b/packages/website/ts/pages/instant/code_demo.tsx index c5e565d61..b7a9f5b17 100644 --- a/packages/website/ts/pages/instant/code_demo.tsx +++ b/packages/website/ts/pages/instant/code_demo.tsx @@ -145,17 +145,32 @@ export interface CodeDemoProps { children: string; } -export const CodeDemo: React.StatelessComponent = props => ( - - - - - - - - {props.children} - - -); +export interface CodeDemoState { + didCopyCode: boolean; +} + +export class CodeDemo extends React.Component { + public state: CodeDemoState = { + didCopyCode: false, + }; + public render(): React.ReactNode { + const copyButtonText = this.state.didCopyCode ? 'Copied!' : 'Copy'; + return ( + + + + + + + + {this.props.children} + + + ); + } + private readonly _handleCopyClick = () => { + this.setState({ didCopyCode: true }); + }; +} -- cgit v1.2.3 From b7603bef192dac4a3e87a365f77dc87c61dbd4f8 Mon Sep 17 00:00:00 2001 From: fragosti Date: Wed, 5 Dec 2018 12:44:23 -0800 Subject: fix: ensure copy button is above code --- packages/website/ts/pages/instant/code_demo.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/website/ts/pages/instant/code_demo.tsx b/packages/website/ts/pages/instant/code_demo.tsx index b7a9f5b17..1bc1980e7 100644 --- a/packages/website/ts/pages/instant/code_demo.tsx +++ b/packages/website/ts/pages/instant/code_demo.tsx @@ -6,6 +6,7 @@ import { Button } from 'ts/components/ui/button'; import { Container } from 'ts/components/ui/container'; import { colors } from 'ts/style/colors'; import { styled } from 'ts/style/theme'; +import { zIndex } from 'ts/style/z_index'; const CustomPre = styled.pre` margin: 0px; @@ -157,7 +158,7 @@ export class CodeDemo extends React.Component { const copyButtonText = this.state.didCopyCode ? 'Copied!' : 'Copy'; return ( - +