├── .browserslistrc ├── .editorconfig ├── .gitignore ├── .vscode └── launch.json ├── LICENSE ├── README.md ├── angular.json ├── e2e ├── protractor.conf.js ├── src │ ├── app.e2e-spec.ts │ └── app.po.ts └── tsconfig.json ├── karma.conf.js ├── other_logos ├── twitter_icon.png ├── xrpl_services_twitter_banner.png └── xrpl_services_twitter_banner_update.png ├── package.json ├── src ├── .well-known │ ├── brave-rewards-verification.txt │ └── xrp-ledger.toml ├── app │ ├── app.component.css │ ├── app.component.html │ ├── app.component.spec.ts │ ├── app.component.ts │ ├── app.module.ts │ ├── app.routes.ts │ ├── components │ │ ├── easyToken │ │ │ ├── blackholeAccount.html │ │ │ ├── blackholeAccount.ts │ │ │ ├── createToken.html │ │ │ ├── createToken.ts │ │ │ ├── issueMoreToken.html │ │ │ ├── issueMoreToken.ts │ │ │ ├── issuedNftList.css │ │ │ ├── issuedNftList.html │ │ │ ├── issuedNftList.ts │ │ │ ├── issuedTokenList.css │ │ │ ├── issuedTokenList.html │ │ │ ├── issuedTokenList.ts │ │ │ ├── tokenDetailsDialog.html │ │ │ ├── tokenDetailsDialog.scss │ │ │ └── tokenDetailsDialog.ts │ │ ├── escrowList │ │ │ ├── escrowList.css │ │ │ ├── escrowList.html │ │ │ └── escrowList.ts │ │ ├── escrowListExecuter │ │ │ ├── escrowListExecuter.css │ │ │ ├── escrowListExecuter.html │ │ │ └── escrowListExecuter.ts │ │ ├── footer.html │ │ ├── footer.ts │ │ ├── genericDialog.html │ │ ├── genericDialog.ts │ │ ├── genericPayloadQRDialog.html │ │ ├── genericPayloadQRDialog.scss │ │ ├── genericPayloadQRDialog.ts │ │ ├── tokenList │ │ │ ├── tokenList.css │ │ │ ├── tokenList.html │ │ │ └── tokenList.ts │ │ ├── tools │ │ │ ├── multiSignFlow.html │ │ │ ├── multiSignFlow.ts │ │ │ ├── norippleCheck.css │ │ │ ├── norippleCheck.html │ │ │ ├── norippleCheck.ts │ │ │ ├── rawTransactions.html │ │ │ ├── rawTransactions.ts │ │ │ ├── testNetCredentials.css │ │ │ ├── testNetCredentials.html │ │ │ ├── testNetCredentials.ts │ │ │ ├── transactionScheduler.html │ │ │ ├── transactionScheduler.ts │ │ │ ├── unlChecker.css │ │ │ ├── unlChecker.html │ │ │ └── unlChecker.ts │ │ ├── topbar.html │ │ ├── topbar.ts │ │ ├── transactions │ │ │ ├── accountdelete.html │ │ │ ├── accountdelete.ts │ │ │ ├── accountset.html │ │ │ ├── accountset.ts │ │ │ ├── escrowcancel.html │ │ │ ├── escrowcancel.ts │ │ │ ├── escrowcreate.html │ │ │ ├── escrowcreate.ts │ │ │ ├── escrowfinish.html │ │ │ ├── escrowfinish.ts │ │ │ ├── setregularkey.html │ │ │ ├── setregularkey.ts │ │ │ ├── signerlistset.css │ │ │ ├── signerlistset.html │ │ │ ├── signerlistset.ts │ │ │ ├── trustset.html │ │ │ └── trustset.ts │ │ ├── trustlineList │ │ │ ├── trustlineList.css │ │ │ ├── trustlineList.html │ │ │ ├── trustlineList.ts │ │ │ ├── trustlineListIssuing.css │ │ │ ├── trustlineListIssuing.html │ │ │ └── trustlineListIssuing.ts │ │ ├── xummSignRequestDialog.html │ │ └── xummSignRequestDialog.ts │ ├── routes │ │ ├── easyToken.html │ │ ├── easyToken.ts │ │ ├── generic-backend.html │ │ ├── generic-backend.ts │ │ ├── nft-api-backend.html │ │ ├── nft-api-backend.ts │ │ ├── nftRoute.html │ │ ├── nftRoute.ts │ │ ├── specific │ │ │ ├── privacy.html │ │ │ ├── privacy.ts │ │ │ ├── terms.html │ │ │ └── terms.ts │ │ ├── statistics.css │ │ ├── statistics.html │ │ ├── statistics.ts │ │ ├── tools.html │ │ ├── tools.ts │ │ ├── xrpl-statistics.css │ │ ├── xrpl-statistics.html │ │ ├── xrpl-statistics.ts │ │ ├── xrpl-transactions.html │ │ └── xrpl-transactions.ts │ ├── services │ │ ├── app.service.ts │ │ ├── util.service.ts │ │ ├── xrplWebSocket.ts │ │ ├── xrpllabs-liquidity-check │ │ │ ├── checkLiquidityForToken.ts │ │ │ └── ledgerExchange.ts │ │ └── xumm.service.ts │ └── utils │ │ ├── IEEE754Float.ts │ │ ├── TypeWriter.ts │ │ ├── flagutils.ts │ │ ├── normalizers.ts │ │ ├── searchHighlight.ts │ │ ├── types.ts │ │ └── utils.ts ├── assets │ ├── .gitkeep │ ├── christmas.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── topbar_logo.png │ ├── xrpl_services_twitter_card.png │ ├── xummDonate.svg │ ├── xummPay.svg │ └── xummSignIn.svg ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── main.ts ├── polyfills.ts ├── robots.txt ├── styles.scss ├── test.ts └── theme.scss ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.spec.json └── tslint.json /.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not ios_saf 15.2-15.3 13 | not safari 15.2-15.3 14 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events*.json 15 | speed-measure-plugin*.json 16 | 17 | # IDEs and editors 18 | /.idea 19 | .project 20 | .classpath 21 | .c9/ 22 | *.launch 23 | .settings/ 24 | *.sublime-workspace 25 | 26 | # IDE - VSCode 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | .history/* 33 | 34 | # misc 35 | /.sass-cache 36 | /connect.lock 37 | /coverage 38 | /libpeerconnection.log 39 | npm-debug.log 40 | yarn-error.log 41 | testem.log 42 | /typings 43 | .angular/ 44 | 45 | # System Files 46 | .DS_Store 47 | Thumbs.db 48 | .vscode/settings.json 49 | package-lock.json 50 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0", 3 | "configurations": [ 4 | { 5 | "name": "Launch index.html", 6 | "type": "chrome", 7 | "request": "launch", 8 | "file": "${workspaceFolder}/index.html" 9 | }, 10 | ] 11 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Xrpl Services 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.3.23. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 28 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "xrpl-services": { 7 | "projectType": "application", 8 | "schematics": {}, 9 | "root": "", 10 | "sourceRoot": "src", 11 | "prefix": "app", 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "outputPath": "dist/xrpl-services", 17 | "index": "src/index.html", 18 | "main": "src/main.ts", 19 | "polyfills": "src/polyfills.ts", 20 | "tsConfig": "tsconfig.app.json", 21 | "assets": [ 22 | "src/favicon.ico", 23 | "src/assets", 24 | "src/.well-known", 25 | "src/manifest.webmanifest", 26 | "src/robots.txt" 27 | ], 28 | "styles": [ 29 | "src/styles.scss", 30 | "src/theme.scss" 31 | ], 32 | "scripts": [], 33 | "allowedCommonJsDependencies": [ 34 | "codemirror", 35 | "xrpl-tagged-address-codec", 36 | "md5", 37 | "copy-to-clipboard", 38 | "qrcode", 39 | "stream-browserify", 40 | "crypto-browserify", 41 | "vm-browserify" 42 | ], 43 | "vendorChunk": true, 44 | "extractLicenses": false, 45 | "buildOptimizer": false, 46 | "sourceMap": true, 47 | "optimization": false, 48 | "namedChunks": true 49 | }, 50 | "configurations": { 51 | "production": { 52 | "fileReplacements": [ 53 | { 54 | "replace": "src/environments/environment.ts", 55 | "with": "src/environments/environment.prod.ts" 56 | } 57 | ], 58 | "optimization": true, 59 | "outputHashing": "all", 60 | "sourceMap": false, 61 | "namedChunks": false, 62 | "extractLicenses": true, 63 | "vendorChunk": false, 64 | "buildOptimizer": true, 65 | "budgets": [ 66 | { 67 | "type": "initial", 68 | "maximumWarning": "3mb", 69 | "maximumError": "5mb" 70 | }, 71 | { 72 | "type": "anyComponentStyle", 73 | "maximumWarning": "6kb", 74 | "maximumError": "10kb" 75 | } 76 | ] 77 | } 78 | }, 79 | "defaultConfiguration": "" 80 | }, 81 | "serve": { 82 | "builder": "@angular-devkit/build-angular:dev-server", 83 | "options": { 84 | "browserTarget": "xrpl-services:build" 85 | }, 86 | "configurations": { 87 | "production": { 88 | "browserTarget": "xrpl-services:build:production" 89 | } 90 | } 91 | }, 92 | "extract-i18n": { 93 | "builder": "@angular-devkit/build-angular:extract-i18n", 94 | "options": { 95 | "browserTarget": "xrpl-services:build" 96 | } 97 | }, 98 | "test": { 99 | "builder": "@angular-devkit/build-angular:karma", 100 | "options": { 101 | "main": "src/test.ts", 102 | "polyfills": "src/polyfills.ts", 103 | "tsConfig": "tsconfig.spec.json", 104 | "karmaConfig": "karma.conf.js", 105 | "assets": [ 106 | "src/favicon.ico", 107 | "src/assets", 108 | "src/.well-known", 109 | "src/manifest.webmanifest", 110 | "src/robots.txt" 111 | ], 112 | "styles": [ 113 | "src/styles.scss", 114 | "src/theme.scss" 115 | ], 116 | "scripts": [] 117 | } 118 | }, 119 | "lint": { 120 | "builder": "@angular-devkit/build-angular:tslint", 121 | "options": { 122 | "tsConfig": [ 123 | "tsconfig.app.json", 124 | "tsconfig.spec.json", 125 | "e2e/tsconfig.json" 126 | ], 127 | "exclude": [ 128 | "**/node_modules/**" 129 | ] 130 | } 131 | }, 132 | "e2e": { 133 | "builder": "@angular-devkit/build-angular:protractor", 134 | "options": { 135 | "protractorConfig": "e2e/protractor.conf.js", 136 | "devServerTarget": "xrpl-services:serve" 137 | }, 138 | "configurations": { 139 | "production": { 140 | "devServerTarget": "xrpl-services:serve:production" 141 | } 142 | } 143 | } 144 | } 145 | } 146 | }, 147 | "defaultProject": "xrpl-services" 148 | } -------------------------------------------------------------------------------- /e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Protractor configuration file, see link for more information 3 | // https://github.com/angular/protractor/blob/master/lib/config.ts 4 | 5 | const { SpecReporter } = require('jasmine-spec-reporter'); 6 | 7 | /** 8 | * @type { import("protractor").Config } 9 | */ 10 | exports.config = { 11 | allScriptsTimeout: 11000, 12 | specs: [ 13 | './src/**/*.e2e-spec.ts' 14 | ], 15 | capabilities: { 16 | browserName: 'chrome' 17 | }, 18 | directConnect: true, 19 | baseUrl: 'http://localhost:4200/', 20 | framework: 'jasmine', 21 | jasmineNodeOpts: { 22 | showColors: true, 23 | defaultTimeoutInterval: 30000, 24 | print: function() {} 25 | }, 26 | onPrepare() { 27 | require('ts-node').register({ 28 | project: require('path').join(__dirname, './tsconfig.json') 29 | }); 30 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 31 | } 32 | }; -------------------------------------------------------------------------------- /e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('xrpl-services app is running!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText() { 9 | return element(by.css('app-root .content span')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es2018", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, './coverage/xrpl-services'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /other_logos/twitter_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nixer89/xrpl-services-frontend/d766a900ce8c615ab0e540a2be31160024a48fb8/other_logos/twitter_icon.png -------------------------------------------------------------------------------- /other_logos/xrpl_services_twitter_banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nixer89/xrpl-services-frontend/d766a900ce8c615ab0e540a2be31160024a48fb8/other_logos/xrpl_services_twitter_banner.png -------------------------------------------------------------------------------- /other_logos/xrpl_services_twitter_banner_update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nixer89/xrpl-services-frontend/d766a900ce8c615ab0e540a2be31160024a48fb8/other_logos/xrpl_services_twitter_banner_update.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xrpl-services", 3 | "version": "4.10.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve --open", 7 | "build": "ng build --configuration production", 8 | "upload": "ng build --configuration production & aws s3 --profile myevride rm s3://xrpl.services/ --recursive & aws s3 --profile myevride cp ./dist/xrpl-services s3://xrpl.services/ --recursive --include '*' --acl public-read --cache-control public,max-age=1800,no-transform", 9 | "invalidate": "aws cloudfront --profile myevride create-invalidation --distribution-id E1FM28C5G2ZK2P --paths /*", 10 | "upload-dev": "ng build --configuration production & aws s3 --profile myevride rm s3://dev.xrpl.services/ --recursive & aws s3 --profile myevride cp ./dist/xrpl-services s3://dev.xrpl.services/ --recursive --include '*' --acl public-read --cache-control public,max-age=1800,no-transform", 11 | "invalidate-dev": "aws cloudfront --profile myevride create-invalidation --distribution-id E6F3YVCFVB1QD --paths /*", 12 | "upload-qa": "ng build --configuration production & aws s3 --profile myevride rm s3://qa.xrpl.services/ --recursive & aws s3 --profile myevride cp ./dist/xrpl-services s3://qa.xrpl.services/ --recursive --include '*' --acl public-read --cache-control public,max-age=1800,no-transform", 13 | "invalidate-qa": "aws cloudfront --profile myevride create-invalidation --distribution-id EL85OZ2PCZH5H --paths /*", 14 | "test": "ng test", 15 | "lint": "ng lint", 16 | "e2e": "ng e2e" 17 | }, 18 | "private": true, 19 | "dependencies": { 20 | "@angular/animations": "^13.1.0", 21 | "@angular/cdk": "^13.1.0", 22 | "@angular/common": "^13.1.0", 23 | "@angular/compiler": "^13.1.0", 24 | "@angular/core": "^13.1.0", 25 | "@angular/flex-layout": "^13.0.0-beta.36", 26 | "@angular/forms": "^13.1.0", 27 | "@angular/material": "^13.1.0", 28 | "@angular/material-moment-adapter": "^13.1.0", 29 | "@angular/platform-browser": "^13.1.0", 30 | "@angular/platform-browser-dynamic": "^13.1.0", 31 | "@angular/router": "^13.1.0", 32 | "@ctrl/ngx-codemirror": "^4.1.1", 33 | "angular-2-local-storage": "^3.0.2", 34 | "buffer": "^6.0.3", 35 | "codemirror": "^5.63.3", 36 | "copy-to-clipboard": "^3.3.1", 37 | "crypto-browserify": "^3.12.0", 38 | "elliptic": "^6.5.4", 39 | "email-validator": "^2.0.4", 40 | "five-bells-condition": "^5.0.1", 41 | "md5": "^2.3.0", 42 | "moment": "^2.29.1", 43 | "ngx-device-detector": "^2.1.1", 44 | "qrcode": "^1.4.4", 45 | "ripple-address-codec": "^4.2.4", 46 | "ripple-keypairs": "^1.3.0", 47 | "rippled-ws-client": "^1.6.5", 48 | "rxjs": "^6.6.7", 49 | "stream-browserify": "^3.0.0", 50 | "tslib": "^2.3.1", 51 | "vm-browserify": "^1.1.2", 52 | "xrpl-orderbook-reader": "^0.3.3", 53 | "xrpl-tagged-address-codec": "^0.2.1", 54 | "xumm-sdk": "^0.4.6", 55 | "zone.js": "~0.11.4" 56 | }, 57 | "devDependencies": { 58 | "@angular-devkit/build-angular": "^13.0.4", 59 | "@angular/cli": "^13.0.4", 60 | "@angular/compiler-cli": "^13.1.0", 61 | "@angular/language-service": "^13.1.0", 62 | "@types/jasmine": "^3.10.2", 63 | "@types/jasminewd2": "^2.0.10", 64 | "@types/md5": "^2.3.1", 65 | "@types/node": "^16.18.123", 66 | "@types/qrcode": "^1.4.1", 67 | "codelyzer": "^6.0.2", 68 | "jasmine-core": "^3.10.1", 69 | "jasmine-spec-reporter": "^6.0.0", 70 | "karma": "^6.3.9", 71 | "karma-chrome-launcher": "~3.1.0", 72 | "karma-coverage-istanbul-reporter": "~3.0.2", 73 | "karma-jasmine": "~4.0.0", 74 | "karma-jasmine-html-reporter": "^1.7.0", 75 | "protractor": "~7.0.0", 76 | "ts-node": "^9.1.1", 77 | "tslint": "~6.1.0", 78 | "typescript": "^4.6.4" 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/.well-known/brave-rewards-verification.txt: -------------------------------------------------------------------------------- 1 | This is a Brave Rewards publisher verification file. 2 | 3 | Domain: xrpl.services 4 | Token: 4e9a74e377816ff3b4cdf450e975fdded52c7871836df904a88e917da96ed9fc 5 | -------------------------------------------------------------------------------- /src/.well-known/xrp-ledger.toml: -------------------------------------------------------------------------------- 1 | [METADATA] 2 | modified = 2021-12-07T13:00:00.000Z 3 | 4 | [[VALIDATORS]] 5 | public_key = "nHB4cGH8jhVLc7QUwp3zysA3JxmTA7Qi2CEQgMn6Yit6E6YqDAsg" 6 | attestation = "BBEC70B3DED5BD24E9CCC3F3073267862156AABC1AE830935FBF27DFDDEA0F94B3A32DE1AD81D1CCDCD93EF43231223B4E1913624C94C24F03CC91173EF7CB01" 7 | network = "main" 8 | owner_country = "de" 9 | server_country = "de" 10 | unl = "https://vl.xrplf.org" 11 | 12 | [[ACCOUNTS]] 13 | address = "rNixerUVPwrhxGDt4UooDu6FJ7zuofvjCF" 14 | desc = "https://xrpl.services address, a website by 'Daniel Siedentopf XRP Ledger Services'" 15 | 16 | [[ACCOUNTS]] 17 | address = "rELeasERs3m4inA1UinRLTpXemqyStqzwh" 18 | desc = "'XRPL Services' Escrow Releaser" 19 | 20 | [[ACCOUNTS]] 21 | address = "r9N4v3cWxfh4x6yUNjxNy3DbWUgbzMBLdk" 22 | desc = "private address, owned by @nixerFFM" 23 | 24 | [[PRINCIPALS]] 25 | name = "XRP Ledger Services" 26 | email = "info@xrpl.services" 27 | twitter = "XrplServices" 28 | -------------------------------------------------------------------------------- /src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nixer89/xrpl-services-frontend/d766a900ce8c615ab0e540a2be31160024a48fb8/src/app/app.component.css -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 |
-------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, waitForAsync } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | 4 | describe('AppComponent', () => { 5 | beforeEach(waitForAsync(() => { 6 | TestBed.configureTestingModule({ 7 | declarations: [ 8 | AppComponent 9 | ], 10 | }).compileComponents(); 11 | })); 12 | 13 | it('should create the app', () => { 14 | const fixture = TestBed.createComponent(AppComponent); 15 | const app = fixture.debugElement.componentInstance; 16 | expect(app).toBeTruthy(); 17 | }); 18 | 19 | it(`should have as title 'xrpl-services'`, () => { 20 | const fixture = TestBed.createComponent(AppComponent); 21 | const app = fixture.debugElement.componentInstance; 22 | expect(app.title).toEqual('xrpl-services'); 23 | }); 24 | 25 | it('should render title', () => { 26 | const fixture = TestBed.createComponent(AppComponent); 27 | fixture.detectChanges(); 28 | const compiled = fixture.debugElement.nativeElement; 29 | expect(compiled.querySelector('.content span').textContent).toContain('xrpl-services app is running!'); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Router, NavigationEnd } from '@angular/router'; 3 | import { LocalStorageService } from 'angular-2-local-storage'; 4 | 5 | declare let gtag: Function; 6 | 7 | @Component({ 8 | selector: 'app-root', 9 | templateUrl: './app.component.html', 10 | styleUrls: ['./app.component.css'] 11 | }) 12 | export class AppComponent implements OnInit { 13 | title = 'XRPL Services'; 14 | darkMode:boolean; 15 | 16 | constructor(private router: Router, private localStorage: LocalStorageService) { 17 | this.router.events.subscribe(event => { 18 | if(event instanceof NavigationEnd) { 19 | let path = (event.urlAfterRedirects.includes('?') ? event.urlAfterRedirects.substring(0, event.urlAfterRedirects.indexOf('?')) : event.urlAfterRedirects); 20 | gtag('config', 'UA-159404162-1', 21 | { 22 | 'page_title': this.getPageTitle(path), 23 | 'page_path': path 24 | } 25 | ); 26 | } 27 | }); 28 | } 29 | 30 | getPageTitle(page_path:string): string { 31 | let title = "XRP Ledger Services"; 32 | 33 | switch(page_path) { 34 | case '': case '/': title = "XRPL Transactions"; break; 35 | case '/easy-iou': title = "Easy-IOU"; break; 36 | case '/tokens': title = "XRPL Tokens"; break; 37 | case '/tools': title = "XRPL Tools"; break; 38 | case '/terms': title = "XRPL Services Terms"; break; 39 | case '/privacy': title = "XRPL Services Privacy"; break; 40 | default: title = "XRPL Services"; break; 41 | } 42 | 43 | return title; 44 | } 45 | 46 | ngOnInit(){ 47 | this.localStorage.remove("xrplAccount"); 48 | this.localStorage.remove("testMode"); 49 | let storageKeys:string[] = this.localStorage.keys(); 50 | if(storageKeys && storageKeys.includes('darkMode')) { 51 | this.darkMode = this.localStorage.get("darkMode"); 52 | } 53 | else { 54 | this.darkMode = true; 55 | this.localStorage.set("darkMode", this.darkMode); 56 | } 57 | 58 | var bodyStyles = document.body.style; 59 | if(this.darkMode) { 60 | bodyStyles.setProperty('--background-color', 'rgba(50, 50, 50)'); 61 | //bodyStyles.setProperty('--dialog-background', 'rgba(50, 50, 50)'); 62 | //bodyStyles.setProperty('--dialog-color', '#ffffff'); 63 | } 64 | else { 65 | bodyStyles.setProperty('--background-color', 'rgba(238,238,238,.5)'); 66 | //bodyStyles.setProperty('--dialog-background', 'rgba(238,238,238)'); 67 | //bodyStyles.setProperty('--dialog-color', 'rgba(0, 0, 0)'); 68 | } 69 | // always scroll to the top of the page on route change: 70 | this.router.events.subscribe(e => e instanceof NavigationEnd ? window.scrollTo(0,0) : null); 71 | } 72 | 73 | darkModeChanged(isDarkMode:boolean) { 74 | //console.log(isDarkMode); 75 | this.darkMode = isDarkMode; 76 | var bodyStyles = document.body.style; 77 | if(isDarkMode) { 78 | bodyStyles.setProperty('--background-color', 'rgba(50, 50, 50)'); 79 | //bodyStyles.setProperty('--dialog-background', 'rgba(50, 50, 50)'); 80 | //bodyStyles.setProperty('--dialog-color', '#ffffff'); 81 | 82 | } 83 | else { 84 | bodyStyles.setProperty('--background-color', 'rgba(238,238,238,.5)'); 85 | //bodyStyles.setProperty('--dialog-background', 'rgba(238,238,238)'); 86 | //bodyStyles.setProperty('--dialog-color', 'rgba(0, 0, 0)'); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes, RouterModule } from '@angular/router'; 2 | import { ModuleWithProviders } from '@angular/core'; 3 | import { XrplTransactionsComponent } from './routes/xrpl-transactions'; 4 | import { Tools } from './routes/tools'; 5 | //import { GenericBackendDefinition } from './routes/generic-backend'; 6 | import { PrivacyComponent } from './routes/specific/privacy'; 7 | import { TermsComponent } from './routes/specific/terms'; 8 | import { EasyToken } from './routes/easyToken'; 9 | import { NftRoute } from './routes/nftRoute'; 10 | import { Statistics } from './routes/statistics'; 11 | import { XrplStatistics } from './routes/xrpl-statistics'; 12 | import { NftApiBackend } from './routes/nft-api-backend'; 13 | 14 | export const routes: Routes = [ 15 | {path: '', component: XrplTransactionsComponent}, 16 | {path: 'tokens', component: EasyToken}, 17 | {path: 'xls-14d', component: NftRoute}, 18 | {path: 'tools', component: Tools}, 19 | //{path: 'generic-backend', component: GenericBackendDefinition}, 20 | {path: 'statistics', component: Statistics}, 21 | {path: 'xrpl-statistics', component: XrplStatistics}, 22 | {path: 'nft-api', component: NftApiBackend}, 23 | {path: 'privacy', component: PrivacyComponent}, 24 | {path: 'terms', component: TermsComponent}, 25 | {path: 'easy-iou', redirectTo: 'tokens'}, 26 | {path: '**', redirectTo: ''} 27 | ]; 28 | 29 | export const AppRoutes: ModuleWithProviders = RouterModule.forRoot(routes, { relativeLinkResolution: 'legacy' }); 30 | -------------------------------------------------------------------------------- /src/app/components/easyToken/tokenDetailsDialog.scss: -------------------------------------------------------------------------------- 1 | .break-words { 2 | word-wrap: break-word !important; 3 | white-space: pre-wrap; 4 | overflow-wrap: break-word; 5 | word-wrap: break-word; 6 | 7 | word-break: break-word; 8 | 9 | -ms-hyphens: auto; 10 | -moz-hyphens: auto; 11 | -webkit-hyphens: auto; 12 | hyphens: auto; 13 | } 14 | .mat-dialog-content { 15 | margin: 5px; 16 | padding: 5px; 17 | } 18 | 19 | .div { 20 | word-wrap: break-word !important; 21 | white-space: pre-wrap; 22 | overflow-wrap: break-word; 23 | word-wrap: break-word; 24 | 25 | word-break: break-word; 26 | 27 | -ms-hyphens: auto; 28 | -moz-hyphens: auto; 29 | -webkit-hyphens: auto; 30 | hyphens: auto; 31 | } 32 | 33 | @media (max-width:599px) { 34 | .mat-dialog-content { 35 | font-size: 0.8em; 36 | } 37 | } -------------------------------------------------------------------------------- /src/app/components/escrowList/escrowList.css: -------------------------------------------------------------------------------- 1 | 2 | table { 3 | width: 100%; 4 | } 5 | 6 | .mat-cell { 7 | justify-content: center; 8 | } 9 | 10 | .mat-cell:hover{ 11 | cursor: pointer; 12 | } 13 | 14 | @media (min-width:600px) { 15 | 16 | .mat-column-destination { 17 | word-wrap: break-word !important; 18 | white-space: unset !important; 19 | flex: 0 0 30% !important; 20 | width: 30% !important; 21 | overflow-wrap: break-word; 22 | word-wrap: break-word; 23 | 24 | word-break: break-word; 25 | 26 | -ms-hyphens: auto; 27 | -moz-hyphens: auto; 28 | -webkit-hyphens: auto; 29 | hyphens: auto; 30 | justify-content: center; 31 | text-align: center; 32 | } 33 | 34 | .mat-column-amount { 35 | word-wrap: break-word !important; 36 | white-space: unset !important; 37 | flex: 0 0 20% !important; 38 | width: 20% !important; 39 | overflow-wrap: break-word; 40 | word-wrap: break-word; 41 | 42 | word-break: break-word; 43 | 44 | -ms-hyphens: auto; 45 | -moz-hyphens: auto; 46 | -webkit-hyphens: auto; 47 | hyphens: auto; 48 | justify-content: center; 49 | text-align: center; 50 | } 51 | 52 | .mat-column-finishafter { 53 | word-wrap: break-word !important; 54 | white-space: unset !important; 55 | flex: 0 0 15% !important; 56 | width: 15% !important; 57 | overflow-wrap: break-word; 58 | word-wrap: break-word; 59 | 60 | word-break: break-word; 61 | 62 | -ms-hyphens: auto; 63 | -moz-hyphens: auto; 64 | -webkit-hyphens: auto; 65 | hyphens: auto; 66 | justify-content: center; 67 | text-align: center; 68 | } 69 | 70 | .mat-column-cancelafter { 71 | word-wrap: break-word !important; 72 | white-space: unset !important; 73 | flex: 0 0 15% !important; 74 | width: 15% !important; 75 | overflow-wrap: break-word; 76 | word-wrap: break-word; 77 | 78 | word-break: break-word; 79 | 80 | -ms-hyphens: auto; 81 | -moz-hyphens: auto; 82 | -webkit-hyphens: auto; 83 | hyphens: auto; 84 | justify-content: center; 85 | text-align: center; 86 | } 87 | 88 | .mat-column-condition { 89 | word-wrap: break-word !important; 90 | white-space: unset !important; 91 | flex: 0 0 15% !important; 92 | width: 20% !important; 93 | overflow-wrap: break-word; 94 | word-wrap: break-word; 95 | 96 | word-break: break-word; 97 | 98 | -ms-hyphens: auto; 99 | -moz-hyphens: auto; 100 | -webkit-hyphens: auto; 101 | hyphens: auto; 102 | justify-content: center; 103 | text-align: center; 104 | } 105 | } 106 | 107 | @media (max-width:599px) { 108 | 109 | .mat-footer-row , .mat-row { 110 | padding-top: 0.5em; 111 | align-items: start; 112 | min-height: 24px; 113 | cursor: pointer; 114 | } 115 | 116 | .mat-cell { 117 | padding-left: 1em; 118 | } 119 | 120 | .mat-column-destination { 121 | word-wrap: break-word !important; 122 | white-space: unset !important; 123 | overflow-wrap: break-word; 124 | word-wrap: break-word; 125 | 126 | word-break: break-word; 127 | 128 | -ms-hyphens: auto; 129 | -moz-hyphens: auto; 130 | -webkit-hyphens: auto; 131 | hyphens: auto; 132 | } 133 | 134 | .mat-column-amount { 135 | word-wrap: break-word !important; 136 | white-space: unset !important; 137 | overflow-wrap: break-word; 138 | word-wrap: break-word; 139 | 140 | word-break: break-word; 141 | 142 | -ms-hyphens: auto; 143 | -moz-hyphens: auto; 144 | -webkit-hyphens: auto; 145 | hyphens: auto; 146 | } 147 | 148 | .mat-column-finishafter { 149 | word-wrap: break-word !important; 150 | white-space: unset !important; 151 | overflow-wrap: break-word; 152 | word-wrap: break-word; 153 | 154 | word-break: break-word; 155 | 156 | -ms-hyphens: auto; 157 | -moz-hyphens: auto; 158 | -webkit-hyphens: auto; 159 | hyphens: auto; 160 | } 161 | 162 | .mat-column-cancelafter { 163 | word-wrap: break-word !important; 164 | white-space: unset !important; 165 | overflow-wrap: break-word; 166 | word-wrap: break-word; 167 | 168 | word-break: break-word; 169 | 170 | -ms-hyphens: auto; 171 | -moz-hyphens: auto; 172 | -webkit-hyphens: auto; 173 | hyphens: auto; 174 | } 175 | 176 | .mat-column-condition { 177 | word-wrap: break-word !important; 178 | white-space: unset !important; 179 | overflow-wrap: break-word; 180 | word-wrap: break-word; 181 | 182 | word-break: break-word; 183 | 184 | -ms-hyphens: auto; 185 | -moz-hyphens: auto; 186 | -webkit-hyphens: auto; 187 | hyphens: auto; 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/app/components/escrowList/escrowList.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | Select Escrow from list 12 | All escrows of the logged in account. 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | Destination 21 | 22 | {{escrow.Destination}} 23 | Destination:
{{escrow.Destination}}
24 |
25 |
26 | 27 | 28 | Amount in XRP 29 | 30 | {{getAmountInXRP(escrow.Amount)}} 31 | Escrow Amount in XRP:
{{getAmountInXRP(escrow.Amount)}}
32 |
33 |
34 | 35 | 36 | Finish After 37 | 38 | {{getTimeFromRippleTime(escrow.FinishAfter)}} 39 | Finish: {{getTimeFromRippleTime(escrow.FinishAfter)}} 40 | 41 | 42 | 43 | 44 | Cancel After 45 | 46 | {{getTimeFromRippleTime(escrow.CancelAfter)}} 47 | Cancel: {{getTimeFromRippleTime(escrow.CancelAfter)}} 48 | 49 | 50 | 51 | 52 | Has Password 53 | 54 | 55 | Has Password:  56 | 57 | 58 | 59 | 60 | 61 | 62 |
63 | 64 |
65 |
66 |
-------------------------------------------------------------------------------- /src/app/components/escrowListExecuter/escrowListExecuter.css: -------------------------------------------------------------------------------- 1 | 2 | table{ 3 | width: 100%; 4 | } 5 | 6 | .mat-cell { 7 | justify-content: center; 8 | } 9 | 10 | @media (min-width:600px) { 11 | 12 | .mat-column-destination { 13 | word-wrap: break-word !important; 14 | white-space: unset !important; 15 | flex: 0 0 30% !important; 16 | width: 30% !important; 17 | overflow-wrap: break-word; 18 | word-wrap: break-word; 19 | 20 | word-break: break-word; 21 | 22 | -ms-hyphens: auto; 23 | -moz-hyphens: auto; 24 | -webkit-hyphens: auto; 25 | hyphens: auto; 26 | justify-content: center; 27 | text-align: center; 28 | } 29 | 30 | .mat-column-amount { 31 | word-wrap: break-word !important; 32 | white-space: unset !important; 33 | flex: 0 0 10% !important; 34 | width: 10% !important; 35 | overflow-wrap: break-word; 36 | word-wrap: break-word; 37 | 38 | word-break: break-word; 39 | 40 | -ms-hyphens: auto; 41 | -moz-hyphens: auto; 42 | -webkit-hyphens: auto; 43 | hyphens: auto; 44 | justify-content: center; 45 | text-align: center; 46 | } 47 | 48 | .mat-column-finishafter { 49 | word-wrap: break-word !important; 50 | white-space: unset !important; 51 | flex: 0 0 15% !important; 52 | width: 15% !important; 53 | overflow-wrap: break-word; 54 | word-wrap: break-word; 55 | 56 | word-break: break-word; 57 | 58 | -ms-hyphens: auto; 59 | -moz-hyphens: auto; 60 | -webkit-hyphens: auto; 61 | hyphens: auto; 62 | justify-content: center; 63 | text-align: center; 64 | } 65 | 66 | .mat-column-autorelease { 67 | word-wrap: break-word !important; 68 | white-space: unset !important; 69 | flex: 0 0 15% !important; 70 | width: 15% !important; 71 | overflow-wrap: break-word; 72 | word-wrap: break-word; 73 | 74 | word-break: break-word; 75 | 76 | -ms-hyphens: auto; 77 | -moz-hyphens: auto; 78 | -webkit-hyphens: auto; 79 | hyphens: auto; 80 | justify-content: center; 81 | text-align: center; 82 | } 83 | 84 | .mat-column-expectedautorelease { 85 | word-wrap: break-word !important; 86 | white-space: unset !important; 87 | flex: 0 0 15% !important; 88 | width: 15% !important; 89 | overflow-wrap: break-word; 90 | word-wrap: break-word; 91 | 92 | word-break: break-word; 93 | 94 | -ms-hyphens: auto; 95 | -moz-hyphens: auto; 96 | -webkit-hyphens: auto; 97 | hyphens: auto; 98 | justify-content: center; 99 | text-align: center; 100 | } 101 | 102 | .mat-column-action { 103 | word-wrap: break-word !important; 104 | white-space: unset !important; 105 | flex: 0 0 15% !important; 106 | width: 15% !important; 107 | overflow-wrap: break-word; 108 | word-wrap: break-word; 109 | 110 | word-break: break-word; 111 | 112 | -ms-hyphens: auto; 113 | -moz-hyphens: auto; 114 | -webkit-hyphens: auto; 115 | hyphens: auto; 116 | justify-content: center; 117 | text-align: center; 118 | } 119 | } 120 | 121 | @media (max-width:599px) { 122 | 123 | .mat-footer-row , .mat-row { 124 | padding-top: 0.5em; 125 | align-items: start; 126 | min-height: 24px; 127 | cursor: pointer; 128 | } 129 | 130 | .mat-cell { 131 | padding-left: 1em; 132 | } 133 | 134 | .mat-column-destination { 135 | word-wrap: break-word !important; 136 | white-space: unset !important; 137 | overflow-wrap: break-word; 138 | word-wrap: break-word; 139 | 140 | word-break: break-word; 141 | 142 | -ms-hyphens: auto; 143 | -moz-hyphens: auto; 144 | -webkit-hyphens: auto; 145 | hyphens: auto; 146 | } 147 | 148 | .mat-column-amount { 149 | word-wrap: break-word !important; 150 | white-space: unset !important; 151 | overflow-wrap: break-word; 152 | word-wrap: break-word; 153 | 154 | word-break: break-word; 155 | 156 | -ms-hyphens: auto; 157 | -moz-hyphens: auto; 158 | -webkit-hyphens: auto; 159 | hyphens: auto; 160 | } 161 | 162 | .mat-column-finishafter { 163 | word-wrap: break-word !important; 164 | white-space: unset !important; 165 | overflow-wrap: break-word; 166 | word-wrap: break-word; 167 | 168 | word-break: break-word; 169 | 170 | -ms-hyphens: auto; 171 | -moz-hyphens: auto; 172 | -webkit-hyphens: auto; 173 | hyphens: auto; 174 | } 175 | 176 | .mat-column-autorelease { 177 | word-wrap: break-word !important; 178 | white-space: unset !important; 179 | overflow-wrap: break-word; 180 | word-wrap: break-word; 181 | 182 | word-break: break-word; 183 | 184 | -ms-hyphens: auto; 185 | -moz-hyphens: auto; 186 | -webkit-hyphens: auto; 187 | hyphens: auto; 188 | } 189 | 190 | .mat-column-expectedautorelease { 191 | word-wrap: break-word !important; 192 | white-space: unset !important; 193 | overflow-wrap: break-word; 194 | word-wrap: break-word; 195 | 196 | word-break: break-word; 197 | 198 | -ms-hyphens: auto; 199 | -moz-hyphens: auto; 200 | -webkit-hyphens: auto; 201 | hyphens: auto; 202 | } 203 | 204 | .mat-column-action { 205 | word-wrap: break-word !important; 206 | white-space: unset !important; 207 | overflow-wrap: break-word; 208 | word-wrap: break-word; 209 | 210 | word-break: break-word; 211 | 212 | -ms-hyphens: auto; 213 | -moz-hyphens: auto; 214 | -webkit-hyphens: auto; 215 | hyphens: auto; 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /src/app/components/footer.html: -------------------------------------------------------------------------------- 1 | 2 | Terms & Conditions 3 | Privacy Policy 4 | 5 | v.{{appVersion}} -  6 | All rights reserved 7 | 8 | 9 | Terms & Conditions 10 | Privacy Policy 11 | v.{{appVersion}} 12 | All rights reserved 13 | -------------------------------------------------------------------------------- /src/app/components/footer.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import packageInfo from '../../../package.json'; 3 | import { XummService } from '../services/xumm.service'; 4 | 5 | @Component({ 6 | selector: 'app-footer', 7 | templateUrl: './footer.html' 8 | }) 9 | export class FooterComponent implements OnInit { 10 | 11 | public appVersion:string = packageInfo.version; 12 | 13 | constructor(private xummApi: XummService) { } 14 | 15 | //transactions:number = -1; 16 | 17 | async ngOnInit() { 18 | /** 19 | let stats = await this.xummApi.getTransactionStatistics(); 20 | 21 | if(stats) 22 | this.transactions = 0; 23 | 24 | for (var trx in stats) { 25 | if (stats.hasOwnProperty(trx)) { 26 | this.transactions += stats[trx]; 27 | } 28 | } 29 | **/ 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/app/components/genericDialog.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 |
6 | 9 | 12 |
13 |
14 |
-------------------------------------------------------------------------------- /src/app/components/genericDialog.ts: -------------------------------------------------------------------------------- 1 | import { Component, Inject, OnInit } from "@angular/core"; 2 | import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; 3 | import { LocalStorageService } from 'angular-2-local-storage'; 4 | import { OverlayContainer } from '@angular/cdk/overlay'; 5 | 6 | @Component({ 7 | selector: "genericDialog", 8 | templateUrl: "genericDialog.html" 9 | }) 10 | export class GenericDialogComponent implements OnInit{ 11 | 12 | constructor( 13 | @Inject(MAT_DIALOG_DATA) public data: any, 14 | public dialogRef: MatDialogRef, 15 | private localStorage: LocalStorageService, 16 | private overlayContainer: OverlayContainer) { 17 | } 18 | 19 | line1:string = null; 20 | line2:string = null; 21 | btnLeft:string = null; 22 | btnRight:string = null; 23 | 24 | async ngOnInit() { 25 | if(this.localStorage && !this.localStorage.get("darkMode")) { 26 | this.overlayContainer.getContainerElement().classList.remove('dark-theme'); 27 | this.overlayContainer.getContainerElement().classList.add('light-theme'); 28 | } else { 29 | this.overlayContainer.getContainerElement().classList.remove('light-theme'); 30 | this.overlayContainer.getContainerElement().classList.add('dark-theme'); 31 | } 32 | 33 | this.line1 = this.data.line1; 34 | this.line2 = this.data.line2; 35 | this.btnLeft = this.data.btnLeft; 36 | this.btnRight = this.data.btnRight; 37 | } 38 | 39 | buttonLeftClicked() { 40 | this.dialogRef.close(true); 41 | } 42 | 43 | buttonRightClicked() { 44 | this.dialogRef.close(false); 45 | } 46 | } -------------------------------------------------------------------------------- /src/app/components/genericPayloadQRDialog.scss: -------------------------------------------------------------------------------- 1 | .break-words { 2 | white-space: pre-wrap; 3 | } 4 | 5 | .mat-dialog-content { 6 | margin: 0px; 7 | padding: 0px; 8 | } 9 | 10 | .break-words { 11 | word-wrap: break-word !important; 12 | white-space: pre-wrap; 13 | overflow-wrap: break-word; 14 | word-wrap: break-word; 15 | 16 | word-break: break-word; 17 | 18 | -ms-hyphens: auto; 19 | -moz-hyphens: auto; 20 | -webkit-hyphens: auto; 21 | hyphens: auto; 22 | } 23 | 24 | @media (max-width:599px) { 25 | .mat-dialog-content { 26 | font-size: 0.8em; 27 | } 28 | } -------------------------------------------------------------------------------- /src/app/components/tokenList/tokenList.css: -------------------------------------------------------------------------------- 1 | 2 | .mat-cell:hover{ 3 | cursor: pointer; 4 | } 5 | 6 | @media (max-width:599px) { 7 | 8 | .mat-footer-row , .mat-row { 9 | padding-top: 0.5em; 10 | align-items: start; 11 | min-height: 24px; 12 | cursor: pointer; 13 | } 14 | 15 | .mat-cell { 16 | padding-left: 1em; 17 | } 18 | 19 | .mat-column-destination { 20 | word-wrap: break-word !important; 21 | white-space: unset !important; 22 | overflow-wrap: break-word; 23 | word-wrap: break-word; 24 | 25 | word-break: break-word; 26 | 27 | -ms-hyphens: auto; 28 | -moz-hyphens: auto; 29 | -webkit-hyphens: auto; 30 | hyphens: auto; 31 | } 32 | 33 | .mat-column-amount { 34 | word-wrap: break-word !important; 35 | white-space: unset !important; 36 | overflow-wrap: break-word; 37 | word-wrap: break-word; 38 | 39 | word-break: break-word; 40 | 41 | -ms-hyphens: auto; 42 | -moz-hyphens: auto; 43 | -webkit-hyphens: auto; 44 | hyphens: auto; 45 | } 46 | 47 | .mat-column-finishafter { 48 | word-wrap: break-word !important; 49 | white-space: unset !important; 50 | overflow-wrap: break-word; 51 | word-wrap: break-word; 52 | 53 | word-break: break-word; 54 | 55 | -ms-hyphens: auto; 56 | -moz-hyphens: auto; 57 | -webkit-hyphens: auto; 58 | hyphens: auto; 59 | } 60 | 61 | .mat-column-cancelafter { 62 | word-wrap: break-word !important; 63 | white-space: unset !important; 64 | overflow-wrap: break-word; 65 | word-wrap: break-word; 66 | 67 | word-break: break-word; 68 | 69 | -ms-hyphens: auto; 70 | -moz-hyphens: auto; 71 | -webkit-hyphens: auto; 72 | hyphens: auto; 73 | } 74 | 75 | .mat-column-condition { 76 | word-wrap: break-word !important; 77 | white-space: unset !important; 78 | overflow-wrap: break-word; 79 | word-wrap: break-word; 80 | 81 | word-break: break-word; 82 | 83 | -ms-hyphens: auto; 84 | -moz-hyphens: auto; 85 | -webkit-hyphens: auto; 86 | hyphens: auto; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/app/components/tokenList/tokenList.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | Select a Token from the list below 11 | 12 | 13 | 14 | 15 | 16 | Token Currency Code 17 | 18 | {{getCurrencyCode(token.currency)}} 19 | Token Currency Code:
{{getCurrencyCode(token.currency)}}
20 |
21 |
22 | 23 | 24 | Total issued value of this currency 25 | 26 | {{stringToFloat(token.amount)}} 27 | Total issued value:
{{stringToFloat(token.amount)}}
28 |
29 |
30 | 31 | 32 | 33 | 34 |
35 |
36 |
-------------------------------------------------------------------------------- /src/app/components/tokenList/tokenList.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input, Output, EventEmitter, OnDestroy } from "@angular/core"; 2 | import { Observable, Subscription } from 'rxjs'; 3 | import { XrplAccountChanged, Token } from 'src/app/utils/types'; 4 | import * as normalizer from 'src/app/utils/normalizers'; 5 | import { AppService } from "src/app/services/app.service"; 6 | import { XRPLWebsocket } from "src/app/services/xrplWebSocket"; 7 | 8 | @Component({ 9 | selector: "tokenList", 10 | templateUrl: "tokenList.html", 11 | styleUrls: ['./tokenList.css'] 12 | }) 13 | export class TokenList implements OnInit, OnDestroy { 14 | 15 | @Input() 16 | issuerAccountChanged: Observable; 17 | 18 | @Output() 19 | issuerCurrencySelected: EventEmitter = new EventEmitter(); 20 | 21 | tokenList:Token[] = []; 22 | displayedColumns: string[] = ['currency', 'amount']; 23 | loading:boolean = false; 24 | testMode:boolean = false; 25 | originalIsserAccount:string; 26 | originalTestModeValue:boolean = false; 27 | tokenClicked:boolean = false; 28 | 29 | private tokenAccountChangedSubscription: Subscription; 30 | 31 | constructor(private xrplWebSocket: XRPLWebsocket, private app: AppService) {} 32 | 33 | ngOnInit() { 34 | this.tokenAccountChangedSubscription = this.issuerAccountChanged.subscribe(accountData => { 35 | //console.log("token account changed received: " + JSON.stringify(accountData)); 36 | 37 | if(accountData.account) { 38 | this.originalIsserAccount = accountData.account; 39 | this.testMode = accountData.mode; 40 | 41 | this.loadTokenList(this.originalIsserAccount); 42 | } 43 | else { 44 | this.originalIsserAccount = null; 45 | this.tokenList = []; 46 | } 47 | }); 48 | } 49 | 50 | ngOnDestroy() { 51 | if(this.tokenAccountChangedSubscription) 52 | this.tokenAccountChangedSubscription.unsubscribe(); 53 | } 54 | 55 | async loadTokenList(xrplAccount: string) { 56 | //console.log("loading Token list for: " + xrplAccount); 57 | 58 | if(xrplAccount) { 59 | this.loading = true; 60 | 61 | try { 62 | 63 | this.tokenList = []; 64 | 65 | if(!this.testMode) { 66 | 67 | let xrpscanResponse:any = await this.app.get("https://api.xrpscan.com/api/v1/account/"+xrplAccount+"/obligations?origin=https://xrpl.services") 68 | 69 | if(xrpscanResponse && xrpscanResponse.length > 0) { 70 | for(let i = 0; i < xrpscanResponse.length; i++) { 71 | this.tokenList.push({currency: xrpscanResponse[i].currency, amount: xrpscanResponse[i].value}); 72 | } 73 | 74 | if(this.tokenList.length > 1) 75 | this.tokenList = this.tokenList.sort((tokenA, tokenB) => this.getCurrencyCode(tokenA.currency).localeCompare(this.getCurrencyCode(tokenB.currency))); 76 | 77 | //if data 0 (no available tokens) -> show message "no tokens available" 78 | if(this.tokenList && this.tokenList.length == 0) 79 | this.tokenList = null; 80 | 81 | } else { 82 | this.tokenList = null; 83 | } 84 | } else { 85 | let gateway_balances_request:any = { 86 | command: "gateway_balances", 87 | account: xrplAccount, 88 | ledger_index: "validated", 89 | } 90 | 91 | let message:any = await this.xrplWebSocket.getWebsocketMessage("tokenList", gateway_balances_request, this.testMode); 92 | 93 | if(message && message.status && message.status === 'success' && message.type && message.type === 'response' && message.result && message.result.obligations) { 94 | this.tokenList = []; 95 | let obligations:any = message.result.obligations; 96 | 97 | if(obligations) { 98 | for (var currency in obligations) { 99 | if (obligations.hasOwnProperty(currency)) { 100 | this.tokenList.push({currency: currency, amount: obligations[currency]}); 101 | } 102 | } 103 | 104 | this.tokenList = this.tokenList.sort((tokenA, tokenB) => this.getCurrencyCode(tokenA.currency).localeCompare(this.getCurrencyCode(tokenB.currency))); 105 | } 106 | 107 | //if data 0 (no available tokens) -> show message "no tokens available" 108 | if(this.tokenList && this.tokenList.length == 0) 109 | this.tokenList = null; 110 | 111 | //console.log(JSON.stringify(this.tokenList)); 112 | } else { 113 | this.tokenList = null; 114 | } 115 | } 116 | } catch(err) { 117 | console.log(err); 118 | } 119 | 120 | this.loading = false; 121 | } 122 | } 123 | 124 | tokenSelected(token: Token) { 125 | //console.log("token selected: " + JSON.stringify(token)); 126 | this.issuerCurrencySelected.emit(token) 127 | } 128 | 129 | stringToFloat(number: string): number { 130 | return parseFloat(number); 131 | } 132 | 133 | getCurrencyCode(currency: string): string { 134 | return normalizer.normalizeCurrencyCodeXummImpl(currency); 135 | } 136 | } -------------------------------------------------------------------------------- /src/app/components/tools/multiSignFlow.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | group_add 5 | Xaman Multi Sign 6 | 7 | 8 | In Planning ... 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | In Planning ... 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/app/components/tools/multiSignFlow.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'multiSignFlow', 5 | templateUrl: './multiSignFlow.html' 6 | }) 7 | export class MultiSignFlowComponent { 8 | } 9 | -------------------------------------------------------------------------------- /src/app/components/tools/norippleCheck.css: -------------------------------------------------------------------------------- 1 | @media (min-width:600px) { 2 | 3 | .mat-column-problem { 4 | word-wrap: break-word !important; 5 | white-space: unset !important; 6 | flex: 0 0 90% !important; 7 | width: 90% !important; 8 | overflow-wrap: break-word; 9 | word-wrap: break-word; 10 | 11 | word-break: break-word; 12 | 13 | -ms-hyphens: auto; 14 | -moz-hyphens: auto; 15 | -webkit-hyphens: auto; 16 | hyphens: auto; 17 | } 18 | 19 | .mat-column-fix { 20 | word-wrap: break-word !important; 21 | white-space: unset !important; 22 | flex: 0 0 10% !important; 23 | width: 10% !important; 24 | overflow-wrap: break-word; 25 | word-wrap: break-word; 26 | 27 | word-break: break-word; 28 | 29 | -ms-hyphens: auto; 30 | -moz-hyphens: auto; 31 | -webkit-hyphens: auto; 32 | hyphens: auto; 33 | justify-content: center; 34 | text-align: center; 35 | } 36 | } 37 | 38 | @media (max-width:599px) { 39 | 40 | .mat-footer-row , .mat-row { 41 | padding-top: 0.5em; 42 | align-items: start; 43 | min-height: 24px; 44 | } 45 | 46 | .mat-cell { 47 | padding-left: 1em; 48 | } 49 | 50 | .mat-column-problem { 51 | word-wrap: break-word !important; 52 | white-space: unset !important; 53 | overflow-wrap: break-word; 54 | word-wrap: break-word; 55 | 56 | word-break: break-word; 57 | 58 | -ms-hyphens: auto; 59 | -moz-hyphens: auto; 60 | -webkit-hyphens: auto; 61 | hyphens: auto; 62 | } 63 | 64 | .mat-column-fix { 65 | word-wrap: break-word !important; 66 | white-space: unset !important; 67 | overflow-wrap: break-word; 68 | word-wrap: break-word; 69 | 70 | word-break: break-word; 71 | 72 | -ms-hyphens: auto; 73 | -moz-hyphens: auto; 74 | -webkit-hyphens: auto; 75 | hyphens: auto; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/app/components/tools/norippleCheck.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | playlist_add_check 5 | Check Trust Lines Settings 6 | 7 | 8 | Check the status of the DefaultRipple field for an account and the NoRipple flag of its trust lines 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | Checks Trustline Settings 17 | 18 | 19 | 20 |
21 | no-ripple check Documentation
22 | 23 | 24 |
25 | 26 | 27 | Not a valid XRPL address 28 | 29 |
30 | 31 | 32 | 33 |
34 | 35 | 36 |
37 | 38 | 39 |
40 | 41 |
42 | 43 | 44 | 45 | Problem description 46 | 47 | {{problem.problem}} 48 | Problem:
{{problem.problem}}
49 |
50 |
51 | 52 | 53 | Fix Problem 54 | 55 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 |
66 |
67 |
68 |
-------------------------------------------------------------------------------- /src/app/components/tools/rawTransactions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | code 5 | Raw JSON Transactions 6 | 7 | 8 | Create raw XRP Ledger transactions in JSON format and send it to your Xaman app! 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | Craft XRPL transactions 17 | 18 | 19 | 20 |
21 |
22 | 23 | 24 |
25 |
26 | 27 | Select a template 28 | 29 | 30 | {{selectedTemplate.transactionType}} 31 | 32 | 33 |
34 | 35 | build 36 |
37 |
38 |
39 |
40 |
41 | 42 |
43 | 46 |
47 | 48 |
49 | 50 |
51 | 52 | 53 | 54 |
55 | 58 | 59 |
60 |
61 |
-------------------------------------------------------------------------------- /src/app/components/tools/rawTransactions.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, Output, EventEmitter, ViewChild, OnInit, OnDestroy } from '@angular/core'; 2 | import { Observable, Subscription } from 'rxjs'; 3 | import { AccountInfoChanged, GenericBackendPostRequest, TransactionTemplate } from 'src/app/utils/types'; 4 | import { XummTypes } from 'xumm-sdk'; 5 | import { LocalStorageService } from 'angular-2-local-storage'; 6 | import { UtilService } from '../../services/util.service'; 7 | import { MatExpansionPanel } from '@angular/material/expansion'; 8 | 9 | @Component({ 10 | selector: 'rawTransactions', 11 | templateUrl: './rawTransactions.html' 12 | }) 13 | export class RawTransactionsComponent implements OnInit, OnDestroy { 14 | constructor(private utilService: UtilService, private localStorage: LocalStorageService) { } 15 | 16 | @Input() 17 | accountInfoChanged: Observable; 18 | 19 | @Input() 20 | transactionSuccessfull: Observable; 21 | 22 | @Output() 23 | onPayload: EventEmitter = new EventEmitter(); 24 | 25 | @ViewChild('inprawjsontransaction') inprawjsontransaction; 26 | rawJsonTransaction: string = "{}"; 27 | 28 | @ViewChild('mep', {static: true}) mep: MatExpansionPanel; 29 | 30 | originalAccountInfo:any = null; 31 | isValidJson:boolean = false; 32 | errorMsg:string; 33 | 34 | private accountInfoChangedSubscription: Subscription; 35 | private darkModeChangedSubscription: Subscription; 36 | 37 | editorOptions = { 38 | tabSize: 2, 39 | mode: 'application/json', 40 | theme: 'material-darker', 41 | lineNumbers: true, 42 | line: true 43 | }; 44 | 45 | transactionTemplates:TransactionTemplate[]; 46 | selectedTemplate:TransactionTemplate; 47 | loadingTemplates:boolean = false; 48 | 49 | 50 | async ngOnInit() { 51 | if(this.localStorage.get("darkMode")) 52 | this.editorOptions.theme = "material-darker"; 53 | else 54 | this.editorOptions.theme = "default"; 55 | 56 | this.accountInfoChangedSubscription = this.accountInfoChanged.subscribe(accountData => { 57 | //console.log("account info changed received: " + JSON.stringify(accountData)); 58 | if(accountData) { 59 | this.originalAccountInfo = accountData.info; 60 | this.loadTransactionTemplates(); 61 | } else { 62 | this.originalAccountInfo = null; 63 | } 64 | }); 65 | 66 | this.darkModeChangedSubscription = this.localStorage.setItems$.subscribe(value => { 67 | //console.log(JSON.stringify(value)); 68 | 69 | if(value.key === "darkMode") { 70 | if(value.newvalue === "true") 71 | this.editorOptions = Object.assign({}, this.editorOptions, { theme: 'material-darker' }); 72 | else 73 | this.editorOptions = Object.assign({}, this.editorOptions, { theme: 'default' }); 74 | } 75 | }); 76 | 77 | this.loadTransactionTemplates(); 78 | } 79 | 80 | async loadTransactionTemplates() { 81 | this.loadingTemplates = true; 82 | 83 | this.transactionTemplates = await this.utilService.getTransactionTypes(this.originalAccountInfo && this.originalAccountInfo.Account ? this.originalAccountInfo.Account : null); 84 | 85 | for(let i = 0; i < this.transactionTemplates.length; i++) { 86 | delete this.transactionTemplates[i].codeSamples[0].Sequence; 87 | delete this.transactionTemplates[i].codeSamples[0].Fee; 88 | } 89 | 90 | this.transactionTemplates = [{transactionType: "None", docLink: null, requiresAmendment: false, codeSamples: [{}]}].concat(this.transactionTemplates) 91 | 92 | this.loadingTemplates = false; 93 | } 94 | 95 | ngOnDestroy() { 96 | if(this.accountInfoChangedSubscription) 97 | this.accountInfoChangedSubscription.unsubscribe(); 98 | 99 | if(this.darkModeChangedSubscription != null) 100 | this.darkModeChangedSubscription.unsubscribe(); 101 | } 102 | 103 | checkJson() { 104 | try { 105 | let json:any = JSON.parse(this.rawJsonTransaction); 106 | if(json.TransactionType) 107 | this.isValidJson = true; 108 | else { 109 | this.isValidJson = false; 110 | this.errorMsg = "Field 'TransactionType' required"; 111 | } 112 | } catch(err) { 113 | this.isValidJson = false; 114 | this.errorMsg = "Invalid JSON format"; 115 | } 116 | } 117 | 118 | sendToXumm() { 119 | 120 | let payload:XummTypes.XummPostPayloadBodyJson = { 121 | txjson: JSON.parse(this.rawJsonTransaction), 122 | options: { 123 | 124 | } 125 | } 126 | this.onPayload.next({options: {isRawTrx: true}, payload: payload}); 127 | } 128 | 129 | changeTemplate() { 130 | //console.log("Selected template: " + JSON.stringify(this.selectedTemplate)); 131 | this.rawJsonTransaction = JSON.stringify(this.selectedTemplate.codeSamples[0], null, ' '); 132 | this.checkJson(); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/app/components/tools/testNetCredentials.css: -------------------------------------------------------------------------------- 1 | @media (max-width:599px) { 2 | 3 | .break-words { 4 | word-wrap: break-word !important; 5 | white-space: unset !important; 6 | overflow-wrap: break-word; 7 | word-wrap: break-word; 8 | 9 | word-break: break-word; 10 | 11 | -ms-hyphens: auto; 12 | -moz-hyphens: auto; 13 | -webkit-hyphens: auto; 14 | hyphens: auto; 15 | } 16 | 17 | .final-container { 18 | margin-left: 20px !important; 19 | } 20 | 21 | ::ng-deep .mat-stepper-vertical { 22 | font-size: 0.9em !important; 23 | padding-left: 5px !important; 24 | } 25 | 26 | ::ng-deep .mat-step-header { 27 | padding: 10px !important; 28 | } 29 | 30 | ::ng-deep .mat-vertical-content-container { 31 | margin-left: 20px !important; 32 | } 33 | 34 | ::ng-deep .mat-vertical-content { 35 | padding: 0 12px 12px 12px !important; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/app/components/tools/testNetCredentials.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChild } from '@angular/core'; 2 | import { AppService } from '../../services/app.service'; 3 | import * as qrcode from 'qrcode'; 4 | import * as clipboard from 'copy-to-clipboard'; 5 | import { MatStepper } from '@angular/material/stepper'; 6 | import { MatSnackBar } from '@angular/material/snack-bar'; 7 | 8 | @Component({ 9 | selector: 'testNetCredentials', 10 | templateUrl: './testNetCredentials.html', 11 | styleUrls: ['./testNetCredentials.css'] 12 | }) 13 | export class TestNetCredentialsComponent { 14 | 15 | constructor(private app: AppService, private snackBar: MatSnackBar) {} 16 | 17 | loading:boolean = false; 18 | error:string = null; 19 | qrCode: string = null; 20 | newAccount:any = null; 21 | checkBoxConsent:boolean = false; 22 | checkBoxXummConnected:boolean = false; 23 | checkBoxQrScannerOpened:boolean = false; 24 | checkBoxAccountAdded:boolean = false; 25 | 26 | @ViewChild('stepper') stepper: MatStepper; 27 | 28 | async createTestNetCredentials(): Promise { 29 | this.loading = true; 30 | this.newAccount = null; 31 | this.qrCode = null; 32 | this.error = null; 33 | 34 | try { 35 | //call ripple servers to get testnet credentials with a balance 36 | this.newAccount = await this.app.post("https://faucet.altnet.rippletest.net/accounts", {}); 37 | //this.newAccount = {"account":{"xAddress":"TVrRjmCE1twUKGbFBhdyR12pXqitZtAv6PjXB3E33Nm8hTQ","seed":"snEtbXeqo7f7Bg2oc552CV6yct4QW","classicAddress":"rUwnrWz9PnV7oFcFcpdkEjCbVUAEaZCgso","address":"rUwnrWz9PnV7oFcFcpdkEjCbVUAEaZCgso"},"amount":1000,"balance":1000}; 38 | console.log("credentials: " + JSON.stringify(this.newAccount)); 39 | 40 | if(this.newAccount && this.newAccount.seed) 41 | this.qrCode = await qrcode.toDataURL(this.newAccount.seed); 42 | 43 | if(!this.newAccount || !this.qrCode) 44 | this.error = "Something went wrong. Please try again later! If the error persists, please report via twitter @XrplServices!" 45 | 46 | } catch(err) { 47 | console.log(err); 48 | this.newAccount = null; 49 | this.qrCode = null; 50 | this.error = "Could not load test net credentials. Please try again later! If the error persists, please report via twitter @XrplServices!"; 51 | } 52 | 53 | this.loading = false; 54 | 55 | } 56 | 57 | copyFamilySeed() { 58 | if(this.newAccount && this.newAccount.seed) { 59 | clipboard(this.newAccount.seed); 60 | this.snackBar.dismiss(); 61 | this.snackBar.open("Family Seed copied to clipboard!", null, {panelClass: 'snackbar-success', duration: 3000, horizontalPosition: 'center', verticalPosition: 'bottom'}); 62 | } 63 | } 64 | 65 | moveNext() { 66 | // complete the current step 67 | this.stepper.selected.completed = true; 68 | this.stepper.selected.editable = false; 69 | // move to next step 70 | this.stepper.next(); 71 | this.stepper.selected.editable = true; 72 | } 73 | 74 | moveBack() { 75 | //console.log("steps: " + this.stepper.steps.length); 76 | // move to previous step 77 | this.stepper.selected.completed = false; 78 | this.stepper.selected.editable = false; 79 | 80 | this.stepper.steps.forEach((item, index) => { 81 | if(index == this.stepper.selectedIndex-1 && this.stepper.selectedIndex-1 >= 0) { 82 | item.editable = true; 83 | item.completed = false; 84 | } 85 | }); 86 | 87 | switch(this.stepper.selectedIndex) { 88 | case 0: break; 89 | case 1: { 90 | this.checkBoxConsent = false; 91 | this.checkBoxXummConnected = false; 92 | break; 93 | } 94 | case 2: { 95 | this.checkBoxXummConnected = false; 96 | this.checkBoxQrScannerOpened = false; 97 | break; 98 | } 99 | case 3: { 100 | this.checkBoxQrScannerOpened = false; 101 | this.checkBoxAccountAdded = false; 102 | this.newAccount = null; 103 | this.qrCode = null; 104 | this.error = null; 105 | this.loading = false; 106 | break; 107 | } 108 | case 4: { 109 | this.checkBoxAccountAdded = false; 110 | break; 111 | } 112 | } 113 | 114 | this.stepper.previous(); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/app/components/tools/transactionScheduler.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | alarm 5 | Escrow Releaser 6 | 7 | 8 | Set your Escrows to be auto released! 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | Auto release your Escrows! 17 | 18 | 19 | 20 |
21 | 22 | 23 |
24 | 25 |
26 | 27 |
28 | 29 |
-------------------------------------------------------------------------------- /src/app/components/tools/transactionScheduler.ts: -------------------------------------------------------------------------------- 1 | import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core'; 2 | import { Observable, Subject, Subscription } from 'rxjs'; 3 | import { AccountInfoChanged, GenericBackendPostRequest, XrplAccountChanged } from 'src/app/utils/types'; 4 | import { isValidXRPAddress } from 'src/app/utils/utils'; 5 | import { ActivatedRoute } from '@angular/router'; 6 | import { MatExpansionPanel } from '@angular/material/expansion'; 7 | 8 | @Component({ 9 | selector: 'transactionScheduler', 10 | templateUrl: './transactionScheduler.html' 11 | }) 12 | export class TransactionSchedulerComponent { 13 | 14 | @Input() 15 | accountInfoChanged: Observable; 16 | 17 | @Input() 18 | transactionSuccessfull: Observable; 19 | 20 | @Output() 21 | onPayload: EventEmitter = new EventEmitter(); 22 | 23 | @ViewChild('trxs', {static: true}) trxs: MatExpansionPanel; 24 | 25 | originalAccountInfo:any = null; 26 | originalTestModeValue:boolean = false; 27 | isTestMode:boolean = false; 28 | 29 | accountNotFound:boolean = false; 30 | 31 | private accountInfoChangedSubscription: Subscription; 32 | private transactionSuccessfullSubscription: Subscription; 33 | escrowAccountChanged: Subject = new Subject(); 34 | 35 | constructor(private route: ActivatedRoute) {} 36 | 37 | ngOnInit() { 38 | 39 | this.route.queryParams.subscribe(async params => { 40 | let openEscrowReleaser = params.escrowReleaser; 41 | 42 | if(openEscrowReleaser && "open" == openEscrowReleaser) { 43 | this.trxs.open(); 44 | } 45 | }); 46 | 47 | this.accountInfoChangedSubscription = this.accountInfoChanged.subscribe(accountData => { 48 | //console.log("account info changed received: " + JSON.stringify(accountData)); 49 | if(accountData) { 50 | this.originalAccountInfo = accountData.info; 51 | this.isTestMode = accountData.mode; 52 | 53 | if(this.originalAccountInfo && this.originalAccountInfo.Account && isValidXRPAddress(this.originalAccountInfo.Account)) { 54 | this.escrowAccountChanged.next({account: this.originalAccountInfo.Account, mode: accountData.mode}); 55 | } 56 | } else { 57 | this.originalAccountInfo = null; 58 | } 59 | }); 60 | } 61 | 62 | ngOnDestroy() { 63 | if(this.accountInfoChangedSubscription) 64 | this.accountInfoChangedSubscription.unsubscribe(); 65 | 66 | if(this.transactionSuccessfullSubscription) 67 | this.transactionSuccessfullSubscription.unsubscribe(); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/app/components/tools/unlChecker.css: -------------------------------------------------------------------------------- 1 | .break-words { 2 | word-wrap: break-word !important; 3 | white-space: unset !important; 4 | overflow-wrap: break-word; 5 | word-wrap: break-word; 6 | 7 | word-break: break-word; 8 | 9 | -ms-hyphens: auto; 10 | -moz-hyphens: auto; 11 | -webkit-hyphens: auto; 12 | hyphens: auto; 13 | } -------------------------------------------------------------------------------- /src/app/components/tools/unlChecker.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | playlist_add_check 5 | UNL Checker 6 | 7 | 8 | Checks and parses an UNL 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | Checks and parses an UNL 17 | 18 | 19 | 20 |
21 | 22 | 23 |
24 | 25 | 26 | 27 |
28 | 29 |
30 | 31 |
32 | 33 | 34 |
35 |
36 |
37 | 38 | 39 |
40 | 41 | 42 | 43 |
44 |
45 | 46 | 47 |
48 | 49 |
50 |
51 | 52 | 53 |
54 |
55 | 56 | 57 |
58 |
59 | 60 | 61 |
62 |
63 | 64 | 65 |
66 |
67 | 68 | 69 |
70 |
71 | 72 | 73 |
74 |
75 | 76 | 77 |
78 |
79 | 80 | 81 | 82 | 83 | 84 |
85 |
86 | 87 | 88 |
89 |
90 |

Validators:

91 |
92 |
93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 |
112 |
113 |
114 |
115 |
-------------------------------------------------------------------------------- /src/app/components/topbar.ts: -------------------------------------------------------------------------------- 1 | import { Component, Output, EventEmitter, OnInit } from '@angular/core'; 2 | import { MatDialog } from '@angular/material/dialog'; 3 | import { MatSnackBar } from '@angular/material/snack-bar'; 4 | import { GenericPayloadQRDialog } from '../components/genericPayloadQRDialog'; 5 | import { GenericBackendPostRequest, TransactionValidation } from '../utils/types'; 6 | import { XummTypes } from 'xumm-sdk'; 7 | import { LocalStorageService } from 'angular-2-local-storage' 8 | import { OverlayContainer } from '@angular/cdk/overlay'; 9 | import { TypeWriter } from '../utils/TypeWriter'; 10 | import { XummService } from '../services/xumm.service'; 11 | 12 | @Component({ 13 | selector: 'app-topbar', 14 | templateUrl: './topbar.html' 15 | }) 16 | export class TopbarComponent implements OnInit { 17 | 18 | @Output() 19 | darkThemeChanged: EventEmitter = new EventEmitter(); 20 | 21 | isDarkTheme: boolean; 22 | 23 | title: string = "XRPL Services"; 24 | tw: TypeWriter 25 | 26 | loadingBackend:boolean = true; 27 | backendAvailable:boolean = false; 28 | 29 | constructor( private xummApi: XummService, private supportDialog: MatDialog, private snackBar: MatSnackBar, private localStorage: LocalStorageService, private overlayContainer: OverlayContainer) { 30 | 31 | } 32 | 33 | async ngOnInit(): Promise { 34 | this.loadingBackend = true; 35 | this.backendAvailable = await this.xummApi.ping(); 36 | this.loadingBackend = false; 37 | //console.log("backendAvailable: " + this.backendAvailable) 38 | 39 | this.isDarkTheme = this.localStorage.get("darkMode"); 40 | this.setOverlayClass(); 41 | 42 | this.tw = new TypeWriter(["XRP Ledger Services", "created by nixerFFM", "XRP Ledger Services"], t => { 43 | this.title = t; 44 | }) 45 | 46 | this.tw.start(); 47 | } 48 | 49 | toggleDarkTheme() { 50 | this.isDarkTheme = !this.isDarkTheme; 51 | this.darkThemeChanged.emit(this.isDarkTheme); 52 | this.localStorage.set("darkMode", this.isDarkTheme); 53 | this.setOverlayClass(); 54 | } 55 | 56 | setOverlayClass() { 57 | if(!this.isDarkTheme) { 58 | this.overlayContainer.getContainerElement().classList.remove('dark-theme'); 59 | this.overlayContainer.getContainerElement().classList.add('light-theme'); 60 | } else { 61 | this.overlayContainer.getContainerElement().classList.remove('light-theme'); 62 | this.overlayContainer.getContainerElement().classList.add('dark-theme'); 63 | } 64 | } 65 | 66 | async supportViaXumm() { 67 | //setting up xumm payload and waiting for websocket 68 | let xummPayload:XummTypes.XummPostPayloadBodyJson = { 69 | txjson: { 70 | TransactionType: "Payment" 71 | }, 72 | custom_meta: { 73 | instruction: "You are about to send some love to xrpl.services,\na project by @nixerFFM and not affiliated with XRPLLabs,\nthe creator of the Xaman wallet.\n\nThank you for your kindness, we appreciate you!!", 74 | blob: { 75 | isDonation: true, 76 | purpose: "freiwillige Zahlung für xrpl.services Webseite" 77 | } 78 | } 79 | } 80 | 81 | this.openGenericDialog(xummPayload); 82 | } 83 | 84 | openGenericDialog(xummPayload: XummTypes.XummPostPayloadBodyJson):void { 85 | let genericBackendRequest:GenericBackendPostRequest = { 86 | options: { 87 | xrplAccount: this.localStorage.get("xrplAccount"), 88 | referer: 'abcde' 89 | }, 90 | payload: xummPayload 91 | } 92 | 93 | const dialogRef = this.supportDialog.open(GenericPayloadQRDialog, { 94 | width: 'auto', 95 | height: 'auto;', 96 | data: genericBackendRequest 97 | }); 98 | 99 | dialogRef.afterClosed().subscribe((transactionInfo:TransactionValidation) => { 100 | //console.log('The generic dialog was closed: ' + JSON.stringify(transactionInfo)); 101 | 102 | if(transactionInfo && transactionInfo.success) { 103 | if(!transactionInfo.testnet) 104 | this.snackBar.open("Thank you so much for your donation!", null, {panelClass: 'snackbar-success', duration: 5000, horizontalPosition: 'center', verticalPosition: 'top'}); 105 | else 106 | this.snackBar.open("Your donation was submitted to the testnet. Thank you! But please consider sending a 'real' donation. :-)", null, {panelClass: 'snackbar-failed', duration: 5000, horizontalPosition: 'center', verticalPosition: 'top'}); 107 | } 108 | }); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/app/components/transactions/accountdelete.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | delete 5 | Account Delete 6 | 7 | 8 | Delete your XRP Ledger account to get back {{(accountReserve-ownerReserve)/1000000}} XRP of the wallet reserve. 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | Delete your XRPL account 17 | 18 | 19 | 20 |
21 | Account Delete Documentation 22 | 23 | 24 |
25 | 26 | 27 |
28 |
29 | 30 | 31 | 32 | 33 | 34 |
35 |
36 | 37 | 38 | 39 | Not a valid XRPL address 40 | You cannot set the Destination address to the logged in address. 41 | This account does not exist on the XRP Ledger {{isTestMode? 'TEST' : 'MAIN'}} net. You need to activate it first with {{accountReserve}} XRP. 42 | 43 |
44 | 45 | 46 |
47 | 48 | 49 | Not a valid Destination Tag. Only whole numbers between 0 and 4294967295 allowed! 50 | 51 | 52 |
53 |
54 | I understand to NOT use an exchange or other custodial wallet as destination account because I might lose my funds! 55 |
56 |
57 |
58 | 61 | Please confirm the checkbox above. 62 |
63 |
64 |
65 |
-------------------------------------------------------------------------------- /src/app/components/transactions/escrowcancel.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | cancel 5 | Escrow Cancel 6 | 7 | 8 | Cancel an Escrow to return the funds back to the Escrow Owner Account. 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | Cancel an Escrow 17 | 18 | 19 | 20 |
21 | Escrow Cancel Documentation
22 | 23 |
24 | 25 | 26 | Not a valid XRPL address 27 | 28 |
29 | 30 |
31 | 32 | 33 | Not a valid Escrow Sequence 34 | 35 |
36 |
37 | 40 | 41 | 42 |
43 |
44 |
-------------------------------------------------------------------------------- /src/app/components/transactions/escrowfinish.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | send 5 | Escrow Finish 6 | 7 | 8 | Execute an Escrow Finish transaction to release your funds in Escrow. 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | Release Escrow Funds 17 | 18 | 19 | 20 |
21 | 22 |
23 | 24 |
25 | 26 |
27 | Escrow Finish Documentation
28 | 29 |
30 | 31 | 32 | Not a valid XRPL address 33 | Escrow Owner changed. Your inserted account was the destination address. 34 | 35 |
36 | 37 |
38 | 39 | 40 | Not a valid Escrow Sequence 41 | 42 | 43 | 44 | Optional. The password you choose when creating the Escrow. 45 | The password you choose when creating the Escrow. 46 | 49 | 50 |
51 |
52 | 55 | 56 | 57 |
58 |
59 |
60 |
-------------------------------------------------------------------------------- /src/app/components/transactions/setregularkey.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | vpn_key 5 | Set Regular Key 6 | 7 | 8 | Authorize a secondary key pair, called a regular key pair, to sign future transactions. 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | Set secondary key pair 17 | 18 | 19 | 20 |
21 | Set Regular Key Documentation
22 | 23 |
24 | 25 | 26 | Not a valid XRPL address 27 | You cannot set the Regular Key to the same address. 28 | 29 |
30 |
31 | 34 | 37 |
38 | Befor you can delete your Regular Key you have to set an alternative signing method. (Master Key or SignerList) 39 |
40 |
-------------------------------------------------------------------------------- /src/app/components/transactions/setregularkey.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, ViewChild, Output, EventEmitter, Input, OnDestroy } from '@angular/core'; 2 | import { Subscription, Observable } from 'rxjs'; 3 | import * as flagsutil from '../../utils/flagutils'; 4 | import { XummTypes } from 'xumm-sdk'; 5 | import { AccountInfoChanged, AccountObjectsChanged } from 'src/app/utils/types'; 6 | import { isValidXRPAddress } from 'src/app/utils/utils'; 7 | 8 | @Component({ 9 | selector: 'setregularkey', 10 | templateUrl: './setregularkey.html' 11 | }) 12 | export class SetRegularKeyComponent implements OnInit, OnDestroy { 13 | 14 | @Input() 15 | accountInfoChanged: Observable; 16 | 17 | @Input() 18 | accountObjectsChanged: Observable; 19 | 20 | @Input() 21 | transactionSuccessfull: Observable; 22 | 23 | @Output() 24 | onPayload: EventEmitter = new EventEmitter(); 25 | 26 | @ViewChild('inpregularkey') inpregularkey; 27 | regularKeyInput: string; 28 | 29 | private accountInfoChangedSubscription: Subscription; 30 | private accountObjectsChangedSubscription: Subscription; 31 | originalAccountInfo:any; 32 | signerList:any; 33 | 34 | private transactionSuccessfullSubscription: Subscription; 35 | 36 | validAddress = false; 37 | 38 | private payload:XummTypes.XummPostPayloadBodyJson = { 39 | txjson: { 40 | TransactionType: "SetRegularKey" 41 | } 42 | } 43 | 44 | ngOnInit() { 45 | this.accountInfoChangedSubscription = this.accountInfoChanged.subscribe(accountData => { 46 | //console.log("account info changed received") 47 | this.originalAccountInfo = accountData.info; 48 | 49 | if(this.originalAccountInfo && this.originalAccountInfo.RegularKey) { 50 | this.regularKeyInput = this.originalAccountInfo.RegularKey; 51 | this.checkChanges(); 52 | } else { 53 | this.clearInputs(); 54 | } 55 | }); 56 | 57 | this.accountObjectsChangedSubscription = this.accountObjectsChanged.subscribe(accountObjects => { 58 | //console.log("account objects changed received") 59 | if(accountObjects && accountObjects.objects) { 60 | this.signerList = accountObjects.objects.filter(object => object.LedgerEntryType === "SignerList")[0]; 61 | } else { 62 | this.signerList = null; 63 | } 64 | }); 65 | 66 | this.transactionSuccessfullSubscription = this.transactionSuccessfull.subscribe(() => { 67 | this.clearInputs() 68 | }); 69 | } 70 | 71 | ngOnDestroy() { 72 | if(this.transactionSuccessfullSubscription) 73 | this.transactionSuccessfullSubscription.unsubscribe(); 74 | 75 | if(this.accountInfoChangedSubscription) 76 | this.accountInfoChangedSubscription.unsubscribe(); 77 | 78 | if(this.accountObjectsChangedSubscription) 79 | this.accountObjectsChangedSubscription.unsubscribe(); 80 | } 81 | 82 | sendPayloadToXumm() { 83 | 84 | if(this.regularKeyInput && this.regularKeyInput.trim().length>0 && this.validAddress) { 85 | this.payload.txjson.RegularKey = this.regularKeyInput.trim(); 86 | this.payload.custom_meta = {} 87 | this.payload.custom_meta.instruction = "Set RegularKey to: " + this.regularKeyInput.trim(); 88 | } 89 | 90 | this.onPayload.emit(this.payload); 91 | } 92 | 93 | checkChanges() { 94 | this.validAddress = this.regularKeyInput && this.regularKeyInput.trim().length > 0 && isValidXRPAddress(this.regularKeyInput.trim()); 95 | 96 | //console.log("validAddress: " + this.validAddress); 97 | } 98 | 99 | hasAlternativeSigningMethod() { 100 | return (this.originalAccountInfo && !flagsutil.isMasterKeyDisabled(this.originalAccountInfo.Flags)) || (this.signerList && this.signerList.SignerEntries && this.signerList.SignerEntries.length > 0); 101 | } 102 | 103 | clearInputs() { 104 | this.regularKeyInput = null; 105 | this.payload = { 106 | txjson: { 107 | TransactionType: "SetRegularKey" 108 | } 109 | } 110 | } 111 | 112 | deleteRegularKey() { 113 | 114 | let payloadToSend:XummTypes.XummPostPayloadBodyJson = { 115 | txjson: { 116 | TransactionType: "SetRegularKey" 117 | }, 118 | custom_meta: { 119 | instruction: 'Delete Regular Key.' 120 | } 121 | } 122 | this.onPayload.emit(payloadToSend); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/app/components/transactions/signerlistset.css: -------------------------------------------------------------------------------- 1 | 2 | @media (max-width:599px) { 3 | 4 | .mat-expansion-panel-header-title { 5 | word-wrap: break-word !important; 6 | white-space: unset !important; 7 | overflow-wrap: break-word; 8 | word-wrap: break-word; 9 | 10 | word-break: break-word; 11 | 12 | -ms-hyphens: auto; 13 | -moz-hyphens: auto; 14 | -webkit-hyphens: auto; 15 | hyphens: auto; 16 | justify-content: start; 17 | text-align: start; 18 | padding: 0 12px; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/app/components/transactions/signerlistset.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | playlist_add_check 5 | Signer List Set 6 | 7 | 8 | Create, replace, or remove a list of signers that can be used to multi-sign a transaction. 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | Set a list of signers 17 | 18 | 19 | 20 |
21 | Signer List Set Documentation 22 | 23 |
24 | 25 | 26 | {{validSignerQuorum.message}} 27 | {{validSignerQuorum.message}} 28 | 29 |
30 | 31 |

Signer List (maximum of 32 XRPL addresses)

32 | Please make sure that the XRPL account this Signer List will be submitted to is not in the list below. Otherwise your transaction will fail. 33 | 34 | 35 | 36 | 37 | 38 |
39 | {{i+1}}. {{signer.SignerEntry.Account}} | Weight:{{signer.SignerEntry.SignerWeight}} 40 |
41 |
42 | {{i+1}}. {{signer.SignerEntry.Account}} error_outline 43 |
44 |
45 | 46 | {{i+1}}.  New Signer List Entry 47 | 48 |
49 | 50 |
51 | 52 | 53 | Not a valid XRPL address 54 | The same XRPL address can only be added once! 55 | The logged in account cannot be one of the signers. 56 | 57 | 58 | 59 | Please provide an integer value 60 | The signature weight must be greater 0 and smaller 65536 61 | 62 |
63 | 66 |
67 |
68 |
69 | 72 |
73 |
74 | 75 |
76 | 79 | 82 |
83 | Befor you can delete your Signer List you have to set an alternative signing method. (Master Key or Regular Key) 84 |
85 |
-------------------------------------------------------------------------------- /src/app/components/transactions/trustset.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | swap_horiz 5 | Trust Set (TrustLines) 6 | 7 | 8 | Create or modify a TrustLine linking two accounts. 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | Create or modify a TrustLine 17 | 18 | 19 | 20 |
21 | Trust Set Documentation
22 | 23 |
24 | 25 |
26 |
27 | 28 | 29 | Not a valid XRPL address 30 | 31 |
32 | 33 |
34 |
35 | 36 | 37 | Not a valid Token currency 38 | 39 | 40 | 41 | Limit cannot have more than 15 decimals. 42 | Not a valid amount. Please use '.' as decimal operator. 43 | Not a valid amount 44 | 45 |
46 |
47 |
48 |
49 | 50 | 51 |
52 |
53 |
54 | 55 | 56 |
57 | 58 |
59 | 62 | 65 | 68 | 69 | 70 | 71 |
72 |
73 |
74 |
75 |
-------------------------------------------------------------------------------- /src/app/components/trustlineList/trustlineListIssuing.css: -------------------------------------------------------------------------------- 1 | .mat-cell:hover{ 2 | cursor: pointer; 3 | } 4 | 5 | @media (min-width:600px) { 6 | 7 | .mat-column-account { 8 | word-wrap: break-word !important; 9 | white-space: unset !important; 10 | flex: 0 0 20% !important; 11 | width: 20% !important; 12 | overflow-wrap: break-word; 13 | word-wrap: break-word; 14 | 15 | word-break: break-word; 16 | 17 | -ms-hyphens: auto; 18 | -moz-hyphens: auto; 19 | -webkit-hyphens: auto; 20 | hyphens: auto; 21 | justify-content: center; 22 | text-align: center; 23 | } 24 | 25 | .mat-column-currency { 26 | word-wrap: break-word !important; 27 | white-space: unset !important; 28 | flex: 0 0 5% !important; 29 | width: 5% !important; 30 | overflow-wrap: break-word; 31 | word-wrap: break-word; 32 | 33 | word-break: break-word; 34 | 35 | -ms-hyphens: auto; 36 | -moz-hyphens: auto; 37 | -webkit-hyphens: auto; 38 | hyphens: auto; 39 | justify-content: center; 40 | text-align: center; 41 | } 42 | 43 | .mat-column-balance { 44 | word-wrap: break-word !important; 45 | white-space: unset !important; 46 | flex: 0 0 15% !important; 47 | width: 15% !important; 48 | overflow-wrap: break-word; 49 | word-wrap: break-word; 50 | 51 | word-break: break-word; 52 | 53 | -ms-hyphens: auto; 54 | -moz-hyphens: auto; 55 | -webkit-hyphens: auto; 56 | hyphens: auto; 57 | justify-content: center; 58 | text-align: center; 59 | } 60 | 61 | .mat-column-limit { 62 | word-wrap: break-word !important; 63 | white-space: unset !important; 64 | flex: 0 0 15% !important; 65 | width: 15% !important; 66 | overflow-wrap: break-word; 67 | word-wrap: break-word; 68 | 69 | word-break: break-word; 70 | 71 | -ms-hyphens: auto; 72 | -moz-hyphens: auto; 73 | -webkit-hyphens: auto; 74 | hyphens: auto; 75 | justify-content: center; 76 | text-align: center; 77 | } 78 | 79 | .mat-column-info { 80 | word-wrap: break-word !important; 81 | white-space: unset !important; 82 | flex: 0 0 40% !important; 83 | width: 40% !important; 84 | overflow-wrap: break-word; 85 | word-wrap: break-word; 86 | 87 | word-break: break-word; 88 | 89 | -ms-hyphens: auto; 90 | -moz-hyphens: auto; 91 | -webkit-hyphens: auto; 92 | hyphens: auto; 93 | justify-content: center; 94 | text-align: center; 95 | } 96 | } 97 | 98 | @media (max-width:599px) { 99 | 100 | .mat-footer-row , .mat-row { 101 | padding-top: 0.5em; 102 | align-items: start; 103 | min-height: 24px; 104 | } 105 | 106 | .mat-cell { 107 | padding-left: 1em; 108 | } 109 | 110 | .mat-column-account { 111 | word-wrap: break-word !important; 112 | white-space: unset !important; 113 | overflow-wrap: break-word; 114 | word-wrap: break-word; 115 | 116 | word-break: break-word; 117 | 118 | -ms-hyphens: auto; 119 | -moz-hyphens: auto; 120 | -webkit-hyphens: auto; 121 | hyphens: auto; 122 | } 123 | 124 | .mat-column-currency { 125 | word-wrap: break-word !important; 126 | white-space: unset !important; 127 | overflow-wrap: break-word; 128 | word-wrap: break-word; 129 | 130 | word-break: break-word; 131 | 132 | -ms-hyphens: auto; 133 | -moz-hyphens: auto; 134 | -webkit-hyphens: auto; 135 | hyphens: auto; 136 | } 137 | 138 | .mat-column-balance { 139 | word-wrap: break-word !important; 140 | white-space: unset !important; 141 | overflow-wrap: break-word; 142 | word-wrap: break-word; 143 | 144 | word-break: break-word; 145 | 146 | -ms-hyphens: auto; 147 | -moz-hyphens: auto; 148 | -webkit-hyphens: auto; 149 | hyphens: auto; 150 | } 151 | 152 | .mat-column-limit { 153 | word-wrap: break-word !important; 154 | white-space: unset !important; 155 | overflow-wrap: break-word; 156 | word-wrap: break-word; 157 | 158 | word-break: break-word; 159 | 160 | -ms-hyphens: auto; 161 | -moz-hyphens: auto; 162 | -webkit-hyphens: auto; 163 | hyphens: auto; 164 | } 165 | 166 | .mat-column-limit_peer { 167 | word-wrap: break-word !important; 168 | white-space: unset !important; 169 | overflow-wrap: break-word; 170 | word-wrap: break-word; 171 | 172 | word-break: break-word; 173 | 174 | -ms-hyphens: auto; 175 | -moz-hyphens: auto; 176 | -webkit-hyphens: auto; 177 | hyphens: auto; 178 | } 179 | 180 | .mat-column-no_ripple { 181 | word-wrap: break-word !important; 182 | white-space: unset !important; 183 | overflow-wrap: break-word; 184 | word-wrap: break-word; 185 | 186 | word-break: break-word; 187 | 188 | -ms-hyphens: auto; 189 | -moz-hyphens: auto; 190 | -webkit-hyphens: auto; 191 | hyphens: auto; 192 | } 193 | 194 | .mat-column-actions { 195 | word-wrap: break-word !important; 196 | white-space: unset !important; 197 | overflow-wrap: break-word; 198 | word-wrap: break-word; 199 | 200 | word-break: break-word; 201 | 202 | -ms-hyphens: auto; 203 | -moz-hyphens: auto; 204 | -webkit-hyphens: auto; 205 | hyphens: auto; 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/app/components/trustlineList/trustlineListIssuing.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | Show TrustLines of signed in account 12 | 13 | 14 | 15 | 16 | 17 | Token Currency Code 18 | 19 | {{getCurrencyCode(trustline.currency)}} 20 | Token Currency Code: {{getCurrencyCode(trustline.currency)}} 21 | 22 | 23 | 24 | 25 | Issuer 26 | 27 | {{trustline.account}} 28 | Issuer:
{{trustline.account}}
29 |
30 |
31 | 32 | 33 | Balance of this TrustLine 34 | 35 | {{stringToFloat(trustline.balance)}} 36 | Balance: {{stringToFloat(trustline.balance)}} 37 | 38 | 39 | 40 | 41 | Limit of this TrustLine 42 | 43 | {{stringToFloat(trustline.limit)}} 44 | Limit: {{stringToFloat(trustline.limit)}} 45 | 46 | 47 | 48 | 49 | Info 50 | 51 |
52 | warning The TrustLine does not have enough limit to issue more tokens. Please ask the recipient to increase the limit. 53 | Info: warning The TrustLine does not have enough limit to issue more tokens. Please ask the recipient to increase the limit. 54 |
55 |
56 |
57 | 58 | 59 | 60 | 61 |
62 |
63 | -------------------------------------------------------------------------------- /src/app/components/trustlineList/trustlineListIssuing.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input, Output, EventEmitter, OnDestroy } from "@angular/core"; 2 | import { Observable, Subscription } from 'rxjs'; 3 | import { XrplAccountChanged, TrustLine } from 'src/app/utils/types'; 4 | import { XRPLWebsocket } from '../../services/xrplWebSocket'; 5 | 6 | @Component({ 7 | selector: "trustlineListIssuing", 8 | templateUrl: "trustlineListIssuing.html", 9 | styleUrls: ['./trustlineListIssuing.css'] 10 | }) 11 | export class TrustLineListIssuing implements OnInit, OnDestroy { 12 | 13 | @Input() 14 | issuerAccountChanged: Observable; 15 | 16 | @Input() 17 | recipientAccountChanged: Observable 18 | 19 | @Output() 20 | trustLineSelected: EventEmitter = new EventEmitter(); 21 | 22 | trustLines:TrustLine[] = []; 23 | displayedColumns: string[] = ['currency', 'account','balance', 'limit', 'info']; 24 | loading:boolean = false; 25 | testMode:boolean = false; 26 | originalTestModeValue:boolean = false; 27 | 28 | issuerAccount:string = null; 29 | recipientAccount:string = null; 30 | 31 | recipientAccountNotFound:boolean = false; 32 | 33 | private recipientAccountChangedSubscription: Subscription; 34 | private issuerAccountChangedSubscription: Subscription; 35 | 36 | constructor(private xrplWebSocket: XRPLWebsocket) {} 37 | 38 | ngOnInit() { 39 | this.recipientAccountChangedSubscription = this.recipientAccountChanged.subscribe(recipientAccountInfo => { 40 | //console.log("recipientAccountChanged " + JSON.stringify(recipientAccountInfo)); 41 | //console.log("test mode: " + recipientAccountInfo.mode); 42 | this.recipientAccount = recipientAccountInfo.account; 43 | this.testMode = recipientAccountInfo.mode; 44 | 45 | if(this.recipientAccount && this.issuerAccount) 46 | this.loadTrustLineList(this.recipientAccount); 47 | else 48 | this.trustLines = []; 49 | }); 50 | 51 | this.issuerAccountChangedSubscription = this.issuerAccountChanged.subscribe(isserAccountInfo => { 52 | //console.log("issuerAccountChanged: " + JSON.stringify(isserAccountInfo)); 53 | //console.log("test mode: " + isserAccountInfo.mode); 54 | this.issuerAccount = isserAccountInfo.account; 55 | this.testMode = isserAccountInfo.mode; 56 | 57 | if(this.recipientAccount && this.issuerAccount) 58 | this.loadTrustLineList(this.recipientAccount); 59 | else 60 | this.trustLines = []; 61 | }); 62 | } 63 | 64 | ngOnDestroy() { 65 | if(this.issuerAccountChangedSubscription) 66 | this.issuerAccountChangedSubscription.unsubscribe(); 67 | 68 | if(this.recipientAccountChangedSubscription) 69 | this.recipientAccountChangedSubscription.unsubscribe(); 70 | } 71 | 72 | async loadTrustLineList(xrplAccount: string) { 73 | 74 | if(xrplAccount) { 75 | this.loading = true; 76 | 77 | this.recipientAccountNotFound = false; 78 | 79 | let account_lines_request:any = { 80 | command: "account_lines", 81 | account: xrplAccount, 82 | ledger_index: "validated", 83 | peer: this.issuerAccount 84 | } 85 | 86 | let message:any = await this.xrplWebSocket.getWebsocketMessage("trustlineListIssuing", account_lines_request, this.testMode); 87 | 88 | if(message.status && message.status === 'success' && message.type && message.type === 'response' && message.result && message.result.lines) { 89 | this.trustLines = message.result.lines; 90 | 91 | if(this.trustLines && this.trustLines.length > 0) { 92 | this.trustLines = this.trustLines.sort((lineA, lineB) => lineA.currency.localeCompare(lineB.currency)); 93 | this.trustLines.forEach(trustline => { 94 | trustline.balanceN = Number(trustline.balance); 95 | trustline.limitN = Number(trustline.limit); 96 | }) 97 | } 98 | 99 | //if data 0 (no available trustlines) -> show message "no trustlines available" 100 | if(this.trustLines && this.trustLines.length == 0) 101 | this.trustLines = null; 102 | 103 | //console.log("Trust lines: " + JSON.stringify(this.trustLines)); 104 | this.loading = false; 105 | } else if(message.status && message.status === 'error' && message.error === 'actNotFound') { 106 | this.trustLines = null; 107 | this.recipientAccountNotFound = true; 108 | this.loading = false; 109 | } else { 110 | this.trustLines = null; 111 | this.loading = false; 112 | } 113 | } 114 | } 115 | 116 | selectTrustLine(trustLine: TrustLine) { 117 | this.trustLineSelected.next(trustLine); 118 | } 119 | 120 | stringToFloat(number: string): number { 121 | return parseFloat(number); 122 | } 123 | 124 | getCurrencyCode(currency: string): string { 125 | if(currency) { 126 | if(currency.length == 40) { 127 | while(currency.endsWith("00")) { 128 | currency = currency.substring(0, currency.length-2); 129 | } 130 | //hex to ascii 131 | return Buffer.from(currency, 'hex').toString('ascii').trim(); 132 | } else 133 | return currency; 134 | } else 135 | return "" 136 | } 137 | } -------------------------------------------------------------------------------- /src/app/components/xummSignRequestDialog.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 |
6 |
7 | Scan QR code to Login. Sign the Xaman request with the account you want to login with: 8 | 9 | 10 | Alternatively click the following link: Open in Xaman 11 |
12 |
13 | Waiting for transaction confirmation... 14 | 15 | 16 |
17 |
18 | 24 |
25 |
26 |
27 |
28 | {{backendErrorMessage}} 29 |
30 |
31 | Sorry, your transaction could not be verified. Please reload the page and try again! 32 |
33 |
34 | Sorry, your request has been expired. Please reload the page and try again! 35 |
36 |
37 | 38 | 44 | 45 |
46 |
47 | 48 |
49 |
50 | 51 | check_circle_outline 52 | 53 |
54 |
55 |

Successfully logged in!

56 |

You will be redirected back shortly. 57 |
58 |
59 |
-------------------------------------------------------------------------------- /src/app/routes/easyToken.html: -------------------------------------------------------------------------------- 1 | 2 |

You can choose to create a new XRPL Token, issue more of your previously created Token or check out the list of existing XRPL tokens.

3 | 4 | 5 | 6 | 7 | 8 | 9 |
-------------------------------------------------------------------------------- /src/app/routes/easyToken.ts: -------------------------------------------------------------------------------- 1 | import { Component} from '@angular/core'; 2 | 3 | 4 | @Component({ 5 | selector: 'easyToken', 6 | templateUrl: './easyToken.html', 7 | }) 8 | export class EasyToken { 9 | } 10 | -------------------------------------------------------------------------------- /src/app/routes/generic-backend.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

xrpl.services Generic Backend

4 |
5 | 6 |

How do I use the Xaman Generic Backend?

7 |

8 | The xrpl.services Generic Backend has a very simple API with some really cool features. Most of the "hard work" is done automatically by the backend. (Setting your destination address, setting up Xaman push notifications, and so on ...)
9 |
10 |

11 | 12 |

13 |
14 | 15 | 16 |

XUMM Buttons

17 | The easy-to-integrate Xrpl.Services Buttons offer you an very simple way to add a Xaman button into your website. As example: for donations/tips/payments but also for offering exclusive content (after payment)
18 |

How to use the Xrpl.Services Buttons:

19 |

20 | You have to be whitelisted for the xrpl.services Generic Backend (see steps above).
21 | For adding one or more of the buttons, follow the detailed steps below. 22 |

23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Add the following script into the <head> section of your website:
33 | <script defer src="https://pay.xrpl.services/payViaXumm.js"></script>

34 | 35 | Now you can add a <div> tag anywhere in your website where you want to have a pay button.

36 | The <div> tag must have the attribute: class="xumm-pay-button":
37 | <div class="xumm-pay-button"></div>

38 | There is also a button without a surrounding border:
39 | <div class="xumm-pay-button-no-border"></div>

40 | The Xaman Generic Pay Button will be inserted into the <div> element.
41 | If you want to change the size of the button, just change the size of the surrounding <div>.

42 |
43 |
44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | Add the following script into the <head> section of your website:
52 | <script defer src="https://donate.xrpl.services/donateViaXumm.js"></script>

53 | Now you can add a <div> tag anywhere in your website where you want to have a donation button.

54 | The <div> tag must have the attribute: class="xumm-donate-button":
55 | <div class="xumm-donate-button"></div>

56 | There is also a button without a surrounding border:
57 | <div class="xumm-donate-button-no-border"></div>

58 | The Xaman Generic Donation Button will be inserted into the <div> element.
59 | If you want to change the size of the button, just change the size of the surrounding <div>.

60 |
61 |
62 |
63 |

64 |
65 |
66 | -------------------------------------------------------------------------------- /src/app/routes/generic-backend.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'generic-backend', 5 | templateUrl: './generic-backend.html', 6 | }) 7 | export class GenericBackendDefinition { 8 | } 9 | -------------------------------------------------------------------------------- /src/app/routes/nft-api-backend.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

XRPL Services NFT API

4 |
5 | 6 |

Payment for elevated limits:

7 | 8 | 9 | 10 |
11 | 12 | Pay for elevated API access:   13 | 16 | 17 |
18 |
19 |
20 | -------------------------------------------------------------------------------- /src/app/routes/nft-api-backend.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { MatDialog } from '@angular/material/dialog'; 3 | import { MatSnackBar } from '@angular/material/snack-bar'; 4 | import { LocalStorageService } from 'angular-2-local-storage'; 5 | import { XummTypes } from 'xumm-sdk'; 6 | import { GenericPayloadQRDialog } from '../components/genericPayloadQRDialog'; 7 | import { GenericBackendPostRequest, TransactionValidation } from '../utils/types'; 8 | 9 | @Component({ 10 | selector: 'nft-api-backend', 11 | templateUrl: './nft-api-backend.html', 12 | }) 13 | export class NftApiBackend { 14 | 15 | constructor(private localStorage: LocalStorageService, private payDialog: MatDialog, private snackBar: MatSnackBar) {} 16 | 17 | async payViaXumm() { 18 | //setting up xumm payload and waiting for websocket 19 | let xummPayload:XummTypes.XummPostPayloadBodyJson = { 20 | txjson: { 21 | TransactionType: "Payment", 22 | Memos: [{Memo: {MemoType: Buffer.from("[https://xrpl.services]-Memo", 'utf8').toString('hex').toUpperCase(), MemoData: Buffer.from("Payment for using NFT API: https://api.xrpldata.com/docs", 'utf8').toString('hex').toUpperCase()}}] 23 | }, 24 | custom_meta: { 25 | instruction: "You are about to pay for the XRP Ledger Services NFT API.\nPlease make sure to set the correct XRP amount for your choosen Tier/Rate Limit!", 26 | blob: { 27 | purpose: "payment for NFT API" 28 | } 29 | } 30 | } 31 | 32 | this.openGenericDialog(xummPayload); 33 | } 34 | 35 | openGenericDialog(xummPayload: XummTypes.XummPostPayloadBodyJson):void { 36 | let genericBackendRequest:GenericBackendPostRequest = { 37 | options: { 38 | xrplAccount: this.localStorage.get("xrplAccount"), 39 | referer: 'abcde' 40 | }, 41 | payload: xummPayload 42 | } 43 | 44 | const dialogRef = this.payDialog.open(GenericPayloadQRDialog, { 45 | width: 'auto', 46 | height: 'auto;', 47 | data: genericBackendRequest 48 | }); 49 | 50 | dialogRef.afterClosed().subscribe((transactionInfo:TransactionValidation) => { 51 | //console.log('The generic dialog was closed: ' + JSON.stringify(transactionInfo)); 52 | 53 | if(transactionInfo && transactionInfo.success) { 54 | if(!transactionInfo.testnet) 55 | this.snackBar.open("Thank you for the purchase!", null, {panelClass: 'snackbar-success', duration: 5000, horizontalPosition: 'center', verticalPosition: 'top'}); 56 | else 57 | this.snackBar.open("Your payment was submitted to the testnet. Your API Key will soon be disabled. Please send a 'real' payment.", null, {panelClass: 'snackbar-failed', duration: 5000, horizontalPosition: 'center', verticalPosition: 'top'}); 58 | } 59 | }); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/app/routes/nftRoute.html: -------------------------------------------------------------------------------- 1 | 2 |

Find below an overview of all issued XLS14D NFT on the XRP Ledger Main net.

3 |

These are the deprecated XLS-14D NFTs and NOT the new, native XRPL NFTs (XLS-20)!

4 | 5 | 6 | 7 |
-------------------------------------------------------------------------------- /src/app/routes/nftRoute.ts: -------------------------------------------------------------------------------- 1 | import { Component} from '@angular/core'; 2 | 3 | 4 | @Component({ 5 | selector: 'nftRoute', 6 | templateUrl: './nftRoute.html', 7 | }) 8 | export class NftRoute { 9 | } 10 | -------------------------------------------------------------------------------- /src/app/routes/specific/privacy.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Title } from '@angular/platform-browser'; 3 | 4 | @Component({ 5 | selector: 'privacy', 6 | templateUrl: 'privacy.html' 7 | }) 8 | 9 | export class PrivacyComponent implements OnInit { 10 | 11 | constructor(private titleService: Title) {} 12 | 13 | ngOnInit() { 14 | this.titleService.setTitle("XRPL Services Privacy"); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/app/routes/specific/terms.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Title } from '@angular/platform-browser'; 3 | 4 | @Component({ 5 | selector: 'terms', 6 | templateUrl: 'terms.html' 7 | }) 8 | 9 | export class TermsComponent implements OnInit { 10 | 11 | constructor(private titleService: Title) {} 12 | 13 | ngOnInit() { 14 | this.titleService.setTitle("XRPL Services Terms"); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/app/routes/statistics.css: -------------------------------------------------------------------------------- 1 | 2 | table { 3 | width: auto; 4 | } 5 | 6 | td { 7 | word-wrap: break-word !important; 8 | white-space: unset !important; 9 | overflow-wrap: break-word; 10 | word-wrap: break-word; 11 | 12 | word-break: break-word; 13 | 14 | -ms-hyphens: auto; 15 | -moz-hyphens: auto; 16 | -webkit-hyphens: auto; 17 | hyphens: auto; 18 | } 19 | 20 | @media (max-width:599px) { 21 | 22 | .mat-footer-row , .mat-row { 23 | padding-top: 0.5em; 24 | align-items: start; 25 | min-height: 24px; 26 | cursor: pointer; 27 | } 28 | 29 | .mat-cell { 30 | padding-left: 1em; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/app/routes/statistics.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { XummService } from '../services/xumm.service'; 3 | 4 | @Component({ 5 | selector: 'statistics', 6 | templateUrl: './statistics.html', 7 | styleUrls: ['./statistics.css'] 8 | }) 9 | export class Statistics implements OnInit { 10 | 11 | constructor(private xummApi: XummService) {} 12 | 13 | statisticsWeb:any[] = []; 14 | statisticsEscrow:any[] = []; 15 | statisticsToken:any[] = []; 16 | statisticsNFT:any[] = []; 17 | statisticsTokenTrasher:any[] = []; 18 | 19 | totalTransactionsWeb:number = 0 20 | totalTransactionsEscrow:number = 0 21 | totalTransactionsToken:number = 0 22 | totalTransactionsNFT:number = 0; 23 | totalTransactionsTokenTrasher:number = 0; 24 | 25 | escrowNextRelease:Date; 26 | escrowLastRelease:Date; 27 | escrowCurrentCount:number = -1; 28 | 29 | displayedColumns: string[] = ['transactiontype', 'number']; 30 | loading:boolean = false; 31 | 32 | async ngOnInit() { 33 | this.loading = true; 34 | let promises:any[] = []; 35 | promises.push(this.xummApi.getTransactionStatistics()); 36 | promises.push(this.xummApi.getTransactionStatistics("https://escrowcreate-xapp.xrpl.services")); 37 | promises.push(this.xummApi.getTransactionStatistics("https://tokencreate-xapp.xrpl.services")); 38 | promises.push(this.xummApi.getEscrowNextRelease()); 39 | promises.push(this.xummApi.getEscrowLastRelease()); 40 | promises.push(this.xummApi.getEscrowCurrentCount()); 41 | promises.push(this.xummApi.getTransactionStatistics("https://nftcreate-xapp.xrpl.services")); 42 | promises.push(this.xummApi.getTransactionStatistics("https://tokentrasher-xapp.xrpl.services")); 43 | 44 | 45 | let results = await Promise.all(promises); 46 | 47 | let statsWeb = results[0]; 48 | let statsEscrow = results[1]; 49 | let statsToken = results[2]; 50 | this.escrowNextRelease = results[3] != null ? new Date(results[3]) : null; 51 | this.escrowLastRelease = results[4] != null ? new Date(results[4]) : null; 52 | this.escrowCurrentCount = results[5]; 53 | let statsNFT = results[6]; 54 | let statsTrasher = results[7]; 55 | 56 | this.calculateStats(statsWeb, this.statisticsWeb, this.totalTransactionsWeb); 57 | this.calculateStats(statsEscrow, this.statisticsEscrow, this.totalTransactionsEscrow); 58 | this.calculateStats(statsToken, this.statisticsToken, this.totalTransactionsToken); 59 | this.calculateStats(statsNFT, this.statisticsNFT, this.totalTransactionsNFT); 60 | this.calculateStats(statsTrasher, this.statisticsTokenTrasher, this.totalTransactionsTokenTrasher); 61 | 62 | this.loading = false; 63 | } 64 | 65 | calculateStats(apiResult: any, statsArray: any[], totalTransaction: number) { 66 | if(apiResult) { 67 | //console.log("received stats: " + JSON.stringify(apiResult)); 68 | for(let trx in apiResult) { 69 | if (apiResult.hasOwnProperty(trx)) { 70 | 71 | let readableName = this.resolveTransactionName(trx); 72 | let readableStats = { 73 | name: readableName, 74 | number: apiResult[trx] 75 | }; 76 | 77 | totalTransaction += apiResult[trx]; 78 | statsArray.push(readableStats); 79 | } 80 | } 81 | 82 | if(statsArray && statsArray.length > 1) 83 | statsArray.sort((trx1, trx2 ) => { return trx2.number - trx1.number }); 84 | 85 | statsArray.push({name: "Overall # of Trx", number: totalTransaction}); 86 | 87 | //console.log("constructed stats array: " + JSON.stringify(statsArray)); 88 | } 89 | } 90 | 91 | resolveTransactionName(deliveredName: string) { 92 | switch(deliveredName.toLowerCase()) { 93 | case "signin": return "Sign In"; 94 | case "accountset": return "Account Set"; 95 | case "accountdelete": return "Account Delete"; 96 | case "checkcancel": return "Check Cancel"; 97 | case "checkcash": return "Check Cash"; 98 | case "checkcreate": return "Check Create"; 99 | case "depositpreauth": return "Deposit Preauth"; 100 | case "escrowcancel": return "Escrow Cancel"; 101 | case "escrowcreate": return "Escrow Create"; 102 | case "escrowfinish": return "Escrow Finish"; 103 | case "offercancel": return "Offer Cancel"; 104 | case "offercreate": return "Offer Create"; 105 | case "payment": return "Payment"; 106 | case "paymentchannelclaim": return "Paymentchannel Claim"; 107 | case "paymentchannelcreate": return "Paymentchannel Create"; 108 | case "paymentchannelfund": return "Paymentchannel Fund"; 109 | case "setregularkey": return "Set Regular Key"; 110 | case "signerlistset": return "Set Signer List"; 111 | case "ticketcreate": return "Ticket Create"; 112 | case "trustset": return "Trust Set"; 113 | case "nftokenacceptoffer": return "NFToken Accept Offer"; 114 | case "nftokenburn": return "NFToken Burn"; 115 | case "nftokencanceloffer": return "NFToken Cancel Offer"; 116 | case "nftokencreateoffer": return "NFToken Create Offer"; 117 | case "nftokenmint": return "NFToken Mint"; 118 | default: return deliveredName; 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/app/routes/tools.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | 7 |
8 |
9 |
10 | 13 |
14 |
15 |
16 | 17 |
18 |
19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 |
27 | 28 | 29 |
30 |
31 | 32 |
33 |
34 |
35 | 36 |
37 | 38 |
39 |   40 |   41 | 42 |
43 |
44 |
45 |
46 | 47 |
48 | 49 |
50 |   51 |   52 | 53 |
54 |
55 |
56 |
57 | View your transaction on the XRP Ledger:
58 | bithomp | 59 | xrpl.org 60 | | xrpscan | 61 | xrp1ntel | 62 | xrplorer 63 |
64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 |
72 | -------------------------------------------------------------------------------- /src/app/routes/xrpl-statistics.css: -------------------------------------------------------------------------------- 1 | .stats-component { 2 | width: 350px; 3 | margin: 10px; 4 | background-color: var(--background-color); 5 | } 6 | 7 | .mat-card > :last-child:not(.mat-card-footer) { 8 | margin: 10px; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/app/routes/xrpl-statistics.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { AppService } from '../services/app.service'; 3 | import * as normalizer from 'src/app/utils/normalizers'; 4 | 5 | @Component({ 6 | selector: 'xrpl-statistics', 7 | templateUrl: './xrpl-statistics.html', 8 | styleUrls: ['./xrpl-statistics.css'] 9 | }) 10 | export class XrplStatistics implements OnInit { 11 | 12 | constructor(private app: AppService) {} 13 | 14 | loading:boolean = false; 15 | tooManyRequests:boolean = false; 16 | 17 | generalStats:any = null; 18 | accountrootStats:any = null; 19 | ripplestateStats:any = null; 20 | directorynodeStats:any = null; 21 | offerStats:any = null; 22 | signerlistStats:any = null; 23 | ticketStats:any = null; 24 | escrowStats:any = null; 25 | ledgerhashesStats:any = null; 26 | paychannelStats:any = null; 27 | checkStats:any = null; 28 | depositpreauthStats:any = null; 29 | feesettingsStats:any = null; 30 | amendmentsStats:any = null; 31 | nftokenpageStats:any = null; 32 | nftokenofferStats:any = null; 33 | 34 | totalNumberOfObjects: number = 0; 35 | 36 | 37 | async ngOnInit() { 38 | this.loading = true; 39 | 40 | try { 41 | let xrplStatistics:any = await this.app.get('https://api.xrpldata.com/api/v1/ledgerdata'); 42 | 43 | if(xrplStatistics) { 44 | this.generalStats = { 45 | ledger_index: xrplStatistics.ledger_index, 46 | ledger_hash: xrplStatistics.ledger_hash, 47 | ledger_close: new Date(normalizer.rippleEpocheTimeToUTC(xrplStatistics.ledger_close_ms)).toLocaleString(), 48 | ledger_close_ms: xrplStatistics.ledger_close_ms, 49 | ledger_size: xrplStatistics.ledger_size, 50 | sizeType: xrplStatistics.sizeType 51 | } 52 | 53 | this.totalNumberOfObjects = 0; 54 | 55 | let ledger_data = xrplStatistics.ledger_data; 56 | 57 | if(ledger_data) { 58 | 59 | for (let xrplObject in ledger_data) { 60 | if (ledger_data[xrplObject].hasOwnProperty('count')) { 61 | this.totalNumberOfObjects = this.totalNumberOfObjects + ledger_data[xrplObject]['count']; 62 | } 63 | } 64 | 65 | this.ripplestateStats = ledger_data.ripplestate; 66 | this.accountrootStats = ledger_data.accountroot; 67 | this.directorynodeStats = ledger_data.directorynode; 68 | this.offerStats = ledger_data.offer; 69 | this.signerlistStats = ledger_data.signerlist; 70 | this.ticketStats = ledger_data.ticket; 71 | this.escrowStats = ledger_data.escrow; 72 | this.ledgerhashesStats = ledger_data.ledgerhashes; 73 | this.paychannelStats = ledger_data.paychannel; 74 | this.checkStats = ledger_data.check; 75 | this.depositpreauthStats = ledger_data.depositpreauth; 76 | this.feesettingsStats = ledger_data.feesettings; 77 | this.amendmentsStats = ledger_data.amendments; 78 | this.nftokenofferStats = ledger_data.nftokenoffer; 79 | this.nftokenpageStats = ledger_data.nftokenpage; 80 | } 81 | 82 | this.tooManyRequests = false; 83 | } 84 | } catch(err) { 85 | console.log(JSON.stringify(err)); 86 | 87 | if(err && err.includes("You are sending too many requests in a short period of time")) { 88 | this.tooManyRequests = true; 89 | } else { 90 | this.tooManyRequests = false; 91 | } 92 | } 93 | 94 | this.loading = false; 95 | } 96 | 97 | roundNumber(number: number, multiplier: number) : number { 98 | return Math.round(number * multiplier) / multiplier; 99 | } 100 | 101 | numberWithCommas(x) { 102 | return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/app/services/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient, HttpHeaders } from "@angular/common/http"; 3 | import { Observable } from 'rxjs'; 4 | 5 | @Injectable() 6 | export class AppService { 7 | 8 | constructor(private http: HttpClient) {} 9 | 10 | requestOptions() { 11 | let headers = new HttpHeaders(); 12 | headers = headers.append('Content-Type', 'application/json'); 13 | return { headers: headers }; 14 | } 15 | 16 | handleResponse(request: Observable): Promise { 17 | return request.toPromise() 18 | .catch(err => this.handleError(err)); 19 | } 20 | 21 | get(url): Promise { 22 | return this.handleResponse(this.http.get(url, this.requestOptions())); 23 | } 24 | 25 | getText(url): Promise { 26 | return this.handleResponse(this.http.get(url, {responseType: "text"})); 27 | } 28 | 29 | post(url, data): Promise { 30 | return this.handleResponse(this.http.post(url, data, this.requestOptions())); 31 | } 32 | 33 | put(url, data): Promise { 34 | return this.handleResponse(this.http.put(url, data, this.requestOptions())); 35 | } 36 | 37 | delete(url): Promise { 38 | return this.handleResponse(this.http.delete(url, this.requestOptions())); 39 | } 40 | 41 | isAvailable(url): Promise { 42 | return this.handleResponse(this.http.get(url, {responseType: "text"})); 43 | } 44 | 45 | private handleError(error: any): Promise { 46 | console.error('An error occurred', JSON.stringify(error)); // XXX for debugging purposes 47 | if (!error.status) error.message = "Sorry, it cannot be reached."; 48 | let errmsg: string = error.error.message || error.message || error; 49 | 50 | return Promise.reject(errmsg); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/app/services/util.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { AppService } from './app.service'; 3 | 4 | @Injectable() 5 | export class UtilService { 6 | constructor(private app: AppService) {} 7 | 8 | async getTransactionTypes(loggedInAccount?:string): Promise { 9 | let dirList:any[] = await this.app.get('https://api.github.com/repos/xrplf/xrpl-dev-portal/contents/docs/references/protocol/transactions/types'); 10 | let relevantFiles:any[] = dirList.filter(r => !r.name.includes('.ja.') && r.name.match(/^[a-zA-Z]+\.md$/)) 11 | 12 | let sources:any[] = []; 13 | 14 | for(let i = 0; i < relevantFiles.length; i++) { 15 | sources.push(this.app.getText(relevantFiles[i].download_url)); 16 | } 17 | 18 | sources = await Promise.all(sources); 19 | 20 | let result:any[] = []; 21 | 22 | for(let i = 0; i < sources.length; i++) { 23 | let source:string = sources[i]; 24 | 25 | if(source.split(`\n`)?.join(' ')?.match(/```.+?```/gm)) { 26 | result.push({ 27 | transactionType: source.match(/^# ([a-zA-Z]+)/gm)[0].slice(2), 28 | docLink: 'https://xrpl.org/' + relevantFiles[i].name.split('.')[0] + '.html', 29 | requiresAmendment: source.match(/^(_\(Requires the).*(:not_enabled:.\).)$/gm) != null, 30 | codeSamples: source.split(`\n`).join(' ').match(/```.+?```/gm) 31 | .map(s => s.split('```')[1].trim().replace(/^json[\n]*/gm, '')) 32 | .map(s => s.replace(/,[ \t\n\\t\\n]*}$/, '}')) 33 | .map(s => { 34 | try { 35 | let parsedTrx = JSON.parse(s); 36 | 37 | if(loggedInAccount && parsedTrx.Account) { 38 | parsedTrx.Account = loggedInAccount; 39 | } 40 | 41 | return parsedTrx; 42 | } catch (e) { 43 | return s 44 | } 45 | }) 46 | }); 47 | } 48 | } 49 | 50 | return result; 51 | } 52 | } -------------------------------------------------------------------------------- /src/app/services/xrplWebSocket.ts: -------------------------------------------------------------------------------- 1 | import { webSocket, WebSocketSubject } from 'rxjs/webSocket'; 2 | import { Injectable, Optional, SkipSelf } from '@angular/core'; 3 | 4 | @Injectable() 5 | export class XRPLWebsocket { 6 | 7 | originalTestModeValue:boolean = false; 8 | mainNodes:string[] = ['wss://xrplcluster.com', 'wss://s2.ripple.com']; 9 | testNodes:string[] = ['wss://testnet.xrpl-labs.com', 'wss://s.altnet.rippletest.net']; 10 | mainFirst:boolean = true; 11 | testFirst:boolean = true; 12 | errorsToSwitch:string[] = ["amendmentBlocked", "failedToForward", "invalid_API_version", "noClosed", "noCurrent", "noNetwork", "tooBusy"] 13 | 14 | websocketMap:Map = new Map(); 15 | 16 | constructor (@Optional() @SkipSelf() parentModule?: XRPLWebsocket) { 17 | if (parentModule) { 18 | throw new Error( 19 | 'XRPLWebsocket is already loaded. Import it in the AppModule only'); 20 | } 21 | } 22 | 23 | async getWebsocketMessage(componentname:string, command:any, newTestMode:boolean, retry?:boolean): Promise { 24 | 25 | if(this.websocketMap.get(componentname) && this.websocketMap.get(componentname).mode != newTestMode) { 26 | //console.log("test mode changed") 27 | this.websocketMap.get(componentname).socket.unsubscribe(); 28 | this.websocketMap.get(componentname).socket.complete(); 29 | this.websocketMap.delete(componentname) 30 | 31 | //console.log("websockets: " + this.websocketMap.size); 32 | } 33 | 34 | if(!this.websocketMap.get(componentname) || this.websocketMap.get(componentname).socket.closed) { 35 | 36 | this.originalTestModeValue = newTestMode; 37 | console.log("connecting websocket with testmode: " + this.originalTestModeValue); 38 | let newWebsocket = webSocket(this.originalTestModeValue ? (this.testFirst ? this.testNodes[0] : this.testNodes[1]) : (this.mainFirst ? this.mainNodes[0] : this.mainNodes[1])); 39 | 40 | this.websocketMap.set(componentname, {socket: newWebsocket, mode: newTestMode, isBusy: false}); 41 | 42 | //console.log("websockets: " + this.websocketMap.size); 43 | } 44 | 45 | return new Promise((resolve, reject) => { 46 | this.websocketMap.get(componentname).socket.asObservable().subscribe(async message => { 47 | //console.log(JSON.stringify(message)); 48 | 49 | if(message && message.error && this.errorsToSwitch.includes(message.error)) { 50 | resolve(await this.cleanupAndChangeNode(componentname, command, retry)); 51 | } else { 52 | resolve(message); 53 | } 54 | }, async error => { 55 | resolve(await this.cleanupAndChangeNode(componentname, command, retry)); 56 | }); 57 | 58 | //console.log("setting up command: " + JSON.stringify(command)) 59 | this.websocketMap.get(componentname).socket.next(command); 60 | }); 61 | } 62 | 63 | async cleanupAndChangeNode(componentname: string, command: any, retry: boolean): Promise { 64 | this.websocketMap.get(componentname).socket.complete(); 65 | this.websocketMap.delete(componentname) 66 | 67 | if(!retry) { 68 | console.log("could not connect websocket! changing node!"); 69 | return this.connectToSecondWS(componentname, command); 70 | } else { 71 | return {error: true, message: "No node connection possible"}; 72 | } 73 | } 74 | 75 | async connectToSecondWS(componentname:string, command:any): Promise { 76 | if(this.originalTestModeValue) 77 | this.testFirst = !this.testFirst; 78 | else 79 | this.mainFirst = !this.mainFirst; 80 | 81 | return this.getWebsocketMessage(componentname, command, this.originalTestModeValue, true); 82 | } 83 | 84 | async send(command:any): Promise { 85 | //console.log("offers command: " + JSON.stringify(command)); 86 | 87 | let newWebsocket:WebSocketSubject = webSocket('wss://xrplcluster.com'); 88 | 89 | return new Promise((resolve, reject) => { 90 | newWebsocket.asObservable().subscribe(async message => { 91 | //console.log(JSON.stringify(message)); 92 | 93 | if(message && message.error) { 94 | reject("error"); 95 | } else { 96 | resolve(message.result); 97 | } 98 | }, async error => { 99 | reject("error"); 100 | }); 101 | 102 | //console.log("setting up command: " + JSON.stringify(command)) 103 | newWebsocket.next(command); 104 | }); 105 | } 106 | } 107 | 108 | -------------------------------------------------------------------------------- /src/app/services/xrpllabs-liquidity-check/checkLiquidityForToken.ts: -------------------------------------------------------------------------------- 1 | import { Errors } from 'xrpl-orderbook-reader'; 2 | import { LedgerExchange } from './ledgerExchange' 3 | 4 | 5 | export class CheckLiquidityClass { 6 | 7 | private static _instance: CheckLiquidityClass; 8 | 9 | private isRunning:boolean = false; 10 | 11 | private constructor() { } 12 | 13 | public static get Instance(): CheckLiquidityClass 14 | { 15 | // Do you need arguments? Make it a regular static method instead. 16 | return this._instance || (this._instance = new this()); 17 | } 18 | 19 | async checkLiquidity(issuer:string, currency: string): Promise { 20 | 21 | let liquidityIndex = -1; 22 | 23 | try { 24 | 25 | if(!this.isRunning) { 26 | 27 | this.isRunning = true; 28 | 29 | const pair = {issuer: issuer, currency: currency, displayName: currency}; 30 | 31 | 32 | let data = await Promise.all( 33 | await Promise.all([100, 1000, 2500, 5000, 10000].map(async a => { 34 | 35 | const Check = new LedgerExchange(pair) 36 | Check.initialize(); 37 | const r = await Check.getLiquidity('sell', a); 38 | 39 | return { 40 | name: pair.displayName, 41 | amount: a, 42 | rate: r.rate, 43 | errors: r.errors 44 | } 45 | } 46 | ) 47 | ) 48 | ); 49 | 50 | liquidityIndex = 5; 51 | data.forEach(d => { 52 | if(d && d.errors) { 53 | if(d.errors.includes(Errors.REQUESTED_LIQUIDITY_NOT_AVAILABLE)) 54 | liquidityIndex -= 1; 55 | else if(d.errors.length == 1) 56 | liquidityIndex -= 0.5; 57 | else if(d.errors.length > 1) 58 | liquidityIndex -= 1; 59 | } else { 60 | liquidityIndex = -1 61 | } 62 | console.log(d) 63 | }) 64 | 65 | this.isRunning = false; 66 | } 67 | } catch(err) { 68 | console.log(err) 69 | return -1; 70 | } 71 | 72 | return liquidityIndex; 73 | 74 | } 75 | } -------------------------------------------------------------------------------- /src/app/services/xrpllabs-liquidity-check/ledgerExchange.ts: -------------------------------------------------------------------------------- 1 | import { 2 | LiquidityCheck, 3 | Params as LiquidityCheckParams, 4 | Result as LiquidityResult, 5 | RatesInCurrency, 6 | Options, 7 | } from 'xrpl-orderbook-reader'; 8 | 9 | import { XRPLWebsocket } from '../xrplWebSocket'; 10 | 11 | const xrplClient = new XRPLWebsocket(); 12 | 13 | /* types ==================================================================== */ 14 | export type ExchangePair = { 15 | currency: string; 16 | issuer?: string; 17 | }; 18 | 19 | /* Class ==================================================================== */ 20 | export class LedgerExchange { 21 | private liquidityCheck: LiquidityCheck; 22 | private pair: ExchangePair; 23 | public boundaryOptions: Options; 24 | public errors: any; 25 | 26 | constructor(pair: ExchangePair) { 27 | this.pair = pair; 28 | this.liquidityCheck = undefined; 29 | 30 | this.boundaryOptions = { 31 | rates: RatesInCurrency.to, 32 | timeoutSeconds: 10, 33 | maxSpreadPercentage: 4, 34 | maxSlippagePercentage: 3, 35 | maxSlippagePercentageReverse: 3, 36 | }; 37 | } 38 | 39 | initialize = () => { 40 | 41 | // build default params 42 | const params = this.getLiquidityCheckParams('sell', 0); 43 | this.liquidityCheck = new LiquidityCheck(params); 44 | }; 45 | 46 | getLiquidityCheckParams = (direction: 'sell' | 'buy', amount: number): LiquidityCheckParams => { 47 | const pair = { 48 | currency: this.pair.currency, 49 | issuer: this.pair.issuer, 50 | }; 51 | 52 | const from = direction === 'sell' ? { currency: 'XRP' } : pair; 53 | const to = direction === 'sell' ? pair : { currency: 'XRP' }; 54 | 55 | return { 56 | trade: { 57 | from, 58 | to, 59 | amount, 60 | }, 61 | options: this.boundaryOptions, 62 | method: xrplClient.send, 63 | }; 64 | }; 65 | 66 | getLiquidity = (direction: 'sell' | 'buy', amount: number): Promise => { 67 | try { 68 | const params = this.getLiquidityCheckParams(direction, amount); 69 | 70 | if (this.liquidityCheck) { 71 | // update params 72 | this.liquidityCheck.refresh(params); 73 | 74 | return this.liquidityCheck.get(); 75 | } 76 | return Promise.reject(new Error('Liquidity check is not initialized yet!')); 77 | } catch (e) { 78 | return Promise.reject(e); 79 | } 80 | }; 81 | } 82 | 83 | export default LedgerExchange; -------------------------------------------------------------------------------- /src/app/utils/IEEE754Float.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * IEEE 754 floating-point. 3 | * 4 | * Supports single- or double-precision 5 | */ 6 | 7 | var allZeros = /^0+$/; 8 | var allOnes = /^1+$/; 9 | 10 | export function fromBytes(bytes) { 11 | // Render in binary. Hackish. 12 | var b = ""; 13 | for (var i = 0, n = bytes.length; i < n; i++) { 14 | var bits = (bytes[i] & 0xff).toString(2); 15 | while (bits.length < 8) bits = "0" + bits; 16 | b += bits; 17 | } 18 | 19 | // Determine configuration. This could have all been precomputed but it is fast enough. 20 | var exponentBits = bytes.length === 4 ? 4 : 11; 21 | var mantissaBits = (bytes.length * 8) - exponentBits - 1; 22 | var bias = Math.pow(2, exponentBits - 1) - 1; 23 | var minExponent = 1 - bias - mantissaBits; 24 | 25 | // Break up the binary representation into its pieces for easier processing. 26 | var s = b[0]; 27 | var e = b.substring(1, exponentBits + 1); 28 | var m = b.substring(exponentBits + 1); 29 | 30 | var value = 0; 31 | var multiplier = (s === "0" ? 1 : -1); 32 | 33 | if (allZeros.test(e)) { 34 | // Zero or denormalized 35 | if (allZeros.test(m)) { 36 | // Value is zero 37 | } else { 38 | value = parseInt(m, 2) * Math.pow(2, minExponent); 39 | } 40 | } else if (allOnes.test(e)) { 41 | // Infinity or NaN 42 | if (allZeros.test(m)) { 43 | value = Infinity; 44 | } else { 45 | value = NaN; 46 | } 47 | } else { 48 | // Normalized 49 | var exponent = parseInt(e, 2) - bias; 50 | var mantissa = parseInt(m, 2); 51 | value = (1 + (mantissa * Math.pow(2, -mantissaBits))) * Math.pow(2, exponent); 52 | } 53 | 54 | return value * multiplier; 55 | }; -------------------------------------------------------------------------------- /src/app/utils/TypeWriter.ts: -------------------------------------------------------------------------------- 1 | export class TypeWriter { 2 | 3 | currentIndex = 0; 4 | currentLength = 14; 5 | currentDir = true; 6 | 7 | constructor(private phrases, private callback) { 8 | } 9 | 10 | start() { 11 | this.next(2000); 12 | }; 13 | 14 | next(waitTime) { 15 | let _this = this; 16 | setTimeout(function () { 17 | if (_this.currentDir) 18 | _this.currentLength--; 19 | else 20 | _this.currentLength++; 21 | var waitMs = _this.currentDir ? 40 : 20+Math.random()*70; 22 | var doContinue = true; 23 | if (!_this.currentDir && _this.currentLength == _this.phrases[_this.currentIndex].length) { 24 | _this.currentDir = true; 25 | waitMs = 3000; 26 | if (_this.currentIndex == _this.phrases.length-1) 27 | doContinue = false; 28 | } 29 | else if (_this.currentDir && _this.currentLength == 0) { 30 | _this.currentDir = false; 31 | _this.currentIndex = (_this.currentIndex + 1) % _this.phrases.length; 32 | } 33 | var isWhole = _this.currentLength == _this.phrases[_this.currentIndex].length; 34 | _this.callback( 35 | _this.phrases[_this.currentIndex].substr(0, _this.currentLength) + ((isWhole || _this.currentLength==0) ? '' : '\u007C')); 36 | if (doContinue) 37 | _this.next(waitMs); 38 | }, waitTime); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/app/utils/flagutils.ts: -------------------------------------------------------------------------------- 1 | export const ROOT_FLAG_DEFAULT_RIPPLE:number = 8388608; 2 | export const ROOT_FLAG_DEPOSIT_AUTH:number = 16777216; 3 | export const ROOT_FLAG_DISABLE_MASTER:number = 1048576; 4 | export const ROOT_FLAG_DISALLOW_XRP:number = 524288; 5 | export const ROOT_FLAG_GLOBAL_FREEZE:number = 4194304; 6 | export const ROOT_FLAG_NO_FREEZE:number = 2097152; 7 | export const ROOT_FLAG_PASSWORD_SPENT:number = 65536; 8 | export const ROOT_FLAG_REQUIRE_AUTH:number = 262144; 9 | export const ROOT_FLAG_REQUIREDESTINATION_TAG:number = 131072; 10 | 11 | export function isRequireDestinationTagEnabled(flags:number): boolean { 12 | return flags && (flags & ROOT_FLAG_REQUIREDESTINATION_TAG) == ROOT_FLAG_REQUIREDESTINATION_TAG; 13 | } 14 | 15 | export function isMasterKeyDisabled(flags:number): boolean { 16 | return flags && (flags & ROOT_FLAG_DISABLE_MASTER) == ROOT_FLAG_DISABLE_MASTER; 17 | } 18 | 19 | export function isDefaultRippleEnabled(flags:number): boolean { 20 | return flags && (flags & ROOT_FLAG_DEFAULT_RIPPLE) == ROOT_FLAG_DEFAULT_RIPPLE; 21 | } 22 | 23 | export function isDisallowXRPEnabled(flags:number): boolean { 24 | return flags && (flags & ROOT_FLAG_DISALLOW_XRP) == ROOT_FLAG_DISALLOW_XRP; 25 | } 26 | 27 | export function isDepositAuthEnabled(flags:number): boolean { 28 | return flags && (flags & ROOT_FLAG_DEPOSIT_AUTH) == ROOT_FLAG_DEPOSIT_AUTH; 29 | } -------------------------------------------------------------------------------- /src/app/utils/searchHighlight.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from "@angular/core"; 2 | 3 | @Pipe({ 4 | name: 'highlightSearch' 5 | }) 6 | export class HighlightSearchPipe implements PipeTransform { 7 | 8 | transform(value: string, search: string): string { 9 | try { 10 | const valueStr = value + ''; // Ensure numeric values are converted to strings 11 | search = search.replace('*','').replace('(', '').replace(')','').replace('?', '').replace('.', '').replace('+',''); 12 | return valueStr.replace(new RegExp('(?![^&;]+;)(?!<[^<>]*)(' + search + ')(?![^<>]*>)(?![^&;]+;)', 'gi'), '$1'); 13 | } catch(err) { 14 | console.log(err); 15 | console.log("error on value: " + value + " and search: " + search); 16 | return ""; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/app/utils/types.ts: -------------------------------------------------------------------------------- 1 | import { XummTypes } from 'xumm-sdk'; 2 | 3 | export interface GenericBackendPostRequestOptions { 4 | frontendId?: string, 5 | web?: boolean, 6 | pushDisabled?: boolean, 7 | referer?: string, 8 | xrplAccount?: string, 9 | signinToValidate?: boolean, 10 | issuing?: boolean, 11 | isRawTrx?: boolean 12 | } 13 | 14 | export interface GenericBackendPostRequest { 15 | options?: GenericBackendPostRequestOptions, 16 | payload: XummTypes.XummPostPayloadBodyJson 17 | } 18 | 19 | export interface TransactionValidation { 20 | success: boolean, 21 | testnet: boolean, 22 | transactionType?: string, 23 | txid?: string, 24 | error?: boolean, 25 | message?: string, 26 | payloadExpired?: boolean, 27 | noValidationTimeFrame?: boolean, 28 | redirect?: boolean, 29 | account?: string, 30 | payloadId?: string, 31 | originalPayload?: XummTypes.XummGetPayloadResponse 32 | } 33 | 34 | export interface AccountInfoChanged { 35 | info: any, 36 | accountReserve: number, 37 | ownerReserve: number, 38 | mode: boolean 39 | } 40 | 41 | export interface AccountObjectsChanged { 42 | objects: any[], 43 | mode: boolean 44 | } 45 | 46 | export interface XrplAccountChanged { 47 | account: string, 48 | mode: boolean 49 | } 50 | 51 | export interface Token { 52 | currency: string, 53 | amount: number, 54 | trustlines?: number, 55 | holders?: number, 56 | offers?: number, 57 | created?: any, 58 | self_assessment?: any 59 | } 60 | 61 | export interface TrustLine { 62 | account:string, 63 | balance: string, 64 | currency: string, 65 | limit: string, 66 | limit_peer: string, 67 | no_ripple: boolean, 68 | balanceN?: number, 69 | limitN?: number 70 | } 71 | 72 | export interface TransactionTemplate { 73 | transactionType: string, 74 | docLink: string, 75 | requiresAmendment: boolean, 76 | codeSamples: any[] 77 | } 78 | 79 | export interface TokenIssuer { 80 | account: string, 81 | currencyCode: string, 82 | currencyCodeUTF8: string, 83 | amount: number, 84 | trustlines: number, 85 | holders: number, 86 | offers: number, 87 | verified: boolean, 88 | kyc?: boolean, 89 | username?: string, 90 | resolvedBy?: string 91 | twitter?: string, 92 | domain?: string, 93 | isHot?: boolean, 94 | newTrustlines?: number, 95 | created?: any, 96 | self_assessment?: any 97 | } 98 | 99 | export interface NftIssuer { 100 | account: string, 101 | currencyCode: string, 102 | currencyCodeUTF8: string, 103 | amount: number, 104 | shownAmount: number, 105 | trustlines: number, 106 | holders: number, 107 | offers: number, 108 | verified: boolean, 109 | kyc?: boolean, 110 | username?: string, 111 | resolvedBy?: string 112 | twitter?: string, 113 | domain?: string, 114 | isHot?: boolean, 115 | newTrustlines?: number, 116 | created?: any 117 | } 118 | 119 | export interface IssuerVerification { 120 | resolvedBy: string, 121 | account: string, 122 | verified: boolean, 123 | kyc?: boolean, 124 | created?: any, 125 | domain?: string, 126 | username?: string, 127 | twitter?: string 128 | } 129 | 130 | export interface XrplCurrency { 131 | currencyCode:string, 132 | currencyCodeUTF8:string 133 | } 134 | 135 | export interface RippleState { 136 | Balance: { 137 | currency: string, 138 | issuer: string, 139 | value: string 140 | }, 141 | Flags: number, 142 | HighLimit: { 143 | currency: string, 144 | issuer: string, 145 | value: string 146 | }, 147 | HighNode: string, 148 | LedgerEntryType: string, 149 | LowLimit: { 150 | currency: string, 151 | issuer: string, 152 | value: string 153 | }, 154 | LowNode: string, 155 | PreviousTxnID: string, 156 | PreviousTxnLgrSeq: number, 157 | index: string 158 | } 159 | 160 | export interface SimpleTrustLine { 161 | account:string, 162 | balance: number, 163 | currency: string, 164 | limit: string, 165 | limit_peer: string, 166 | no_ripple: boolean, 167 | isFrozen: boolean, 168 | currencyN: string, 169 | balanceN?: number 170 | } -------------------------------------------------------------------------------- /src/app/utils/utils.ts: -------------------------------------------------------------------------------- 1 | import { Encode } from 'xrpl-tagged-address-codec'; 2 | 3 | export function isValidXRPAddress(address: string): boolean { 4 | try { 5 | //console.log("encoding address: " + address); 6 | let xAddress = Encode({account: address}); 7 | //console.log("xAddress: " + xAddress); 8 | return xAddress && xAddress.length > 0; 9 | } catch(err) { 10 | //no valid address 11 | //console.log("err encoding " + err); 12 | return false; 13 | } 14 | } -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nixer89/xrpl-services-frontend/d766a900ce8c615ab0e540a2be31160024a48fb8/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/assets/christmas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nixer89/xrpl-services-frontend/d766a900ce8c615ab0e540a2be31160024a48fb8/src/assets/christmas.png -------------------------------------------------------------------------------- /src/assets/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nixer89/xrpl-services-frontend/d766a900ce8c615ab0e540a2be31160024a48fb8/src/assets/favicon-16x16.png -------------------------------------------------------------------------------- /src/assets/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nixer89/xrpl-services-frontend/d766a900ce8c615ab0e540a2be31160024a48fb8/src/assets/favicon-32x32.png -------------------------------------------------------------------------------- /src/assets/topbar_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nixer89/xrpl-services-frontend/d766a900ce8c615ab0e540a2be31160024a48fb8/src/assets/topbar_logo.png -------------------------------------------------------------------------------- /src/assets/xrpl_services_twitter_card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nixer89/xrpl-services-frontend/d766a900ce8c615ab0e540a2be31160024a48fb8/src/assets/xrpl_services_twitter_card.png -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/plugins/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nixer89/xrpl-services-frontend/d766a900ce8c615ab0e540a2be31160024a48fb8/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | XRPL Services 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | 2 | import { enableProdMode } from '@angular/core'; 3 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 4 | 5 | import { AppModule } from './app/app.module'; 6 | import { environment } from './environments/environment'; 7 | 8 | import 'codemirror/mode/javascript/javascript'; 9 | 10 | if (environment.production) { 11 | enableProdMode(); 12 | } 13 | 14 | platformBrowserDynamic().bootstrapModule(AppModule) 15 | .catch(err => console.error(err)); 16 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags.ts'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by default for Angular itself. 57 | */ 58 | import 'zone.js'; // Included with Angular CLI. 59 | 60 | 61 | /*************************************************************************************************** 62 | * APPLICATION IMPORTS 63 | */ 64 | (window as any).global = window; 65 | // @ts-ignore 66 | window.Buffer = window.Buffer || require('buffer').Buffer; 67 | 68 | (window as any).process = { 69 | env: { DEBUG: undefined }, 70 | version: "" 71 | }; -------------------------------------------------------------------------------- /src/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: /assets/test -------------------------------------------------------------------------------- /src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | @import './theme'; 3 | @import "~codemirror/lib/codemirror"; 4 | @import "~codemirror/theme/material-darker"; 5 | 6 | html, body { 7 | height: 100%; 8 | } 9 | 10 | body { 11 | margin: 0; 12 | font-family: Roboto, "Helvetica Neue", sans-serif; 13 | background-color: var(--background-color); 14 | } 15 | 16 | a.footerlink { 17 | margin-right: 1em; 18 | } 19 | 20 | .mat-toolbar.second-toolbar { 21 | background-color: #003297 !important; 22 | height: 36px; 23 | font-size: 0.8em; 24 | } 25 | 26 | .floatcenter { 27 | display: inline-block; 28 | margin: auto; 29 | flex-direction: column; 30 | justify-content: center; 31 | align-items: center; 32 | text-align: center; 33 | vertical-align: middle; 34 | } 35 | 36 | .floatleft { 37 | float: left; 38 | } 39 | 40 | .floatright { 41 | float: right; 42 | } 43 | 44 | .mat-button.sign-in-button { 45 | width: 230; 46 | min-width: 230px; 47 | height: 40px; 48 | padding-left: 0px; 49 | padding-right: 0px; 50 | } 51 | 52 | .mat-expansion-panel-header-title { 53 | width: 20%; 54 | align-items: center; 55 | } 56 | 57 | .mat-expansion-panel-header-description { 58 | width: 80%; 59 | align-items: center; 60 | } 61 | 62 | .mat-expansion-panel-body { 63 | padding-top: 6px !important; 64 | } 65 | 66 | body .snackbar-success { 67 | color: green !important; 68 | display: flex; 69 | justify-content: center; 70 | } 71 | 72 | body .snackbar-failed { 73 | color: #EB0000 !important; 74 | display: flex; 75 | justify-content: center; 76 | } 77 | 78 | .mat-card.margin-5 { 79 | margin: 5px; 80 | } 81 | 82 | .changed { 83 | transition: color 0s ease-in, text-shadow 0s ease-in, background-color 1.1s linear 0s; 84 | text-shadow: #bbb 2px 2px 2px; 85 | color: red; 86 | font-weight: 600; 87 | } 88 | 89 | .mat-hint { 90 | color: #f44336 !important; 91 | } 92 | 93 | // adds breaks to the mat-checkboxes with long labels 94 | .mat-checkbox-layout { 95 | white-space: normal !important; 96 | } 97 | 98 | // rather than center the checkbox, put the checkbox in the first line 99 | .mat-checkbox-inner-container { 100 | margin-top: 3px !important; 101 | } 102 | 103 | .title { 104 | font-weight: bold; 105 | } 106 | 107 | .CodeMirror { 108 | height: 400px; 109 | } 110 | 111 | @media (max-width:599px) { 112 | 113 | .mat-expansion-panel-header { 114 | padding: 0 12px !important; 115 | } 116 | 117 | .mat-expansion-panel-header-title { 118 | width: 40%; 119 | margin-right: 8px !important; 120 | padding: 0px !important; 121 | } 122 | 123 | .mat-expansion-panel-header-description { 124 | width: 60%; 125 | } 126 | 127 | .mat-expansion-panel-body { 128 | padding-right: 12px !important; 129 | padding-left: 12px !important; 130 | padding-top: 6px !important; 131 | } 132 | 133 | .mat-card { 134 | padding: 12px 8px 12px !important; 135 | } 136 | 137 | .mat-dialog-container { 138 | padding: 12px !important; 139 | } 140 | 141 | .CodeMirror { 142 | height: 300px; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /src/theme.scss: -------------------------------------------------------------------------------- 1 | @use '@angular/material' as mat; 2 | @import '@angular/material/theming'; 3 | 4 | @include mat.core(); 5 | 6 | .light-theme { 7 | $primary: mat.define-palette(mat.$indigo-palette); 8 | $accent: mat.define-palette(mat.$pink-palette, A200, A100, A400); 9 | $warn: mat.define-palette(mat.$red-palette); 10 | $light-theme: mat.define-light-theme($primary, $accent, $warn); 11 | 12 | .mat-expansion-panel { 13 | background-color: rgba(238,238,238, 0.5) !important; 14 | } 15 | 16 | .mat-expansion-panel.expansion-panel-light { 17 | background-color: #C0C0C0 !important; 18 | } 19 | 20 | .mat-expansion-panel-header.expantion-panel-light { 21 | background-color: #C0C0C0 !important; 22 | } 23 | 24 | .mat-expansion-panel.expansion-panel-darker { 25 | background-color: #A0A0A0 !important; 26 | } 27 | 28 | .mat-expansion-panel-header.expansion-panel-darker { 29 | background-color: #A0A0A0 !important; 30 | } 31 | 32 | .warning-background { 33 | background-color: #fc1a1a !important; 34 | } 35 | 36 | .warning { 37 | color: #EB0000; 38 | } 39 | 40 | .loading { 41 | color: blue; 42 | } 43 | 44 | .right-label-stats { 45 | color: blue; 46 | text-align: end; 47 | } 48 | 49 | .success { 50 | color: green 51 | } 52 | 53 | .theme-background { 54 | background-color: rgba(238,238,238, 0.5) !important; 55 | } 56 | 57 | .snackbar-success { 58 | background-color: white !important; 59 | } 60 | 61 | @include mat.all-component-themes($light-theme); 62 | } 63 | 64 | // Our dark theme 65 | .dark-theme { 66 | $dark-primary: mat.define-palette(mat.$indigo-palette); 67 | $dark-accent: mat.define-palette(mat.$amber-palette, A200, A100, A400); 68 | $dark-warn: mat.define-palette(mat.$red-palette); 69 | $dark-theme: mat.define-dark-theme($dark-primary, $dark-accent, $dark-warn); 70 | 71 | a { 72 | color: #FFB300; 73 | } 74 | 75 | a:hover { 76 | color: #FFA000; 77 | } 78 | 79 | a:active { 80 | color: #FF8F00; 81 | } 82 | 83 | .mat-form-field.mat-focused .mat-form-field-label { 84 | color: rgba(255, 255, 255, 0.7) !important; 85 | } 86 | 87 | .warning-background { 88 | background-color: #ff3131 !important; 89 | } 90 | 91 | .warning { 92 | color: #FF6B6B; 93 | } 94 | 95 | .loading { 96 | color: #FFB300; 97 | } 98 | 99 | .right-label-stats { 100 | color: #FFB300; 101 | text-align: end; 102 | } 103 | 104 | .success { 105 | color: #00B300 106 | } 107 | 108 | .mat-expansion-panel { 109 | background-color: rgba(50, 50, 50, 0.5) !important; 110 | } 111 | 112 | .mat-expansion-panel.expansion-panel-light { 113 | background-color: #303030 !important; 114 | } 115 | 116 | .mat-expansion-panel-header.expansion-panel-light { 117 | background-color: #303030 !important; 118 | } 119 | 120 | .mat-expansion-panel.expansion-panel-darker { 121 | background-color: #202020 !important; 122 | } 123 | 124 | .mat-expansion-panel-header.expansion-panel-darker { 125 | background-color: #202020 !important; 126 | } 127 | 128 | .theme-background { 129 | background-color:rgba(50, 50, 50, 0.5) !important; 130 | } 131 | 132 | .snackbar-success { 133 | background-color: #303030 !important; 134 | } 135 | 136 | .mat-option.mat-selected:not(.mat-option-disabled) { 137 | color: #FF8F00 !important 138 | } 139 | 140 | @include mat.all-component-themes($dark-theme); 141 | } -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/app", 5 | "types": [] 6 | }, 7 | "files": [ 8 | "src/main.ts", 9 | "src/polyfills.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.d.ts" 13 | ], 14 | "exclude": [ 15 | "src/test.ts", 16 | "src/**/*.spec.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "downlevelIteration": true, 9 | "experimentalDecorators": true, 10 | "module": "es2020", 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "resolveJsonModule": true, 14 | "allowSyntheticDefaultImports": true, 15 | "target": "es2020", 16 | "typeRoots": [ 17 | "node_modules/@types" 18 | ], 19 | "lib": [ 20 | "es2018", 21 | "dom" 22 | ], 23 | "paths": { 24 | "stream": ["node_modules/stream-browserify"], 25 | "crypto": ["node_modules/crypto-browserify"], 26 | "vm": ["node_modules/vm-browserify"] 27 | } 28 | }, 29 | "angularCompilerOptions": { 30 | "fullTemplateTypeCheck": true, 31 | "strictInjectionParameters": true 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rules": { 4 | "align": { 5 | "options": [ 6 | "parameters", 7 | "statements" 8 | ] 9 | }, 10 | "array-type": false, 11 | "arrow-parens": false, 12 | "arrow-return-shorthand": true, 13 | "deprecation": { 14 | "severity": "warning" 15 | }, 16 | "component-class-suffix": true, 17 | "contextual-lifecycle": true, 18 | "curly": true, 19 | "directive-class-suffix": true, 20 | "directive-selector": [ 21 | true, 22 | "attribute", 23 | "app", 24 | "camelCase" 25 | ], 26 | "component-selector": [ 27 | true, 28 | "element", 29 | "app", 30 | "kebab-case" 31 | ], 32 | "eofline": true, 33 | "import-blacklist": [ 34 | true, 35 | "rxjs/Rx" 36 | ], 37 | "import-spacing": true, 38 | "indent": { 39 | "options": [ 40 | "spaces" 41 | ] 42 | }, 43 | "interface-name": false, 44 | "max-classes-per-file": false, 45 | "max-line-length": [ 46 | true, 47 | 140 48 | ], 49 | "member-access": false, 50 | "member-ordering": [ 51 | true, 52 | { 53 | "order": [ 54 | "static-field", 55 | "instance-field", 56 | "static-method", 57 | "instance-method" 58 | ] 59 | } 60 | ], 61 | "no-consecutive-blank-lines": false, 62 | "no-console": [ 63 | true, 64 | "debug", 65 | "info", 66 | "time", 67 | "timeEnd", 68 | "trace" 69 | ], 70 | "no-empty": false, 71 | "no-inferrable-types": [ 72 | true, 73 | "ignore-params" 74 | ], 75 | "no-non-null-assertion": true, 76 | "no-redundant-jsdoc": true, 77 | "no-switch-case-fall-through": true, 78 | "no-var-requires": false, 79 | "object-literal-key-quotes": [ 80 | true, 81 | "as-needed" 82 | ], 83 | "object-literal-sort-keys": false, 84 | "ordered-imports": false, 85 | "quotemark": [ 86 | true, 87 | "single" 88 | ], 89 | "trailing-comma": false, 90 | "no-conflicting-lifecycle": true, 91 | "no-host-metadata-property": true, 92 | "no-input-rename": true, 93 | "no-inputs-metadata-property": true, 94 | "no-output-native": true, 95 | "no-output-on-prefix": true, 96 | "no-output-rename": true, 97 | "semicolon": { 98 | "options": [ 99 | "always" 100 | ] 101 | }, 102 | "space-before-function-paren": { 103 | "options": { 104 | "anonymous": "never", 105 | "asyncArrow": "always", 106 | "constructor": "never", 107 | "method": "never", 108 | "named": "never" 109 | } 110 | }, 111 | "no-outputs-metadata-property": true, 112 | "template-banana-in-box": true, 113 | "template-no-negated-async": true, 114 | "typedef-whitespace": { 115 | "options": [ 116 | { 117 | "call-signature": "nospace", 118 | "index-signature": "nospace", 119 | "parameter": "nospace", 120 | "property-declaration": "nospace", 121 | "variable-declaration": "nospace" 122 | }, 123 | { 124 | "call-signature": "onespace", 125 | "index-signature": "onespace", 126 | "parameter": "onespace", 127 | "property-declaration": "onespace", 128 | "variable-declaration": "onespace" 129 | } 130 | ] 131 | }, 132 | "use-lifecycle-interface": true, 133 | "use-pipe-transform-interface": true, 134 | "variable-name": { 135 | "options": [ 136 | "ban-keywords", 137 | "check-format", 138 | "allow-pascal-case" 139 | ] 140 | }, 141 | "whitespace": { 142 | "options": [ 143 | "check-branch", 144 | "check-decl", 145 | "check-operator", 146 | "check-separator", 147 | "check-type", 148 | "check-typecast" 149 | ] 150 | } 151 | }, 152 | "rulesDirectory": [ 153 | "codelyzer" 154 | ] 155 | } --------------------------------------------------------------------------------