├── head.es6 ├── styles ├── main.footer.scss ├── _accountUnfunded.scss ├── _sendPane.scss ├── _dashboardOverviewBack.scss ├── _siteHeader.scss ├── _history.scss ├── _sendOutcome.scss ├── _sendWaiting.scss ├── _additionalSigners.scss ├── _dashboardOverviewRow.scss ├── _welcomeBox.scss ├── _loginBox.scss ├── _historyTable.scss ├── _sendConfirm.scss └── _sendSetup.scss ├── .gitignore ├── .dockerignore ├── images ├── info.png ├── monitor.png └── sending.gif ├── jenkins.bash ├── vendor ├── @ledgerhq │ ├── hw-transport │ │ ├── .flowconfig │ │ ├── package.json │ │ ├── lib-es │ │ │ ├── Transport.js │ │ │ └── Transport.js.flow │ │ ├── README.md │ │ ├── src │ │ │ └── Transport.js │ │ ├── lib │ │ │ └── Transport.js.flow │ │ └── LICENSE │ └── hw-transport-u2f │ │ ├── .flowconfig │ │ ├── package.json │ │ ├── README.md │ │ ├── lib-es │ │ ├── TransportU2F.js │ │ ├── TransportU2F.js.flow │ │ └── TransportU2F.js.map │ │ ├── src │ │ └── TransportU2F.js │ │ ├── lib │ │ ├── TransportU2F.js.flow │ │ ├── TransportU2F.js.map │ │ └── TransportU2F.js │ │ └── LICENSE └── interstellar-network │ ├── test │ ├── network.service.unit-test.es6 │ └── network.connection.unit-test.es6 │ ├── services │ ├── server.service.es6 │ └── account-observable.service.es6 │ ├── package.json │ └── index.es6 ├── errors.es6 ├── config.json ├── CONTRIBUTING.md ├── config-prd.json ├── Makefile ├── Dockerfile ├── metrics.es6 ├── controllers ├── header.controller.es6 ├── history-widget.controller.es6 ├── balance-widget.controller.es6 └── login.controller.es6 ├── index.html ├── known_accounts.es6 ├── package.json ├── README.md ├── main.es6 ├── templates ├── balance-widget.template.html ├── history-widget.template.html ├── dashboard.template.html ├── login.template.html └── send-widget.template.html └── LICENSE /head.es6: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /styles/main.footer.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /.tmp 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .tmp 3 | Dockerfile 4 | -------------------------------------------------------------------------------- /images/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stellar-deprecated/account-viewer/HEAD/images/info.png -------------------------------------------------------------------------------- /images/monitor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stellar-deprecated/account-viewer/HEAD/images/monitor.png -------------------------------------------------------------------------------- /images/sending.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stellar-deprecated/account-viewer/HEAD/images/sending.gif -------------------------------------------------------------------------------- /styles/_accountUnfunded.scss: -------------------------------------------------------------------------------- 1 | .accountUnfunded { 2 | } 3 | .accountUnfunded__alert { 4 | margin-top: 2em; 5 | margin-bottom: 0; 6 | } -------------------------------------------------------------------------------- /jenkins.bash: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | set -e 3 | 4 | npm install yarn 5 | ./node_modules/.bin/yarn install --ignore-engines 6 | ./node_modules/.bin/interstellar build --env=prd 7 | -------------------------------------------------------------------------------- /vendor/@ledgerhq/hw-transport/.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | /lib 3 | 4 | [include] 5 | 6 | [libs] 7 | flow-typed 8 | 9 | [lints] 10 | 11 | [options] 12 | 13 | [strict] 14 | -------------------------------------------------------------------------------- /vendor/@ledgerhq/hw-transport-u2f/.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | /lib 3 | 4 | [include] 5 | 6 | [libs] 7 | flow-typed 8 | 9 | [lints] 10 | 11 | [options] 12 | 13 | [strict] 14 | -------------------------------------------------------------------------------- /styles/_sendPane.scss: -------------------------------------------------------------------------------- 1 | .sendPane { 2 | padding: 2em 0; 3 | } 4 | .sendPane__title { 5 | margin-bottom: 1em; 6 | } 7 | .sendPane__submit { 8 | padding-left: 5em; 9 | padding-right: 5em; 10 | } 11 | -------------------------------------------------------------------------------- /styles/_dashboardOverviewBack.scss: -------------------------------------------------------------------------------- 1 | .dashboardOverviewBack { 2 | padding-top: 2em; 3 | padding-bottom: 2em; 4 | border-top: 1px solid $s-color-neutral7; 5 | border-bottom: 1px solid $s-color-neutral7; 6 | 7 | background: $s-color-neutral8; 8 | } 9 | -------------------------------------------------------------------------------- /errors.es6: -------------------------------------------------------------------------------- 1 | function BasicClientError(name, data) { 2 | this.name = name; 3 | this.data = data; 4 | } 5 | BasicClientError.prototype = Object.create(Error.prototype); 6 | BasicClientError.prototype.constructor = BasicClientError; 7 | 8 | export default BasicClientError; 9 | -------------------------------------------------------------------------------- /styles/_siteHeader.scss: -------------------------------------------------------------------------------- 1 | .siteHeader { 2 | justify-content: space-between; 3 | } 4 | .siteHeader__signout { 5 | @include S-flexItem-full; 6 | @include r-media(m) { 7 | @include S-flexItem-noFlex; 8 | } 9 | } 10 | .siteHeader__signout__link:hover { 11 | color: #7D00FF; 12 | } 13 | -------------------------------------------------------------------------------- /vendor/interstellar-network/test/network.service.unit-test.es6: -------------------------------------------------------------------------------- 1 | import "../index"; 2 | 3 | describe("stellard.Network", function() { 4 | injectNg("stellard", {network: "stellard.Network"}); 5 | 6 | it("is not null", function() { 7 | expect(this.network).to.be.an('object'); 8 | }); 9 | }); -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "inviteServer": "http://localhost:8002", 3 | "permanentSession": true, 4 | "modules": { 5 | "interstellar-network": { 6 | "networkPassphrase": "Test SDF Network ; September 2015", 7 | "horizon": "https://horizon-testnet.stellar.org" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /styles/_history.scss: -------------------------------------------------------------------------------- 1 | .history { 2 | padding-top: 2em; 3 | padding-bottom: 2em; 4 | border-top: 1px solid $s-color-neutral7; 5 | } 6 | .history__title { 7 | margin-bottom: .5em; 8 | } 9 | .history__table { 10 | overflow: auto; 11 | -webkit-overflow-scrolling: touch; 12 | } 13 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | Please read the [Contribution Guide](https://github.com/stellar/docs/blob/master/CONTRIBUTING.md). 4 | 5 | Then please [sign the Contributor License Agreement](https://docs.google.com/forms/d/1g7EF6PERciwn7zfmfke5Sir2n10yddGGSXyZsq98tVY/viewform?usp=send_form). 6 | 7 | -------------------------------------------------------------------------------- /config-prd.json: -------------------------------------------------------------------------------- 1 | { 2 | "inviteServer": "https://invite.stellar.org", 3 | "permanentSession": false, 4 | "modules": { 5 | "interstellar-network": { 6 | "networkPassphrase": "Public Global Stellar Network ; September 2015", 7 | "horizon": "https://horizon.stellar.org" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /styles/_sendOutcome.scss: -------------------------------------------------------------------------------- 1 | .sendOutcome { 2 | } 3 | .sendOutcome__label { 4 | text-align: center; 5 | font-size: $s-scale-3; 6 | } 7 | .sendOutcome__code { 8 | background: $s-color-neutral8; 9 | padding: 1em; 10 | } 11 | .sendOutcome__return { 12 | display: block; 13 | margin: 0 auto; 14 | } 15 | -------------------------------------------------------------------------------- /vendor/interstellar-network/services/server.service.es6: -------------------------------------------------------------------------------- 1 | import {Inject, Service} from 'interstellar-core' 2 | import {Server as LibServer} from 'stellar-sdk'; 3 | 4 | @Service('Server') 5 | @Inject('interstellar-core.Config') 6 | export default class Server { 7 | constructor(Config) { 8 | return new LibServer(Config.get('modules.interstellar-network.horizon')); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /styles/_sendWaiting.scss: -------------------------------------------------------------------------------- 1 | .sendWaiting { 2 | } 3 | .sendWaiting__rocket { 4 | padding: 3em 0; 5 | text-align: center; 6 | } 7 | .sendWaiting__status { 8 | text-align: center; 9 | font-size: $s-scale-3; 10 | } 11 | .sendWaiting__ledger { 12 | text-align: center; 13 | font-size: $s-scale-4; 14 | } 15 | 16 | .sendPane--pending { 17 | background-color: #fbfbfb; 18 | } 19 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Check if we need to prepend docker commands with sudo 2 | SUDO := $(shell docker version >/dev/null 2>&1 || echo "sudo") 3 | 4 | # If TAG is not provided set default value 5 | TAG ?= stellar/account-viewer:$(shell git rev-parse --short HEAD)$(and $(shell git status -s),-dirty-$(shell id -u -n)) 6 | 7 | docker-build: 8 | $(SUDO) docker build -t $(TAG) . 9 | 10 | docker-push: 11 | $(SUDO) docker push $(TAG) 12 | -------------------------------------------------------------------------------- /styles/_additionalSigners.scss: -------------------------------------------------------------------------------- 1 | .additionalSigners { 2 | } 3 | .additionalSigners__label { 4 | text-align: center; 5 | font-size: $s-scale-3; 6 | } 7 | .additionalSigners__xdr { 8 | background: $s-color-neutral8; 9 | padding: 1em; 10 | white-space: pre-wrap; 11 | word-wrap: break-word; 12 | overflow-wrap: break-word; 13 | } 14 | .additionalSigners__return { 15 | display: block; 16 | margin: 0 auto; 17 | } 18 | -------------------------------------------------------------------------------- /styles/_dashboardOverviewRow.scss: -------------------------------------------------------------------------------- 1 | .dashboardOverviewRow { 2 | margin-bottom: 1em; 3 | } 4 | .dashboardOverviewRow:last-child { 5 | margin-bottom: 0; 6 | } 7 | .dashboardOverviewRow__label { 8 | margin-bottom: 0; 9 | } 10 | .dashboardOverviewRow__text { 11 | font-size: $s-scale-2; 12 | word-wrap: break-word; 13 | word-break: break-word; 14 | margin-bottom: 0; 15 | @include r-media(m) { 16 | font-size: $s-scale-1; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:16.04 as build 2 | 3 | MAINTAINER SDF Ops Team 4 | 5 | ADD . /app/src 6 | 7 | WORKDIR /app/src 8 | 9 | RUN apt-get update && apt-get install -y curl git make build-essential && \ 10 | curl -sL https://deb.nodesource.com/setup_6.x | bash - && \ 11 | apt-get update && apt-get install -y nodejs && \ 12 | /app/src/jenkins.bash 13 | 14 | FROM nginx:1.17 15 | 16 | COPY --from=build /app/src/.tmp/webpacked /usr/share/nginx/html/ 17 | -------------------------------------------------------------------------------- /metrics.es6: -------------------------------------------------------------------------------- 1 | import Amplitude from 'amplitude-js' 2 | 3 | const instance = Amplitude.getInstance(); 4 | instance.init('c666c8ed260d8e90cc5ac3f242c2fcae', null, { 5 | trackingOptions: { 6 | ip_address: false 7 | } 8 | }) 9 | 10 | export function logEvent(type, properties = {}) { 11 | console.log(`[METRICS]: "${type}", ${JSON.stringify(properties)}`) 12 | // Override the IP so we don't collect it 13 | properties.ip = '' 14 | instance.logEvent(type, properties) 15 | } 16 | -------------------------------------------------------------------------------- /controllers/header.controller.es6: -------------------------------------------------------------------------------- 1 | import {Intent} from "interstellar-core"; 2 | import {Controller, Inject} from "interstellar-core"; 3 | import {isArray, sortBy} from "lodash"; 4 | 5 | @Controller("HeaderController") 6 | @Inject("interstellar-core.IntentBroadcast") 7 | export default class HeaderController { 8 | constructor(IntentBroadcast) { 9 | this.IntentBroadcast = IntentBroadcast; 10 | } 11 | 12 | logout() { 13 | this.IntentBroadcast.sendBroadcast(new Intent(Intent.TYPES.LOGOUT)); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Stellar Account Viewer 6 | 7 | 8 | 15 | 16 | 17 |
18 | Loading... 19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /vendor/interstellar-network/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "interstellar-network", 3 | "version": "0.0.12", 4 | "description": "The interstellar-network module provides communication layer between Interstellar application and Stellar network.", 5 | "keywords": [ 6 | "stellar", 7 | "interstellar" 8 | ], 9 | "scripts": { 10 | "test": "gulp test", 11 | "postversion": "git push && git push --tags" 12 | }, 13 | "author": "Stellar Development Foundation ", 14 | "license": "Apache-2.0", 15 | "repository": { 16 | "type": "git", 17 | "url": "http://github.com/stellar/interstellar-network.git" 18 | }, 19 | "dependencies": { 20 | "stellar-sdk": "*", 21 | "lodash": "^2.4.1", 22 | "interstellar-core": "~0.0.3", 23 | "bluebird": "^2.9.25" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /vendor/interstellar-network/index.es6: -------------------------------------------------------------------------------- 1 | import {Module, Intent} from "interstellar-core"; 2 | import interstellarSessions from "interstellar-sessions"; 3 | 4 | import {Networks} from "stellar-sdk"; 5 | export const NETWORK_PUBLIC = Networks.PUBLIC; 6 | export const NETWORK_TESTNET = Networks.TESTNET; 7 | 8 | const mod = new Module('interstellar-network'); 9 | export default mod; 10 | 11 | mod.services = require.context("./services", true); 12 | 13 | let addConfig = ConfigProvider => { 14 | ConfigProvider.addModuleConfig(mod.name, { 15 | networkPassphrase: NETWORK_TESTNET, 16 | horizon: { 17 | secure: true, 18 | hostname: "horizon-testnet.stellar.org", 19 | port: 443 20 | } 21 | }); 22 | }; 23 | addConfig.$inject = ['interstellar-core.ConfigProvider']; 24 | mod.config(addConfig); 25 | 26 | mod.define(); 27 | -------------------------------------------------------------------------------- /known_accounts.es6: -------------------------------------------------------------------------------- 1 | const knownAccounts = { 2 | 'GCGNWKCJ3KHRLPM3TM6N7D3W5YKDJFL6A2YCXFXNMRTZ4Q66MEMZ6FI2': { 3 | name: 'Poloniex', 4 | requiredMemoType: 'MEMO_ID' 5 | }, 6 | 'GA5XIGA5C7QTPTWXQHY6MCJRMTRZDOSHR6EFIBNDQTCQHG262N4GGKTM': { 7 | name: 'Kraken', 8 | requiredMemoType: 'MEMO_ID' 9 | }, 10 | 'GB6YPGW5JFMMP2QB2USQ33EUWTXVL4ZT5ITUNCY3YKVWOJPP57CANOF3': { 11 | name: 'Bittrex', 12 | requiredMemoType: 'MEMO_TEXT' 13 | }, 14 | 'GB7GRJ5DTE3AA2TCVHQS2LAD3D7NFG7YLTOEWEBVRNUUI2Q3TJ5UQIFM': { 15 | name: 'BTC38', 16 | requiredMemoType: 'MEMO_ID' 17 | }, 18 | 'GBV4ZDEPNQ2FKSPKGJP2YKDAIZWQ2XKRQD4V4ACH3TCTFY6KPY3OAVS7': { 19 | name: 'Changelly', 20 | requiredMemoType: 'MEMO_ID' 21 | }, 22 | 'GC4KAS6W2YCGJGLP633A6F6AKTCV4WSLMTMIQRSEQE5QRRVKSX7THV6S': { 23 | name: 'BitcoinIndonesia', 24 | requiredMemoType: 'MEMO_TEXT' 25 | }, 26 | 'GAHK7EEG2WWHVKDNT4CEQFZGKF2LGDSW2IVM4S5DP42RBW3K6BTODB4A': { 27 | name: 'Binance', 28 | requiredMemoType: 'MEMO_ID' 29 | } 30 | }; 31 | export default knownAccounts; 32 | -------------------------------------------------------------------------------- /styles/_welcomeBox.scss: -------------------------------------------------------------------------------- 1 | .welcomeBox { 2 | max-width: 40em; 3 | padding: 1.5em 2em 1em 2em; 4 | border: 1px solid $s-color-primary7; 5 | border-bottom: 0; 6 | background: $s-color-primary9; 7 | border-radius: $s-var-radius $s-var-radius 0 0; 8 | } 9 | .welcomeBox__title { 10 | font-weight: normal; 11 | margin-bottom: 1em; 12 | } 13 | 14 | .welcomeBox__content { 15 | margin-bottom: 0; 16 | } 17 | 18 | .middleBox { 19 | max-width: 40em; 20 | padding: 1.5em 2em 1em 2em; 21 | border-top: 1px solid $s-color-primary7; 22 | border-left: 1px solid $s-color-primary7; 23 | border-right: 1px solid $s-color-primary7; 24 | background: $s-color-primary9; 25 | } 26 | 27 | .keypair { 28 | border: 1px solid $s-color-primary7; 29 | background-color: white; 30 | padding: 5px; 31 | margin-bottom: 10px; 32 | overflow: auto; 33 | } 34 | 35 | .legacyBox { 36 | max-width: 40em; 37 | padding: 1.5em 2em 1em 2em; 38 | border: 1px solid $s-color-primary7; 39 | background: $s-color-primary9; 40 | border-radius: 0 0 $s-var-radius $s-var-radius; 41 | } 42 | -------------------------------------------------------------------------------- /styles/_loginBox.scss: -------------------------------------------------------------------------------- 1 | .loginBox { 2 | max-width: 40em; 3 | padding: 1.5em 2em 2em 2em; 4 | border: 1px solid $s-color-primary7; 5 | border-bottom: 0; 6 | background: $s-color-primary9; 7 | } 8 | .loginBox__label { 9 | margin-bottom: 0.25em; 10 | } 11 | .loginBox__info { 12 | font-size: 0.85em; 13 | margin-top: 0.25em; 14 | margin-bottom: 0.25em; 15 | } 16 | .loginBox__ledgerAccount { 17 | font-size: 0.85em; 18 | margin-top: 0.25em; 19 | margin-bottom: 0.50em; 20 | height: 1.5em; 21 | } 22 | .loginBox__ledgerAccount input[type=checkbox] { 23 | float: left; 24 | margin-right: 0.50em; 25 | margin-top: 0.25em; 26 | } 27 | .loginBox__ledgerAccount input[type=text] { 28 | width: 8em; 29 | height: 1.5em; 30 | margin-left: 1.5em; 31 | margin-bottom: 0em; 32 | padding: 0.5em; 33 | } 34 | .loginBox__input { 35 | display: block; 36 | width: 100%; 37 | margin-bottom: 1em; 38 | } 39 | .loginBox__submit { 40 | padding-left: 2em; 41 | padding-right: 2em; 42 | @include r-media(s) { 43 | padding-left: 5em; 44 | padding-right: 5em; 45 | } 46 | } 47 | .loginBox__warning { 48 | color: $s-color-alert; 49 | } 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "account-viewer", 3 | "version": "0.0.1", 4 | "private": true, 5 | "description": "A simple account-viewer", 6 | "scripts": { 7 | "start": "interstellar develop", 8 | "build": "interstellar build --env=prd" 9 | }, 10 | "author": "Stellar Development Foundation ", 11 | "license": "Apache-2.0", 12 | "repository": { 13 | "type": "git", 14 | "url": "http://github.com/stellar/account-viewer.git" 15 | }, 16 | "dependencies": { 17 | "@ledgerhq/errors": "^5.9.0", 18 | "@ledgerhq/hw-app-str": "^5.9.0", 19 | "@ledgerhq/logs": "^5.9.0", 20 | "amplitude-js": "^5.2.2", 21 | "babel-runtime": "^6.26.0", 22 | "bignumber.js": "^2.0.8", 23 | "interstellar": "https://github.com/stellar/interstellar.git#yarn", 24 | "interstellar-core": "~0.0.4", 25 | "interstellar-network": "./vendor/interstellar-network", 26 | "interstellar-sessions": "~0.0.10", 27 | "interstellar-ui-messages": "~0.0.3", 28 | "lodash": "^4.17.13", 29 | "regenerator-runtime": "^0.13.3", 30 | "stellar-sdk": "^5.0.0", 31 | "u2f-api": "^1.1.1" 32 | }, 33 | "devDependencies": { 34 | "gulp": "^3.9.0" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /vendor/@ledgerhq/hw-transport/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ledgerhq/hw-transport", 3 | "version": "5.9.0", 4 | "description": "Ledger Hardware Wallet common interface of the communication layer", 5 | "keywords": [ 6 | "Ledger", 7 | "LedgerWallet", 8 | "NanoS", 9 | "Blue", 10 | "Hardware Wallet" 11 | ], 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/LedgerHQ/ledgerjs" 15 | }, 16 | "bugs": { 17 | "url": "https://github.com/LedgerHQ/ledgerjs/issues" 18 | }, 19 | "homepage": "https://github.com/LedgerHQ/ledgerjs", 20 | "publishConfig": { 21 | "access": "public" 22 | }, 23 | "main": "lib/Transport.js", 24 | "module": "lib-es/Transport.js", 25 | "license": "Apache-2.0", 26 | "dependencies": { 27 | "@ledgerhq/devices": "^5.9.0", 28 | "@ledgerhq/errors": "^5.9.0", 29 | "events": "^3.1.0" 30 | }, 31 | "devDependencies": { 32 | "flow-bin": "^0.118.0" 33 | }, 34 | "scripts": { 35 | "flow": "flow", 36 | "clean": "bash ../../script/clean.sh", 37 | "build": "bash ../../script/build.sh", 38 | "watch": "bash ../../script/watch.sh", 39 | "doc": "bash ../../script/doc.sh" 40 | }, 41 | "gitHead": "210ff622ec320ef3fb7bde29dad343306beae3e7" 42 | } 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # account-viewer 2 | 3 | A simple tool to view an account on the Stellar network and make transactions 4 | from it. 5 | 6 | ## Developing 7 | 8 | Because of the build tools used, this requires node 6 to build without errors. 9 | The best way to handle this is with `nvm` 10 | ([install instructions](https://github.com/nvm-sh/nvm#installation-and-update)). 11 | Since node 6 is end-of-life, it's best to install a modern LTS as the default 12 | and only use 6 when necessary. 13 | 14 | ```sh 15 | # The first version installed becomes the default 16 | nvm install 10 17 | nvm install 6 18 | nvm use 6 19 | ``` 20 | 21 | Once you have that configured, install deps and then start 22 | the project. Sass must be compiled targeting a specific node version, so if 23 | you've installed deps while running a node version other than 6, you'll need to 24 | `rm -rf node_modules`. 25 | 26 | We use yarn for dependencies. 27 | 28 | `yarn` 29 | 30 | To start the app in development mode, which will watch for changes to files, rebuild, and reload the site automatically, run the start script. 31 | 32 | `yarn start` 33 | 34 | ## Building for production 35 | 36 | The production build produces static files that expect to be served from an `/account-viewer/` base name. 37 | 38 | `yarn build` 39 | -------------------------------------------------------------------------------- /vendor/@ledgerhq/hw-transport-u2f/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ledgerhq/hw-transport-u2f", 3 | "version": "5.9.0", 4 | "description": "Ledger Hardware Wallet Web implementation of the communication layer, using U2F api", 5 | "keywords": [ 6 | "Ledger", 7 | "LedgerWallet", 8 | "U2F", 9 | "browser", 10 | "web", 11 | "NanoS", 12 | "Blue", 13 | "Hardware Wallet" 14 | ], 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/LedgerHQ/ledgerjs" 18 | }, 19 | "bugs": { 20 | "url": "https://github.com/LedgerHQ/ledgerjs/issues" 21 | }, 22 | "homepage": "https://github.com/LedgerHQ/ledgerjs", 23 | "publishConfig": { 24 | "access": "public" 25 | }, 26 | "main": "lib/TransportU2F.js", 27 | "module": "lib-es/TransportU2F.js", 28 | "license": "Apache-2.0", 29 | "dependencies": { 30 | "@ledgerhq/errors": "^5.9.0", 31 | "@ledgerhq/hw-transport": "^5.9.0", 32 | "@ledgerhq/logs": "^5.9.0", 33 | "u2f-api": "0.2.7" 34 | }, 35 | "devDependencies": { 36 | "flow-bin": "^0.118.0" 37 | }, 38 | "scripts": { 39 | "flow": "flow", 40 | "clean": "bash ../../script/clean.sh", 41 | "build": "bash ../../script/build.sh", 42 | "watch": "bash ../../script/watch.sh", 43 | "doc": "bash ../../script/doc.sh" 44 | }, 45 | "gitHead": "210ff622ec320ef3fb7bde29dad343306beae3e7" 46 | } 47 | -------------------------------------------------------------------------------- /styles/_historyTable.scss: -------------------------------------------------------------------------------- 1 | .historyTable { 2 | min-width: 42em; 3 | } 4 | 5 | .historyTable__row--header { 6 | border-bottom: 2px solid $s-color-neutral6; 7 | } 8 | .historyTable__row--data:nth-child(odd) { 9 | background: #fafafa; 10 | } 11 | .historyTable__row--data:hover { 12 | background: $s-color-neutral7; 13 | } 14 | 15 | .historyTable__cell { 16 | padding: 0.5em 0.5em; 17 | } 18 | @include r-media(xl) { 19 | .historyTable__cell { 20 | padding: 0.5em 1em; 21 | } 22 | } 23 | 24 | .historyTable__date { 25 | // white-space: nowrap; 26 | } 27 | .historyTable__accountID { 28 | word-break: break-all; 29 | } 30 | .historyTable__amount { 31 | text-align: right; 32 | } 33 | .historyTable__amount__number { 34 | white-space: nowrap; 35 | } 36 | .historyTable__amount__number--debit { 37 | color: $s-color-alert; 38 | } 39 | .historyTable__amount__code { 40 | background: rgba(125, 0, 255, .16); 41 | border-radius: $s-var-radius; 42 | white-space: nowrap; 43 | } 44 | .historyTable__memo { 45 | 46 | } 47 | .historyTable__memo__data { 48 | background: rgba(125, 0, 255, .16); 49 | border-radius: $s-var-radius; 50 | border-radius: $s-var-radius; 51 | word-break: break-all; 52 | } 53 | .historyTable__details { 54 | white-space: nowrap; 55 | } 56 | -------------------------------------------------------------------------------- /styles/_sendConfirm.scss: -------------------------------------------------------------------------------- 1 | .sendConfirm { 2 | } 3 | .sendConfirm__addressLabel { 4 | text-align: center; 5 | font-size: $s-scale-5; 6 | margin-bottom: 0em; 7 | } 8 | .sendConfirm__address { 9 | padding: 0 2em; 10 | font-size: $s-scale-3; 11 | text-align: center; 12 | word-break: break-all; 13 | font-weight: bold; 14 | } 15 | .sendConfirm__sendingContainer { 16 | @include S-flex-row; 17 | background: $s-color-neutral8; 18 | margin-bottom: 0.25em; 19 | } 20 | .sendConfirm__sendingContainer__label { 21 | @include S-flex-row; 22 | @include S-flexItem-1of3; 23 | align-items: center; 24 | padding-left: 2em; 25 | } 26 | .sendConfirm__sendingContainer__amount { 27 | @include S-flexItem-share; 28 | padding: 1.5em 1em; 29 | } 30 | .sendConfirm__sendingContainer__amount__info { 31 | font-size: $s-scale-1; 32 | font-weight: bold; 33 | line-height: 1; 34 | word-wrap: break-word; 35 | word-break: break-word; 36 | } 37 | .sendConfirm__sendingContainer__amount__helper { 38 | font-size: $s-scale-4; 39 | } 40 | 41 | .sendConfirm__submit { 42 | width: 100%; 43 | font-size: $s-scale-2; 44 | margin-top: 1em; 45 | margin-bottom: 1em; 46 | } 47 | .sendConfirm__edit { 48 | display: block; 49 | text-align: center; 50 | } 51 | -------------------------------------------------------------------------------- /vendor/interstellar-network/test/network.connection.unit-test.es6: -------------------------------------------------------------------------------- 1 | import { NetworkConnection } from "../lib/network-connection"; 2 | 3 | describe("NetworkConnection", function() { 4 | dontUseAngularQ(); 5 | 6 | injectNg("stellard", { 7 | network: "stellard.Network", 8 | config: "core.Config", 9 | q: "$q", 10 | }); 11 | 12 | lazy("connectionSpec", function() { 13 | return this.config.get("stellard/connections/live"); 14 | }); 15 | 16 | subject(function() { 17 | return new NetworkConnection(this.q, this.network, "live", this.connectionSpec); 18 | }); 19 | 20 | afterEach(function() { 21 | this.subject.disconnect(); 22 | }); 23 | 24 | describe("#ensureConnected", function() { 25 | 26 | context("when the remote websocket is successfully connected and the stellard returns an online status", function() { 27 | setupMockSocket("online"); 28 | 29 | it("resolves the returns promise", function(done) { 30 | this.subject.ensureConnected() 31 | .then(nc => { done(); }) 32 | .catch(done) 33 | ; 34 | }); 35 | 36 | }); 37 | 38 | context("when the remote websocket closes immediately", function() { 39 | setupMockSocket("immediate-close"); 40 | this.timeout(5000); 41 | 42 | it("never resolves", function(done) { 43 | // below is a crude race... if the subject resolved before 2 seconds pass. 44 | // we fail 45 | setTimeout(done, 2000); 46 | this.subject.ensureConnected() 47 | .then(nc => { done(new Error("did not expect resolve")); }); 48 | }); 49 | }); 50 | 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /main.es6: -------------------------------------------------------------------------------- 1 | require('./styles/main.header.scss'); 2 | require('./styles/main.footer.scss'); 3 | 4 | import "regenerator-runtime/runtime"; 5 | import interstellarCore, {App, Intent} from "interstellar-core"; 6 | import interstellarNetwork from "interstellar-network"; 7 | import interstellarSessions from "interstellar-sessions"; 8 | import interstellarUiMessages from "interstellar-ui-messages"; 9 | 10 | import { logEvent } from './metrics.es6' 11 | 12 | logEvent('page: account viewer loaded') 13 | 14 | let config; 15 | if (INTERSTELLAR_ENV === 'prd') { 16 | config = require('./config-prd.json'); 17 | } else { 18 | config = require('./config.json'); 19 | } 20 | const app = new App("interstellar-basic-client", config); 21 | 22 | app.use(interstellarCore); 23 | app.use(interstellarNetwork); 24 | app.use(interstellarSessions); 25 | app.use(interstellarUiMessages); 26 | 27 | app.templates = require.context("raw!./templates", true); 28 | app.controllers = require.context("./controllers", true); 29 | 30 | app.routes = ($stateProvider) => { 31 | $stateProvider.state('login', { 32 | url: "/", 33 | templateUrl: "interstellar-basic-client/login" 34 | }); 35 | $stateProvider.state('dashboard', { 36 | url: "/dashboard", 37 | templateUrl: "interstellar-basic-client/dashboard", 38 | requireSession: true 39 | }); 40 | }; 41 | 42 | // Register BroadcastReceivers 43 | let registerBroadcastReceivers = ($state, IntentBroadcast) => { 44 | IntentBroadcast.registerReceiver(Intent.TYPES.SHOW_DASHBOARD, intent => { 45 | $state.go('dashboard'); 46 | }); 47 | 48 | IntentBroadcast.registerReceiver(Intent.TYPES.LOGOUT, intent => { 49 | $state.go('login'); 50 | }); 51 | }; 52 | registerBroadcastReceivers.$inject = ["$state", "interstellar-core.IntentBroadcast"]; 53 | app.run(registerBroadcastReceivers); 54 | 55 | let goToMainStateWithoutSession = ($state, $rootScope, Sessions) => { 56 | $rootScope.$on('$stateChangeStart', (event, toState, toParams, fromState, fromParams) => { 57 | let hasDefault = Sessions.hasDefault(); 58 | if (toState.requireSession && !hasDefault) { 59 | event.preventDefault(); 60 | $state.go('login'); 61 | } 62 | }) 63 | }; 64 | 65 | goToMainStateWithoutSession.$inject = ["$state", "$rootScope", "interstellar-sessions.Sessions"]; 66 | app.run(goToMainStateWithoutSession); 67 | 68 | app.bootstrap(); 69 | -------------------------------------------------------------------------------- /vendor/@ledgerhq/hw-transport-u2f/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | [Github](https://github.com/LedgerHQ/ledgerjs/), 4 | [Ledger Devs Slack](https://ledger-dev.slack.com/) 5 | 6 | ## @ledgerhq/hw-transport-u2f 7 | 8 | Allows to communicate with Ledger Hardware Wallets. 9 | 10 | **[Web]** **(U2F)** (legacy but reliable) – FIDO U2F api. [check browser support](https://caniuse.com/u2f). 11 | 12 | ## API 13 | 14 | 15 | 16 | #### Table of Contents 17 | 18 | - [TransportU2F](#transportu2f) 19 | - [Examples](#examples) 20 | - [exchange](#exchange) 21 | - [Parameters](#parameters) 22 | - [setScrambleKey](#setscramblekey) 23 | - [Parameters](#parameters-1) 24 | - [setUnwrap](#setunwrap) 25 | - [Parameters](#parameters-2) 26 | - [open](#open) 27 | - [Parameters](#parameters-3) 28 | 29 | ### TransportU2F 30 | 31 | **Extends Transport** 32 | 33 | U2F web Transport implementation 34 | 35 | #### Examples 36 | 37 | ```javascript 38 | import TransportU2F from "@ledgerhq/hw-transport-u2f"; 39 | ... 40 | TransportU2F.create().then(transport => ...) 41 | ``` 42 | 43 | #### exchange 44 | 45 | Exchange with the device using APDU protocol. 46 | 47 | ##### Parameters 48 | 49 | - `apdu` **[Buffer](https://nodejs.org/api/buffer.html)** 50 | 51 | Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Buffer](https://nodejs.org/api/buffer.html)>** a promise of apdu response 52 | 53 | #### setScrambleKey 54 | 55 | ##### Parameters 56 | 57 | - `scrambleKey` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** 58 | 59 | #### setUnwrap 60 | 61 | ##### Parameters 62 | 63 | - `unwrap` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** 64 | 65 | #### open 66 | 67 | static function to create a new Transport from a connected Ledger device discoverable via U2F (browser support) 68 | 69 | ##### Parameters 70 | 71 | - `_` **any** 72 | - `_openTimeout` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** (optional, default `5000`) 73 | 74 | Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[TransportU2F](#transportu2f)>** 75 | -------------------------------------------------------------------------------- /templates/balance-widget.template.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Your balance:

5 |

{{widget.balance-widget.balance%1 | number}}.{{widget.balance%1 | number : 7 | limitTo : -7}} lumens

6 |

7 | 8 |

9 |
10 |
11 |

Your Stellar public key:

12 |

{{widget.address}}  

13 |
14 |
15 | 16 |
17 |
18 |
19 |
20 | This account is currently inactive. To activate it, send at least 1 lumen (XLM) to the Stellar public key displayed above. 21 |
22 |
23 |
24 |
25 | 26 |
27 |
28 |
29 |
30 |

You haven't yet made a transaction with this account. You have {{widget.invite.days}} left to do so.

31 |

Send ANY numbers of Lumens to ANY public Stellar address using the form below to permanently claim this account.

32 |
33 | 34 |
35 | This account didn't have a transactions within 7 days of its creation, so its lumens were released. The current balance is 0 lumens. However you are able to claim an invite again, ask a friend for another invite to claim lumens. 36 |
37 |
38 |
39 |
40 |
41 | -------------------------------------------------------------------------------- /vendor/interstellar-network/services/account-observable.service.es6: -------------------------------------------------------------------------------- 1 | import {Inject, Service} from 'interstellar-core'; 2 | import {contains, cloneDeep} from 'lodash'; 3 | import Promise from 'bluebird'; 4 | 5 | @Service('AccountObservable') 6 | @Inject('interstellar-network.Server') 7 | export default class AccountObservable { 8 | constructor(Server) { 9 | this.Server = Server; 10 | this.streamingAddresses = []; 11 | this.paymentListeners = {}; 12 | this.balanceChangeListeners = {}; 13 | this.balances = {}; 14 | } 15 | 16 | _setupStreaming(address) { 17 | if (!contains(this.streamingAddresses, address)) { 18 | this.streamingAddresses.push(address); 19 | this.paymentListeners[address] = []; 20 | this.balanceChangeListeners[address] = []; 21 | 22 | this.Server.payments() 23 | .forAccount(address) 24 | .stream({ 25 | onmessage: payment => this._onPayment.call(this, address, payment) 26 | }); 27 | } 28 | } 29 | 30 | getPayments(address) { 31 | return this.Server.payments() 32 | .forAccount(address) 33 | .order('desc') 34 | .call() 35 | .then(payments => { 36 | return cloneDeep(payments); 37 | }); 38 | } 39 | 40 | getBalances(address) { 41 | if (this.balances[address]) { 42 | return Promise.resolve(cloneDeep(this.balances[address])); 43 | } else { 44 | return this._getBalances(address) 45 | .then(balances => { 46 | this.balances[address] = balances; 47 | return cloneDeep(balances); 48 | }); 49 | } 50 | } 51 | 52 | registerPaymentListener(address, listener) { 53 | this._setupStreaming(address); 54 | this.paymentListeners[address].push(listener); 55 | } 56 | 57 | registerBalanceChangeListener(address, listener) { 58 | this._setupStreaming(address); 59 | this.balanceChangeListeners[address].push(listener); 60 | } 61 | 62 | _getBalances(address) { 63 | return this.Server.accounts() 64 | .address(address) 65 | .call() 66 | .then(account => Promise.resolve(account.balances)) 67 | .catch(e => { 68 | if (e.name === 'NotFoundError') { 69 | return []; 70 | } else { 71 | throw e; 72 | } 73 | }); 74 | } 75 | 76 | _onPayment(address, payment) { 77 | if (this.paymentListeners[address]) { 78 | for (var listener of this.paymentListeners[address]) { 79 | listener(cloneDeep(payment)); 80 | } 81 | } 82 | 83 | if (this.balanceChangeListeners[address]) { 84 | this._getBalances(address) 85 | .then(balances => { 86 | this.balances[address] = balances; 87 | for (var listener of this.balanceChangeListeners[address]) { 88 | listener(cloneDeep(balances)); 89 | } 90 | }); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /templates/history-widget.template.html: -------------------------------------------------------------------------------- 1 |
2 |

Transaction History

3 | 4 |

5 | This tool shows up to 100 most recent payments. To view more, use the Horizon API. 6 |

7 | 8 |
9 |
10 |

11 | 12 | This tool is hiding payments smaller than {{widget.minimumAmountToDisplay}} XLM.
13 | 14 |
15 |

16 | 17 |

18 | 19 | This tool is showing all payments, including payments smaller than {{widget.minimumAmountToDisplay}} XLM.
20 | 21 |
22 |

23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 43 | 50 | 51 | 52 |
Account IDAmountMemoOperation ID
{{payment.display_address}} 36 | {{payment.display_amount}} 41 | {{payment.display_asset_code}} 42 | 44 | 45 | MEMO_{{payment.memo_type|uppercase}}
46 | {{payment.memo}} 47 |
48 | Loading... 49 |
{{payment.id}}
53 |
54 | 55 |
56 |
Loading...
57 |
No payments yet...
58 |
59 |
60 | -------------------------------------------------------------------------------- /templates/dashboard.template.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 11 |
12 |
13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |
21 | 22 | 41 | -------------------------------------------------------------------------------- /styles/_sendSetup.scss: -------------------------------------------------------------------------------- 1 | .sendSetup { 2 | } 3 | .sendSetup__titleBar { 4 | margin: 0; 5 | color: $s-color-neutral3; 6 | } 7 | .sendSetup__titleBar__side { 8 | float: right; 9 | } 10 | 11 | .network_congestion_normal { 12 | color: green; 13 | font-weight: bold; 14 | } 15 | 16 | .network_congestion_medium { 17 | color: orange; 18 | font-weight: bold; 19 | } 20 | 21 | .network_congestion_high { 22 | color: red; 23 | font-weight: bold; 24 | } 25 | 26 | .sendSetup__destination { 27 | margin-bottom: 1em; 28 | } 29 | .sendSetup__destination__input { 30 | width: 100%; 31 | } 32 | 33 | .sendSetup__amount { 34 | @include S-flex-row; 35 | margin-bottom: 1em; 36 | } 37 | .sendSetup__amount__input { 38 | @include S-flexItem-share; 39 | border-top-right-radius: 0; 40 | border-bottom-right-radius: 0; 41 | @include r-media(m) { 42 | font-size: $s-scale-2; 43 | } 44 | } 45 | .sendSetup__amount__tag { 46 | display: flex; 47 | align-items: center; 48 | 49 | @include S-flexItem-noFlex; 50 | border: 1px solid $s-color-neutral6; 51 | border-left: none; 52 | 53 | border-top-left-radius: 0; 54 | border-bottom-left-radius: 0; 55 | padding: 0 1em; 56 | background: $s-color-neutral8; 57 | } 58 | .sendSetup__error, .sendSetup__info { 59 | margin-top: -0.5em; 60 | margin-bottom: 1em; 61 | } 62 | 63 | .sendSetup__fee { 64 | @include S-flex-row; 65 | } 66 | .sendSetup__fee__input { 67 | @include S-flexItem-share; 68 | border-top-right-radius: 0; 69 | border-bottom-right-radius: 0; 70 | @include r-media(m) { 71 | font-size: $s-scale-4; 72 | } 73 | } 74 | .sendSetup__fee__tag { 75 | display: flex; 76 | align-items: center; 77 | 78 | @include S-flexItem-noFlex; 79 | border: 1px solid $s-color-neutral6; 80 | border-left: none; 81 | 82 | @include r-media(m) { 83 | font-size: $s-scale-4; 84 | } 85 | 86 | border-top-left-radius: 0; 87 | border-bottom-left-radius: 0; 88 | padding: 0 1em; 89 | background: $s-color-neutral8; 90 | } 91 | 92 | .sendSetup__submit { 93 | width: 100%; 94 | margin-top: 1em; 95 | 96 | @include r-media(s) { 97 | width: auto; 98 | padding-left: 4em; 99 | padding-right: 4em; 100 | } 101 | } 102 | 103 | .sendSetup__memo { 104 | margin-bottom: 1em; 105 | } 106 | .sendSetup__memo__memoSetup { 107 | @include S-flex-row; 108 | } 109 | .sendSetup__memo__memoSetup__dropdown { 110 | margin-bottom: 0.5em; 111 | @include r-media(m) { 112 | margin-bottom: 0; 113 | margin-right: 0.5em; 114 | } 115 | } 116 | .sendSetup__memo__memoSetup__memoLabel__help { 117 | margin-left: 0.5em; 118 | color: $s-color-neutral5; 119 | text-decoration: none; 120 | line-height: 1; 121 | border-bottom: 1px dotted currentColor; 122 | } 123 | .sendSetup__memo__memoSetup__selectTag { 124 | @include S-flexItem-noFlex; 125 | @include S-flex-row; 126 | border: 0; 127 | border-radius: $s-var-radius 0 0 $s-var-radius; 128 | align-self: stretch; 129 | } 130 | .sendSetup__memo__memoSetup__selectTag__dropdown { 131 | border: 0; 132 | } 133 | .sendSetup__memo__memoSetup__selectTag__select { 134 | @include S-flexItem-share; 135 | align-self: stretch; 136 | border: 0; 137 | background: transparent; // Transparent so that the parent color can show 138 | } 139 | .sendSetup__memo__memoSetup__input { 140 | @include S-flexItem-full; 141 | border-radius: $s-var-radius; 142 | @include r-media(m) { 143 | @include S-flexItem-share; 144 | } 145 | } 146 | 147 | .sendSetup__memo__memoSetup__selectTag.is-disabled, 148 | .sendSetup__memo__memoSetup__input.is-disabled { 149 | background: $s-color-neutral8; 150 | } 151 | .sendSetup__memo__memoSetup__selectTag__select.is-disabled, 152 | .sendSetup__memo__memoSetup__input.is-disabled { 153 | cursor: not-allowed; 154 | } 155 | -------------------------------------------------------------------------------- /controllers/history-widget.controller.es6: -------------------------------------------------------------------------------- 1 | import {Widget, Inject} from 'interstellar-core'; 2 | import BigNumber from 'bignumber.js'; 3 | import _ from 'lodash'; 4 | 5 | @Widget('history', 'HistoryWidgetController', 'interstellar-basic-client/history-widget') 6 | @Inject("$scope", "interstellar-sessions.Sessions", "interstellar-network.Server") 7 | export default class HistoryWidgetController { 8 | constructor($scope, Sessions, Server) { 9 | if (!Sessions.hasDefault()) { 10 | console.error('No session. This widget should be used with active session.'); 11 | return; 12 | } 13 | 14 | this.$scope = $scope; 15 | this.Server = Server; 16 | let session = Sessions.default; 17 | this.address = session.getAddress(); 18 | this.payments = []; 19 | this.loading = true; 20 | this.showLengthLimitAlert = false; 21 | this.minimumAmountToDisplay = 0.5; 22 | this.hideSmallAmounts = true; 23 | 24 | this.loadPayments(); 25 | } 26 | 27 | visiblePayments() { 28 | if (this.hideSmallAmounts) { 29 | return this.filteredPayments(); 30 | } else { 31 | return this.payments; 32 | } 33 | } 34 | 35 | filteredPayments() { 36 | return this.payments.filter(payment => { 37 | if (payment.type === 'account_merge') { 38 | return true; 39 | } 40 | 41 | if (payment.from === this.address) { 42 | return true; 43 | } 44 | 45 | return new BigNumber(payment.amount).gte(this.minimumAmountToDisplay); 46 | }); 47 | } 48 | 49 | toggleDisplaySmallAmounts() { 50 | this.hideSmallAmounts = !this.hideSmallAmounts; 51 | } 52 | 53 | showSmallAmountsToggle() { 54 | return this.filteredPayments().length !== this.payments.length; 55 | } 56 | 57 | loadPayments() { 58 | return this.Server.payments() 59 | .forAccount(this.address) 60 | .order('desc') 61 | .limit(100) 62 | .call() 63 | .then(payments => { 64 | this.payments = _.map(payments.records, payment => { 65 | return this._transformPaymentFields(payment); 66 | }); 67 | 68 | if (this.payments.length >= 100) { 69 | this.showLengthLimitAlert = true; 70 | } 71 | 72 | this.setupStreaming(); 73 | }) 74 | .catch(e => { 75 | if (e.name === 'NotFoundError') { 76 | setTimeout(() => { 77 | this.loadPayments(); 78 | }, 10*1000); 79 | } else { 80 | throw e; 81 | } 82 | }) 83 | .finally(() => { 84 | this.loading = false; 85 | this.$scope.$apply(); 86 | }); 87 | } 88 | 89 | setupStreaming() { 90 | // Setup event stream 91 | let cursor; 92 | if (this.payments.length > 0) { 93 | cursor = this.payments[0].paging_token; 94 | } else { 95 | cursor = 'now'; 96 | } 97 | 98 | this.Server.payments() 99 | .forAccount(this.address) 100 | .cursor(cursor) 101 | .stream({ 102 | onmessage: payment => this.onNewPayment.call(this, payment) 103 | }); 104 | } 105 | 106 | onNewPayment(payment) { 107 | this.payments.unshift(this._transformPaymentFields(payment)); 108 | } 109 | 110 | _transformPaymentFields(payment) { 111 | if (payment.type === 'create_account') { 112 | payment.from = payment.funder; 113 | payment.to = payment.account; 114 | payment.amount = payment.starting_balance; 115 | } 116 | 117 | if (payment.type === 'account_merge') { 118 | payment.direction = (payment.account === this.address) ? 'out' : 'in'; 119 | payment.display_address = (payment.account === this.address) ? payment.into : payment.account; 120 | let sign = payment.direction === 'in' ? '+' : '-'; 121 | payment.display_amount = '[account merge]'; 122 | } else { 123 | payment.direction = (payment.from === this.address) ? 'out' : 'in'; 124 | payment.display_address = (payment.from === this.address) ? payment.to : payment.from; 125 | let sign = payment.direction === 'in' ? '+' : '-'; 126 | let formattedAmount = new BigNumber(payment.amount).toFormat(); 127 | payment.display_amount = `${sign}${formattedAmount}`; 128 | 129 | if (payment.asset_code) { 130 | payment.display_asset_code = payment.asset_code; 131 | } else { 132 | payment.display_asset_code = 'XLM'; 133 | } 134 | } 135 | 136 | payment.link = payment._links.self.href; 137 | 138 | this._getMemoForPayment(payment) 139 | .then(memoObj => { 140 | payment.memo_type = memoObj.memoType; 141 | payment.memo = memoObj.memo; 142 | }); 143 | 144 | return payment; 145 | } 146 | 147 | _getMemoForPayment(payment) { 148 | return payment.transaction() 149 | .then(transaction => { 150 | let memoType = transaction.memo_type; 151 | let memo = transaction.memo; 152 | return {memoType, memo}; 153 | }); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /controllers/balance-widget.controller.es6: -------------------------------------------------------------------------------- 1 | import {Widget, Inject} from 'interstellar-core'; 2 | import BigNumber from 'bignumber.js'; 3 | import _ from 'lodash'; 4 | import LedgerTransport from '../vendor/@ledgerhq/hw-transport-u2f'; 5 | import LedgerStr from '@ledgerhq/hw-app-str'; 6 | 7 | @Widget('balance', 'BalanceWidgetController', 'interstellar-basic-client/balance-widget') 8 | @Inject("$scope", "$rootScope", "$http", "interstellar-core.Config", "interstellar-sessions.Sessions", "interstellar-network.Server") 9 | export default class BalanceWidgetController { 10 | constructor($scope, $rootScope, $http, Config, Sessions, Server) { 11 | if (!Sessions.hasDefault()) { 12 | console.error('No session. This widget should be used with active session.'); 13 | return; 14 | } 15 | 16 | this.$scope = $scope; 17 | this.$rootScope = $rootScope; 18 | this.Server = Server; 19 | let session = Sessions.default; 20 | this.address = session.getAddress(); 21 | this.balanceLoaded = false; 22 | this.showRefreshButton = false; 23 | this.accountNotFound = false; 24 | 25 | if (session.data && session.data['useLedger'] && session.data['ledgerAppVersion']) { 26 | let ledgerAppMajorVersion = Number(session.data['ledgerAppVersion'].substring(0, session.data['ledgerAppVersion'].indexOf('.'))); 27 | this.checkAddressAvailable = ledgerAppMajorVersion > 1; 28 | } 29 | this.bip32Path = session.data && session.data['bip32Path']; 30 | this.monitorImage = require('../images/monitor.png'); 31 | 32 | this.$rootScope.$on('account-viewer.transaction-success', () => { 33 | this.invite = null; 34 | }); 35 | 36 | Server.accounts() 37 | .accountId(this.address) 38 | .stream({ 39 | onmessage: account => this.onBalanceChange.call(this, account.balances), 40 | onerror: error => { 41 | this.onStreamError.call(this, error) 42 | } 43 | }); 44 | 45 | Server.operations() 46 | .forAccount(this.address) 47 | .call() 48 | .then(operations => { 49 | // Merged_back = 2ops: create_account, account_merge 50 | if (operations.records.length <= 2) { 51 | $http({ 52 | method: 'GET', 53 | url: Config.get("inviteServer")+'/account-viewer/check?id='+this.address 54 | }).then(response => { 55 | this.invite = response.data; 56 | 57 | if (this.invite.state == "queued") { 58 | // Not `merged_back` and operations.records.length > 1 = transaction sent 59 | if (operations.records.length > 1) { 60 | this.invite = null; 61 | return; 62 | } 63 | 64 | var claimedAt = new Date(this.invite.claimed_at); 65 | var days = 7-Math.floor((new Date() - claimedAt) / (1000*60*60*24)); 66 | var daysString; 67 | if (days <= 0) { 68 | daysString = "less than a day"; 69 | } else if (days == 1) { 70 | daysString = "1 day"; 71 | } else { 72 | daysString = days+" days"; 73 | } 74 | this.invite.days = daysString; 75 | } 76 | }, response => {}); 77 | } 78 | }); 79 | } 80 | 81 | onStreamError(error) { 82 | if (error === 'EventSource not supported') { 83 | this.showRefreshButton = true; 84 | this.loadAccount(); 85 | this.$rootScope.$on('account-viewer.transaction-success', () => { 86 | this.loadAccount(); 87 | }); 88 | } else { 89 | this.loadAccount(); 90 | } 91 | } 92 | 93 | loadAccount() { 94 | return this.Server.accounts() 95 | .accountId(this.address) 96 | .call() 97 | .then(account => this.onBalanceChange.call(this, account.balances)) 98 | .catch(e => { 99 | if (e.name === 'NotFoundError') { 100 | this.onBalanceChange.call(this, null); 101 | } else { 102 | throw e; 103 | } 104 | }); 105 | } 106 | 107 | onBalanceChange(balances) { 108 | if (balances === null) { 109 | this.accountNotFound = true; 110 | } else { 111 | this.accountNotFound = false; 112 | } 113 | 114 | let balance; 115 | if (_.isArray(balances) && balances.length > 0) { 116 | let nativeBalance = _(balances).find(balance => balance.asset_type === 'native'); 117 | if (nativeBalance) { 118 | balance = nativeBalance.balance; 119 | } else { 120 | balance = 0; 121 | } 122 | } else { 123 | balance = 0; 124 | } 125 | this.balance = new BigNumber(balance).toNumber(); 126 | this.balanceLoaded = true; 127 | this.$scope.$apply(); 128 | } 129 | 130 | checkAddress() { 131 | try { 132 | LedgerTransport.create().then((transport) =>{ 133 | new LedgerStr(transport).getPublicKey(this.bip32Path, false, true); 134 | }); 135 | } catch (err) { 136 | console.log('error checking address'); 137 | console.log(err); 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /vendor/@ledgerhq/hw-transport-u2f/lib-es/TransportU2F.js: -------------------------------------------------------------------------------- 1 | import { sign, isSupported } from "u2f-api"; 2 | import Transport from "@ledgerhq/hw-transport"; 3 | import { log } from "@ledgerhq/logs"; 4 | import { TransportError } from "@ledgerhq/errors"; 5 | 6 | function wrapU2FTransportError(originalError, message, id) { 7 | const err = new TransportError(message, id); // $FlowFixMe 8 | 9 | err.originalError = originalError; 10 | return err; 11 | } 12 | 13 | function wrapApdu(apdu, key) { 14 | const result = Buffer.alloc(apdu.length); 15 | 16 | for (let i = 0; i < apdu.length; i++) { 17 | result[i] = apdu[i] ^ key[i % key.length]; 18 | } 19 | 20 | return result; 21 | } // Convert from normal to web-safe, strip trailing "="s 22 | 23 | 24 | const webSafe64 = base64 => base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); // Convert from web-safe to normal, add trailing "="s 25 | 26 | 27 | const normal64 = base64 => base64.replace(/-/g, "+").replace(/_/g, "/") + "==".substring(0, 3 * base64.length % 4); 28 | 29 | function attemptExchange(apdu, timeoutMillis, scrambleKey, unwrap) { 30 | const keyHandle = wrapApdu(apdu, scrambleKey); 31 | const challenge = Buffer.from("0000000000000000000000000000000000000000000000000000000000000000", "hex"); 32 | const signRequest = { 33 | version: "U2F_V2", 34 | keyHandle: webSafe64(keyHandle.toString("base64")), 35 | challenge: webSafe64(challenge.toString("base64")), 36 | appId: location.origin 37 | }; 38 | log("apdu", "=> " + apdu.toString("hex")); 39 | return sign(signRequest, timeoutMillis / 1000).then(response => { 40 | const { 41 | signatureData 42 | } = response; 43 | 44 | if (typeof signatureData === "string") { 45 | const data = Buffer.from(normal64(signatureData), "base64"); 46 | let result; 47 | 48 | if (!unwrap) { 49 | result = data; 50 | } else { 51 | result = data.slice(5); 52 | } 53 | 54 | log("apdu", "<= " + result.toString("hex")); 55 | return result; 56 | } else { 57 | throw response; 58 | } 59 | }); 60 | } 61 | 62 | let transportInstances = []; 63 | 64 | function emitDisconnect() { 65 | transportInstances.forEach(t => t.emit("disconnect")); 66 | transportInstances = []; 67 | } 68 | 69 | function isTimeoutU2FError(u2fError) { 70 | return u2fError.metaData.code === 5; 71 | } 72 | /** 73 | * U2F web Transport implementation 74 | * @example 75 | * import TransportU2F from "@ledgerhq/hw-transport-u2f"; 76 | * ... 77 | * TransportU2F.create().then(transport => ...) 78 | */ 79 | 80 | 81 | export default class TransportU2F extends Transport { 82 | /* 83 | */ 84 | 85 | /* 86 | */ 87 | 88 | /** 89 | * static function to create a new Transport from a connected Ledger device discoverable via U2F (browser support) 90 | */ 91 | static async open(_, _openTimeout = 5000) { 92 | return new TransportU2F(); 93 | } 94 | 95 | constructor() { 96 | super(); 97 | this.scrambleKey = void 0; 98 | this.unwrap = true; 99 | transportInstances.push(this); 100 | } 101 | /** 102 | * Exchange with the device using APDU protocol. 103 | * @param apdu 104 | * @returns a promise of apdu response 105 | */ 106 | 107 | 108 | async exchange(apdu) { 109 | try { 110 | return await attemptExchange(apdu, this.exchangeTimeout, this.scrambleKey, this.unwrap); 111 | } catch (e) { 112 | const isU2FError = typeof e.metaData === "object"; 113 | 114 | if (isU2FError) { 115 | if (isTimeoutU2FError(e)) { 116 | emitDisconnect(); 117 | } // the wrapping make error more usable and "printable" to the end user. 118 | 119 | 120 | throw wrapU2FTransportError(e, "Failed to sign with Ledger device: U2F " + e.metaData.type, "U2F_" + e.metaData.code); 121 | } else { 122 | throw e; 123 | } 124 | } 125 | } 126 | /** 127 | */ 128 | 129 | 130 | setScrambleKey(scrambleKey) { 131 | this.scrambleKey = Buffer.from(scrambleKey, "ascii"); 132 | } 133 | /** 134 | */ 135 | 136 | 137 | setUnwrap(unwrap) { 138 | this.unwrap = unwrap; 139 | } 140 | 141 | close() { 142 | // u2f have no way to clean things up 143 | return Promise.resolve(); 144 | } 145 | 146 | } 147 | TransportU2F.isSupported = isSupported; 148 | 149 | TransportU2F.list = () => // this transport is not discoverable but we are going to guess if it is here with isSupported() 150 | isSupported().then(supported => supported ? [null] : []); 151 | 152 | TransportU2F.listen = observer => { 153 | let unsubscribed = false; 154 | isSupported().then(supported => { 155 | if (unsubscribed) return; 156 | 157 | if (supported) { 158 | observer.next({ 159 | type: "add", 160 | descriptor: null 161 | }); 162 | observer.complete(); 163 | } else { 164 | observer.error(new TransportError("U2F browser support is needed for Ledger. " + "Please use Chrome, Opera or Firefox with a U2F extension. " + "Also make sure you're on an HTTPS connection", "U2FNotSupported")); 165 | } 166 | }); 167 | return { 168 | unsubscribe: () => { 169 | unsubscribed = true; 170 | } 171 | }; 172 | }; 173 | //# sourceMappingURL=TransportU2F.js.map -------------------------------------------------------------------------------- /controllers/login.controller.es6: -------------------------------------------------------------------------------- 1 | import {Intent} from "interstellar-core"; 2 | import {Controller, Inject} from "interstellar-core"; 3 | import {Keypair} from 'stellar-sdk'; 4 | import {Alert, AlertGroup} from 'interstellar-ui-messages'; 5 | import LedgerTransport from '../vendor/@ledgerhq/hw-transport-u2f'; 6 | import LedgerStr from '@ledgerhq/hw-app-str'; 7 | import { logEvent } from "../metrics.es6"; 8 | 9 | @Controller("LoginController") 10 | @Inject("$scope", "interstellar-core.Config", "interstellar-core.IntentBroadcast", "interstellar-sessions.Sessions", "interstellar-ui-messages.Alerts") 11 | export default class LoginController { 12 | constructor($scope, Config, IntentBroadcast, Sessions, Alerts) { 13 | this.$scope = $scope; 14 | this.Config = Config; 15 | this.IntentBroadcast = IntentBroadcast; 16 | this.Sessions = Sessions; 17 | this.failedAttempts = 0; 18 | 19 | if (this.Sessions.hasDefault()) { 20 | this.broadcastShowDashboardIntent(); 21 | } 22 | 23 | this.alertGroup = new AlertGroup(); 24 | this.alertGroup.registerUpdateListener(alerts => { 25 | this.alerts = alerts; 26 | }); 27 | 28 | this.ledgerAlertGroup = new AlertGroup(); 29 | this.ledgerAlertGroup.registerUpdateListener(alerts => { 30 | this.ledgerAlerts = alerts; 31 | }); 32 | this.bip32Path = "44'/148'/0'"; 33 | this.connectLedger(); 34 | 35 | this.infoImage = require('../images/info.png'); 36 | this.showInfo = false; 37 | 38 | Alerts.registerGroup(this.alertGroup); 39 | } 40 | 41 | broadcastShowDashboardIntent() { 42 | this.IntentBroadcast.sendBroadcast( 43 | new Intent( 44 | Intent.TYPES.SHOW_DASHBOARD 45 | ) 46 | ); 47 | } 48 | 49 | toggleInfo() { 50 | this.showInfo = !this.showInfo; 51 | } 52 | 53 | connectLedger() { 54 | this.ledgerStatus = 'Not connected'; 55 | LedgerTransport.create().then((transport) => { 56 | logEvent('login: connect ledger') 57 | return new LedgerStr(transport).getAppConfiguration().then((result) =>{ 58 | this.ledgerStatus = 'Connected'; 59 | this.ledgerAppVersion = result.version; 60 | this.$scope.$apply(); 61 | }) 62 | }).catch(err => { 63 | console.log(err); 64 | this.ledgerStatus = 'Error: ' + err; 65 | this.$scope.$apply(); 66 | 67 | logEvent('login: connect ledger: error', { 68 | message: err.message, isHttps: location.protocol === 'https' 69 | }) 70 | 71 | // Try again in 5 seconds if timeout error: 72 | if (err.message && err.message.indexOf("U2F TIMEOUT") !== -1) { 73 | console.log("Connecting to Ledger failed. Trying again in 5 seconds..."); 74 | setTimeout(this.connectLedger(), 5*1000); 75 | } 76 | }); 77 | } 78 | 79 | proceedWithLedger() { 80 | try { 81 | LedgerTransport.create().then((transport) => { 82 | new LedgerStr(transport).getPublicKey(this.bip32Path).then((result) => { 83 | let permanent = this.Config.get("permanentSession"); 84 | let data = { useLedger: true, bip32Path: this.bip32Path, ledgerAppVersion: this.ledgerAppVersion }; 85 | let address = result.publicKey; 86 | this.Sessions.createDefault({address, data, permanent}) 87 | .then(() => this.broadcastShowDashboardIntent()); 88 | }); 89 | }).catch((err) => { 90 | let alert = new Alert({ 91 | title: 'Failed to connect', 92 | text: err, 93 | type: Alert.TYPES.ERROR 94 | }); 95 | this.ledgerAlertGroup.show(alert); 96 | }); 97 | } catch (err) { 98 | let alert = new Alert({ 99 | title: 'Failed to connect', 100 | text: err, 101 | type: Alert.TYPES.ERROR 102 | }); 103 | this.ledgerAlertGroup.show(alert); 104 | } 105 | } 106 | 107 | generate() { 108 | logEvent('login: generate new kepair') 109 | let keypair = Keypair.random(); 110 | this.newKeypair = { 111 | publicKey: keypair.publicKey(), 112 | secretKey: keypair.secret() 113 | }; 114 | } 115 | 116 | submit() { 117 | this.alertGroup.clear(); 118 | 119 | if (this.failedAttempts > 8) { 120 | let alert = new Alert({ 121 | title: "You're doing that too much", 122 | text: 'Please wait a few seconds before attempting to log in again.', 123 | type: Alert.TYPES.ERROR 124 | }); 125 | this.alertGroup.show(alert); 126 | return 127 | } 128 | 129 | this.processing = true; 130 | let secret = this.secret; 131 | try { 132 | let keypair = Keypair.fromSecret(secret); 133 | let address = keypair.publicKey(); 134 | let permanent = this.Config.get("permanentSession"); 135 | this.Sessions.createDefault({address, secret, permanent}) 136 | .then(() => { 137 | logEvent('login: success') 138 | this.broadcastShowDashboardIntent(); 139 | }); 140 | } catch(e) { 141 | logEvent('login: error: invalid secret key') 142 | 143 | // Rate limit with exponential backoff. 144 | this.failedAttempts++; 145 | setTimeout(() => { 146 | this.failedAttempts-- 147 | }, (2 ** this.failedAttempts) * 1000) 148 | 149 | this.processing = false; 150 | let alert = new Alert({ 151 | title: 'Invalid secret key', 152 | text: 'Secret keys are uppercase and begin with the letter "S."', 153 | type: Alert.TYPES.ERROR 154 | }); 155 | this.alertGroup.show(alert); 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /vendor/@ledgerhq/hw-transport-u2f/src/TransportU2F.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import { sign, isSupported } from "u2f-api"; 4 | import Transport from "@ledgerhq/hw-transport"; 5 | import { log } from "@ledgerhq/logs"; 6 | import { TransportError } from "@ledgerhq/errors"; 7 | 8 | function wrapU2FTransportError(originalError, message, id) { 9 | const err = new TransportError(message, id); 10 | // $FlowFixMe 11 | err.originalError = originalError; 12 | return err; 13 | } 14 | 15 | function wrapApdu(apdu: Buffer, key: Buffer) { 16 | const result = Buffer.alloc(apdu.length); 17 | for (let i = 0; i < apdu.length; i++) { 18 | result[i] = apdu[i] ^ key[i % key.length]; 19 | } 20 | return result; 21 | } 22 | 23 | // Convert from normal to web-safe, strip trailing "="s 24 | const webSafe64 = (base64: string) => 25 | base64 26 | .replace(/\+/g, "-") 27 | .replace(/\//g, "_") 28 | .replace(/=+$/, ""); 29 | 30 | // Convert from web-safe to normal, add trailing "="s 31 | const normal64 = (base64: string) => 32 | base64.replace(/-/g, "+").replace(/_/g, "/") + 33 | "==".substring(0, (3 * base64.length) % 4); 34 | 35 | function attemptExchange( 36 | apdu: Buffer, 37 | timeoutMillis: number, 38 | scrambleKey: Buffer, 39 | unwrap: boolean 40 | ): Promise { 41 | const keyHandle = wrapApdu(apdu, scrambleKey); 42 | const challenge = Buffer.from( 43 | "0000000000000000000000000000000000000000000000000000000000000000", 44 | "hex" 45 | ); 46 | const signRequest = { 47 | version: "U2F_V2", 48 | keyHandle: webSafe64(keyHandle.toString("base64")), 49 | challenge: webSafe64(challenge.toString("base64")), 50 | appId: location.origin 51 | }; 52 | log("apdu", "=> " + apdu.toString("hex")); 53 | return sign(signRequest, timeoutMillis / 1000).then(response => { 54 | const { signatureData } = response; 55 | if (typeof signatureData === "string") { 56 | const data = Buffer.from(normal64(signatureData), "base64"); 57 | let result; 58 | if (!unwrap) { 59 | result = data; 60 | } else { 61 | result = data.slice(5); 62 | } 63 | log("apdu", "<= " + result.toString("hex")); 64 | return result; 65 | } else { 66 | throw response; 67 | } 68 | }); 69 | } 70 | 71 | let transportInstances = []; 72 | 73 | function emitDisconnect() { 74 | transportInstances.forEach(t => t.emit("disconnect")); 75 | transportInstances = []; 76 | } 77 | 78 | function isTimeoutU2FError(u2fError) { 79 | return u2fError.metaData.code === 5; 80 | } 81 | 82 | /** 83 | * U2F web Transport implementation 84 | * @example 85 | * import TransportU2F from "@ledgerhq/hw-transport-u2f"; 86 | * ... 87 | * TransportU2F.create().then(transport => ...) 88 | */ 89 | export default class TransportU2F extends Transport { 90 | static isSupported = isSupported; 91 | 92 | /* 93 | */ 94 | static list = (): * => 95 | // this transport is not discoverable but we are going to guess if it is here with isSupported() 96 | isSupported().then(supported => (supported ? [null] : [])); 97 | 98 | /* 99 | */ 100 | static listen = (observer: *) => { 101 | let unsubscribed = false; 102 | isSupported().then(supported => { 103 | if (unsubscribed) return; 104 | if (supported) { 105 | observer.next({ type: "add", descriptor: null }); 106 | observer.complete(); 107 | } else { 108 | observer.error( 109 | new TransportError( 110 | "U2F browser support is needed for Ledger. " + 111 | "Please use Chrome, Opera or Firefox with a U2F extension. " + 112 | "Also make sure you're on an HTTPS connection", 113 | "U2FNotSupported" 114 | ) 115 | ); 116 | } 117 | }); 118 | return { 119 | unsubscribe: () => { 120 | unsubscribed = true; 121 | } 122 | }; 123 | }; 124 | 125 | scrambleKey: Buffer; 126 | 127 | unwrap: boolean = true; 128 | 129 | /** 130 | * static function to create a new Transport from a connected Ledger device discoverable via U2F (browser support) 131 | */ 132 | static async open(_: *, _openTimeout?: number = 5000): Promise { 133 | return new TransportU2F(); 134 | } 135 | 136 | constructor() { 137 | super(); 138 | transportInstances.push(this); 139 | } 140 | 141 | /** 142 | * Exchange with the device using APDU protocol. 143 | * @param apdu 144 | * @returns a promise of apdu response 145 | */ 146 | async exchange(apdu: Buffer): Promise { 147 | try { 148 | return await attemptExchange( 149 | apdu, 150 | this.exchangeTimeout, 151 | this.scrambleKey, 152 | this.unwrap 153 | ); 154 | } catch (e) { 155 | const isU2FError = typeof e.metaData === "object"; 156 | if (isU2FError) { 157 | if (isTimeoutU2FError(e)) { 158 | emitDisconnect(); 159 | } 160 | // the wrapping make error more usable and "printable" to the end user. 161 | throw wrapU2FTransportError( 162 | e, 163 | "Failed to sign with Ledger device: U2F " + e.metaData.type, 164 | "U2F_" + e.metaData.code 165 | ); 166 | } else { 167 | throw e; 168 | } 169 | } 170 | } 171 | 172 | /** 173 | */ 174 | setScrambleKey(scrambleKey: string) { 175 | this.scrambleKey = Buffer.from(scrambleKey, "ascii"); 176 | } 177 | 178 | /** 179 | */ 180 | setUnwrap(unwrap: boolean) { 181 | this.unwrap = unwrap; 182 | } 183 | 184 | close(): Promise { 185 | // u2f have no way to clean things up 186 | return Promise.resolve(); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /vendor/@ledgerhq/hw-transport-u2f/lib/TransportU2F.js.flow: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import { sign, isSupported } from "u2f-api"; 4 | import Transport from "@ledgerhq/hw-transport"; 5 | import { log } from "@ledgerhq/logs"; 6 | import { TransportError } from "@ledgerhq/errors"; 7 | 8 | function wrapU2FTransportError(originalError, message, id) { 9 | const err = new TransportError(message, id); 10 | // $FlowFixMe 11 | err.originalError = originalError; 12 | return err; 13 | } 14 | 15 | function wrapApdu(apdu: Buffer, key: Buffer) { 16 | const result = Buffer.alloc(apdu.length); 17 | for (let i = 0; i < apdu.length; i++) { 18 | result[i] = apdu[i] ^ key[i % key.length]; 19 | } 20 | return result; 21 | } 22 | 23 | // Convert from normal to web-safe, strip trailing "="s 24 | const webSafe64 = (base64: string) => 25 | base64 26 | .replace(/\+/g, "-") 27 | .replace(/\//g, "_") 28 | .replace(/=+$/, ""); 29 | 30 | // Convert from web-safe to normal, add trailing "="s 31 | const normal64 = (base64: string) => 32 | base64.replace(/-/g, "+").replace(/_/g, "/") + 33 | "==".substring(0, (3 * base64.length) % 4); 34 | 35 | function attemptExchange( 36 | apdu: Buffer, 37 | timeoutMillis: number, 38 | scrambleKey: Buffer, 39 | unwrap: boolean 40 | ): Promise { 41 | const keyHandle = wrapApdu(apdu, scrambleKey); 42 | const challenge = Buffer.from( 43 | "0000000000000000000000000000000000000000000000000000000000000000", 44 | "hex" 45 | ); 46 | const signRequest = { 47 | version: "U2F_V2", 48 | keyHandle: webSafe64(keyHandle.toString("base64")), 49 | challenge: webSafe64(challenge.toString("base64")), 50 | appId: location.origin 51 | }; 52 | log("apdu", "=> " + apdu.toString("hex")); 53 | return sign(signRequest, timeoutMillis / 1000).then(response => { 54 | const { signatureData } = response; 55 | if (typeof signatureData === "string") { 56 | const data = Buffer.from(normal64(signatureData), "base64"); 57 | let result; 58 | if (!unwrap) { 59 | result = data; 60 | } else { 61 | result = data.slice(5); 62 | } 63 | log("apdu", "<= " + result.toString("hex")); 64 | return result; 65 | } else { 66 | throw response; 67 | } 68 | }); 69 | } 70 | 71 | let transportInstances = []; 72 | 73 | function emitDisconnect() { 74 | transportInstances.forEach(t => t.emit("disconnect")); 75 | transportInstances = []; 76 | } 77 | 78 | function isTimeoutU2FError(u2fError) { 79 | return u2fError.metaData.code === 5; 80 | } 81 | 82 | /** 83 | * U2F web Transport implementation 84 | * @example 85 | * import TransportU2F from "@ledgerhq/hw-transport-u2f"; 86 | * ... 87 | * TransportU2F.create().then(transport => ...) 88 | */ 89 | export default class TransportU2F extends Transport { 90 | static isSupported = isSupported; 91 | 92 | /* 93 | */ 94 | static list = (): * => 95 | // this transport is not discoverable but we are going to guess if it is here with isSupported() 96 | isSupported().then(supported => (supported ? [null] : [])); 97 | 98 | /* 99 | */ 100 | static listen = (observer: *) => { 101 | let unsubscribed = false; 102 | isSupported().then(supported => { 103 | if (unsubscribed) return; 104 | if (supported) { 105 | observer.next({ type: "add", descriptor: null }); 106 | observer.complete(); 107 | } else { 108 | observer.error( 109 | new TransportError( 110 | "U2F browser support is needed for Ledger. " + 111 | "Please use Chrome, Opera or Firefox with a U2F extension. " + 112 | "Also make sure you're on an HTTPS connection", 113 | "U2FNotSupported" 114 | ) 115 | ); 116 | } 117 | }); 118 | return { 119 | unsubscribe: () => { 120 | unsubscribed = true; 121 | } 122 | }; 123 | }; 124 | 125 | scrambleKey: Buffer; 126 | 127 | unwrap: boolean = true; 128 | 129 | /** 130 | * static function to create a new Transport from a connected Ledger device discoverable via U2F (browser support) 131 | */ 132 | static async open(_: *, _openTimeout?: number = 5000): Promise { 133 | return new TransportU2F(); 134 | } 135 | 136 | constructor() { 137 | super(); 138 | transportInstances.push(this); 139 | } 140 | 141 | /** 142 | * Exchange with the device using APDU protocol. 143 | * @param apdu 144 | * @returns a promise of apdu response 145 | */ 146 | async exchange(apdu: Buffer): Promise { 147 | try { 148 | return await attemptExchange( 149 | apdu, 150 | this.exchangeTimeout, 151 | this.scrambleKey, 152 | this.unwrap 153 | ); 154 | } catch (e) { 155 | const isU2FError = typeof e.metaData === "object"; 156 | if (isU2FError) { 157 | if (isTimeoutU2FError(e)) { 158 | emitDisconnect(); 159 | } 160 | // the wrapping make error more usable and "printable" to the end user. 161 | throw wrapU2FTransportError( 162 | e, 163 | "Failed to sign with Ledger device: U2F " + e.metaData.type, 164 | "U2F_" + e.metaData.code 165 | ); 166 | } else { 167 | throw e; 168 | } 169 | } 170 | } 171 | 172 | /** 173 | */ 174 | setScrambleKey(scrambleKey: string) { 175 | this.scrambleKey = Buffer.from(scrambleKey, "ascii"); 176 | } 177 | 178 | /** 179 | */ 180 | setUnwrap(unwrap: boolean) { 181 | this.unwrap = unwrap; 182 | } 183 | 184 | close(): Promise { 185 | // u2f have no way to clean things up 186 | return Promise.resolve(); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /vendor/@ledgerhq/hw-transport-u2f/lib-es/TransportU2F.js.flow: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import { sign, isSupported } from "u2f-api"; 4 | import Transport from "@ledgerhq/hw-transport"; 5 | import { log } from "@ledgerhq/logs"; 6 | import { TransportError } from "@ledgerhq/errors"; 7 | 8 | function wrapU2FTransportError(originalError, message, id) { 9 | const err = new TransportError(message, id); 10 | // $FlowFixMe 11 | err.originalError = originalError; 12 | return err; 13 | } 14 | 15 | function wrapApdu(apdu: Buffer, key: Buffer) { 16 | const result = Buffer.alloc(apdu.length); 17 | for (let i = 0; i < apdu.length; i++) { 18 | result[i] = apdu[i] ^ key[i % key.length]; 19 | } 20 | return result; 21 | } 22 | 23 | // Convert from normal to web-safe, strip trailing "="s 24 | const webSafe64 = (base64: string) => 25 | base64 26 | .replace(/\+/g, "-") 27 | .replace(/\//g, "_") 28 | .replace(/=+$/, ""); 29 | 30 | // Convert from web-safe to normal, add trailing "="s 31 | const normal64 = (base64: string) => 32 | base64.replace(/-/g, "+").replace(/_/g, "/") + 33 | "==".substring(0, (3 * base64.length) % 4); 34 | 35 | function attemptExchange( 36 | apdu: Buffer, 37 | timeoutMillis: number, 38 | scrambleKey: Buffer, 39 | unwrap: boolean 40 | ): Promise { 41 | const keyHandle = wrapApdu(apdu, scrambleKey); 42 | const challenge = Buffer.from( 43 | "0000000000000000000000000000000000000000000000000000000000000000", 44 | "hex" 45 | ); 46 | const signRequest = { 47 | version: "U2F_V2", 48 | keyHandle: webSafe64(keyHandle.toString("base64")), 49 | challenge: webSafe64(challenge.toString("base64")), 50 | appId: location.origin 51 | }; 52 | log("apdu", "=> " + apdu.toString("hex")); 53 | return sign(signRequest, timeoutMillis / 1000).then(response => { 54 | const { signatureData } = response; 55 | if (typeof signatureData === "string") { 56 | const data = Buffer.from(normal64(signatureData), "base64"); 57 | let result; 58 | if (!unwrap) { 59 | result = data; 60 | } else { 61 | result = data.slice(5); 62 | } 63 | log("apdu", "<= " + result.toString("hex")); 64 | return result; 65 | } else { 66 | throw response; 67 | } 68 | }); 69 | } 70 | 71 | let transportInstances = []; 72 | 73 | function emitDisconnect() { 74 | transportInstances.forEach(t => t.emit("disconnect")); 75 | transportInstances = []; 76 | } 77 | 78 | function isTimeoutU2FError(u2fError) { 79 | return u2fError.metaData.code === 5; 80 | } 81 | 82 | /** 83 | * U2F web Transport implementation 84 | * @example 85 | * import TransportU2F from "@ledgerhq/hw-transport-u2f"; 86 | * ... 87 | * TransportU2F.create().then(transport => ...) 88 | */ 89 | export default class TransportU2F extends Transport { 90 | static isSupported = isSupported; 91 | 92 | /* 93 | */ 94 | static list = (): * => 95 | // this transport is not discoverable but we are going to guess if it is here with isSupported() 96 | isSupported().then(supported => (supported ? [null] : [])); 97 | 98 | /* 99 | */ 100 | static listen = (observer: *) => { 101 | let unsubscribed = false; 102 | isSupported().then(supported => { 103 | if (unsubscribed) return; 104 | if (supported) { 105 | observer.next({ type: "add", descriptor: null }); 106 | observer.complete(); 107 | } else { 108 | observer.error( 109 | new TransportError( 110 | "U2F browser support is needed for Ledger. " + 111 | "Please use Chrome, Opera or Firefox with a U2F extension. " + 112 | "Also make sure you're on an HTTPS connection", 113 | "U2FNotSupported" 114 | ) 115 | ); 116 | } 117 | }); 118 | return { 119 | unsubscribe: () => { 120 | unsubscribed = true; 121 | } 122 | }; 123 | }; 124 | 125 | scrambleKey: Buffer; 126 | 127 | unwrap: boolean = true; 128 | 129 | /** 130 | * static function to create a new Transport from a connected Ledger device discoverable via U2F (browser support) 131 | */ 132 | static async open(_: *, _openTimeout?: number = 5000): Promise { 133 | return new TransportU2F(); 134 | } 135 | 136 | constructor() { 137 | super(); 138 | transportInstances.push(this); 139 | } 140 | 141 | /** 142 | * Exchange with the device using APDU protocol. 143 | * @param apdu 144 | * @returns a promise of apdu response 145 | */ 146 | async exchange(apdu: Buffer): Promise { 147 | try { 148 | return await attemptExchange( 149 | apdu, 150 | this.exchangeTimeout, 151 | this.scrambleKey, 152 | this.unwrap 153 | ); 154 | } catch (e) { 155 | const isU2FError = typeof e.metaData === "object"; 156 | if (isU2FError) { 157 | if (isTimeoutU2FError(e)) { 158 | emitDisconnect(); 159 | } 160 | // the wrapping make error more usable and "printable" to the end user. 161 | throw wrapU2FTransportError( 162 | e, 163 | "Failed to sign with Ledger device: U2F " + e.metaData.type, 164 | "U2F_" + e.metaData.code 165 | ); 166 | } else { 167 | throw e; 168 | } 169 | } 170 | } 171 | 172 | /** 173 | */ 174 | setScrambleKey(scrambleKey: string) { 175 | this.scrambleKey = Buffer.from(scrambleKey, "ascii"); 176 | } 177 | 178 | /** 179 | */ 180 | setUnwrap(unwrap: boolean) { 181 | this.unwrap = unwrap; 182 | } 183 | 184 | close(): Promise { 185 | // u2f have no way to clean things up 186 | return Promise.resolve(); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /templates/login.template.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 10 |
11 |
12 | 13 |
14 |
15 |
16 |

Stellar Account Viewer

17 |

Use this lightweight client to send and receive lumens over the Stellar network.

18 |
19 |
20 |

Secret key:

21 | 22 | 23 |
24 | 25 | {{alert.title}}

26 | {{alert.text}} 27 |
28 |
29 | 30 | 31 |
32 |
33 |

34 | Generate key pair for a new account 35 |

36 | 37 |
38 |

39 | ATTENTION: 40 | Please write down your secret key and keep it safe. It won't be displayed again. 41 | You will lose access to your lumens if you lose your secret key. 42 |

43 | 44 |
45 | Public Key:
46 | {{login.newKeypair.publicKey}}

47 | 48 | Secret Key:
49 | {{login.newKeypair.secretKey}} 50 |
51 |
52 | 53 | 54 |
55 |
56 | Connect Ledger Hardware Wallet 57 |
58 |
59 | Available on Chrome and Opera. Install the Stellar app from Ledger and enable browser support in the app settings. 60 |
61 |
62 | Notice: Please update your Ledger Nano S to use version 3.1.0 or newer of the Stellar app. Older versions frequently experience timeout errors. 63 |
64 |
65 | 66 | 67 | 68 |
69 |
70 | 71 | {{alert.title}}

72 | {{alert.text}} 73 |
74 |
75 | 76 |
77 |
78 | Looking for a more advanced wallet? Proceed here » 79 |
80 |
81 | Looking for legacy login? Proceed here » 82 |
83 |
84 |
85 | 86 | 105 | -------------------------------------------------------------------------------- /vendor/@ledgerhq/hw-transport/lib-es/Transport.js: -------------------------------------------------------------------------------- 1 | import EventEmitter from "events"; 2 | import { TransportRaceCondition, TransportError, StatusCodes, getAltStatusMessage, TransportStatusError } from "@ledgerhq/errors"; 3 | export { TransportError, TransportStatusError, StatusCodes, getAltStatusMessage }; 4 | /** 5 | */ 6 | 7 | /** 8 | * Transport defines the generic interface to share between node/u2f impl 9 | * A **Descriptor** is a parametric type that is up to be determined for the implementation. 10 | * it can be for instance an ID, an file path, a URL,... 11 | */ 12 | export default class Transport { 13 | constructor() { 14 | this.exchangeTimeout = 30000; 15 | this.unresponsiveTimeout = 15000; 16 | this._events = new EventEmitter(); 17 | 18 | this.send = async (cla, ins, p1, p2, data = Buffer.alloc(0), statusList = [StatusCodes.OK]) => { 19 | if (data.length >= 256) { 20 | throw new TransportError("data.length exceed 256 bytes limit. Got: " + data.length, "DataLengthTooBig"); 21 | } 22 | 23 | const response = await this.exchange(Buffer.concat([Buffer.from([cla, ins, p1, p2]), Buffer.from([data.length]), data])); 24 | const sw = response.readUInt16BE(response.length - 2); 25 | 26 | if (!statusList.some(s => s === sw)) { 27 | throw new TransportStatusError(sw); 28 | } 29 | 30 | return response; 31 | }; 32 | 33 | this.exchangeBusyPromise = void 0; 34 | 35 | this.exchangeAtomicImpl = async f => { 36 | if (this.exchangeBusyPromise) { 37 | throw new TransportRaceCondition("An action was already pending on the Ledger device. Please deny or reconnect."); 38 | } 39 | 40 | let resolveBusy; 41 | const busyPromise = new Promise(r => { 42 | resolveBusy = r; 43 | }); 44 | this.exchangeBusyPromise = busyPromise; 45 | let unresponsiveReached = false; 46 | const timeout = setTimeout(() => { 47 | unresponsiveReached = true; 48 | this.emit("unresponsive"); 49 | }, this.unresponsiveTimeout); 50 | 51 | try { 52 | const res = await f(); 53 | 54 | if (unresponsiveReached) { 55 | this.emit("responsive"); 56 | } 57 | 58 | return res; 59 | } finally { 60 | clearTimeout(timeout); 61 | if (resolveBusy) resolveBusy(); 62 | this.exchangeBusyPromise = null; 63 | } 64 | }; 65 | 66 | this._appAPIlock = null; 67 | } 68 | 69 | /** 70 | * low level api to communicate with the device 71 | * This method is for implementations to implement but should not be directly called. 72 | * Instead, the recommanded way is to use send() method 73 | * @param apdu the data to send 74 | * @return a Promise of response data 75 | */ 76 | exchange(_apdu) { 77 | throw new Error("exchange not implemented"); 78 | } 79 | /** 80 | * set the "scramble key" for the next exchanges with the device. 81 | * Each App can have a different scramble key and they internally will set it at instanciation. 82 | * @param key the scramble key 83 | */ 84 | 85 | 86 | setScrambleKey(_key) {} 87 | /** 88 | * close the exchange with the device. 89 | * @return a Promise that ends when the transport is closed. 90 | */ 91 | 92 | 93 | close() { 94 | return Promise.resolve(); 95 | } 96 | 97 | /** 98 | * Listen to an event on an instance of transport. 99 | * Transport implementation can have specific events. Here is the common events: 100 | * * `"disconnect"` : triggered if Transport is disconnected 101 | */ 102 | on(eventName, cb) { 103 | this._events.on(eventName, cb); 104 | } 105 | /** 106 | * Stop listening to an event on an instance of transport. 107 | */ 108 | 109 | 110 | off(eventName, cb) { 111 | this._events.removeListener(eventName, cb); 112 | } 113 | 114 | emit(event, ...args) { 115 | this._events.emit(event, ...args); 116 | } 117 | /** 118 | * Enable or not logs of the binary exchange 119 | */ 120 | 121 | 122 | setDebugMode() { 123 | console.warn("setDebugMode is deprecated. use @ledgerhq/logs instead. No logs are emitted in this anymore."); 124 | } 125 | /** 126 | * Set a timeout (in milliseconds) for the exchange call. Only some transport might implement it. (e.g. U2F) 127 | */ 128 | 129 | 130 | setExchangeTimeout(exchangeTimeout) { 131 | this.exchangeTimeout = exchangeTimeout; 132 | } 133 | /** 134 | * Define the delay before emitting "unresponsive" on an exchange that does not respond 135 | */ 136 | 137 | 138 | setExchangeUnresponsiveTimeout(unresponsiveTimeout) { 139 | this.unresponsiveTimeout = unresponsiveTimeout; 140 | } 141 | /** 142 | * wrapper on top of exchange to simplify work of the implementation. 143 | * @param cla 144 | * @param ins 145 | * @param p1 146 | * @param p2 147 | * @param data 148 | * @param statusList is a list of accepted status code (shorts). [0x9000] by default 149 | * @return a Promise of response buffer 150 | */ 151 | 152 | 153 | /** 154 | * create() allows to open the first descriptor available or 155 | * throw if there is none or if timeout is reached. 156 | * This is a light helper, alternative to using listen() and open() (that you may need for any more advanced usecase) 157 | * @example 158 | TransportFoo.create().then(transport => ...) 159 | */ 160 | static create(openTimeout = 3000, listenTimeout) { 161 | return new Promise((resolve, reject) => { 162 | let found = false; 163 | const sub = this.listen({ 164 | next: e => { 165 | found = true; 166 | if (sub) sub.unsubscribe(); 167 | if (listenTimeoutId) clearTimeout(listenTimeoutId); 168 | this.open(e.descriptor, openTimeout).then(resolve, reject); 169 | }, 170 | error: e => { 171 | if (listenTimeoutId) clearTimeout(listenTimeoutId); 172 | reject(e); 173 | }, 174 | complete: () => { 175 | if (listenTimeoutId) clearTimeout(listenTimeoutId); 176 | 177 | if (!found) { 178 | reject(new TransportError(this.ErrorMessage_NoDeviceFound, "NoDeviceFound")); 179 | } 180 | } 181 | }); 182 | const listenTimeoutId = listenTimeout ? setTimeout(() => { 183 | sub.unsubscribe(); 184 | reject(new TransportError(this.ErrorMessage_ListenTimeout, "ListenTimeout")); 185 | }, listenTimeout) : null; 186 | }); 187 | } 188 | 189 | decorateAppAPIMethods(self, methods, scrambleKey) { 190 | for (let methodName of methods) { 191 | self[methodName] = this.decorateAppAPIMethod(methodName, self[methodName], self, scrambleKey); 192 | } 193 | } 194 | 195 | decorateAppAPIMethod(methodName, f, ctx, scrambleKey) { 196 | return async (...args) => { 197 | const { 198 | _appAPIlock 199 | } = this; 200 | 201 | if (_appAPIlock) { 202 | return Promise.reject(new TransportError("Ledger Device is busy (lock " + _appAPIlock + ")", "TransportLocked")); 203 | } 204 | 205 | try { 206 | this._appAPIlock = methodName; 207 | this.setScrambleKey(scrambleKey); 208 | return await f.apply(ctx, args); 209 | } finally { 210 | this._appAPIlock = null; 211 | } 212 | }; 213 | } 214 | 215 | } 216 | Transport.isSupported = void 0; 217 | Transport.list = void 0; 218 | Transport.listen = void 0; 219 | Transport.open = void 0; 220 | Transport.ErrorMessage_ListenTimeout = "No Ledger device found (timeout)"; 221 | Transport.ErrorMessage_NoDeviceFound = "No Ledger device found"; 222 | //# sourceMappingURL=Transport.js.map -------------------------------------------------------------------------------- /vendor/@ledgerhq/hw-transport-u2f/lib/TransportU2F.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../src/TransportU2F.js"],"names":["wrapU2FTransportError","originalError","message","id","err","TransportError","wrapApdu","apdu","key","result","Buffer","alloc","length","i","webSafe64","base64","replace","normal64","substring","attemptExchange","timeoutMillis","scrambleKey","unwrap","keyHandle","challenge","from","signRequest","version","toString","appId","location","origin","then","response","signatureData","data","slice","transportInstances","emitDisconnect","forEach","t","emit","isTimeoutU2FError","u2fError","metaData","code","TransportU2F","Transport","open","_","_openTimeout","constructor","push","exchange","exchangeTimeout","e","isU2FError","type","setScrambleKey","setUnwrap","close","Promise","resolve","isSupported","list","supported","listen","observer","unsubscribed","next","descriptor","complete","error","unsubscribe"],"mappings":"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","sourcesContent":["//@flow\n\nimport { sign, isSupported } from \"u2f-api\";\nimport Transport from \"@ledgerhq/hw-transport\";\nimport { log } from \"@ledgerhq/logs\";\nimport { TransportError } from \"@ledgerhq/errors\";\n\nfunction wrapU2FTransportError(originalError, message, id) {\n const err = new TransportError(message, id);\n // $FlowFixMe\n err.originalError = originalError;\n return err;\n}\n\nfunction wrapApdu(apdu: Buffer, key: Buffer) {\n const result = Buffer.alloc(apdu.length);\n for (let i = 0; i < apdu.length; i++) {\n result[i] = apdu[i] ^ key[i % key.length];\n }\n return result;\n}\n\n// Convert from normal to web-safe, strip trailing \"=\"s\nconst webSafe64 = (base64: string) =>\n base64\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n\n// Convert from web-safe to normal, add trailing \"=\"s\nconst normal64 = (base64: string) =>\n base64.replace(/-/g, \"+\").replace(/_/g, \"/\") +\n \"==\".substring(0, (3 * base64.length) % 4);\n\nfunction attemptExchange(\n apdu: Buffer,\n timeoutMillis: number,\n scrambleKey: Buffer,\n unwrap: boolean\n): Promise {\n const keyHandle = wrapApdu(apdu, scrambleKey);\n const challenge = Buffer.from(\n \"0000000000000000000000000000000000000000000000000000000000000000\",\n \"hex\"\n );\n const signRequest = {\n version: \"U2F_V2\",\n keyHandle: webSafe64(keyHandle.toString(\"base64\")),\n challenge: webSafe64(challenge.toString(\"base64\")),\n appId: location.origin\n };\n log(\"apdu\", \"=> \" + apdu.toString(\"hex\"));\n return sign(signRequest, timeoutMillis / 1000).then(response => {\n const { signatureData } = response;\n if (typeof signatureData === \"string\") {\n const data = Buffer.from(normal64(signatureData), \"base64\");\n let result;\n if (!unwrap) {\n result = data;\n } else {\n result = data.slice(5);\n }\n log(\"apdu\", \"<= \" + result.toString(\"hex\"));\n return result;\n } else {\n throw response;\n }\n });\n}\n\nlet transportInstances = [];\n\nfunction emitDisconnect() {\n transportInstances.forEach(t => t.emit(\"disconnect\"));\n transportInstances = [];\n}\n\nfunction isTimeoutU2FError(u2fError) {\n return u2fError.metaData.code === 5;\n}\n\n/**\n * U2F web Transport implementation\n * @example\n * import TransportU2F from \"@ledgerhq/hw-transport-u2f\";\n * ...\n * TransportU2F.create().then(transport => ...)\n */\nexport default class TransportU2F extends Transport {\n static isSupported = isSupported;\n\n /*\n */\n static list = (): * =>\n // this transport is not discoverable but we are going to guess if it is here with isSupported()\n isSupported().then(supported => (supported ? [null] : []));\n\n /*\n */\n static listen = (observer: *) => {\n let unsubscribed = false;\n isSupported().then(supported => {\n if (unsubscribed) return;\n if (supported) {\n observer.next({ type: \"add\", descriptor: null });\n observer.complete();\n } else {\n observer.error(\n new TransportError(\n \"U2F browser support is needed for Ledger. \" +\n \"Please use Chrome, Opera or Firefox with a U2F extension. \" +\n \"Also make sure you're on an HTTPS connection\",\n \"U2FNotSupported\"\n )\n );\n }\n });\n return {\n unsubscribe: () => {\n unsubscribed = true;\n }\n };\n };\n\n scrambleKey: Buffer;\n\n unwrap: boolean = true;\n\n /**\n * static function to create a new Transport from a connected Ledger device discoverable via U2F (browser support)\n */\n static async open(_: *, _openTimeout?: number = 5000): Promise {\n return new TransportU2F();\n }\n\n constructor() {\n super();\n transportInstances.push(this);\n }\n\n /**\n * Exchange with the device using APDU protocol.\n * @param apdu\n * @returns a promise of apdu response\n */\n async exchange(apdu: Buffer): Promise {\n try {\n return await attemptExchange(\n apdu,\n this.exchangeTimeout,\n this.scrambleKey,\n this.unwrap\n );\n } catch (e) {\n const isU2FError = typeof e.metaData === \"object\";\n if (isU2FError) {\n if (isTimeoutU2FError(e)) {\n emitDisconnect();\n }\n // the wrapping make error more usable and \"printable\" to the end user.\n throw wrapU2FTransportError(\n e,\n \"Failed to sign with Ledger device: U2F \" + e.metaData.type,\n \"U2F_\" + e.metaData.code\n );\n } else {\n throw e;\n }\n }\n }\n\n /**\n */\n setScrambleKey(scrambleKey: string) {\n this.scrambleKey = Buffer.from(scrambleKey, \"ascii\");\n }\n\n /**\n */\n setUnwrap(unwrap: boolean) {\n this.unwrap = unwrap;\n }\n\n close(): Promise {\n // u2f have no way to clean things up\n return Promise.resolve();\n }\n}\n"],"file":"TransportU2F.js"} -------------------------------------------------------------------------------- /vendor/@ledgerhq/hw-transport-u2f/lib-es/TransportU2F.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../src/TransportU2F.js"],"names":["sign","isSupported","Transport","log","TransportError","wrapU2FTransportError","originalError","message","id","err","wrapApdu","apdu","key","result","Buffer","alloc","length","i","webSafe64","base64","replace","normal64","substring","attemptExchange","timeoutMillis","scrambleKey","unwrap","keyHandle","challenge","from","signRequest","version","toString","appId","location","origin","then","response","signatureData","data","slice","transportInstances","emitDisconnect","forEach","t","emit","isTimeoutU2FError","u2fError","metaData","code","TransportU2F","open","_","_openTimeout","constructor","push","exchange","exchangeTimeout","e","isU2FError","type","setScrambleKey","setUnwrap","close","Promise","resolve","list","supported","listen","observer","unsubscribed","next","descriptor","complete","error","unsubscribe"],"mappings":"AAEA,SAASA,IAAT,EAAeC,WAAf,QAAkC,SAAlC;AACA,OAAOC,SAAP,MAAsB,wBAAtB;AACA,SAASC,GAAT,QAAoB,gBAApB;AACA,SAASC,cAAT,QAA+B,kBAA/B;;AAEA,SAASC,qBAAT,CAA+BC,aAA/B,EAA8CC,OAA9C,EAAuDC,EAAvD,EAA2D;AACzD,QAAMC,GAAG,GAAG,IAAIL,cAAJ,CAAmBG,OAAnB,EAA4BC,EAA5B,CAAZ,CADyD,CAEzD;;AACAC,EAAAA,GAAG,CAACH,aAAJ,GAAoBA,aAApB;AACA,SAAOG,GAAP;AACD;;AAED,SAASC,QAAT,CAAkBC,IAAlB,EAAgCC,GAAhC,EAA6C;AAC3C,QAAMC,MAAM,GAAGC,MAAM,CAACC,KAAP,CAAaJ,IAAI,CAACK,MAAlB,CAAf;;AACA,OAAK,IAAIC,CAAC,GAAG,CAAb,EAAgBA,CAAC,GAAGN,IAAI,CAACK,MAAzB,EAAiCC,CAAC,EAAlC,EAAsC;AACpCJ,IAAAA,MAAM,CAACI,CAAD,CAAN,GAAYN,IAAI,CAACM,CAAD,CAAJ,GAAUL,GAAG,CAACK,CAAC,GAAGL,GAAG,CAACI,MAAT,CAAzB;AACD;;AACD,SAAOH,MAAP;AACD,C,CAED;;;AACA,MAAMK,SAAS,GAAIC,MAAD,IAChBA,MAAM,CACHC,OADH,CACW,KADX,EACkB,GADlB,EAEGA,OAFH,CAEW,KAFX,EAEkB,GAFlB,EAGGA,OAHH,CAGW,KAHX,EAGkB,EAHlB,CADF,C,CAMA;;;AACA,MAAMC,QAAQ,GAAIF,MAAD,IACfA,MAAM,CAACC,OAAP,CAAe,IAAf,EAAqB,GAArB,EAA0BA,OAA1B,CAAkC,IAAlC,EAAwC,GAAxC,IACA,KAAKE,SAAL,CAAe,CAAf,EAAmB,IAAIH,MAAM,CAACH,MAAZ,GAAsB,CAAxC,CAFF;;AAIA,SAASO,eAAT,CACEZ,IADF,EAEEa,aAFF,EAGEC,WAHF,EAIEC,MAJF,EAKmB;AACjB,QAAMC,SAAS,GAAGjB,QAAQ,CAACC,IAAD,EAAOc,WAAP,CAA1B;AACA,QAAMG,SAAS,GAAGd,MAAM,CAACe,IAAP,CAChB,kEADgB,EAEhB,KAFgB,CAAlB;AAIA,QAAMC,WAAW,GAAG;AAClBC,IAAAA,OAAO,EAAE,QADS;AAElBJ,IAAAA,SAAS,EAAET,SAAS,CAACS,SAAS,CAACK,QAAV,CAAmB,QAAnB,CAAD,CAFF;AAGlBJ,IAAAA,SAAS,EAAEV,SAAS,CAACU,SAAS,CAACI,QAAV,CAAmB,QAAnB,CAAD,CAHF;AAIlBC,IAAAA,KAAK,EAAEC,QAAQ,CAACC;AAJE,GAApB;AAMAhC,EAAAA,GAAG,CAAC,MAAD,EAAS,QAAQQ,IAAI,CAACqB,QAAL,CAAc,KAAd,CAAjB,CAAH;AACA,SAAOhC,IAAI,CAAC8B,WAAD,EAAcN,aAAa,GAAG,IAA9B,CAAJ,CAAwCY,IAAxC,CAA6CC,QAAQ,IAAI;AAC9D,UAAM;AAAEC,MAAAA;AAAF,QAAoBD,QAA1B;;AACA,QAAI,OAAOC,aAAP,KAAyB,QAA7B,EAAuC;AACrC,YAAMC,IAAI,GAAGzB,MAAM,CAACe,IAAP,CAAYR,QAAQ,CAACiB,aAAD,CAApB,EAAqC,QAArC,CAAb;AACA,UAAIzB,MAAJ;;AACA,UAAI,CAACa,MAAL,EAAa;AACXb,QAAAA,MAAM,GAAG0B,IAAT;AACD,OAFD,MAEO;AACL1B,QAAAA,MAAM,GAAG0B,IAAI,CAACC,KAAL,CAAW,CAAX,CAAT;AACD;;AACDrC,MAAAA,GAAG,CAAC,MAAD,EAAS,QAAQU,MAAM,CAACmB,QAAP,CAAgB,KAAhB,CAAjB,CAAH;AACA,aAAOnB,MAAP;AACD,KAVD,MAUO;AACL,YAAMwB,QAAN;AACD;AACF,GAfM,CAAP;AAgBD;;AAED,IAAII,kBAAkB,GAAG,EAAzB;;AAEA,SAASC,cAAT,GAA0B;AACxBD,EAAAA,kBAAkB,CAACE,OAAnB,CAA2BC,CAAC,IAAIA,CAAC,CAACC,IAAF,CAAO,YAAP,CAAhC;AACAJ,EAAAA,kBAAkB,GAAG,EAArB;AACD;;AAED,SAASK,iBAAT,CAA2BC,QAA3B,EAAqC;AACnC,SAAOA,QAAQ,CAACC,QAAT,CAAkBC,IAAlB,KAA2B,CAAlC;AACD;AAED;;;;;;;;;AAOA,eAAe,MAAMC,YAAN,SAA2BhD,SAA3B,CAA2C;AAGxD;;;AAMA;;;AA+BA;;;AAGA,eAAaiD,IAAb,CAAkBC,CAAlB,EAAwBC,YAAqB,GAAG,IAAhD,EAA6E;AAC3E,WAAO,IAAIH,YAAJ,EAAP;AACD;;AAEDI,EAAAA,WAAW,GAAG;AACZ;AADY,SAXd7B,WAWc;AAAA,SATdC,MASc,GATI,IASJ;AAEZe,IAAAA,kBAAkB,CAACc,IAAnB,CAAwB,IAAxB;AACD;AAED;;;;;;;AAKA,QAAMC,QAAN,CAAe7C,IAAf,EAA8C;AAC5C,QAAI;AACF,aAAO,MAAMY,eAAe,CAC1BZ,IAD0B,EAE1B,KAAK8C,eAFqB,EAG1B,KAAKhC,WAHqB,EAI1B,KAAKC,MAJqB,CAA5B;AAMD,KAPD,CAOE,OAAOgC,CAAP,EAAU;AACV,YAAMC,UAAU,GAAG,OAAOD,CAAC,CAACV,QAAT,KAAsB,QAAzC;;AACA,UAAIW,UAAJ,EAAgB;AACd,YAAIb,iBAAiB,CAACY,CAAD,CAArB,EAA0B;AACxBhB,UAAAA,cAAc;AACf,SAHa,CAId;;;AACA,cAAMrC,qBAAqB,CACzBqD,CADyB,EAEzB,4CAA4CA,CAAC,CAACV,QAAF,CAAWY,IAF9B,EAGzB,SAASF,CAAC,CAACV,QAAF,CAAWC,IAHK,CAA3B;AAKD,OAVD,MAUO;AACL,cAAMS,CAAN;AACD;AACF;AACF;AAED;;;;AAEAG,EAAAA,cAAc,CAACpC,WAAD,EAAsB;AAClC,SAAKA,WAAL,GAAmBX,MAAM,CAACe,IAAP,CAAYJ,WAAZ,EAAyB,OAAzB,CAAnB;AACD;AAED;;;;AAEAqC,EAAAA,SAAS,CAACpC,MAAD,EAAkB;AACzB,SAAKA,MAAL,GAAcA,MAAd;AACD;;AAEDqC,EAAAA,KAAK,GAAkB;AACrB;AACA,WAAOC,OAAO,CAACC,OAAR,EAAP;AACD;;AAlGuD;AAArCf,Y,CACZjD,W,GAAcA,W;;AADFiD,Y,CAKZgB,I,GAAO,MACZ;AACAjE,WAAW,GAAGmC,IAAd,CAAmB+B,SAAS,IAAKA,SAAS,GAAG,CAAC,IAAD,CAAH,GAAY,EAAtD,C;;AAPiBjB,Y,CAWZkB,M,GAAUC,QAAD,IAAiB;AAC/B,MAAIC,YAAY,GAAG,KAAnB;AACArE,EAAAA,WAAW,GAAGmC,IAAd,CAAmB+B,SAAS,IAAI;AAC9B,QAAIG,YAAJ,EAAkB;;AAClB,QAAIH,SAAJ,EAAe;AACbE,MAAAA,QAAQ,CAACE,IAAT,CAAc;AAAEX,QAAAA,IAAI,EAAE,KAAR;AAAeY,QAAAA,UAAU,EAAE;AAA3B,OAAd;AACAH,MAAAA,QAAQ,CAACI,QAAT;AACD,KAHD,MAGO;AACLJ,MAAAA,QAAQ,CAACK,KAAT,CACE,IAAItE,cAAJ,CACE,+CACE,4DADF,GAEE,8CAHJ,EAIE,iBAJF,CADF;AAQD;AACF,GAfD;AAgBA,SAAO;AACLuE,IAAAA,WAAW,EAAE,MAAM;AACjBL,MAAAA,YAAY,GAAG,IAAf;AACD;AAHI,GAAP;AAKD,C","sourcesContent":["//@flow\n\nimport { sign, isSupported } from \"u2f-api\";\nimport Transport from \"@ledgerhq/hw-transport\";\nimport { log } from \"@ledgerhq/logs\";\nimport { TransportError } from \"@ledgerhq/errors\";\n\nfunction wrapU2FTransportError(originalError, message, id) {\n const err = new TransportError(message, id);\n // $FlowFixMe\n err.originalError = originalError;\n return err;\n}\n\nfunction wrapApdu(apdu: Buffer, key: Buffer) {\n const result = Buffer.alloc(apdu.length);\n for (let i = 0; i < apdu.length; i++) {\n result[i] = apdu[i] ^ key[i % key.length];\n }\n return result;\n}\n\n// Convert from normal to web-safe, strip trailing \"=\"s\nconst webSafe64 = (base64: string) =>\n base64\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n\n// Convert from web-safe to normal, add trailing \"=\"s\nconst normal64 = (base64: string) =>\n base64.replace(/-/g, \"+\").replace(/_/g, \"/\") +\n \"==\".substring(0, (3 * base64.length) % 4);\n\nfunction attemptExchange(\n apdu: Buffer,\n timeoutMillis: number,\n scrambleKey: Buffer,\n unwrap: boolean\n): Promise {\n const keyHandle = wrapApdu(apdu, scrambleKey);\n const challenge = Buffer.from(\n \"0000000000000000000000000000000000000000000000000000000000000000\",\n \"hex\"\n );\n const signRequest = {\n version: \"U2F_V2\",\n keyHandle: webSafe64(keyHandle.toString(\"base64\")),\n challenge: webSafe64(challenge.toString(\"base64\")),\n appId: location.origin\n };\n log(\"apdu\", \"=> \" + apdu.toString(\"hex\"));\n return sign(signRequest, timeoutMillis / 1000).then(response => {\n const { signatureData } = response;\n if (typeof signatureData === \"string\") {\n const data = Buffer.from(normal64(signatureData), \"base64\");\n let result;\n if (!unwrap) {\n result = data;\n } else {\n result = data.slice(5);\n }\n log(\"apdu\", \"<= \" + result.toString(\"hex\"));\n return result;\n } else {\n throw response;\n }\n });\n}\n\nlet transportInstances = [];\n\nfunction emitDisconnect() {\n transportInstances.forEach(t => t.emit(\"disconnect\"));\n transportInstances = [];\n}\n\nfunction isTimeoutU2FError(u2fError) {\n return u2fError.metaData.code === 5;\n}\n\n/**\n * U2F web Transport implementation\n * @example\n * import TransportU2F from \"@ledgerhq/hw-transport-u2f\";\n * ...\n * TransportU2F.create().then(transport => ...)\n */\nexport default class TransportU2F extends Transport {\n static isSupported = isSupported;\n\n /*\n */\n static list = (): * =>\n // this transport is not discoverable but we are going to guess if it is here with isSupported()\n isSupported().then(supported => (supported ? [null] : []));\n\n /*\n */\n static listen = (observer: *) => {\n let unsubscribed = false;\n isSupported().then(supported => {\n if (unsubscribed) return;\n if (supported) {\n observer.next({ type: \"add\", descriptor: null });\n observer.complete();\n } else {\n observer.error(\n new TransportError(\n \"U2F browser support is needed for Ledger. \" +\n \"Please use Chrome, Opera or Firefox with a U2F extension. \" +\n \"Also make sure you're on an HTTPS connection\",\n \"U2FNotSupported\"\n )\n );\n }\n });\n return {\n unsubscribe: () => {\n unsubscribed = true;\n }\n };\n };\n\n scrambleKey: Buffer;\n\n unwrap: boolean = true;\n\n /**\n * static function to create a new Transport from a connected Ledger device discoverable via U2F (browser support)\n */\n static async open(_: *, _openTimeout?: number = 5000): Promise {\n return new TransportU2F();\n }\n\n constructor() {\n super();\n transportInstances.push(this);\n }\n\n /**\n * Exchange with the device using APDU protocol.\n * @param apdu\n * @returns a promise of apdu response\n */\n async exchange(apdu: Buffer): Promise {\n try {\n return await attemptExchange(\n apdu,\n this.exchangeTimeout,\n this.scrambleKey,\n this.unwrap\n );\n } catch (e) {\n const isU2FError = typeof e.metaData === \"object\";\n if (isU2FError) {\n if (isTimeoutU2FError(e)) {\n emitDisconnect();\n }\n // the wrapping make error more usable and \"printable\" to the end user.\n throw wrapU2FTransportError(\n e,\n \"Failed to sign with Ledger device: U2F \" + e.metaData.type,\n \"U2F_\" + e.metaData.code\n );\n } else {\n throw e;\n }\n }\n }\n\n /**\n */\n setScrambleKey(scrambleKey: string) {\n this.scrambleKey = Buffer.from(scrambleKey, \"ascii\");\n }\n\n /**\n */\n setUnwrap(unwrap: boolean) {\n this.unwrap = unwrap;\n }\n\n close(): Promise {\n // u2f have no way to clean things up\n return Promise.resolve();\n }\n}\n"],"file":"TransportU2F.js"} -------------------------------------------------------------------------------- /vendor/@ledgerhq/hw-transport/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | [Github](https://github.com/LedgerHQ/ledgerjs/), 4 | [Ledger Devs Slack](https://ledger-dev.slack.com/) 5 | 6 | ## @ledgerhq/hw-transport 7 | 8 | `@ledgerhq/hw-transport` implements the generic interface of a Ledger Hardware Wallet transport. 9 | 10 | ## API 11 | 12 | 13 | 14 | #### Table of Contents 15 | 16 | - [Subscription](#subscription) 17 | - [Properties](#properties) 18 | - [Device](#device) 19 | - [DescriptorEvent](#descriptorevent) 20 | - [Properties](#properties-1) 21 | - [Observer](#observer) 22 | - [Transport](#transport) 23 | - [exchange](#exchange) 24 | - [Parameters](#parameters) 25 | - [setScrambleKey](#setscramblekey) 26 | - [Parameters](#parameters-1) 27 | - [close](#close) 28 | - [on](#on) 29 | - [Parameters](#parameters-2) 30 | - [off](#off) 31 | - [Parameters](#parameters-3) 32 | - [setDebugMode](#setdebugmode) 33 | - [setExchangeTimeout](#setexchangetimeout) 34 | - [Parameters](#parameters-4) 35 | - [setExchangeUnresponsiveTimeout](#setexchangeunresponsivetimeout) 36 | - [Parameters](#parameters-5) 37 | - [send](#send) 38 | - [Parameters](#parameters-6) 39 | - [isSupported](#issupported) 40 | - [list](#list) 41 | - [Examples](#examples) 42 | - [listen](#listen) 43 | - [Parameters](#parameters-7) 44 | - [Examples](#examples-1) 45 | - [open](#open) 46 | - [Parameters](#parameters-8) 47 | - [Examples](#examples-2) 48 | - [create](#create) 49 | - [Parameters](#parameters-9) 50 | - [Examples](#examples-3) 51 | 52 | ### Subscription 53 | 54 | Type: {unsubscribe: function (): void} 55 | 56 | #### Properties 57 | 58 | - `unsubscribe` **function (): void** 59 | 60 | ### Device 61 | 62 | Type: [Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object) 63 | 64 | ### DescriptorEvent 65 | 66 | type: add or remove event 67 | descriptor: a parameter that can be passed to open(descriptor) 68 | deviceModel: device info on the model (is it a nano s, nano x, ...) 69 | device: transport specific device info 70 | 71 | Type: {type: (`"add"` \| `"remove"`), descriptor: Descriptor, deviceModel: DeviceModel??, device: [Device](#device)?} 72 | 73 | #### Properties 74 | 75 | - `type` **(`"add"` \| `"remove"`)** 76 | - `descriptor` **Descriptor** 77 | - `deviceModel` **DeviceModel??** 78 | - `device` **[Device](#device)?** 79 | 80 | ### Observer 81 | 82 | Type: $ReadOnly<{next: function (event: Ev): any, error: function (e: any): any, complete: function (): any}> 83 | 84 | ### Transport 85 | 86 | Transport defines the generic interface to share between node/u2f impl 87 | A **Descriptor** is a parametric type that is up to be determined for the implementation. 88 | it can be for instance an ID, an file path, a URL,... 89 | 90 | #### exchange 91 | 92 | low level api to communicate with the device 93 | This method is for implementations to implement but should not be directly called. 94 | Instead, the recommanded way is to use send() method 95 | 96 | ##### Parameters 97 | 98 | - `_apdu` **[Buffer](https://nodejs.org/api/buffer.html)** 99 | - `apdu` the data to send 100 | 101 | Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Buffer](https://nodejs.org/api/buffer.html)>** a Promise of response data 102 | 103 | #### setScrambleKey 104 | 105 | set the "scramble key" for the next exchanges with the device. 106 | Each App can have a different scramble key and they internally will set it at instanciation. 107 | 108 | ##### Parameters 109 | 110 | - `_key` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** 111 | - `key` the scramble key 112 | 113 | #### close 114 | 115 | close the exchange with the device. 116 | 117 | Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<void>** a Promise that ends when the transport is closed. 118 | 119 | #### on 120 | 121 | Listen to an event on an instance of transport. 122 | Transport implementation can have specific events. Here is the common events: 123 | 124 | - `"disconnect"` : triggered if Transport is disconnected 125 | 126 | ##### Parameters 127 | 128 | - `eventName` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** 129 | - `cb` **[Function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)** 130 | 131 | #### off 132 | 133 | Stop listening to an event on an instance of transport. 134 | 135 | ##### Parameters 136 | 137 | - `eventName` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** 138 | - `cb` **[Function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)** 139 | 140 | #### setDebugMode 141 | 142 | Enable or not logs of the binary exchange 143 | 144 | #### setExchangeTimeout 145 | 146 | Set a timeout (in milliseconds) for the exchange call. Only some transport might implement it. (e.g. U2F) 147 | 148 | ##### Parameters 149 | 150 | - `exchangeTimeout` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** 151 | 152 | #### setExchangeUnresponsiveTimeout 153 | 154 | Define the delay before emitting "unresponsive" on an exchange that does not respond 155 | 156 | ##### Parameters 157 | 158 | - `unresponsiveTimeout` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** 159 | 160 | #### send 161 | 162 | wrapper on top of exchange to simplify work of the implementation. 163 | 164 | ##### Parameters 165 | 166 | - `cla` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** 167 | - `ins` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** 168 | - `p1` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** 169 | - `p2` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** 170 | - `data` **[Buffer](https://nodejs.org/api/buffer.html)** (optional, default `Buffer.alloc(0)`) 171 | - `statusList` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)>** is a list of accepted status code (shorts). [0x9000] by default (optional, default `[StatusCodes.OK]`) 172 | 173 | Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Buffer](https://nodejs.org/api/buffer.html)>** a Promise of response buffer 174 | 175 | #### isSupported 176 | 177 | Statically check if a transport is supported on the user's platform/browser. 178 | 179 | Type: function (): [Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)> 180 | 181 | #### list 182 | 183 | List once all available descriptors. For a better granularity, checkout `listen()`. 184 | 185 | Type: function (): [Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<Descriptor>> 186 | 187 | ##### Examples 188 | 189 | ```javascript 190 | TransportFoo.list().then(descriptors => ...) 191 | ``` 192 | 193 | Returns **any** a promise of descriptors 194 | 195 | #### listen 196 | 197 | Listen all device events for a given Transport. The method takes an Obverver of DescriptorEvent and returns a Subscription (according to Observable paradigm ) 198 | a DescriptorEvent is a `{ descriptor, type }` object. type can be `"add"` or `"remove"` and descriptor is a value you can pass to `open(descriptor)`. 199 | each listen() call will first emit all potential device already connected and then will emit events can come over times, 200 | for instance if you plug a USB device after listen() or a bluetooth device become discoverable. 201 | 202 | Type: function (observer: [Observer](#observer)<[DescriptorEvent](#descriptorevent)<Descriptor>>): [Subscription](#subscription) 203 | 204 | ##### Parameters 205 | 206 | - `observer` is an object with a next, error and complete function (compatible with observer pattern) 207 | 208 | ##### Examples 209 | 210 | ```javascript 211 | const sub = TransportFoo.listen({ 212 | next: e => { 213 | if (e.type==="add") { 214 | sub.unsubscribe(); 215 | const transport = await TransportFoo.open(e.descriptor); 216 | ... 217 | } 218 | }, 219 | error: error => {}, 220 | complete: () => {} 221 | }) 222 | ``` 223 | 224 | Returns **any** a Subscription object on which you can `.unsubscribe()` to stop listening descriptors. 225 | 226 | #### open 227 | 228 | attempt to create a Transport instance with potentially a descriptor. 229 | 230 | Type: function (descriptor: Descriptor, timeout: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)): [Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Transport](#transport)<Descriptor>> 231 | 232 | ##### Parameters 233 | 234 | - `descriptor` : the descriptor to open the transport with. 235 | - `timeout` : an optional timeout 236 | 237 | ##### Examples 238 | 239 | ```javascript 240 | TransportFoo.open(descriptor).then(transport => ...) 241 | ``` 242 | 243 | Returns **any** a Promise of Transport instance 244 | 245 | #### create 246 | 247 | create() allows to open the first descriptor available or 248 | throw if there is none or if timeout is reached. 249 | This is a light helper, alternative to using listen() and open() (that you may need for any more advanced usecase) 250 | 251 | ##### Parameters 252 | 253 | - `openTimeout` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** (optional, default `3000`) 254 | - `listenTimeout` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** 255 | 256 | ##### Examples 257 | 258 | ```javascript 259 | TransportFoo.create().then(transport => ...) 260 | ``` 261 | 262 | Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Transport](#transport)<Descriptor>>** 263 | -------------------------------------------------------------------------------- /templates/send-widget.template.html: -------------------------------------------------------------------------------- 1 |
3 |
4 |
5 |

Send lumens

6 |
7 | 8 |
9 | 10 |
11 |
12 | Loading federation info... 13 |
14 |
15 | Stellar address: {{widget.stellarAddress}} 16 | resolved to: {{widget.destination}} 17 |
18 |
19 | 20 | {{alert.title}} {{alert.text}} 21 | 22 |
23 | 24 | 25 |
26 | 27 | 28 | lumens 29 | 30 |
31 |
32 | 33 | {{alert.title}} 34 | 35 |
36 | 37 |
38 |

39 | Add memo 40 | 41 | What's a memo? 42 | 43 |

44 |
45 | 53 |
54 |
55 | 58 | 66 | 67 |
68 | 73 |
74 |
75 |
76 |
77 | 78 | Note: The specified federation address requires this transaction's memo to be a certain value. 79 | 80 |
81 |
82 | 83 | {{alert.title}} {{alert.text}} 84 | 85 |
86 |
87 | 88 | {{alert.title}} {{alert.text}} 89 | 90 |
91 | 92 | 93 |
94 | 95 | 96 | lumens 97 | 98 |
99 |

100 | 101 | What's a fee? 102 | 103 | 104 | 105 | Normal traffic 106 | 107 | 108 | 109 | Medium congestion! 110 | 111 | 112 | 113 | High congestion! 114 | 115 | 116 | Recommended fee: {{widget.recommendedFee}} lumens 117 |

118 |
119 | 120 | {{alert.title}} 121 | 122 |
123 | 124 | 125 |
126 | 127 |
128 |

You're sending to this public key:

129 |

{{widget.destination}}

130 | 131 |
132 |
Sending
133 |
134 | {{widget.displayedAmount}} 135 |
136 | lumens 137 |
138 |
139 | 140 |
141 |
Memo
142 |
143 | {{widget.memoType}}: 144 |
145 | {{widget.memoValue}} 146 |
147 |
148 |
149 | 150 | {{alert.title}} {{alert.text}} 151 | 152 |
153 | 154 | 155 | 156 | 157 | ← Edit 158 |
159 | 160 |
161 |
162 | Submitting... 163 |
164 |

Check the transaction using your Ledger device.

165 |

Submitting transaction to the network...

166 |
167 | 168 |
169 |
170 |

Transaction successfully completed

171 |
172 |
173 |

Transaction failed

174 | 175 |

176 | Warning We submitted your transaction to the network but because of the network conditions we are 177 | not certain about it's status: it could either succeed or fail. Instead of recreating the transaction you should use 178 | the button below to safely resubmit the transaction:
179 |
180 | 181 |

182 | 183 |

184 | This destination requires a memo. Forgotton memos can lead to delays in receiving your funds, or losing them altogether. Read our community page on missing memos for more information. 185 |

186 | 187 |
{{widget.outcomeMessage}}
188 |
189 | 190 | 191 | 192 |
193 | 194 |
195 |

Requires more signatures

196 | 197 |

This transaction requires additional signatures before it can be submitted.

198 |

Copy the transaction XDR below and add the required signatures before submitting it to the network.

199 | 200 |
{{widget.lastTransactionXDR}}
201 | 202 | 203 |
204 | 205 |
206 | 207 |
208 |
209 | -------------------------------------------------------------------------------- /vendor/@ledgerhq/hw-transport/src/Transport.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import EventEmitter from "events"; 4 | import type { DeviceModel } from "@ledgerhq/devices"; 5 | import { 6 | TransportRaceCondition, 7 | TransportError, 8 | StatusCodes, 9 | getAltStatusMessage, 10 | TransportStatusError 11 | } from "@ledgerhq/errors"; 12 | 13 | export { 14 | TransportError, 15 | TransportStatusError, 16 | StatusCodes, 17 | getAltStatusMessage 18 | }; 19 | 20 | /** 21 | */ 22 | export type Subscription = { unsubscribe: () => void }; 23 | 24 | /** 25 | */ 26 | export type Device = Object; 27 | 28 | /** 29 | * type: add or remove event 30 | * descriptor: a parameter that can be passed to open(descriptor) 31 | * deviceModel: device info on the model (is it a nano s, nano x, ...) 32 | * device: transport specific device info 33 | */ 34 | export type DescriptorEvent = { 35 | type: "add" | "remove", 36 | descriptor: Descriptor, 37 | deviceModel?: ?DeviceModel, 38 | device?: Device 39 | }; 40 | /** 41 | */ 42 | export type Observer = $ReadOnly<{ 43 | next: (event: Ev) => mixed, 44 | error: (e: any) => mixed, 45 | complete: () => mixed 46 | }>; 47 | 48 | /** 49 | * Transport defines the generic interface to share between node/u2f impl 50 | * A **Descriptor** is a parametric type that is up to be determined for the implementation. 51 | * it can be for instance an ID, an file path, a URL,... 52 | */ 53 | export default class Transport { 54 | exchangeTimeout: number = 30000; 55 | unresponsiveTimeout: number = 15000; 56 | 57 | /** 58 | * Statically check if a transport is supported on the user's platform/browser. 59 | */ 60 | static +isSupported: () => Promise; 61 | 62 | /** 63 | * List once all available descriptors. For a better granularity, checkout `listen()`. 64 | * @return a promise of descriptors 65 | * @example 66 | * TransportFoo.list().then(descriptors => ...) 67 | */ 68 | static +list: () => Promise>; 69 | 70 | /** 71 | * Listen all device events for a given Transport. The method takes an Obverver of DescriptorEvent and returns a Subscription (according to Observable paradigm https://github.com/tc39/proposal-observable ) 72 | * a DescriptorEvent is a `{ descriptor, type }` object. type can be `"add"` or `"remove"` and descriptor is a value you can pass to `open(descriptor)`. 73 | * each listen() call will first emit all potential device already connected and then will emit events can come over times, 74 | * for instance if you plug a USB device after listen() or a bluetooth device become discoverable. 75 | * @param observer is an object with a next, error and complete function (compatible with observer pattern) 76 | * @return a Subscription object on which you can `.unsubscribe()` to stop listening descriptors. 77 | * @example 78 | const sub = TransportFoo.listen({ 79 | next: e => { 80 | if (e.type==="add") { 81 | sub.unsubscribe(); 82 | const transport = await TransportFoo.open(e.descriptor); 83 | ... 84 | } 85 | }, 86 | error: error => {}, 87 | complete: () => {} 88 | }) 89 | */ 90 | static +listen: ( 91 | observer: Observer> 92 | ) => Subscription; 93 | 94 | /** 95 | * attempt to create a Transport instance with potentially a descriptor. 96 | * @param descriptor: the descriptor to open the transport with. 97 | * @param timeout: an optional timeout 98 | * @return a Promise of Transport instance 99 | * @example 100 | TransportFoo.open(descriptor).then(transport => ...) 101 | */ 102 | static +open: ( 103 | descriptor: Descriptor, 104 | timeout?: number 105 | ) => Promise>; 106 | 107 | /** 108 | * low level api to communicate with the device 109 | * This method is for implementations to implement but should not be directly called. 110 | * Instead, the recommanded way is to use send() method 111 | * @param apdu the data to send 112 | * @return a Promise of response data 113 | */ 114 | exchange(_apdu: Buffer): Promise { 115 | throw new Error("exchange not implemented"); 116 | } 117 | 118 | /** 119 | * set the "scramble key" for the next exchanges with the device. 120 | * Each App can have a different scramble key and they internally will set it at instanciation. 121 | * @param key the scramble key 122 | */ 123 | setScrambleKey(_key: string) {} 124 | 125 | /** 126 | * close the exchange with the device. 127 | * @return a Promise that ends when the transport is closed. 128 | */ 129 | close(): Promise { 130 | return Promise.resolve(); 131 | } 132 | 133 | _events = new EventEmitter(); 134 | 135 | /** 136 | * Listen to an event on an instance of transport. 137 | * Transport implementation can have specific events. Here is the common events: 138 | * * `"disconnect"` : triggered if Transport is disconnected 139 | */ 140 | on(eventName: string, cb: Function) { 141 | this._events.on(eventName, cb); 142 | } 143 | 144 | /** 145 | * Stop listening to an event on an instance of transport. 146 | */ 147 | off(eventName: string, cb: Function) { 148 | this._events.removeListener(eventName, cb); 149 | } 150 | 151 | emit(event: string, ...args: *) { 152 | this._events.emit(event, ...args); 153 | } 154 | 155 | /** 156 | * Enable or not logs of the binary exchange 157 | */ 158 | setDebugMode() { 159 | console.warn( 160 | "setDebugMode is deprecated. use @ledgerhq/logs instead. No logs are emitted in this anymore." 161 | ); 162 | } 163 | 164 | /** 165 | * Set a timeout (in milliseconds) for the exchange call. Only some transport might implement it. (e.g. U2F) 166 | */ 167 | setExchangeTimeout(exchangeTimeout: number) { 168 | this.exchangeTimeout = exchangeTimeout; 169 | } 170 | 171 | /** 172 | * Define the delay before emitting "unresponsive" on an exchange that does not respond 173 | */ 174 | setExchangeUnresponsiveTimeout(unresponsiveTimeout: number) { 175 | this.unresponsiveTimeout = unresponsiveTimeout; 176 | } 177 | 178 | /** 179 | * wrapper on top of exchange to simplify work of the implementation. 180 | * @param cla 181 | * @param ins 182 | * @param p1 183 | * @param p2 184 | * @param data 185 | * @param statusList is a list of accepted status code (shorts). [0x9000] by default 186 | * @return a Promise of response buffer 187 | */ 188 | send = async ( 189 | cla: number, 190 | ins: number, 191 | p1: number, 192 | p2: number, 193 | data: Buffer = Buffer.alloc(0), 194 | statusList: Array = [StatusCodes.OK] 195 | ): Promise => { 196 | if (data.length >= 256) { 197 | throw new TransportError( 198 | "data.length exceed 256 bytes limit. Got: " + data.length, 199 | "DataLengthTooBig" 200 | ); 201 | } 202 | const response = await this.exchange( 203 | Buffer.concat([ 204 | Buffer.from([cla, ins, p1, p2]), 205 | Buffer.from([data.length]), 206 | data 207 | ]) 208 | ); 209 | const sw = response.readUInt16BE(response.length - 2); 210 | if (!statusList.some(s => s === sw)) { 211 | throw new TransportStatusError(sw); 212 | } 213 | return response; 214 | }; 215 | 216 | /** 217 | * create() allows to open the first descriptor available or 218 | * throw if there is none or if timeout is reached. 219 | * This is a light helper, alternative to using listen() and open() (that you may need for any more advanced usecase) 220 | * @example 221 | TransportFoo.create().then(transport => ...) 222 | */ 223 | static create( 224 | openTimeout?: number = 3000, 225 | listenTimeout?: number 226 | ): Promise> { 227 | return new Promise((resolve, reject) => { 228 | let found = false; 229 | const sub = this.listen({ 230 | next: e => { 231 | found = true; 232 | if (sub) sub.unsubscribe(); 233 | if (listenTimeoutId) clearTimeout(listenTimeoutId); 234 | this.open(e.descriptor, openTimeout).then(resolve, reject); 235 | }, 236 | error: e => { 237 | if (listenTimeoutId) clearTimeout(listenTimeoutId); 238 | reject(e); 239 | }, 240 | complete: () => { 241 | if (listenTimeoutId) clearTimeout(listenTimeoutId); 242 | if (!found) { 243 | reject( 244 | new TransportError( 245 | this.ErrorMessage_NoDeviceFound, 246 | "NoDeviceFound" 247 | ) 248 | ); 249 | } 250 | } 251 | }); 252 | const listenTimeoutId = listenTimeout 253 | ? setTimeout(() => { 254 | sub.unsubscribe(); 255 | reject( 256 | new TransportError( 257 | this.ErrorMessage_ListenTimeout, 258 | "ListenTimeout" 259 | ) 260 | ); 261 | }, listenTimeout) 262 | : null; 263 | }); 264 | } 265 | 266 | exchangeBusyPromise: ?Promise; 267 | 268 | // $FlowFixMe 269 | exchangeAtomicImpl = async f => { 270 | if (this.exchangeBusyPromise) { 271 | throw new TransportRaceCondition( 272 | "An action was already pending on the Ledger device. Please deny or reconnect." 273 | ); 274 | } 275 | let resolveBusy; 276 | const busyPromise = new Promise(r => { 277 | resolveBusy = r; 278 | }); 279 | this.exchangeBusyPromise = busyPromise; 280 | let unresponsiveReached = false; 281 | const timeout = setTimeout(() => { 282 | unresponsiveReached = true; 283 | this.emit("unresponsive"); 284 | }, this.unresponsiveTimeout); 285 | try { 286 | const res = await f(); 287 | if (unresponsiveReached) { 288 | this.emit("responsive"); 289 | } 290 | return res; 291 | } finally { 292 | clearTimeout(timeout); 293 | if (resolveBusy) resolveBusy(); 294 | this.exchangeBusyPromise = null; 295 | } 296 | }; 297 | 298 | decorateAppAPIMethods( 299 | self: Object, 300 | methods: Array, 301 | scrambleKey: string 302 | ) { 303 | for (let methodName of methods) { 304 | self[methodName] = this.decorateAppAPIMethod( 305 | methodName, 306 | self[methodName], 307 | self, 308 | scrambleKey 309 | ); 310 | } 311 | } 312 | 313 | _appAPIlock = null; 314 | decorateAppAPIMethod( 315 | methodName: string, 316 | f: (...args: A) => Promise, 317 | ctx: *, 318 | scrambleKey: string 319 | ): (...args: A) => Promise { 320 | return async (...args) => { 321 | const { _appAPIlock } = this; 322 | if (_appAPIlock) { 323 | return Promise.reject( 324 | new TransportError( 325 | "Ledger Device is busy (lock " + _appAPIlock + ")", 326 | "TransportLocked" 327 | ) 328 | ); 329 | } 330 | try { 331 | this._appAPIlock = methodName; 332 | this.setScrambleKey(scrambleKey); 333 | return await f.apply(ctx, args); 334 | } finally { 335 | this._appAPIlock = null; 336 | } 337 | }; 338 | } 339 | 340 | static ErrorMessage_ListenTimeout = "No Ledger device found (timeout)"; 341 | static ErrorMessage_NoDeviceFound = "No Ledger device found"; 342 | } 343 | -------------------------------------------------------------------------------- /vendor/@ledgerhq/hw-transport/lib/Transport.js.flow: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import EventEmitter from "events"; 4 | import type { DeviceModel } from "@ledgerhq/devices"; 5 | import { 6 | TransportRaceCondition, 7 | TransportError, 8 | StatusCodes, 9 | getAltStatusMessage, 10 | TransportStatusError 11 | } from "@ledgerhq/errors"; 12 | 13 | export { 14 | TransportError, 15 | TransportStatusError, 16 | StatusCodes, 17 | getAltStatusMessage 18 | }; 19 | 20 | /** 21 | */ 22 | export type Subscription = { unsubscribe: () => void }; 23 | 24 | /** 25 | */ 26 | export type Device = Object; 27 | 28 | /** 29 | * type: add or remove event 30 | * descriptor: a parameter that can be passed to open(descriptor) 31 | * deviceModel: device info on the model (is it a nano s, nano x, ...) 32 | * device: transport specific device info 33 | */ 34 | export type DescriptorEvent = { 35 | type: "add" | "remove", 36 | descriptor: Descriptor, 37 | deviceModel?: ?DeviceModel, 38 | device?: Device 39 | }; 40 | /** 41 | */ 42 | export type Observer = $ReadOnly<{ 43 | next: (event: Ev) => mixed, 44 | error: (e: any) => mixed, 45 | complete: () => mixed 46 | }>; 47 | 48 | /** 49 | * Transport defines the generic interface to share between node/u2f impl 50 | * A **Descriptor** is a parametric type that is up to be determined for the implementation. 51 | * it can be for instance an ID, an file path, a URL,... 52 | */ 53 | export default class Transport { 54 | exchangeTimeout: number = 30000; 55 | unresponsiveTimeout: number = 15000; 56 | 57 | /** 58 | * Statically check if a transport is supported on the user's platform/browser. 59 | */ 60 | static +isSupported: () => Promise; 61 | 62 | /** 63 | * List once all available descriptors. For a better granularity, checkout `listen()`. 64 | * @return a promise of descriptors 65 | * @example 66 | * TransportFoo.list().then(descriptors => ...) 67 | */ 68 | static +list: () => Promise>; 69 | 70 | /** 71 | * Listen all device events for a given Transport. The method takes an Obverver of DescriptorEvent and returns a Subscription (according to Observable paradigm https://github.com/tc39/proposal-observable ) 72 | * a DescriptorEvent is a `{ descriptor, type }` object. type can be `"add"` or `"remove"` and descriptor is a value you can pass to `open(descriptor)`. 73 | * each listen() call will first emit all potential device already connected and then will emit events can come over times, 74 | * for instance if you plug a USB device after listen() or a bluetooth device become discoverable. 75 | * @param observer is an object with a next, error and complete function (compatible with observer pattern) 76 | * @return a Subscription object on which you can `.unsubscribe()` to stop listening descriptors. 77 | * @example 78 | const sub = TransportFoo.listen({ 79 | next: e => { 80 | if (e.type==="add") { 81 | sub.unsubscribe(); 82 | const transport = await TransportFoo.open(e.descriptor); 83 | ... 84 | } 85 | }, 86 | error: error => {}, 87 | complete: () => {} 88 | }) 89 | */ 90 | static +listen: ( 91 | observer: Observer> 92 | ) => Subscription; 93 | 94 | /** 95 | * attempt to create a Transport instance with potentially a descriptor. 96 | * @param descriptor: the descriptor to open the transport with. 97 | * @param timeout: an optional timeout 98 | * @return a Promise of Transport instance 99 | * @example 100 | TransportFoo.open(descriptor).then(transport => ...) 101 | */ 102 | static +open: ( 103 | descriptor: Descriptor, 104 | timeout?: number 105 | ) => Promise>; 106 | 107 | /** 108 | * low level api to communicate with the device 109 | * This method is for implementations to implement but should not be directly called. 110 | * Instead, the recommanded way is to use send() method 111 | * @param apdu the data to send 112 | * @return a Promise of response data 113 | */ 114 | exchange(_apdu: Buffer): Promise { 115 | throw new Error("exchange not implemented"); 116 | } 117 | 118 | /** 119 | * set the "scramble key" for the next exchanges with the device. 120 | * Each App can have a different scramble key and they internally will set it at instanciation. 121 | * @param key the scramble key 122 | */ 123 | setScrambleKey(_key: string) {} 124 | 125 | /** 126 | * close the exchange with the device. 127 | * @return a Promise that ends when the transport is closed. 128 | */ 129 | close(): Promise { 130 | return Promise.resolve(); 131 | } 132 | 133 | _events = new EventEmitter(); 134 | 135 | /** 136 | * Listen to an event on an instance of transport. 137 | * Transport implementation can have specific events. Here is the common events: 138 | * * `"disconnect"` : triggered if Transport is disconnected 139 | */ 140 | on(eventName: string, cb: Function) { 141 | this._events.on(eventName, cb); 142 | } 143 | 144 | /** 145 | * Stop listening to an event on an instance of transport. 146 | */ 147 | off(eventName: string, cb: Function) { 148 | this._events.removeListener(eventName, cb); 149 | } 150 | 151 | emit(event: string, ...args: *) { 152 | this._events.emit(event, ...args); 153 | } 154 | 155 | /** 156 | * Enable or not logs of the binary exchange 157 | */ 158 | setDebugMode() { 159 | console.warn( 160 | "setDebugMode is deprecated. use @ledgerhq/logs instead. No logs are emitted in this anymore." 161 | ); 162 | } 163 | 164 | /** 165 | * Set a timeout (in milliseconds) for the exchange call. Only some transport might implement it. (e.g. U2F) 166 | */ 167 | setExchangeTimeout(exchangeTimeout: number) { 168 | this.exchangeTimeout = exchangeTimeout; 169 | } 170 | 171 | /** 172 | * Define the delay before emitting "unresponsive" on an exchange that does not respond 173 | */ 174 | setExchangeUnresponsiveTimeout(unresponsiveTimeout: number) { 175 | this.unresponsiveTimeout = unresponsiveTimeout; 176 | } 177 | 178 | /** 179 | * wrapper on top of exchange to simplify work of the implementation. 180 | * @param cla 181 | * @param ins 182 | * @param p1 183 | * @param p2 184 | * @param data 185 | * @param statusList is a list of accepted status code (shorts). [0x9000] by default 186 | * @return a Promise of response buffer 187 | */ 188 | send = async ( 189 | cla: number, 190 | ins: number, 191 | p1: number, 192 | p2: number, 193 | data: Buffer = Buffer.alloc(0), 194 | statusList: Array = [StatusCodes.OK] 195 | ): Promise => { 196 | if (data.length >= 256) { 197 | throw new TransportError( 198 | "data.length exceed 256 bytes limit. Got: " + data.length, 199 | "DataLengthTooBig" 200 | ); 201 | } 202 | const response = await this.exchange( 203 | Buffer.concat([ 204 | Buffer.from([cla, ins, p1, p2]), 205 | Buffer.from([data.length]), 206 | data 207 | ]) 208 | ); 209 | const sw = response.readUInt16BE(response.length - 2); 210 | if (!statusList.some(s => s === sw)) { 211 | throw new TransportStatusError(sw); 212 | } 213 | return response; 214 | }; 215 | 216 | /** 217 | * create() allows to open the first descriptor available or 218 | * throw if there is none or if timeout is reached. 219 | * This is a light helper, alternative to using listen() and open() (that you may need for any more advanced usecase) 220 | * @example 221 | TransportFoo.create().then(transport => ...) 222 | */ 223 | static create( 224 | openTimeout?: number = 3000, 225 | listenTimeout?: number 226 | ): Promise> { 227 | return new Promise((resolve, reject) => { 228 | let found = false; 229 | const sub = this.listen({ 230 | next: e => { 231 | found = true; 232 | if (sub) sub.unsubscribe(); 233 | if (listenTimeoutId) clearTimeout(listenTimeoutId); 234 | this.open(e.descriptor, openTimeout).then(resolve, reject); 235 | }, 236 | error: e => { 237 | if (listenTimeoutId) clearTimeout(listenTimeoutId); 238 | reject(e); 239 | }, 240 | complete: () => { 241 | if (listenTimeoutId) clearTimeout(listenTimeoutId); 242 | if (!found) { 243 | reject( 244 | new TransportError( 245 | this.ErrorMessage_NoDeviceFound, 246 | "NoDeviceFound" 247 | ) 248 | ); 249 | } 250 | } 251 | }); 252 | const listenTimeoutId = listenTimeout 253 | ? setTimeout(() => { 254 | sub.unsubscribe(); 255 | reject( 256 | new TransportError( 257 | this.ErrorMessage_ListenTimeout, 258 | "ListenTimeout" 259 | ) 260 | ); 261 | }, listenTimeout) 262 | : null; 263 | }); 264 | } 265 | 266 | exchangeBusyPromise: ?Promise; 267 | 268 | // $FlowFixMe 269 | exchangeAtomicImpl = async f => { 270 | if (this.exchangeBusyPromise) { 271 | throw new TransportRaceCondition( 272 | "An action was already pending on the Ledger device. Please deny or reconnect." 273 | ); 274 | } 275 | let resolveBusy; 276 | const busyPromise = new Promise(r => { 277 | resolveBusy = r; 278 | }); 279 | this.exchangeBusyPromise = busyPromise; 280 | let unresponsiveReached = false; 281 | const timeout = setTimeout(() => { 282 | unresponsiveReached = true; 283 | this.emit("unresponsive"); 284 | }, this.unresponsiveTimeout); 285 | try { 286 | const res = await f(); 287 | if (unresponsiveReached) { 288 | this.emit("responsive"); 289 | } 290 | return res; 291 | } finally { 292 | clearTimeout(timeout); 293 | if (resolveBusy) resolveBusy(); 294 | this.exchangeBusyPromise = null; 295 | } 296 | }; 297 | 298 | decorateAppAPIMethods( 299 | self: Object, 300 | methods: Array, 301 | scrambleKey: string 302 | ) { 303 | for (let methodName of methods) { 304 | self[methodName] = this.decorateAppAPIMethod( 305 | methodName, 306 | self[methodName], 307 | self, 308 | scrambleKey 309 | ); 310 | } 311 | } 312 | 313 | _appAPIlock = null; 314 | decorateAppAPIMethod( 315 | methodName: string, 316 | f: (...args: A) => Promise, 317 | ctx: *, 318 | scrambleKey: string 319 | ): (...args: A) => Promise { 320 | return async (...args) => { 321 | const { _appAPIlock } = this; 322 | if (_appAPIlock) { 323 | return Promise.reject( 324 | new TransportError( 325 | "Ledger Device is busy (lock " + _appAPIlock + ")", 326 | "TransportLocked" 327 | ) 328 | ); 329 | } 330 | try { 331 | this._appAPIlock = methodName; 332 | this.setScrambleKey(scrambleKey); 333 | return await f.apply(ctx, args); 334 | } finally { 335 | this._appAPIlock = null; 336 | } 337 | }; 338 | } 339 | 340 | static ErrorMessage_ListenTimeout = "No Ledger device found (timeout)"; 341 | static ErrorMessage_NoDeviceFound = "No Ledger device found"; 342 | } 343 | -------------------------------------------------------------------------------- /vendor/@ledgerhq/hw-transport/lib-es/Transport.js.flow: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import EventEmitter from "events"; 4 | import type { DeviceModel } from "@ledgerhq/devices"; 5 | import { 6 | TransportRaceCondition, 7 | TransportError, 8 | StatusCodes, 9 | getAltStatusMessage, 10 | TransportStatusError 11 | } from "@ledgerhq/errors"; 12 | 13 | export { 14 | TransportError, 15 | TransportStatusError, 16 | StatusCodes, 17 | getAltStatusMessage 18 | }; 19 | 20 | /** 21 | */ 22 | export type Subscription = { unsubscribe: () => void }; 23 | 24 | /** 25 | */ 26 | export type Device = Object; 27 | 28 | /** 29 | * type: add or remove event 30 | * descriptor: a parameter that can be passed to open(descriptor) 31 | * deviceModel: device info on the model (is it a nano s, nano x, ...) 32 | * device: transport specific device info 33 | */ 34 | export type DescriptorEvent = { 35 | type: "add" | "remove", 36 | descriptor: Descriptor, 37 | deviceModel?: ?DeviceModel, 38 | device?: Device 39 | }; 40 | /** 41 | */ 42 | export type Observer = $ReadOnly<{ 43 | next: (event: Ev) => mixed, 44 | error: (e: any) => mixed, 45 | complete: () => mixed 46 | }>; 47 | 48 | /** 49 | * Transport defines the generic interface to share between node/u2f impl 50 | * A **Descriptor** is a parametric type that is up to be determined for the implementation. 51 | * it can be for instance an ID, an file path, a URL,... 52 | */ 53 | export default class Transport { 54 | exchangeTimeout: number = 30000; 55 | unresponsiveTimeout: number = 15000; 56 | 57 | /** 58 | * Statically check if a transport is supported on the user's platform/browser. 59 | */ 60 | static +isSupported: () => Promise; 61 | 62 | /** 63 | * List once all available descriptors. For a better granularity, checkout `listen()`. 64 | * @return a promise of descriptors 65 | * @example 66 | * TransportFoo.list().then(descriptors => ...) 67 | */ 68 | static +list: () => Promise>; 69 | 70 | /** 71 | * Listen all device events for a given Transport. The method takes an Obverver of DescriptorEvent and returns a Subscription (according to Observable paradigm https://github.com/tc39/proposal-observable ) 72 | * a DescriptorEvent is a `{ descriptor, type }` object. type can be `"add"` or `"remove"` and descriptor is a value you can pass to `open(descriptor)`. 73 | * each listen() call will first emit all potential device already connected and then will emit events can come over times, 74 | * for instance if you plug a USB device after listen() or a bluetooth device become discoverable. 75 | * @param observer is an object with a next, error and complete function (compatible with observer pattern) 76 | * @return a Subscription object on which you can `.unsubscribe()` to stop listening descriptors. 77 | * @example 78 | const sub = TransportFoo.listen({ 79 | next: e => { 80 | if (e.type==="add") { 81 | sub.unsubscribe(); 82 | const transport = await TransportFoo.open(e.descriptor); 83 | ... 84 | } 85 | }, 86 | error: error => {}, 87 | complete: () => {} 88 | }) 89 | */ 90 | static +listen: ( 91 | observer: Observer> 92 | ) => Subscription; 93 | 94 | /** 95 | * attempt to create a Transport instance with potentially a descriptor. 96 | * @param descriptor: the descriptor to open the transport with. 97 | * @param timeout: an optional timeout 98 | * @return a Promise of Transport instance 99 | * @example 100 | TransportFoo.open(descriptor).then(transport => ...) 101 | */ 102 | static +open: ( 103 | descriptor: Descriptor, 104 | timeout?: number 105 | ) => Promise>; 106 | 107 | /** 108 | * low level api to communicate with the device 109 | * This method is for implementations to implement but should not be directly called. 110 | * Instead, the recommanded way is to use send() method 111 | * @param apdu the data to send 112 | * @return a Promise of response data 113 | */ 114 | exchange(_apdu: Buffer): Promise { 115 | throw new Error("exchange not implemented"); 116 | } 117 | 118 | /** 119 | * set the "scramble key" for the next exchanges with the device. 120 | * Each App can have a different scramble key and they internally will set it at instanciation. 121 | * @param key the scramble key 122 | */ 123 | setScrambleKey(_key: string) {} 124 | 125 | /** 126 | * close the exchange with the device. 127 | * @return a Promise that ends when the transport is closed. 128 | */ 129 | close(): Promise { 130 | return Promise.resolve(); 131 | } 132 | 133 | _events = new EventEmitter(); 134 | 135 | /** 136 | * Listen to an event on an instance of transport. 137 | * Transport implementation can have specific events. Here is the common events: 138 | * * `"disconnect"` : triggered if Transport is disconnected 139 | */ 140 | on(eventName: string, cb: Function) { 141 | this._events.on(eventName, cb); 142 | } 143 | 144 | /** 145 | * Stop listening to an event on an instance of transport. 146 | */ 147 | off(eventName: string, cb: Function) { 148 | this._events.removeListener(eventName, cb); 149 | } 150 | 151 | emit(event: string, ...args: *) { 152 | this._events.emit(event, ...args); 153 | } 154 | 155 | /** 156 | * Enable or not logs of the binary exchange 157 | */ 158 | setDebugMode() { 159 | console.warn( 160 | "setDebugMode is deprecated. use @ledgerhq/logs instead. No logs are emitted in this anymore." 161 | ); 162 | } 163 | 164 | /** 165 | * Set a timeout (in milliseconds) for the exchange call. Only some transport might implement it. (e.g. U2F) 166 | */ 167 | setExchangeTimeout(exchangeTimeout: number) { 168 | this.exchangeTimeout = exchangeTimeout; 169 | } 170 | 171 | /** 172 | * Define the delay before emitting "unresponsive" on an exchange that does not respond 173 | */ 174 | setExchangeUnresponsiveTimeout(unresponsiveTimeout: number) { 175 | this.unresponsiveTimeout = unresponsiveTimeout; 176 | } 177 | 178 | /** 179 | * wrapper on top of exchange to simplify work of the implementation. 180 | * @param cla 181 | * @param ins 182 | * @param p1 183 | * @param p2 184 | * @param data 185 | * @param statusList is a list of accepted status code (shorts). [0x9000] by default 186 | * @return a Promise of response buffer 187 | */ 188 | send = async ( 189 | cla: number, 190 | ins: number, 191 | p1: number, 192 | p2: number, 193 | data: Buffer = Buffer.alloc(0), 194 | statusList: Array = [StatusCodes.OK] 195 | ): Promise => { 196 | if (data.length >= 256) { 197 | throw new TransportError( 198 | "data.length exceed 256 bytes limit. Got: " + data.length, 199 | "DataLengthTooBig" 200 | ); 201 | } 202 | const response = await this.exchange( 203 | Buffer.concat([ 204 | Buffer.from([cla, ins, p1, p2]), 205 | Buffer.from([data.length]), 206 | data 207 | ]) 208 | ); 209 | const sw = response.readUInt16BE(response.length - 2); 210 | if (!statusList.some(s => s === sw)) { 211 | throw new TransportStatusError(sw); 212 | } 213 | return response; 214 | }; 215 | 216 | /** 217 | * create() allows to open the first descriptor available or 218 | * throw if there is none or if timeout is reached. 219 | * This is a light helper, alternative to using listen() and open() (that you may need for any more advanced usecase) 220 | * @example 221 | TransportFoo.create().then(transport => ...) 222 | */ 223 | static create( 224 | openTimeout?: number = 3000, 225 | listenTimeout?: number 226 | ): Promise> { 227 | return new Promise((resolve, reject) => { 228 | let found = false; 229 | const sub = this.listen({ 230 | next: e => { 231 | found = true; 232 | if (sub) sub.unsubscribe(); 233 | if (listenTimeoutId) clearTimeout(listenTimeoutId); 234 | this.open(e.descriptor, openTimeout).then(resolve, reject); 235 | }, 236 | error: e => { 237 | if (listenTimeoutId) clearTimeout(listenTimeoutId); 238 | reject(e); 239 | }, 240 | complete: () => { 241 | if (listenTimeoutId) clearTimeout(listenTimeoutId); 242 | if (!found) { 243 | reject( 244 | new TransportError( 245 | this.ErrorMessage_NoDeviceFound, 246 | "NoDeviceFound" 247 | ) 248 | ); 249 | } 250 | } 251 | }); 252 | const listenTimeoutId = listenTimeout 253 | ? setTimeout(() => { 254 | sub.unsubscribe(); 255 | reject( 256 | new TransportError( 257 | this.ErrorMessage_ListenTimeout, 258 | "ListenTimeout" 259 | ) 260 | ); 261 | }, listenTimeout) 262 | : null; 263 | }); 264 | } 265 | 266 | exchangeBusyPromise: ?Promise; 267 | 268 | // $FlowFixMe 269 | exchangeAtomicImpl = async f => { 270 | if (this.exchangeBusyPromise) { 271 | throw new TransportRaceCondition( 272 | "An action was already pending on the Ledger device. Please deny or reconnect." 273 | ); 274 | } 275 | let resolveBusy; 276 | const busyPromise = new Promise(r => { 277 | resolveBusy = r; 278 | }); 279 | this.exchangeBusyPromise = busyPromise; 280 | let unresponsiveReached = false; 281 | const timeout = setTimeout(() => { 282 | unresponsiveReached = true; 283 | this.emit("unresponsive"); 284 | }, this.unresponsiveTimeout); 285 | try { 286 | const res = await f(); 287 | if (unresponsiveReached) { 288 | this.emit("responsive"); 289 | } 290 | return res; 291 | } finally { 292 | clearTimeout(timeout); 293 | if (resolveBusy) resolveBusy(); 294 | this.exchangeBusyPromise = null; 295 | } 296 | }; 297 | 298 | decorateAppAPIMethods( 299 | self: Object, 300 | methods: Array, 301 | scrambleKey: string 302 | ) { 303 | for (let methodName of methods) { 304 | self[methodName] = this.decorateAppAPIMethod( 305 | methodName, 306 | self[methodName], 307 | self, 308 | scrambleKey 309 | ); 310 | } 311 | } 312 | 313 | _appAPIlock = null; 314 | decorateAppAPIMethod( 315 | methodName: string, 316 | f: (...args: A) => Promise, 317 | ctx: *, 318 | scrambleKey: string 319 | ): (...args: A) => Promise { 320 | return async (...args) => { 321 | const { _appAPIlock } = this; 322 | if (_appAPIlock) { 323 | return Promise.reject( 324 | new TransportError( 325 | "Ledger Device is busy (lock " + _appAPIlock + ")", 326 | "TransportLocked" 327 | ) 328 | ); 329 | } 330 | try { 331 | this._appAPIlock = methodName; 332 | this.setScrambleKey(scrambleKey); 333 | return await f.apply(ctx, args); 334 | } finally { 335 | this._appAPIlock = null; 336 | } 337 | }; 338 | } 339 | 340 | static ErrorMessage_ListenTimeout = "No Ledger device found (timeout)"; 341 | static ErrorMessage_NoDeviceFound = "No Ledger device found"; 342 | } 343 | -------------------------------------------------------------------------------- /vendor/@ledgerhq/hw-transport-u2f/lib/TransportU2F.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = void 0; 7 | 8 | var _u2fApi = require("u2f-api"); 9 | 10 | var _hwTransport = _interopRequireDefault(require("../../hw-transport")); 11 | 12 | var _logs = require("@ledgerhq/logs"); 13 | 14 | var _errors = require("@ledgerhq/errors"); 15 | 16 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 17 | 18 | function wrapU2FTransportError(originalError, message, id) { 19 | const err = new _errors.TransportError(message, id); // $FlowFixMe 20 | 21 | err.originalError = originalError; 22 | return err; 23 | } 24 | 25 | function wrapApdu(apdu, key) { 26 | const result = Buffer.alloc(apdu.length); 27 | 28 | for (let i = 0; i < apdu.length; i++) { 29 | result[i] = apdu[i] ^ key[i % key.length]; 30 | } 31 | 32 | return result; 33 | } // Convert from normal to web-safe, strip trailing "="s 34 | 35 | 36 | const webSafe64 = base64 => base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); // Convert from web-safe to normal, add trailing "="s 37 | 38 | 39 | const normal64 = base64 => base64.replace(/-/g, "+").replace(/_/g, "/") + "==".substring(0, 3 * base64.length % 4); 40 | 41 | function attemptExchange(apdu, timeoutMillis, scrambleKey, unwrap) { 42 | const keyHandle = wrapApdu(apdu, scrambleKey); 43 | const challenge = Buffer.from("0000000000000000000000000000000000000000000000000000000000000000", "hex"); 44 | const signRequest = { 45 | version: "U2F_V2", 46 | keyHandle: webSafe64(keyHandle.toString("base64")), 47 | challenge: webSafe64(challenge.toString("base64")), 48 | appId: location.origin 49 | }; 50 | (0, _logs.log)("apdu", "=> " + apdu.toString("hex")); 51 | return (0, _u2fApi.sign)(signRequest, timeoutMillis / 1000).then(response => { 52 | const { 53 | signatureData 54 | } = response; 55 | 56 | if (typeof signatureData === "string") { 57 | const data = Buffer.from(normal64(signatureData), "base64"); 58 | let result; 59 | 60 | if (!unwrap) { 61 | result = data; 62 | } else { 63 | result = data.slice(5); 64 | } 65 | 66 | (0, _logs.log)("apdu", "<= " + result.toString("hex")); 67 | return result; 68 | } else { 69 | throw response; 70 | } 71 | }); 72 | } 73 | 74 | let transportInstances = []; 75 | 76 | function emitDisconnect() { 77 | transportInstances.forEach(t => t.emit("disconnect")); 78 | transportInstances = []; 79 | } 80 | 81 | function isTimeoutU2FError(u2fError) { 82 | return u2fError.metaData.code === 5; 83 | } 84 | /** 85 | * U2F web Transport implementation 86 | * @example 87 | * import TransportU2F from "@ledgerhq/hw-transport-u2f"; 88 | * ... 89 | * TransportU2F.create().then(transport => ...) 90 | */ 91 | 92 | function _instanceof(left, right) { if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) { return !!right[Symbol.hasInstance](left); } else { return left instanceof right; } } 93 | 94 | function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } 95 | 96 | function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } 97 | 98 | function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } 99 | 100 | function _classCallCheck(instance, Constructor) { if (!_instanceof(instance, Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 101 | 102 | function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } 103 | 104 | function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } 105 | 106 | function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } 107 | 108 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } 109 | 110 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 111 | 112 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } 113 | 114 | function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } 115 | 116 | var TransportU2F = 117 | /*#__PURE__*/ 118 | function (_hwTransport$default) { 119 | _inherits(TransportU2F, _hwTransport$default); 120 | 121 | _createClass(TransportU2F, null, [{ 122 | key: "open", 123 | 124 | /* 125 | */ 126 | 127 | /* 128 | */ 129 | 130 | /** 131 | * static function to create a new Transport from a connected Ledger device discoverable via U2F (browser support) 132 | */ 133 | value: function () { 134 | var _open = _asyncToGenerator( 135 | /*#__PURE__*/ 136 | regeneratorRuntime.mark(function _callee(_) { 137 | var _openTimeout, 138 | _args = arguments; 139 | 140 | return regeneratorRuntime.wrap(function _callee$(_context) { 141 | while (1) { 142 | switch (_context.prev = _context.next) { 143 | case 0: 144 | _openTimeout = _args.length > 1 && _args[1] !== undefined ? _args[1] : 5000; 145 | return _context.abrupt("return", new TransportU2F()); 146 | 147 | case 2: 148 | case "end": 149 | return _context.stop(); 150 | } 151 | } 152 | }, _callee); 153 | })); 154 | 155 | function open(_x) { 156 | return _open.apply(this, arguments); 157 | } 158 | 159 | return open; 160 | }() 161 | }]); 162 | 163 | function TransportU2F() { 164 | var _this; 165 | 166 | _classCallCheck(this, TransportU2F); 167 | 168 | _this = _possibleConstructorReturn(this, _getPrototypeOf(TransportU2F).call(this)); 169 | _this.scrambleKey = void 0; 170 | _this.unwrap = true; 171 | transportInstances.push(_assertThisInitialized(_this)); 172 | return _this; 173 | } 174 | /** 175 | * Exchange with the device using APDU protocol. 176 | * @param apdu 177 | * @returns a promise of apdu response 178 | */ 179 | 180 | 181 | _createClass(TransportU2F, [{ 182 | key: "exchange", 183 | value: function () { 184 | var _exchange = _asyncToGenerator( 185 | /*#__PURE__*/ 186 | regeneratorRuntime.mark(function _callee2(apdu) { 187 | var isU2FError; 188 | return regeneratorRuntime.wrap(function _callee2$(_context2) { 189 | while (1) { 190 | switch (_context2.prev = _context2.next) { 191 | case 0: 192 | _context2.prev = 0; 193 | _context2.next = 3; 194 | return attemptExchange(apdu, this.exchangeTimeout, this.scrambleKey, this.unwrap); 195 | 196 | case 3: 197 | return _context2.abrupt("return", _context2.sent); 198 | 199 | case 6: 200 | _context2.prev = 6; 201 | _context2.t0 = _context2["catch"](0); 202 | isU2FError = _typeof(_context2.t0.metaData) === "object"; 203 | 204 | if (!isU2FError) { 205 | _context2.next = 14; 206 | break; 207 | } 208 | 209 | if (isTimeoutU2FError(_context2.t0)) { 210 | emitDisconnect(); 211 | } // the wrapping make error more usable and "printable" to the end user. 212 | 213 | 214 | throw wrapU2FTransportError(_context2.t0, "Failed to sign with Ledger device: U2F " + _context2.t0.metaData.type, "U2F_" + _context2.t0.metaData.code); 215 | 216 | case 14: 217 | throw _context2.t0; 218 | 219 | case 15: 220 | case "end": 221 | return _context2.stop(); 222 | } 223 | } 224 | }, _callee2, this, [[0, 6]]); 225 | })); 226 | 227 | function exchange(_x2) { 228 | return _exchange.apply(this, arguments); 229 | } 230 | 231 | return exchange; 232 | }() 233 | /** 234 | */ 235 | 236 | }, { 237 | key: "setScrambleKey", 238 | value: function setScrambleKey(scrambleKey) { 239 | this.scrambleKey = Buffer.from(scrambleKey, "ascii"); 240 | } 241 | /** 242 | */ 243 | 244 | }, { 245 | key: "setUnwrap", 246 | value: function setUnwrap(unwrap) { 247 | this.unwrap = unwrap; 248 | } 249 | }, { 250 | key: "close", 251 | value: function close() { 252 | // u2f have no way to clean things up 253 | return Promise.resolve(); 254 | } 255 | }]); 256 | 257 | return TransportU2F; 258 | }(_hwTransport.default); 259 | 260 | exports.default = TransportU2F; 261 | TransportU2F.isSupported = _u2fApi.isSupported; 262 | 263 | TransportU2F.list = () => // this transport is not discoverable but we are going to guess if it is here with isSupported() 264 | (0, _u2fApi.isSupported)().then(supported => supported ? [null] : []); 265 | 266 | TransportU2F.listen = observer => { 267 | let unsubscribed = false; 268 | (0, _u2fApi.isSupported)().then(supported => { 269 | if (unsubscribed) return; 270 | 271 | if (supported) { 272 | observer.next({ 273 | type: "add", 274 | descriptor: null 275 | }); 276 | observer.complete(); 277 | } else { 278 | observer.error(new _errors.TransportError("U2F browser support is needed for Ledger. " + "Please use Chrome, Opera or Firefox with a U2F extension. " + "Also make sure you're on an HTTPS connection", "U2FNotSupported")); 279 | } 280 | }); 281 | return { 282 | unsubscribe: () => { 283 | unsubscribed = true; 284 | } 285 | }; 286 | }; 287 | //# sourceMappingURL=TransportU2F.js.map 288 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2015 Stellar Development Foundation 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | 204 | -------------------------------------------------------------------------------- /vendor/@ledgerhq/hw-transport-u2f/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 | -------------------------------------------------------------------------------- /vendor/@ledgerhq/hw-transport/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 | --------------------------------------------------------------------------------