diff --git a/slate-docs/.dockerignore b/slate-docs/.dockerignore new file mode 100644 index 00000000..1cf06112 --- /dev/null +++ b/slate-docs/.dockerignore @@ -0,0 +1,2 @@ +build/ +.github/ diff --git a/slate-docs/.editorconfig b/slate-docs/.editorconfig new file mode 100644 index 00000000..1692977c --- /dev/null +++ b/slate-docs/.editorconfig @@ -0,0 +1,18 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# Top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +[*.rb] +charset = utf-8 + +[*.md] +trim_trailing_whitespace = false diff --git a/slate-docs/.gitattributes b/slate-docs/.gitattributes new file mode 100644 index 00000000..3069c432 --- /dev/null +++ b/slate-docs/.gitattributes @@ -0,0 +1 @@ +source/javascripts/lib/* linguist-vendored diff --git a/slate-docs/.gitignore b/slate-docs/.gitignore new file mode 100644 index 00000000..1b64d4fe --- /dev/null +++ b/slate-docs/.gitignore @@ -0,0 +1,29 @@ +*.gem +*.rbc +.bundle +.config +coverage +InstalledFiles +lib/bundler/man +pkg +rdoc +spec/reports +test/tmp +test/version_tmp +tmp +*.DS_STORE +build/ +.cache +.vagrant +.sass-cache +vendor/ +bin/ + +# YARD artifacts +.yardoc +_yardoc +doc/ +.idea/ + +# Vagrant artifacts +ubuntu-*-console.log diff --git a/slate-docs/CHANGELOG.md b/slate-docs/CHANGELOG.md new file mode 100644 index 00000000..ad63e5f8 --- /dev/null +++ b/slate-docs/CHANGELOG.md @@ -0,0 +1,233 @@ +# Changelog + +## Version 2.7.0 + +*June 21, 2020* + +* __[security]__ Bumped rack in Gemfile.lock from 2.2.2 to 2.2.3 +* Bumped bundled jQuery from 3.2.1 to 3.5.1 +* Bumped bundled lunr from 0.5.7 to 2.3.8 +* Bumped imagesloaded from 3.1.8 to 4.1.4 +* Bumped rouge from 3.17.0 to 3.20.0 +* Bumped redcarpet from 3.4.0 to 3.5.0 +* Fix color of highlighted code being unreadable when printing page +* Add clipboard icon for "Copy to Clipboard" functionality to code boxes (see note below) +* Fix handling of ToC selectors that contain punctutation (thanks @gruis) +* Fix language bar truncating languages that overflow screen width +* Strip HTML tags from ToC title before displaying it in title bar in JS (backup to stripping done in Ruby code) (thanks @atic) + +To enable the new clipboard icon, you need to add `code_clipboard: true` to the frontmatter of source/index.html.md. +See [this line](https://github.com/slatedocs/slate/blame/main/source/index.html.md#L19) for an example of usage. + +## Version 2.6.1 + +*May 30, 2020* + +* __[security]__ update child dependency activesupport in Gemfile.lock to 5.4.2.3 +* Update Middleman in Gemfile.lock to 4.3.7 +* Replace Travis-CI with GitHub actions for continuous integration +* Replace Spectrum with GitHub discussions + +## Version 2.6.0 + +*May 18, 2020* + +__Note__: 2.5.0 was "pulled" due to a breaking bug discovered after release. It is recommended to skip it, and move straight to 2.6.0. + +* Fix large whitespace gap in middle column for sections with codeblocks +* Fix highlighted code elements having a different background than rest of code block +* Change JSON keys to have a different font color than their values +* Disable asset hashing for woff and woff2 elements due to middleman bug breaking woff2 asset hashing in general +* Move Dockerfile to Debian from Alpine +* Converted repo to a [GitHub template](https://help.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-template-repository) +* Update sassc to 2.3.0 in Gemfile.lock + +## Version 2.5.0 + +*May 8, 2020* + +* __[security]__ update nokogiri to ~> 1.10.8 +* Update links in example docs to https://github.com/slatedocs/slate from https://github.com/lord/slate +* Update LICENSE to include full Apache 2.0 text +* Test slate against Ruby 2.5 and 2.6 on Travis-CI +* Update Vagrantfile to use Ubuntu 18.04 (thanks @bradthurber) +* Parse arguments and flags for deploy.sh on script start, instead of potentially after building source files +* Install nodejs inside Vagrantfile (thanks @fernandoaguilar) +* Add Dockerfile for running slate (thanks @redhatxl) +* update middleman-syntax and rouge to ~>3.2 +* update middleman to 4.3.6 + +## Version 2.4.0 + +*October 19, 2019* + +- Move repository from lord/slate to slatedocs/slate +- Fix documentation to point at new repo link, thanks to [Arun](https://github.com/slash-arun), [Gustavo Gawryszewski](https://github.com/gawry), and [Daniel Korbit](https://github.com/danielkorbit) +- Update `nokogiri` to 1.10.4 +- Update `ffi` in `Gemfile.lock` to fix security warnings, thanks to [Grey Baker](https://github.com/greysteil) and [jakemack](https://github.com/jakemack) +- Update `rack` to 2.0.7 in `Gemfile.lock` to fix security warnings, thanks to [Grey Baker](https://github.com/greysteil) and [jakemack](https://github.com/jakemack) +- Update middleman to `4.3` and relax constraints on middleman related gems, thanks to [jakemack](https://github.com/jakemack) +- Add sass gem, thanks to [jakemack](https://github.com/jakemack) +- Activate `asset_cache` in middleman to improve cacheability of static files, thanks to [Sam Gilman](https://github.com/thenengah) +- Update to using bundler 2 for `Gemfile.lock`, thanks to [jakemack](https://github.com/jakemack) + +## Version 2.3.1 + +*July 5, 2018* + +- Update `sprockets` in `Gemfile.lock` to fix security warnings + +## Version 2.3 + +*July 5, 2018* + +- Allows strikethrough in markdown by default. +- Upgrades jQuery to 3.2.1, thanks to [Tomi Takussaari](https://github.com/TomiTakussaari) +- Fixes invalid HTML in `layout.erb`, thanks to [Eric Scouten](https://github.com/scouten) for pointing out +- Hopefully fixes Vagrant memory issues, thanks to [Petter Blomberg](https://github.com/p-blomberg) for the suggestion +- Cleans HTML in headers before setting `document.title`, thanks to [Dan Levy](https://github.com/justsml) +- Allows trailing whitespace in markdown files, thanks to [Samuel Cousin](https://github.com/kuzyn) +- Fixes pushState/replaceState problems with scrolling not changing the document hash, thanks to [Andrey Fedorov](https://github.com/anfedorov) +- Removes some outdated examples, thanks [@al-tr](https://github.com/al-tr), [Jerome Dahdah](https://github.com/jdahdah), and [Ricardo Castro](https://github.com/mccricardo) +- Fixes `nav-padding` bug, thanks [Jerome Dahdah](https://github.com/jdahdah) +- Code style fixes thanks to [Sebastian Zaremba](https://github.com/vassyz) +- Nokogiri version bump thanks to [Grey Baker](https://github.com/greysteil) +- Fix to default `index.md` text thanks to [Nick Busey](https://github.com/NickBusey) + +Thanks to everyone who contributed to this release! + +## Version 2.2 + +*January 19, 2018* + +- Fixes bugs with some non-roman languages not generating unique headers +- Adds editorconfig, thanks to [Jay Thomas](https://github.com/jaythomas) +- Adds optional `NestingUniqueHeadCounter`, thanks to [Vladimir Morozov](https://github.com/greenhost87) +- Small fixes to typos and language, thx [Emir Ribić](https://github.com/ribice), [Gregor Martynus](https://github.com/gr2m), and [Martius](https://github.com/martiuslim)! +- Adds links to Spectrum chat for questions in README and ISSUE_TEMPLATE + +## Version 2.1 + +*October 30, 2017* + +- Right-to-left text stylesheet option, thanks to [Mohammad Hossein Rabiee](https://github.com/mhrabiee) +- Fix for HTML5 history state bug, thanks to [Zach Toolson](https://github.com/ztoolson) +- Small styling changes, typo fixes, small bug fixes from [Marian Friedmann](https://github.com/rnarian), [Ben Wilhelm](https://github.com/benwilhelm), [Fouad Matin](https://github.com/fouad), [Nicolas Bonduel](https://github.com/NicolasBonduel), [Christian Oliff](https://github.com/coliff) + +Thanks to everyone who submitted PRs for this version! + +## Version 2.0 + +*July 17, 2017* + +- All-new statically generated table of contents + - Should be much faster loading and scrolling for large pages + - Smaller Javascript file sizes + - Avoids the problem with the last link in the ToC not ever highlighting if the section was shorter than the page + - Fixes control-click not opening in a new page + - Automatically updates the HTML title as you scroll +- Updated design + - New default colors! + - New spacings and sizes! + - System-default typefaces, just like GitHub +- Added search input delay on large corpuses to reduce lag +- We even bumped the major version cause hey, why not? +- Various small bug fixes + +Thanks to everyone who helped debug or wrote code for this version! It was a serious community effort, and I couldn't have done it alone. + +## Version 1.5 + +*February 23, 2017* + +- Add [multiple tabs per programming language](https://github.com/lord/slate/wiki/Multiple-language-tabs-per-programming-language) feature +- Upgrade Middleman to add Ruby 1.4.0 compatibility +- Switch default code highlighting color scheme to better highlight JSON +- Various small typo and bug fixes + +## Version 1.4 + +*November 24, 2016* + +- Upgrade Middleman and Rouge gems, should hopefully solve a number of bugs +- Update some links in README +- Fix broken Vagrant startup script +- Fix some problems with deploy.sh help message +- Fix bug with language tabs not hiding properly if no error +- Add `!default` to SASS variables +- Fix bug with logo margin +- Bump tested Ruby versions in .travis.yml + +## Version 1.3.3 + +*June 11, 2016* + +Documentation and example changes. + +## Version 1.3.2 + +*February 3, 2016* + +A small bugfix for slightly incorrect background colors on code samples in some cases. + +## Version 1.3.1 + +*January 31, 2016* + +A small bugfix for incorrect whitespace in code blocks. + +## Version 1.3 + +*January 27, 2016* + +We've upgraded Middleman and a number of other dependencies, which should fix quite a few bugs. + +Instead of `rake build` and `rake deploy`, you should now run `bundle exec middleman build --clean` to build your server, and `./deploy.sh` to deploy it to Github Pages. + +## Version 1.2 + +*June 20, 2015* + +**Fixes:** + +- Remove crash on invalid languages +- Update Tocify to scroll to the highlighted header in the Table of Contents +- Fix variable leak and update search algorithms +- Update Python examples to be valid Python +- Update gems +- More misc. bugfixes of Javascript errors +- Add Dockerfile +- Remove unused gems +- Optimize images, fonts, and generated asset files +- Add chinese font support +- Remove RedCarpet header ID patch +- Update language tabs to not disturb existing query strings + +## Version 1.1 + +*July 27, 2014* + +**Fixes:** + +- Finally, a fix for the redcarpet upgrade bug + +## Version 1.0 + +*July 2, 2014* + +[View Issues](https://github.com/tripit/slate/issues?milestone=1&state=closed) + +**Features:** + +- Responsive designs for phones and tablets +- Started tagging versions + +**Fixes:** + +- Fixed 'unrecognized expression' error +- Fixed #undefined hash bug +- Fixed bug where the current language tab would be unselected +- Fixed bug where tocify wouldn't highlight the current section while searching +- Fixed bug where ids of header tags would have special characters that caused problems +- Updated layout so that pages with disabled search wouldn't load search.js +- Cleaned up Javascript diff --git a/slate-docs/CNAME b/slate-docs/CNAME new file mode 100644 index 00000000..bfd6e1f9 --- /dev/null +++ b/slate-docs/CNAME @@ -0,0 +1 @@ +docs.dydx.exchange \ No newline at end of file diff --git a/slate-docs/CODE_OF_CONDUCT.md b/slate-docs/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..cc17fd98 --- /dev/null +++ b/slate-docs/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at hello@lord.io. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/slate-docs/Dockerfile b/slate-docs/Dockerfile new file mode 100644 index 00000000..a07e252e --- /dev/null +++ b/slate-docs/Dockerfile @@ -0,0 +1,19 @@ +FROM ruby:2.6-slim + +WORKDIR /usr/src/slate + +EXPOSE 4567 + +COPY . . + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + build-essential \ + nodejs \ + && gem install bundler \ + && bundle install \ + && apt-get remove -y build-essential \ + && apt-get autoremove -y \ + && rm -rf /var/lib/apt/lists/* + +CMD ["bundle", "exec", "middleman", "server", "--watcher-force-polling"] diff --git a/slate-docs/Gemfile b/slate-docs/Gemfile new file mode 100644 index 00000000..315a86d0 --- /dev/null +++ b/slate-docs/Gemfile @@ -0,0 +1,12 @@ +ruby '>=2.3.1' +source 'https://rubygems.org' + +# Middleman +gem 'middleman', '~>4.3' +gem 'middleman-syntax', '~> 3.2' +gem 'middleman-autoprefixer', '~> 2.7' +gem 'middleman-sprockets', '~> 4.1' +gem 'rouge', '~> 3.20' +gem 'redcarpet', '~> 3.5.0' +gem 'nokogiri', '~> 1.10.8' +gem 'sass' diff --git a/slate-docs/Gemfile.lock b/slate-docs/Gemfile.lock new file mode 100644 index 00000000..b3db4b0e --- /dev/null +++ b/slate-docs/Gemfile.lock @@ -0,0 +1,134 @@ +GEM + remote: https://rubygems.org/ + specs: + activesupport (5.2.4.3) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 0.7, < 2) + minitest (~> 5.1) + tzinfo (~> 1.1) + addressable (2.7.0) + public_suffix (>= 2.0.2, < 5.0) + autoprefixer-rails (9.5.1.1) + execjs + backports (3.17.2) + coffee-script (2.4.1) + coffee-script-source + execjs + coffee-script-source (1.12.2) + concurrent-ruby (1.1.6) + contracts (0.13.0) + dotenv (2.7.5) + erubis (2.7.0) + execjs (2.7.0) + fast_blank (1.0.0) + fastimage (2.1.7) + ffi (1.12.2) + haml (5.1.2) + temple (>= 0.8.0) + tilt + hamster (3.0.0) + concurrent-ruby (~> 1.0) + hashie (3.6.0) + i18n (0.9.5) + concurrent-ruby (~> 1.0) + kramdown (1.17.0) + listen (3.0.8) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + memoist (0.16.2) + middleman (4.3.7) + coffee-script (~> 2.2) + haml (>= 4.0.5) + kramdown (~> 1.2) + middleman-cli (= 4.3.7) + middleman-core (= 4.3.7) + middleman-autoprefixer (2.10.1) + autoprefixer-rails (~> 9.1) + middleman-core (>= 3.3.3) + middleman-cli (4.3.7) + thor (>= 0.17.0, < 2.0) + middleman-core (4.3.7) + activesupport (>= 4.2, < 6.0) + addressable (~> 2.3) + backports (~> 3.6) + bundler + contracts (~> 0.13.0) + dotenv + erubis + execjs (~> 2.0) + fast_blank + fastimage (~> 2.0) + hamster (~> 3.0) + hashie (~> 3.4) + i18n (~> 0.9.0) + listen (~> 3.0.0) + memoist (~> 0.14) + padrino-helpers (~> 0.13.0) + parallel + rack (>= 1.4.5, < 3) + sassc (~> 2.0) + servolux + tilt (~> 2.0.9) + uglifier (~> 3.0) + middleman-sprockets (4.1.1) + middleman-core (~> 4.0) + sprockets (>= 3.0) + middleman-syntax (3.2.0) + middleman-core (>= 3.2) + rouge (~> 3.2) + mini_portile2 (2.4.0) + minitest (5.14.1) + nokogiri (1.10.9) + mini_portile2 (~> 2.4.0) + padrino-helpers (0.13.3.4) + i18n (~> 0.6, >= 0.6.7) + padrino-support (= 0.13.3.4) + tilt (>= 1.4.1, < 3) + padrino-support (0.13.3.4) + activesupport (>= 3.1) + parallel (1.19.1) + public_suffix (4.0.5) + rack (2.2.3) + rb-fsevent (0.10.4) + rb-inotify (0.10.1) + ffi (~> 1.0) + redcarpet (3.5.0) + rouge (3.20.0) + sass (3.7.4) + sass-listen (~> 4.0.0) + sass-listen (4.0.0) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + sassc (2.3.0) + ffi (~> 1.9) + servolux (0.13.0) + sprockets (3.7.2) + concurrent-ruby (~> 1.0) + rack (> 1, < 3) + temple (0.8.2) + thor (1.0.1) + thread_safe (0.3.6) + tilt (2.0.10) + tzinfo (1.2.7) + thread_safe (~> 0.1) + uglifier (3.2.0) + execjs (>= 0.3.0, < 3) + +PLATFORMS + ruby + +DEPENDENCIES + middleman (~> 4.3) + middleman-autoprefixer (~> 2.7) + middleman-sprockets (~> 4.1) + middleman-syntax (~> 3.2) + nokogiri (~> 1.10.8) + redcarpet (~> 3.5.0) + rouge (~> 3.20) + sass + +RUBY VERSION + ruby 2.3.3p222 + +BUNDLED WITH + 2.1.4 diff --git a/slate-docs/LICENSE b/slate-docs/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/slate-docs/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/slate-docs/README.md b/slate-docs/README.md new file mode 100644 index 00000000..d692425b --- /dev/null +++ b/slate-docs/README.md @@ -0,0 +1,45 @@ +
+pip install dydx-v3-python ++ +### Usage + +See [dydxprotocol/dydx-v3-python](https://github.com/dydxprotocol/dydx-v3-python). + +See the [examples](https://github.com/dydxprotocol/dydx-v3-python/tree/master/examples) folder for simple python examples. + +## TypeScript Client + +### Installation + +Install `@dydxprotocol/v3-client` from [NPM](https://www.npmjs.com/package/@dydxprotocol/v3-client): + +
+npm i -s @dydxprotocol/v3-client ++ +### Usage + +See [dydxprotocol/v3-client](https://github.com/dydxprotocol/v3-client). + +See the [examples](https://github.com/dydxprotocol/v3-client/tree/master/examples) folder for simple typescript examples. + +## Client Initialization + +> Initialize + +```python +client = Client( + host='https://api.dydx.exchange', + web3=Web3('...'), + stark_private_key='01234abcd...', +) +``` + +```typescript +const client: DydxClient = new Client( + 'host', + { + apiTimeout: 3000, + starkPrivateKey: '01234abcd...', + }, +); +``` + +The client is organized into modules, based on the type of authentication needed for different requests. The configuration options passed into the client determine which modules are available. See [Authentication](#authentication) for more information. + + + +| Module | Description | +|------------|------------------------------------------------------------------| +| public | Public API endpoints. Does not require authentication. | +| onboarding | Endpoint to create a new user, authenticated via Ethereum key. | +| api_keys | Endpoints for managing API keys, authenticated via Ethereum key. | +| private | All other private endpoints, authenticated via API key. | +| eth | Calling and querying L1 Ethereum smart contracts. | + +The following configuration options are available: + +| Parameter | Description | +|--------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| host | The HTTP API host. | +| api_timeout | Timeout for HTTP requests, in milliseconds. | +| default_ethereum_address | (Optional) The default account for Ethereum key auth and sending Ethereum transactions. | +| eth_private_key | (Optional) May be used for Ethereum key auth. | +| eth_send_options | (Optional) Options for Ethereum transactions, see [`sendTransaction`](https://web3py.readthedocs.io/en/stable/web3.eth.html?highlight=signTransaction#web3.eth.Eth.sendTransaction). | +| network_id | (Optional) Chain ID for Ethereum key auth and smart contract addresses. Defaults to `web3.net.version` if available, or `1` (mainnet). | +| stark_private_key | (Optional) STARK private key, used to sign orders and withdrawals. | +| web3 | (Optional) Web3 object used for Ethereum key auth and/or smart contract interactions. | +| web3_account | (Optional) May be used for Ethereum key auth. | +| web3_provider | (Optional) Web3 provider object, same usage as `web3`. | +| api_key_credentials | (Optional) Dictionary containing the key, secret and passphrase required for the private module to sign requests. | +| crypto_c_exports_path | (Optional) For python only, will use faster C++ code to run hashing, signing and verifying. It's expected to be compiled from the `crypto_c_exports` target from Starkware's [repository](https://github.com/starkware-libs/crypto-cpp/blob/master/src/starkware/crypto/ffi/CMakeLists.txt). See [section on this below for more information](#c-methods-for-faster-stark-signing).| + + +### C++ Methods for Faster STARK Signing + + + +The C++ wrapper methods in the client expect an absolute path to a [Shared Object](https://www.cprogramming.com/tutorial/shared-libraries-linux-gcc.html). This has to be compiled from [Starkware's crypto C++ library](https://github.com/starkware-libs/crypto-cpp/blob/master/src/starkware/crypto/ffi/CMakeLists.txt). diff --git a/slate-docs/source/includes/_general-v3.md b/slate-docs/source/includes/_general-v3.md new file mode 100644 index 00000000..c066207b --- /dev/null +++ b/slate-docs/source/includes/_general-v3.md @@ -0,0 +1,160 @@ +# Terms of Service and Privacy Policy + +By using any API provided by dYdX Trading Inc., you agree to its [Terms of Use](https://dydx.exchange/terms) and [Privacy Policy](https://dydx.exchange/privacy). If you do not agree to the foregoing, then do not use any such API. + +# General + +These docs describe the v3 API for the dYdX decentralized perpetual contracts exchange. The exchange runs on an L2 (layer-2) blockchain system, and operates independently of previous dYdX protocols and systems, including the v1 and v2 APIs. + +Like the previous iteration of dYdX perpetuals, the exchange uses a centralized order book, but remains non-custodial, and settles trades and liquidations in a trustless manner. + + + +## Layer 2: ZK-Rollups + +Trades are settled in an L2 (layer-2) system, which publishes ZK (zero-knowledge) proofs periodically to an Ethereum smart contract in order to prove that state transitions within L2 are valid. Funds must be deposited to the Ethereum smart contract before they can be used to trade on dYdX. + +By settling trades on L2, the exchange is able to offer much higher trade throughput and lower minimum order sizes, compared with systems settling trades directly on Ethereum (i.e. L1). This is achieved while maintaining decentralization, and the exchange is fully non-custodial. + +The L2 system was developed with, and is operated jointly with, Starkware. More information about the L2 design can be found in [Starkware's documentation](https://docs.starkware.co/starkex-docs/). (Note: Some of the details described there may be specific to Starkware's previous StarkEx system and may not apply to the dYdX system.) + +## Data Centers + +Our data centers are located in the AWS AP-NORTHEAST-1 region (Tokyo). + + + +## Number Formats + +All amounts and prices in the clients and API are represented in “human readable,” natural units. For example, an amount of 1.25 ETH is represented as `1.25`, and a price of $31,000.50 per BTC is represented as `31000.5`. + +## Base URLs + +Base URLs for API endpoints are as follows: + +* **Production (Mainnet)**: `https://api.dydx.exchange` +* **Staging (Goerli)**: `https://api.stage.dydx.exchange` + +## Testnet + +We have one testnet which is on `Goerli`. To use the testnet, use the above Staging URL for your endpoint. Also use a `networkId` of `5` (Goerli) instead of `1` (Mainnet). + +The user interface for testnet can be found [here](https://trade.stage.dydx.exchange). + +The `dYdX Goerli USDC` token address is `0xF7a2fa2c2025fFe64427dd40Dc190d47ecC8B36e`. Users can deposit via the Testnet website. + +## Rate-Limits + +All rate-limits are subject to change. + +Please make use of the WebSockets API if you need real-time data. + +### Rate Limit - API + +Limits are enforced by IP Address for public endpoints, and by both IP Address and Account for private endpoints. + +Each request consumes 1 point towards the rate limit. [`POST v3/orders`](#place-order-rate-limits) consumes variable points based on the order. Points refresh at the end of each time window. Please take note of the `RateLimit-Remaining` header to track points usage. + +#### Response Headers + +Field | Description +----------------------------------------| ----------- +`RateLimit-Remaining` | Points remaining in the time window. +`RateLimit-Reset` | Timestamp that the time window ends, in Epoch milliseconds. +`Retry-After` | Milliseconds until the next time window. Header included only when the limit has been reached. +`RateLimit-Limit` | The maximum amount of points allowed per time window. + +Request | Limit +----------------------------------------| ----------- +`GET v3/*` | 175 requests per 10 seconds. +`PUT v3/emails/send-verification-email` | 2 requests for 10 minutes. +`DELETE v3/orders` | See `Cancel-Order Rate Limits` +`POST v3/orders` | See `Place-Order Rate-Limits` +`POST v3/testnet/tokens` | 5 requests per 24 hours. +`GET v3/active-orders` | See `Active-Order Rate-Limits` +`DELETE v3/active-orders` | See `Active-Order Rate-Limits` +`All other requests` | 10 requests per minute. + +### Rate Limit - Websocket + +Limits are enforced per `connectionId`. + + + +Request | Limit +----------------------------------------| ----------- +`subscribe v3_accounts, v3_markets` | 2 requests per 1 second. +`subscribe v3_orderbook, v3_trades` | 2 requests for 1 second per market. +`ping` | 5 requests per 1 second. + +### Cancel-Order Rate Limits + +Canceling orders is limited per asset-pair and is intended to be higher than the limit on placing orders. + +`DELETE v3/orders` requests are limited to `3` requests per `10` seconds per asset-pair. + +`DELETE v3/orders/:id` requests are limited to `250` requests per `10` seconds per asset-pair. + +### Place-Order Rate-Limits + +Order rate limits are limited to `maxPoints` spent (per asset-pair) in a fixed window of `windowSec` seconds. + +We want to give priority to those who are making the largest orders and who are contributing the most liquidity to the exchange. +Therefore, placing larger orders is subject to higher limits (i.e. larger orders carry a lower point cost). +The point cost is based on the `orderNotional` which is equal to the `size * price` of the order. + +Limit-order point consumption is equal to: + +
+orderConsumption = clamp( + ceil(targetNotional / orderNotional), + minOrderConsumption, + maxOrderConsumption +) ++ +The `minOrderConsumption` is different for each order type, and can be one of `minLimitConsumption`, `minMarketConsumption`, or `minTriggerableConsumption`. Limit orders that are Fill-or-Kill or Immediate-or-Cancel are considered to be market orders for the purposes of rate limiting. + +The values of the above variables as of March 15th, 2022 are listed below, but the most up-to-date values can be found in the [v3/config endpoint](#get-global-configuration-variables). + +Variable | Value +---------------- | ------- +`maxPoints` | `1,750` +`windowSec` | `10` +`targetNotional` | `40,000` +`minLimitConsumption` | `4` +`minMarketConsumption` | `20` +`minTriggerableConsumption` | `100` +`maxOrderConsumption` | `100` + +### Active-Order Rate-Limits + +Querying active orders is limited per endpoint and per asset and is intended to be higher than the respective DELETE and GET endpoints these new endpoints replace. + +#### DELETE Active-Orders Rate Limits + +`DELETE v3/active-orders/*` + +- 425 points allotted per 10 seconds per market. +- 1 point consumed if order id included. +- 25 points consumed if order side included. +- 50 points consumed otherwise. + +#### GET Active-Orders Rate Limits + +`GET v3/active-orders/*` + +- 175 points allotted per 10 seconds per market. +- 1 point consumed if order id included. +- 3 points consumed if order side included. +- 5 points consumed otherwise. + +## Other Limits + +Accounts may only have up to 20 open orders for a given market/side pair at any one time. (For example up to 20 open `BTC-USD` bids). diff --git a/slate-docs/source/includes/_perpetual-contracts-v3.md b/slate-docs/source/includes/_perpetual-contracts-v3.md new file mode 100644 index 00000000..6a476671 --- /dev/null +++ b/slate-docs/source/includes/_perpetual-contracts-v3.md @@ -0,0 +1,614 @@ +# Perpetual Contracts + +The dYdX Perpetual is a non-custodial, decentralized margin product that offers synthetic exposure to a variety of assets. + +## Margin + +Collateral is held as USDC, and the quote asset for all perpetual markets is USDC. Cross-margining is used by default, meaning an account can open multiple positions that share the same collateral. Isolated margin can be achieved by creating separate accounts (sub-accounts) under the same user. + +Each market has three risk parameters, the `initialMarginFraction`, `maintenanceMarginFraction` and `incrementalInitialMarginFraction`, which determine the max leverage available within that market. These are used to calculate the value that must be held by an account in order to open or increase positions (in the case of initial margin) or avoid liquidation (in the case of maintenance margin). + +### Risk Parameters and Related Fields + +| Risk | Description | +|------------------------------------|------------------------------------------------------------------------------------------------------------------------------| +| `initialMarginFraction` | The margin fraction needed to open a position. | +| `maintenanceMarginFraction` | The margin fraction required to prevent liquidation. | +| `incrementalInitialMarginFraction` | The increase of `initialMarginFraction` for each `incrementalPositionSize` above the `baselinePositionSize` the position is. | +| baselinePositionSize | The max position size (in base token) before increasing the initial-margin-fraction. | +| incrementalPositionSize | The step size (in base token) for increasing the `initialMarginFraction` by (`incrementalInitialMarginFraction` per step). | + +### Portfolio Margining + +There is no distinction between realized and unrealized PnL for the purposes of margin calculations. Gains from one position will offset losses from another position within the same account, regardless of whether the profitable position is closed. + +### Margin Calculation + +The margin requirement for a single position is calculated as follows: + +
+Initial Margin Requirement = abs(S × P × I) +Maintenance Margin Requirement = abs(S × P × M) ++ +Where: + +* `S` is the size of the position (positive if long, negative if short) +* `P` is the oracle price for the market +* `I` is the initial margin fraction for the market +* `M` is the maintenance margin fraction for the market + +The margin requirement for the account as a whole is the sum of the margin requirement over each market `i` in which the account holds a position: + +
+Total Initial Margin Requirement = Σ abs(Si × Pi × Ii) +Total Maintenance Margin Requirement = Σ abs(Si × Pi × Mi) ++ +The total margin requirement is compared against the total value of the account, which incorporates the quote asset (USDC) balance of the account as well as the value of the positions held by the account: + +
+Total Account Value = Q + Σ (Si × Pi) ++ +The Total Account Value is also referred to as equity. + +Where: + +* `Q` is the account's USDC balance (note that `Q` may be negative). In the API, this is called `quoteBalance`. Every time a transfer, deposit or withdrawal occurs for an account, the balance changes. Also, when a position is modified for an account, the `quoteBalance` changes. Also funding payments and liquidations will change an account's `quoteBalance`. +* `S` and `P` are as defined above (note that `S` may be negative) + +An account cannot open new positions or increase the size of existing positions if it would lead the total account value of the account to drop below the total initial margin requirement. If the total account value ever falls below the total maintenance margin requirement, the account may be liquidated. + +Free collateral is calculated as: + +
+Free collateral = Total Account Value - Total Initial Margin Requirement ++ +Equity and free collateral can be tracked over time using the latest oracle price (obtained from the markets websocket). + +## Liquidations + +Accounts whose total value falls below the maintenance margin requirement (described above) may have their positions automatically closed by the liquidation engine. Positions are closed at the close price described below. Profits or losses from liquidations are taken on by the insurance fund. + +### Close Price for Liquidations + +The close price for a position being liquidated is calculated as follows, depending whether it is a short or long position: + +
+Close Price (Short) = P × (1 + (M × V / W)) +Close Price (Long) = P × (1 − (M × V / W)) ++ +Where: + +* `P` is the oracle price for the market +* `M` is the maintenance margin fraction for the market +* `V` is the total account value, as defined above +* `W` is the total maintentance margin requirement, as defined above + +This formula is chosen such that the ratio `V / W` is unchanged as individual positions are liquidated. + +## Funding + +Funding payments are exchanged between long and short traders to encourage the price of a perpetual contract to trade close to the price of the underlying. If the perpetual trades at a premium relative to the index, long traders will typically make payments to short traders, whereas if the perpetual trades at a discount relative to the index, short traders will typically make payments to long traders. + +The payments are credited or debited at the start of each hour, and are included in the realized PnL for the position. + +Funding payments can be found by calling [Get /v3/funding](#get-funding-payments) and the predicted funding rate can be found by calling [Get v3/markets](#get-markets). + +### Funding Rate Units + +Since funding payments are exchanged every hour, the dYdX funding rate is usually represented as a 1-hour rate, which represents the return a position may expect to earn or pay every hour. + +When calculating the funding rate, the premium is scaled to have a realization period of 8 hours. That means, for example, that if a certain perpetual market trades consistently at a 0.1% premium relative to the underlying, long traders may expect to pay ~0.1% every 8 hours, and short traders may expect to earn a ~0.1% return every 8 hours (not accounting for the interest rate component). + +### Funding Payment Calculation + +At the start of each hour, an account receives USDC (if `F` is positive) or pays USDC (if `F` is negative) in an amount equal to: + +
+F = (-1) × S × P × R ++ +Where: + +* `S` is the size of the position (positive if long, negative if short) +* `P` is the oracle price for the market +* `R` is the funding rate (as a 1-hour rate) + +### Funding Rate Calculation + +The main component of the funding rate is a premium that takes into account market activity for the perpetual. It is calculated for each market, every minute (at a random point within the minute) using the formula: + +
+Premium = (Max(0, Impact Bid Price - Index Price) - Max(0, Index Price - Impact Ask Price)) / Index Price ++ +Where the impact bid and impact ask prices are defined as: + +
+Impact Bid Price = Average execution price for a market sell of the impact notional value +Impact Ask Price = Average execution price for a market buy of the impact notional value ++ +And the impact notional amount for a market is: + +
+Impact Notional Amount = 500 USDC / Initial Margin Fraction ++ +For example, for a market with a 10% initial margin fraction, the impact notional value is 5,000 USDC. + +At the end of each hour, the premium component is calculated as the simple average (i.e. TWAP) of the 60 premiums calculated over the course of the last hour. In addition to the premium component, each market has a fixed interest rate component that aims to account for the difference in interest rates of the base and quote currencies. The funding rate is then: + +
+Funding Rate = (Premium Component / 8) + Interest Rate Component ++ +Currently, the interest rate component for all dYdX markets is `0.00125%` (equivalent to `0.01%` per 8 hours). + +## Contract Loss Mechanisms + +During periods of high volatility in the markets underlying the perpetual contracts, the value of some accounts may drop below zero before they can be liquidated. + +The insurance fund is the first backstop to maintain the solvency of the system when an account has a negative balance. The account will be liquidated, and the insurance fund will take on the loss. + +In the event that the insurance fund is depleted, positions with the most profit and leverage may be used to offset negative-balance accounts, in order to maintain the stability of the system. + +## Oracle Prices + +The `Oracle Price` for each trading pair is used for the following: + +* Ensuring that each account is well-collateralized after each trade +* Determining when an account should be liquidated + +### Calculation + +Oracle prices are equal to the median of the reported prices of 15 independent Chainlink nodes. + +### Node Providers + +* Chainlayer +* Inotel +* LinkForest +* SimplyVC +* DexTrac +* Fiews +* dMakers +* linkPool +* SDL +* Ztake +* stakFacils +* infStones +* 01node +* Syncnode +* Vulcan + +## Index Prices + +The `Index Price` for each trading pair is used for the following: + +* Calculating the funding rate +* Triggering "triggerable" order types such as Stop-Limit and Take-Profit orders + +### Calculation + +Index prices are equal to the median of several exchanges' spot prices. + +Each exchange's spot price is calculated as the median of the best-ask, best-bid, and last-traded prices of its spot pair. + +If the exchange's quote-asset is a non-USD asset (including USDT), the price is adjusted by our index price for that asset. + +### Exchange Sources + +#### USDT-USD + +For `USDT` pairs where `USDT` is the quote asset, the implied price of `USDT` is taken to be the reciprocal of: the exchange price divided by the index price of the base asset. + +* Binance: `USDT-USDC` +* Bitstamp: `USDT-USD` +* Bybit: `USDC-USDT` +* CoinbasePro: `USDT-USD` +* Crypto: `USDT-USD` +* Huobi: `ETH-USDT` +* Kraken: `USDT-USD` +* KuCoin: `BTC-USDT` +* MEXC: `ETH-USDT` +* OKX: `BTC-USDT` + +#### ETH-USD + +* Binance: `ETH-USDT` +* Bitstamp: `ETH-USD` +* Bybit: `ETH-USDT` +* CoinbasePro: `ETH-USD` +* Kraken: `ETH-USD` +* KuCoin: `ETH-USDT` +* OKX: `ETH-USDT` + +#### BTC-USD + +* Binance: `BTC-USDT` +* Bitstamp: `BTC-USD` +* Bybit: `BTC-USDT` +* CoinbasePro: `BTC-USD` +* Kraken: `BTC-USD` +* KuCoin: `BTC-USDT` +* OKX: `BTC-USDT` + +#### LINK-USD + +* Binance: `LINK-USDT` +* Bybit: `LINK-USDT` +* CoinbasePro: `LINK-USD` +* Huobi: `LINK-USDT` +* Kraken: `LINK-USD` +* KuCoin: `LINK-USDT` +* MEXC: `LINK-USDT` +* OKX: `LINK-USDT` + +#### AAVE-USD + +* Binance: `AAVE-USDT` +* Bybit: `AAVE-USDT` +* CoinbasePro: `AAVE-USD` +* Huobi: `AAVE-USDT` +* Kraken: `AAVE-USD` +* KuCoin: `AAVE-USDT` +* MEXC: `AAVE-USDT` +* OKX: `AAVE-USDT` + +#### UNI-USD + +* Binance: `UNI-USDT` +* Bybit: `UNI-USDT` +* CoinbasePro: `UNI-USD` +* Gate: `UNI-USDT` +* Huobi: `UNI-USDT` +* Kraken: `UNI-USD` +* MEXC: `UNI-USDT` +* OKX: `UNI-USDT` + +#### SUSHI-USD + +* Binance: `SUSHI-USDT` +* Bybit: `SUSHI-USDT` +* CoinbasePro: `SUSHI-USD` +* Gate: `SUSHI-USDT` +* Huobi: `SUSHI-USDT` +* Kraken: `SUSHI-USD` +* KuCoin: `SUSHI-USDT` +* MEXC: `SUSHI-USDT` +* OKX: `SUSHI-USDT` + +#### SOL-USD + +* Binance: `SOL-USDT` +* Bybit: `SOL-USDT` +* CoinbasePro: `SOL-USD` +* Huobi: `SOL-USDT` +* Kraken: `SOL-USD` +* KuCoin: `SOL-USDT` +* MEXC: `SOL-USDT` +* OKX: `SOL-USDT` + +#### YFI-USD + +* Binance: `YFI-USDT` +* Bybit: `YFI-USDT` +* CoinbasePro: `YFI-USD` +* Gate: `YFI-USDT` +* Huobi: `YFI-USDT` +* Kraken: `YFI-USD` +* MEXC: `YFI-USDT` +* OKX: `YFI-USDT` + +#### 1INCH-USD + +* Binance: `1INCH-USDT` +* Bybit: `1INCH-USDT` +* CoinbasePro: `1INCH-USD` +* Gate: `1INCH-USDT` +* Huobi: `1INCH-USDT` +* KuCoin: `1INCH-USDT` +* MEXC: `1INCH-USDT` +* OKX: `1INCH-USDT` + +#### AVAX-USD + +* Binance: `AVAX-USDT` +* Bybit: `AVAX-USDT` +* Coinbase: `AVAX-USD` +* Gate: `AVAX-USDT` +* Huobi: `AVAX-USDT` +* Kraken: `AVAX-USD` +* KuCoin: `AVAX-USDT` +* OKX: `AVAX-USDT` + +#### SNX-USD + +* Binance: `SNX-USDT` +* Bybit: `SNX-USDT` +* CoinbasePro: `SNX-USD` +* Gate: `SNX-USDT` +* Huobi: `SNX-USDT` +* Kraken: `SNX-USD` +* Kucoin: `SNX-USDT` +* MEXC: `SNX-USDT` +* OKX: `SNX-USDT` + +#### CRV-USD + +* Binance: `CRV-USDT` +* Bybit: `CRV-USDT` +* CoinbasePro: `CRV-USD` +* Gate: `CRV-USDT` +* Huobi: `CRV-USDT` +* Kraken: `CRV-USD` +* Kucoin: `CRV-USDT` +* MEXC: `CRV-USDT` +* OKX: `CRV-USDT` + +#### UMA-USD + +* Binance: `UMA-USDT` +* Bybit: `UMA-USDT` +* CoinbasePro: `UMA-USD` +* Gate: `UMA-USDT` +* Huobi: `UMA-USDT` +* KuCoin: `UMA-USDT` +* MEXC: `UMA-USDT` +* OKX: `UMA-USDT` + +#### DOT-USD + +* Binance: `DOT-USDT` +* Bybit: `DOT-USDT` +* Coinbase: `DOT-USD` +* Gate: `DOT-USDT` +* Huobi: `DOT-USDT` +* Kraken: `DOT-USD` +* KuCoin: `DOT-USDT` +* OKX: `DOT-USDT` + +#### DOGE-USD + +* Binance: `DOGE-USDT` +* Bybit: `DOGE-USDT` +* Coinbase: `DOGE-USD` +* Gate: `DOGE-USDT` +* Huobi: `DOGE-USDT` +* Kraken: `DOGE-USD` +* KuCoin: `DOGE-USDT` +* OKX: `DOGE-USDT` + +#### MATIC-USD + +* Binance: `MATIC-USDT` +* Bybit: `MATIC-USDT` +* CoinbasePro: `MATIC-USD` +* Gate: `MATIC-USDT` +* Huobi: `MATIC-USDT` +* Kraken: `MATIC-USD` +* KuCoin: `MATIC-USDT` +* OKX: `MATIC-USDT` + +#### MKR-USD + +* Binance: `MKR-USDT` +* Bybit: `MKR-USDT` +* CoinbasePro: `MKR-USD` +* Gate: `MKR-USDT` +* Huobi: `MKR-USDT` +* Kraken: `MKR-USD` +* KuCoin: `MKR-USDT` +* OKX: `MKR-USDT` + +#### FIL-USD + +* Binance: `FIL-USDT` +* Bybit: `FIL-USDT` +* CoinbasePro: `FIL-USD` +* Gate: `FIL-USDT` +* Huobi: `FIL-USDT` +* Kraken: `FIL-USD` +* KuCoin: `FIL-USDT` +* OKX: `FIL-USDT` + +#### ADA-USD + +* Binance: `ADA-USDT` +* Bybit: `ADA-USDT` +* CoinbasePro: `ADA-USD` +* Gate: `ADA-USDT` +* Huobi: `ADA-USDT` +* Kraken: `ADA-USD` +* KuCoin: `ADA-USDT` +* OKX: `ADA-USDT` + +#### ATOM-USD + +* Binance: `ATOM-USDT` +* ByBit: `ATOM-USDT` +* CoinbasePro: `ATOM-USD` +* Huobi: `ATOM-USDT` +* Kraken: `ATOM-USD` +* KuCoin: `ATOM-USDT` +* MEXC: `ATOM-USDT` +* OKX: `ATOM-USDT` + +#### COMP-USD + +* Binance: `COMP-USDT` +* ByBit: `COMP-USDT` +* CoinbasePro: `COMP-USD` +* Huobi: `COMP-USDT` +* Kraken: `COMP-USD` +* KuCoin: `COMP-USDT` +* MEXC: `COMP-USDT` +* OKX: `COMP-USDT` + +#### BCH-USD + +* Binance: `BCH-USDT` +* Bybit: `BCH-USDT` +* CoinbasePro: `BCH-USD` +* Huobi: `BCH-USDT` +* Kraken: `BCH-USD` +* KuCoin: `BCH-USDT` +* MEXC: `BCH-USDT` +* OKX: `BCH-USDT` + +#### LTC-USD + +* Binance: `LTC-USDT` +* ByBit: `LTC-USDT` +* CoinbasePro: `LTC-USD` +* Huobi: `LTC-USDT` +* Kraken: `LTC-USD` +* KuCoin: `LTC-USDT` +* MEXC: `LTC-USDT` +* OKX: `LTC-USDT` + +#### EOS-USD + +* Binance: `EOS-USDT` +* Bybit: `EOS-USDT` +* CoinbasePro: `EOS-USD` +* Huobi: `EOS-USDT` +* Kraken: `EOS-USD` +* KuCoin: `EOS-USDT` +* MEXC: `EOS-USDT` +* OKX: `EOS-USDT` + +#### ALGO-USD + +* Binance: `ALGO-USDT` +* Bybit: `ALGO-USDT` +* CoinbasePro: `ALGO-USD` +* Gate: `ALGO-USDT` +* Huobi: `ALGO-USDT` +* Kraken: `ALGO-USD` +* KuCoin: `ALGO-USDT` +* OKX: `ALGO-USDT` + +#### ZRX-USD + +* Binance: `ZRX-USDT` +* Bybit: `ZRX-USDT` +* CoinbasePro: `ZRX-USD` +* Gate: `ZRX-USDT` +* Huobi: `ZRX-USDT` +* Kraken: `ZRX-USD` +* MEXC: `ZRX-USDT` +* OKX: `ZRX-USDT` + +#### XMR-USD + +* Gate: `XMR-USDT` +* Huobi: `XMR-USDT` +* Kraken: `XMR-USD` +* KuCoin: `XMR-USDT` +* MEXC: `XMR-USDT` + +#### ZEC-USD + +* Binance: `ZEC-USDT` +* CoinbasePro: `ZEC-USD` +* Gate: `ZEC-USDT` +* HTX: `ZEC-USDT` +* Kraken: `ZEC-USD` +* KuCoin: `ZEC-USDT` +* MEXC: `ZEC-USDT` + +#### ENJ-USD + +* Binance: `ENJ-USDT` +* CoinbasePro: `ENJ-USD` +* Gate: `ENJ-USDT` +* Huobi: `ENJ-USDT` +* Kraken: `ENJ-USD` +* MEXC: `ENJ-USDT` + +#### ETC-USD + +* Binance: `ETC-USDT` +* Bybit: `ETC-USDT` +* CoinbasePro: `ETC-USD` +* Gate: `ETC-USDT` +* Huobi: `ETC-USDT` +* Kraken: `ETC-USD` +* KuCoin: `ETC-USDT` +* OKX: `ETC-USDT` + +#### XLM-USD + +* Binance: `XLM-USDT` +* Bybit: `XLM-USDT` +* CoinbasePro: `XLM-USD` +* Gate: `XLM-USDT` +* Kraken: `XLM-USD` +* KuCoin: `XLM-USDT` +* OKX: `XLM-USDT` + +#### TRX-USD + +* Binance: `TRX-USDT` +* Bybit: `TRX-USDT` +* Gate: `TRX-USDT` +* Huobi: `TRX-USDT` +* KuCoin: `TRX-USDT` +* MEXC: `TRX-USDT` +* OKX: `TRX-USDT` + +#### XTZ-USD + +* Binance: `XTZ-USDT` +* Bybit: `XTZ-USDT` +* CoinbasePro: `XTZ-USD` +* Gate: `XTZ-USDT` +* Huobi: `XTZ-USDT` +* Kraken: `XTZ-USD` +* KuCoin: `XTZ-USDT` +* OKX: `XTZ-USDT` + +#### ICP-USD + +* Binance: `ICP-USDT` +* Bybit: `ICP-USDT` +* CoinbasePro: `ICP-USD` +* Gate: `ICP-USDT` +* Huobi: `ICP-USDT` +* KuCoin: `ICP-USDT` +* OKX: `ICP-USDT` + +#### RUNE-USD + +* Binance: `RUNE-USDT` +* Bybit: `RUNE-USDT` +* Bitget: `RUNE-USDT` +* Gate: `RUNE-USDT` +* Kraken: `RUNE-USD` +* KuCoin: `RUNE-USDT` + +#### NEAR-USD + +* Binance: `NEAR-USDT` +* ByBit: `NEAR-USDT` +* CoinbasePro `NEAR-USD` +* Gate: `NEAR-USDT` +* Huobi: `NEAR-USDT` +* KuCoin: `NEAR-USDT` +* OKX: `NEAR-USDT` + +#### CELO-USD + +* Binance: `CELO-USDT` +* Bybit: `CELO-USDT` +* CoinbasePro: `CELO-USD` +* Gate: `CELO-USDT` +* KuCoin: `CELO-USDT` +* MEXC: `CELO-USDT` +* OKX: `CELO-USDT` diff --git a/slate-docs/source/includes/_private-v3.md b/slate-docs/source/includes/_private-v3.md new file mode 100644 index 00000000..16d192c5 --- /dev/null +++ b/slate-docs/source/includes/_private-v3.md @@ -0,0 +1,2824 @@ +# Private HTTP API + +## Authentication + +There are three levels of authentication to be considered when using dYdX. All signing can be handled directly by the client libraries. + +### Ethereum Key Authentication + +The highest level of authentication is via an account's Ethereum private key. The Ethereum key remains in control of an account's funds while they are within the L2 system. This includes the ability to forcibly close an account's positions and exit the system, in the event that the L2 operators (dYdX and Starkware) were to unexpectedly go offline or otherwise censor requests. + +Ethereum key authentication is required for the following operations: + +* Register a new user or STARK key +* Create or revoke API keys +* Request a forced withdrawal or forced trade + +### STARK Key Authentication + +Within the L2 system, authentication is handled by a separate key pair, known as the account's STARK key pair. + +STARK key authentication is required for the following operations: + +* Place an order +* Withdraw funds + +### API Key Authentication + +The third level of authentication consists of the API key, secret and passphrase which are used solely to authenticate API requests made to dYdX. This includes operations such as canceling orders or retrieving an account's fills, which do not affect the L2 system. + +When a user onboards via `POST v3/onboarding`, the server will use the signature as a seed to determinstically generate default API key credentials. An API key includes three fields: + +* `key`: UUID identifying the credentials. +* `secret`: Secret string used to generate HMACs, not sent with requests. +* `passphrase`: Secret string sent with each request, used to encrypt/decrypt the secret in our DB, and never stored in our DB. + +API keys can be added and managed via the `/v3/api-keys` endpoints. + +All requests which are not signed by an Ethereum key and which are made to private endpoints require an API key signature. + +### STARK Key Cryptography + +The STARK and API keys are ECDSA key pairs on the [STARK curve](https://docs.starkware.co/starkex-docs/crypto/stark-curve). More info on the cryptography used on L2 is available in [Starkware's documentation](https://docs.starkware.co/starkex-docs/crypto/signatures). + +## Creating and Signing Requests + + + +Within the private HTTP API, there are three groups of endpoints which each require different headers and authentication. + +(Separately, and in addition to the above, STARK signatures are required for orders and withdrawals. For details, please refer to the [Python](https://github.com/dydxprotocol/dydx-v3-python/tree/master/dydx3/starkex) and [TypeScript](https://github.com/dydxprotocol/starkex-lib/tree/master/src/signable) reference implementations.) + +### Onboarding Endpoint: `POST v3/onboarding` + +**Request Headers** + +| Header | Required? | Description | +|-----------------------|-----------|------------------------------| +| DYDX-SIGNATURE | yes | Ethereum key authentication | +| DYDX-ETHEREUM-ADDRESS | yes | Ethereum address of the user | + +**Signing** + +The header `DYDX-SIGNATURE` is an EIP-712 Ethereum signature on a static message containing the fields: + +* `action`: The string `DYDX-ONBOARDING`. +* `onlySignOn`: The string `https://trade.dydx.exchange`. + +See reference implementations: [[Python]](https://github.com/dydxprotocol/dydx-v3-python/blob/master/dydx3/modules/onboarding.py) [[TypeScript]](https://github.com/dydxprotocol/v3-client/blob/master/src/modules/onboarding.ts) + +### Ethereum Key Private Endpoints + +This group includes the `POST` and `DELETE` `v3/api-keys` endpoints for managing API keys. Like the onboarding endpoint, requests to these endpoints require signatures by the user's Ethereum key. + +**Request Headers** + +| Header | Required? | Description | +|-----------------------|-----------|---------------------------------------------------------------------------------------------| +| DYDX-SIGNATURE | yes | Ethereum key authentication | +| DYDX-ETHEREUM-ADDRESS | yes | Ethereum address of the user | +| DYDX-TIMESTAMP | yes | ISO timestamp of when the request was signed. Must be within 30 seconds of the server time. | + +**Signing** + +The header `DYDX-SIGNATURE` is an EIP-712-compliant Ethereum signature on a message containing the fields: + +* `method`: The name of the HTTP method used, uppercase (e.g. `GET`). +* `requestPath`: The API endpoint path, beginning with `/v3/`. +* `body`: The HTTP request body (normally empty for `GET` and `DELETE`). +* `timestamp`: Equal to the header `DYDX-TIMESTAMP`. + +See reference implementations: [[Python]](https://github.com/dydxprotocol/dydx-v3-python/blob/master/dydx3/modules/api_keys.py) [[TypeScript]](https://github.com/dydxprotocol/v3-client/blob/master/src/modules/keys.ts) + +### API Key Private Endpoints + +All private endpoints not listed above fall in this category, and must be authenticated via an API key. + +**Request Headers** + +| Header | Required? | Description | +|-----------------------|-----------|---------------------------------------------------------------------------------------------| +| DYDX-SIGNATURE | yes | HMAC of the request. | +| DYDX-API-KEY | yes | Api key for the account. | +| DYDX-TIMESTAMP | yes | ISO timestamp of when the request was signed. Must be within 30 seconds of the server time. | +| DYDX-PASSPHRASE | yes | The `passphrase` field of the API key. | +| DYDX-ACCOUNT-NUMBER | no | Account number used to scope the request. Defaults to zero. | + +**Signing** + +The `DYDX-SIGNATURE` is a SHA-256 HMAC produced as described below, and encoded as a `Base64` string. + +A SHA-256 HMAC is created using the API key `secret` and the message `timestamp + method + requestPath + body` defined as follows: + +* `timestamp`: The `DYDX-TIMESTAMP` header, which must be within 30 seconds of the server time. +* `method`: The name of the HTTP method used, uppercase (e.g. `GET`). +* `requestPath`: The API endpoint path, beginning with `/v3/`. +* `body`: The HTTP request body (normally empty for `GET` and `DELETE`). + +The HMAC should be encoded as a Base64 string and sent as the `DYDX-SIGNATURE` header. + +See reference implementations: [[Python]](https://github.com/dydxprotocol/dydx-v3-python/blob/master/dydx3/modules/private.py) [[TypeScript]](https://github.com/dydxprotocol/v3-client/blob/master/src/modules/private.ts) + +## Onboarding + +### Overview + +A few steps are required of all accounts before they can begin trading: + +1. [Create a user](#onboarding), providing a STARK public key to be associated with the main account. +1. [Request registration](#get-registration) signature from dYdX. +1. Send registration request to the L1 smart contract. +1. Approve collateral token allowance on the L1 smart contract. +1. Deposit collateral token to the L1 smart contract. + +All of these steps are supported by the Python and TypeScript clients. See the Python [integration tests](https://github.com/dydxprotocol/dydx-v3-python/blob/master/integration_tests/test_integration.py) for an example of onboarding and usage of various endpoints. + +> Create User + +```python +onboarding_information = client.onboarding.create_user( + # Optional if stark_private_key was provided. + stark_public_key='012340bcd...', + stark_public_key_y_coordinate='01234abcd...', + # Optional if eth_private_key or web3.eth.defaultAccount was provided. + ethereum_address='ethereumAddress', + country='SG', +) +``` + +```typescript +const onboardingInformation: { + apiKey: ApiKeyCredentials, + user: UserResponseObject, + account: AccountResponseObject, +} = await client.onboarding.createUser( + { + starkKey: '71234abcd...', + starkKeyYCoordinate: '01234abcd...', + country: 'SG', + }, + ethereumAddress: 'ethereumAddress', +); +``` + +```json +{ + "apiKey": { + "key": "290decd9-548b-62a8-d603-45a988386fc8", + "passphrase": "S6a8lUhACPY2L5MWDvPl", + "secret": "KQ3s2VSLYqjWA0WpiDhvyEumvJVIQAj2Ni-TFg7z" + }, + "user": { + "ethereumAddress": "0x0913017c740260fea4b2c62828a4008ca8b0d6e4", + "isRegistered": true, + "email": "email@dydx.exchange", + "username": "supersam15o", + "referredByAffiliateLink": null, + "makerFeeRate": "0.01", + "takerFeeRate": "0.01", + "makerVolume30D": "1000.00", + "takerVolume30D": "1000.00", + "fees30D": "00.50", + "userData": {}, + "dydxTokenBalance": "0", + "stakedDydxTokenBalance": "0", + "isEmailVerified": false, + "isSharingUsername": null, + "isSharingAddress": true, + "country": "SG", + }, + "account": { + "starkKey": "180913017c740260fea4b2c62828a4008ca8b0d6e4", + "positionId": "1812", + "equity": "10000", + "freeCollateral": "10000", + "quoteBalance": "10000", + "pendingDeposits": "0", + "pendingWithdrawals": "0", + "createdAt": "2021-04-09T21:08:34.984Z", + "openPositions": { + "BTC-USD": { + "market": "BTC-USD", + "status": "OPEN", + "side": "LONG", + "size": "1000", + "maxSize": "1050", + "entryPrice": "100", + "exitPrice": null, + "unrealizedPnl": "50", + "realizedPnl": "100", + "createdAt": "2021-01-04T23:44:59.690Z", + "closedAt": null, + "netFunding": "500", + "sumOpen": "1050", + "sumClose": "50" + } + }, + "accountNumber": "5", + "id": "id" + } +} +``` + +### HTTP Request +`POST v3/onboarding` + + + +Description: Onboard a user so they can begin using dYdX V3 API. This will generate a user, account and derive a key, passphrase and secret from the signature. + +### Request + +Parameter | Description +---------------------- | ----------- +starkKey | Public starkKey associated with the key-pair you created. +starkKeyYCoordinate | Public starkKey Y-Coordinate associated with the key-pair you created. +ethereumAddress | Ethereum address associated with the user being created. +referredByAffiliateLink| (Optional) Link to affiliate the user was referred by. +country | (Optional) Country of the user's residence. Must be [ISO 3166-1 Alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) compliant. + + +### Response + +Parameter | Description +---------------| ----------- +apiKey | See [ApiKeyCredentials](#post-api-keys). +user | See [User](#get-user). +account | See [Account](#get-account). + +## Derive StarkKey +> Derive StarkKey + +```python +key_pair_with_y_coordinate = client.onboarding.derive_stark_key( + # Optional if eth_private_key or web3.eth.defaultAccount was provided. + ethereum_address='ethereumAddress', +) +``` + +```typescript +const keyPairWithYCoordinate: KeyPairWithYCoordinate = await client.onboarding.deriveStarkKey( + 'ethereumAddress', +); +``` + + + +### Request + +Parameter | Description +------------------ | ----------- +ethereumAddress | Ethereum address associated with the user being created. + +### Response + +Parameter | Description +----------------------| ----------- +keyPairWithYCoordinate| KeyPairWithYCoordinate. + +### KeyPairWithYCoordinate + +|field|type|description| +|-----|----|-----------| +|publicKey|string|The x-coordinate of the publicKey.| +|publicKeyYCoordinate|string|The y-coordinate of the publicKey.| +|privateKey|string|The privateKey for the key pair.| + +## Recover Default API Credentials +> Recover Default API Credentials + +```python +api_credentials = client.onboarding.recover_default_api_key_credentials( + # Optional if eth_private_key or web3.eth.defaultAccount was provided. + ethereum_address='ethereumAddress', +) +``` + +```typescript +const apiCredentials: ApiKeyCredentials = await client.onboarding.recoverDefaultApiCredentials( + 'ethereumAddress', +); +``` + + + +### Request + +Parameter | Description +------------------ | ----------- +ethereumAddress | Ethereum address associated with the user being created. + +### Response + +Parameter | Description +---------------| ----------- +apiCredentials | ApiKeyCredentials. + +### ApiKeyCredentials + +|field|type|description| +|-----|----|-----------| +|key|string|UUID identifying the credentials.| +|secret|string|Secret string used to generate HMACs.| +|passphrase|string|Secret string sent with each request.| + +## Recover StarkKey, QuoteBalance and Open Positions +> Recovery + +```python +recovery = client.eth_private.recovery( + # Optional if eth_private_key or web3.eth.defaultAccount was provided. + ethereum_address='ethereumAddress', +) +``` + +```typescript +const recovery: { + starkKey: string, + positionId: string, + quoteBalance: string, + positions: PositionResponseObject[], +} = client.ethPrivate.recovery( + 'ethereumAddress', +); +``` + +```json +{ + "starkKey": "180913017c740260fea4b2c62828a4008ca8b0d6e4", + "positionId": "1812", + "equity": "10000", + "freeCollateral": "10000", + "quoteBalance": "10000", + "positions": [ + { + "market": "BTC-USD", + "status": "OPEN", + "side": "LONG", + "size": "1000", + "maxSize": "1050", + "entryPrice": "100", + "exitPrice": null, + "unrealizedPnl": "50", + "realizedPnl": "100", + "createdAt": "2021-01-04T23:44:59.690Z", + "closedAt": null, + "netFunding": "500", + "sumOpen": "1050", + "sumClose": "50" + } + ] +} +``` + +### HTTP Request +`GET v3/recovery` + +Description: This is for if you can't recover your starkKey or apiKey and need an additional way to get your starkKey and balance on our exchange, both of which are needed to call the L1 solidity function needed to recover your funds. + +### Response + +Parameter | Description +------------- | ---------- +starkKey | Public starkKey associated with the key-pair you created. +positionId | Starkware-specific positionId. +equity | The amount of equity (value) in the account. Uses balances and oracle-prices to calculate. +freeCollateral| The amount of collateral that is withdrawable from the account. +quoteBalance | Human readable quote token balance. Can be negative. +positions | See [Positions](#get-positions). Note, only open position are returned. + +## Get Registration +> Get Registration + +```python +signature = client.private.get_registration() +``` + +```typescript +const signature: { signature: string } = await client.private.getRegistration(); +``` + +```json +{ + "signature": "foo" +} +``` + +### HTTP Request +`GET v3/registration` + +Description: Gets the dYdX provided Ethereum signature required to send a registration transaction to the Starkware smart contract. + +### Response + +Parameter | Description +---------------| ----------- +signature | Ethereum signature authorizing the user's Ethereum address to register for the corresponding position id. + +## Register API Key +> Register API Key + +```python +api_key_response = client.eth_private.create_api_key( + # Optional if eth_private_key or web3.eth.defaultAccount was provided. + ethereum_address='0x0123...', +) +``` + +```typescript +const apiKey: { apiKey: ApiKeyCredentials } = await client.ethPrivate.createApiKey( + '0x0123...', +); +``` + +```json +{ + "apiKey": { + "key": "290decd9-548b-62a8-d603-45a988386fc8", + "passphrase": "S6a8lUhACPY2L5MWDvPl", + "secret": "KQ3s2VSLYqjWA0WpiDhvyEumvJVIQAj2Ni-TFg7z" + } +} +``` + +### HTTP Request +`POST v3/api-keys` + +Description: Create new API key credentials for a user. +Limit: 20 API keys per user. + +### Response + +Parameter | Description +---------------| ----------- +apiKey | [ApiKeyCredentials](#apikeycredentials). + +## Get API Keys +> Get API Keys + +```python +api_keys = client.private.get_api_keys() +``` + +```typescript +const apiKeys: { keys: string[] } = await client.private.getApiKeys(); +``` + +```json +{ + "apiKeys": [ + "290decd9-548b-62a8-d603-45a988386fc8", + "390decd9-548b-62a8-d603-45a988386fc8", + ... + ] +} +``` + +### HTTP Request +`GET v3/api-keys` + + +Description: Get all api keys associated with an Ethereum address. + + + +### Response + +Parameter | Description +---------------| ----------- +apiKeys | Array of apiKey strings corresponding to the ethereumAddress in the request. + +## Delete API Key +> Delete API Key + +```python +client.eth_private.delete_api_key( + api_key='290decd9-548b-...', + # Optional if eth_private_key or web3.eth.defaultAccount was provided. + ethereum_address='0x0123...', +) +``` + +```typescript +await client.ethPrivate.delete_api_key( + '290decd9-548b-...', // API key + '0x0123...', // Ethereum address +); +``` + +```json +{ + "apiKey": "foo" +} +``` + +### HTTP Request +`DELETE v3/api-keys` + +Description: Delete an api key by key and Ethereum address. + + + +### Request + +Parameter | Description +----------------| ----------- +apiKey | Public api key being deleted. +ethereumAdddress| Ethereum address the api key is associated with. + +### Response + +Returns a 200 on success. + +## Get User +> Get User + +```python +user = client.private.get_user() +``` + +```typescript +const user: { user: UserResponseObject } = await client.private.getUser(); +``` + +```json +{ + "user": { + "ethereumAddress": "0x0913017c740260fea4b2c62828a4008ca8b0d6e4", + "isRegistered": true, + "email": "email@dydx.exchange", + "username": "supersam15o", + "referredByAffiliateLink": null, + "makerFeeRate": "0.01", + "takerFeeRate": "0.01", + "makerVolume30D": "1000.00", + "takerVolume30D": "1000.00", + "fees30D": "00.50", + "userData": {}, + "dydxTokenBalance": "0", + "stakedDydxTokenBalance": "0", + "isEmailVerified": false, + "hedgiesHeld": [1, 2, 3000], + "country": "CN", + "languageCode": "zh-CN" + } +} +``` + +### HTTP Request +`GET v3/users` + +Description: return the user and user information. + +### Response + +Parameter | Description +------------------------| ----------- +ethereumAddress | The 20-byte Ethereum address. +isRegistered | True if the user is registered on the starkware smart contract. This is false otherwise. +email | Email address. +username | User defined username. +referredByAffiliateLink | The affiliate link that referred this user, or null if the user was not referred. +makerFeeRate | The fee rate the user would be willing to take as the maker. Fee rates are rounded to a 100th of a basis point, or 0.0001%. Note, 1% would be represented as 0.01. +takerFeeRate | The fee rate the user would be willing to take as the taker. Fee rates are rounded to a 100th of a basis point, or 0.0001%. Note, 1% would be represented as 0.01. +makerVolume30D | The user's trailing-thirty-day maker volume in USD. +takerVolume30D | The user's trailing-thirty-day taker volume in USD. +fees30D | The user's trailing-thirty-day fees in USD. +userData | The user's unstructured user data. +dydxTokenBalance | The user's DYDX token holdings. +stakedDydxTokenBalance | The user's staked DYDX token holdings +isEmailVerified | If the user's email address is verified to receive emails from dYdX. +hedgiesHeld | Indices of all Hedgies held by the user. +country | Country of the user's residence. Must be [ISO 3166-1 Alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) compliant. +languageCode | The user's preferred language. Must be [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) compliant, including 'zh-CN'. + +## Update User +> Update user + +```python +user = client.private.update_user( + user_data={}, + email='user@example.com', + username='username', + is_sharing_email=False, + is_sharing_address=True, + country='SG', + language_code='en', +) +``` + +```typescript +const user: { user: UserResponseObject } = await client.private.updateUser({ + email: 'user@example.com', + username: 'username', + isSharingEmail: false, + isSharingAddress: false, + userData: {}, + country: 'SG', + languageCode: 'en', +}); +``` + +```json +{ + "user": { + "ethereumAddress": "0x0913017c740260fea4b2c62828a4008ca8b0d6e4", + "isRegistered": true, + "email": "email@dydx.exchange", + "username": "supersam15o", + "referredByAffiliateLink": null, + "makerFeeRate": "0.01", + "takerFeeRate": "0.01", + "makerVolume30D": "1000.00", + "takerVolume30D": "1000.00", + "fees30D": "00.50", + "userData": {}, + "dydxTokenBalance": "0", + "stakedDydxTokenBalance": "0", + "isEmailVerified": false, + "country": "SG", + "languageCode": "en" + } +} +``` + +### HTTP Request +`PUT v3/users` + +Description: Update user information and return the updated user. + +Parameter | Description +------------------| ----------- +userData | User metadata in a JSON blob. +email | (Optional) Email to be used with the user. +username | (Optional) Username to be used for the user. +isSharingUsername | (Optional) Share username publically on leaderboard rankings. +isSharingAddress | (Optional) Share ETH address publically on leaderboard rankings. +country | (Optional) Country of the user's residence. Must be [ISO 3166-1 Alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) compliant. +languageCode | (Optional) The user's preferred language. Must be [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) compliant, including 'zh-CN'. + +### Response + +Parameter | Description +---------------| ----------- +user | See [User](#get-user). + +## Get User Active Links +> Get User Active Links + +```python +links = client.private.get_user_links() +``` + +```typescript +const userLinks: UserLinksResponseObject = await client.private.getUserLinks(); +``` + +```json +{ + "userType": "SECONDARY", + "primaryAddress": "0x0913017c740260fea4b2c62828a4008ca8b0d6e4", + "secondaryAddresses": null +} +``` + +### HTTP Request +`GET v3/users/links` + +Description: return active user links. + +### Response + +Parameter | Description +------------------------| ----------- +userType | `PRIMARY`, `SECONDARY`, or null if no active links. +primaryAddress | Address of the `PRIMARY` user if `userType = SECONDARY`. null otherwise. +linkedAddresses | Addresses of the `SECONDARY` users if `userType = PRIMARY`. null otherwise. + +## Send User Link Request +> Send User Link Request + +```python +pending_links = client.private.send_link_request('CREATE_SECONDARY_REQUEST', '0x0913017c740260fea4b2c62828a4008ca8b0d6e4') +``` + +```typescript +const res: {} = await client.private.sendLinkRequest({ + action: LinkAction.REMOVE, + address: "0x0913017c740260fea4b2c62828a4008ca8b0d6e4" +}); +``` + +```json +{} +``` + +### HTTP Request +`POST v3/users/links` + +Description: Send a new request to link users, respond to a pending request, or remove a link. + +All DYDX rewards will be calculated and distributed to the primary address following the current rewards formulas. + +For trading rewards, all formula terms will be summed and aggregated across linked addresses, including fees paid, open interest, and stkDYDX. +For liquidity provider rewards, all formula terms will be summed and aggregated across linked addresses, including depth/score score, stkDYDX, and maker volume. For each market, the max uptime across linked addresses will be used. + +### Request + +Parameter | Description +------------------ | ----------- +action | `CREATE_SECONDARY_REQUEST`, `DELETE_SECONDARY_REQUEST`, `ACCEPT_PRIMARY_REQUEST`, `REJECT_PRIMARY_REQUEST`, or `REMOVE`. +address | Address that the link is with (should not be your own). + +### Response + +Parameter | Description +------------------------| ----------- +{} | Empty object upon success. + +### Link Actions + +Action | Description +-------------------------- | ----------- +`CREATE_SECONDARY_REQUEST` | Create a pending link request for the address to become `SECONDARY`, and your address to become `PRIMARY`. Request will be rejected if either address is already linked. +`DELETE_SECONDARY_REQUEST` | Delete an outgoing link request from your address. +`ACCEPT_PRIMARY_REQUEST` | Accept a pending link request for your address to become `SECONDARY` and their address to become `PRIMARY`. +`REJECT_PRIMARY_REQUEST` | Reject an incoming pending link request. +`REMOVE` | Remove an active link between your address and the other's. + +## Get User Pending Link Requests +> Get User Pending Link Requests + +```python +pending_links = client.private.get_user_pending_link_requests() +``` + +```typescript +const userPendingLinks: UserLinkRequestsResponseObject = await client.private.getUserPendingLinkRequests(); +``` + +```json +{ + "userType": null, + "outgoingRequests": [], + "incomingRequests": [ + { + "primaryAddress": "0x99b0599952a4fd2d1a1561fa4c010827ead30354", + "secondaryAddress": "0x0913017c740260fea4b2c62828a4008ca8b0d6e4" + } + ] +} +``` + +### HTTP Request +`GET v3/users/links/requests` + +Description: return pending user links. + +### Response + +Parameter | Description +------------------------| ----------- +userType | `PRIMARY`, `SECONDARY`, or null if no active links. +outgoingRequests | Outgoing requests for another user to be linked as `SECONDARY` to this user. null if `userType = SECONDARY`. +incomingRequests | Incoming requests for this user to be linked as `SECONDARY` to another user. null if `userType != null`. + +## Create An Account +> Create Account + +```python +client.private.create_account( + stark_public_key='701234abcd...', + stark_public_key_y_coordinate='1234abcd...', +) +``` + +```typescript +const account: { account: AccountResponseObject } = await client.private.createAccount( + '701234abcd...', // starkKey + '1234abcd...', // starkKeyYCoordinate +); +``` + +```json +{ + "account": { + "starkKey": "180913017c740260fea4b2c62828a4008ca8b0d6e4", + "positionId": "1812", + "equity": "10000", + "freeCollateral": "10000", + "quoteBalance": "10000", + "pendingDeposits": "0", + "pendingWithdrawals": "0", + "createdAt": "2021-04-09T21:08:34.984Z", + "openPositions": { + "BTC-USD": { + "market": "BTC-USD", + "status": "OPEN", + "side": "LONG", + "size": "1000", + "maxSize": "1050", + "entryPrice": "100", + "exitPrice": null, + "unrealizedPnl": "50", + "realizedPnl": "100", + "createdAt": "2021-01-04T23:44:59.690Z", + "closedAt": null, + "netFunding": "500", + "sumOpen": "1050", + "sumClose": "50" + } + }, + "accountNumber": "5", + "id": "id" + } +} +``` + + + +### HTTP Request +`POST v3/accounts` + +Description: Create an account with a given starkKey. + +### Request + +Parameter | Description +------------------ | ----------- +starkKey | Public starkKey associated with the key-pair you created. +starkKeyYCoordinate| Public starkKey Y-Coordinate associated with the key-pair you created. + + +### Response + +Parameter | Description +---------------| ----------- +account | See [Account](#get-account). + +## Get Account +> Get Account + +```python +account = client.private.get_account( + # Optional if eth_private_key or web3.eth.defaultAccount was provided. + ethereum_address='0x0123...', +) +``` + +```typescript +const account: { account: AccountResponseObject } = await client.private.getAccount( + '0x0123...', +); +``` + +```json +{ + "account": { + "starkKey": "180913017c740260fea4b2c62828a4008ca8b0d6e4", + "positionId": "1812", + "equity": "10000", + "freeCollateral": "10000", + "quoteBalance": "10000", + "pendingDeposits": "0", + "pendingWithdrawals": "0", + "createdAt": "2021-04-09T21:08:34.984Z", + "openPositions": { + "BTC-USD": { + "market": "BTC-USD", + "status": "OPEN", + "side": "LONG", + "size": "1000", + "maxSize": "1050", + "entryPrice": "100", + "exitPrice": null, + "unrealizedPnl": "50", + "realizedPnl": "100", + "createdAt": "2021-01-04T23:44:59.690Z", + "closedAt": null, + "netFunding": "500", + "sumOpen": "1050", + "sumClose": "50" + } + }, + "accountNumber": "5", + "id": "id" + } +} +``` + +### HTTP Request +`GET v3/accounts/:id` + +Description: Get an account for a user by id. Using the client, the id will be generated with client +information and an Ethereum address. + +### Request + +Parameter | Description +---------------| ----------- +ethereumAddress| Ethereum address associated with an account. + +### Response + +Parameter | Description +------------------| ----------- +starkKey | Public StarkKey associated with an account. +positionId | Starkware-specific positionId. +equity | The amount of equity (value) in the account. Uses balances and oracle-prices to calculate. +freeCollateral | The amount of collateral that is withdrawable from the account. +quoteBalance | Human readable quote token balance. Can be negative. +pendingDeposits | The sum amount of all pending deposits. +pendingWithdrawals| The sum amount of all pending withdrawal requests. +createdAt | When the account was first created in UTC. +openPositions | See [Positions](#get-positions). Note, markets where the user has no position are not returned in the map. +accountNumber | Unique accountNumber for the account. +id | Unique id of the account hashed from the userId and the accountNumber. + +## Get Account Leaderboard PNLs +> Get Account Leaderboard PNLs + +```typescript +const account: { accountPnls: AccountLeaderboardPnlResponseObject } = await client.private.getAccountLeaderboardPnl( + period=LeaderboardPnlPeriod.DAILY, +); +``` + +```json +{ + "absolutePnl": "100.000000", + "percentPnl": "100.000000", + "absoluteRank": 10, + "percentRank": 10, + "startedAt": "2021-08-01T00:00:00.000Z", + "endsAt": "2021-08-10T00:00:00.000Z", + "updatedAt": "2021-08-02T22:53:45.659Z", + "accountId": "afoo", + "period": "BRONZE", + "seasonExpectedOutcome": "PROMOTION", + "seasonNumber": 16, + "hedgieWon": null, + "prizeWon": "100000" +} +``` + +### HTTP Request +`GET v3/accounts/leaderboard-pnl/:period` + +Description: Get an account's personal leaderboard pnls. + +### Request + +Parameter | Description +-------------------| ----------- +period | "DAILY", "WEEKLY", "MONTHLY", "ALLTIME", "COMPETITION", "DAILY_COMPETITION", or "LEAGUES". +startingBeforeOrAt | (Optional) Latest the leaderboard starts at. + +### Response + +Parameter | Description +----------------------| ----------- +absolutePnl | The account's latest updated absolute PNL. +percentPnl | The account's latest updated percent PNL. +absoluteRank | User's absolute PNL rank. `null` if not ranked. +percentRank | User's percent PNL rank. `null` if not ranked. +startedAt | Starting time for this pnl. Note: will only be set if being used for a competition or leagues. Otherwise, this value will always be `null`. +endsAt | Ending time for this pnl. Note: will only be set if being used for a competition or leagues. Otherwise, this value will always be `null`. (Can be a future time.) +updatedAt | When these leaderboard PNLs were last updated. +accountId | The account the PNLs are for. +period | "DAILY", "WEEKLY", "MONTHLY", "ALLTIME", "COMPETITION", "BRONZE", "SILVER", "GOLD", "PLATINUM", "DIAMOND". +seasonExpectedOutcome | User's expected outcome of latest season. "PROMOTION", "DEMOTION", or "SAME_LEAGUE". `null` if not "LEAGUES". +seasonNumber | Leagues season number. Starts at 1. `null` if not "LEAGUES". +hedgieWon | Index of hedgie won. `null` if no hedgie won. +prizeWon | Amount of cash prize won in dollars. `null` if no prize won. + + +## Get Account Historical Leaderboard PNLs +> Get Account Historical Leaderboard PNLs + +```python +historical_leaderboard_pnls = client.private.get_historical_leaderboard_pnls("LEAGUES") +``` + +```typescript +const historicalLeaderboardPnls: HistoricalLeaderboardPnlsResponseObject = await client.private.getAccountHistoricalLeaderboardPnl( + period=AccountLeaderboardPnlPeriod.DAILY, +); +``` + +```json +{ + "leaderboardPnls" : [ + { + "absolutePnl": "100.000000", + "percentPnl": "100.000000", + "absoluteRank": 10, + "percentRank": 10, + "startedAt": "2021-08-01T00:00:00.000Z", + "endsAt": "2021-08-10T00:00:00.000Z", + "updatedAt": "2021-08-02T22:53:45.659Z", + "accountId": "afoo", + "period": "BRONZE", + "seasonOutcome": "PROMOTION", + "seasonNumber": 16, + "hedgieWon": null, + "prizeWon": "100000" + }, + ... + ], +} +``` + +### HTTP Request +`GET v3/accounts/historical-leaderboard-pnls/:period` + +Description: Get an account's historical leaderboard pnls. + +### Request + +Parameter | Description +-------------------| ----------- +period | Leaderboard period. "LEAGUES", "DAILY", or "DAILY_COMPETITION". +limit | Integer between 1 to 10, which indicates the number of most recent leaderboard pnls to be returned. By default this value will be 10. + +### Response + +Parameter | Description +----------------------| ----------- +leaderboardPnls | Array of "LeaderboardPnl" from oldest to most recent. See "LeaderboardPnl" below. + +### LeaderboardPnl + +Parameter | Description +----------------------| ----------- +absolutePnl | The account's latest updated absolute PNL. +percentPnl | The account's latest updated percent PNL. +absoluteRank | User's absolute PNL rank. `null` if not ranked. +percentRank | User's percent PNL rank. `null` if not ranked. +startedAt | Starting time for this pnl. Note: will only be set if being used for a competition or leagues. Otherwise, this value will always be `null`. +endsAt | Ending time for this pnl. Note: will only be set if being used for a competition or leagues. Otherwise, this value will always be `null`. (Can be a future time.) +updatedAt | When these leaderboard PNLs were last updated. +accountId | The account the PNLs are for. +period | Leaderboard period. "LEAGUES", "DAILY", or "DAILY_COMPETITION". +seasonExpectedOutcome | User's expected outcome of latest season. "PROMOTION", "DEMOTION", or "SAME_LEAGUE". `null` if not "LEAGUES". +seasonNumber | Leagues season number. Starts at 1. `null` if not "LEAGUES". +hedgieWon | Index of hedgie won. `null` if no hedgie won. +prizeWon | Amount of cash prize won in dollars. `null` if no prize won. + +## Get Accounts +> Get Account + +```python +accounts = client.private.get_accounts() +``` + +```typescript +const accounts: { accounts: AccountResponseObject[] } = await client.private.getAccounts(); +``` + +```json +{ "accounts": [{ + "starkKey": "180913017c740260fea4b2c62828a4008ca8b0d6e4", + "positionId": "1812", + "equity": "10000", + "freeCollateral": "10000", + "quoteBalance": "10000", + "pendingDeposits": "0", + "pendingWithdrawals": "0", + "createdAt": "2021-04-09T21:08:34.984Z", + "openPositions": { + "BTC-USD": { + "market": "BTC-USD", + "status": "OPEN", + "side": "LONG", + "size": "1000", + "maxSize": "1050", + "entryPrice": "100", + "exitPrice": null, + "unrealizedPnl": "50", + "realizedPnl": "100", + "createdAt": "2021-01-04T23:44:59.690Z", + "closedAt": null, + "netFunding": "500", + "sumOpen": "1050", + "sumClose": "50" + } + }, + "accountNumber": "5", + "id": "id" + }] +} +``` + +### HTTP Request +`GET v3/accounts` + +Description: Get all accounts for a user. + +### Response + +Parameter | Description +---------------| ----------- +accounts | See [Account](#get-account). Returns an array of Accounts. + +## Get Positions +>Get Positions + +```python +from dydx3.constants import MARKET_BTC_USD +from dydx3.constants import POSITION_STATUS_OPEN + +all_positions = client.private.get_positions( + market=MARKET_BTC_USD, + status=POSITION_STATUS_OPEN, +) +``` + +```typescript +const positions: { positions: PositionResponseObject[] } = await client.private.getPositions( + { + market: Market.BTC_USD, + status: PositionStatus.OPEN, + }, +); +``` + +```json +{ + "market": "BTC-USD", + "status": "OPEN", + "side": "LONG", + "size": "1000", + "maxSize": "1050", + "entryPrice": "100", + "exitPrice": null, + "unrealizedPnl": "50", + "realizedPnl": "100", + "createdAt": "2021-01-04T23:44:59.690Z", + "closedAt": null, + "netFunding": "500", + "sumOpen": "1050", + "sumClose": "50" +} +``` + +### HTTP Request +`GET v3/positions` + +Description: Get all current positions for a user by specified query parameters. + +For each market, a position is created with `status=OPEN`. A position is set to `status=CLOSED` when it goes to market-neutral (i.e. `size=0`). On a per-market basis, there should be at most one `status=OPEN` position at any given time. + +### Request + +Parameter | Description +------------------| ----------- +market | (Optional) Market of the position. +status | (Optional) Status of the position. Can be
OPEN
, CLOSED
or LIQUIDATED
.
+limit | (Optional) The maximum number of positions that can be fetched via this request. Note, this cannot be greater than 100.
+createdBeforeOrAt | (Optional) Set a date by which the positions had to be created.
+
+### Response
+
+Parameter | Description
+------------------| -----------
+market | The market of the position.
+status | The status of the position.
+side | The side of the position. LONG
or SHORT
.
+size | The current size of the position. Positive if long, negative if short, 0 if closed.
+maxSize | The maximum (absolute value) size of the position. Positive if long, negative if short.
+entryPrice | Average price paid to enter the position.
+exitPrice | Average price paid to exit the position.
+unrealizedPnl | The unrealized pnl of the position in quote currency using the market's [index-price](#index-price-sources) for the position to calculate.
+realizedPnl | The realized pnl of the position in quote currency.
+createdAt | Timestamp of when the position was opened.
+closedAt | Timestamp of when the position was closed.
+netFunding | Sum of all funding payments for this position.
+sumOpen | Sum of all trades sizes that increased the size of this position.
+sumClose | Sum of all trades sizes that decreased the size of this position.
+
+## Get Transfers
+> Get Transfers
+
+```python
+from dydx3.constants import ACCOUNT_ACTION_DEPOSIT
+
+transfers = client.private.get_transfers(
+ transfer_type=ACCOUNT_ACTION_DEPOSIT,
+ limit=50,
+)
+```
+
+```typescript
+const transfers: { transfers: TransferResponseObject[] } = await client.private.getTransfers(
+ {
+ type: AccountAction.DEPOSIT,
+ limit: 50,
+ },
+);
+```
+
+```json
+{
+ "transfers": [{
+ "id": "foo",
+ "type": "DEPOSIT",
+ "debitAsset": "USDC",
+ "creditAsset": "USDT",
+ "debitAmount": "3000",
+ "creditAmount": "2800",
+ "transactionHash": "hash",
+ "status": "PENDING",
+ "createdAt": "2021-01-04T23:44:59.690Z",
+ "confirmedAt": null,
+ "clientId": "foo",
+ "fromAddress": "0x0913017c740260fea4b2c62828a4008ca8b0d6e4",
+ "toAddress": null
+ }]
+}
+```
+
+### HTTP Request
+`GET v3/transfers`
+
+Description: Get transfers for a user, limited by query parameters.
+
+### Request
+
+Parameter | Description
+------------------| -----------
+transferType | (Optional) Type of the transfer. Can be DEPOSIT
, WITHDRAWAL
or FAST_WITHDRAWAL
.
+limit | (Optional) The maximum number of transfers that can be fetched via this request. Note, this cannot be greater than 100.
+createdBeforeOrAt | Latest that the transfers could have been created.
+
+### Response
+
+Parameter | Description
+------------------| -----------
+id | Unique id assigned by dYdX.
+type | Type of the transfer. Will be DEPOSIT
, WITHDRAWAL
or FAST_WITHDRAWAL
.
+debitAsset | Asset that was debited (USDC, USDT, USD, etc).
+creditAsset | Asset that was credited (USDC, USDT, USD, etc).
+debitAmount | Amount that was sent in for the deposit in debitAsset.
+creditAmount | Amount that was credited to the account in creditAsset.
+transactionHash | Ethereum transaction hash of the transfer.
+status | Status of the transfer. Will be PENDING
or CONFIRMED
.
+createdAt | Timestamp when created.
+confirmedAt | Timestamp when confirmed.
+clientId | ClientId of transfer.
+fromAddress | The Ethereum address the transfer is from.
+toAddress | The Ethereum address the transfer is for.
+
+## Fast vs. Slow Withdrawal
+
+The normal process for withdrawing from L2 to L1 requires waiting for a block of L2 transactions to be collected, and the zero-knowledge proof for the block to be constructed and verified on-chain.
+
+Using the fast withdrawal process, users can get their funds on L1 much faster by essentially trading their L2 funds to an “LP” account operated by dYdX, in order to receive immediate liquidity on L1. Since the LP must then recycle these funds from L2 to L1 via the regular withdrawal process, dYdX is only able to process a certain volume of fast withdrawals within a given period of time.
+
+## Create Withdrawal
+> Create Withdrawal
+
+```python
+from dydx3.constants import ASSET_USDC
+
+withdrawal = client.private.create_withdrawal(
+ position_id=1, # required for creating the withdrawal signature
+ amount='100',
+ asset=ASSET_USDC,
+ expiration_epoch_seconds=1613988637,
+)
+```
+
+```typescript
+const withdrawal: { withdrawal: TransferResponseObject } = await client.private.createWithdrawal(
+ {
+ amount: '100',
+ asset: Asset.USDC,
+ expiration: '2020-12-28T22:49:31.588Z',
+ },
+ '1', // positionId required for creating the withdrawal signature
+);
+```
+
+```json
+{
+ "withdrawal": {
+ "id": "foo",
+ "type": "WITHDRAWAL",
+ "debitAsset": "USDC",
+ "creditAsset": "USDC",
+ "debitAmount": "3000",
+ "creditAmount": "2800",
+ "transactionHash": "hash",
+ "status": "PENDING",
+ "createdAt": "2021-01-04T23:44:59.690Z",
+ "confirmedAt": null,
+ "clientId": "foo",
+ "fromAddress": "0x0913017c740260fea4b2c62828a4008ca8b0d6e4",
+ "toAddress": null
+ }
+}
+```
+
+### HTTP Request
+`POST v3/withdrawals`
+
+Description: Create a withdrawal from an account.
+
+
+
+An additional L1 transaction has to be sent to the Starkware contract to retrieve funds after a slow withdrawal. This cannot be done until the zero-knowledge proof for the block has been constructed and verified on-chain. For the L1 transaction, the Ethereum address that the starkKey is registered to must call either the [withdraw](https://github.com/dydxprotocol/starkex-eth/blob/master/src/contracts/starkware-perpetual-abi.json#L1802) or [withdrawTo](https://github.com/dydxprotocol/starkex-eth/blob/master/src/contracts/starkware-perpetual-abi.json#L1907) smart-contract functions. The contract ABI is not tied to a particular client but can be accessed via a [client](https://github.com/dydxprotocol/starkex-eth). All withdrawable funds are withdrawn at once.
+
+Both Layer 1 withdrawal methods can be accessed from [starkex-eth](https://github.com/dydxprotocol/starkex-eth/blob/master/src/modules/Exchange.ts).
+
+### Request
+
+Parameter | Description
+------------------| -----------
+amount | Amount to be withdrawn.
+asset | Asset being withdrawn. Can currently only be USDC
.
+expiration | Datetime at which the withdrawal expires if it has not been completed. Expiration must be at least seven days in the future.
+clientId | Unique id of the client associated with the withdrawal. Must be <= 40 characters. When using the client, if not included, will be randomly generated by the client.
+signature | The signature for the withdrawal, signed with the account's STARK private key. When using the client, if not included, will be done by the client. For more information see [above](#creating-and-signing-requests).
+
+### Response
+
+Parameter | Description
+------------------| -----------
+withdrawal | See [Transfers](#get-transfers).
+
+## Create Fast Withdrawal
+> Create Fast Withdrawal
+
+```python
+from dydx3.constants import ASSET_USDC
+
+fast_withdrawal = client.private.create_fast_withdrawal(
+ position_id='1', # required for creating the fast-withdrawal signature
+ credit_asset=ASSET_USDC,
+ credit_amount='100',
+ debit_amount='110',
+ to_address='0x98ab...',
+ lp_position_id='2',
+ expiration_epoch_seconds=1613988637,
+ signature='0abc12...', # Optional if stark_private_key was provided to client.
+)
+```
+
+```typescript
+const fastWithdrawal: { withdrawal: TransferResponseObject } = await client.private.createFastWithdrawal(
+ {
+ creditAsset: Asset.USDC,
+ creditAmount: '100',
+ debitAmount: '110',
+ toAddress: '0x98ab...',
+ lpPositionId: '2',
+ clientId: 'client',
+ signature: '0abc12...', // Optional if starkPrivateKey was provided to client.
+ },
+ '1', // positionId required for creating the fast-withdrawal signature
+);
+```
+
+```json
+{
+ "withdrawal": {
+ "id": "foo",
+ "type": "FAST_WITHDRAWAL",
+ "debitAsset": "USDC",
+ "creditAsset": "USDC",
+ "debitAmount": "3000",
+ "creditAmount": "2800",
+ "transactionHash": "hash",
+ "status": "PENDING",
+ "createdAt": "2021-01-04T23:44:59.690Z",
+ "confirmedAt": null,
+ "clientId": "foo",
+ "fromAddress": "0x0913017c740260fea4b2c62828a4008ca8b0d6e4",
+ "toAddress": null
+ }
+}
+```
+
+### HTTP Request
+`POST v3/fast-withdrawals`
+
+Description: Create a fast-withdrawal. [dYdX article on how fast withdrawals work](https://help.dydx.exchange/en/articles/4797387-how-do-deposits-and-withdrawals-work).
+
+### Request
+
+Parameter | Description
+------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+creditAsset | Asset being withdrawn. Can currently only be USDC
.
+creditAmount | Amount that is expected.
+debitAmount | Amount offered in USDC
for the credit amount.
+toAddress | Address to be credited.
+lpPositionId | LP Position Id of the debit account.
+expiration | Datetime at which the withdrawal expires if it has not been completed. Expiration must be at least seven days in the future.
+signature | Signature for the fast-withdrawal, signed with the account's STARK private key. When using the client, if not included, will be done by the client. For more information see [above](#creating-and-signing-requests).
+clientId | Unique id of the client associated with the fast-withdrawal. Must be <= 40 characters. When using the client, if not included, will be randomly generated by the client.
+
+`expectedCredit` is the result of computing `debitAmount` - min(`gas_fee`).
+
+### Response
+
+Parameter | Description
+------------------| -----------
+withdrawal | See [Transfers](#get-transfers).
+
+Returns 400 if `expectedCredit` != `creditAmount`.
+
+## Order Types
+
+Type | Description
+-------------- | -----------
+MARKET | Market order (must be FOK or IOC).
+LIMIT | Limit order.
+STOP | Stop limit order.
+TRAILING_STOP | Trailing stop limit order.
+TAKE_PROFIT | Take-profit order.
+LIQUIDATED | Indicates the account was liquidated (fills only).
+LIQUIDATION | Indicates the account took over a liquidated account (fills only).
+
+## Create A New Order
+
+> Create Order
+
+```python
+from dydx3.constants import MARKET_BTC_USD
+from dydx3.constants import ORDER_SIDE_SELL
+from dydx3.constants import ORDER_TYPE_LIMIT
+from dydx3.constants import TIME_IN_FORCE_GTT
+
+placed_order = client.private.create_order(
+ position_id=1, # required for creating the order signature
+ market=MARKET_BTC_USD,
+ side=ORDER_SIDE_SELL,
+ order_type=ORDER_TYPE_LIMIT,
+ post_only=False,
+ size='100',
+ price='18000',
+ limit_fee='0.015',
+ expiration_epoch_seconds=1613988637,
+ time_in_force=TIME_IN_FORCE_GTT,
+)
+```
+
+```typescript
+const order: { order: OrderResponseObject } = await client.private.createOrder(
+ {
+ side: OrderSide.SELL,
+ type: OrderType.LIMIT,
+ timeInForce: TimeInForce.GTT,
+ postOnly: false,
+ size: '100',
+ price: '18000',
+ limitFee: '0.015',
+ expiration: '2022-12-21T21:30:20.200Z',
+ },
+ '1', // required for creating the order signature
+);
+```
+
+```json
+{
+ "order": {
+ "id": "foo",
+ "clientId": "foo",
+ "accountId": "afoo",
+ "market": "BTC-USD",
+ "side": "SELL",
+ "price": "18000",
+ "triggerPrice": null,
+ "trailingPercent": null,
+ "size": "100",
+ "remainingSize": "100",
+ "type": "LIMIT",
+ "createdAt": "2021-01-04T23:44:59.690Z",
+ "unfillableAt": null,
+ "expiresAt": "2022-12-21T21:30:20.200Z",
+ "status": "PENDING",
+ "timeInForce": "GTT",
+ "postOnly": false,
+ "reduceOnly": false,
+ "cancelReason": null
+ }
+}
+```
+
+### HTTP Request
+`POST v3/orders`
+
+Description: Create a new order.
+
+### Request
+
+Parameter | Description
+-------------- | -----------
+market | Market of the order.
+side | Either BUY
or SELL.
+type | The type of order. This can be MARKET
, LIMIT
, STOP_LIMIT
, TRAILING_STOP
or TAKE_PROFIT
.
+postOnly | Whether the order should be canceled if it would fill immediately on reaching the matching-engine.
+size | Size of the order, in base currency (i.e. an ETH-USD position of size 1 represents 1 ETH).
+price | Worst accepted price of the base asset in USD.
+limitFee | Is the highest accepted fee for the trade. See [below](#order-limitfee) for more information.
+expiration | Time at which the order will expire if not filled. This is the Good-Til-Time and is accurate to a granularity of about 15 seconds.
+timeInForce | (Optional) One of GTT
(Good til time), FOK
(Fill or kill) or IOC
(Immediate or cancel). This will default to GTT
.
+cancelId | (Optional) The id of the order that is being replaced by this one.
+triggerPrice | (Optional) The triggerPrice at which this order will go to the matching-engine.
+trailingPercent| (Optional) The percent that the triggerPrice trails the [index price](#index-price-sources) of the market.
+reduceOnly | (Optional) Whether the order should be [reduce-only](#reduce-only). Only supported on FOK
(Fill or kill) or IOC
(Immediate or cancel) orders.
+clientId | Unique id of the client associated with the order. Must be <= 40 characters. When using the client, if not included, will be randomly generated by the client.
+signature | Signature for the order, signed with the account's STARK private key. When using the client, if not included, will be done by the client. For more information see [above](#creating-and-signing-requests).
+
+
+
+### Response
+
+Parameter | Description
+------------ | -----------
+order | See [order](#get-orders).
+
+## Order LimitFee
+
+The `limitFee` is the highest fee a user would be willing to accept on an order. This should be in decimal form (i.e. 0.1 is 10%). To see current fees, call [GET /v3/users](#get-user) and the maker/taker fee rates show what fees will be. If the order is `postOnly` dYdX will validate against `makerFeeRate` only. The opposite is true if the order is `FOK` or `IOC` - dYdX will only validate against `takerFeeRate`. Otherwise, dYdX assesses against the maximum of maker and taker fee rate.
+
+## Tick Size and Minimum Size
+
+### Tick Size
+
+Each market has a specified tickSize
. Order price
must be a multiple of the tickSize. The same applies to triggerPrice
and trailingPercent
if either of these are not null.
+
+### Minimum Size
+
+Each market has a specified minOrderSize
. Order size
must be not be less than the minOrderSize.
+
+## Order Deletion
+
+Canceled orders older than one month are deleted from the dYdX database.
+
+## Reduce Only
+
+A reduce-only order can only reduce an existing position.
+
+- When user holds no open position, a reduce-only order will always be rejected.
+- When user holds an open position, a reduce-only order can only be placed on the other side of the book, with size smaller or equal to the existing position size.
+
+The reduce-only option can be combined with any order type (Limit, Market, Stop Loss, Take Profit, Trailing Stop), but is only available for taker orders (Immediate-or-Cancel and Fill-or-Kill).
+
+`UNTRIGGERED` reduce-only orders are either resized or canceled (with cancel reason `REDUCE_ONLY_RESIZED`) if the underlying position shrinks or no longer exists. When there are multiple `UNTRIGGERED` reduce-only orders and the total order size exceeds the existing position, they will be resized/canceled starting from the order that will be filled last.
+
+
+
+## Cancel An Order
+> Cancel an order
+
+```python
+client.private.cancel_order(order_id='0x0000')
+```
+
+```typescript
+await client.private.cancelOrder('0x0000');
+```
+
+```json
+{}
+```
+
+### HTTP Request
+`DELETE v3/orders/:id`
+
+Description: Cancel an order by its unique id.
+
+### Request
+
+Parameter | Description
+---------------- | -----------
+orderId | Unique id of the order to be canceled.
+
+### Response
+
+
+
+Parameter | Description
+------------ | -----------
+cancelOrder | See [order](#get-orders).
+
+## Cancel Orders
+> Cancel Orders
+
+```python
+from dydx3.constants import MARKET_BTC_USD
+
+client.private.cancel_all_orders(market=MARKET_BTC_USD)
+```
+
+```typescript
+await client.private.cancelAllOrders(Market.BTC_USD);
+```
+
+
+```json
+{}
+```
+
+### HTTP Request
+`DELETE v3/orders`
+
+Description: Either bulk cancel all orders or just all orders for a specific market.
+
+### Request
+
+Parameter | Description
+--------------- | -----------
+market | (Optional) Market of the orders being canceled.
+
+### Response
+
+
+
+Parameter | Description
+------------ | -----------
+cancelOrders | Returns an array of orders to be canceled. See [order](#get-orders).
+
+## Cancel Active Orders
+> Cancel Active Orders
+
+```python
+from dydx3.constants import MARKET_BTC_USD
+from dydx3.constants import ORDER_SIDE_SELL
+
+market_side_orders = client.private.cancel_active_orders(
+ market=MARKET_BTC_USD,
+ side=ORDER_SIDE_SELL,
+)
+```
+
+```typescript
+const marketSideOrders: {
+ cancelOrders: ActiveOrderResponseObject[],
+} = await client.private.cancelActiveOrders(
+ {
+ market: Market.BTC_USD,
+ side: OrderSide.SELL,
+ },
+);
+```
+
+```json
+{
+ "cancelOrders": [
+ {
+ "id": "id",
+ "accountId": "afoo",
+ "market": "BTC-USD",
+ "side": "SELL",
+ "price": "29000",
+ "remainingSize": "0.500",
+ },
+ ...
+ ]
+}
+```
+
+### HTTP Request
+`DELETE v3/active-orders`
+
+Description: Cancel active orders that match request parameters.
+
+
+
+### Request
+
+Parameter | Description
+---------------- | -----------
+market | Market of the order.
+side | (Optional) Either BUY
or SELL. This parameter is required if `id` is included.
+id | (Optional) The unique id assigned by dYdX. Note, if id is not found, will return a 400.
+
+
+### Response
+
+
+
+
+Parameter | Description
+---------------- | -----------
+cancelOrders | Returns an array of active orders to be canceled. See [activeOrder](#get-active-orders).
+
+## Get Orders
+> Get Orders
+
+```python
+from dydx3.constants import MARKET_BTC_USD
+from dydx3.constants import ORDER_SIDE_SELL
+from dydx3.constants import ORDER_TYPE_LIMIT
+from dydx3.constants import ORDER_STATUS_OPEN
+
+all_orders = client.private.get_orders(
+ market=MARKET_BTC_USD,
+ status=ORDER_STATUS_OPEN,
+ side=ORDER_SIDE_SELL,
+ type=ORDER_TYPE_LIMIT,
+ limit=50,
+)
+```
+
+```typescript
+const allOrders: { orders: OrderResponseObject[] } = await client.private.getOrders(
+ {
+ market: Market.BTC_USD,
+ status: OrderStatus.OPEN,
+ side: OrderSide.SELL,
+ type: OrderType.LIMIT,
+ limit: 50,
+ },
+);
+```
+
+```json
+{
+ "orders": [
+ {
+ "id": "id",
+ "clientId": "foo",
+ "accountId": "afoo",
+ "market": "BTC-USD",
+ "side": "SELL",
+ "price": "29000",
+ "triggerPrice": null,
+ "trailingPercent": null,
+ "size": "0.500",
+ "remainingSize": "0.500",
+ "type": "LIMIT",
+ "createdAt": "2021-01-04T23:44:59.690Z",
+ "unfillableAt": null,
+ "expiresAt": "2021-02-04T23:44:59.690Z",
+ "status": "OPEN",
+ "timeInForce": "GTT",
+ "postOnly": false,
+ "cancelReason": null
+ },
+ ...
+ ]
+}
+```
+
+### HTTP Request
+`GET v3/orders`
+
+Description: Get active (not filled or canceled) orders for a user by specified parameters.
+
+### Request
+
+Parameter | Description
+---------------- | -----------
+market | (Optional) Market of the order.
+status | (Optional) A list of statuses to filter by. Must be in the subset PENDING|OPEN|UNTRIGGERED
.
+side | (Optional) Either BUY
or SELL.
+type | (Optional) The expected type of the order. This can be LIMIT
, STOP
, TRAILING_STOP
or TAKE_PROFIT
.
+limit | (Optional) The maximum number of orders that can be fetched via this request. Note, this cannot be greater than 100.
+createdBeforeOrAt| (Optional) Set a date by which the orders had to be created.
+returnLatestOrders| (Optional) Returns the most recently created orders instead of the oldest and the order is from most recent to least recent (up to `limit`).
+
+### Response
+
+Parameter | Description
+---------------- | -----------
+orders | An array of orders. See order below.
+
+### Order
+
+Parameter | Description
+---------------- | -----------
+id | The unique id assigned by dYdX.
+clientId | The unique id assigned by the client.
+accountId | The id of the account.
+market | Market of the fill.
+side | Either BUY
or SELL.
+price | The price of the order. Must adhere to the market's tick size.
+triggerPrice | The trigger price of the order. Must adhere to the market's tick size.
+trailingPercent | Used for trailing stops. Percent drop from maximum price that will trigger the order.
+size | Total size (base currency) of the order
+remainingSize | Size of order not yet filled.
+type | The [type](#order-types) of the fill.
+createdAt | Timestamp when the fill was created.
+unfillableAt | Time order was either filled or canceled.
+expiresAt | Time order will expire.
+status | See order statuses below.
+timeInForce | One of GTT
(Good til time), FOK
(Fill or kill) or IOC
(Immediate or cancel). This will default to GTT
.
+postOnly | If the order will cancel if it would take the position of TAKER
.
+cancelReason | See cancel reasons below.
+
+### Order Statuses
+
+Status | Description
+---------------- | -----------
+PENDING | Order has yet to be processed by the matching engine.
+OPEN | Order is active and on the orderbook. Could be partially filled.
+FILLED | Fully filled.
+CANCELED | Canceled, for one of the cancel reasons. Could be partially filled.
+UNTRIGGERED | Triggerable order that has not yet been triggered.
+
+### Cancel Reasons
+
+Reason | Description
+-------------------- | -----------
+UNDERCOLLATERALIZED | Order would have led to an undercollateralized state for the user.
+EXPIRED | Order expired.
+USER_CANCELED | Order was canceled by the user.
+SELF_TRADE | Order would have resulted in a self trade for the user.
+FAILED | An internal issue caused the order to be canceled.
+COULD_NOT_FILL | A FOK or IOC order could not be fully filled.
+POST_ONLY_WOULD_CROSS| A post-only order would cross the orderbook.
+
+## Get Active Orders
+> Get Active Orders
+
+```python
+from dydx3.constants import MARKET_BTC_USD
+from dydx3.constants import ORDER_SIDE_SELL
+
+market_side_orders = client.private.get_active_orders(
+ market=MARKET_BTC_USD,
+ side=ORDER_SIDE_SELL,
+)
+```
+
+```typescript
+const marketSideOrders: {
+ orders: ActiveOrderResponseObject[],
+} = await client.private.getActiveOrders(
+ {
+ market: Market.BTC_USD,
+ side: OrderSide.SELL,
+ },
+);
+```
+
+```json
+{
+ "orders": [
+ {
+ "id": "id",
+ "accountId": "afoo",
+ "market": "BTC-USD",
+ "side": "SELL",
+ "price": "29000",
+ "remainingSize": "0.500",
+ },
+ ...
+ ]
+}
+```
+
+### HTTP Request
+`GET v3/active-orders`
+
+Description: Get active (not filled or canceled) orders for a user by specified parameters.
+
+
+
+### Request
+
+Parameter | Description
+---------------- | -----------
+market | Market of the order.
+side | (Optional) Either BUY
or SELL. This parameter is required if `id` is included.
+id | (Optional) The unique id assigned by dYdX. Note, if id is not found, will return a 400.
+
+
+### Response
+
+Parameter | Description
+---------------- | -----------
+orders | An array of activeOrders. See activeOrder below.
+
+### ActiveOrder
+
+Parameter | Description
+---------------- | -----------
+id | The unique id assigned by dYdX.
+accountId | The id of the account.
+market | Market of the fill.
+side | Either BUY
or SELL.
+price | The price of the order. Must adhere to the market's tick size.
+remainingSize | Size of order not yet filled.
+
+## Get Order By Id
+>Get Order By Id
+
+```python
+order = client.private.get_order_by_id('foo')
+```
+
+```typescript
+const orderResponse: { order: OrderResponseObject } = await client.private.getOrderById('foo');
+```
+
+```json
+{
+ "order": {
+ "id": "foo",
+ "clientId": "foo",
+ "accountId": "afoo",
+ "market": "BTC-USD",
+ "side": "SELL",
+ "price": "29000",
+ "triggerPrice": null,
+ "trailingPercent": null,
+ "size": "0.500",
+ "remainingSize": "0.500",
+ "type": "LIMIT",
+ "createdAt": "2021-01-04T23:44:59.690Z",
+ "unfillableAt": null,
+ "expiresAt": "2021-02-04T23:44:59.690Z",
+ "status": "OPEN",
+ "timeInForce": "GTT",
+ "postOnly": false,
+ "cancelReason": null
+ }
+}
+```
+
+### HTTP Request
+`GET v3/orders/:id`
+
+Description: Get an order by id
from the active orderbook and order history.
+
+### Request
+
+Parameter | Description
+------------ | -----------
+id | Unique id of the order
+
+### Response
+
+Parameter | Description
+------------ | -----------
+order | See [order](#get-orders).
+
+## Get Order by ClientId
+>Get Order By ClientId
+
+```python
+order = client.private.get_order_by_client_id('clientId')
+```
+
+```typescript
+const allOrders: { order: OrderResponseObject } = await client.private.getOrderByClientId('clientId');
+```
+
+```json
+{
+ "order": {
+ "id": "foo",
+ "clientId": "foo",
+ "accountId": "afoo",
+ "market": "BTC-USD",
+ "side": "SELL",
+ "price": "29000",
+ "triggerPrice": null,
+ "trailingPercent": null,
+ "size": "0.500",
+ "remainingSize": "0.500",
+ "type": "LIMIT",
+ "createdAt": "2021-01-04T23:44:59.690Z",
+ "unfillableAt": null,
+ "expiresAt": "2021-02-04T23:44:59.690Z",
+ "status": "OPEN",
+ "timeInForce": "GTT",
+ "postOnly": false,
+ "cancelReason": null
+ }
+}
+```
+
+### HTTP Request
+`GET v3/orders/client/:id`
+
+Description: Get an order by clientId
from the active orderbook and order history. Only the latest 1 hour of orders can be fetched from this endpoint.
+
+### Request
+
+Parameter | Description
+------------ | -----------
+id | Unique clientId of the order
+
+### Response
+
+Parameter | Description
+------------ | -----------
+order | See [order](#get-orders).
+
+## Get Fills
+>Get Fills
+
+```python
+from dydx3.constants import MARKET_BTC_USD
+
+all_fills = client.private.get_fills(
+ market=MARKET_BTC_USD,
+)
+```
+
+```typescript
+const allFills: { fills: FillResponseObject[] } = await client.private.getFills(
+ {
+ market: Market.BTC_USD,
+ },
+);
+```
+
+```json
+{
+ "fills": [
+ {
+ "id": "foo",
+ "side": "BUY",
+ "liquidity": "TAKER",
+ "type": "LIMIT",
+ "market": "BTC-USD",
+ "orderId": "id",
+ "price": "29000",
+ "size": "0.001",
+ "fee": "100",
+ "createdAt": "2021-01-05T16:33:43.163Z"
+ },
+ ...
+ ]
+}
+```
+
+### HTTP Request
+`GET v3/fills`
+
+Description: Get Fills for a user by specified parameters.
+
+### Request
+
+Parameter | Description
+---------------- | -----------
+market | (Optional) Market of the fills.
+orderId | (Optional) Unique order id. Will only fetch a single order.
+limit | (Optional) The maximum number of fills that can be fetched via this request. Note, this cannot be greater than 100.
+createdBeforeOrAt| (Optional) Set a date by which the fills had to be created.
+
+### Response
+
+Parameter | Description
+---------------- | -----------
+fills | Array of fills. See below for an individual example.
+
+### Fill
+
+Parameter | Description
+---------------- | -----------
+id | The unique id assigned by dYdX.
+side | Either BUY
or SELL.
+liquidity | Either MAKER
or TAKER.
+type | The [type](#order-types) of the fill.
+market | Market of the fill.
+orderId | Id of the order which caused this fill. null if type is LIQUIDATED
or LIQUIDATION`
.
+price | The price the fill occurred at (in quote / base currency).
+size | Size that was filled (in base currency).
+fee | Fee that was charged (in quote currency).
+createdAt | Timestamp when the fill was created.
+
+## Get Funding Payments
+> Get Funding Payments
+
+```python
+from dydx3.constants import MARKET_BTC_USD
+
+funding_payments = client.private.get_funding_payments(
+ market=MARKET_BTC_USD,
+ limit=75,
+)
+```
+
+```typescript
+const fundingPayments: { fundingPayments: FundingResponseObject } = await client.private.getFundingPayments(
+ {
+ market: Market.BTC_USD,
+ limit: 75,
+ },
+);
+```
+
+```json
+{
+ "fundingPayments": [{
+ "market": "BTC-USD",
+ "payment": "10000",
+ "rate": "0.0000125000",
+ "positionSize": "500",
+ "price": "90",
+ "effectiveAt": "2021-01-04T23:44:59.690Z"
+ }]
+}
+```
+
+### HTTP Request
+`GET v3/funding`
+
+Description: Get Funding Payments made to an account.
+
+### Request
+
+Parameter | Description
+-------------------| -----------
+market | (Optional) Market of the funding payments.
+limit | (Optional) The maximum number of funding payments that can be fetched via this request. Note, this cannot be greater than 100.
+effectiveBeforeOrAt| (Optional) Set a date by which the funding payments had to be created.
+
+### Response
+
+Parameter | Description
+-------------------| -----------
+market | Market corresponding to the funding payment.
+payment | Change in the `quoteBalance` of the account. Positive if the user received funding and negative if the user paid funding.
+rate | Funding rate at the time of this payment (as a 1-hour rate).
+positionSize | User's position size at the time of this funding payment. positive if long, negative if short.
+price | Oracle price used to calculate this funding payment.
+effectiveAt | Time of this funding payment.
+
+## Get Historical PNL Ticks
+> Get Historical PNL Ticks
+
+```python
+historical_pnl = client.private.get_historical_pnl(
+ created_before_or_at='2021-04-09T22:02:46+0000',
+)
+```
+
+```typescript
+const historicalPnlTicks: {
+ historicalPnl: HistoricalPnlResponseObject[],
+} = await client.private.getHistoricalPnl(
+ {
+ createdBeforeOrAt: '2021-04-09T22:02:46+0000',
+ },
+);
+```
+
+```json
+{
+ "historicalPnl": [{
+ "equity": "0.0000",
+ "totalPnl": "0.0000",
+ "createdAt": "2021-04-09T21:08:34.984Z",
+ "netTransfers": "0.0000",
+ "accountId": "49979004..."
+ }]
+}
+```
+
+### HTTP Request
+`GET v3/historical-pnl`
+
+Description: Get Historical PNL for an account during an interval.
+
+
+
+### Request
+
+Parameter | Description
+-------------------| -----------
+createdBeforeOrAt | (Optional) Used for setting a ending bounds on the ticks.
+createdOnOrAfter | (Optional) Used for setting a starting bounds on the ticks.
+
+### Response
+
+Parameter | Description
+-------------------| -----------
+historicalPnl | Array of HistoricalAggregatedPnl. See "HistoricalAggregatedPnl" below.
+
+### HistoricalAggregatedPnl
+Parameter | Description
+-------------------| -----------
+equity | The total account equity.
+totalPnl | The total PNL for the account since inception.
+createdAt | When the tick was recorded.
+netTransfers | The value into or out of the account of transfers since the last interval.
+accountId | Account the tick is for.
+
+## Get Trading Rewards
+> Get Trading Rewards
+
+```python
+rewards = client.private.get_trading_rewards(
+ epoch=0,
+)
+```
+
+```typescript
+const rewards: {
+ tradingRewards: TradingRewardsResponseObject,
+} = await client.private.getTradingRewards(
+ {
+ epoch: 5,
+ },
+);
+```
+
+```json
+{
+ "epoch": 5,
+ "epochStart": "2021-12-21T15:00:00.000Z",
+ "epochEnd": "2022-01-18T15:00:00.000Z",
+ "fees": {
+ "feesPaid": "0.1",
+ "totalFeesPaid": "1"
+ },
+ "openInterest": {
+ "averageOpenInterest": "10",
+ "totalAverageOpenInterest": "100"
+ },
+ "stakedDYDX": {
+ "primaryStakedDYDX": "0",
+ "averageStakedDYDX": "200",
+ "averageStakedDYDXWithFloor": "200",
+ "totalAverageStakedDYDX": "2000"
+ },
+ "weight": {
+ "weight": "0.1",
+ "totalWeight": "1"
+ },
+ "totalRewards": "383561.6",
+ "estimatedRewards": "3835616"
+}
+```
+
+### HTTP Request
+`GET v3/rewards/weight`
+
+Description: Get the rewards weight of a given epoch.
+
+### Request
+
+Parameter | Description
+-------------------| -----------
+epoch | (Optional) Epoch number to request rewards data for. Defaults to current epoch.
+secondaryAddress | (Optional) Get rewards for a linked, `SECONDARY` address.
+
+### Response
+
+Parameter | Description
+-------------------| -----------
+epoch | ID of the epoch.
+epochStart | Time when the epoch starts.
+epochEnd | Time when the epoch ends.
+fees | See "Fees" below.
+openInterest | See "OpenInterest" below.
+weight | See "Weight" below.
+stakedDYDX | See "StakedDYDX" below.
+totalRewards | The total number of tokens that will be given out at the end of the epoch.
+estimatedRewards | The user's estimated number of dYdX tokens as rewards.
+
+### Weight
+Parameter | Description
+-------------------| -----------
+weight | The user's current weight score for this epoch.
+totalWeight | The sum of the weight score of all users for this epoch.
+
+### Fees
+Parameter | Description
+-------------------| -----------
+feesPaid | The USD amount of fees paid by the user in the epoch.
+totalFeesPaid | The total USD amount of fees paid by all users in the epoch.
+
+### OpenInterest
+Parameter | Description
+-------------------------| -----------
+averageOpenInterest | The average open interest for the user.
+totalAverageOpenInterest | The total average open interest for all users.
+
+### StakedDYDX
+Parameter |Description
+---------------------------|-----------
+primaryStakedDYDX | The average staked DYDX of the primary user if own user `linkType = SECONDARY` or `secondaryAddress` is included. `'0'` for epochs 0-4 (old rewards formula). `null` otherwise.
+averageStakedDYDX | The average staked DYDX for the user. This value is `'0'` for epochs 0-4 (old rewards formula does not take into account stakedDYDX).
+averageStakedDYDXWithFloor | The average staked DYDX for the user, taking into account both `primaryStakedDYDX` and the [rewards formula's](https://commonwealth.im/dydx/proposal/discussion/2940-drc-update-trading-liquidity-provider-rewards-formulas-to-include-holding-of-stkdydx) floor stakedDYDX value. This value is `'0'` for epochs 0-4 (old rewards formula does not take into account stakedDYDX).
+totalAverageStakedDYDX | The total average staked DYDX for all users. This value is `'0'` for epochs 0-4 (old rewards formula does not take into account stakedDYDX).
+
+## Get Liquidity Provider Rewards
+> Get Liquidity Provider Rewards
+
+
+
+```python
+rewards = client.private.get_liquidity_provider_rewards_v2(
+ epoch=13,
+)
+```
+
+```typescript
+const rewards: {
+ liquidityRewards: LiquidityProviderRewardsV2ResponseObject,
+} = await client.private.getLiquidityProviderRewardsV2(
+ {
+ epoch: 13,
+ },
+);
+```
+
+```json
+{
+ "epoch": 13,
+ "epochStart": "2021-8-02T15:00:00.000Z",
+ "epochEnd": "2022-08-30T15:00:00.000Z",
+ "markets": {
+ "BTC-USD": {
+ "market": "BTC-USD",
+ "depthSpreadScore": "0.5",
+ "uptime": "500",
+ "linkedUptime": "7500",
+ "maxUptime": "10000",
+ "score": "0.00098821176880261854125",
+ "totalScore": "1",
+ "makerVolume": "10000",
+ "totalMakerVolume": "100000",
+ "totalRewards": "230137",
+ "estimatedRewards": "227.42409183692822322765125",
+ "secondaryAllocation": "0"
+ }
+ ...
+ },
+ "stakedDYDX": {
+ "averageStakedDYDX": "1000",
+ "totalAverageStakedDYDX": "10000"
+ },
+ "linkedAddressRewards": {
+ "0x0913017c740260fea4b2c62828a4008ca8b0d6e4": {
+ "markets": {
+ "BTC-USD": {
+ "market": "BTC-USD",
+ "depthSpreadScore": "0.5",
+ "uptime": "500",
+ "linkedUptime": "7500",
+ "maxUptime": "10000",
+ "score": "0.00098821176880261854125",
+ "totalScore": "1",
+ "makerVolume": "10000",
+ "totalMakerVolume": "100000",
+ "totalRewards": "230137",
+ "estimatedRewards": "227.42409183692822322765125",
+ "secondaryAllocation": "0.5"
+ }
+ ...
+ },
+ "averageStakedDYDX": "750"
+ }
+ }
+}
+```
+
+### HTTP Request
+`GET v3/rewards/liquidity-provider`
+
+Description: Get the liquidity provider rewards of a given epoch (epochs 13+).
+
+### Request
+
+Parameter | Description
+-------------------| -----------
+epoch | (Optional) Epoch number to request rewards data for. Defaults to current epoch.
+
+### Response
+
+Parameter | Description
+---------------------| -----------
+epoch | ID of the epoch.
+epochStart | Time when the epoch starts.
+epochEnd | Time when the epoch ends.
+markets | Map of market name to liquidity rewards for that market. See "LiquidityRewards" below.
+stakedDYDX | See "StakedDYDX" below.
+linkedAddressRewards | For a `PRIMARY` address, map of linked address to rewards data for that address. Includes the `PRIMARY` address. See "PerAddressRewards" below.
+
+### LiquidityRewards
+Parameter | Description
+--------------------| -----------
+market | The market for which the rewards are for.
+depthSpreadScore | The user's depth and spread score for this market.
+uptime | The ratio of uptime (non-zero scores) that the user has for this market.
+linkedUptime | For a `SECONDARY` address, the max uptime of linked addresses, which will be used in rewards calculation. `0` otherwise.
+maxUptime | The number of samples taken for this market.
+score | The user's total score for this market.
+totalScore | The total score of all liquidity providers who are eligible for liquidity rewards.
+makerVolume | The maker volume for the user.
+totalMakerVolume | The total maker volume for all liquidity providers.
+estimatedRewards | The user's estimated number of dYdX tokens as rewards for this market. For a `SECONDARY` address, this field is the amount of rewards contributed to the `PRIMARY` address (`SECONDARY` addresses do not receive rewards).
+totalRewards | The total number of tokens that will be given out at the end of the epoch for this market.
+secondaryAllocation | For a `SECONDARY` address, the proportion (0 to 1) of the `PRIMARY` address rewards that are based on this address contribution. `0` otherwise.
+
+### StakedDYDX
+Parameter | Description
+-----------------------| -----------
+averageStakedDYDX | The average staked DYDX for the user. For a `PRIMARY` address, this is the aggregate `averageStakedDYDX` across all linked addresses.
+totalAverageStakedDYDX | The total average staked DYDX for all eligible users.
+
+### PerAddressRewards
+Parameter | Description
+-----------------------| -----------
+markets | Map of market name to liquidity rewards for that market for the respective address. See "LiquidityRewards" above.
+averageStakedDYDX | The average staked DYDX for the respective address.
+
+## Get Liquidity Rewards (Deprecated)
+> Get Liquidity Rewards
+
+
+
+```python
+rewards = client.private.get_liquidity_provider_rewards(
+ epoch=0,
+)
+```
+
+```typescript
+const rewards: {
+ liquidityRewards: LiquidityProviderRewardsResponseObject,
+} = await client.private.getLiquidityProviderRewards(
+ {
+ epoch: 5,
+ },
+);
+```
+
+```json
+{
+ "epoch": 5,
+ "epochStart": "2021-12-21T15:00:00.000Z",
+ "epochEnd": "2022-01-18T15:00:00.000Z",
+ "markets": {
+ "BTC-USD": {
+ "market": "BTC-USD",
+ "uptime": "0.5",
+ "score": "0.00098821176880261854125",
+ "totalScore": "1",
+ "totalRewards": "230137",
+ "estimatedRewards": "227.42409183692822322765125",
+ }
+ ...
+ },
+ "stakedDYDX": {
+ "primaryStakedDYDX": "0",
+ "averageStakedDYDX": "1000",
+ "totalAverageStakedDYDX": "10000"
+ }
+}
+```
+
+### HTTP Request
+`GET v3/rewards/liquidity`
+
+Description: Get the liquidity rewards of a given epoch.
+
+### Request
+
+Parameter | Description
+-------------------| -----------
+epoch | (Optional) Epoch number to request rewards data for. Defaults to current epoch.
+secondaryAddress | (Optional) Get rewards for a linked, `SECONDARY` address.
+
+### Response
+
+Parameter | Description
+-------------------| -----------
+epoch | ID of the epoch.
+epochStart | Time when the epoch starts.
+epochEnd | Time when the epoch ends.
+markets | Map of market name to rewards for that market. See "LiquidityRewards" below.
+stakedDYDX | See "StakedDYDX" below.
+
+### LiquidityRewards
+Parameter | Description
+-------------------| -----------
+market | The market for which the rewards are for.
+depthSpreadScore | The user's depth and spread score for this market.
+uptime | The ratio of uptime (non-zero scores) that the user has for this market.
+maxUptime | The number of samples taken for this market.
+score | The user's total score for this market.
+totalScore | The total score of all liquidity providers who are eligible for liquidity rewards.
+makerVolume | The maker volume for the user. `0` for epochs 0-9 (old rewards formulas).
+totalMakerVolume | The total maker volume for all liquidity providers. `0` for epochs 0-4 (old rewards formula).
+totalRewards | The total number of tokens that will be given out at the end of the epoch for this market.
+estimatedRewards | The user's estimated number of dYdX tokens as rewards for this market.
+
+### StakedDYDX
+Parameter | Description
+-----------------------| -----------
+primaryStakedDYDX | The average staked DYDX of the primary user if own user `linkType = SECONDARY` or if `secondaryAddress` is included. `'0'` for epochs 0-4 (old rewards formula). `null` otherwise.
+averageStakedDYDX | The average staked DYDX for the user. This value is `'0'` for epochs 0-4 (old rewards formula does not take into account stakedDYDX).
+totalAverageStakedDYDX | The total average staked DYDX for all eligible users. This value is `'0'` for epochs 0-4 (old rewards formula does not take into account stakedDYDX).
+
+## Get Retroactive Mining Rewards
+> Get Retroactive Mining Rewards
+
+```python
+rewards = client.private.get_retroactive_mining_rewards()
+```
+
+```typescript
+const rewards: {
+ retroactiveMiningRewards: RetroactiveMiningRewardsResponseObject,
+} = await client.private.getRetroactiveMiningRewards();
+```
+
+```json
+{
+ "epoch": 0,
+ "epochStart": "2021-08-03T15:00:00.000Z",
+ "epochEnd": "2021-08-31T15:00:00.000Z",
+ "retroactiveMining": {
+ "allocation": "1000",
+ "targetVolume": "500",
+ "volume": "100"
+ },
+ "estimatedRewards": "500"
+}
+```
+
+### HTTP Request
+`GET v3/rewards/retroactive-mining`
+
+Description: Get the retroactive mining rewards of a given epoch.
+
+### Response
+
+Parameter | Description
+-------------------| -----------
+epoch | Will always be '0'.
+epochStart | Time when the epoch starts.
+epochEnd | Time when the epoch ends.
+retroactiveMining | See "RetroactiveMiningRewards" below.
+estimatedRewards | The user's estimated number of dYdX tokens as rewards.
+
+### RetroactiveMiningRewards
+Parameter | Description
+-------------------| -----------
+allocation | The number of allocated dYdX tokens for the user.
+targetVolume | The user's required trade volume (in USD) to be able to claim the allocation.
+volume | The user's current total trade volume (in USD) in the epoch.
+
+## Send Verification Email
+> Send a verification email
+
+```python
+client.private.send_verification_email()
+```
+
+```typescript
+await client.private.sendVerificationEmail();
+```
+
+```json
+{}
+```
+
+### HTTP Request
+`PUT v3/emails/send-verification-email`
+
+Description: Send an email to the email address associated with the user, requesting that they click on a link to verify their email address.
+
+### Response
+
+On success, returns a `204` response with an empty body. Responds with a `400` error if no email address is on file for the user, or their email address has already been verified.
+
+### Setting Notification Status
+
+In addition to verifying an email, notifications must be set in `user.userData` to start receiving emails per category.
+
+> Example userData:
+
+```
+user.userData = {
+ notifications: {
+ deposit: {
+ email: true
+ },
+ trades: {
+ email: true
+ }
+ }
+}
+```
+
+## Request Testnet Tokens
+> Request Testnet Tokens
+
+```python
+transfer = client.private.request_testnet_tokens()
+```
+
+```typescript
+const transfer: { transfer: TransferResponseObject } = await client.private.requestTestnetTokens();
+```
+
+```json
+{
+ "transfer": {
+ "id": "e5ed0207-27fe-5cfa-a74e-b3908a113dca",
+ "type": "TRANSFER_OUT",
+ "debitAsset": "USDC",
+ "creditAsset": "USDC",
+ "debitAmount": "10000",
+ "creditAmount": "0",
+ "transactionHash": null,
+ "status": "PENDING",
+ "createdAt": "2021-11-09T01:29:59.960Z",
+ "confirmedAt": null,
+ "clientId": "521ba97550e9299",
+ "fromAddress": null,
+ "toAddress": null
+ }
+}
+```
+
+### HTTP Request
+`POST v3/testnet/tokens`
+
+Description: Requests tokens on dYdX's staging server.
+
+
+
+A fixed number of tokens will be transferred to the account. Please take note of [rate limits](#rate-limit-api).
+
+
+
+### Response
+
+Parameter | Description
+------------------| -----------
+transfer | See [Transfers](#get-transfers).
+
+## Get Private Profile
+Get private profile data for the user. This is a superset of the `/v3/profile/:publicId` endpoint.
+
+```python
+profile_private = client.private.get_profile()
+```
+
+```typescript
+const profilePrivate: ProfilePrivateResponseObject = await client.private.getProfilePrivate();
+```
+
+```json
+{
+ "username": "foo",
+ "ethereumAddress": "0x0913017c740260fea4b2c62828a4008ca8b0d6e4",
+ "DYDXHoldings": "250",
+ "stakedDYDXHoldings": "250",
+ "hedgiesHeld": [111],
+ "twitterHandle": "bar",
+ "affiliateLinks": [{
+ "link": "mrAffiliate",
+ "discountRate": "0.95",
+ }],
+ "affiliateApplicationStatus": "APPROVED",
+ "publicId": "ABCDEFGH",
+ "tradingLeagues": {
+ "currentLeague": "SILVER",
+ "currentLeagueRanking": 12,
+ },
+ "tradingPnls": {
+ "absolutePnl30D": "324",
+ "percentPnl30D": "25",
+ "volume30D": "4000",
+ },
+ "tradingRewards": {
+ "curEpoch": "8",
+ "curEpochEstimatedRewards": 280,
+ "prevEpochEstimatedRewards": 125,
+ },
+ "affiliateStatistics": {
+ "currentEpoch": {
+ "usersReferred": "12",
+ "revenue": "12.50",
+ "revenueShareRate": "0.24",
+ },
+ "previousEpochs": {
+ "usersReferred": "20",
+ "revenue": "1427.30",
+ },
+ "lastPaidEpoch": "9",
+ }
+}
+```
+
+### HTTP Request
+`GET v3/profile/private`
+
+Description: Get private profile data for the user.
+
+### Response
+
+Parameter | Description
+--------------------------| -----------
+username | Publically-displayed username.
+publicId | User's public id used in the public profile endpoint
+ethereumAddress | User's associated ethereum address.
+DYDXHoldings | The user's DYDX token holdings. `null` if not sharing ethereum address.
+stakedDYDXHoldings | The user's stkDYDX token holdings. `null` if not sharing ethereum address.
+hedgiesHeld | Indices of all Hedgies held.
+twitterHandle | The username that appears at the end of a unique Twitter url.
+affiliateLinks | The affiliate links that the user can share and earn revenue on. [] if the user is not an affiliate. See "AffiliateLinkData" below.
+affiliateApplicationStatus| The status of the affiliate application, can be `APPROVED`, `PENDING`, `REJECTED`, and `REJECTED_AND_BANNED`. `null` if no affiliate application had been submitted.
+tradingLeagues | See "TradingLeagues" below.
+tradingPnls | See "TradingPnls" below.
+tradingRewards | See "TradingRewards" below.
+affiliateStatistics | See "AffiliateStatistics" below.
+
+### AffiliateLinkData
+
+Parameter | Description
+--------------------| -----------
+link | The affiliate link. Ex: `mrAffiliate` in affiliate link `trade.dydx.exchange/r/mrAffiliate`.
+discountRate | The discount rate used to calculate the referred user's fee. Ex: `0.95` would mean that users get a 5% discount to their fees.
+
+### TradingLeagues
+
+Parameter | Description
+--------------------| -----------
+currentLeague | `null, "BRONZE", "SILVER", "GOLD", "PLATINUM", or "DIAMOND"`.
+currentLeagueRanking| `null`, or positive integer ranking.
+
+### TradingPnls
+
+Parameter | Description
+--------------------| -----------
+absolutePnl30D | `null`, or user's 30 day absolute pnl (based on leaderboard formula).
+percentPnl30D | `null`, or user's 30 day percent pnl (based on leaderboard formula).
+volume30D | The sum of a user's 30 day maker and taker trading volume.
+
+### TradingRewards
+
+Parameter | Description
+-------------------------| -----------
+curEpoch | Current epoch number.
+curEpochEstimatedRewards | The user's estimated number of dYdX tokens as rewards for the current epoch.
+prevEpochEstimatedRewards| The user's estimated number of dYdX tokens as rewards for the previous epoch.
+
+### AffiliateStatistics
+
+Parameter | Description
+-------------------------| -----------
+currentEpoch | See "CurrentEpochAffiliateStatistics" below.
+previousEpochs | See "PreviousEpochAffiliateStatistics" below.
+lastEpochPaid | The last epoch that has been paid out to affiliates.
+
+### CurrentEpochAffiliateStatistics
+
+Parameter | Description
+-------------------------| -----------
+usersReferred | Total number of users referred by this affiliate in this epoch.
+revenue | Expected current affiliate payout in this epoch.
+revenueShareRate | Current revenue share rate for the user depending on their $stkDYDX and if manual override is enabled for the user. Will be a number between 0 and 1 inclusive, 0.1 would indicate that the affiliate will receive 10% of all net revenue generated by their referred users.
+
+### PreviousEpochsAffiliateStatistics
+
+Parameter | Description
+-------------------------| -----------
+usersReferred | Total number of users referred by this affiliate in all previous epochs.
+revenue | Total amount of revenue this user has earned in all previous epochs.
diff --git a/slate-docs/source/includes/_public-v3.md b/slate-docs/source/includes/_public-v3.md
new file mode 100644
index 00000000..c71caab1
--- /dev/null
+++ b/slate-docs/source/includes/_public-v3.md
@@ -0,0 +1,1058 @@
+# Public HTTP API
+
+## Get Markets
+> Get Markets
+
+```python
+markets = client.public.get_markets()
+```
+
+```typescript
+const markets: { markets: MarketsResponseObject } = await client.public.getMarkets();
+```
+
+```json
+{
+ "markets": {
+ "LINK-USD": {
+ "market": "LINK-USD",
+ "status": "ONLINE",
+ "baseAsset": "LINK",
+ "quoteAsset": "USD",
+ "stepSize": "0.1",
+ "tickSize": "0.01",
+ "indexPrice": "12",
+ "oraclePrice": "101",
+ "priceChange24H": "0",
+ "nextFundingRate": "0.0000125000",
+ "nextFundingAt": "2021-03-01T18:00:00.000Z",
+ "minOrderSize": "1",
+ "type": "PERPETUAL",
+ "initialMarginFraction": "0.10",
+ "maintenanceMarginFraction": "0.05",
+ "baselinePositionSize": "1000",
+ "incrementalPositionSize": "1000",
+ "incrementalInitialMarginFraction": "0.2",
+ "volume24H": "0",
+ "trades24H": "0",
+ "openInterest": "0",
+ "maxPositionSize": "10000",
+ "assetResolution": "10000000",
+ "syntheticAssetId": "0x4c494e4b2d37000000000000000000",
+ },
+ ...
+}
+```
+
+### HTTP Request
+`GET v3/markets`
+
+Description: Get one or all markets as well as metadata about each retrieved market.
+
+### Request
+
+Parameter | Description
+------------------| -----------
+market | (Optional): Specific market to be fetched.
+
+### Response
+
+### Market
+
+Parameter | Description
+------------------| -----------
+markets | Map of market objects. See below for individual market.
+
+Parameter | Description
+-------------------------| -----------
+market | Symbol of the market.
+status | Status of the market. Can be one of ONLINE
, OFFLINE
, POST_ONLY
or CANCEL_ONLY
.
+baseAsset | Symbol of the base asset. e.g. "BTC".
+quoteAsset | Symbol of the quote asset. e.g. "BTC".
+stepSize | The minimum step size (in base currency) of trade sizes for the market.
+tickSize | The Tick size of the market.
+indexPrice | The current [index price](#index-price-sources) of the market.
+oraclePrice | The current oracle price of the market.
+priceChange24H | The absolute price change of the [index price](#index-price-sources) over the past 24 hours.
+nextFundingRate | The predicted next funding rate (as a 1-hour rate). Can be up to 5 seconds delayed.
+nextFundingAt | The timestamp of the next funding update.
+minOrderSize | Minimum order size for the market.
+type | Type of the market. This will always be PERPETUAL
for now.
+initialMarginFraction | The margin fraction needed to open a position.
+maintenanceMarginFraction| The margin fraction required to prevent liquidation.
+baselinePositionSize|The max position size (in base token) before increasing the initial-margin-fraction.
+incrementalPositionSize|The step size (in base token) for increasing the `initialMarginFraction` by (`incrementalInitialMarginFraction` per step).
+incrementalInitialMarginFraction|The increase of `initialMarginFraction` for each `incrementalPositionSize` above the `baselinePositionSize` the position is.
+maxPositionSize | The max position size for this market in base token.
+volume24H | The USD volume of the market in the previous 24 hours.
+trades24H | The number of trades in the market in the previous 24 hours.
+openInterest | The open interest in base token.
+assetResolution | The asset resolution is the number of quantums (Starkware units) that fit within one "human-readable" unit of the asset.
+syntheticAssetId | The id of the synthetic asset traded in the market. Only used for cryptographically signing orders.
+
+## Get Orderbook
+> Get Orderbook
+
+```python
+from dydx3.constants import MARKET_BTC_USD
+
+orderbook = client.public.get_orderbook(
+ market=MARKET_BTC_USD,
+)
+```
+
+```typescript
+const orderbook: OrderbookResponseObject = await client.public.getOrderbook(
+ Market.BTC_USD,
+);
+```
+
+```json
+{
+ "bids": [
+ {
+ "price": "29000",
+ "size": "1"
+ },
+ ...
+ ],
+ "asks": [
+ {
+ "price": "29500",
+ "size": "0.499"
+ },
+ ...
+ ]
+}
+```
+
+### HTTP Request
+`GET v3/orderbook/:market`
+
+
+
+Description: Returns the active orderbook for a market. All bids and asks that are fillable are returned.
+
+### Request
+
+Parameter | Description
+----------------- | -----------
+market | Market of the Orderbook.
+
+### Response
+
+Parameter | Description
+----------------- | -----------
+bids | See Orderbook Order below. Sorted by price in descending order.
+asks | See Orderbook Order below. Sorted by price in ascending order.
+
+### Orderbook Order
+
+Parameter | Description
+----------------- | -----------
+price | The price of the order (in quote / base currency).
+size | The size of the order (in base currency).
+
+## Get Trades
+> Get Trades
+
+```python
+from dydx3.constants import MARKET_BTC_USD
+
+all_trades = client.public.get_trades(
+ market=MARKET_BTC_USD,
+)
+```
+
+```typescript
+const trades: { trades: Trade[] } = await client.public.getTrades({
+ market: Market.BTC_USD,
+ startingBeforeOrAt: "2021-01-05T17:33:43.163Z",
+ limit: 1,
+});
+```
+
+```json
+{
+ "trades": [
+ {
+ "side": "BUY",
+ "size": "0.001",
+ "price": "29000",
+ "createdAt": "2021-01-05T16:33:43.163Z",
+ "liquidation": false
+ },
+ ...
+ ]
+}
+```
+
+### HTTP Request
+`GET v3/trades/:market`
+
+Description: Get Trades by specified parameters. Passing in all query parameters to the HTTP endpoint would look like: `GET v3/trades/BTC-USD?startingBeforeOrAt=2021-09-05T17:33:43.163Z&limit=1`.
+
+
+
+### Request
+
+Parameter | Description
+----------------- | -----------
+market | Market of the trades.
+startingBeforeOrAt| (Optional): Set a date by which the trades had to be created.
+limit | (Optional): The number of candles to fetch (Max 100).
+
+### Response
+
+Parameter | Description
+----------------- | -----------
+trades | An array of trades. See trade below
+
+### Trade
+
+Parameter | Description
+----------------- | -----------
+side | Either BUY
or SELL.
+size | The size of the trade.
+price | The price of the trade.
+createdAt | The time of the trade.
+liquidation | true
if the trade was the result of a liquidation. false
otherwise.
+
+## Get Fast Withdrawal Liquidity
+> Get Fast Withdrawal Liquidity
+
+```python
+fast_withdrawals_info = client.public.get_fast_withdrawal()
+```
+
+```typescript
+const availableFundsMap: {
+ liquidityProviders: {
+ [positionId: string]: {
+ availableFunds: string,
+ starkKey: string,
+ quote: {
+ creditAsset: string,
+ creditAmount: string,
+ debitAmount: string,
+ } | null,
+ }
+ }
+} = await client.public.getFastWithdrawalAvailableFunds();
+```
+
+```json
+{
+ "liquidityProviders": {
+ "1812": {
+ "availableFunds": "1000",
+ "starkKey": "180913017c740260fea4b2c62828a4008ca8b0d6e4",
+ "quote": null,
+ },
+ }
+}
+```
+
+### HTTP Request
+`GET v3/fast-withdrawals`
+
+Description: Returns a map of all LP provider accounts that have available funds for fast withdrawals. Given a `debitAmount` and asset the user wants sent to L1, this endpoint also returns amount of the desired asset the user will be credited on L1. Given a `creditAmount` and asset the user wants sent to L1, this endpoint also returns the amount the user will be debited on L2.
+
+### Request
+
+Parameter | Description
+-------------| -----------
+creditAsset | (Optional): The asset that would be sent to the user. Required if creditAmount or debitAmount are set.
+creditAmount | (Optional): Set this value if the user wants a quote based on the creditAmount.
+debitAmount | (Optional): Set this value if the user wants a quote based on the debitAmount.
+
+
+
+### Response
+
+Parameter | Description
+-------------------| -----------
+liquidityProviders | Map of LP position IDs to [Liquidity Provider](#liquidity-provider).
+
+### Liquidity Provider
+
+Field | Description
+---------------| -----------
+availableFunds | The funds available for the LP.
+starkKey | The public stark key for the LP.
+quote | The [Liquidity Provider Quote](#liquidity-provider-quote) given the user's request. Null if no request from the user or the request is unfillable by this LP.
+
+### Liquidity Provider Quote
+
+Field | Description
+-------------| -----------
+creditAsset | The asset that would be sent to the user on L1.
+creditAmount | The amount of creditAsset that would be sent to the user (human readable).
+debitAmount | The amount of USD that would be deducted from the users L2 account (human readable).
+
+
+## Get Market Stats
+> Get Market Stats
+
+```python
+from dydx3.constants import MARKET_BTC_USD
+
+market_statistics = client.public.get_stats(
+ market=MARKET_BTC_USD,
+ days=MARKET_STATISTIC_DAY_SEVEN,
+)
+```
+
+```typescript
+const marketStatistics = await client.public.getStats({
+ market: Market.BTC_USD,
+ days: MarketStatisticDay.SEVEN,
+});
+```
+
+```json
+{
+ "markets": {
+ "ETH-USD": {
+ "market": "ETH-USD",
+ "open": "1100",
+ "close": "1100",
+ "high": "1100",
+ "low": "1095",
+ "baseVolume": "10000",
+ "quoteVolume": "100000",
+ "type": "PERPETUAL",
+ "fees": "1000"
+ }
+ }
+}
+```
+
+### HTTP Request
+`GET v3/stats/:market`
+
+Description: Get an individual market's statistics over a set period of time or all available periods of time.
+
+### Request
+
+Parameter | Description
+------------------| -----------
+market | Market whose statistics are being fetched.
+days | (Optional): Specified day range for the statistics to have been compiled over. Can be one of `1`, `7`, `30`. Defaults to `1`.
+
+### Response
+
+Parameter | Description
+------------------| -----------
+markets | Map of market to MarketStats. See example below.
+
+### MarketStats
+
+Parameter | Description
+------------------| -----------
+market | The symbol of the market, e.g. ETH-USD.
+open | The open price of the market.
+high | The high price of the market.
+low | The low price of the market.
+close | The close price of the market.
+baseVolume | The total amount of base asset traded.
+quoteVolume | The total amount of quote asset traded.
+type | Type of the market. This will always be PERPETUAL
for now.
+
+## Get Historical Funding
+> Get Historical Funding
+
+```python
+from dydx3.constants import MARKET_BTC_USD
+
+historical_funding = client.public.get_historical_funding(
+ market=MARKET_BTC_USD,
+)
+```
+
+```typescript
+const historicalFunding = await client.public.getHistoricalFunding({
+ market: Market.BTC_USD,
+});
+```
+
+```json
+{
+ "historicalFunding": [
+ {
+ "market": "BTC-USD",
+ "rate": "0.0000125000",
+ "price": "31297.5000008009374142",
+ "effectiveAt": "2021-01-05T09:10:49.000Z"
+ },
+ ...
+ ]
+}
+```
+
+### HTTP Request
+`GET v3/historical-funding/:market`
+
+Description: Get the historical funding rates for a market.
+
+### Request
+
+Parameter | Description
+-------------------| -----------
+market | Market whose historical funding rates are being fetched.
+effectiveBeforeOrAt| (Optional): Set a date by which the historical funding rates had to be created.
+
+### Response
+
+Parameter | Description
+-------------------| -----------
+historicalFunding | Array of HistoricalFunding. See below for individual example.
+
+### Historical Funding
+
+Parameter | Description
+-------------------| -----------
+market | Market for which to query historical funding.
+rate | The funding rate (as a 1-hour rate).
+price | Oracle price used to calculate the funding rate.
+effectiveAt | Time at which funding payments were exchanged at this rate.
+
+## Get Candles for Market
+> Get Candles for Market
+
+```python
+from dydx3.constants import MARKET_BTC_USD
+
+candles = client.public.get_candles(
+ market=MARKET_BTC_USD,
+ resolution='1DAY',
+)
+```
+
+```typescript
+const candles: {
+ candles: CandleResponseObject,
+} = await client.public.getCandles({
+ market: Market.BTC_USD,
+ resolution: CandleResolution.1DAY,
+})
+```
+
+```json
+ "candles": [
+ {
+ "startedAt": "2021-01-05T00:00:00.000Z",
+ "updatedAt": "2021-01-05T00:00:00.000Z",
+ "market": "BTC-USD",
+ "resolution": "1DAY",
+ "low": "40000",
+ "high": "45000",
+ "open": "45000",
+ "close": "40000",
+ "baseTokenVolume": "1.002",
+ "trades": "3",
+ "usdVolume": "45085",
+ "startingOpenInterest": "28"
+ },
+ ...
+ ]
+```
+
+### HTTP Request
+`GET v3/candles/:market`
+
+Description: Get the candle statistics for a market.
+
+### Request
+
+Parameter | Description
+-------------------| -----------
+market | Market whose candles are being fetched.
+resolution | (Optional): Specific candle resolution being fetched. Can be one of 1DAY
, 4HOURS
, 1HOUR
, 30MINS
, 15MINS
, 5MINS
, 1MIN
.
+fromISO | (Optional): Starting point for the candles.
+toISO | (Optional): Ending point for the candles.
+limit | (Optional): The number of candles to fetch (Max 100).
+
+### Response
+
+Parameter | Description
+---------------------| -----------
+startedAt | When the candle started, time of first trade in candle.
+updatedAt | When the candle was last updated
+market | Market the candle is for.
+resolution | Time-period of candle (currently 1HOUR or 1DAY).
+low | Low trade price of the candle.
+high | High trade price of the candle.
+open | Open trade price of the candle.
+close | Close trade price of the candle.
+baseTokenVolume | Volume of trade in baseToken currency for the candle.
+trades | Count of trades during the candle.
+usdVolume | Volume of trade in USD for the candle.
+startingOpenInterest | The open interest in baseToken at the start of the candle.
+
+## Get Global Configuration Variables
+
+```python
+config = client.public.get_config()
+```
+
+```typescript
+const config: ConfigResponseObject = await client.public.getConfig();
+```
+
+```json
+ {
+ "collateralAssetId": "0x02c04d8b650f44092278a7cb1e1028c82025dff622db96c934b611b84cc8de5a",
+ "collateralTokenAddress": "0x8707a5bf4c2842d46b31a405ba41b858c0f876c4",
+ "defaultMakerFee": "0.0005",
+ "defaultTakerFee": "0.001",
+ "exchangeAddress": "0x014F738EAd8Ec6C50BCD456a971F8B84Cd693BBe",
+ "maxExpectedBatchLengthMinutes": "240",
+ "maxFastWithdrawalAmount": "200000",
+ "cancelOrderRateLimiting": {
+ "maxPointsMulti": 3,
+ "maxPointsSingle": 8500,
+ "windowSecMulti": 10,
+ "windowSecSingle": 10
+ },
+ "placeOrderRateLimiting": {
+ "maxPoints": 1750,
+ "windowSec": 10,
+ "targetNotional": 40000,
+ "minLimitConsumption": 4,
+ "minMarketConsumption": 20,
+ "minTriggerableConsumption": 100,
+ "maxOrderConsumption": 100
+ }
+ }
+```
+
+### HTTP Request
+`GET v3/config`
+
+Description: Get any global configuration variables for the exchange as a whole.
+
+### Response
+
+Parameter | Description
+----------------------------- | -----------
+collateralAssetId | The assetId of the collateral asset in the Starkware system.
+collateralTokenAddress | The address of the token used as collateral.
+defaultMakerFee | The default maker fee for new accounts.
+defaultTakerFee | The default taker fee for new accounts.
+exchangeAddress | The address of the exchange contract.
+maxExpectedBatchLengthMinutes | The maximum expected time between batches L2 (in minutes).
+maxFastWithdrawalAmount | The maximum amount (in USDC) allowed for fast withdrawals.
+cancelOrderRateLimiting | See `cancelOrderRateLimiting` below.
+placeOrderRateLimiting | See `placeOrderRateLimiting` below.
+
+### cancelOrderRateLimiting
+
+Parameter | Description
+----------------------------- | -----------
+maxPointsMulti | The number of rate limiting points given per window for canceling multiple orders.
+maxPointsSingle | The number of rate limiting points given per window for canceling single orders.
+windowSecMulti | The length of a rate limiting window for canceling multiple orders, in seconds.
+windowSecSingle | The length of a rate limiting window for canceling single orders, in seconds.
+
+### placeOrderRateLimiting
+
+Parameter | Description
+----------------------------- | -----------
+maxPoints | The number of rate limiting points given per window.
+windowSec | The length of a rate limiting window, in seconds.
+targetNotional | The `(size * price)` target used for determining points consumption.
+minLimitConsumption | The minimum number of points used when placing a limit order.
+minMarketConsumption | The minimum number of points used when placing a market order.
+minTriggerableConsumption | The minimum number of points used when placing a triggerable (e.g. stop-loss) order.
+maxOrderConsumption | The maximum number of points used when placing an order.
+
+## Check If User Exists
+> Check If User Exists
+
+```python
+user_exists = client.public.check_if_user_exists(
+ ethereum_address='foo',
+)
+```
+
+```typescript
+const userExists: { exists: boolean } = await client.public.doesUserExistWithAddress(
+ 'foo',
+);
+```
+
+```json
+{
+ "exists": true
+}
+```
+
+### HTTP Request
+`GET v3/users/exists`
+
+Description: Check if a user exists for a given Ethereum address.
+
+### Request
+
+Parameter | Description
+-------------- | -----------
+ethereumAddress| Ethereum address that the user would be associated with.
+
+### Response
+
+Parameter | Description
+-------------- | -----------
+exists | If a user exists for the given Ethereum address.
+
+## Check If Username Exists
+> Check If Username Exists
+
+```python
+username_exists = client.public.check_if_username_exists(
+ username='username',
+)
+```
+
+```typescript
+const usernameExists: { exists: boolean } = await client.public.doesUserExistWithUsername(
+ 'username',
+);
+```
+
+```json
+{
+ "exists": true
+}
+```
+
+### HTTP Request
+`GET v3/usernames`
+
+Description: Check if a username has been taken by a user.
+
+### Request
+
+Parameter | Description
+---------- | -----------
+username | Unique username being checked.
+
+### Response
+
+Parameter | Description
+-------------- | -----------
+exists | If a username has been taken by any user.
+
+## Get API Server Time
+> Get API Server Time
+
+```python
+time_object = client.public.get_time()
+```
+
+```typescript
+const time: { time: { iso: string, epoch: number } } = await client.public.getTime();
+```
+
+```json
+{
+ "iso": "2021-02-02T18:35:45Z",
+ "epoch": "1611965998.515",
+}
+```
+
+### HTTP Request
+`GET v3/time`
+
+Description: Get the current time of the API server.
+
+### Response
+
+Parameter | Description
+-------------- | -----------
+iso | ISO time of the server in UTC.
+epoch | Epoch time in seconds with milliseconds.
+
+## Get Public Leaderboard PNLs
+> Get Public Leaderboard PNLs
+
+```typescript
+const leaderboardPnls: { pnls: LeaderboardPnlResponseObject } = await client.public.getLeaderboardPnls(
+ period=LeaderboardPnlPeriod.WEEKLY,
+ sortBy=LeaderboardPnlSortBy.ABSOLUTE,
+ limit=10,
+);
+```
+
+```json
+{
+ "prizePool": 50000,
+ "numHedgiesWinners": 1,
+ "numPrizeWinners": 50,
+ "ratioPromoted": 0.25,
+ "ratioDemoted": 0.5,
+ "minimumEquity": 500,
+ "minimumDYDXTokens": 0,
+ "seasonNumber": 16,
+ "topPnls": [
+ {
+ "username": "user",
+ "ethereumAddress": "0x3408105669f73e814be44cbf598679a50eb2f7ed",
+ "publicId": "ABCDEFG",
+ "absolutePnl": "10206.971314",
+ "percentPnl": "0.409100",
+ "absoluteRank": 20,
+ "percentRank": 1,
+ "seasonExpectedOutcome": "SAME_LEAGUE",
+ "hedgieWon": null,
+ "prizeWon": null
+ },
+ ...
+ ],
+ "numParticipants": 1,
+ "updatedAt": "2022-02-02T15:31:10.813Z",
+ "startedAt": "2022-02-01T15:30:00.000Z",
+ "endsAt": "2022-02-02T15:30:00.000Z"
+}
+```
+
+### HTTP Request
+`GET v3/leaderboard-pnl`
+
+
+
+Description: Get the top PNLs for a specified period and how they rank against each other.
+
+### Request
+
+Parameter | Description
+------------------ | -----------
+period | "DAILY", "WEEKLY", "MONTHLY", "ALLTIME", "COMPETITION", "DAILY_COMPETITION", or "LEAGUES".
+startingBeforeOrAt | Latest the leaderboard starts at.
+sortBy | Which PNL to sort ranks by. "ABSOLUTE" or "PERCENT".
+limit | (Optional): The number of leaderboard PNLs to fetch (Max 100).
+
+### Response
+
+Parameter | Description
+----------------- | -----------
+topPnls | Array of PNLForPeriod (see below).
+numParticipants | Number of participants in this leaderboard. Includes ranked and unranked participants.
+startedAt | Starting time for this pnl. Note: will only be set if being used for a competition or leagues. Otherwise, this value will always be `null`.
+endsAt | Ending time for this pnl. Note: will only be set if being used for a competition or leagues. Otherwise, this value will always be `null`. (Can be a future time.)
+updatedAt | The time this pnl was updated.
+seasonNumber | Trading leagues season number. Starts at 1. `null` if not leagues.
+prizePool | Prize pool size for period. `null` if not "COMPETITION" or leagues.
+numHedgiesWinners | Number of hedgies winners for league. `null` if not a leagues period.
+numPrizeWinners | Number of prize winners for league. `null` if not a leagues period.
+ratioPromoted | Ratio of users promoted for league. `null` if not a leagues period.
+ratioDemoted | Ratio of users demoted for league. `null` if not a leagues period.
+minimumEquity | Minimum account equity required to join league. `null` if not a leagues period.
+minimumDYDXTokens | Minimum user DYDX + stkDYDX Token balance required to join league. `null` if not a leagues period.
+numHedgiesWinners | Number of hedgies prizes for period. `null` if not leagues.
+
+#### PNLForPeriod
+
+Parameter | Description
+--------------------- | -----------
+username | Publically-displayed username. `null` if not sharing.
+ethereumAddress | User's associated ethereum address. `null` if not sharing.
+publicId | User's public id used in the public profile endpoint.
+absolutePnl | The PNL (in USD) for the specified period. Sorted DESC for "ABSOLUTE" sortBy.
+percentPnl | The percent PNL for the specified period. Sorted DESC for "PERCENT" sortBy.
+absoluteRank | User's absolute PNL rank.
+percentRank | User's percent PNL rank.
+seasonExpectedOutcome | User's expected outcome of latest season. "PROMOTION", "DEMOTION", or "SAME_LEAGUE". `null` if not leagues.
+
+## Get Public Retroactive Mining Rewards
+> Get Public Retroactive Mining Rewards
+
+```python
+rewards = client.public.get_public_retroactive_mining_rewards(
+ ethereum_address='foo',
+)
+```
+
+```typescript
+const rewards: PublicRetroactiveMiningRewardsResponseObject = await client.public.getPublicRetroactiveMiningRewards(
+ 'foo'
+);
+```
+
+```json
+{
+ "allocation": "0",
+ "targetVolume": "0"
+}
+```
+
+### HTTP Request
+`GET v3/rewards/public-retroactive-mining`
+
+Description: Get the retroactive mining rewards for an ethereum address.
+
+### Request
+
+Parameter | Description
+--------------- | -----------
+ethereumAddress | An Ethereum address.
+
+### Response
+
+Parameter | Description
+------------------ | -----------
+allocation | The number of allocated dYdX tokens for the address.
+targetVolume | The addresses' required trade volume (in USD) to be able to claim the allocation.
+
+## Verify an Email Address
+> Verify an Email Address
+
+```python
+client.public.verify_email(
+ token='token',
+)
+```
+
+```typescript
+await client.public.verifyEmail('token');
+```
+
+```json
+{}
+```
+
+### HTTP Request
+`PUT v3/emails/verify-email`
+
+Description: Verify an email address by providing the verification token sent to the email address.
+
+### Request
+
+Parameter | Description
+--------------- | -----------
+token | Confirmation token that was sent to a user's email.
+
+### Response
+
+On success, returns a `204` response with an empty body. After receiving a `204`, the user associated with the email the token was sent to will begin getting notification emails for all types [they have specified in their userData](#send-verification-email). Responds with a `400` error if the token is invalid.
+
+## Get Currently Revealed Hedgies
+> Get Currently Revealed Hedgies
+
+```typescript
+const currentlyRevealedHedgies: {
+ daily?: HedgiePeriodResponseObject,
+ weekly?: HedgiePeriodResponseObject,
+} = await client.public.getCurrentlyRevealedHedgies();
+```
+
+```json
+{
+ "daily": {
+ "blockNumber": 14135506,
+ "competitionPeriod": 1,
+ "tokenIds": [4100]
+ },
+ "weekly": {
+ "blockNumber": 14135506,
+ "competitionperiod": 0,
+ "tokenIds": [2790, 3000, 4109]
+ }
+}
+```
+
+### HTTP Request
+`GET v3/hedgies/current`
+
+
+
+Description: Get the currently revealed [Hedgies](https://hedgies.wtf/) for competition distribution.
+
+### Response
+
+Parameter | Description
+------------------ | -----------
+daily | NftPeriodInformation for daily Hedgie or undefined.
+weekly | NftPeriodInformation for weekly Hedgies or undefined.
+
+### NftPeriodInformation
+
+Parameter | Description
+------------------ | -----------
+blockNumber | The number of the block whose hash was used to randomly select the Hedgie tokenId from the remaining unrevealed Hedgies (or currently revealed Hedgies in the case of distributing weekly Hedgies).
+competitionPeriod | The zero-indexed period of the competition. Competition 0 was the very first day a Hedgie was revealed for competition winners.
+tokenIds | An array of the numeric tokenIds of the Hedgies.
+
+## Get Historically Revealed Hedgies
+> Get Historically Revealed Hedgies
+
+```typescript
+const historicallyRevealedHedgies: {
+ historicalTokenIds: HedgiePeriodResponseObject[],
+} = await client.public.getHistoricallyRevealedHedgies({
+ nftRevealType: WEEK,
+ start: 1,
+ });
+```
+
+```json
+{
+ "historicalTokenIds": [{
+ "blockNumber": 14135506,
+ "competitionperiod": 0,
+ "tokenIds": [2790, 3000, 4109]
+ }]
+}
+```
+
+### HTTP Request
+`GET v3/hedgies/history`
+
+
+
+Description: Get the historically revealed [Hedgies](https://hedgies.wtf/) from competition distributions.
+
+### Request
+
+Parameter | Description
+--------------- | -----------
+nftRevealType | The competition type the Hedgies are being revealed for (`Day` or `Week`).
+start | (Optional): Oldest competition period to be looking from (inclusive).
+end | (Optional): Newest competition period to be looking up to (inclusive).
+
+### Response
+
+Parameter | Description
+------------------ | -----------
+historicalTokenIds | [NftPeriodInformation](#get-currently-revealed-hedgies) array.
+
+
+
+## Get Insurance Fund Balance
+> Get Insurance Fund Balance
+
+```python
+balance = client.public.get_insurance_fund_balance()
+```
+
+```typescript
+const balance: { balance: number } = await client.public.getInsuranceFundBalance();
+```
+
+```json
+{
+ "balance":"9868319.469028"
+}
+```
+
+### HTTP Request
+`GET v3/insurance-fund/balance`
+
+Description: Get the balance of the [dYdX insurance fund](https://help.dydx.exchange/en/articles/4797358-contract-loss-mechanisms).
+
+### Response
+
+Parameter | Description
+------------------ | -----------
+balance | Balance of the dYdX insurance fund in USD.
+
+## Get Public Profile
+Get Public Profile data. This is a subset of the `v3/profile/private` endpoint.
+
+```python
+balance = client.public.get_profile("publicId")
+```
+
+```typescript
+const publicProfile: ProfilePublicResponseObject = await client.public.getProfilePublic("publicId");
+```
+
+```json
+{
+ "username": "foo",
+ "ethereumAddress": "0x0913017c740260fea4b2c62828a4008ca8b0d6e4",
+ "DYDXHoldings": "250",
+ "stakedDYDXHoldings": "250",
+ "hedgiesHeld": [111],
+ "twitterHandle": "bar",
+ "tradingLeagues": {
+ "currentLeague": "SILVER",
+ "currentLeagueRanking": 12,
+ },
+ "tradingPnls": {
+ "absolutePnl30D": "324",
+ "percentPnl30D": "25",
+ "volume30D": "4000",
+ },
+ "tradingRewards": {
+ "curEpoch": "8",
+ "curEpochEstimatedRewards": 280,
+ "prevEpochEstimatedRewards": 125,
+ },
+}
+```
+
+### HTTP Request
+`GET v3/profile/:publicId`
+
+Description: Get the public profile of a user given their public id.
+
+### Response
+
+Parameter | Description
+------------------ | -----------
+balance | Balance of the dYdX insurance fund in USD.
+
+### Request
+
+Parameter | Description
+-------------------| -----------
+publicId | Public id of the user
+
+### Response
+Parameter | Description
+--------------------| -----------
+username | Publically-displayed username.
+publicId | User's public id used in the public profile endpoint
+ethereumAddress | User's associated ethereum address.
+DYDXHoldings | The user's DYDX token holdings. `null` if not sharing ethereum address.
+stakedDYDXHoldings | The user's stkDYDX token holdings. `null` if not sharing ethereum address.
+hedgiesHeld | Indices of all Hedgies held.
+twitterHandle | The username that appears at the end of a unique Twitter url.
+tradingLeagues | See "TradingLeagues" below.
+tradingPnls | See "TradingPnls" below.
+tradingRewards | See "TradingRewards" below.
+
+### TradingLeagues
+Parameter | Description
+--------------------| -----------
+currentLeague | `null, "BRONZE", "SILVER", "GOLD", "PLATINUM", or "DIAMOND"`.
+currentLeagueRanking| `null`, or positive integer ranking.
+
+### TradingPnls
+Parameter | Description
+--------------------| -----------
+absolutePnl30D | `null`, or user's 30 day absolute pnl (based on leaderboard formula).
+percentPnl30D | `null`, or user's 30 day percent pnl (based on leaderboard formula).
+volume30D | The sum of a user's 30 day maker and taker trading volume.
+
+### TradingRewards
+Parameter | Description
+-------------------------| -----------
+curEpoch | Current epoch number.
+curEpochEstimatedRewards | The user's estimated number of dYdX tokens as rewards for the current epoch.
+prevEpochEstimatedRewards| The user's estimated number of dYdX tokens as rewards for the previous epoch.
diff --git a/slate-docs/source/includes/_security-v3.md b/slate-docs/source/includes/_security-v3.md
new file mode 100644
index 00000000..98f13df2
--- /dev/null
+++ b/slate-docs/source/includes/_security-v3.md
@@ -0,0 +1,47 @@
+# Security
+
+## Independent Audits
+
+The Starkware Perpetual smart contracts were audited independently by
+[PeckShield](https://peckshield.com).
+
+**[PeckShield Audit Report](https://github.com/starkware-libs/starkex-contracts/blob/master/audit/StarkPerpetual_v1.0_Audit_Report.pdf)**
+
+## Vulnerability Disclosure Policy
+
+The disclosure of security vulnerabilities helps us ensure the security of our users.
+
+**How to report a security vulnerability?**
+
+If you believe you’ve found a security vulnerability in one of our contracts or platforms,
+send it to us by emailing [security@dydx.exchange](mailto:security@dydx.exchange).
+Please include the following details with your report:
+
+* A description of the location and potential impact of the vulnerability.
+
+* A detailed description of the steps required to reproduce the vulnerability.
+
+**Scope**
+
+Any vulnerability not previously disclosed by us or our independent auditors in their reports.
+
+**Guidelines**
+
+We require that all reporters:
+
+* Make every effort to avoid privacy violations, degradation of user experience,
+disruption to production systems, and destruction of data during security testing.
+
+* Use the identified communication channels to report vulnerability information to us.
+
+* Keep information about any vulnerabilities you’ve discovered confidential between yourself and
+dYdX until we’ve had 30 days to resolve the issue.
+
+If you follow these guidelines when reporting an issue to us, we commit to:
+
+* Not pursue or support any legal action related to your findings.
+
+* Work with you to understand and resolve the issue quickly
+(including an initial confirmation of your report within 72 hours of submission).
+
+* Grant a monetary reward based on the [OWASP risk assessment methodology](https://medium.com/dydxderivatives/announcing-bug-bounties-for-the-dydx-margin-trading-protocol-d0c817d1cda4).
diff --git a/slate-docs/source/includes/_websocket-v3.md b/slate-docs/source/includes/_websocket-v3.md
new file mode 100644
index 00000000..13313c9d
--- /dev/null
+++ b/slate-docs/source/includes/_websocket-v3.md
@@ -0,0 +1,550 @@
+# V3 Websocket API
+
+dYdX offers a WebSocket API for streaming v3 updates.
+
+You can connect to the v3 WebSockets at:
+
+* **Production**: `wss://api.dydx.exchange/v3/ws`
+* **Staging (Goerli)**: `wss://api.stage.dydx.exchange/v3/ws`
+
+The server will send pings every 30s and expects a pong within 10s. The server does not expect pings, but will respond with a pong if sent one.
+
+## Accounts channel
+
+This channel provides realtime information about orders, fills, funding updates and positions for a user. To subscribe, you will need to
+be authenticated.
+
+To subscribe:
+
+| field | type | description |
+|---------------|--------|--------------------------------------------------|
+| type | string | Set to subscribe
|
+| channel | string | Set to v3_accounts
|
+| accountNumber | string | The account number to subscribe to |
+| apiKey | string | The apiKey for the user |
+| signature | string | validation signature. See below |
+| timestamp | string | timestamp used for the signature |
+| passphrase | string | The passphrase
field of the API key |
+
+**Authentication**
+
+The authentication in the accounts channel is identical to [private endpoint authentication](#authentication) with one key difference. The `requestPath` is `/ws/accounts`.
+
+### Initial Response:
+
+The initial response will contain the information about the account, open positions, recent transfers, and open orders, i.e. everything from GET `/v3/accounts/:id`, GET `/v3/transfers`, GET `/v3/funding` and GET `/v3/orders` (with `accountId` in the header).
+
+Note that the `freeCollateral` and `equity` (also called `total account value`) for an account are only sent in the initial response. To track these over time, refer to [this section](#margin).
+
+> Example initial response
+
+```json
+{
+ "type": "subscribed",
+ "channel": "v3_accounts",
+ "connection_id": "e2a6c717-6f77-4c1c-ac22-72ce2b7ed77d",
+ "id": "e33a8007-57ca-52ab-887d-d162d1666f3b",
+ "message_id": 1,
+ "contents": {
+ "orders": [
+ {
+ "id": "797fc129eeb7c54163f3947f1f250594",
+ "clientId": "2",
+ "market": "BTC-USD",
+ "accountId": "e33a8007-57ca-52ab-887d-d162d1666f3b",
+ "side": "BUY",
+ "size": "112",
+ "remainingSize": "0",
+ "price": "34",
+ "limitFee": "0.0005",
+ "type": "LIMIT",
+ "status": "OPEN",
+ "signature": "0x456...",
+ "timeInForce": "FOK",
+ "postOnly": "false",
+ "expiresAt": "2021-09-22T20:22:26.399Z",
+ "createdAt": "2020-09-22T20:22:26.399Z"
+ }
+ ],
+ "account": {
+ "id": "e33a8007-57ca-52ab-887d-d162d1666f3b",
+ "positionId": "9356",
+ "userId": "fe71e7df-c633-4ba1-870e-61f36580cfc5",
+ "accountNumber": "0",
+ "starkKey": "041c2ae647ee91807eed6471488983ab4addc2a602d4ceeb04dfda470e33f148",
+ "quoteBalance": "300",
+ "pendingDeposits": "0",
+ "pendingWithdrawals": "0",
+ "lastTransactionId": "14",
+ "equity": "1879.090000",
+ "freeCollateral": "1879.090000",
+ "createdAt": "2021-04-09T21:08:34.984Z",
+ "openPositions": {
+ "LINK-USD": {
+ "id": "677dad3b-d848-5e7c-84bf-18760f3414f6",
+ "accountId": "e33a8007-57ca-52ab-887d-d162d1666f3b",
+ "market": "LINK-USD",
+ "side": "LONG",
+ "status": "OPEN",
+ "size": "200",
+ "maxSize": "300",
+ "entryPrice": "36",
+ "exitPrice": "38",
+ "realizedPnl": "50",
+ "createdAt": "2020-09-22T20:25:26.399Z",
+ "openTransactionId": "2",
+ "lastTransactionId": "14",
+ "sumOpen": "300",
+ "sumClose": "100"
+ }
+ }
+ }
+ },
+ "transfers": [
+ {
+ "id": "8d303634-da14-56bb-99f5-122e34b1ce34",
+ "type": "FAST_WITHDRAWAL",
+ "debitAsset": "USDC",
+ "creditAsset": "USDC",
+ "debitAmount": "500",
+ "creditAmount": "500",
+ "transactionHash": "0xb86e98d05098de6249d7c10616ffefa0b001976238083dc34a8e747fd7960029",
+ "status": "CONFIRMED",
+ "createdAt": "2021-02-05T00:37:43.009Z",
+ "confirmedAt": null,
+ "clientId": "9407156494718159",
+ "fromAddress": "0x3ebe6781be6d436cb7999cfce8b52e40819721cb",
+ "toAddress": "0x14c2a496e5b7a52d54748cba0bd9f4b24ed27fdd"
+ }
+ ],
+ "fundingPayments": [],
+}
+```
+
+### Channel Data
+
+Subsequent responses will contain any updates to open orders, or changes to account balance, or the open positions, or transfers, in a single message.
+
+> A fill occurs, and a position is closed, and the account balance modified
+
+```json
+{
+ "type": "channel_data",
+ "channel": "v3_accounts",
+ "connection_id": "e2a6c717-6f77-4c1c-ac22-72ce2b7ed77d",
+ "id": "e33a8007-57ca-52ab-887d-d162d1666f3b",
+ "message_id": 2,
+ "contents": {
+ "fills": [{
+ "id": "677dad3b-d848-5e7c-84bf-18760f3414f6",
+ "accountId": "e33a8007-57ca-52ab-887d-d162d1666f3b",
+ "side": "BUY",
+ "liquidity": "TAKER",
+ "market": "LINK-USD",
+ "orderId": "797fc129eeb7c54163f3947f1f250594",
+ "size": "112",
+ "price": "35",
+ "fee": "10",
+ "transactionId": "1",
+ "orderClientId": "31391968951033844",
+ "createdAt": "2020-09-22T20:25:26.399Z",
+ }],
+ "orders": [{
+ "id": "797fc129eeb7c54163f3947f1f250594",
+ "clientId": "2",
+ "market": "BTC-USD",
+ "accountId": "e33a8007-57ca-52ab-887d-d162d1666f3b",
+ "side": "BUY",
+ "size": "112",
+ "remainingSize": "0",
+ "price": "34",
+ "limitFee": "0.0005",
+ "type": "LIMIT",
+ "status": "ENTIRELY_FILLED",
+ "signature": "0x456...",
+ "timeInForce": "FOK",
+ "postOnly": "false",
+ "expiresAt": "2021-09-22T20:22:26.399Z",
+ "createdAt": "2020-09-22T20:22:26.399Z"
+ }],
+ "positions": [{
+ "id": "677dad3b-d848-5e7c-84bf-18760f3414f6",
+ "accountId": "e33a8007-57ca-52ab-887d-d162d1666f3b",
+ "market": "LINK-USD",
+ "side": "LONG",
+ "status": "CLOSED",
+ "size": "200",
+ "maxSize": "300",
+ "entryPrice": "36",
+ "exitPrice": "38",
+ "realizedPnl": "50",
+ "createdAt": "2020-09-22T20:25:26.399Z",
+ "openTransactionId": "2",
+ "closeTransactionId": "23",
+ "lastTransactionId": "23",
+ "closedAt": "2020-14-22T20:25:26.399Z",
+ "sumOpen": "300",
+ "sumClose": "100"
+ }],
+ "accounts": [{
+ "id": "e33a8007-57ca-52ab-887d-d162d1666f3b",
+ "positionId": "b2759094-12af-4b59-8071-661e99148a14",
+ "userId": "fe71e7df-c633-4ba1-870e-61f36580cfc5",
+ "accountNumber": "0",
+ "starkKey": "0x456...",
+ "quoteBalance": "700",
+ "pendingDeposits": "400",
+ "pendingWithdrawals": "0",
+ "lastTransactionId": "14"
+ }]
+ }
+}
+```
+
+> a deposit occurs
+
+```json
+{
+ "type": "channel_data",
+ "channel": "v3_accounts",
+ "connection_id": "e2a6c717-6f77-4c1c-ac22-72ce2b7ed77d",
+ "id": "e33a8007-57ca-52ab-887d-d162d1666f3b",
+ "message_id": 2,
+ "contents": {
+ "fills": [],
+ "orders": [],
+ "positions": [],
+ "accounts": [{
+ "id": "e33a8007-57ca-52ab-887d-d162d1666f3b",
+ "positionId": "b2759094-12af-4b59-8071-661e99148a14",
+ "userId": "fe71e7df-c633-4ba1-870e-61f36580cfc5",
+ "accountNumber": "0",
+ "starkKey": "0x456...",
+ "quoteBalance": "7000",
+ "pendingDeposits": "200",
+ "pendingWithdrawals": "0",
+ "lastTransactionId": "14"
+ }],
+ "transfers": [{
+ "id" : "35bb84a8-d8b5-5f8e-a49e-8ad979fb7567",
+ "accountId" : "e33a8007-57ca-52ab-887d-d162d1666f3b",
+ "type" : "DEPOSIT",
+ "debitAsset" : "USDC",
+ "creditAsset" : "USDC",
+ "debitAmount" : "10000",
+ "creditAmount" : "10000",
+ "transactionHash" : "0xec2bd16e73e4bb54c1ee25415233ded15f6e8c4edb8480ce9774a28c7846d4f0",
+ "status" : "PENDING",
+ "clientId" : "18",
+ "updatedAt" : "2021-01-17 22:24:54.661+00",
+ "createdAt" : "2021-01-17 22:24:54.560426+00",
+ }]
+ }
+}
+```
+
+## Orderbook
+
+To subscribe:
+
+| field | type | description |
+|---------------------------|---------|---------------------------------------------------------------------------------|
+| type | string | Set to subscribe
|
+| channel | string | Set to v3_orderbook
|
+| id | string | The market to subscribe to e.g. BTC-USD, LINK-USD |
+| includeOffsets (optional) | boolean | If specified, this will return an initial response with per-price level offsets |
+
+### Initial Response:
+
+The initial response will contain the state of the orderbook and will be the same structure as GET `/v3/orderbook/:market`. If includeOffsets
is sent and set to true in the subscription message, there will be an offset included for each price level. (See the example included)
+
+| field | description |
+|----------|--------------------------------------------------|
+| type | will be subscribed
|
+| channel | the channel name, i.e. v3_orderbook
|
+| id | the market subscribed to e.g. BTC-USD |
+| contents | the message contents |
+
+The contents is structured as:
+
+| field | type | description |
+|--------|--------------------|-------------------------------------------------------------------------|
+| offset | string | A number used for ordering. See offset
below. |
+| bids | array | See PublicOrder
below. Sorted by price in descending order |
+| asks | array | See PublicOrder
below. Sorted by price in ascending order |
+
+PublicOrder:
+
+| field | type | description |
+|--------|--------|-----------------------------------------------------------------------------------|
+| price | string | human readable price of the order (in quote / base currency) |
+| size | string | human readable size of the order (in base currency) |
+| offset | string | (if includeOffsets
is set to true) the offset for the specific price |
+
+
+Offset:
+
+The price updates are not guaranteed to be sent in order. So it is possible to receive an older price update later. For this reason, the offset is included in the message, to help order. The offset increases monotonically, and increasing values of offsets indicate more recent values.
+
+
+
+Example messages:
+
+
+> Example initial response:
+
+```json
+{
+ "type": "subscribed",
+ "connection_id": "87b25218-0170-4111-bfbf-d9f0a506fcab",
+ "message_id": 1,
+ "channel": "v3_orderbook",
+ "id": "ETH-USD",
+ "contents": {
+ "bids": [
+ {
+ "price": "1779",
+ "size": "11.24"
+ },
+ {
+ "price": "1778.5",
+ "size": "18"
+ }
+ ],
+ "asks": [
+ {
+ "price": "1782.8",
+ "size": "10"
+ },
+ {
+ "price": "1784",
+ "size": "2.81"
+ }
+ ]
+ }
+}
+```
+
+> Example initial response if includeOffsets
is set to true:
+
+Request:
+
+```json
+{
+ "type": "subscribe",
+ "channel": "v3_orderbook",
+ "id": "ETH-USD",
+ "includeOffsets": "true"
+}
+```
+
+Response:
+
+```json
+{
+ "type": "subscribed",
+ "connection_id": "14f7c481-1e1f-4f5c-8c5c-7b114209d8ce",
+ "message_id": 1,
+ "channel": "v3_orderbook",
+ "id": "ETH-USD",
+ "contents": {
+ "bids": [
+ {
+ "price": "1778.8",
+ "offset": "36850163",
+ "size": "11"
+ },
+ {
+ "price": "1776.7",
+ "offset": "36849225",
+ "size": "5.9"
+ }
+ ],
+ "asks": [
+ {
+ "price": "1783",
+ "offset": "36848764",
+ "size": "13"
+ },
+ {
+ "price": "1784",
+ "offset": "36848433",
+ "size": "4.3"
+ }
+ ]
+ }
+}
+```
+
+### Channel Data
+
+Subsequent responses will contain the new order sizes for any price levels that have changed since the previous update:
+
+e.g:
+
+> Subsequent messages
+
+```json
+{
+ "type": "channel_data",
+ "id": "BTC-USD",
+ "connection_id": "e2a6c717-6f77-4c1c-ac22-72ce2b7ed77d",
+ "channel": "v3_orderbook",
+ "message_id": 2,
+ "contents": {
+ "offset": "178",
+ "bids": [["102", "12"]],
+ "asks": [["104", "0" ]]
+ }
+}
+```
+
+E.g: if some orders at "102" price, get filled, then the update would be ["102", "12"], where "12" is the new size.
+If there are no more asks at "104", then the ask update would be ["104", "0"].
+
+## Trades
+
+To subscribe:
+
+| field | type | description |
+|---------|--------|---------------------------------------------------|
+| type | string | Set to subscribe
|
+| channel | string | Set to v3_trades
|
+| id | string | The market to subscribe to e.g. BTC-USD, LINK-USD |
+
+### Initial Response:
+
+The initial response will contain the historical trades and will be the same structure as GET `/v3/trades/:market`.
+
+| field | description |
+|----------|-----------------------------------------------|
+| type | will be subscribed
|
+| channel | the channel name, i.e. v3_trades
|
+| id | the market subscribed to e.g. BTC-USD |
+| contents | the message contents |
+
+The contents is structured as:
+
+| field | type | description |
+|--------|--------------------|-------------------------------------|
+| trades | array | See PublicTrade
below. |
+
+PublicTrade:
+
+| field | type | description |
+|-------------|-----------------------|--------------------------------------------------------------------|
+| side | string | BUY
or SELL
|
+| size | string | size of the trade |
+| price | string | price of the trade |
+| createdAt | ISO time of the trade | time of the trade |
+| liquidation | boolean | true
if the trade was the result of a liquidation, false
otherwise |
+
+Example messages:
+
+> Example initial response:
+
+```json
+{
+ "type": "subscribed",
+ "id": "BTC-USD",
+ "connection_id": "e2a6c717-6f77-4c1c-ac22-72ce2b7ed77d",
+ "channel": "v3_trades",
+ "message_id": 1,
+ "contents": {
+ "trades": [
+ {
+ "side": "BUY",
+ "size": "100",
+ "price": "4000",
+ "createdAt": "2020-10-29T00:26:30.759Z"
+ },
+ {
+ "side": "BUY",
+ "size": "100",
+ "price": "4000",
+ "createdAt": "2020-11-02T19:45:42.886Z"
+ },
+ {
+ "side": "BUY",
+ "size": "100",
+ "price": "4000",
+ "createdAt": "2020-10-29T00:26:57.382Z"
+ }
+ ]
+ }
+}
+```
+
+### Channel Data
+
+Subsequent responses will contain the recently created trades. e.g:
+
+> Subsequent responses
+
+```json
+{
+ "type": "channel_data",
+ "id": "BTC-USD",
+ "connection_id": "e2a6c717-6f77-4c1c-ac22-72ce2b7ed77d",
+ "channel": "v3_trades",
+ "message_id": 2,
+ "contents": {
+ "trades": [
+ {
+ "side": "BUY",
+ "size": "100",
+ "price": "4000",
+ "createdAt": "2020-11-29T00:26:30.759Z"
+ },
+ {
+ "side": "SELL",
+ "size": "100",
+ "price": "4000",
+ "createdAt": "2020-11-29T14:00:03.382Z"
+ }
+ ]
+ }
+}
+```
+
+## Markets
+
+To subscribe:
+
+| field | type | description |
+|---------|--------|--------------------------------|
+| type | string | Set to subscribe
|
+| channel | string | Set to v3_markets
|
+
+### Initial Response:
+
+Same as [GET /v3/markets](#get-markets)
+
+### Channel Data
+
+Subsequent responses will contain an update for one or more markets. Updates will be sent any time a field(s) changes on a market(s). Updates will only contain the field(s) that have changed:
+
+> Subsequent responses
+
+```json
+{
+ "type": "channel_data",
+ "connection_id": "e2a6c717-6f77-4c1c-ac22-72ce2b7ed77d",
+ "channel": "v3_markets",
+ "message_id": 2,
+ "contents": {
+ "ETH-USD": {
+ "oraclePrice": "100.23"
+ },
+ "BTC-USD": {
+ "indexPrice": "100.23",
+ "priceChange24H": "0.12",
+ "initialMarginFraction": "1.23"
+ }
+ }
+}
+```
diff --git a/slate-docs/source/index.html.md b/slate-docs/source/index.html.md
new file mode 100644
index 00000000..e9589bdb
--- /dev/null
+++ b/slate-docs/source/index.html.md
@@ -0,0 +1,21 @@
+---
+title: v3 dYdX Documentation
+
+language_tabs: # must be one of https://git.io/vQNgJ
+ - python: Python
+ - typescript: TypeScript
+ - json: HTTP
+
+includes:
+ - general-v3
+ - perpetual-contracts-v3
+ - clients-v3
+ - private-v3
+ - public-v3
+ - websocket-v3
+ - security-v3
+
+search: true
+
+code_clipboard: true
+---
diff --git a/slate-docs/source/javascripts/all.js b/slate-docs/source/javascripts/all.js
new file mode 100644
index 00000000..5f5d4067
--- /dev/null
+++ b/slate-docs/source/javascripts/all.js
@@ -0,0 +1,2 @@
+//= require ./all_nosearch
+//= require ./app/_search
diff --git a/slate-docs/source/javascripts/all_nosearch.js b/slate-docs/source/javascripts/all_nosearch.js
new file mode 100644
index 00000000..55704a20
--- /dev/null
+++ b/slate-docs/source/javascripts/all_nosearch.js
@@ -0,0 +1,30 @@
+//= require ./lib/_energize
+//= require ./app/_copy
+//= require ./app/_toc
+//= require ./app/_lang
+
+function adjustLanguageSelectorWidth() {
+ const elem = $('.dark-box > .lang-selector');
+ elem.width(elem.parent().width());
+}
+
+$(function() {
+ if ($(window.location.hash).get(0)) {
+ $(window.location.hash).get(0).scrollIntoView(true);
+ }
+ loadToc($('#toc'), '.toc-link', '.toc-list-h2, .toc-list-h3', 10);
+ setupLanguages($('body').data('languages'));
+ $('.content').imagesLoaded( function() {
+ window.recacheHeights();
+ window.refreshToc();
+ });
+
+ $(window).resize(function() {
+ adjustLanguageSelectorWidth();
+ });
+ adjustLanguageSelectorWidth();
+});
+
+window.onpopstate = function() {
+ activateLanguage(getLanguageFromQueryString());
+};
diff --git a/slate-docs/source/javascripts/app/_copy.js b/slate-docs/source/javascripts/app/_copy.js
new file mode 100644
index 00000000..f1e3a432
--- /dev/null
+++ b/slate-docs/source/javascripts/app/_copy.js
@@ -0,0 +1,15 @@
+function copyToClipboard(container) {
+ const el = document.createElement('textarea');
+ el.value = container.textContent;
+ document.body.appendChild(el);
+ el.select();
+ document.execCommand('copy');
+ document.body.removeChild(el);
+}
+
+function setupCodeCopy() {
+ $('pre.highlight').prepend('');
+ $('.copy-clipboard').on('click', function() {
+ copyToClipboard(this.parentNode.children[1]);
+ });
+}
diff --git a/slate-docs/source/javascripts/app/_lang.js b/slate-docs/source/javascripts/app/_lang.js
new file mode 100644
index 00000000..63776511
--- /dev/null
+++ b/slate-docs/source/javascripts/app/_lang.js
@@ -0,0 +1,165 @@
+//= require ../lib/_jquery
+
+/*
+Copyright 2008-2013 Concur Technologies, Inc.
+
+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.
+*/
+;(function () {
+ 'use strict';
+
+ var languages = [];
+
+ window.setupLanguages = setupLanguages;
+ window.activateLanguage = activateLanguage;
+ window.getLanguageFromQueryString = getLanguageFromQueryString;
+
+ function activateLanguage(language) {
+ if (!language) return;
+ if (language === "") return;
+
+ $(".lang-selector a").removeClass('active');
+ $(".lang-selector a[data-language-name='" + language + "']").addClass('active');
+ for (var i=0; i < languages.length; i++) {
+ $(".highlight.tab-" + languages[i]).hide();
+ $(".lang-specific." + languages[i]).hide();
+ }
+ $(".highlight.tab-" + language).show();
+ $(".lang-specific." + language).show();
+
+ window.recacheHeights();
+
+ // scroll to the new location of the position
+ // UNCOMMENT BELOW TO RE-ENABLE SCROLL-TO ON LANGUAGE CHANGE
+ if ($(window.location.hash).get(0)) {
+ $(window.location.hash).get(0).scrollIntoView(true);
+ }
+ }
+
+ // parseURL and stringifyURL are from https://github.com/sindresorhus/query-string
+ // MIT licensed
+ // https://github.com/sindresorhus/query-string/blob/7bee64c16f2da1a326579e96977b9227bf6da9e6/license
+ function parseURL(str) {
+ if (typeof str !== 'string') {
+ return {};
+ }
+
+ str = str.trim().replace(/^(\?|#|&)/, '');
+
+ if (!str) {
+ return {};
+ }
+
+ return str.split('&').reduce(function (ret, param) {
+ var parts = param.replace(/\+/g, ' ').split('=');
+ var key = parts[0];
+ var val = parts[1];
+
+ key = decodeURIComponent(key);
+ // missing `=` should be `null`:
+ // http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters
+ val = val === undefined ? null : decodeURIComponent(val);
+
+ if (!ret.hasOwnProperty(key)) {
+ ret[key] = val;
+ } else if (Array.isArray(ret[key])) {
+ ret[key].push(val);
+ } else {
+ ret[key] = [ret[key], val];
+ }
+
+ return ret;
+ }, {});
+ };
+
+ function stringifyURL(obj) {
+ return obj ? Object.keys(obj).sort().map(function (key) {
+ var val = obj[key];
+
+ if (Array.isArray(val)) {
+ return val.sort().map(function (val2) {
+ return encodeURIComponent(key) + '=' + encodeURIComponent(val2);
+ }).join('&');
+ }
+
+ return encodeURIComponent(key) + '=' + encodeURIComponent(val);
+ }).join('&') : '';
+ };
+
+ // gets the language set in the query string
+ function getLanguageFromQueryString() {
+ if (location.search.length >= 1) {
+ var language = parseURL(location.search).language;
+ if (language) {
+ return language;
+ } else if (jQuery.inArray(location.search.substr(1), languages) != -1) {
+ return location.search.substr(1);
+ }
+ }
+
+ return false;
+ }
+
+ // returns a new query string with the new language in it
+ function generateNewQueryString(language) {
+ var url = parseURL(location.search);
+ if (url.language) {
+ url.language = language;
+ return stringifyURL(url);
+ }
+ return language;
+ }
+
+ // if a button is clicked, add the state to the history
+ function pushURL(language) {
+ if (!history) { return; }
+ var hash = window.location.hash;
+ if (hash) {
+ hash = hash.replace(/^#+/, '');
+ }
+ history.pushState({}, '', '?' + generateNewQueryString(language) + '#' + hash);
+
+ // save language as next default
+ localStorage.setItem("language", language);
+ }
+
+ function setupLanguages(l) {
+ var defaultLanguage = localStorage.getItem("language");
+
+ languages = l;
+
+ var presetLanguage = getLanguageFromQueryString();
+ if (presetLanguage) {
+ // the language is in the URL, so use that language!
+ activateLanguage(presetLanguage);
+
+ localStorage.setItem("language", presetLanguage);
+ } else if ((defaultLanguage !== null) && (jQuery.inArray(defaultLanguage, languages) != -1)) {
+ // the language was the last selected one saved in localstorage, so use that language!
+ activateLanguage(defaultLanguage);
+ } else {
+ // no language selected, so use the default
+ activateLanguage(languages[0]);
+ }
+ }
+
+ // if we click on a language tab, activate that language
+ $(function() {
+ $(".lang-selector a").on("click", function() {
+ var language = $(this).data("language-name");
+ pushURL(language);
+ activateLanguage(language);
+ return false;
+ });
+ });
+})();
diff --git a/slate-docs/source/javascripts/app/_search.js b/slate-docs/source/javascripts/app/_search.js
new file mode 100644
index 00000000..0b0ccd97
--- /dev/null
+++ b/slate-docs/source/javascripts/app/_search.js
@@ -0,0 +1,102 @@
+//= require ../lib/_lunr
+//= require ../lib/_jquery
+//= require ../lib/_jquery.highlight
+;(function () {
+ 'use strict';
+
+ var content, searchResults;
+ var highlightOpts = { element: 'span', className: 'search-highlight' };
+ var searchDelay = 0;
+ var timeoutHandle = 0;
+ var index;
+
+ function populate() {
+ index = lunr(function(){
+
+ this.ref('id');
+ this.field('title', { boost: 10 });
+ this.field('body');
+ this.pipeline.add(lunr.trimmer, lunr.stopWordFilter);
+ var lunrConfig = this;
+
+ $('h1, h2').each(function() {
+ var title = $(this);
+ var body = title.nextUntil('h1, h2');
+ lunrConfig.add({
+ id: title.prop('id'),
+ title: title.text(),
+ body: body.text()
+ });
+ });
+
+ });
+ determineSearchDelay();
+ }
+
+ $(populate);
+ $(bind);
+
+ function determineSearchDelay() {
+ if (index.tokenSet.toArray().length>5000) {
+ searchDelay = 300;
+ }
+ }
+
+ function bind() {
+ content = $('.content');
+ searchResults = $('.search-results');
+
+ $('#input-search').on('keyup',function(e) {
+ var wait = function() {
+ return function(executingFunction, waitTime){
+ clearTimeout(timeoutHandle);
+ timeoutHandle = setTimeout(executingFunction, waitTime);
+ };
+ }();
+ wait(function(){
+ search(e);
+ }, searchDelay);
+ });
+ }
+
+ function search(event) {
+
+ var searchInput = $('#input-search')[0];
+
+ unhighlight();
+ searchResults.addClass('visible');
+
+ // ESC clears the field
+ if (event.keyCode === 27) searchInput.value = '';
+
+ if (searchInput.value) {
+ var results = index.search(searchInput.value).filter(function(r) {
+ return r.score > 0.0001;
+ });
+
+ if (results.length) {
+ searchResults.empty();
+ $.each(results, function (index, result) {
+ var elem = document.getElementById(result.ref);
+ searchResults.append("" + $(elem).text() + " ");
+ });
+ highlight.call(searchInput);
+ } else {
+ searchResults.html('');
+ $('.search-results li').text('No Results Found for "' + searchInput.value + '"');
+ }
+ } else {
+ unhighlight();
+ searchResults.removeClass('visible');
+ }
+ }
+
+ function highlight() {
+ if (this.value) content.highlight(this.value, highlightOpts);
+ }
+
+ function unhighlight() {
+ content.unhighlight(highlightOpts);
+ }
+})();
+
diff --git a/slate-docs/source/javascripts/app/_toc.js b/slate-docs/source/javascripts/app/_toc.js
new file mode 100644
index 00000000..a34f38c9
--- /dev/null
+++ b/slate-docs/source/javascripts/app/_toc.js
@@ -0,0 +1,139 @@
+//= require ../lib/_jquery
+//= require ../lib/_imagesloaded.min
+;(function () {
+ 'use strict';
+
+ var htmlPattern = /<[^>]*>/g;
+ var loaded = false;
+
+ var debounce = function(func, waitTime) {
+ var timeout = false;
+ return function() {
+ if (timeout === false) {
+ setTimeout(function() {
+ func();
+ timeout = false;
+ }, waitTime);
+ timeout = true;
+ }
+ };
+ };
+
+ var closeToc = function() {
+ $(".toc-wrapper").removeClass('open');
+ $("#nav-button").removeClass('open');
+ };
+
+ function loadToc($toc, tocLinkSelector, tocListSelector, scrollOffset) {
+ var headerHeights = {};
+ var pageHeight = 0;
+ var windowHeight = 0;
+ var originalTitle = document.title;
+
+ var recacheHeights = function() {
+ headerHeights = {};
+ pageHeight = $(document).height();
+ windowHeight = $(window).height();
+
+ $toc.find(tocLinkSelector).each(function() {
+ var targetId = $(this).attr('href');
+ if (targetId[0] === "#") {
+ headerHeights[targetId] = $("#" + $.escapeSelector(targetId.substring(1))).offset().top;
+ }
+ });
+ };
+
+ var refreshToc = function() {
+ var currentTop = $(document).scrollTop() + scrollOffset;
+
+ if (currentTop + windowHeight >= pageHeight) {
+ // at bottom of page, so just select last header by making currentTop very large
+ // this fixes the problem where the last header won't ever show as active if its content
+ // is shorter than the window height
+ currentTop = pageHeight + 1000;
+ }
+
+ var best = null;
+ for (var name in headerHeights) {
+ if ((headerHeights[name] < currentTop && headerHeights[name] > headerHeights[best]) || best === null) {
+ best = name;
+ }
+ }
+
+ // Catch the initial load case
+ if (currentTop == scrollOffset && !loaded) {
+ best = window.location.hash;
+ loaded = true;
+ }
+
+ var $best = $toc.find("[href='" + best + "']").first();
+ if (!$best.hasClass("active")) {
+ // .active is applied to the ToC link we're currently on, and its parent s selected by tocListSelector
+ // .active-expanded is applied to the ToC links that are parents of this one
+ $toc.find(".active").removeClass("active");
+ $toc.find(".active-parent").removeClass("active-parent");
+ $best.addClass("active");
+ $best.parents(tocListSelector).addClass("active").siblings(tocLinkSelector).addClass('active-parent');
+ $best.siblings(tocListSelector).addClass("active");
+ $toc.find(tocListSelector).filter(":not(.active)").slideUp(150);
+ $toc.find(tocListSelector).filter(".active").slideDown(150);
+ if (window.history.replaceState) {
+ window.history.replaceState(null, "", best);
+ }
+
+ var thisTitle = $best.data("title");
+ // Only set document title for h1, h2 toc
+ if ($toc.find(".active-parent").length > 1) {
+ var $parent = $toc.find(".active-parent").last()
+ thisTitle = $parent.data("title");
+ }
+ if (thisTitle !== undefined && thisTitle.length > 0) {
+ document.title = thisTitle.replace(htmlPattern, "") + " – " + originalTitle;
+ } else {
+ document.title = originalTitle;
+ }
+ }
+ };
+
+ var makeToc = function() {
+ recacheHeights();
+ refreshToc();
+
+ $("#nav-button").click(function() {
+ $(".toc-wrapper").toggleClass('open');
+ $("#nav-button").toggleClass('open');
+ return false;
+ });
+ $(".page-wrapper").click(closeToc);
+ $(".toc-link").click(closeToc);
+
+ // reload immediately after scrolling on toc click
+ $toc.find(tocLinkSelector).click(function() {
+ setTimeout(function() {
+ refreshToc();
+ }, 0);
+ });
+
+ var w = $(window).width();
+ $(window).scroll(debounce(refreshToc, 200));
+ // Remain at current anchor while resizing
+ $(window).resize(debounce(
+ function() {
+ if ($(window).width() === w) return;
+ w = $(window).width();
+ recacheHeights();
+ if ($(window.location.hash).get(0)) {
+ $(window.location.hash).get(0).scrollIntoView(true);
+ }
+ },
+ 200));
+ };
+
+ makeToc();
+
+ window.recacheHeights = recacheHeights;
+ window.refreshToc = refreshToc;
+ }
+
+ window.loadToc = loadToc;
+})();
diff --git a/slate-docs/source/javascripts/lib/_energize.js b/slate-docs/source/javascripts/lib/_energize.js
new file mode 100644
index 00000000..6798f3c0
--- /dev/null
+++ b/slate-docs/source/javascripts/lib/_energize.js
@@ -0,0 +1,169 @@
+/**
+ * energize.js v0.1.0
+ *
+ * Speeds up click events on mobile devices.
+ * https://github.com/davidcalhoun/energize.js
+ */
+
+(function() { // Sandbox
+ /**
+ * Don't add to non-touch devices, which don't need to be sped up
+ */
+ if(!('ontouchstart' in window)) return;
+
+ var lastClick = {},
+ isThresholdReached, touchstart, touchmove, touchend,
+ click, closest;
+
+ /**
+ * isThresholdReached
+ *
+ * Compare touchstart with touchend xy coordinates,
+ * and only fire simulated click event if the coordinates
+ * are nearby. (don't want clicking to be confused with a swipe)
+ */
+ isThresholdReached = function(startXY, xy) {
+ return Math.abs(startXY[0] - xy[0]) > 5 || Math.abs(startXY[1] - xy[1]) > 5;
+ };
+
+ /**
+ * touchstart
+ *
+ * Save xy coordinates when the user starts touching the screen
+ */
+ touchstart = function(e) {
+ this.startXY = [e.touches[0].clientX, e.touches[0].clientY];
+ this.threshold = false;
+ };
+
+ /**
+ * touchmove
+ *
+ * Check if the user is scrolling past the threshold.
+ * Have to check here because touchend will not always fire
+ * on some tested devices (Kindle Fire?)
+ */
+ touchmove = function(e) {
+ // NOOP if the threshold has already been reached
+ if(this.threshold) return false;
+
+ this.threshold = isThresholdReached(this.startXY, [e.touches[0].clientX, e.touches[0].clientY]);
+ };
+
+ /**
+ * touchend
+ *
+ * If the user didn't scroll past the threshold between
+ * touchstart and touchend, fire a simulated click.
+ *
+ * (This will fire before a native click)
+ */
+ touchend = function(e) {
+ // Don't fire a click if the user scrolled past the threshold
+ if(this.threshold || isThresholdReached(this.startXY, [e.changedTouches[0].clientX, e.changedTouches[0].clientY])) {
+ return;
+ }
+
+ /**
+ * Create and fire a click event on the target element
+ * https://developer.mozilla.org/en/DOM/event.initMouseEvent
+ */
+ var touch = e.changedTouches[0],
+ evt = document.createEvent('MouseEvents');
+ evt.initMouseEvent('click', true, true, window, 0, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
+ evt.simulated = true; // distinguish from a normal (nonsimulated) click
+ e.target.dispatchEvent(evt);
+ };
+
+ /**
+ * click
+ *
+ * Because we've already fired a click event in touchend,
+ * we need to listed for all native click events here
+ * and suppress them as necessary.
+ */
+ click = function(e) {
+ /**
+ * Prevent ghost clicks by only allowing clicks we created
+ * in the click event we fired (look for e.simulated)
+ */
+ var time = Date.now(),
+ timeDiff = time - lastClick.time,
+ x = e.clientX,
+ y = e.clientY,
+ xyDiff = [Math.abs(lastClick.x - x), Math.abs(lastClick.y - y)],
+ target = closest(e.target, 'A') || e.target, // needed for standalone apps
+ nodeName = target.nodeName,
+ isLink = nodeName === 'A',
+ standAlone = window.navigator.standalone && isLink && e.target.getAttribute("href");
+
+ lastClick.time = time;
+ lastClick.x = x;
+ lastClick.y = y;
+
+ /**
+ * Unfortunately Android sometimes fires click events without touch events (seen on Kindle Fire),
+ * so we have to add more logic to determine the time of the last click. Not perfect...
+ *
+ * Older, simpler check: if((!e.simulated) || standAlone)
+ */
+ if((!e.simulated && (timeDiff < 500 || (timeDiff < 1500 && xyDiff[0] < 50 && xyDiff[1] < 50))) || standAlone) {
+ e.preventDefault();
+ e.stopPropagation();
+ if(!standAlone) return false;
+ }
+
+ /**
+ * Special logic for standalone web apps
+ * See http://stackoverflow.com/questions/2898740/iphone-safari-web-app-opens-links-in-new-window
+ */
+ if(standAlone) {
+ window.location = target.getAttribute("href");
+ }
+
+ /**
+ * Add an energize-focus class to the targeted link (mimics :focus behavior)
+ * TODO: test and/or remove? Does this work?
+ */
+ if(!target || !target.classList) return;
+ target.classList.add("energize-focus");
+ window.setTimeout(function(){
+ target.classList.remove("energize-focus");
+ }, 150);
+ };
+
+ /**
+ * closest
+ * @param {HTMLElement} node current node to start searching from.
+ * @param {string} tagName the (uppercase) name of the tag you're looking for.
+ *
+ * Find the closest ancestor tag of a given node.
+ *
+ * Starts at node and goes up the DOM tree looking for a
+ * matching nodeName, continuing until hitting document.body
+ */
+ closest = function(node, tagName){
+ var curNode = node;
+
+ while(curNode !== document.body) { // go up the dom until we find the tag we're after
+ if(!curNode || curNode.nodeName === tagName) { return curNode; } // found
+ curNode = curNode.parentNode; // not found, so keep going up
+ }
+
+ return null; // not found
+ };
+
+ /**
+ * Add all delegated event listeners
+ *
+ * All the events we care about bubble up to document,
+ * so we can take advantage of event delegation.
+ *
+ * Note: no need to wait for DOMContentLoaded here
+ */
+ document.addEventListener('touchstart', touchstart, false);
+ document.addEventListener('touchmove', touchmove, false);
+ document.addEventListener('touchend', touchend, false);
+ document.addEventListener('click', click, true); // TODO: why does this use capture?
+
+})();
\ No newline at end of file
diff --git a/slate-docs/source/javascripts/lib/_imagesloaded.min.js b/slate-docs/source/javascripts/lib/_imagesloaded.min.js
new file mode 100644
index 00000000..e443a77d
--- /dev/null
+++ b/slate-docs/source/javascripts/lib/_imagesloaded.min.js
@@ -0,0 +1,7 @@
+/*!
+ * imagesLoaded PACKAGED v4.1.4
+ * JavaScript is all like "You images are done yet or what?"
+ * MIT License
+ */
+
+!function(e,t){"function"==typeof define&&define.amd?define("ev-emitter/ev-emitter",t):"object"==typeof module&&module.exports?module.exports=t():e.EvEmitter=t()}("undefined"!=typeof window?window:this,function(){function e(){}var t=e.prototype;return t.on=function(e,t){if(e&&t){var i=this._events=this._events||{},n=i[e]=i[e]||[];return n.indexOf(t)==-1&&n.push(t),this}},t.once=function(e,t){if(e&&t){this.on(e,t);var i=this._onceEvents=this._onceEvents||{},n=i[e]=i[e]||{};return n[t]=!0,this}},t.off=function(e,t){var i=this._events&&this._events[e];if(i&&i.length){var n=i.indexOf(t);return n!=-1&&i.splice(n,1),this}},t.emitEvent=function(e,t){var i=this._events&&this._events[e];if(i&&i.length){i=i.slice(0),t=t||[];for(var n=this._onceEvents&&this._onceEvents[e],o=0;o (default options)
+ * $('#content').highlight('lorem');
+ *
+ * // search for and highlight more terms at once
+ * // so you can save some time on traversing DOM
+ * $('#content').highlight(['lorem', 'ipsum']);
+ * $('#content').highlight('lorem ipsum');
+ *
+ * // search only for entire word 'lorem'
+ * $('#content').highlight('lorem', { wordsOnly: true });
+ *
+ * // don't ignore case during search of term 'lorem'
+ * $('#content').highlight('lorem', { caseSensitive: true });
+ *
+ * // wrap every occurrance of term 'ipsum' in content
+ * // with
+ * $('#content').highlight('ipsum', { element: 'em', className: 'important' });
+ *
+ * // remove default highlight
+ * $('#content').unhighlight();
+ *
+ * // remove custom highlight
+ * $('#content').unhighlight({ element: 'em', className: 'important' });
+ *
+ *
+ * Copyright (c) 2009 Bartek Szopka
+ *
+ * Licensed under MIT license.
+ *
+ */
+
+jQuery.extend({
+ highlight: function (node, re, nodeName, className) {
+ if (node.nodeType === 3) {
+ var match = node.data.match(re);
+ if (match) {
+ var highlight = document.createElement(nodeName || 'span');
+ highlight.className = className || 'highlight';
+ var wordNode = node.splitText(match.index);
+ wordNode.splitText(match[0].length);
+ var wordClone = wordNode.cloneNode(true);
+ highlight.appendChild(wordClone);
+ wordNode.parentNode.replaceChild(highlight, wordNode);
+ return 1; //skip added node in parent
+ }
+ } else if ((node.nodeType === 1 && node.childNodes) && // only element nodes that have children
+ !/(script|style)/i.test(node.tagName) && // ignore script and style nodes
+ !(node.tagName === nodeName.toUpperCase() && node.className === className)) { // skip if already highlighted
+ for (var i = 0; i < node.childNodes.length; i++) {
+ i += jQuery.highlight(node.childNodes[i], re, nodeName, className);
+ }
+ }
+ return 0;
+ }
+});
+
+jQuery.fn.unhighlight = function (options) {
+ var settings = { className: 'highlight', element: 'span' };
+ jQuery.extend(settings, options);
+
+ return this.find(settings.element + "." + settings.className).each(function () {
+ var parent = this.parentNode;
+ parent.replaceChild(this.firstChild, this);
+ parent.normalize();
+ }).end();
+};
+
+jQuery.fn.highlight = function (words, options) {
+ var settings = { className: 'highlight', element: 'span', caseSensitive: false, wordsOnly: false };
+ jQuery.extend(settings, options);
+
+ if (words.constructor === String) {
+ words = [words];
+ }
+ words = jQuery.grep(words, function(word, i){
+ return word != '';
+ });
+ words = jQuery.map(words, function(word, i) {
+ return word.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
+ });
+ if (words.length == 0) { return this; };
+
+ var flag = settings.caseSensitive ? "" : "i";
+ var pattern = "(" + words.join("|") + ")";
+ if (settings.wordsOnly) {
+ pattern = "\\b" + pattern + "\\b";
+ }
+ var re = new RegExp(pattern, flag);
+
+ return this.each(function () {
+ jQuery.highlight(this, re, settings.element, settings.className);
+ });
+};
+
diff --git a/slate-docs/source/javascripts/lib/_jquery.js b/slate-docs/source/javascripts/lib/_jquery.js
new file mode 100644
index 00000000..50937333
--- /dev/null
+++ b/slate-docs/source/javascripts/lib/_jquery.js
@@ -0,0 +1,10872 @@
+/*!
+ * jQuery JavaScript Library v3.5.1
+ * https://jquery.com/
+ *
+ * Includes Sizzle.js
+ * https://sizzlejs.com/
+ *
+ * Copyright JS Foundation and other contributors
+ * Released under the MIT license
+ * https://jquery.org/license
+ *
+ * Date: 2020-05-04T22:49Z
+ */
+( function( global, factory ) {
+
+ "use strict";
+
+ if ( typeof module === "object" && typeof module.exports === "object" ) {
+
+ // For CommonJS and CommonJS-like environments where a proper `window`
+ // is present, execute the factory and get jQuery.
+ // For environments that do not have a `window` with a `document`
+ // (such as Node.js), expose a factory as module.exports.
+ // This accentuates the need for the creation of a real `window`.
+ // e.g. var jQuery = require("jquery")(window);
+ // See ticket #14549 for more info.
+ module.exports = global.document ?
+ factory( global, true ) :
+ function( w ) {
+ if ( !w.document ) {
+ throw new Error( "jQuery requires a window with a document" );
+ }
+ return factory( w );
+ };
+ } else {
+ factory( global );
+ }
+
+// Pass this if window is not defined yet
+} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
+
+// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1
+// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode
+// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common
+// enough that all such attempts are guarded in a try block.
+"use strict";
+
+var arr = [];
+
+var getProto = Object.getPrototypeOf;
+
+var slice = arr.slice;
+
+var flat = arr.flat ? function( array ) {
+ return arr.flat.call( array );
+} : function( array ) {
+ return arr.concat.apply( [], array );
+};
+
+
+var push = arr.push;
+
+var indexOf = arr.indexOf;
+
+var class2type = {};
+
+var toString = class2type.toString;
+
+var hasOwn = class2type.hasOwnProperty;
+
+var fnToString = hasOwn.toString;
+
+var ObjectFunctionString = fnToString.call( Object );
+
+var support = {};
+
+var isFunction = function isFunction( obj ) {
+
+ // Support: Chrome <=57, Firefox <=52
+ // In some browsers, typeof returns "function" for HTML