├── .gitignore ├── INSTALL.md ├── Makefile ├── README.md ├── blue_app_icon.gif ├── blue_app_icon_testnet.gif ├── client-js ├── .babelrc ├── .eslintrc.json ├── .gitignore ├── .nvmrc ├── .prettierignore ├── LICENSE ├── README.md ├── documentation.yml ├── lerna.json ├── package.json ├── packages │ ├── documentation-website │ │ └── public │ │ │ └── docs │ │ │ ├── assets │ │ │ ├── anchor.js │ │ │ ├── bass-addons.css │ │ │ ├── bass.css │ │ │ ├── fonts │ │ │ │ ├── EOT │ │ │ │ │ ├── SourceCodePro-Bold.eot │ │ │ │ │ └── SourceCodePro-Regular.eot │ │ │ │ ├── LICENSE.txt │ │ │ │ ├── OTF │ │ │ │ │ ├── SourceCodePro-Bold.otf │ │ │ │ │ └── SourceCodePro-Regular.otf │ │ │ │ ├── TTF │ │ │ │ │ ├── SourceCodePro-Bold.ttf │ │ │ │ │ └── SourceCodePro-Regular.ttf │ │ │ │ ├── WOFF │ │ │ │ │ ├── OTF │ │ │ │ │ │ ├── SourceCodePro-Bold.otf.woff │ │ │ │ │ │ └── SourceCodePro-Regular.otf.woff │ │ │ │ │ └── TTF │ │ │ │ │ │ ├── SourceCodePro-Bold.ttf.woff │ │ │ │ │ │ └── SourceCodePro-Regular.ttf.woff │ │ │ │ ├── WOFF2 │ │ │ │ │ ├── OTF │ │ │ │ │ │ ├── SourceCodePro-Bold.otf.woff2 │ │ │ │ │ │ └── SourceCodePro-Regular.otf.woff2 │ │ │ │ │ └── TTF │ │ │ │ │ │ ├── SourceCodePro-Bold.ttf.woff2 │ │ │ │ │ │ └── SourceCodePro-Regular.ttf.woff2 │ │ │ │ └── source-code-pro.css │ │ │ ├── github.css │ │ │ ├── site.js │ │ │ ├── split.css │ │ │ ├── split.js │ │ │ └── style.css │ │ │ └── index.html │ ├── hw-app-icx │ │ ├── .flowconfig │ │ ├── package-lock.json │ │ ├── package.json │ │ └── src │ │ │ ├── Icx.js │ │ │ └── utils.js │ └── test-icx │ │ ├── .babelrc │ │ ├── .gitignore │ │ ├── index.css │ │ ├── index.html │ │ ├── index.js │ │ └── package.json └── yarn.lock ├── dive.sh ├── doc └── icon.asc ├── docker ├── .gitattributes ├── Dockerfile ├── build.sh ├── clang+llvm-4.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz ├── gcc-arm-none-eabi-5_3-2016q1-20160330-linux.tar.bz2 └── nanos-secure-sdk-nanos-1421.tar.gz ├── icon.png ├── icon_testnet.png ├── nanos_app_icon.gif ├── nanos_app_icon_testnet.gif └── src ├── main.c ├── uint256.c └── uint256.h /.gitignore: -------------------------------------------------------------------------------- 1 | dep/* 2 | obj/* 3 | bin/* 4 | debug/* 5 | src/glyphs.* 6 | -------------------------------------------------------------------------------- /INSTALL.md: -------------------------------------------------------------------------------- 1 | # Installation Guide 2 | 3 | ## Platform Requirements 4 | * Docker 5 | * 64bit Linux in virtual or real machine for install. 6 | 7 | If you want to just build binary, docker is enough. But if you want to upload 8 | this binary with python tools, then you should have virtual machine with USB 9 | device support or real machine. 10 | 11 | ## Build Nano-S Application 12 | 13 | ### Prepare docker image 14 | If you already have docker image named `nanos-dev`, then you may skip this 15 | sequence, but if you don't, then Simply run the script in `docker` directory. 16 | 17 | # cd docker 18 | # ./build.sh 19 | 20 | It will build docker image named `nanos-dev`. It may takes several minutes 21 | 22 | ### Compiling sources in the container. 23 | Now, you can build your own application in docker. 24 | 25 | # ./dive.sh make 26 | 27 | And if it doesn't meet any errors, then it makes binary as `bin/app.elf`. 28 | 29 | ## Install Nano-S Application 30 | To upload your application on the device, you should have virtual machine 31 | or real machine with 64bit Linux. If you use virtual machine, then please 32 | make sure that it can access your Nano-S device. 33 | 34 | ### 1. Change virtual machine or real machine udev rule. 35 | 36 | Before you plug your device to the computer. You should add new rule 37 | for the device. 38 | Here is content of `/etc/udev/rules.d/80-nanos.rules` file. 39 | 40 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="0000", MODE="0666" 41 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="0001", MODE="0666" 42 | 43 | To make the file, you should have administrator permission. Please use 44 | `sudo` command for it. 45 | 46 | ### 2. Prepare device. 47 | 48 | Prepare your Nano-S device, and connect it to the devie. And make it show 49 | application selection menu, **DashBoard** application after some steps. 50 | After successful detection you may confirm it with `lsusb` command. 51 | 52 | # lsusb 53 | Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub 54 | Bus 002 Device 021: ID 2c97:0001 55 | Bus 002 Device 002: ID 80ee:0021 VirtualBox USB Tablet 56 | Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub 57 | 58 | You may see the device with the ID, **`2c97`**. 59 | 60 | ### 3. Install application 61 | 62 | Now you can install application with simple commands. 63 | 64 | # ./dive.sh make load 65 | 66 | If you want to remove application 67 | 68 | #./dive.sh make delete 69 | 70 | 71 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # IconX App 3 | # (c) 2018 TheLoop 4 | ################################################################################ 5 | ifeq ($(BOLOS_SDK),) 6 | $(error Environment variable BOLOS_SDK is not set) 7 | endif 8 | include $(BOLOS_SDK)/Makefile.defines 9 | 10 | 11 | # All but bitcoin app use dependency onto the bitcoin app/lib 12 | APP_LOAD_FLAGS=--appFlags 0x50 13 | APP_LOAD_PARAMS=--curve secp256k1 $(COMMON_LOAD_PARAMS) 14 | 15 | APPVERSION_M=1 16 | APPVERSION_N=0 17 | APPVERSION_P=3 18 | APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P) 19 | 20 | APP_LOAD_PARAMS += $(APP_LOAD_FLAGS) 21 | 22 | DEFINES += $(DEFINES_LIB) 23 | 24 | ifeq ($(COIN),) 25 | COIN=icon 26 | endif 27 | 28 | ifeq ($(TARGET_NAME),TARGET_BLUE) 29 | ICONNAME=blue_app_$(COIN).gif 30 | else 31 | ICONNAME=nanos_app_$(COIN).gif 32 | endif 33 | 34 | 35 | ifeq ($(COIN),icon) 36 | APP_PATH = "44'/4801368'" 37 | APPNAME=ICON 38 | else ifeq ($(COIN),icon_testnet) 39 | APP_PATH = "44'/1'" 40 | APPNAME="ICON testnet" 41 | else 42 | ifeq ($(filter clean,$(MAKECMDGOALS)),) 43 | $(error Unsupported COIN - use icon, icon_testnet) 44 | endif 45 | endif 46 | 47 | APP_LOAD_PARAMS += --path $(APP_PATH) 48 | 49 | 50 | 51 | ################ 52 | # Default rule # 53 | ################ 54 | 55 | all: default 56 | 57 | ############ 58 | # Platform # 59 | ############ 60 | 61 | DEFINES += OS_IO_SEPROXYHAL IO_SEPROXYHAL_BUFFER_SIZE_B=300 62 | DEFINES += HAVE_BAGL HAVE_SPRINTF 63 | #DEFINES += HAVE_PRINTF PRINTF=screen_printf 64 | DEFINES += PRINTF\(...\)= 65 | DEFINES += HAVE_IO_USB HAVE_L4_USBLIB IO_USB_MAX_ENDPOINTS=6 IO_HID_EP_LENGTH=64 HAVE_USB_APDU 66 | DEFINES += LEDGER_MAJOR_VERSION=$(APPVERSION_M) LEDGER_MINOR_VERSION=$(APPVERSION_N) LEDGER_PATCH_VERSION=$(APPVERSION_P) TCS_LOADER_PATCH_VERSION=0 67 | DEFINES += UNUSED\(x\)=\(void\)x 68 | DEFINES += APPVERSION=\"$(APPVERSION)\" 69 | 70 | # We don't need compliance with 141 version of cx_xxx APIs. 71 | #DEFINES += CX_COMPLIANCE_141 72 | 73 | CC := $(CLANGPATH)clang 74 | #CFLAGS += -O0 75 | CFLAGS += -O3 -Os 76 | 77 | AS := $(GCCPATH)arm-none-eabi-gcc 78 | 79 | LD := $(GCCPATH)arm-none-eabi-gcc 80 | LDFLAGS += -O3 -Os 81 | LDLIBS += -lm -lgcc -lc 82 | 83 | # import rules to compile glyphs(/pone) 84 | #include $(BOLOS_SDK)/Makefile.glyphs 85 | 86 | ### variables processed by the common makefile.rules of the SDK to grab source files and include dirs 87 | APP_SOURCE_PATH += src 88 | SDK_SOURCE_PATH += lib_stusb 89 | SDK_SOURCE_PATH += lib_u2f lib_stusb_impl 90 | 91 | DEFINES += USB_SEGMENT_SIZE=64 92 | DEFINES += U2F_PROXY_MAGIC=\"ICON\" 93 | DEFINES += HAVE_IO_U2F HAVE_U2F 94 | 95 | load: all 96 | python -m ledgerblue.loadApp $(APP_LOAD_PARAMS) 97 | 98 | delete: 99 | python -m ledgerblue.deleteApp $(COMMON_DELETE_PARAMS) 100 | 101 | # import generic rules from the sdk 102 | include $(BOLOS_SDK)/Makefile.rules 103 | 104 | #add dependency on custom makefile filename 105 | dep/%.d: %.c Makefile 106 | 107 | listvariants: 108 | @echo VARIANTS COIN icon icon_testnet 109 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ICON Ledger App 2 | ICON wallet application for Ledger Nano S. 3 | 4 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 5 | 6 | * Technical Specification: Refer to `doc/icon.asc`. 7 | * JS library to communicate with this ICON Ledger app: Refer to `README.md` under `client-js/` folder. 8 | * Wallet Chrome extension (ICONex) 9 | * [Chrome Web Store](https://chrome.google.com/webstore/detail/iconex/flpiciilemghbmfalicajoolhkkenfel) 10 | * [Source code](https://github.com/icon-project/iconex_chrome_extension) 11 | 12 | ## Release Notes 13 | Refer to [github release page](https://github.com/icon-project/ledger-app-icx/releases). -------------------------------------------------------------------------------- /blue_app_icon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/ledger-app-icx/c2d15e89641b05c9a26e9e094b786c512faacff9/blue_app_icon.gif -------------------------------------------------------------------------------- /blue_app_icon_testnet.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/ledger-app-icx/c2d15e89641b05c9a26e9e094b786c512faacff9/blue_app_icon_testnet.gif -------------------------------------------------------------------------------- /client-js/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "env", 5 | { 6 | "targets": { 7 | "browsers": ["last 2 versions"], 8 | "node": 8 9 | }, 10 | "forceAllTransforms": true 11 | } 12 | ], 13 | "flow", 14 | "react", 15 | "stage-0" 16 | ], 17 | "plugins": ["transform-runtime"] 18 | } 19 | -------------------------------------------------------------------------------- /client-js/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "env": { 4 | "browser": true, 5 | "commonjs": true, 6 | "es6": true, 7 | "jest": true, 8 | "node": true 9 | }, 10 | "extends": [ 11 | "eslint:recommended", 12 | "plugin:react/recommended", 13 | "plugin:flowtype/recommended", 14 | "prettier" 15 | ], 16 | "plugins": ["flowtype", "react"], 17 | "rules": { 18 | "no-console": 0, 19 | "no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], 20 | "linebreak-style": ["error", "unix"], 21 | "quotes": ["error", "double"], 22 | "semi": ["error", "always"], 23 | "flowtype/space-after-type-colon": [ 2, "always", { "allowLineBreak": true } ] 24 | }, 25 | "settings": { 26 | "flowtype": { 27 | "onlyFilesWithFlowAnnotation": true 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /client-js/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | *.pem 4 | browser/ 5 | packages/**/lib/ 6 | flow-typed/ 7 | lerna-debug.log 8 | lerna-error.log 9 | yarn-error.log 10 | *.swp 11 | -------------------------------------------------------------------------------- /client-js/.nvmrc: -------------------------------------------------------------------------------- 1 | v8.9.4 2 | -------------------------------------------------------------------------------- /client-js/.prettierignore: -------------------------------------------------------------------------------- 1 | package.json 2 | -------------------------------------------------------------------------------- /client-js/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /client-js/README.md: -------------------------------------------------------------------------------- 1 | # ICON Ledger JS API 2 | It is the javascript version of client library to communicatie with Ledger Nano S applications. 3 | 4 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 5 | 6 | ## Prerequisite 7 | 8 | ### Build Environment 9 | * yarn 10 | * Node.js 11 | 12 | ### Test Environment 13 | * Ledger device with ICON application installed 14 | 15 | ## Getting Started 16 | 17 | ### Preparation 18 | 1. Connect Ledger Nano S device on a USB port 19 | 1. Make sure Ledger Nano S is initialized and unlocked, and ICON application is installed and started 20 | 21 | ### Build and test 22 | 1. Simply install dependencies, build, and start a simple web server with simple 23 | test-purpose ICON web app 24 | ``` 25 | $ cd client-js 26 | $ yarn 27 | $ yarn build 28 | $ yarn test 29 | ``` 30 | 31 | 1. Access "https://[IP]:9966" on web browser and test 32 | 33 | > It uses chrome built-in extension for U2F so use Chrome web browser 34 | 35 | > Allow web browser to access site of untrusted certificate 36 | which a simple web server BUDO uses. U2F communication is only allowed on HTTPS 37 | 38 | ### Build scripts 39 | 40 | #### Install dependencies 41 | ```bash 42 | yarn 43 | ``` 44 | 45 | #### Build 46 | Build all packages 47 | ```bash 48 | yarn build 49 | ``` 50 | 51 | #### Watch 52 | Watch all packages change. Very useful during development to build only file that changes. 53 | ```bash 54 | yarn watch 55 | ``` 56 | 57 | #### Lint 58 | Lint all packages 59 | ```bash 60 | yarn lint 61 | ``` 62 | 63 | #### Run tests 64 | Launch simple web server with ICON test web app 65 | ``` 66 | yarn test 67 | ``` 68 | 69 | #### Clean 70 | Clean all build output 71 | ``` 72 | yarn clean 73 | ``` 74 | 75 | #### Documentation 76 | Generate API documents at ./packages/documentation-website 77 | ``` 78 | yarn documentation 79 | ``` 80 | 81 | ## API 82 | Refer to [API document](https://icon-project.github.io/ledger-app-icx/client-js/packages/documentation-website/public/docs/index.html). 83 | 84 | ## Examples 85 | ```js 86 | import Transport from "@ledgerhq/hw-transport-u2f"; 87 | import Icx from "@ledgerhq/hw-app-icx"; 88 | 89 | const getIcxAddress = async () => { 90 | const transport = await TransportU2F.create(); 91 | transport.setDebugMode(true); // if you want to print log 92 | transport.setExchangeTimeout(60000); // Set if you want to change U2F timeout. default: 30 sec 93 | 94 | const icx = new Icx(transport); 95 | // coin type: ICX(4801368), ICON testnet(1) 96 | const result = await icx.getAddress("44'/4801368'/0'", true, true); 97 | return result.address; 98 | }; 99 | getIcxAddress().then(a => console.log(a)); 100 | ``` 101 | 102 | ## Remarks 103 | 104 | ### Integration with Chrome Extension 105 | JS library uses chrome pre-built U2F extension "CryptTokenExtension" which allows 106 | https web page as the trusted origin, and chrome extension can't use 107 | directly U2F extension. Therefore chrome extension needs to create an iframe 108 | which pulls https web page integrating with this library and communicate 109 | through Message Channel. 110 | 111 | Refer to https://bugs.chromium.org/p/chromium/issues/detail?id=823736 112 | for the reason U2F extension doesn't allow chrome extension origin. 113 | 114 | -------------------------------------------------------------------------------- /client-js/documentation.yml: -------------------------------------------------------------------------------- 1 | toc: 2 | - Icx 3 | -------------------------------------------------------------------------------- /client-js/lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "lerna": "2.9.0", 3 | "packages": [ 4 | "packages/*" 5 | ], 6 | "version": "4.14.0", 7 | "npmClient": "yarn", 8 | "useWorkspaces": true 9 | } 10 | -------------------------------------------------------------------------------- /client-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "ledger-libs-icx", 4 | "version": "0.0.0", 5 | "workspaces": [ 6 | "packages/*" 7 | ], 8 | "scripts": { 9 | "build": "BABEL_ENV=production && lerna run build", 10 | "clean": "rm -rf node_modules packages/*/node_modules/ packages/*/flow-typed/ packages/*/lib flow-typed && lerna run clean", 11 | "documentation": "documentation build packages/hw-*/src/** --project-name '@ledgerhq/*' --project-version `node -e \"console.log(require('./lerna.json').version)\"` --project-homepage https://repo.theloop.co.kr/enterprise/ledger-app-icx --config documentation.yml -g -f html -o packages/documentation-website/public/docs", 12 | "lint": "eslint packages/**/src", 13 | "test": "cd packages/test-icx && yarn run test", 14 | "watch": "lerna run --parallel watch" 15 | }, 16 | "dependencies": { 17 | "babel-cli": "^6.26.0", 18 | "babel-eslint": "^8.0.2", 19 | "babel-plugin-transform-runtime": "^6.23.0", 20 | "babel-preset-env": "^1.6.1", 21 | "babel-preset-flow": "^6.23.0", 22 | "babel-preset-react": "^6.24.1", 23 | "babel-preset-stage-0": "^6.24.1", 24 | "babel-runtime": "^6.26.0", 25 | "browserify": "^16.1.1", 26 | "create-hash": "^1.1.3", 27 | "documentation": "^6.1.0", 28 | "eslint": "^4.11.0", 29 | "eslint-config-prettier": "^2.7.0", 30 | "eslint-plugin-flowtype": "^2.42.0", 31 | "eslint-plugin-react": "^7.6.1", 32 | "flow-bin": "^0.68.0", 33 | "flow-copy-source": "^1.2.2", 34 | "flow-mono-cli": "^1.3.0", 35 | "flow-typed": "^2.4.0", 36 | "handlebars": "4.1.0", 37 | "lerna": "2.9.0", 38 | "lodash.merge": "4.6.2", 39 | "prettier": "^1.10.2", 40 | "uglify-js": "^3.3.15" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /client-js/packages/documentation-website/public/docs/assets/anchor.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * AnchorJS - v4.0.0 - 2017-06-02 3 | * https://github.com/bryanbraun/anchorjs 4 | * Copyright (c) 2017 Bryan Braun; Licensed MIT 5 | */ 6 | /* eslint-env amd, node */ 7 | 8 | // https://github.com/umdjs/umd/blob/master/templates/returnExports.js 9 | (function(root, factory) { 10 | 'use strict'; 11 | if (typeof define === 'function' && define.amd) { 12 | // AMD. Register as an anonymous module. 13 | define([], factory); 14 | } else if (typeof module === 'object' && module.exports) { 15 | // Node. Does not work with strict CommonJS, but 16 | // only CommonJS-like environments that support module.exports, 17 | // like Node. 18 | module.exports = factory(); 19 | } else { 20 | // Browser globals (root is window) 21 | root.AnchorJS = factory(); 22 | root.anchors = new root.AnchorJS(); 23 | } 24 | })(this, function() { 25 | 'use strict'; 26 | function AnchorJS(options) { 27 | this.options = options || {}; 28 | this.elements = []; 29 | 30 | /** 31 | * Assigns options to the internal options object, and provides defaults. 32 | * @param {Object} opts - Options object 33 | */ 34 | function _applyRemainingDefaultOptions(opts) { 35 | opts.icon = opts.hasOwnProperty('icon') ? opts.icon : '\ue9cb'; // Accepts characters (and also URLs?), like '#', '¶', '❡', or '§'. 36 | opts.visible = opts.hasOwnProperty('visible') ? opts.visible : 'hover'; // Also accepts 'always' & 'touch' 37 | opts.placement = opts.hasOwnProperty('placement') 38 | ? opts.placement 39 | : 'right'; // Also accepts 'left' 40 | opts.class = opts.hasOwnProperty('class') ? opts.class : ''; // Accepts any class name. 41 | // Using Math.floor here will ensure the value is Number-cast and an integer. 42 | opts.truncate = opts.hasOwnProperty('truncate') 43 | ? Math.floor(opts.truncate) 44 | : 64; // Accepts any value that can be typecast to a number. 45 | } 46 | 47 | _applyRemainingDefaultOptions(this.options); 48 | 49 | /** 50 | * Checks to see if this device supports touch. Uses criteria pulled from Modernizr: 51 | * https://github.com/Modernizr/Modernizr/blob/da22eb27631fc4957f67607fe6042e85c0a84656/feature-detects/touchevents.js#L40 52 | * @returns {Boolean} - true if the current device supports touch. 53 | */ 54 | this.isTouchDevice = function() { 55 | return !!( 56 | 'ontouchstart' in window || 57 | (window.DocumentTouch && document instanceof DocumentTouch) 58 | ); 59 | }; 60 | 61 | /** 62 | * Add anchor links to page elements. 63 | * @param {String|Array|Nodelist} selector - A CSS selector for targeting the elements you wish to add anchor links 64 | * to. Also accepts an array or nodeList containing the relavant elements. 65 | * @returns {this} - The AnchorJS object 66 | */ 67 | this.add = function(selector) { 68 | var elements, 69 | elsWithIds, 70 | idList, 71 | elementID, 72 | i, 73 | index, 74 | count, 75 | tidyText, 76 | newTidyText, 77 | readableID, 78 | anchor, 79 | visibleOptionToUse, 80 | indexesToDrop = []; 81 | 82 | // We reapply options here because somebody may have overwritten the default options object when setting options. 83 | // For example, this overwrites all options but visible: 84 | // 85 | // anchors.options = { visible: 'always'; } 86 | _applyRemainingDefaultOptions(this.options); 87 | 88 | visibleOptionToUse = this.options.visible; 89 | if (visibleOptionToUse === 'touch') { 90 | visibleOptionToUse = this.isTouchDevice() ? 'always' : 'hover'; 91 | } 92 | 93 | // Provide a sensible default selector, if none is given. 94 | if (!selector) { 95 | selector = 'h2, h3, h4, h5, h6'; 96 | } 97 | 98 | elements = _getElements(selector); 99 | 100 | if (elements.length === 0) { 101 | return this; 102 | } 103 | 104 | _addBaselineStyles(); 105 | 106 | // We produce a list of existing IDs so we don't generate a duplicate. 107 | elsWithIds = document.querySelectorAll('[id]'); 108 | idList = [].map.call(elsWithIds, function assign(el) { 109 | return el.id; 110 | }); 111 | 112 | for (i = 0; i < elements.length; i++) { 113 | if (this.hasAnchorJSLink(elements[i])) { 114 | indexesToDrop.push(i); 115 | continue; 116 | } 117 | 118 | if (elements[i].hasAttribute('id')) { 119 | elementID = elements[i].getAttribute('id'); 120 | } else if (elements[i].hasAttribute('data-anchor-id')) { 121 | elementID = elements[i].getAttribute('data-anchor-id'); 122 | } else { 123 | tidyText = this.urlify(elements[i].textContent); 124 | 125 | // Compare our generated ID to existing IDs (and increment it if needed) 126 | // before we add it to the page. 127 | newTidyText = tidyText; 128 | count = 0; 129 | do { 130 | if (index !== undefined) { 131 | newTidyText = tidyText + '-' + count; 132 | } 133 | 134 | index = idList.indexOf(newTidyText); 135 | count += 1; 136 | } while (index !== -1); 137 | index = undefined; 138 | idList.push(newTidyText); 139 | 140 | elements[i].setAttribute('id', newTidyText); 141 | elementID = newTidyText; 142 | } 143 | 144 | readableID = elementID.replace(/-/g, ' '); 145 | 146 | // The following code builds the following DOM structure in a more effiecient (albeit opaque) way. 147 | // ''; 148 | anchor = document.createElement('a'); 149 | anchor.className = 'anchorjs-link ' + this.options.class; 150 | anchor.href = '#' + elementID; 151 | anchor.setAttribute('aria-label', 'Anchor link for: ' + readableID); 152 | anchor.setAttribute('data-anchorjs-icon', this.options.icon); 153 | 154 | if (visibleOptionToUse === 'always') { 155 | anchor.style.opacity = '1'; 156 | } 157 | 158 | if (this.options.icon === '\ue9cb') { 159 | anchor.style.font = '1em/1 anchorjs-icons'; 160 | 161 | // We set lineHeight = 1 here because the `anchorjs-icons` font family could otherwise affect the 162 | // height of the heading. This isn't the case for icons with `placement: left`, so we restore 163 | // line-height: inherit in that case, ensuring they remain positioned correctly. For more info, 164 | // see https://github.com/bryanbraun/anchorjs/issues/39. 165 | if (this.options.placement === 'left') { 166 | anchor.style.lineHeight = 'inherit'; 167 | } 168 | } 169 | 170 | if (this.options.placement === 'left') { 171 | anchor.style.position = 'absolute'; 172 | anchor.style.marginLeft = '-1em'; 173 | anchor.style.paddingRight = '0.5em'; 174 | elements[i].insertBefore(anchor, elements[i].firstChild); 175 | } else { 176 | // if the option provided is `right` (or anything else). 177 | anchor.style.paddingLeft = '0.375em'; 178 | elements[i].appendChild(anchor); 179 | } 180 | } 181 | 182 | for (i = 0; i < indexesToDrop.length; i++) { 183 | elements.splice(indexesToDrop[i] - i, 1); 184 | } 185 | this.elements = this.elements.concat(elements); 186 | 187 | return this; 188 | }; 189 | 190 | /** 191 | * Removes all anchorjs-links from elements targed by the selector. 192 | * @param {String|Array|Nodelist} selector - A CSS selector string targeting elements with anchor links, 193 | * OR a nodeList / array containing the DOM elements. 194 | * @returns {this} - The AnchorJS object 195 | */ 196 | this.remove = function(selector) { 197 | var index, 198 | domAnchor, 199 | elements = _getElements(selector); 200 | 201 | for (var i = 0; i < elements.length; i++) { 202 | domAnchor = elements[i].querySelector('.anchorjs-link'); 203 | if (domAnchor) { 204 | // Drop the element from our main list, if it's in there. 205 | index = this.elements.indexOf(elements[i]); 206 | if (index !== -1) { 207 | this.elements.splice(index, 1); 208 | } 209 | // Remove the anchor from the DOM. 210 | elements[i].removeChild(domAnchor); 211 | } 212 | } 213 | return this; 214 | }; 215 | 216 | /** 217 | * Removes all anchorjs links. Mostly used for tests. 218 | */ 219 | this.removeAll = function() { 220 | this.remove(this.elements); 221 | }; 222 | 223 | /** 224 | * Urlify - Refine text so it makes a good ID. 225 | * 226 | * To do this, we remove apostrophes, replace nonsafe characters with hyphens, 227 | * remove extra hyphens, truncate, trim hyphens, and make lowercase. 228 | * 229 | * @param {String} text - Any text. Usually pulled from the webpage element we are linking to. 230 | * @returns {String} - hyphen-delimited text for use in IDs and URLs. 231 | */ 232 | this.urlify = function(text) { 233 | // Regex for finding the nonsafe URL characters (many need escaping): & +$,:;=?@"#{}|^~[`%!'<>]./()*\ 234 | var nonsafeChars = /[& +$,:;=?@"#{}|^~[`%!'<>\]\.\/\(\)\*\\]/g, 235 | urlText; 236 | 237 | // The reason we include this _applyRemainingDefaultOptions is so urlify can be called independently, 238 | // even after setting options. This can be useful for tests or other applications. 239 | if (!this.options.truncate) { 240 | _applyRemainingDefaultOptions(this.options); 241 | } 242 | 243 | // Note: we trim hyphens after truncating because truncating can cause dangling hyphens. 244 | // Example string: // " ⚡⚡ Don't forget: URL fragments should be i18n-friendly, hyphenated, short, and clean." 245 | urlText = text 246 | .trim() // "⚡⚡ Don't forget: URL fragments should be i18n-friendly, hyphenated, short, and clean." 247 | .replace(/\'/gi, '') // "⚡⚡ Dont forget: URL fragments should be i18n-friendly, hyphenated, short, and clean." 248 | .replace(nonsafeChars, '-') // "⚡⚡-Dont-forget--URL-fragments-should-be-i18n-friendly--hyphenated--short--and-clean-" 249 | .replace(/-{2,}/g, '-') // "⚡⚡-Dont-forget-URL-fragments-should-be-i18n-friendly-hyphenated-short-and-clean-" 250 | .substring(0, this.options.truncate) // "⚡⚡-Dont-forget-URL-fragments-should-be-i18n-friendly-hyphenated-" 251 | .replace(/^-+|-+$/gm, '') // "⚡⚡-Dont-forget-URL-fragments-should-be-i18n-friendly-hyphenated" 252 | .toLowerCase(); // "⚡⚡-dont-forget-url-fragments-should-be-i18n-friendly-hyphenated" 253 | 254 | return urlText; 255 | }; 256 | 257 | /** 258 | * Determines if this element already has an AnchorJS link on it. 259 | * Uses this technique: http://stackoverflow.com/a/5898748/1154642 260 | * @param {HTMLElemnt} el - a DOM node 261 | * @returns {Boolean} true/false 262 | */ 263 | this.hasAnchorJSLink = function(el) { 264 | var hasLeftAnchor = 265 | el.firstChild && 266 | (' ' + el.firstChild.className + ' ').indexOf(' anchorjs-link ') > -1, 267 | hasRightAnchor = 268 | el.lastChild && 269 | (' ' + el.lastChild.className + ' ').indexOf(' anchorjs-link ') > -1; 270 | 271 | return hasLeftAnchor || hasRightAnchor || false; 272 | }; 273 | 274 | /** 275 | * Turns a selector, nodeList, or array of elements into an array of elements (so we can use array methods). 276 | * It also throws errors on any other inputs. Used to handle inputs to .add and .remove. 277 | * @param {String|Array|Nodelist} input - A CSS selector string targeting elements with anchor links, 278 | * OR a nodeList / array containing the DOM elements. 279 | * @returns {Array} - An array containing the elements we want. 280 | */ 281 | function _getElements(input) { 282 | var elements; 283 | if (typeof input === 'string' || input instanceof String) { 284 | // See https://davidwalsh.name/nodelist-array for the technique transforming nodeList -> Array. 285 | elements = [].slice.call(document.querySelectorAll(input)); 286 | // I checked the 'input instanceof NodeList' test in IE9 and modern browsers and it worked for me. 287 | } else if (Array.isArray(input) || input instanceof NodeList) { 288 | elements = [].slice.call(input); 289 | } else { 290 | throw new Error('The selector provided to AnchorJS was invalid.'); 291 | } 292 | return elements; 293 | } 294 | 295 | /** 296 | * _addBaselineStyles 297 | * Adds baseline styles to the page, used by all AnchorJS links irregardless of configuration. 298 | */ 299 | function _addBaselineStyles() { 300 | // We don't want to add global baseline styles if they've been added before. 301 | if (document.head.querySelector('style.anchorjs') !== null) { 302 | return; 303 | } 304 | 305 | var style = document.createElement('style'), 306 | linkRule = 307 | ' .anchorjs-link {' + 308 | ' opacity: 0;' + 309 | ' text-decoration: none;' + 310 | ' -webkit-font-smoothing: antialiased;' + 311 | ' -moz-osx-font-smoothing: grayscale;' + 312 | ' }', 313 | hoverRule = 314 | ' *:hover > .anchorjs-link,' + 315 | ' .anchorjs-link:focus {' + 316 | ' opacity: 1;' + 317 | ' }', 318 | anchorjsLinkFontFace = 319 | ' @font-face {' + 320 | ' font-family: "anchorjs-icons";' + // Icon from icomoon; 10px wide & 10px tall; 2 empty below & 4 above 321 | ' src: url(data:n/a;base64,AAEAAAALAIAAAwAwT1MvMg8yG2cAAAE4AAAAYGNtYXDp3gC3AAABpAAAAExnYXNwAAAAEAAAA9wAAAAIZ2x5ZlQCcfwAAAH4AAABCGhlYWQHFvHyAAAAvAAAADZoaGVhBnACFwAAAPQAAAAkaG10eASAADEAAAGYAAAADGxvY2EACACEAAAB8AAAAAhtYXhwAAYAVwAAARgAAAAgbmFtZQGOH9cAAAMAAAAAunBvc3QAAwAAAAADvAAAACAAAQAAAAEAAHzE2p9fDzz1AAkEAAAAAADRecUWAAAAANQA6R8AAAAAAoACwAAAAAgAAgAAAAAAAAABAAADwP/AAAACgAAA/9MCrQABAAAAAAAAAAAAAAAAAAAAAwABAAAAAwBVAAIAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAMCQAGQAAUAAAKZAswAAACPApkCzAAAAesAMwEJAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAQAAg//0DwP/AAEADwABAAAAAAQAAAAAAAAAAAAAAIAAAAAAAAAIAAAACgAAxAAAAAwAAAAMAAAAcAAEAAwAAABwAAwABAAAAHAAEADAAAAAIAAgAAgAAACDpy//9//8AAAAg6cv//f///+EWNwADAAEAAAAAAAAAAAAAAAAACACEAAEAAAAAAAAAAAAAAAAxAAACAAQARAKAAsAAKwBUAAABIiYnJjQ3NzY2MzIWFxYUBwcGIicmNDc3NjQnJiYjIgYHBwYUFxYUBwYGIwciJicmNDc3NjIXFhQHBwYUFxYWMzI2Nzc2NCcmNDc2MhcWFAcHBgYjARQGDAUtLXoWOR8fORYtLTgKGwoKCjgaGg0gEhIgDXoaGgkJBQwHdR85Fi0tOAobCgoKOBoaDSASEiANehoaCQkKGwotLXoWOR8BMwUFLYEuehYXFxYugC44CQkKGwo4GkoaDQ0NDXoaShoKGwoFBe8XFi6ALjgJCQobCjgaShoNDQ0NehpKGgobCgoKLYEuehYXAAAADACWAAEAAAAAAAEACAAAAAEAAAAAAAIAAwAIAAEAAAAAAAMACAAAAAEAAAAAAAQACAAAAAEAAAAAAAUAAQALAAEAAAAAAAYACAAAAAMAAQQJAAEAEAAMAAMAAQQJAAIABgAcAAMAAQQJAAMAEAAMAAMAAQQJAAQAEAAMAAMAAQQJAAUAAgAiAAMAAQQJAAYAEAAMYW5jaG9yanM0MDBAAGEAbgBjAGgAbwByAGoAcwA0ADAAMABAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAH//wAP) format("truetype");' + 322 | ' }', 323 | pseudoElContent = 324 | ' [data-anchorjs-icon]::after {' + 325 | ' content: attr(data-anchorjs-icon);' + 326 | ' }', 327 | firstStyleEl; 328 | 329 | style.className = 'anchorjs'; 330 | style.appendChild(document.createTextNode('')); // Necessary for Webkit. 331 | 332 | // We place it in the head with the other style tags, if possible, so as to 333 | // not look out of place. We insert before the others so these styles can be 334 | // overridden if necessary. 335 | firstStyleEl = document.head.querySelector('[rel="stylesheet"], style'); 336 | if (firstStyleEl === undefined) { 337 | document.head.appendChild(style); 338 | } else { 339 | document.head.insertBefore(style, firstStyleEl); 340 | } 341 | 342 | style.sheet.insertRule(linkRule, style.sheet.cssRules.length); 343 | style.sheet.insertRule(hoverRule, style.sheet.cssRules.length); 344 | style.sheet.insertRule(pseudoElContent, style.sheet.cssRules.length); 345 | style.sheet.insertRule(anchorjsLinkFontFace, style.sheet.cssRules.length); 346 | } 347 | } 348 | 349 | return AnchorJS; 350 | }); 351 | -------------------------------------------------------------------------------- /client-js/packages/documentation-website/public/docs/assets/bass-addons.css: -------------------------------------------------------------------------------- 1 | .input { 2 | font-family: inherit; 3 | display: block; 4 | width: 100%; 5 | height: 2rem; 6 | padding: .5rem; 7 | margin-bottom: 1rem; 8 | border: 1px solid #ccc; 9 | font-size: .875rem; 10 | border-radius: 3px; 11 | box-sizing: border-box; 12 | } 13 | -------------------------------------------------------------------------------- /client-js/packages/documentation-website/public/docs/assets/bass.css: -------------------------------------------------------------------------------- 1 | /*! Basscss | http://basscss.com | MIT License */ 2 | 3 | .h1{ font-size: 2rem } 4 | .h2{ font-size: 1.5rem } 5 | .h3{ font-size: 1.25rem } 6 | .h4{ font-size: 1rem } 7 | .h5{ font-size: .875rem } 8 | .h6{ font-size: .75rem } 9 | 10 | .font-family-inherit{ font-family:inherit } 11 | .font-size-inherit{ font-size:inherit } 12 | .text-decoration-none{ text-decoration:none } 13 | 14 | .bold{ font-weight: bold; font-weight: bold } 15 | .regular{ font-weight:normal } 16 | .italic{ font-style:italic } 17 | .caps{ text-transform:uppercase; letter-spacing: .2em; } 18 | 19 | .left-align{ text-align:left } 20 | .center{ text-align:center } 21 | .right-align{ text-align:right } 22 | .justify{ text-align:justify } 23 | 24 | .nowrap{ white-space:nowrap } 25 | .break-word{ word-wrap:break-word } 26 | 27 | .line-height-1{ line-height: 1 } 28 | .line-height-2{ line-height: 1.125 } 29 | .line-height-3{ line-height: 1.25 } 30 | .line-height-4{ line-height: 1.5 } 31 | 32 | .list-style-none{ list-style:none } 33 | .underline{ text-decoration:underline } 34 | 35 | .truncate{ 36 | max-width:100%; 37 | overflow:hidden; 38 | text-overflow:ellipsis; 39 | white-space:nowrap; 40 | } 41 | 42 | .list-reset{ 43 | list-style:none; 44 | padding-left:0; 45 | } 46 | 47 | .inline{ display:inline } 48 | .block{ display:block } 49 | .inline-block{ display:inline-block } 50 | .table{ display:table } 51 | .table-cell{ display:table-cell } 52 | 53 | .overflow-hidden{ overflow:hidden } 54 | .overflow-scroll{ overflow:scroll } 55 | .overflow-auto{ overflow:auto } 56 | 57 | .clearfix:before, 58 | .clearfix:after{ 59 | content:" "; 60 | display:table 61 | } 62 | .clearfix:after{ clear:both } 63 | 64 | .left{ float:left } 65 | .right{ float:right } 66 | 67 | .fit{ max-width:100% } 68 | 69 | .max-width-1{ max-width: 24rem } 70 | .max-width-2{ max-width: 32rem } 71 | .max-width-3{ max-width: 48rem } 72 | .max-width-4{ max-width: 64rem } 73 | 74 | .border-box{ box-sizing:border-box } 75 | 76 | .align-baseline{ vertical-align:baseline } 77 | .align-top{ vertical-align:top } 78 | .align-middle{ vertical-align:middle } 79 | .align-bottom{ vertical-align:bottom } 80 | 81 | .m0{ margin:0 } 82 | .mt0{ margin-top:0 } 83 | .mr0{ margin-right:0 } 84 | .mb0{ margin-bottom:0 } 85 | .ml0{ margin-left:0 } 86 | .mx0{ margin-left:0; margin-right:0 } 87 | .my0{ margin-top:0; margin-bottom:0 } 88 | 89 | .m1{ margin: .5rem } 90 | .mt1{ margin-top: .5rem } 91 | .mr1{ margin-right: .5rem } 92 | .mb1{ margin-bottom: .5rem } 93 | .ml1{ margin-left: .5rem } 94 | .mx1{ margin-left: .5rem; margin-right: .5rem } 95 | .my1{ margin-top: .5rem; margin-bottom: .5rem } 96 | 97 | .m2{ margin: 1rem } 98 | .mt2{ margin-top: 1rem } 99 | .mr2{ margin-right: 1rem } 100 | .mb2{ margin-bottom: 1rem } 101 | .ml2{ margin-left: 1rem } 102 | .mx2{ margin-left: 1rem; margin-right: 1rem } 103 | .my2{ margin-top: 1rem; margin-bottom: 1rem } 104 | 105 | .m3{ margin: 2rem } 106 | .mt3{ margin-top: 2rem } 107 | .mr3{ margin-right: 2rem } 108 | .mb3{ margin-bottom: 2rem } 109 | .ml3{ margin-left: 2rem } 110 | .mx3{ margin-left: 2rem; margin-right: 2rem } 111 | .my3{ margin-top: 2rem; margin-bottom: 2rem } 112 | 113 | .m4{ margin: 4rem } 114 | .mt4{ margin-top: 4rem } 115 | .mr4{ margin-right: 4rem } 116 | .mb4{ margin-bottom: 4rem } 117 | .ml4{ margin-left: 4rem } 118 | .mx4{ margin-left: 4rem; margin-right: 4rem } 119 | .my4{ margin-top: 4rem; margin-bottom: 4rem } 120 | 121 | .mxn1{ margin-left: -.5rem; margin-right: -.5rem; } 122 | .mxn2{ margin-left: -1rem; margin-right: -1rem; } 123 | .mxn3{ margin-left: -2rem; margin-right: -2rem; } 124 | .mxn4{ margin-left: -4rem; margin-right: -4rem; } 125 | 126 | .ml-auto{ margin-left:auto } 127 | .mr-auto{ margin-right:auto } 128 | .mx-auto{ margin-left:auto; margin-right:auto; } 129 | 130 | .p0{ padding:0 } 131 | .pt0{ padding-top:0 } 132 | .pr0{ padding-right:0 } 133 | .pb0{ padding-bottom:0 } 134 | .pl0{ padding-left:0 } 135 | .px0{ padding-left:0; padding-right:0 } 136 | .py0{ padding-top:0; padding-bottom:0 } 137 | 138 | .p1{ padding: .5rem } 139 | .pt1{ padding-top: .5rem } 140 | .pr1{ padding-right: .5rem } 141 | .pb1{ padding-bottom: .5rem } 142 | .pl1{ padding-left: .5rem } 143 | .py1{ padding-top: .5rem; padding-bottom: .5rem } 144 | .px1{ padding-left: .5rem; padding-right: .5rem } 145 | 146 | .p2{ padding: 1rem } 147 | .pt2{ padding-top: 1rem } 148 | .pr2{ padding-right: 1rem } 149 | .pb2{ padding-bottom: 1rem } 150 | .pl2{ padding-left: 1rem } 151 | .py2{ padding-top: 1rem; padding-bottom: 1rem } 152 | .px2{ padding-left: 1rem; padding-right: 1rem } 153 | 154 | .p3{ padding: 2rem } 155 | .pt3{ padding-top: 2rem } 156 | .pr3{ padding-right: 2rem } 157 | .pb3{ padding-bottom: 2rem } 158 | .pl3{ padding-left: 2rem } 159 | .py3{ padding-top: 2rem; padding-bottom: 2rem } 160 | .px3{ padding-left: 2rem; padding-right: 2rem } 161 | 162 | .p4{ padding: 4rem } 163 | .pt4{ padding-top: 4rem } 164 | .pr4{ padding-right: 4rem } 165 | .pb4{ padding-bottom: 4rem } 166 | .pl4{ padding-left: 4rem } 167 | .py4{ padding-top: 4rem; padding-bottom: 4rem } 168 | .px4{ padding-left: 4rem; padding-right: 4rem } 169 | 170 | .col{ 171 | float:left; 172 | box-sizing:border-box; 173 | } 174 | 175 | .col-right{ 176 | float:right; 177 | box-sizing:border-box; 178 | } 179 | 180 | .col-1{ 181 | width:8.33333%; 182 | } 183 | 184 | .col-2{ 185 | width:16.66667%; 186 | } 187 | 188 | .col-3{ 189 | width:25%; 190 | } 191 | 192 | .col-4{ 193 | width:33.33333%; 194 | } 195 | 196 | .col-5{ 197 | width:41.66667%; 198 | } 199 | 200 | .col-6{ 201 | width:50%; 202 | } 203 | 204 | .col-7{ 205 | width:58.33333%; 206 | } 207 | 208 | .col-8{ 209 | width:66.66667%; 210 | } 211 | 212 | .col-9{ 213 | width:75%; 214 | } 215 | 216 | .col-10{ 217 | width:83.33333%; 218 | } 219 | 220 | .col-11{ 221 | width:91.66667%; 222 | } 223 | 224 | .col-12{ 225 | width:100%; 226 | } 227 | @media (min-width: 40em){ 228 | 229 | .sm-col{ 230 | float:left; 231 | box-sizing:border-box; 232 | } 233 | 234 | .sm-col-right{ 235 | float:right; 236 | box-sizing:border-box; 237 | } 238 | 239 | .sm-col-1{ 240 | width:8.33333%; 241 | } 242 | 243 | .sm-col-2{ 244 | width:16.66667%; 245 | } 246 | 247 | .sm-col-3{ 248 | width:25%; 249 | } 250 | 251 | .sm-col-4{ 252 | width:33.33333%; 253 | } 254 | 255 | .sm-col-5{ 256 | width:41.66667%; 257 | } 258 | 259 | .sm-col-6{ 260 | width:50%; 261 | } 262 | 263 | .sm-col-7{ 264 | width:58.33333%; 265 | } 266 | 267 | .sm-col-8{ 268 | width:66.66667%; 269 | } 270 | 271 | .sm-col-9{ 272 | width:75%; 273 | } 274 | 275 | .sm-col-10{ 276 | width:83.33333%; 277 | } 278 | 279 | .sm-col-11{ 280 | width:91.66667%; 281 | } 282 | 283 | .sm-col-12{ 284 | width:100%; 285 | } 286 | 287 | } 288 | @media (min-width: 52em){ 289 | 290 | .md-col{ 291 | float:left; 292 | box-sizing:border-box; 293 | } 294 | 295 | .md-col-right{ 296 | float:right; 297 | box-sizing:border-box; 298 | } 299 | 300 | .md-col-1{ 301 | width:8.33333%; 302 | } 303 | 304 | .md-col-2{ 305 | width:16.66667%; 306 | } 307 | 308 | .md-col-3{ 309 | width:25%; 310 | } 311 | 312 | .md-col-4{ 313 | width:33.33333%; 314 | } 315 | 316 | .md-col-5{ 317 | width:41.66667%; 318 | } 319 | 320 | .md-col-6{ 321 | width:50%; 322 | } 323 | 324 | .md-col-7{ 325 | width:58.33333%; 326 | } 327 | 328 | .md-col-8{ 329 | width:66.66667%; 330 | } 331 | 332 | .md-col-9{ 333 | width:75%; 334 | } 335 | 336 | .md-col-10{ 337 | width:83.33333%; 338 | } 339 | 340 | .md-col-11{ 341 | width:91.66667%; 342 | } 343 | 344 | .md-col-12{ 345 | width:100%; 346 | } 347 | 348 | } 349 | @media (min-width: 64em){ 350 | 351 | .lg-col{ 352 | float:left; 353 | box-sizing:border-box; 354 | } 355 | 356 | .lg-col-right{ 357 | float:right; 358 | box-sizing:border-box; 359 | } 360 | 361 | .lg-col-1{ 362 | width:8.33333%; 363 | } 364 | 365 | .lg-col-2{ 366 | width:16.66667%; 367 | } 368 | 369 | .lg-col-3{ 370 | width:25%; 371 | } 372 | 373 | .lg-col-4{ 374 | width:33.33333%; 375 | } 376 | 377 | .lg-col-5{ 378 | width:41.66667%; 379 | } 380 | 381 | .lg-col-6{ 382 | width:50%; 383 | } 384 | 385 | .lg-col-7{ 386 | width:58.33333%; 387 | } 388 | 389 | .lg-col-8{ 390 | width:66.66667%; 391 | } 392 | 393 | .lg-col-9{ 394 | width:75%; 395 | } 396 | 397 | .lg-col-10{ 398 | width:83.33333%; 399 | } 400 | 401 | .lg-col-11{ 402 | width:91.66667%; 403 | } 404 | 405 | .lg-col-12{ 406 | width:100%; 407 | } 408 | 409 | } 410 | .flex{ display:-webkit-box; display:-webkit-flex; display:-ms-flexbox; display:flex } 411 | 412 | @media (min-width: 40em){ 413 | .sm-flex{ display:-webkit-box; display:-webkit-flex; display:-ms-flexbox; display:flex } 414 | } 415 | 416 | @media (min-width: 52em){ 417 | .md-flex{ display:-webkit-box; display:-webkit-flex; display:-ms-flexbox; display:flex } 418 | } 419 | 420 | @media (min-width: 64em){ 421 | .lg-flex{ display:-webkit-box; display:-webkit-flex; display:-ms-flexbox; display:flex } 422 | } 423 | 424 | .flex-column{ -webkit-box-orient:vertical; -webkit-box-direction:normal; -webkit-flex-direction:column; -ms-flex-direction:column; flex-direction:column } 425 | .flex-wrap{ -webkit-flex-wrap:wrap; -ms-flex-wrap:wrap; flex-wrap:wrap } 426 | 427 | .items-start{ -webkit-box-align:start; -webkit-align-items:flex-start; -ms-flex-align:start; -ms-grid-row-align:flex-start; align-items:flex-start } 428 | .items-end{ -webkit-box-align:end; -webkit-align-items:flex-end; -ms-flex-align:end; -ms-grid-row-align:flex-end; align-items:flex-end } 429 | .items-center{ -webkit-box-align:center; -webkit-align-items:center; -ms-flex-align:center; -ms-grid-row-align:center; align-items:center } 430 | .items-baseline{ -webkit-box-align:baseline; -webkit-align-items:baseline; -ms-flex-align:baseline; -ms-grid-row-align:baseline; align-items:baseline } 431 | .items-stretch{ -webkit-box-align:stretch; -webkit-align-items:stretch; -ms-flex-align:stretch; -ms-grid-row-align:stretch; align-items:stretch } 432 | 433 | .self-start{ -webkit-align-self:flex-start; -ms-flex-item-align:start; align-self:flex-start } 434 | .self-end{ -webkit-align-self:flex-end; -ms-flex-item-align:end; align-self:flex-end } 435 | .self-center{ -webkit-align-self:center; -ms-flex-item-align:center; align-self:center } 436 | .self-baseline{ -webkit-align-self:baseline; -ms-flex-item-align:baseline; align-self:baseline } 437 | .self-stretch{ -webkit-align-self:stretch; -ms-flex-item-align:stretch; align-self:stretch } 438 | 439 | .justify-start{ -webkit-box-pack:start; -webkit-justify-content:flex-start; -ms-flex-pack:start; justify-content:flex-start } 440 | .justify-end{ -webkit-box-pack:end; -webkit-justify-content:flex-end; -ms-flex-pack:end; justify-content:flex-end } 441 | .justify-center{ -webkit-box-pack:center; -webkit-justify-content:center; -ms-flex-pack:center; justify-content:center } 442 | .justify-between{ -webkit-box-pack:justify; -webkit-justify-content:space-between; -ms-flex-pack:justify; justify-content:space-between } 443 | .justify-around{ -webkit-justify-content:space-around; -ms-flex-pack:distribute; justify-content:space-around } 444 | 445 | .content-start{ -webkit-align-content:flex-start; -ms-flex-line-pack:start; align-content:flex-start } 446 | .content-end{ -webkit-align-content:flex-end; -ms-flex-line-pack:end; align-content:flex-end } 447 | .content-center{ -webkit-align-content:center; -ms-flex-line-pack:center; align-content:center } 448 | .content-between{ -webkit-align-content:space-between; -ms-flex-line-pack:justify; align-content:space-between } 449 | .content-around{ -webkit-align-content:space-around; -ms-flex-line-pack:distribute; align-content:space-around } 450 | .content-stretch{ -webkit-align-content:stretch; -ms-flex-line-pack:stretch; align-content:stretch } 451 | .flex-auto{ 452 | -webkit-box-flex:1; 453 | -webkit-flex:1 1 auto; 454 | -ms-flex:1 1 auto; 455 | flex:1 1 auto; 456 | min-width:0; 457 | min-height:0; 458 | } 459 | .flex-none{ -webkit-box-flex:0; -webkit-flex:none; -ms-flex:none; flex:none } 460 | .fs0{ flex-shrink: 0 } 461 | 462 | .order-0{ -webkit-box-ordinal-group:1; -webkit-order:0; -ms-flex-order:0; order:0 } 463 | .order-1{ -webkit-box-ordinal-group:2; -webkit-order:1; -ms-flex-order:1; order:1 } 464 | .order-2{ -webkit-box-ordinal-group:3; -webkit-order:2; -ms-flex-order:2; order:2 } 465 | .order-3{ -webkit-box-ordinal-group:4; -webkit-order:3; -ms-flex-order:3; order:3 } 466 | .order-last{ -webkit-box-ordinal-group:100000; -webkit-order:99999; -ms-flex-order:99999; order:99999 } 467 | 468 | .relative{ position:relative } 469 | .absolute{ position:absolute } 470 | .fixed{ position:fixed } 471 | 472 | .top-0{ top:0 } 473 | .right-0{ right:0 } 474 | .bottom-0{ bottom:0 } 475 | .left-0{ left:0 } 476 | 477 | .z1{ z-index: 1 } 478 | .z2{ z-index: 2 } 479 | .z3{ z-index: 3 } 480 | .z4{ z-index: 4 } 481 | 482 | .border{ 483 | border-style:solid; 484 | border-width: 1px; 485 | } 486 | 487 | .border-top{ 488 | border-top-style:solid; 489 | border-top-width: 1px; 490 | } 491 | 492 | .border-right{ 493 | border-right-style:solid; 494 | border-right-width: 1px; 495 | } 496 | 497 | .border-bottom{ 498 | border-bottom-style:solid; 499 | border-bottom-width: 1px; 500 | } 501 | 502 | .border-left{ 503 | border-left-style:solid; 504 | border-left-width: 1px; 505 | } 506 | 507 | .border-none{ border:0 } 508 | 509 | .rounded{ border-radius: 3px } 510 | .circle{ border-radius:50% } 511 | 512 | .rounded-top{ border-radius: 3px 3px 0 0 } 513 | .rounded-right{ border-radius: 0 3px 3px 0 } 514 | .rounded-bottom{ border-radius: 0 0 3px 3px } 515 | .rounded-left{ border-radius: 3px 0 0 3px } 516 | 517 | .not-rounded{ border-radius:0 } 518 | 519 | .hide{ 520 | position:absolute !important; 521 | height:1px; 522 | width:1px; 523 | overflow:hidden; 524 | clip:rect(1px, 1px, 1px, 1px); 525 | } 526 | 527 | @media (max-width: 40em){ 528 | .xs-hide{ display:none !important } 529 | } 530 | 531 | @media (min-width: 40em) and (max-width: 52em){ 532 | .sm-hide{ display:none !important } 533 | } 534 | 535 | @media (min-width: 52em) and (max-width: 64em){ 536 | .md-hide{ display:none !important } 537 | } 538 | 539 | @media (min-width: 64em){ 540 | .lg-hide{ display:none !important } 541 | } 542 | 543 | .display-none{ display:none !important } 544 | 545 | -------------------------------------------------------------------------------- /client-js/packages/documentation-website/public/docs/assets/fonts/EOT/SourceCodePro-Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/ledger-app-icx/c2d15e89641b05c9a26e9e094b786c512faacff9/client-js/packages/documentation-website/public/docs/assets/fonts/EOT/SourceCodePro-Bold.eot -------------------------------------------------------------------------------- /client-js/packages/documentation-website/public/docs/assets/fonts/EOT/SourceCodePro-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/ledger-app-icx/c2d15e89641b05c9a26e9e094b786c512faacff9/client-js/packages/documentation-website/public/docs/assets/fonts/EOT/SourceCodePro-Regular.eot -------------------------------------------------------------------------------- /client-js/packages/documentation-website/public/docs/assets/fonts/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | 5 | This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /client-js/packages/documentation-website/public/docs/assets/fonts/OTF/SourceCodePro-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/ledger-app-icx/c2d15e89641b05c9a26e9e094b786c512faacff9/client-js/packages/documentation-website/public/docs/assets/fonts/OTF/SourceCodePro-Bold.otf -------------------------------------------------------------------------------- /client-js/packages/documentation-website/public/docs/assets/fonts/OTF/SourceCodePro-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/ledger-app-icx/c2d15e89641b05c9a26e9e094b786c512faacff9/client-js/packages/documentation-website/public/docs/assets/fonts/OTF/SourceCodePro-Regular.otf -------------------------------------------------------------------------------- /client-js/packages/documentation-website/public/docs/assets/fonts/TTF/SourceCodePro-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/ledger-app-icx/c2d15e89641b05c9a26e9e094b786c512faacff9/client-js/packages/documentation-website/public/docs/assets/fonts/TTF/SourceCodePro-Bold.ttf -------------------------------------------------------------------------------- /client-js/packages/documentation-website/public/docs/assets/fonts/TTF/SourceCodePro-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/ledger-app-icx/c2d15e89641b05c9a26e9e094b786c512faacff9/client-js/packages/documentation-website/public/docs/assets/fonts/TTF/SourceCodePro-Regular.ttf -------------------------------------------------------------------------------- /client-js/packages/documentation-website/public/docs/assets/fonts/WOFF/OTF/SourceCodePro-Bold.otf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/ledger-app-icx/c2d15e89641b05c9a26e9e094b786c512faacff9/client-js/packages/documentation-website/public/docs/assets/fonts/WOFF/OTF/SourceCodePro-Bold.otf.woff -------------------------------------------------------------------------------- /client-js/packages/documentation-website/public/docs/assets/fonts/WOFF/OTF/SourceCodePro-Regular.otf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/ledger-app-icx/c2d15e89641b05c9a26e9e094b786c512faacff9/client-js/packages/documentation-website/public/docs/assets/fonts/WOFF/OTF/SourceCodePro-Regular.otf.woff -------------------------------------------------------------------------------- /client-js/packages/documentation-website/public/docs/assets/fonts/WOFF/TTF/SourceCodePro-Bold.ttf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/ledger-app-icx/c2d15e89641b05c9a26e9e094b786c512faacff9/client-js/packages/documentation-website/public/docs/assets/fonts/WOFF/TTF/SourceCodePro-Bold.ttf.woff -------------------------------------------------------------------------------- /client-js/packages/documentation-website/public/docs/assets/fonts/WOFF/TTF/SourceCodePro-Regular.ttf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/ledger-app-icx/c2d15e89641b05c9a26e9e094b786c512faacff9/client-js/packages/documentation-website/public/docs/assets/fonts/WOFF/TTF/SourceCodePro-Regular.ttf.woff -------------------------------------------------------------------------------- /client-js/packages/documentation-website/public/docs/assets/fonts/WOFF2/OTF/SourceCodePro-Bold.otf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/ledger-app-icx/c2d15e89641b05c9a26e9e094b786c512faacff9/client-js/packages/documentation-website/public/docs/assets/fonts/WOFF2/OTF/SourceCodePro-Bold.otf.woff2 -------------------------------------------------------------------------------- /client-js/packages/documentation-website/public/docs/assets/fonts/WOFF2/OTF/SourceCodePro-Regular.otf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/ledger-app-icx/c2d15e89641b05c9a26e9e094b786c512faacff9/client-js/packages/documentation-website/public/docs/assets/fonts/WOFF2/OTF/SourceCodePro-Regular.otf.woff2 -------------------------------------------------------------------------------- /client-js/packages/documentation-website/public/docs/assets/fonts/WOFF2/TTF/SourceCodePro-Bold.ttf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/ledger-app-icx/c2d15e89641b05c9a26e9e094b786c512faacff9/client-js/packages/documentation-website/public/docs/assets/fonts/WOFF2/TTF/SourceCodePro-Bold.ttf.woff2 -------------------------------------------------------------------------------- /client-js/packages/documentation-website/public/docs/assets/fonts/WOFF2/TTF/SourceCodePro-Regular.ttf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/ledger-app-icx/c2d15e89641b05c9a26e9e094b786c512faacff9/client-js/packages/documentation-website/public/docs/assets/fonts/WOFF2/TTF/SourceCodePro-Regular.ttf.woff2 -------------------------------------------------------------------------------- /client-js/packages/documentation-website/public/docs/assets/fonts/source-code-pro.css: -------------------------------------------------------------------------------- 1 | @font-face{ 2 | font-family: 'Source Code Pro'; 3 | font-weight: 400; 4 | font-style: normal; 5 | font-stretch: normal; 6 | src: url('EOT/SourceCodePro-Regular.eot') format('embedded-opentype'), 7 | url('WOFF2/TTF/SourceCodePro-Regular.ttf.woff2') format('woff2'), 8 | url('WOFF/OTF/SourceCodePro-Regular.otf.woff') format('woff'), 9 | url('OTF/SourceCodePro-Regular.otf') format('opentype'), 10 | url('TTF/SourceCodePro-Regular.ttf') format('truetype'); 11 | } 12 | 13 | @font-face{ 14 | font-family: 'Source Code Pro'; 15 | font-weight: 700; 16 | font-style: normal; 17 | font-stretch: normal; 18 | src: url('EOT/SourceCodePro-Bold.eot') format('embedded-opentype'), 19 | url('WOFF2/TTF/SourceCodePro-Bold.ttf.woff2') format('woff2'), 20 | url('WOFF/OTF/SourceCodePro-Bold.otf.woff') format('woff'), 21 | url('OTF/SourceCodePro-Bold.otf') format('opentype'), 22 | url('TTF/SourceCodePro-Bold.ttf') format('truetype'); 23 | } 24 | -------------------------------------------------------------------------------- /client-js/packages/documentation-website/public/docs/assets/github.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | github.com style (c) Vasily Polovnyov 4 | 5 | */ 6 | 7 | .hljs { 8 | display: block; 9 | overflow-x: auto; 10 | padding: 0.5em; 11 | color: #333; 12 | background: #f8f8f8; 13 | -webkit-text-size-adjust: none; 14 | } 15 | 16 | .hljs-comment, 17 | .diff .hljs-header, 18 | .hljs-javadoc { 19 | color: #998; 20 | font-style: italic; 21 | } 22 | 23 | .hljs-keyword, 24 | .css .rule .hljs-keyword, 25 | .hljs-winutils, 26 | .nginx .hljs-title, 27 | .hljs-subst, 28 | .hljs-request, 29 | .hljs-status { 30 | color: #1184CE; 31 | } 32 | 33 | .hljs-number, 34 | .hljs-hexcolor, 35 | .ruby .hljs-constant { 36 | color: #ed225d; 37 | } 38 | 39 | .hljs-string, 40 | .hljs-tag .hljs-value, 41 | .hljs-phpdoc, 42 | .hljs-dartdoc, 43 | .tex .hljs-formula { 44 | color: #ed225d; 45 | } 46 | 47 | .hljs-title, 48 | .hljs-id, 49 | .scss .hljs-preprocessor { 50 | color: #900; 51 | font-weight: bold; 52 | } 53 | 54 | .hljs-list .hljs-keyword, 55 | .hljs-subst { 56 | font-weight: normal; 57 | } 58 | 59 | .hljs-class .hljs-title, 60 | .hljs-type, 61 | .vhdl .hljs-literal, 62 | .tex .hljs-command { 63 | color: #458; 64 | font-weight: bold; 65 | } 66 | 67 | .hljs-tag, 68 | .hljs-tag .hljs-title, 69 | .hljs-rules .hljs-property, 70 | .django .hljs-tag .hljs-keyword { 71 | color: #000080; 72 | font-weight: normal; 73 | } 74 | 75 | .hljs-attribute, 76 | .hljs-variable, 77 | .lisp .hljs-body { 78 | color: #008080; 79 | } 80 | 81 | .hljs-regexp { 82 | color: #009926; 83 | } 84 | 85 | .hljs-symbol, 86 | .ruby .hljs-symbol .hljs-string, 87 | .lisp .hljs-keyword, 88 | .clojure .hljs-keyword, 89 | .scheme .hljs-keyword, 90 | .tex .hljs-special, 91 | .hljs-prompt { 92 | color: #990073; 93 | } 94 | 95 | .hljs-built_in { 96 | color: #0086b3; 97 | } 98 | 99 | .hljs-preprocessor, 100 | .hljs-pragma, 101 | .hljs-pi, 102 | .hljs-doctype, 103 | .hljs-shebang, 104 | .hljs-cdata { 105 | color: #999; 106 | font-weight: bold; 107 | } 108 | 109 | .hljs-deletion { 110 | background: #fdd; 111 | } 112 | 113 | .hljs-addition { 114 | background: #dfd; 115 | } 116 | 117 | .diff .hljs-change { 118 | background: #0086b3; 119 | } 120 | 121 | .hljs-chunk { 122 | color: #aaa; 123 | } 124 | -------------------------------------------------------------------------------- /client-js/packages/documentation-website/public/docs/assets/site.js: -------------------------------------------------------------------------------- 1 | /* global anchors */ 2 | 3 | // add anchor links to headers 4 | anchors.options.placement = 'left'; 5 | anchors.add('h3'); 6 | 7 | // Filter UI 8 | var tocElements = document.getElementById('toc').getElementsByTagName('li'); 9 | 10 | document.getElementById('filter-input').addEventListener('keyup', function(e) { 11 | var i, element, children; 12 | 13 | // enter key 14 | if (e.keyCode === 13) { 15 | // go to the first displayed item in the toc 16 | for (i = 0; i < tocElements.length; i++) { 17 | element = tocElements[i]; 18 | if (!element.classList.contains('display-none')) { 19 | location.replace(element.firstChild.href); 20 | return e.preventDefault(); 21 | } 22 | } 23 | } 24 | 25 | var match = function() { 26 | return true; 27 | }; 28 | 29 | var value = this.value.toLowerCase(); 30 | 31 | if (!value.match(/^\s*$/)) { 32 | match = function(element) { 33 | var html = element.firstChild.innerHTML; 34 | return html && html.toLowerCase().indexOf(value) !== -1; 35 | }; 36 | } 37 | 38 | for (i = 0; i < tocElements.length; i++) { 39 | element = tocElements[i]; 40 | children = Array.from(element.getElementsByTagName('li')); 41 | if (match(element) || children.some(match)) { 42 | element.classList.remove('display-none'); 43 | } else { 44 | element.classList.add('display-none'); 45 | } 46 | } 47 | }); 48 | 49 | var items = document.getElementsByClassName('toggle-sibling'); 50 | for (var j = 0; j < items.length; j++) { 51 | items[j].addEventListener('click', toggleSibling); 52 | } 53 | 54 | function toggleSibling() { 55 | var stepSibling = this.parentNode.getElementsByClassName('toggle-target')[0]; 56 | var icon = this.getElementsByClassName('icon')[0]; 57 | var klass = 'display-none'; 58 | if (stepSibling.classList.contains(klass)) { 59 | stepSibling.classList.remove(klass); 60 | icon.innerHTML = '▾'; 61 | } else { 62 | stepSibling.classList.add(klass); 63 | icon.innerHTML = '▸'; 64 | } 65 | } 66 | 67 | function showHashTarget(targetId) { 68 | if (targetId) { 69 | var hashTarget = document.getElementById(targetId); 70 | // new target is hidden 71 | if ( 72 | hashTarget && 73 | hashTarget.offsetHeight === 0 && 74 | hashTarget.parentNode.parentNode.classList.contains('display-none') 75 | ) { 76 | hashTarget.parentNode.parentNode.classList.remove('display-none'); 77 | } 78 | } 79 | } 80 | 81 | function scrollIntoView(targetId) { 82 | // Only scroll to element if we don't have a stored scroll position. 83 | if (targetId && !history.state) { 84 | var hashTarget = document.getElementById(targetId); 85 | if (hashTarget) { 86 | hashTarget.scrollIntoView(); 87 | } 88 | } 89 | } 90 | 91 | function gotoCurrentTarget() { 92 | showHashTarget(location.hash.substring(1)); 93 | scrollIntoView(location.hash.substring(1)); 94 | } 95 | 96 | window.addEventListener('hashchange', gotoCurrentTarget); 97 | gotoCurrentTarget(); 98 | 99 | var toclinks = document.getElementsByClassName('pre-open'); 100 | for (var k = 0; k < toclinks.length; k++) { 101 | toclinks[k].addEventListener('mousedown', preOpen, false); 102 | } 103 | 104 | function preOpen() { 105 | showHashTarget(this.hash.substring(1)); 106 | } 107 | 108 | var split_left = document.querySelector('#split-left'); 109 | var split_right = document.querySelector('#split-right'); 110 | var split_parent = split_left.parentNode; 111 | var cw_with_sb = split_left.clientWidth; 112 | split_left.style.overflow = 'hidden'; 113 | var cw_without_sb = split_left.clientWidth; 114 | split_left.style.overflow = ''; 115 | 116 | Split(['#split-left', '#split-right'], { 117 | elementStyle: function(dimension, size, gutterSize) { 118 | return { 119 | 'flex-basis': 'calc(' + size + '% - ' + gutterSize + 'px)' 120 | }; 121 | }, 122 | gutterStyle: function(dimension, gutterSize) { 123 | return { 124 | 'flex-basis': gutterSize + 'px' 125 | }; 126 | }, 127 | gutterSize: 20, 128 | sizes: [33, 67] 129 | }); 130 | 131 | // Chrome doesn't remember scroll position properly so do it ourselves. 132 | // Also works on Firefox and Edge. 133 | 134 | function updateState() { 135 | history.replaceState( 136 | { 137 | left_top: split_left.scrollTop, 138 | right_top: split_right.scrollTop 139 | }, 140 | document.title 141 | ); 142 | } 143 | 144 | function loadState(ev) { 145 | if (ev) { 146 | // Edge doesn't replace change history.state on popstate. 147 | history.replaceState(ev.state, document.title); 148 | } 149 | if (history.state) { 150 | split_left.scrollTop = history.state.left_top; 151 | split_right.scrollTop = history.state.right_top; 152 | } 153 | } 154 | 155 | window.addEventListener('load', function() { 156 | // Restore after Firefox scrolls to hash. 157 | setTimeout(function() { 158 | loadState(); 159 | // Update with initial scroll position. 160 | updateState(); 161 | // Update scroll positions only after we've loaded because Firefox 162 | // emits an initial scroll event with 0. 163 | split_left.addEventListener('scroll', updateState); 164 | split_right.addEventListener('scroll', updateState); 165 | }, 1); 166 | }); 167 | 168 | window.addEventListener('popstate', loadState); 169 | -------------------------------------------------------------------------------- /client-js/packages/documentation-website/public/docs/assets/split.css: -------------------------------------------------------------------------------- 1 | .gutter { 2 | background-color: #f5f5f5; 3 | background-repeat: no-repeat; 4 | background-position: 50%; 5 | } 6 | 7 | .gutter.gutter-vertical { 8 | background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAFAQMAAABo7865AAAABlBMVEVHcEzMzMzyAv2sAAAAAXRSTlMAQObYZgAAABBJREFUeF5jOAMEEAIEEFwAn3kMwcB6I2AAAAAASUVORK5CYII='); 9 | cursor: ns-resize; 10 | } 11 | 12 | .gutter.gutter-horizontal { 13 | background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg=='); 14 | cursor: ew-resize; 15 | } 16 | -------------------------------------------------------------------------------- /client-js/packages/documentation-website/public/docs/assets/split.js: -------------------------------------------------------------------------------- 1 | /*! Split.js - v1.3.5 */ 2 | // https://github.com/nathancahill/Split.js 3 | // Copyright (c) 2017 Nathan Cahill; Licensed MIT 4 | 5 | (function(global, factory) { 6 | typeof exports === 'object' && typeof module !== 'undefined' 7 | ? (module.exports = factory()) 8 | : typeof define === 'function' && define.amd 9 | ? define(factory) 10 | : (global.Split = factory()); 11 | })(this, function() { 12 | 'use strict'; 13 | // The programming goals of Split.js are to deliver readable, understandable and 14 | // maintainable code, while at the same time manually optimizing for tiny minified file size, 15 | // browser compatibility without additional requirements, graceful fallback (IE8 is supported) 16 | // and very few assumptions about the user's page layout. 17 | var global = window; 18 | var document = global.document; 19 | 20 | // Save a couple long function names that are used frequently. 21 | // This optimization saves around 400 bytes. 22 | var addEventListener = 'addEventListener'; 23 | var removeEventListener = 'removeEventListener'; 24 | var getBoundingClientRect = 'getBoundingClientRect'; 25 | var NOOP = function() { 26 | return false; 27 | }; 28 | 29 | // Figure out if we're in IE8 or not. IE8 will still render correctly, 30 | // but will be static instead of draggable. 31 | var isIE8 = global.attachEvent && !global[addEventListener]; 32 | 33 | // This library only needs two helper functions: 34 | // 35 | // The first determines which prefixes of CSS calc we need. 36 | // We only need to do this once on startup, when this anonymous function is called. 37 | // 38 | // Tests -webkit, -moz and -o prefixes. Modified from StackOverflow: 39 | // http://stackoverflow.com/questions/16625140/js-feature-detection-to-detect-the-usage-of-webkit-calc-over-calc/16625167#16625167 40 | var calc = 41 | ['', '-webkit-', '-moz-', '-o-'] 42 | .filter(function(prefix) { 43 | var el = document.createElement('div'); 44 | el.style.cssText = 'width:' + prefix + 'calc(9px)'; 45 | 46 | return !!el.style.length; 47 | }) 48 | .shift() + 'calc'; 49 | 50 | // The second helper function allows elements and string selectors to be used 51 | // interchangeably. In either case an element is returned. This allows us to 52 | // do `Split([elem1, elem2])` as well as `Split(['#id1', '#id2'])`. 53 | var elementOrSelector = function(el) { 54 | if (typeof el === 'string' || el instanceof String) { 55 | return document.querySelector(el); 56 | } 57 | 58 | return el; 59 | }; 60 | 61 | // The main function to initialize a split. Split.js thinks about each pair 62 | // of elements as an independant pair. Dragging the gutter between two elements 63 | // only changes the dimensions of elements in that pair. This is key to understanding 64 | // how the following functions operate, since each function is bound to a pair. 65 | // 66 | // A pair object is shaped like this: 67 | // 68 | // { 69 | // a: DOM element, 70 | // b: DOM element, 71 | // aMin: Number, 72 | // bMin: Number, 73 | // dragging: Boolean, 74 | // parent: DOM element, 75 | // isFirst: Boolean, 76 | // isLast: Boolean, 77 | // direction: 'horizontal' | 'vertical' 78 | // } 79 | // 80 | // The basic sequence: 81 | // 82 | // 1. Set defaults to something sane. `options` doesn't have to be passed at all. 83 | // 2. Initialize a bunch of strings based on the direction we're splitting. 84 | // A lot of the behavior in the rest of the library is paramatized down to 85 | // rely on CSS strings and classes. 86 | // 3. Define the dragging helper functions, and a few helpers to go with them. 87 | // 4. Loop through the elements while pairing them off. Every pair gets an 88 | // `pair` object, a gutter, and special isFirst/isLast properties. 89 | // 5. Actually size the pair elements, insert gutters and attach event listeners. 90 | var Split = function(ids, options) { 91 | if (options === void 0) options = {}; 92 | 93 | var dimension; 94 | var clientDimension; 95 | var clientAxis; 96 | var position; 97 | var paddingA; 98 | var paddingB; 99 | var elements; 100 | 101 | // All DOM elements in the split should have a common parent. We can grab 102 | // the first elements parent and hope users read the docs because the 103 | // behavior will be whacky otherwise. 104 | var parent = elementOrSelector(ids[0]).parentNode; 105 | var parentFlexDirection = global.getComputedStyle(parent).flexDirection; 106 | 107 | // Set default options.sizes to equal percentages of the parent element. 108 | var sizes = 109 | options.sizes || 110 | ids.map(function() { 111 | return 100 / ids.length; 112 | }); 113 | 114 | // Standardize minSize to an array if it isn't already. This allows minSize 115 | // to be passed as a number. 116 | var minSize = options.minSize !== undefined ? options.minSize : 100; 117 | var minSizes = Array.isArray(minSize) 118 | ? minSize 119 | : ids.map(function() { 120 | return minSize; 121 | }); 122 | var gutterSize = options.gutterSize !== undefined ? options.gutterSize : 10; 123 | var snapOffset = options.snapOffset !== undefined ? options.snapOffset : 30; 124 | var direction = options.direction || 'horizontal'; 125 | var cursor = 126 | options.cursor || 127 | (direction === 'horizontal' ? 'ew-resize' : 'ns-resize'); 128 | var gutter = 129 | options.gutter || 130 | function(i, gutterDirection) { 131 | var gut = document.createElement('div'); 132 | gut.className = 'gutter gutter-' + gutterDirection; 133 | return gut; 134 | }; 135 | var elementStyle = 136 | options.elementStyle || 137 | function(dim, size, gutSize) { 138 | var style = {}; 139 | 140 | if (typeof size !== 'string' && !(size instanceof String)) { 141 | if (!isIE8) { 142 | style[dim] = calc + '(' + size + '% - ' + gutSize + 'px)'; 143 | } else { 144 | style[dim] = size + '%'; 145 | } 146 | } else { 147 | style[dim] = size; 148 | } 149 | 150 | return style; 151 | }; 152 | var gutterStyle = 153 | options.gutterStyle || 154 | function(dim, gutSize) { 155 | return (obj = {}), (obj[dim] = gutSize + 'px'), obj; 156 | var obj; 157 | }; 158 | 159 | // 2. Initialize a bunch of strings based on the direction we're splitting. 160 | // A lot of the behavior in the rest of the library is paramatized down to 161 | // rely on CSS strings and classes. 162 | if (direction === 'horizontal') { 163 | dimension = 'width'; 164 | clientDimension = 'clientWidth'; 165 | clientAxis = 'clientX'; 166 | position = 'left'; 167 | paddingA = 'paddingLeft'; 168 | paddingB = 'paddingRight'; 169 | } else if (direction === 'vertical') { 170 | dimension = 'height'; 171 | clientDimension = 'clientHeight'; 172 | clientAxis = 'clientY'; 173 | position = 'top'; 174 | paddingA = 'paddingTop'; 175 | paddingB = 'paddingBottom'; 176 | } 177 | 178 | // 3. Define the dragging helper functions, and a few helpers to go with them. 179 | // Each helper is bound to a pair object that contains it's metadata. This 180 | // also makes it easy to store references to listeners that that will be 181 | // added and removed. 182 | // 183 | // Even though there are no other functions contained in them, aliasing 184 | // this to self saves 50 bytes or so since it's used so frequently. 185 | // 186 | // The pair object saves metadata like dragging state, position and 187 | // event listener references. 188 | 189 | function setElementSize(el, size, gutSize) { 190 | // Split.js allows setting sizes via numbers (ideally), or if you must, 191 | // by string, like '300px'. This is less than ideal, because it breaks 192 | // the fluid layout that `calc(% - px)` provides. You're on your own if you do that, 193 | // make sure you calculate the gutter size by hand. 194 | var style = elementStyle(dimension, size, gutSize); 195 | 196 | // eslint-disable-next-line no-param-reassign 197 | Object.keys(style).forEach(function(prop) { 198 | return (el.style[prop] = style[prop]); 199 | }); 200 | } 201 | 202 | function setGutterSize(gutterElement, gutSize) { 203 | var style = gutterStyle(dimension, gutSize); 204 | 205 | // eslint-disable-next-line no-param-reassign 206 | Object.keys(style).forEach(function(prop) { 207 | return (gutterElement.style[prop] = style[prop]); 208 | }); 209 | } 210 | 211 | // Actually adjust the size of elements `a` and `b` to `offset` while dragging. 212 | // calc is used to allow calc(percentage + gutterpx) on the whole split instance, 213 | // which allows the viewport to be resized without additional logic. 214 | // Element a's size is the same as offset. b's size is total size - a size. 215 | // Both sizes are calculated from the initial parent percentage, 216 | // then the gutter size is subtracted. 217 | function adjust(offset) { 218 | var a = elements[this.a]; 219 | var b = elements[this.b]; 220 | var percentage = a.size + b.size; 221 | 222 | a.size = offset / this.size * percentage; 223 | b.size = percentage - offset / this.size * percentage; 224 | 225 | setElementSize(a.element, a.size, this.aGutterSize); 226 | setElementSize(b.element, b.size, this.bGutterSize); 227 | } 228 | 229 | // drag, where all the magic happens. The logic is really quite simple: 230 | // 231 | // 1. Ignore if the pair is not dragging. 232 | // 2. Get the offset of the event. 233 | // 3. Snap offset to min if within snappable range (within min + snapOffset). 234 | // 4. Actually adjust each element in the pair to offset. 235 | // 236 | // --------------------------------------------------------------------- 237 | // | | <- a.minSize || b.minSize -> | | 238 | // | | | <- this.snapOffset || this.snapOffset -> | | | 239 | // | | | || | | | 240 | // | | | || | | | 241 | // --------------------------------------------------------------------- 242 | // | <- this.start this.size -> | 243 | function drag(e) { 244 | var offset; 245 | 246 | if (!this.dragging) { 247 | return; 248 | } 249 | 250 | // Get the offset of the event from the first side of the 251 | // pair `this.start`. Supports touch events, but not multitouch, so only the first 252 | // finger `touches[0]` is counted. 253 | if ('touches' in e) { 254 | offset = e.touches[0][clientAxis] - this.start; 255 | } else { 256 | offset = e[clientAxis] - this.start; 257 | } 258 | 259 | // If within snapOffset of min or max, set offset to min or max. 260 | // snapOffset buffers a.minSize and b.minSize, so logic is opposite for both. 261 | // Include the appropriate gutter sizes to prevent overflows. 262 | if (offset <= elements[this.a].minSize + snapOffset + this.aGutterSize) { 263 | offset = elements[this.a].minSize + this.aGutterSize; 264 | } else if ( 265 | offset >= 266 | this.size - (elements[this.b].minSize + snapOffset + this.bGutterSize) 267 | ) { 268 | offset = this.size - (elements[this.b].minSize + this.bGutterSize); 269 | } 270 | 271 | // Actually adjust the size. 272 | adjust.call(this, offset); 273 | 274 | // Call the drag callback continously. Don't do anything too intensive 275 | // in this callback. 276 | if (options.onDrag) { 277 | options.onDrag(); 278 | } 279 | } 280 | 281 | // Cache some important sizes when drag starts, so we don't have to do that 282 | // continously: 283 | // 284 | // `size`: The total size of the pair. First + second + first gutter + second gutter. 285 | // `start`: The leading side of the first element. 286 | // 287 | // ------------------------------------------------ 288 | // | aGutterSize -> ||| | 289 | // | ||| | 290 | // | ||| | 291 | // | ||| <- bGutterSize | 292 | // ------------------------------------------------ 293 | // | <- start size -> | 294 | function calculateSizes() { 295 | // Figure out the parent size minus padding. 296 | var a = elements[this.a].element; 297 | var b = elements[this.b].element; 298 | 299 | this.size = 300 | a[getBoundingClientRect]()[dimension] + 301 | b[getBoundingClientRect]()[dimension] + 302 | this.aGutterSize + 303 | this.bGutterSize; 304 | this.start = a[getBoundingClientRect]()[position]; 305 | } 306 | 307 | // stopDragging is very similar to startDragging in reverse. 308 | function stopDragging() { 309 | var self = this; 310 | var a = elements[self.a].element; 311 | var b = elements[self.b].element; 312 | 313 | if (self.dragging && options.onDragEnd) { 314 | options.onDragEnd(); 315 | } 316 | 317 | self.dragging = false; 318 | 319 | // Remove the stored event listeners. This is why we store them. 320 | global[removeEventListener]('mouseup', self.stop); 321 | global[removeEventListener]('touchend', self.stop); 322 | global[removeEventListener]('touchcancel', self.stop); 323 | 324 | self.parent[removeEventListener]('mousemove', self.move); 325 | self.parent[removeEventListener]('touchmove', self.move); 326 | 327 | // Delete them once they are removed. I think this makes a difference 328 | // in memory usage with a lot of splits on one page. But I don't know for sure. 329 | delete self.stop; 330 | delete self.move; 331 | 332 | a[removeEventListener]('selectstart', NOOP); 333 | a[removeEventListener]('dragstart', NOOP); 334 | b[removeEventListener]('selectstart', NOOP); 335 | b[removeEventListener]('dragstart', NOOP); 336 | 337 | a.style.userSelect = ''; 338 | a.style.webkitUserSelect = ''; 339 | a.style.MozUserSelect = ''; 340 | a.style.pointerEvents = ''; 341 | 342 | b.style.userSelect = ''; 343 | b.style.webkitUserSelect = ''; 344 | b.style.MozUserSelect = ''; 345 | b.style.pointerEvents = ''; 346 | 347 | self.gutter.style.cursor = ''; 348 | self.parent.style.cursor = ''; 349 | } 350 | 351 | // startDragging calls `calculateSizes` to store the inital size in the pair object. 352 | // It also adds event listeners for mouse/touch events, 353 | // and prevents selection while dragging so avoid the selecting text. 354 | function startDragging(e) { 355 | // Alias frequently used variables to save space. 200 bytes. 356 | var self = this; 357 | var a = elements[self.a].element; 358 | var b = elements[self.b].element; 359 | 360 | // Call the onDragStart callback. 361 | if (!self.dragging && options.onDragStart) { 362 | options.onDragStart(); 363 | } 364 | 365 | // Don't actually drag the element. We emulate that in the drag function. 366 | e.preventDefault(); 367 | 368 | // Set the dragging property of the pair object. 369 | self.dragging = true; 370 | 371 | // Create two event listeners bound to the same pair object and store 372 | // them in the pair object. 373 | self.move = drag.bind(self); 374 | self.stop = stopDragging.bind(self); 375 | 376 | // All the binding. `window` gets the stop events in case we drag out of the elements. 377 | global[addEventListener]('mouseup', self.stop); 378 | global[addEventListener]('touchend', self.stop); 379 | global[addEventListener]('touchcancel', self.stop); 380 | 381 | self.parent[addEventListener]('mousemove', self.move); 382 | self.parent[addEventListener]('touchmove', self.move); 383 | 384 | // Disable selection. Disable! 385 | a[addEventListener]('selectstart', NOOP); 386 | a[addEventListener]('dragstart', NOOP); 387 | b[addEventListener]('selectstart', NOOP); 388 | b[addEventListener]('dragstart', NOOP); 389 | 390 | a.style.userSelect = 'none'; 391 | a.style.webkitUserSelect = 'none'; 392 | a.style.MozUserSelect = 'none'; 393 | a.style.pointerEvents = 'none'; 394 | 395 | b.style.userSelect = 'none'; 396 | b.style.webkitUserSelect = 'none'; 397 | b.style.MozUserSelect = 'none'; 398 | b.style.pointerEvents = 'none'; 399 | 400 | // Set the cursor, both on the gutter and the parent element. 401 | // Doing only a, b and gutter causes flickering. 402 | self.gutter.style.cursor = cursor; 403 | self.parent.style.cursor = cursor; 404 | 405 | // Cache the initial sizes of the pair. 406 | calculateSizes.call(self); 407 | } 408 | 409 | // 5. Create pair and element objects. Each pair has an index reference to 410 | // elements `a` and `b` of the pair (first and second elements). 411 | // Loop through the elements while pairing them off. Every pair gets a 412 | // `pair` object, a gutter, and isFirst/isLast properties. 413 | // 414 | // Basic logic: 415 | // 416 | // - Starting with the second element `i > 0`, create `pair` objects with 417 | // `a = i - 1` and `b = i` 418 | // - Set gutter sizes based on the _pair_ being first/last. The first and last 419 | // pair have gutterSize / 2, since they only have one half gutter, and not two. 420 | // - Create gutter elements and add event listeners. 421 | // - Set the size of the elements, minus the gutter sizes. 422 | // 423 | // ----------------------------------------------------------------------- 424 | // | i=0 | i=1 | i=2 | i=3 | 425 | // | | isFirst | | isLast | 426 | // | pair 0 pair 1 pair 2 | 427 | // | | | | | 428 | // ----------------------------------------------------------------------- 429 | var pairs = []; 430 | elements = ids.map(function(id, i) { 431 | // Create the element object. 432 | var element = { 433 | element: elementOrSelector(id), 434 | size: sizes[i], 435 | minSize: minSizes[i] 436 | }; 437 | 438 | var pair; 439 | 440 | if (i > 0) { 441 | // Create the pair object with it's metadata. 442 | pair = { 443 | a: i - 1, 444 | b: i, 445 | dragging: false, 446 | isFirst: i === 1, 447 | isLast: i === ids.length - 1, 448 | direction: direction, 449 | parent: parent 450 | }; 451 | 452 | // For first and last pairs, first and last gutter width is half. 453 | pair.aGutterSize = gutterSize; 454 | pair.bGutterSize = gutterSize; 455 | 456 | if (pair.isFirst) { 457 | pair.aGutterSize = gutterSize / 2; 458 | } 459 | 460 | if (pair.isLast) { 461 | pair.bGutterSize = gutterSize / 2; 462 | } 463 | 464 | // if the parent has a reverse flex-direction, switch the pair elements. 465 | if ( 466 | parentFlexDirection === 'row-reverse' || 467 | parentFlexDirection === 'column-reverse' 468 | ) { 469 | var temp = pair.a; 470 | pair.a = pair.b; 471 | pair.b = temp; 472 | } 473 | } 474 | 475 | // Determine the size of the current element. IE8 is supported by 476 | // staticly assigning sizes without draggable gutters. Assigns a string 477 | // to `size`. 478 | // 479 | // IE9 and above 480 | if (!isIE8) { 481 | // Create gutter elements for each pair. 482 | if (i > 0) { 483 | var gutterElement = gutter(i, direction); 484 | setGutterSize(gutterElement, gutterSize); 485 | 486 | gutterElement[addEventListener]( 487 | 'mousedown', 488 | startDragging.bind(pair) 489 | ); 490 | gutterElement[addEventListener]( 491 | 'touchstart', 492 | startDragging.bind(pair) 493 | ); 494 | 495 | parent.insertBefore(gutterElement, element.element); 496 | 497 | pair.gutter = gutterElement; 498 | } 499 | } 500 | 501 | // Set the element size to our determined size. 502 | // Half-size gutters for first and last elements. 503 | if (i === 0 || i === ids.length - 1) { 504 | setElementSize(element.element, element.size, gutterSize / 2); 505 | } else { 506 | setElementSize(element.element, element.size, gutterSize); 507 | } 508 | 509 | var computedSize = element.element[getBoundingClientRect]()[dimension]; 510 | 511 | if (computedSize < element.minSize) { 512 | element.minSize = computedSize; 513 | } 514 | 515 | // After the first iteration, and we have a pair object, append it to the 516 | // list of pairs. 517 | if (i > 0) { 518 | pairs.push(pair); 519 | } 520 | 521 | return element; 522 | }); 523 | 524 | function setSizes(newSizes) { 525 | newSizes.forEach(function(newSize, i) { 526 | if (i > 0) { 527 | var pair = pairs[i - 1]; 528 | var a = elements[pair.a]; 529 | var b = elements[pair.b]; 530 | 531 | a.size = newSizes[i - 1]; 532 | b.size = newSize; 533 | 534 | setElementSize(a.element, a.size, pair.aGutterSize); 535 | setElementSize(b.element, b.size, pair.bGutterSize); 536 | } 537 | }); 538 | } 539 | 540 | function destroy() { 541 | pairs.forEach(function(pair) { 542 | pair.parent.removeChild(pair.gutter); 543 | elements[pair.a].element.style[dimension] = ''; 544 | elements[pair.b].element.style[dimension] = ''; 545 | }); 546 | } 547 | 548 | if (isIE8) { 549 | return { 550 | setSizes: setSizes, 551 | destroy: destroy 552 | }; 553 | } 554 | 555 | return { 556 | setSizes: setSizes, 557 | getSizes: function getSizes() { 558 | return elements.map(function(element) { 559 | return element.size; 560 | }); 561 | }, 562 | collapse: function collapse(i) { 563 | if (i === pairs.length) { 564 | var pair = pairs[i - 1]; 565 | 566 | calculateSizes.call(pair); 567 | 568 | if (!isIE8) { 569 | adjust.call(pair, pair.size - pair.bGutterSize); 570 | } 571 | } else { 572 | var pair$1 = pairs[i]; 573 | 574 | calculateSizes.call(pair$1); 575 | 576 | if (!isIE8) { 577 | adjust.call(pair$1, pair$1.aGutterSize); 578 | } 579 | } 580 | }, 581 | destroy: destroy 582 | }; 583 | }; 584 | 585 | return Split; 586 | }); 587 | -------------------------------------------------------------------------------- /client-js/packages/documentation-website/public/docs/assets/style.css: -------------------------------------------------------------------------------- 1 | .documentation { 2 | font-family: Helvetica, sans-serif; 3 | color: #666; 4 | line-height: 1.5; 5 | background: #f5f5f5; 6 | } 7 | 8 | .black { 9 | color: #666; 10 | } 11 | 12 | .bg-white { 13 | background-color: #fff; 14 | } 15 | 16 | h4 { 17 | margin: 20px 0 10px 0; 18 | } 19 | 20 | .documentation h3 { 21 | color: #000; 22 | } 23 | 24 | .border-bottom { 25 | border-color: #ddd; 26 | } 27 | 28 | a { 29 | color: #1184CE; 30 | text-decoration: none; 31 | } 32 | 33 | .documentation a[href]:hover { 34 | text-decoration: underline; 35 | } 36 | 37 | a:hover { 38 | cursor: pointer; 39 | } 40 | 41 | .py1-ul li { 42 | padding: 5px 0; 43 | } 44 | 45 | .max-height-100 { 46 | max-height: 100%; 47 | } 48 | 49 | .height-viewport-100 { 50 | height: 100vh; 51 | } 52 | 53 | section:target h3 { 54 | font-weight:700; 55 | } 56 | 57 | .documentation td, 58 | .documentation th { 59 | padding: .25rem .25rem; 60 | } 61 | 62 | h1:hover .anchorjs-link, 63 | h2:hover .anchorjs-link, 64 | h3:hover .anchorjs-link, 65 | h4:hover .anchorjs-link { 66 | opacity: 1; 67 | } 68 | 69 | .fix-3 { 70 | width: 25%; 71 | max-width: 244px; 72 | } 73 | 74 | .fix-3 { 75 | width: 25%; 76 | max-width: 244px; 77 | } 78 | 79 | @media (min-width: 52em) { 80 | .fix-margin-3 { 81 | margin-left: 25%; 82 | } 83 | } 84 | 85 | .pre, pre, code, .code { 86 | font-family: Source Code Pro,Menlo,Consolas,Liberation Mono,monospace; 87 | font-size: 14px; 88 | } 89 | 90 | .fill-light { 91 | background: #F9F9F9; 92 | } 93 | 94 | .width2 { 95 | width: 1rem; 96 | } 97 | 98 | .input { 99 | font-family: inherit; 100 | display: block; 101 | width: 100%; 102 | height: 2rem; 103 | padding: .5rem; 104 | margin-bottom: 1rem; 105 | border: 1px solid #ccc; 106 | font-size: .875rem; 107 | border-radius: 3px; 108 | box-sizing: border-box; 109 | } 110 | 111 | table { 112 | border-collapse: collapse; 113 | } 114 | 115 | .prose table th, 116 | .prose table td { 117 | text-align: left; 118 | padding:8px; 119 | border:1px solid #ddd; 120 | } 121 | 122 | .prose table th:nth-child(1) { border-right: none; } 123 | .prose table th:nth-child(2) { border-left: none; } 124 | 125 | .prose table { 126 | border:1px solid #ddd; 127 | } 128 | 129 | .prose-big { 130 | font-size: 18px; 131 | line-height: 30px; 132 | } 133 | 134 | .quiet { 135 | opacity: 0.7; 136 | } 137 | 138 | .minishadow { 139 | box-shadow: 2px 2px 10px #f3f3f3; 140 | } 141 | -------------------------------------------------------------------------------- /client-js/packages/documentation-website/public/docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | @ledgerhq/* 4.14.0 | Documentation 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 |

@ledgerhq/*

17 |
4.14.0
18 | 23 |
24 | 73 |
74 | 77 |
78 |
79 |
80 | 81 | 82 |
83 | 84 | 85 |
86 | 87 |

88 | Icx 89 |

90 | 91 | 92 | 93 | client-js/packages/hw-app-icx/src/Icx.js 94 | 95 | 96 |
97 | 98 | 99 |

ICON API

100 | 101 | 102 |
new Icx(transport: Transport<any>)
103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 |
Parameters
115 |
116 | 117 |
118 |
119 | transport (Transport<any>) 120 | 121 |
122 | 123 |
124 | 125 |
126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 |
Example
136 | 137 | 138 |
import Icx from "@ledgerhq/hw-app-icx";
139 | const icx = new Icx(transport)
140 | 141 | 142 | 143 | 144 | 145 | 146 |
Instance Members
147 |
148 | 149 |
150 |
151 |
152 | 153 | getAddress(path, boolDisplay, boolChaincode) 154 |
155 |
156 | 249 |
250 | 251 |
252 |
253 |
254 | 255 | signTransaction(path, rawTxAscii) 256 |
257 |
258 | 346 |
347 | 348 |
349 |
350 |
351 | 352 | getAppConfiguration() 353 |
354 |
355 | 409 |
410 | 411 |
412 |
413 |
414 | 415 | setTestPrivateKey(privateKeyHex) 416 |
417 |
418 | 489 |
490 | 491 |
492 | 493 | 494 | 495 | 496 |
497 | 498 | 499 | 500 |
501 |
502 | 503 | 504 | 505 | 506 | 507 | -------------------------------------------------------------------------------- /client-js/packages/hw-app-icx/.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | /lib 3 | 4 | [include] 5 | 6 | [libs] 7 | flow-typed 8 | 9 | [lints] 10 | 11 | [options] 12 | 13 | [strict] 14 | -------------------------------------------------------------------------------- /client-js/packages/hw-app-icx/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ledgerhq/hw-app-icx", 3 | "version": "4.14.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "js-sha3": { 8 | "version": "0.7.0", 9 | "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.7.0.tgz", 10 | "integrity": "sha512-Wpks3yBDm0UcL5qlVhwW9Jr9n9i4FfeWBFOOXP5puDS/SiudJGhw7DPyBqn3487qD4F0lsC0q3zxink37f7zeA==" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client-js/packages/hw-app-icx/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ledgerhq/hw-app-icx", 3 | "version": "0.1.0", 4 | "description": "Ledger Hardware Wallet ICON Application API", 5 | "keywords": [ 6 | "Ledger", 7 | "LedgerWallet", 8 | "ICON", 9 | "eth", 10 | "NanoS", 11 | "Blue", 12 | "Hardware Wallet" 13 | ], 14 | "main": "lib/Icx.js", 15 | "license": "Apache-2.0", 16 | "dependencies": { 17 | "@ledgerhq/hw-transport": "^4.13.0" 18 | }, 19 | "devDependencies": { 20 | "flow-bin": "^0.68.0", 21 | "flow-typed": "^2.4.0" 22 | }, 23 | "scripts": { 24 | "flow": "flow", 25 | "clean": "rm -rf lib/", 26 | "build": "cd ../.. && export PATH=$(yarn bin):$PATH && cd - && babel --source-maps -d lib src && flow-copy-source -v src lib", 27 | "watch": "cd ../.. && export PATH=$(yarn bin):$PATH && cd - && babel --watch --source-maps -d lib src & flow-copy-source -w -v src lib" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /client-js/packages/hw-app-icx/src/Icx.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * Ledger JS API for ICON 3 | * (c) 2018 ICON Foundation 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | ********************************************************************************/ 17 | //@flow 18 | 19 | // FIXME drop: 20 | import { splitPath, foreach, hexToBase64 } from "./utils"; 21 | import type Transport from "@ledgerhq/hw-transport"; 22 | 23 | /** 24 | * ICON API 25 | * 26 | * @example 27 | * import Icx from "@ledgerhq/hw-app-icx"; 28 | * const icx = new Icx(transport) 29 | */ 30 | export default class Icx { 31 | transport: Transport<*>; 32 | 33 | constructor(transport: Transport<*>) { 34 | this.transport = transport; 35 | transport.decorateAppAPIMethods( 36 | this, 37 | [ 38 | "getAddress", 39 | "signTransaction", 40 | "getAppConfiguration", 41 | "setTestPrivateKey" 42 | ], 43 | "ICON" 44 | ); 45 | } 46 | 47 | /** 48 | * Returns public key and ICON address for a given BIP 32 path. 49 | * @param path a path in BIP 32 format 50 | * @option boolDisplay optionally enable or not the display 51 | * @option boolChaincode optionally enable or not the chaincode request 52 | * @return an object with a publickey(hexa string), address(string) and 53 | * (optionally) chaincode(hexa string) 54 | * @example 55 | * icx.getAddress("44'/4801368'/0'", true, true).then(o => o.address) 56 | */ 57 | getAddress( 58 | path: string, 59 | boolDisplay?: boolean = false, 60 | boolChaincode?: boolean = true 61 | ): Promise<{ 62 | publicKey: string, 63 | address: string, 64 | chainCode?: string 65 | }> { 66 | let paths = splitPath(path); 67 | let buffer = new Buffer(1 + paths.length * 4); 68 | buffer[0] = paths.length; 69 | paths.forEach((element, index) => { 70 | buffer.writeUInt32BE(element, 1 + 4 * index); 71 | }); 72 | return this.transport 73 | .send( 74 | 0xe0, 75 | 0x02, 76 | boolDisplay ? 0x01 : 0x00, 77 | boolChaincode ? 0x01 : 0x00, 78 | buffer 79 | ) 80 | .then(response => { 81 | let result = {}; 82 | let publicKeyLength = response[0]; 83 | result.publicKey = response.slice(1, 1 + publicKeyLength).toString("hex"); 84 | let addressLength = response[1 + publicKeyLength]; 85 | result.address = response.slice(1 + publicKeyLength + 1, 1 + publicKeyLength + 1 + addressLength); 86 | if (boolChaincode) { 87 | result.chainCode = response.slice(-32).toString("hex"); 88 | } 89 | return result; 90 | }); 91 | } 92 | 93 | /** 94 | * Signs a transaction and returns signed message given the raw transaction 95 | * and the BIP 32 path of the account to sign 96 | * @param path a path in BIP 32 format 97 | * @param rawTxAscii raw transaction data to sign in ASCII string format 98 | * @return an object with a base64 encoded signature and hash in hexa string 99 | * @example 100 | * icx.signTransaction("44'/4801368'/0'", 101 | * "icx_sendTransaction.fee.0x2386f26fc10000." + 102 | * "from.hxc9ecad30b05a0650a337452fce031e0c60eacc3a.nonce.0x3." + 103 | * "to.hx4c5101add2caa6a920420cf951f7dd7c7df6ca24.value.0xde0b6b3a7640000") 104 | * .then(result => ...) 105 | */ 106 | signTransaction( 107 | path: string, 108 | rawTxAscii: string 109 | ): Promise<{ 110 | signedRawTxBase64: string, 111 | hashHex: string 112 | }> { 113 | let paths = splitPath(path); 114 | let offset = 0; 115 | let rawTx = new Buffer(rawTxAscii); 116 | let toSend = []; 117 | let response; 118 | while (offset !== rawTx.length) { 119 | let maxChunkSize = offset === 0 ? 150 - 1 - paths.length * 4 - 4: 150; 120 | let chunkSize = 121 | offset + maxChunkSize > rawTx.length 122 | ? rawTx.length - offset 123 | : maxChunkSize; 124 | let buffer = new Buffer( 125 | offset === 0 ? 1 + paths.length * 4 + 4 + chunkSize : chunkSize 126 | ); 127 | if (offset === 0) { 128 | buffer[0] = paths.length; 129 | paths.forEach((element, index) => { 130 | buffer.writeUInt32BE(element, 1 + 4 * index); 131 | }); 132 | buffer.writeUInt32BE(rawTx.length, 1 + 4 * paths.length); 133 | rawTx.copy(buffer, 1 + 4 * paths.length + 4, offset, offset + chunkSize); 134 | } else { 135 | rawTx.copy(buffer, 0, offset, offset + chunkSize); 136 | } 137 | toSend.push(buffer); 138 | offset += chunkSize; 139 | } 140 | return foreach(toSend, (data, i) => 141 | this.transport 142 | .send(0xe0, 0x04, i === 0 ? 0x00 : 0x80, 0x00, data) 143 | .then(apduResponse => { 144 | response = apduResponse; 145 | }) 146 | ).then(() => { 147 | let result = {}; 148 | // r, s, v are aligned sequencially 149 | result.signedRawTxBase64 = 150 | hexToBase64(response.slice(0, 32 + 32 + 1).toString("hex")); 151 | result.hashHex = 152 | response.slice(32 + 32 + 1, 32 + 32 + 1 + 32).toString("hex"); 153 | return result; 154 | }); 155 | } 156 | 157 | /** 158 | * Returns the application configurations such as versions. 159 | * @return major/minor/patch versions of Icon application 160 | */ 161 | getAppConfiguration(): Promise<{ 162 | majorVersion: number, 163 | minorVersion: number, 164 | patchVersion: number 165 | }> { 166 | return this.transport 167 | .send(0xe0, 0x06, 0x00, 0x00) 168 | .then(response => { 169 | let result = {}; 170 | result.majorVersion = response[0]; 171 | result.minorVersion = response[1]; 172 | result.patchVersion = response[2]; 173 | return result; 174 | }); 175 | } 176 | 177 | /** 178 | * Sets the given key as the test purpose private key corresponding to 179 | * "\0'" of BIP 32 path just for test purpose. After calling this function, 180 | * all functions with "\0'" path works based on this private key. 181 | * REMARK: Test purpose only such as verifying signTransaction function. 182 | * @param privateKeyHex private key in hexadecimal string format 183 | * @example 184 | * icx.setTestPrivateKey("23498dc21b9ee52e63e8d6566e0911ac255a38d3fcbc68a51e6b298520b72d6e") 185 | * .then(result => ...) 186 | * icx.getAddress("0'", false, false).then(o => o.address) 187 | */ 188 | setTestPrivateKey(privateKeyHex: string) { 189 | let data = new Buffer(32); 190 | for (let i = 0; i < privateKeyHex.length; i += 2) { 191 | data[i / 2] = parseInt(privateKeyHex.substr(i, 2), 16); 192 | } 193 | return this.transport.send(0xe0, 0xff, 0x00, 0x00, data).then(); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /client-js/packages/hw-app-icx/src/utils.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * Ledger Node JS API for ICON 3 | * (c) 2016-2017 Ledger 4 | * 5 | * Modifications (c) 2018 ICON Foundation 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | ********************************************************************************/ 19 | //@flow 20 | 21 | type Defer = { 22 | promise: Promise, 23 | resolve: T => void, 24 | reject: any => void 25 | }; 26 | 27 | export function defer(): Defer { 28 | let resolve, reject; 29 | let promise = new Promise(function(success, failure) { 30 | resolve = success; 31 | reject = failure; 32 | }); 33 | if (!resolve || !reject) throw "defer() error"; // this never happens and is just to make flow happy 34 | return { promise, resolve, reject }; 35 | } 36 | 37 | // TODO use bip32-path library 38 | export function splitPath(path: string): number[] { 39 | let result = []; 40 | let components = path.split("/"); 41 | components.forEach(element => { 42 | let number = parseInt(element, 10); 43 | if (isNaN(number)) { 44 | return; // FIXME shouldn't it throws instead? 45 | } 46 | if (element.length > 1 && element[element.length - 1] === "'") { 47 | number += 0x80000000; 48 | } 49 | result.push(number); 50 | }); 51 | return result; 52 | } 53 | 54 | // TODO use async await 55 | 56 | export function eachSeries(arr: A[], fun: A => Promise<*>): Promise<*> { 57 | return arr.reduce((p, e) => p.then(() => fun(e)), Promise.resolve()); 58 | } 59 | 60 | export function foreach( 61 | arr: T[], 62 | callback: (T, number) => Promise 63 | ): Promise { 64 | function iterate(index, array, result) { 65 | if (index >= array.length) { 66 | return result; 67 | } else 68 | return callback(array[index], index).then(function(res) { 69 | result.push(res); 70 | return iterate(index + 1, array, result); 71 | }); 72 | } 73 | return Promise.resolve().then(() => iterate(0, arr, [])); 74 | } 75 | 76 | export function doIf( 77 | condition: boolean, 78 | callback: () => any | Promise 79 | ): Promise { 80 | return Promise.resolve().then(() => { 81 | if (condition) { 82 | return callback(); 83 | } 84 | }); 85 | } 86 | 87 | export function asyncWhile( 88 | predicate: () => boolean, 89 | callback: () => Promise 90 | ): Promise> { 91 | function iterate(result) { 92 | if (!predicate()) { 93 | return result; 94 | } else { 95 | return callback().then(res => { 96 | result.push(res); 97 | return iterate(result); 98 | }); 99 | } 100 | } 101 | return Promise.resolve([]).then(iterate); 102 | } 103 | 104 | export function hexToBase64(hexString: string) { 105 | return btoa(hexString.match(/\w{2}/g).map(function(a) { 106 | return String.fromCharCode(parseInt(a, 16)); 107 | }).join("")); 108 | } 109 | -------------------------------------------------------------------------------- /client-js/packages/test-icx/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react-app"], 3 | "plugins": ["transform-class-properties"] 4 | } 5 | -------------------------------------------------------------------------------- /client-js/packages/test-icx/.gitignore: -------------------------------------------------------------------------------- 1 | .cache/ 2 | dist/ 3 | node_modules/ 4 | -------------------------------------------------------------------------------- /client-js/packages/test-icx/index.css: -------------------------------------------------------------------------------- 1 | .root { 2 | font-family: "Arial"; 3 | font-size: small; 4 | } 5 | 6 | .action { 7 | font-size: 90%; 8 | background-color: #fcfcfc; 9 | } 10 | 11 | .result { 12 | background-color: #eee; 13 | color: #222; 14 | font-size: 1.0em; 15 | } 16 | 17 | .error { 18 | color: #c00; 19 | } -------------------------------------------------------------------------------- /client-js/packages/test-icx/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ICON simple test 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /client-js/packages/test-icx/index.js: -------------------------------------------------------------------------------- 1 | import "./index.css"; 2 | import React, { Component } from "react"; 3 | import ReactDOM from "react-dom"; 4 | import TransportU2F from "@ledgerhq/hw-transport-u2f"; 5 | import Icx from "@ledgerhq/hw-app-icx"; 6 | import Btc from "@ledgerhq/hw-app-btc"; 7 | 8 | class App extends Component { 9 | state = { 10 | result: null, 11 | error: null 12 | }; 13 | 14 | clear = () => { 15 | this.setState({ result: null }); 16 | this.setState({ error: null }); 17 | }; 18 | showProcessing = () => { 19 | this.setState({ error: null }); 20 | this.setState({ result: "processing..." }); 21 | }; 22 | showTestResult = (failMessage: string) => { 23 | if (failMessage.length > 0) { 24 | this.setState({ error: "FAIL: " + failMessage }); 25 | } 26 | else { 27 | this.setState({ result: "PASS" }); 28 | } 29 | } 30 | 31 | hexToBase64 = (hexString: string) => { 32 | return btoa(hexString.match(/\w{2}/g).map(function(a) { 33 | return String.fromCharCode(parseInt(a, 16)); 34 | }).join("")); 35 | } 36 | 37 | onBtcGetAddress = async () => { 38 | try { 39 | this.showProcessing(); 40 | const transport = await TransportU2F.create(); 41 | transport.setDebugMode(true); 42 | const btc = new Btc(transport); 43 | const { bitcoinAddress } = await btc.getWalletPublicKey("44'/0'/0'/0"); 44 | this.setState({ result: bitcoinAddress }); 45 | } catch (error) { 46 | this.setState({ error }); 47 | } 48 | }; 49 | 50 | createIcx = async (timeout?: number = 30000): Icx => { 51 | const transport = await TransportU2F.create(); 52 | transport.setDebugMode(true); 53 | transport.setExchangeTimeout(timeout); 54 | return new Icx(transport); 55 | }; 56 | 57 | onGetAddress = async () => { 58 | try { 59 | this.showProcessing(); 60 | const icx = await this.createIcx(); 61 | const { publicKey, address, chainCode } = await icx.getAddress("44'/4801368'/0'"); 62 | const resultText = "[publicKey=" + publicKey + "],[address=" + address + "],[chainCode=" + chainCode + "]"; 63 | this.setState({ result: resultText }); 64 | } catch (error) { 65 | this.setState({ error }); 66 | } 67 | }; 68 | onSignTransaction = async () => { 69 | try { 70 | this.showProcessing(); 71 | const icx = await this.createIcx(90000); 72 | const path = "44'/4801368'/0'"; 73 | const rawTx = 74 | "icx_sendTransaction.fee.0x2386f26fc10000." + 75 | "from.hxc9ecad30b05a0650a337452fce031e0c60eacc3a.nonce.0x3." + 76 | "to.hx4c5101add2caa6a920420cf951f7dd7c7df6ca24.value.0xde0b6b3a7640000"; 77 | const { signedRawTxBase64, hashHex } = 78 | await icx.signTransaction(path, rawTx); 79 | const resultText = "[signature=" + signedRawTxBase64 + "],[hash=" + hashHex + "]"; 80 | this.setState({ result: resultText }); 81 | } catch (error) { 82 | this.setState({ error }); 83 | } 84 | }; 85 | onSignTransactionV3 = async () => { 86 | try { 87 | this.showProcessing(); 88 | const icx = await this.createIcx(90000); 89 | const path = "44'/4801368'/0'"; 90 | const rawTx = 91 | "icx_sendTransaction." + 92 | "from.hxc9ecad30b05a0650a337452fce031e0c60eacc3a.nonce.0x3." + 93 | "stepLimit.0xff." + 94 | "to.hx4c5101add2caa6a920420cf951f7dd7c7df6ca24.value.0xde0b6b3a7640000." + 95 | "version.0x3"; 96 | const { signedRawTxBase64, hashHex } = 97 | await icx.signTransaction(path, rawTx); 98 | const resultText = "[signature=" + signedRawTxBase64 + "],[hash=" + hashHex + "]"; 99 | this.setState({ result: resultText }); 100 | } catch (error) { 101 | this.setState({ error }); 102 | } 103 | }; 104 | onGetAppConfiguration = async () => { 105 | try { 106 | this.showProcessing(); 107 | const icx = await this.createIcx(); 108 | const { 109 | majorVersion, 110 | minorVersion, 111 | patchVersion 112 | } = await icx.getAppConfiguration(); 113 | const resultText = 114 | "[majorVer=" + majorVersion + "],[minorVer=" + minorVersion + 115 | "],[patchVer=" + patchVersion + "]"; 116 | this.setState({ result: resultText }); 117 | } catch (error) { 118 | this.setState({ error }); 119 | } 120 | }; 121 | onTestGetAddress = async() => { 122 | try { 123 | this.showProcessing(); 124 | const icx = await this.createIcx(90000); 125 | await icx.setTestPrivateKey("23498dc21b9ee52e63e8d6566e0911ac255a38d3fcbc68a51e6b298520b72d6e"); 126 | const { publicKey, address, chainCode } = await icx.getAddress("0'", true, false); 127 | 128 | let isSamePublicKey = false; 129 | let isSameAddress = false; 130 | const answerPublicKey = "0498cd4a92d7d491e3552d6abafe96e4add9f4f068bf385325e0dab902baad1602a150ea36b36974fcc7535f24e6715270277848c8421deb5586f611bdd7551f72"; 131 | const answerAddress = "hxfa1602a01d0ca2c2256f1a508be6498df801a5b2"; 132 | if (publicKey == answerPublicKey) { 133 | isSamePublicKey = true; 134 | } 135 | if (address == answerAddress) { 136 | isSameAddress = true; 137 | } 138 | let failMessage = ""; 139 | if (!isSamePublicKey) { 140 | failMessage += "wrong public key ('" + publicKey + "' should be '" + answerPublicKey + "'.)" ; 141 | } 142 | else if (!isSameAddress) { 143 | failMessage += "wrong address ('" + address + "' should be '" + answerAddress + "'.)" ; 144 | } 145 | this.showTestResult(failMessage); 146 | } catch (error) { 147 | this.setState({ error }); 148 | } 149 | }; 150 | onTestSignTransaction = async() => { 151 | try { 152 | this.showProcessing(); 153 | const icx = await this.createIcx(90000); 154 | await icx.setTestPrivateKey("c8e2edf81129f07720ed5ae36311316fb84ac6f7ddbc0f175f9df5848ad431ed"); 155 | const rawTx = 156 | "icx_sendTransaction.fee.0x2386f26fc10000" + 157 | ".from.hx36f93789103fa3b5da3d40c13d08c2ca2457ca86.timestamp.1530259517236639" + 158 | ".to.hx30d027ff0b2e52645861d85efb04896127ec166e.value.0xde0b6b3a7640000"; 159 | const { signedRawTxBase64, hashHex } = await icx.signTransaction("0'", rawTx); 160 | 161 | let isSameSignature = false; 162 | let isSameHash = false; 163 | const answerSignature = this.hexToBase64("efc780adffc77b4fad0a18a3df5203128e69494e3abfc5f113e6d16b32ab0fd9204e97cdbcd61cd70997138ae74312b5b04945e38d67cfc2a9352af28b3599f300"); 164 | const answerHash = "72f26c66527755484d4a9877457b7092827cad9ece8685ce392ebeccf17babad"; 165 | if (signedRawTxBase64 == answerSignature) { 166 | isSameSignature = true; 167 | } 168 | if (hashHex == answerHash) { 169 | isSameHash = true; 170 | } 171 | let failMessage = ""; 172 | if (!isSameSignature) { 173 | failMessage += "wrong signature ('" + signedRawTxBase64 + "' should be '" + answerSignature + "'.)" ; 174 | } 175 | else if (!isSameHash) { 176 | failMessage += "wrong hash ('" + hashHex + "' should be '" + answerHash + "'.)" ; 177 | } 178 | this.showTestResult(failMessage); 179 | } catch (error) { 180 | this.setState({ error }); 181 | } 182 | }; 183 | onTestSignTransactionLongData = async() => { 184 | try { 185 | this.showProcessing(); 186 | const icx = await this.createIcx(90000); 187 | await icx.setTestPrivateKey("e0b18a065bb0bd663e71aff34d1bd44dbf746113968533058440eca75b061c9e"); 188 | // I don't know it's a right JSON format of transaction data 189 | let rawTx = "icx_sendTransaction.amount.ox1234.content.0x"; 190 | for (let i = 0; i < 200; i++) { 191 | rawTx += "1867291283973610982301923812873419826abcdef91827319263187263a732" // 64 192 | } 193 | rawTx += ".contentType.application/zip.dataType.deploy." + 194 | "fee.0x2386f26fc10000.from.hx6ad6f9a5adada3e484861b52378990bd6f473450." + 195 | "stepLimit.0x14.timestamp.1529897548414400." + 196 | "to.hx609c1c454528bae228514ceccec0c0939637a3fb.value.0xde0b6b3a7640000"; 197 | const { signedRawTxBase64, hashHex } = await icx.signTransaction("0'", rawTx); 198 | 199 | let isSameSignature = false; 200 | let isSameHash = false; 201 | const answerSignature = this.hexToBase64("08ed94735506cddba0fe935bc1246b7408915cd1eaf7fc9203fea754a0bbdae562e5c3960195d226abcb0e272ead200cf5fdebd0583dc715207db8818cea386000"); 202 | const answerHash = "0651e37662eab417666e8dd664b6f62a8c42e9cf9c8eaababb6727cc203d0e18"; 203 | if (signedRawTxBase64 == answerSignature) { 204 | isSameSignature = true; 205 | } 206 | if (hashHex == answerHash) { 207 | isSameHash = true; 208 | } 209 | let failMessage = ""; 210 | if (!isSameSignature) { 211 | failMessage += "wrong signature ('" + signedRawTxBase64 + "' should be '" + answerSignature + "'.)" ; 212 | } 213 | else if (!isSameHash) { 214 | failMessage += "wrong hash ('" + hashHex + "' should be '" + answerHash + "'.)" ; 215 | } 216 | this.showTestResult(failMessage); 217 | } catch (error) { 218 | this.setState({ error }); 219 | } 220 | }; 221 | onTestSignTransactionMultiLevelParams= async () => { 222 | try { 223 | this.showProcessing(); 224 | const icx = await this.createIcx(90000); 225 | const path = "44'/4801368'/0'"; 226 | const rawTx = 227 | "icx_sendTransaction.data." + 228 | "{method.transfer.params." + 229 | "{_to.hx5be671cf246b6eb9438914494cbab57e2641d37a._value.0xde0b6b3a7640000}}." + 230 | "dataType.call.from.hx772d8fa231eef810b9e39270963f97dacc69f2a5.nid.0x3." + 231 | "stepLimit.0x2b750.timestamp.0x573e910b5cb48." + 232 | "to.cxe382845b0fa8d00d748f7b29c9ee7369eee20897.value.0x0.version.0x3"; 233 | const { signedRawTxBase64, hashHex } = 234 | await icx.signTransaction(path, rawTx); 235 | this.showTestResult(""); 236 | } catch (error) { 237 | this.setState({ error }); 238 | } 239 | }; 240 | onTestSignTransactionNoValue= async () => { 241 | try { 242 | this.showProcessing(); 243 | const icx = await this.createIcx(90000); 244 | const path = "44'/4801368'/0'"; 245 | const rawTx = 246 | "icx_sendTransaction.data.{method.transfer.params.{_to.hxb2d0d07dcdd887a8b60e3069d87e91a312947adc._value.0x1bc16d674ec80000}}.dataType.call.from.hx772d8fa231eef810b9e39270963f97dacc69f2a5.nid.0x1.stepLimit.0x30d40.timestamp.0x5815c9daa8a18.to.cxc248ee72f58f7ec0e9a382379d67399f45b596c7.version.0x3" 247 | const { signedRawTxBase64, hashHex } = 248 | await icx.signTransaction(path, rawTx); 249 | this.showTestResult(""); 250 | } catch (error) { 251 | this.setState({ error }); 252 | } 253 | }; 254 | onTestSignTransactionBigValue= async () => { 255 | // Current display buffer is 47, so test value should be within int256 range 256 | // in loop and longer than 47 decimal numbers in ICX. 257 | try { 258 | this.showProcessing(); 259 | const icx = await this.createIcx(90000); 260 | const path = "44'/4801368'/0'"; 261 | const rawTx = 262 | "icx_sendTransaction.data." + 263 | "{method.transfer.params." + 264 | "{_to.hx5be671cf246b6eb9438914494cbab57e2641d37a._value.0xde0b6b3a7640000}}." + 265 | "dataType.call.from.hx772d8fa231eef810b9e39270963f97dacc69f2a5.nid.0x3." + 266 | "stepLimit.0x2b750.timestamp.0x573e910b5cb48." + 267 | "to.cxe382845b0fa8d00d748f7b29c9ee7369eee20897.value.0xde0b6b3a76400000000000000000000000000000.version.0x3"; 268 | const { signedRawTxBase64, hashHex } = 269 | await icx.signTransaction(path, rawTx); 270 | this.showTestResult("It should occur an error of code 0x6a80."); 271 | } catch (error) { 272 | if (error.statusCode == 0x6a80) { 273 | this.showTestResult(""); 274 | } else { 275 | this.setState({ error }); 276 | } 277 | } 278 | }; 279 | onClear = async() => { 280 | this.clear(); 281 | }; 282 | 283 | render() { 284 | const { result, error } = this.state; 285 | return ( 286 |
287 |
    288 |
  • simple function call 289 |
      290 |
    • 291 | 294 |
    • 295 |
    • 296 | 299 |
        300 |
      • check tx info on Ledger display and confirm. amount=1, fee=0x01
      • 301 |
      302 |
    • 303 |
    • 304 | 307 |
        308 |
      • check tx info on Ledger display and confirm. amount=1, stepLimit=255
      • 309 |
      310 |
    • 311 |
    • 312 | 315 |
    • 316 |
    317 |
  • 318 |
319 |
    320 |
  • ledger app test cases 321 |
      322 |
    • 323 | 326 |
        327 |
      • check if the address on display is 'hxfa1602a01d0ca2c2256f1a508be6498df801a5b2' and confirm
      • 328 |
      329 |
    • 330 |
    331 |
      332 |
    • 333 | 336 |
        337 |
      • check tx info on Ledger display and confirm
      • 338 |
      339 |
    • 340 |
    341 |
      342 |
    • 343 | 346 |
        347 |
      • check tx info on Ledger display and confirm
      • 348 |
      349 |
    • 350 |
    351 |
      352 |
    • 353 | 356 |
        357 |
      • check tx info on Ledger display and confirm
      • 358 |
      • NOTE: It doesn't check the signature validity
      • 359 |
      360 |
    • 361 |
    362 |
      363 |
    • 364 | 367 |
        368 |
      • check if tx info on Ledger display doesn't have wrong amount such as "ICX StepLimit" and confirm
      • 369 |
      • NOTE: It doesn't check the signature validity
      • 370 |
      371 |
    • 372 |
    373 |
      374 |
    • 375 | 378 |
    • 379 |
    380 |
  • 381 |
382 |
    383 |
  • reference function 384 |
      385 |
    • 386 | 389 |
        390 |
      • NOTE: make sure BTC app is running
      • 391 |
      392 |
    • 393 |
    394 |
  • 395 |
396 |

397 | RESULT: 398 |

399 | {error ? ( 400 | {error.toString()} 401 | ) : ( 402 | {result} 403 | )} 404 |

405 |

406 |
407 | ); 408 | } 409 | } 410 | 411 | ReactDOM.render(, document.getElementById("root")); 412 | -------------------------------------------------------------------------------- /client-js/packages/test-icx/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ledgerhq/test-icx", 3 | "license": "Apache-2.0", 4 | "scripts": { 5 | "start": "cross-env NODE_ENV=development parcel index.html --https", 6 | "build": "rm -rf .cache/ && cross-env NODE_ENV=production parcel build index.html --no-minify --public-url /", 7 | "clean": "rm -rf dist/ .cache/", 8 | "test": "budo --ssl -d dist" 9 | }, 10 | "dependencies": { 11 | "@ledgerhq/hw-app-btc": "^4.13.0", 12 | "@ledgerhq/hw-app-icx": "^0.1.0", 13 | "@ledgerhq/hw-transport-u2f": "^4.13.0", 14 | "react": "^16.2.0", 15 | "react-dom": "^16.2.0", 16 | "budo": "^11.0.1" 17 | }, 18 | "devDependencies": { 19 | "babel-plugin-transform-class-properties": "^6.24.1", 20 | "babel-preset-react-app": "^3.1.1", 21 | "cross-env": "^5.1.4", 22 | "parcel-bundler": "^1.6.2" 23 | }, 24 | "private": true, 25 | "version": "4.14.0" 26 | } 27 | -------------------------------------------------------------------------------- /dive.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | IMAGE="nanos-dev:latest" 4 | SCRIPT_DIR="$(cd `dirname ${0}` ; pwd)" 5 | 6 | declare -a ARGS 7 | ARGS=() 8 | ARGS+=("-it" "--rm") 9 | ARGS+=("-v" "${SCRIPT_DIR}:${SCRIPT_DIR}" -w "$(pwd)") 10 | ARGS+=("-v" "/dev:/dev" "--privileged") 11 | ARGS+=("$IMAGE" "$@") 12 | 13 | 14 | docker run "${ARGS[@]}" 15 | -------------------------------------------------------------------------------- /doc/icon.asc: -------------------------------------------------------------------------------- 1 | ICON application : Common Technical Specifications 2 | ================================================== 3 | 4 | ## 1.0 5 | - Initial release 6 | 7 | ## About 8 | 9 | This specification describes the APDU messages interface to communicate with the ICON 10 | application. 11 | 12 | ## General purpose APDUs 13 | 14 | ### GET PUBLIC KEY 15 | 16 | #### Description 17 | 18 | This command returns the public key and ICON address for the given BIP 32 path. 19 | 20 | The address can be optionally checked on the device before being returned. 21 | 22 | #### Coding 23 | 24 | 'Command' 25 | 26 | [width="80%"] 27 | |============================================================================================================================== 28 | | *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* 29 | | E0 | 02 | 00 : return address 30 | 31 | 01 : display address and confirm before returning 32 | 33 | | 00 : do not return the chain code 34 | 35 | 01 : return the chain code 36 | 37 | | variable | variable 38 | |============================================================================================================================== 39 | 40 | 'Input data' 41 | 42 | [width="80%"] 43 | |============================================================================================================================== 44 | | *Description* | *Length* 45 | | Number of BIP 32 derivations to perform (max 10) | 1 46 | | First derivation index (big endian) | 4 47 | | ... | 4 48 | | Last derivation index (big endian) | 4 49 | |============================================================================================================================== 50 | 51 | 'Output data' 52 | 53 | [width="80%"] 54 | |============================================================================================================================== 55 | | *Description* | *Length* 56 | | Public Key length | 1 57 | | Uncompressed Public Key | var 58 | | ICON address length | 1 59 | | ICON address including hx | var 60 | | Chain code if requested | 32 61 | |============================================================================================================================== 62 | 63 | 64 | ### SIGN ICON TRANSACTION 65 | 66 | #### Description 67 | 68 | This command signs an ICON transaction. 69 | 70 | The transaction data to be provided should be encoded using ICON standard raw transaction encoding. 71 | 72 | #### Coding 73 | 74 | 'Command' 75 | 76 | [width="80%"] 77 | |============================================================================================================================== 78 | | *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* 79 | | E0 | 04 | 00 : first transaction data block 80 | 81 | 80 : subsequent transaction data block 82 | | 00 | variable | variable 83 | |============================================================================================================================== 84 | 85 | 'Input data (first transaction data block)' 86 | 87 | [width="80%"] 88 | |============================================================================================================================== 89 | | *Description* | *Length* 90 | | Number of BIP 32 derivations to perform (max 10) | 1 91 | | First derivation index (big endian) | 4 92 | | ... | 4 93 | | Last derivation index (big endian) | 4 94 | | transcation length | 4 95 | | transaction chunk | variable 96 | |============================================================================================================================== 97 | 98 | 'Input data (other transaction data block)' 99 | 100 | [width="80%"] 101 | |============================================================================================================================== 102 | | *Description* | *Length* 103 | | transaction chunk | variable 104 | |============================================================================================================================== 105 | 106 | 107 | 'Output data' 108 | 109 | [width="80%"] 110 | |============================================================================================================================== 111 | | *Description* | *Length* 112 | | r | 32 113 | | s | 32 114 | | v | 1 115 | | hash | 32 116 | |============================================================================================================================== 117 | 118 | ### GET APP CONFIGURATION 119 | 120 | #### Description 121 | 122 | This command returns specific application configuration 123 | 124 | #### Coding 125 | 126 | 'Command' 127 | 128 | [width="80%"] 129 | |============================================================================================================================== 130 | | *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* 131 | | E0 | 06 | 00 | 00 | 00 | 03 132 | |============================================================================================================================== 133 | 134 | 'Input data' 135 | 136 | None 137 | 138 | 'Output data' 139 | 140 | [width="80%"] 141 | |============================================================================================================================== 142 | | *Description* | *Length* 143 | | Application major version | 01 144 | | Application minor version | 01 145 | | Application patch version | 01 146 | |============================================================================================================================== 147 | 148 | ## Test purpose APDUs 149 | 150 | ### SET TEST PRIVATE KEY 151 | 152 | #### Description 153 | 154 | This command sets test private key. 155 | 156 | For all BIP-32 pathes under m/0', test private key is used instead of derived key. This feature is enabled only for test version wallets. 157 | 158 | #### Coding 159 | 160 | 'Command' 161 | 162 | [width="80%"] 163 | |============================================================================================================================== 164 | | *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* 165 | | E0 | FF | 00 | 00 | 32 | 00 166 | |============================================================================================================================== 167 | 168 | 'Input data' 169 | 170 | [width="80%"] 171 | |============================================================================================================================== 172 | | *Description* | *Length* 173 | | Private Key | 32 174 | |============================================================================================================================== 175 | 176 | 'Output data' 177 | 178 | [width="80%"] 179 | |============================================================================================================================== 180 | | *Description* | *Length* 181 | |============================================================================================================================== 182 | 183 | ## Transport protocol 184 | 185 | ### General transport description 186 | 187 | Ledger APDUs requests and responses are encapsulated using a flexible protocol allowing to fragment large payloads over different underlying transport mechanisms. 188 | 189 | The common transport header is defined as follows : 190 | 191 | [width="80%"] 192 | |============================================================================================================================== 193 | | *Description* | *Length* 194 | | Communication channel ID (big endian) | 2 195 | | Command tag | 1 196 | | Packet sequence index (big endian) | 2 197 | | Payload | var 198 | |============================================================================================================================== 199 | 200 | The Communication channel ID allows commands multiplexing over the same physical link. It is not used for the time being, and should be set to 0101 to avoid compatibility issues with implementations ignoring a leading 00 byte. 201 | 202 | The Command tag describes the message content. Use TAG_APDU (0x05) for standard APDU payloads, or TAG_PING (0x02) for a simple link test. 203 | 204 | The Packet sequence index describes the current sequence for fragmented payloads. The first fragment index is 0x00. 205 | 206 | ### APDU Command payload encoding 207 | 208 | APDU Command payloads are encoded as follows : 209 | 210 | [width="80%"] 211 | |============================================================================================================================== 212 | | *Description* | *Length* 213 | | APDU length (big endian) | 2 214 | | APDU CLA | 1 215 | | APDU INS | 1 216 | | APDU P1 | 1 217 | | APDU P2 | 1 218 | | APDU length | 1 219 | | Optional APDU data | var 220 | |============================================================================================================================== 221 | 222 | APDU payload is encoded according to the APDU case 223 | 224 | [width="80%"] 225 | |======================================================================================= 226 | | Case Number | *Lc* | *Le* | Case description 227 | | 1 | 0 | 0 | No data in either direction - L is set to 00 228 | | 2 | 0 | !0 | Input Data present, no Output Data - L is set to Lc 229 | | 3 | !0 | 0 | Output Data present, no Input Data - L is set to Le 230 | | 4 | !0 | !0 | Both Input and Output Data are present - L is set to Lc 231 | |======================================================================================= 232 | 233 | ### APDU Response payload encoding 234 | 235 | APDU Response payloads are encoded as follows : 236 | 237 | [width="80%"] 238 | |============================================================================================================================== 239 | | *Description* | *Length* 240 | | APDU response length (big endian) | 2 241 | | APDU response data and Status Word | var 242 | |============================================================================================================================== 243 | 244 | ### USB mapping 245 | 246 | Messages are exchanged with the dongle over HID endpoints over interrupt transfers, with each chunk being 64 bytes long. The HID Report ID is ignored. 247 | 248 | 249 | ## Status Words 250 | 251 | The following standard Status Words are returned for all APDUs - some specific Status Words can be used for specific commands and are mentioned in the command description. 252 | 253 | 'Status Words' 254 | 255 | [width="80%"] 256 | |=============================================================================================== 257 | | *SW* | *Description* 258 | | 6700 | Incorrect length 259 | | 6982 | No APDU 260 | | 6985 | Security status not satisfied (Canceled by user) 261 | | 6A80 | Invalid data 262 | | 6B00 | Incorrect parameter P1 or P2 263 | | 6Fxx | Technical problem (Internal error, please report) 264 | | 9000 | Normal ending of the command 265 | |=============================================================================================== 266 | 267 | -------------------------------------------------------------------------------- /docker/.gitattributes: -------------------------------------------------------------------------------- 1 | *.tar.* filter=lfs diff=lfs merge=lfs -text 2 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:16.04 2 | RUN apt update && apt -y install build-essential libc6-i386 libc6-dev-i386 python python-pip libudev-dev libusb-1.0-0-dev python3-dev git 3 | ENV BOLOS_ENV /work/bolos 4 | RUN mkdir -p ${BOLOS_ENV} 5 | WORKDIR ${BOLOS_ENV} 6 | ADD clang+llvm-4.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz ${BOLOS_ENV} 7 | RUN ln -s "clang+llvm-4.0.0-x86_64-linux-gnu-ubuntu-16.04" "clang-arm-fropi" 8 | ADD gcc-arm-none-eabi-5_3-2016q1-20160330-linux.tar.bz2 ${BOLOS_ENV} 9 | ARG BOLOS_SDK_NAME=nanos-secure-sdk-nanos-1421 10 | ADD ${BOLOS_SDK_NAME}.* ${BOLOS_ENV} 11 | ENV BOLOS_SDK ${BOLOS_ENV}/${BOLOS_SDK_NAME} 12 | RUN git clone https://github.com/LedgerHQ/blue-loader-python.git && ( cd blue-loader-python ; pip install ledgerblue ) 13 | RUN rm -rf blue-loader-python 14 | CMD /bin/bash 15 | -------------------------------------------------------------------------------- /docker/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT_DIR=$(cd `dirname "$0"` ; pwd) 4 | NAME="nanos-dev" 5 | VERSION="latest" 6 | 7 | docker build -t "${NAME}:${VERSION}" ${SCRIPT_DIR} 8 | -------------------------------------------------------------------------------- /docker/clang+llvm-4.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:8e920c166b00e7869cea443e305933cc410bd1b982c7c5dc56995b6cffb0586f 3 | size 221329760 4 | -------------------------------------------------------------------------------- /docker/gcc-arm-none-eabi-5_3-2016q1-20160330-linux.tar.bz2: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:217850b0f3297014e8e52010aa52da0a83a073ddec4dc49b1a747458c5d6a223 3 | size 93090908 4 | -------------------------------------------------------------------------------- /docker/nanos-secure-sdk-nanos-1421.tar.gz: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:0c12b4ffaf9b2c71311d5aa0cb07e619bc02b2d82daaa80f97083e5cda4dbad8 3 | size 238463 4 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/ledger-app-icx/c2d15e89641b05c9a26e9e094b786c512faacff9/icon.png -------------------------------------------------------------------------------- /icon_testnet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/ledger-app-icx/c2d15e89641b05c9a26e9e094b786c512faacff9/icon_testnet.png -------------------------------------------------------------------------------- /nanos_app_icon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/ledger-app-icx/c2d15e89641b05c9a26e9e094b786c512faacff9/nanos_app_icon.gif -------------------------------------------------------------------------------- /nanos_app_icon_testnet.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/ledger-app-icx/c2d15e89641b05c9a26e9e094b786c512faacff9/nanos_app_icon_testnet.gif -------------------------------------------------------------------------------- /src/uint256.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Ledger Blue 3 | * (c) 2016 Ledger 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | ********************************************************************************/ 17 | 18 | // Adapted from https://github.com/calccrypto/uint256_t 19 | 20 | #include 21 | #include 22 | #include "uint256.h" 23 | 24 | static const char HEXDIGITS[] = "0123456789abcdef"; 25 | 26 | static uint64_t readUint64BE(uint8_t *buffer) { 27 | return (((uint64_t)buffer[0]) << 56) | (((uint64_t)buffer[1]) << 48) | 28 | (((uint64_t)buffer[2]) << 40) | (((uint64_t)buffer[3]) << 32) | 29 | (((uint64_t)buffer[4]) << 24) | (((uint64_t)buffer[5]) << 16) | 30 | (((uint64_t)buffer[6]) << 8) | (((uint64_t)buffer[7])); 31 | } 32 | 33 | void readu128BE(uint8_t *buffer, uint128_t *target) { 34 | UPPER_P(target) = readUint64BE(buffer); 35 | LOWER_P(target) = readUint64BE(buffer + 8); 36 | } 37 | 38 | void readu256BE(uint8_t *buffer, uint256_t *target) { 39 | readu128BE(buffer, &UPPER_P(target)); 40 | readu128BE(buffer + 16, &LOWER_P(target)); 41 | } 42 | 43 | bool zero128(uint128_t *number) { 44 | return ((LOWER_P(number) == 0) && (UPPER_P(number) == 0)); 45 | } 46 | 47 | bool zero256(uint256_t *number) { 48 | return (zero128(&LOWER_P(number)) && zero128(&UPPER_P(number))); 49 | } 50 | 51 | void copy128(uint128_t *target, uint128_t *number) { 52 | UPPER_P(target) = UPPER_P(number); 53 | LOWER_P(target) = LOWER_P(number); 54 | } 55 | 56 | void copy256(uint256_t *target, uint256_t *number) { 57 | copy128(&UPPER_P(target), &UPPER_P(number)); 58 | copy128(&LOWER_P(target), &LOWER_P(number)); 59 | } 60 | 61 | void clear128(uint128_t *target) { 62 | UPPER_P(target) = 0; 63 | LOWER_P(target) = 0; 64 | } 65 | 66 | void clear256(uint256_t *target) { 67 | clear128(&UPPER_P(target)); 68 | clear128(&LOWER_P(target)); 69 | } 70 | 71 | void shiftl128(uint128_t *number, uint32_t value, uint128_t *target) { 72 | if (value >= 128) { 73 | clear128(target); 74 | } else if (value == 64) { 75 | UPPER_P(target) = LOWER_P(number); 76 | LOWER_P(target) = 0; 77 | } else if (value == 0) { 78 | copy128(target, number); 79 | } else if (value < 64) { 80 | UPPER_P(target) = 81 | (UPPER_P(number) << value) + (LOWER_P(number) >> (64 - value)); 82 | LOWER_P(target) = (LOWER_P(number) << value); 83 | } else if ((128 > value) && (value > 64)) { 84 | UPPER_P(target) = LOWER_P(number) << (value - 64); 85 | LOWER_P(target) = 0; 86 | } else { 87 | clear128(target); 88 | } 89 | } 90 | 91 | void shiftl256(uint256_t *number, uint32_t value, uint256_t *target) { 92 | if (value >= 256) { 93 | clear256(target); 94 | } else if (value == 128) { 95 | copy128(&UPPER_P(target), &LOWER_P(number)); 96 | clear128(&LOWER_P(target)); 97 | } else if (value == 0) { 98 | copy256(target, number); 99 | } else if (value < 128) { 100 | uint128_t tmp1; 101 | uint128_t tmp2; 102 | uint256_t result; 103 | shiftl128(&UPPER_P(number), value, &tmp1); 104 | shiftr128(&LOWER_P(number), (128 - value), &tmp2); 105 | add128(&tmp1, &tmp2, &UPPER(result)); 106 | shiftl128(&LOWER_P(number), value, &LOWER(result)); 107 | copy256(target, &result); 108 | } else if ((256 > value) && (value > 128)) { 109 | shiftl128(&LOWER_P(number), (value - 128), &UPPER_P(target)); 110 | clear128(&LOWER_P(target)); 111 | } else { 112 | clear256(target); 113 | } 114 | } 115 | 116 | void shiftr128(uint128_t *number, uint32_t value, uint128_t *target) { 117 | if (value >= 128) { 118 | clear128(target); 119 | } else if (value == 64) { 120 | UPPER_P(target) = 0; 121 | LOWER_P(target) = UPPER_P(number); 122 | } else if (value == 0) { 123 | copy128(target, number); 124 | } else if (value < 64) { 125 | uint128_t result; 126 | UPPER(result) = UPPER_P(number) >> value; 127 | LOWER(result) = 128 | (UPPER_P(number) << (64 - value)) + (LOWER_P(number) >> value); 129 | copy128(target, &result); 130 | } else if ((128 > value) && (value > 64)) { 131 | LOWER_P(target) = UPPER_P(number) >> (value - 64); 132 | UPPER_P(target) = 0; 133 | } else { 134 | clear128(target); 135 | } 136 | } 137 | 138 | void shiftr256(uint256_t *number, uint32_t value, uint256_t *target) { 139 | if (value >= 256) { 140 | clear256(target); 141 | } else if (value == 128) { 142 | copy128(&LOWER_P(target), &UPPER_P(number)); 143 | clear128(&UPPER_P(target)); 144 | } else if (value == 0) { 145 | copy256(target, number); 146 | } else if (value < 128) { 147 | uint128_t tmp1; 148 | uint128_t tmp2; 149 | uint256_t result; 150 | shiftr128(&UPPER_P(number), value, &UPPER(result)); 151 | shiftr128(&LOWER_P(number), value, &tmp1); 152 | shiftl128(&UPPER_P(number), (128 - value), &tmp2); 153 | add128(&tmp1, &tmp2, &LOWER(result)); 154 | copy256(target, &result); 155 | } else if ((256 > value) && (value > 128)) { 156 | shiftr128(&UPPER_P(number), (value - 128), &LOWER_P(target)); 157 | clear128(&UPPER_P(target)); 158 | } else { 159 | clear256(target); 160 | } 161 | } 162 | 163 | uint32_t bits128(uint128_t *number) { 164 | uint32_t result = 0; 165 | if (UPPER_P(number)) { 166 | result = 64; 167 | uint64_t up = UPPER_P(number); 168 | while (up) { 169 | up >>= 1; 170 | result++; 171 | } 172 | } else { 173 | uint64_t low = LOWER_P(number); 174 | while (low) { 175 | low >>= 1; 176 | result++; 177 | } 178 | } 179 | return result; 180 | } 181 | 182 | uint32_t bits256(uint256_t *number) { 183 | uint32_t result = 0; 184 | if (!zero128(&UPPER_P(number))) { 185 | result = 128; 186 | uint128_t up; 187 | copy128(&up, &UPPER_P(number)); 188 | while (!zero128(&up)) { 189 | shiftr128(&up, 1, &up); 190 | result++; 191 | } 192 | } else { 193 | uint128_t low; 194 | copy128(&low, &LOWER_P(number)); 195 | while (!zero128(&low)) { 196 | shiftr128(&low, 1, &low); 197 | result++; 198 | } 199 | } 200 | return result; 201 | } 202 | 203 | bool equal128(uint128_t *number1, uint128_t *number2) { 204 | return (UPPER_P(number1) == UPPER_P(number2)) && 205 | (LOWER_P(number1) == LOWER_P(number2)); 206 | } 207 | 208 | bool equal256(uint256_t *number1, uint256_t *number2) { 209 | return (equal128(&UPPER_P(number1), &UPPER_P(number2)) && 210 | equal128(&LOWER_P(number1), &LOWER_P(number2))); 211 | } 212 | 213 | bool gt128(uint128_t *number1, uint128_t *number2) { 214 | if (UPPER_P(number1) == UPPER_P(number2)) { 215 | return (LOWER_P(number1) > LOWER_P(number2)); 216 | } 217 | return (UPPER_P(number1) > UPPER_P(number2)); 218 | } 219 | 220 | bool gt256(uint256_t *number1, uint256_t *number2) { 221 | if (equal128(&UPPER_P(number1), &UPPER_P(number2))) { 222 | return gt128(&LOWER_P(number1), &LOWER_P(number2)); 223 | } 224 | return gt128(&UPPER_P(number1), &UPPER_P(number2)); 225 | } 226 | 227 | bool gte128(uint128_t *number1, uint128_t *number2) { 228 | return gt128(number1, number2) || equal128(number1, number2); 229 | } 230 | 231 | bool gte256(uint256_t *number1, uint256_t *number2) { 232 | return gt256(number1, number2) || equal256(number1, number2); 233 | } 234 | 235 | void add128(uint128_t *number1, uint128_t *number2, uint128_t *target) { 236 | UPPER_P(target) = 237 | UPPER_P(number1) + UPPER_P(number2) + 238 | ((LOWER_P(number1) + LOWER_P(number2)) < LOWER_P(number1)); 239 | LOWER_P(target) = LOWER_P(number1) + LOWER_P(number2); 240 | } 241 | 242 | void add256(uint256_t *number1, uint256_t *number2, uint256_t *target) { 243 | uint128_t tmp; 244 | add128(&UPPER_P(number1), &UPPER_P(number2), &UPPER_P(target)); 245 | add128(&LOWER_P(number1), &LOWER_P(number2), &tmp); 246 | if (gt128(&LOWER_P(number1), &tmp)) { 247 | uint128_t one; 248 | UPPER(one) = 0; 249 | LOWER(one) = 1; 250 | add128(&UPPER_P(target), &one, &UPPER_P(target)); 251 | } 252 | add128(&LOWER_P(number1), &LOWER_P(number2), &LOWER_P(target)); 253 | } 254 | 255 | void minus128(uint128_t *number1, uint128_t *number2, uint128_t *target) { 256 | UPPER_P(target) = 257 | UPPER_P(number1) - UPPER_P(number2) - 258 | ((LOWER_P(number1) - LOWER_P(number2)) > LOWER_P(number1)); 259 | LOWER_P(target) = LOWER_P(number1) - LOWER_P(number2); 260 | } 261 | 262 | void minus256(uint256_t *number1, uint256_t *number2, uint256_t *target) { 263 | uint128_t tmp; 264 | minus128(&UPPER_P(number1), &UPPER_P(number2), &UPPER_P(target)); 265 | minus128(&LOWER_P(number1), &LOWER_P(number2), &tmp); 266 | if (gt128(&tmp, &LOWER_P(number1))) { 267 | uint128_t one; 268 | UPPER(one) = 0; 269 | LOWER(one) = 1; 270 | minus128(&UPPER_P(target), &one, &UPPER_P(target)); 271 | } 272 | minus128(&LOWER_P(number1), &LOWER_P(number2), &LOWER_P(target)); 273 | } 274 | 275 | void or128(uint128_t *number1, uint128_t *number2, uint128_t *target) { 276 | UPPER_P(target) = UPPER_P(number1) | UPPER_P(number2); 277 | LOWER_P(target) = LOWER_P(number1) | LOWER_P(number2); 278 | } 279 | 280 | void or256(uint256_t *number1, uint256_t *number2, uint256_t *target) { 281 | or128(&UPPER_P(number1), &UPPER_P(number2), &UPPER_P(target)); 282 | or128(&LOWER_P(number1), &LOWER_P(number2), &LOWER_P(target)); 283 | } 284 | 285 | void mul128(uint128_t *number1, uint128_t *number2, uint128_t *target) { 286 | uint64_t top[4] = {UPPER_P(number1) >> 32, UPPER_P(number1) & 0xffffffff, 287 | LOWER_P(number1) >> 32, LOWER_P(number1) & 0xffffffff}; 288 | uint64_t bottom[4] = {UPPER_P(number2) >> 32, UPPER_P(number2) & 0xffffffff, 289 | LOWER_P(number2) >> 32, 290 | LOWER_P(number2) & 0xffffffff}; 291 | uint64_t products[4][4]; 292 | uint128_t tmp, tmp2; 293 | 294 | for (int y = 3; y > -1; y--) { 295 | for (int x = 3; x > -1; x--) { 296 | products[3 - x][y] = top[x] * bottom[y]; 297 | } 298 | } 299 | 300 | uint64_t fourth32 = products[0][3] & 0xffffffff; 301 | uint64_t third32 = (products[0][2] & 0xffffffff) + (products[0][3] >> 32); 302 | uint64_t second32 = (products[0][1] & 0xffffffff) + (products[0][2] >> 32); 303 | uint64_t first32 = (products[0][0] & 0xffffffff) + (products[0][1] >> 32); 304 | 305 | third32 += products[1][3] & 0xffffffff; 306 | second32 += (products[1][2] & 0xffffffff) + (products[1][3] >> 32); 307 | first32 += (products[1][1] & 0xffffffff) + (products[1][2] >> 32); 308 | 309 | second32 += products[2][3] & 0xffffffff; 310 | first32 += (products[2][2] & 0xffffffff) + (products[2][3] >> 32); 311 | 312 | first32 += products[3][3] & 0xffffffff; 313 | 314 | UPPER(tmp) = first32 << 32; 315 | LOWER(tmp) = 0; 316 | UPPER(tmp2) = third32 >> 32; 317 | LOWER(tmp2) = third32 << 32; 318 | add128(&tmp, &tmp2, target); 319 | UPPER(tmp) = second32; 320 | LOWER(tmp) = 0; 321 | add128(&tmp, target, &tmp2); 322 | UPPER(tmp) = 0; 323 | LOWER(tmp) = fourth32; 324 | add128(&tmp, &tmp2, target); 325 | } 326 | 327 | void mul256(uint256_t *number1, uint256_t *number2, uint256_t *target) { 328 | uint128_t top[4]; 329 | uint128_t bottom[4]; 330 | uint128_t products[4][4]; 331 | uint128_t tmp, tmp2, fourth64, third64, second64, first64; 332 | uint256_t target1, target2; 333 | UPPER(top[0]) = 0; 334 | LOWER(top[0]) = UPPER(UPPER_P(number1)); 335 | UPPER(top[1]) = 0; 336 | LOWER(top[1]) = LOWER(UPPER_P(number1)); 337 | UPPER(top[2]) = 0; 338 | LOWER(top[2]) = UPPER(LOWER_P(number1)); 339 | UPPER(top[3]) = 0; 340 | LOWER(top[3]) = LOWER(LOWER_P(number1)); 341 | UPPER(bottom[0]) = 0; 342 | LOWER(bottom[0]) = UPPER(UPPER_P(number2)); 343 | UPPER(bottom[1]) = 0; 344 | LOWER(bottom[1]) = LOWER(UPPER_P(number2)); 345 | UPPER(bottom[2]) = 0; 346 | LOWER(bottom[2]) = UPPER(LOWER_P(number2)); 347 | UPPER(bottom[3]) = 0; 348 | LOWER(bottom[3]) = LOWER(LOWER_P(number2)); 349 | 350 | for (int y = 3; y > -1; y--) { 351 | for (int x = 3; x > -1; x--) { 352 | mul128(&top[x], &bottom[y], &products[3 - x][y]); 353 | } 354 | } 355 | 356 | UPPER(fourth64) = 0; 357 | LOWER(fourth64) = LOWER(products[0][3]); 358 | UPPER(tmp) = 0; 359 | LOWER(tmp) = LOWER(products[0][2]); 360 | UPPER(tmp2) = 0; 361 | LOWER(tmp2) = UPPER(products[0][3]); 362 | add128(&tmp, &tmp2, &third64); 363 | UPPER(tmp) = 0; 364 | LOWER(tmp) = LOWER(products[0][1]); 365 | UPPER(tmp2) = 0; 366 | LOWER(tmp2) = UPPER(products[0][2]); 367 | add128(&tmp, &tmp2, &second64); 368 | UPPER(tmp) = 0; 369 | LOWER(tmp) = LOWER(products[0][0]); 370 | UPPER(tmp2) = 0; 371 | LOWER(tmp2) = UPPER(products[0][1]); 372 | add128(&tmp, &tmp2, &first64); 373 | 374 | UPPER(tmp) = 0; 375 | LOWER(tmp) = LOWER(products[1][3]); 376 | add128(&tmp, &third64, &tmp2); 377 | copy128(&third64, &tmp2); 378 | UPPER(tmp) = 0; 379 | LOWER(tmp) = LOWER(products[1][2]); 380 | add128(&tmp, &second64, &tmp2); 381 | UPPER(tmp) = 0; 382 | LOWER(tmp) = UPPER(products[1][3]); 383 | add128(&tmp, &tmp2, &second64); 384 | UPPER(tmp) = 0; 385 | LOWER(tmp) = LOWER(products[1][1]); 386 | add128(&tmp, &first64, &tmp2); 387 | UPPER(tmp) = 0; 388 | LOWER(tmp) = UPPER(products[1][2]); 389 | add128(&tmp, &tmp2, &first64); 390 | 391 | UPPER(tmp) = 0; 392 | LOWER(tmp) = LOWER(products[2][3]); 393 | add128(&tmp, &second64, &tmp2); 394 | copy128(&second64, &tmp2); 395 | UPPER(tmp) = 0; 396 | LOWER(tmp) = LOWER(products[2][2]); 397 | add128(&tmp, &first64, &tmp2); 398 | UPPER(tmp) = 0; 399 | LOWER(tmp) = UPPER(products[2][3]); 400 | add128(&tmp, &tmp2, &first64); 401 | 402 | UPPER(tmp) = 0; 403 | LOWER(tmp) = LOWER(products[3][3]); 404 | add128(&tmp, &first64, &tmp2); 405 | copy128(&first64, &tmp2); 406 | 407 | clear256(&target1); 408 | shiftl128(&first64, 64, &UPPER(target1)); 409 | clear256(&target2); 410 | UPPER(UPPER(target2)) = UPPER(third64); 411 | shiftl128(&third64, 64, &LOWER(target2)); 412 | add256(&target1, &target2, target); 413 | clear256(&target1); 414 | copy128(&UPPER(target1), &second64); 415 | add256(&target1, target, &target2); 416 | clear256(&target1); 417 | copy128(&LOWER(target1), &fourth64); 418 | add256(&target1, &target2, target); 419 | } 420 | 421 | void divmod128(uint128_t *l, uint128_t *r, uint128_t *retDiv, 422 | uint128_t *retMod) { 423 | uint128_t copyd, adder, resDiv, resMod; 424 | uint128_t one; 425 | UPPER(one) = 0; 426 | LOWER(one) = 1; 427 | uint32_t diffBits = bits128(l) - bits128(r); 428 | clear128(&resDiv); 429 | copy128(&resMod, l); 430 | if (gt128(r, l)) { 431 | copy128(retMod, l); 432 | clear128(retDiv); 433 | } else { 434 | shiftl128(r, diffBits, ©d); 435 | shiftl128(&one, diffBits, &adder); 436 | if (gt128(©d, &resMod)) { 437 | shiftr128(©d, 1, ©d); 438 | shiftr128(&adder, 1, &adder); 439 | } 440 | while (gte128(&resMod, r)) { 441 | if (gte128(&resMod, ©d)) { 442 | minus128(&resMod, ©d, &resMod); 443 | or128(&resDiv, &adder, &resDiv); 444 | } 445 | shiftr128(©d, 1, ©d); 446 | shiftr128(&adder, 1, &adder); 447 | } 448 | copy128(retDiv, &resDiv); 449 | copy128(retMod, &resMod); 450 | } 451 | } 452 | 453 | void divmod256(uint256_t *l, uint256_t *r, uint256_t *retDiv, 454 | uint256_t *retMod) { 455 | uint256_t copyd, adder, resDiv, resMod; 456 | uint256_t one; 457 | clear256(&one); 458 | UPPER(LOWER(one)) = 0; 459 | LOWER(LOWER(one)) = 1; 460 | uint32_t diffBits = bits256(l) - bits256(r); 461 | clear256(&resDiv); 462 | copy256(&resMod, l); 463 | if (gt256(r, l)) { 464 | copy256(retMod, l); 465 | clear256(retDiv); 466 | } else { 467 | shiftl256(r, diffBits, ©d); 468 | shiftl256(&one, diffBits, &adder); 469 | if (gt256(©d, &resMod)) { 470 | shiftr256(©d, 1, ©d); 471 | shiftr256(&adder, 1, &adder); 472 | } 473 | while (gte256(&resMod, r)) { 474 | if (gte256(&resMod, ©d)) { 475 | minus256(&resMod, ©d, &resMod); 476 | or256(&resDiv, &adder, &resDiv); 477 | } 478 | shiftr256(©d, 1, ©d); 479 | shiftr256(&adder, 1, &adder); 480 | } 481 | copy256(retDiv, &resDiv); 482 | copy256(retMod, &resMod); 483 | } 484 | } 485 | 486 | static void reverseString(char *str, uint32_t length) { 487 | uint32_t i, j; 488 | for (i = 0, j = length - 1; i < j; i++, j--) { 489 | uint8_t c; 490 | c = str[i]; 491 | str[i] = str[j]; 492 | str[j] = c; 493 | } 494 | } 495 | 496 | bool tostring128(uint128_t *number, uint32_t baseParam, char *out, 497 | uint32_t outLength) { 498 | uint128_t rDiv; 499 | uint128_t rMod; 500 | uint128_t base; 501 | copy128(&rDiv, number); 502 | clear128(&rMod); 503 | clear128(&base); 504 | LOWER(base) = baseParam; 505 | uint32_t offset = 0; 506 | if ((baseParam < 2) || (baseParam > 16)) { 507 | return false; 508 | } 509 | do { 510 | if (offset > (outLength - 1)) { 511 | return false; 512 | } 513 | divmod128(&rDiv, &base, &rDiv, &rMod); 514 | out[offset++] = HEXDIGITS[(uint8_t)LOWER(rMod)]; 515 | } while (!zero128(&rDiv)); 516 | out[offset] = '\0'; 517 | reverseString(out, offset); 518 | return true; 519 | } 520 | 521 | bool tostring256(uint256_t *number, uint32_t baseParam, char *out, 522 | uint32_t outLength) { 523 | uint256_t rDiv; 524 | uint256_t rMod; 525 | uint256_t base; 526 | copy256(&rDiv, number); 527 | clear256(&rMod); 528 | clear256(&base); 529 | UPPER(LOWER(base)) = 0; 530 | LOWER(LOWER(base)) = baseParam; 531 | uint32_t offset = 0; 532 | if ((baseParam < 2) || (baseParam > 16)) { 533 | return false; 534 | } 535 | do { 536 | if (offset > (outLength - 1)) { 537 | return false; 538 | } 539 | divmod256(&rDiv, &base, &rDiv, &rMod); 540 | out[offset++] = HEXDIGITS[(uint8_t)LOWER(LOWER(rMod))]; 541 | } while (!zero256(&rDiv)); 542 | out[offset] = '\0'; 543 | reverseString(out, offset); 544 | return true; 545 | } 546 | -------------------------------------------------------------------------------- /src/uint256.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Ledger Blue 3 | * (c) 2016 Ledger 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | ********************************************************************************/ 17 | 18 | // Adapted from https://github.com/calccrypto/uint256_t 19 | 20 | #include 21 | #include 22 | 23 | typedef struct uint128_t { uint64_t elements[2]; } uint128_t; 24 | 25 | typedef struct uint256_t { uint128_t elements[2]; } uint256_t; 26 | 27 | #define UPPER_P(x) x->elements[0] 28 | #define LOWER_P(x) x->elements[1] 29 | #define UPPER(x) x.elements[0] 30 | #define LOWER(x) x.elements[1] 31 | 32 | void readu128BE(uint8_t *buffer, uint128_t *target); 33 | void readu256BE(uint8_t *buffer, uint256_t *target); 34 | bool zero128(uint128_t *number); 35 | bool zero256(uint256_t *number); 36 | void copy128(uint128_t *target, uint128_t *number); 37 | void copy256(uint256_t *target, uint256_t *number); 38 | void clear128(uint128_t *target); 39 | void clear256(uint256_t *target); 40 | void shiftl128(uint128_t *number, uint32_t value, uint128_t *target); 41 | void shiftr128(uint128_t *number, uint32_t value, uint128_t *target); 42 | void shiftl256(uint256_t *number, uint32_t value, uint256_t *target); 43 | void shiftr256(uint256_t *number, uint32_t value, uint256_t *target); 44 | uint32_t bits128(uint128_t *number); 45 | uint32_t bits256(uint256_t *number); 46 | bool equal128(uint128_t *number1, uint128_t *number2); 47 | bool equal256(uint256_t *number1, uint256_t *number2); 48 | bool gt128(uint128_t *number1, uint128_t *number2); 49 | bool gt256(uint256_t *number1, uint256_t *number2); 50 | bool gte128(uint128_t *number1, uint128_t *number2); 51 | bool gte256(uint256_t *number1, uint256_t *number2); 52 | void add128(uint128_t *number1, uint128_t *number2, uint128_t *target); 53 | void add256(uint256_t *number1, uint256_t *number2, uint256_t *target); 54 | void minus128(uint128_t *number1, uint128_t *number2, uint128_t *target); 55 | void minus256(uint256_t *number1, uint256_t *number2, uint256_t *target); 56 | void or128(uint128_t *number1, uint128_t *number2, uint128_t *target); 57 | void or256(uint256_t *number1, uint256_t *number2, uint256_t *target); 58 | void mul128(uint128_t *number1, uint128_t *number2, uint128_t *target); 59 | void mul256(uint256_t *number1, uint256_t *number2, uint256_t *target); 60 | void divmod128(uint128_t *l, uint128_t *r, uint128_t *div, uint128_t *mod); 61 | void divmod256(uint256_t *l, uint256_t *r, uint256_t *div, uint256_t *mod); 62 | bool tostring128(uint128_t *number, uint32_t base, char *out, 63 | uint32_t outLength); 64 | bool tostring256(uint256_t *number, uint32_t base, char *out, 65 | uint32_t outLength); 66 | --------------------------------------------------------------------------------