├── static ├── .gitkeep ├── Icon.icns ├── icon.ico ├── logo.ico ├── logo.png ├── icon_16x16.png ├── icon_32x32.png ├── iconMessage.ico ├── iconMessage.png ├── icon_128x128.png ├── icon_16x16@2x.png ├── icon_256x256.png ├── icon_32x32@2x.png ├── iconTransparent.ico ├── iconTransparent.png ├── icon_128x128@2x.png ├── icon_256x256@2x.png ├── iconMessageNotWin.png ├── iconMessageNotWin@2x.png ├── iconMessageNotWin@3x.png ├── iconTransparentNotWin.png ├── iconTransparentNotWin@2x.png └── iconTransparentNotWin@3x.png ├── connector.dll ├── connector.dylib ├── .env.example ├── .eslintignore ├── test ├── .eslintrc ├── e2e │ ├── specs │ │ ├── Launch.spec.js │ │ ├── deviceManager.spec.js │ │ └── api.spec.js │ ├── index.js │ └── utils.js └── unit │ ├── specs │ └── page.spec.js │ ├── index.js │ └── karma.conf.js ├── src ├── proto │ ├── substrate.proto │ ├── common.proto │ ├── tron.proto │ ├── cosmos.proto │ ├── eth.proto │ ├── filecoin.proto │ ├── btcfork.proto │ ├── eos.proto │ ├── btc.proto │ ├── device.proto │ ├── api.proto │ ├── substrate_pb.js │ └── common_pb.js ├── renderer │ ├── store │ │ ├── index.js │ │ └── dapps.js │ ├── App.vue │ ├── router │ │ └── index.js │ ├── main.js │ ├── views │ │ ├── appStart.vue │ │ └── welcomeHome.vue │ └── common │ │ └── lang │ │ └── zh.js ├── main │ └── index.dev.js ├── index.ejs ├── worker.ejs ├── common │ ├── path.js │ └── constants.js └── api │ ├── loadFail.html │ ├── crypto.js │ ├── callimkeycore.js │ ├── ethereumdapp_imkey_web3.js │ ├── polkadotdapp.js │ └── ethereumdapp.js ├── .gitignore ├── appveyor.yml ├── .eslintrc.js ├── protobuild.sh ├── .babelrc ├── README.md ├── .travis.yml ├── .electron-vue ├── dev-client.js ├── webpack.main.config.js ├── build.js ├── dev-runner.js ├── webpack.web.config.js └── webpack.renderer.config.js ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── package.json └── LICENSE /static/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /connector.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/consenlabs/imkey-manager/HEAD/connector.dll -------------------------------------------------------------------------------- /connector.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/consenlabs/imkey-manager/HEAD/connector.dylib -------------------------------------------------------------------------------- /static/Icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/consenlabs/imkey-manager/HEAD/static/Icon.icns -------------------------------------------------------------------------------- /static/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/consenlabs/imkey-manager/HEAD/static/icon.ico -------------------------------------------------------------------------------- /static/logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/consenlabs/imkey-manager/HEAD/static/logo.ico -------------------------------------------------------------------------------- /static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/consenlabs/imkey-manager/HEAD/static/logo.png -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | bindCode_encryptionKey= 2 | TEAM_SHORT_NAME= 3 | APPLE_ID= 4 | APPLE_ID_PASSWORD= 5 | -------------------------------------------------------------------------------- /static/icon_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/consenlabs/imkey-manager/HEAD/static/icon_16x16.png -------------------------------------------------------------------------------- /static/icon_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/consenlabs/imkey-manager/HEAD/static/icon_32x32.png -------------------------------------------------------------------------------- /static/iconMessage.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/consenlabs/imkey-manager/HEAD/static/iconMessage.ico -------------------------------------------------------------------------------- /static/iconMessage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/consenlabs/imkey-manager/HEAD/static/iconMessage.png -------------------------------------------------------------------------------- /static/icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/consenlabs/imkey-manager/HEAD/static/icon_128x128.png -------------------------------------------------------------------------------- /static/icon_16x16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/consenlabs/imkey-manager/HEAD/static/icon_16x16@2x.png -------------------------------------------------------------------------------- /static/icon_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/consenlabs/imkey-manager/HEAD/static/icon_256x256.png -------------------------------------------------------------------------------- /static/icon_32x32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/consenlabs/imkey-manager/HEAD/static/icon_32x32@2x.png -------------------------------------------------------------------------------- /static/iconTransparent.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/consenlabs/imkey-manager/HEAD/static/iconTransparent.ico -------------------------------------------------------------------------------- /static/iconTransparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/consenlabs/imkey-manager/HEAD/static/iconTransparent.png -------------------------------------------------------------------------------- /static/icon_128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/consenlabs/imkey-manager/HEAD/static/icon_128x128@2x.png -------------------------------------------------------------------------------- /static/icon_256x256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/consenlabs/imkey-manager/HEAD/static/icon_256x256@2x.png -------------------------------------------------------------------------------- /static/iconMessageNotWin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/consenlabs/imkey-manager/HEAD/static/iconMessageNotWin.png -------------------------------------------------------------------------------- /static/iconMessageNotWin@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/consenlabs/imkey-manager/HEAD/static/iconMessageNotWin@2x.png -------------------------------------------------------------------------------- /static/iconMessageNotWin@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/consenlabs/imkey-manager/HEAD/static/iconMessageNotWin@3x.png -------------------------------------------------------------------------------- /static/iconTransparentNotWin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/consenlabs/imkey-manager/HEAD/static/iconTransparentNotWin.png -------------------------------------------------------------------------------- /static/iconTransparentNotWin@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/consenlabs/imkey-manager/HEAD/static/iconTransparentNotWin@2x.png -------------------------------------------------------------------------------- /static/iconTransparentNotWin@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/consenlabs/imkey-manager/HEAD/static/iconTransparentNotWin@3x.png -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | test/unit/coverage/** 2 | test/unit/*.js 3 | test/e2e/*.js 4 | src/proto/** 5 | src/api/devicemanagerapi.js 6 | src/api/walletapi.js 7 | src/api/imkey_web3_provider.js -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | }, 5 | "globals": { 6 | "assert": true, 7 | "expect": true, 8 | "should": true, 9 | "__static": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/proto/substrate.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package substrateapi; 3 | 4 | message SubstrateRawTxIn { 5 | string rawData = 1; 6 | } 7 | 8 | message SubstrateTxOut { 9 | string signature = 1; 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | dist/electron/* 3 | dist/web/* 4 | build/* 5 | !build/icons 6 | node_modules/ 7 | npm-debug.log 8 | npm-debug.log.* 9 | thumbs.db 10 | imkey-desktop.iml 11 | !.gitkeep 12 | .idea/ 13 | yarn-error.log 14 | key.env 15 | key.env.prod 16 | 17 | .env 18 | -------------------------------------------------------------------------------- /src/proto/common.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package common; 3 | 4 | import "google/protobuf/any.proto"; 5 | message SignParam { 6 | string chainType = 1; 7 | string path = 2; 8 | string network = 3; 9 | google.protobuf.Any input = 4; 10 | string payment = 5; 11 | string receiver = 6; 12 | string sender = 7; 13 | string fee = 8; 14 | } -------------------------------------------------------------------------------- /src/proto/tron.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package tronapi; 3 | 4 | message TronTxInput { 5 | string raw_data = 2; 6 | } 7 | 8 | message TronTxOutput { 9 | string signature = 1; 10 | } 11 | 12 | message TronMessageInput { 13 | string message = 2; 14 | bool is_hex =4; 15 | bool is_tron_header=5; 16 | } 17 | 18 | message TronMessageOutput { 19 | string signature = 1; 20 | } -------------------------------------------------------------------------------- /test/e2e/specs/Launch.spec.js: -------------------------------------------------------------------------------- 1 | // import utils from '../utils' 2 | // 3 | // describe('Launch', function () { 4 | // beforeEach(utils.beforeEach) 5 | // afterEach(utils.afterEach) 6 | // 7 | // it('shows the proper application title', function () { 8 | // return this.app.client.getTitle() 9 | // .then(title => { 10 | // expect(title).to.equal('{{ name }}') 11 | // }) 12 | // }) 13 | // }) 14 | -------------------------------------------------------------------------------- /test/unit/specs/page.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import stepPage from '../../../src/renderer/views/steps/step' 3 | 4 | describe('step.vue', () => { 5 | it('should render correct contents', () => { 6 | const vm = new Vue({ 7 | el: document.createElement('div'), 8 | render: h => h(stepPage) 9 | }).$mount() 10 | 11 | expect(vm.$el.querySelector('.title').textContent).to.contain('连接') 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /test/e2e/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // Set BABEL_ENV to use proper .env config 4 | process.env.BABEL_ENV = 'test' 5 | 6 | // Enable use of ES6+ on required files 7 | require('@babel/register')({ 8 | ignore: [/node_modules/] 9 | }) 10 | 11 | // Attach Chai APIs to global scope 12 | let { expect,should,assert } = require('chai') 13 | global.expect = expect 14 | global.should = should 15 | global.assert = assert 16 | 17 | // Require all JS files in `./specs` for Mocha to consume 18 | require('require-dir')('./specs') 19 | -------------------------------------------------------------------------------- /src/proto/cosmos.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package cosmosapi; 3 | 4 | message Coin { 5 | string amount = 1; 6 | string denom = 2; 7 | } 8 | 9 | message StdFee { 10 | repeated Coin amount = 1; 11 | string gas = 2; 12 | } 13 | 14 | 15 | message CosmosTxInput { 16 | string account_number = 1; 17 | string chain_id = 2; 18 | StdFee fee = 3; 19 | string memo = 4; 20 | string msgs = 5; 21 | string sequence = 6; 22 | } 23 | 24 | message CosmosTxOutput { 25 | string signature = 1; 26 | string txHash = 2; 27 | } 28 | -------------------------------------------------------------------------------- /src/proto/eth.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ethapi; 3 | 4 | message EthTxInput { 5 | string nonce = 1; 6 | string gas_price = 2; 7 | string gas_limit = 3; 8 | string to = 4; 9 | string value = 5; 10 | string data = 6; 11 | string chain_id = 7; 12 | } 13 | 14 | message EthTxOutput { 15 | string signature = 1; 16 | string txHash = 2; 17 | } 18 | 19 | message EthMessageInput { 20 | string message = 1; 21 | bool isPersonalSign = 2; 22 | } 23 | 24 | message EthMessageOutput { 25 | string signature = 1; 26 | } -------------------------------------------------------------------------------- /test/e2e/utils.js: -------------------------------------------------------------------------------- 1 | import electron from 'electron' 2 | import { Application } from 'spectron' 3 | 4 | export default { 5 | afterEach () { 6 | this.timeout(10000) 7 | 8 | if (this.app && this.app.isRunning()) { 9 | return this.app.stop() 10 | } 11 | }, 12 | beforeEach () { 13 | this.timeout(10000) 14 | this.app = new Application({ 15 | path: electron, 16 | args: ['dist/electron/main.js'], 17 | startTimeout: 10000, 18 | waitTimeout: 10000 19 | }) 20 | 21 | return this.app.start() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 0.1.{build} 2 | 3 | branches: 4 | only: 5 | - master 6 | 7 | image: Visual Studio 2019 8 | platform: 9 | - x64 10 | 11 | cache: 12 | - node_modules 13 | - '%APPDATA%\npm-cache' 14 | - '%USERPROFILE%\.electron' 15 | - '%USERPROFILE%\AppData\Local\Yarn\cache' 16 | 17 | init: 18 | - git config --global core.autocrlf input 19 | 20 | install: 21 | - ps: Install-Product node 12 x64 22 | - git reset --hard HEAD 23 | - yarn 24 | - node --version 25 | 26 | build_script: 27 | #- yarn build 28 | - npm run release 29 | 30 | test: off 31 | -------------------------------------------------------------------------------- /test/unit/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | Vue.config.devtools = false 3 | Vue.config.productionTip = false 4 | 5 | // require all test files (files that ends with .spec.js) 6 | const testsContext = require.context('./specs', true, /\.spec$/) 7 | testsContext.keys().forEach(testsContext) 8 | 9 | // require all src files except main.js for coverage. 10 | // you can also change this to match only the subset of files that 11 | // you want coverage for. 12 | const srcContext = require.context('../../src/renderer', true, /^\.\/(?!main(\.js)?$)/) 13 | srcContext.keys().forEach(srcContext) 14 | -------------------------------------------------------------------------------- /src/proto/filecoin.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package filecoinapi; 3 | 4 | message FilecoinTxInput { 5 | string to = 1; 6 | string from = 2; 7 | uint64 nonce = 3; 8 | string value = 4; 9 | int64 gasLimit = 5; 10 | string gasFeeCap = 6; 11 | string gasPremium = 7; 12 | uint64 method = 8; 13 | string params = 9; 14 | } 15 | 16 | message FilecoinTxOutput { 17 | string cid = 1; 18 | FilecoinTxInput message = 2; 19 | Signature signature = 3; 20 | } 21 | 22 | message Signature { 23 | uint32 type = 1; 24 | string data = 2; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: 'babel-eslint', 4 | parser: 'vue-eslint-parser', 5 | parserOptions: { 6 | sourceType: 'module' 7 | }, 8 | env: { 9 | browser: true, 10 | node: true 11 | }, 12 | extends: 'standard', 13 | globals: { 14 | __static: true 15 | }, 16 | plugins: [ 17 | 'html' 18 | ], 19 | 'rules': { 20 | // allow paren-less arrow functions 21 | 'arrow-parens': 0, 22 | // allow async-await 23 | 'generator-star-spacing': 0, 24 | // allow debugger during development 25 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/proto/btcfork.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package btcforkapi; 3 | 4 | message Utxo { 5 | string txHash = 1; 6 | int32 vout = 2; 7 | int64 amount = 3; 8 | string address = 4; 9 | string scriptPubKey = 5; 10 | string derivedPath = 6; 11 | int64 sequence = 7; 12 | } 13 | 14 | message BtcForkTxInput { 15 | string to = 1; 16 | int64 amount = 2; 17 | repeated Utxo unspents = 3; 18 | int64 fee = 4; 19 | uint32 changeAddressIndex = 5; 20 | string changeAddress = 6; 21 | string segWit = 7; 22 | } 23 | 24 | message BtcForkTxOutput { 25 | string signature = 1; 26 | string txHash = 2; 27 | string wtxHash = 3; 28 | } -------------------------------------------------------------------------------- /src/renderer/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | Vue.use(Vuex) 5 | 6 | export default new Vuex.Store({ 7 | state: { 8 | agree: true, 9 | message: '', 10 | activeStatus: '', 11 | apps: [], 12 | isCosUpdate: false, 13 | cosOldVersionData: '', 14 | cosNewVersionData: '', 15 | bleOldVersionData: '', 16 | bleNewVersionData: '', 17 | buttonTexts: '', 18 | userPath: '', 19 | isFirstGoToManagerPage: true, 20 | WalletAddress: [], 21 | installedBleVersion: '' 22 | }, 23 | mutations: {}, 24 | actions: {}, 25 | modules: {}, 26 | accounts: ['0xa6c82cf246f820f70d3c11b1b518b2d0eaca3258'] 27 | }) 28 | -------------------------------------------------------------------------------- /protobuild.sh: -------------------------------------------------------------------------------- 1 | cd src/proto 2 | protoc --js_out=import_style=commonjs,binary:. api.proto 3 | protoc --js_out=import_style=commonjs,binary:. btc.proto 4 | protoc --js_out=import_style=commonjs,binary:. btcfork.proto 5 | protoc --js_out=import_style=commonjs,binary:. cosmos.proto 6 | protoc --js_out=import_style=commonjs,binary:. device.proto 7 | protoc --js_out=import_style=commonjs,binary:. eos.proto 8 | protoc --js_out=import_style=commonjs,binary:. eth.proto 9 | protoc --js_out=import_style=commonjs,binary:. common.proto 10 | protoc --js_out=import_style=commonjs,binary:. filecoin.proto 11 | protoc --js_out=import_style=commonjs,binary:. substrate.proto 12 | protoc --js_out=import_style=commonjs,binary:. tron.proto 13 | cd ../../ 14 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "comments": false, 3 | "env": { 4 | "test": { 5 | "presets": [ 6 | ["@babel/env", { 7 | "targets": { "node": 12 } 8 | }], 9 | ], 10 | "plugins": ["istanbul"] 11 | }, 12 | "main": { 13 | "presets": [ 14 | ["@babel/env", { 15 | "targets": { "node": 12 } 16 | }], 17 | ] 18 | }, 19 | "renderer": { 20 | "presets": [ 21 | ["@babel/env", { 22 | "modules": false 23 | }], 24 | ] 25 | }, 26 | "web": { 27 | "presets": [ 28 | ["@babel/env", { 29 | "modules": false 30 | }], 31 | ] 32 | } 33 | }, 34 | "plugins": ["@babel/plugin-transform-runtime"] 35 | } 36 | -------------------------------------------------------------------------------- /src/proto/eos.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package eosapi; 3 | 4 | message EosTxInput { 5 | repeated EosSignData transactions = 1; 6 | } 7 | 8 | message EosSignData { 9 | string txHex = 1; 10 | repeated string publicKeys = 2; 11 | string chainId = 3; 12 | string receiver = 4; 13 | string payment = 5; 14 | string sender = 6; 15 | } 16 | 17 | message EosTxOutput { 18 | repeated EosSignResult trans_multi_signs = 1; 19 | } 20 | 21 | message EosSignResult { 22 | string hash = 1; 23 | repeated string signs = 2; 24 | } 25 | 26 | 27 | message EosMessageInput { 28 | string data = 1; 29 | string pubkey = 2; 30 | bool isHex = 3; 31 | } 32 | 33 | message EosMessageOutput { 34 | string signature = 1; 35 | } -------------------------------------------------------------------------------- /src/main/index.dev.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is used specifically and only for development. It installs 3 | * `electron-debug` & `vue-devtools`. There shouldn't be any need to 4 | * modify this file, but it can be used to extend your development 5 | * environment. 6 | */ 7 | 8 | /* eslint-disable */ 9 | 10 | // Install `electron-debug` with `devtron` 11 | require('electron-debug')({showDevTools: true}) 12 | 13 | // Install `vue-devtools` 14 | require('electron').app.on('ready', () => { 15 | let installExtension = require('electron-devtools-installer') 16 | installExtension.default(installExtension.VUEJS_DEVTOOLS) 17 | .then(() => { 18 | }) 19 | .catch(err => { 20 | console.log('Unable to install `vue-devtools`: \n', err) 21 | }) 22 | }) 23 | 24 | // Require `main` process to boot app 25 | require('./index') -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # imKey Manager Tool 2 | 3 | Desktop management tools for imKey wallet 4 | 5 | WARNING: not production ready yet. 6 | 7 | ## Goals 8 | * Unify interface for wallet common logic with multi blockchain support 9 | * Cross platform, on macos, linux, windows 10 | 11 | ## Layout 12 | * `.electron-vue` compile and package scripts 13 | * `build` build reference files 14 | * `dist` compilation output directory 15 | * `src ` project core code 16 | * `static` static file directory 17 | 18 | ## Code Styles 19 | This project is using pre-commit. Please install the git pre-commit hooks on you clone. 20 | 21 | Every time you will try to commit, pre-commit will run checks on your files to make sure they follow our style standards 22 | and they aren't affected by some simple issues. If the checks fail, pre-commit won't let you commit. 23 | 24 | ## License 25 | Apache Licence v2.0 26 | -------------------------------------------------------------------------------- /src/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | imKey Manager 6 | 7 | 8 | <% if (htmlWebpackPlugin.options.nodeModules) { %> 9 | 10 | 13 | <% } %> 14 | 15 | 16 |
17 | 18 | <% if (!process.browser) { %> 19 | 22 | <% } %> 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/worker.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | imkey-manager 6 | 7 | 8 | <% if (htmlWebpackPlugin.options.nodeModules) { %> 9 | 10 | 13 | <% } %> 14 | 15 | 16 |
17 | 18 | <% if (!process.browser) { %> 19 | 22 | <% } %> 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/proto/btc.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package btcapi; 3 | 4 | message Utxo { 5 | string tx_hash = 1; 6 | int32 vout = 2; 7 | int64 amount = 3; 8 | string address = 4; 9 | string script_pubKey = 5; 10 | string derived_path = 6; 11 | int64 sequence = 7; 12 | } 13 | 14 | message BtcTxExtra { 15 | string opReturn = 1; 16 | int32 propertyId = 2; 17 | string feeMode = 3; 18 | } 19 | message BtcTxInput { 20 | string to = 1; 21 | int64 amount = 2; 22 | int64 fee = 3; 23 | uint32 change_address_index = 4; 24 | repeated Utxo unspents = 5; 25 | string segWit = 6; 26 | string protocol = 7; 27 | BtcTxExtra extra = 8; 28 | } 29 | 30 | message BtcTxOutput { 31 | string signature = 1; 32 | string txHash = 2; 33 | string wtxHash = 3; 34 | } 35 | 36 | message BtcXpubReq { 37 | string network = 1; 38 | string path = 2; 39 | } 40 | 41 | message BtcXpubRes { 42 | string xpub = 1; 43 | } 44 | 45 | 46 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | osx_image: xcode8.3 2 | sudo: required 3 | dist: trusty 4 | language: c 5 | matrix: 6 | include: 7 | - os: osx 8 | - os: linux 9 | env: CC=clang CXX=clang++ npm_config_clang=1 10 | compiler: clang 11 | cache: 12 | directories: 13 | - node_modules 14 | - "$HOME/.electron" 15 | - "$HOME/.cache" 16 | addons: 17 | apt: 18 | packages: 19 | - libgnome-keyring-dev 20 | - icnsutils 21 | before_install: 22 | - mkdir -p /tmp/git-lfs && curl -L https://github.com/github/git-lfs/releases/download/v1.2.1/git-lfs-$([ 23 | "$TRAVIS_OS_NAME" == "linux" ] && echo "linux" || echo "darwin")-amd64-1.2.1.tar.gz 24 | | tar -xz -C /tmp/git-lfs --strip-components 1 && /tmp/git-lfs/git-lfs pull 25 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install --no-install-recommends -y icnsutils graphicsmagick xz-utils; fi 26 | install: 27 | - nvm install 12 28 | - curl -o- -L https://yarnpkg.com/install.sh | bash 29 | - source ~/.bashrc 30 | - npm install -g xvfb-maybe 31 | - yarn 32 | script: 33 | - npm run release 34 | - yarn run build 35 | branches: 36 | only: 37 | - master 38 | -------------------------------------------------------------------------------- /src/common/path.js: -------------------------------------------------------------------------------- 1 | const BTC_PATH_PREFIX = "m/44'/0'/0'" 2 | const BITCOIN_TESTNET_PATH = "m/44'/1'/0'" 3 | const BTC_SEGWIT_PATH_PREFIX = "m/49'/0'/0'/" 4 | const BITCOIN_SEGWIT_TESTNET_PATH = "m/49'/1'/0'/" 5 | const ETH_LEDGER = "m/44'/60'/0'/0/0" 6 | const EOS_LEDGER = "m/44'/194'/0'/0/0" 7 | const COSMOS_LEDGER = "m/44'/118'/0'/0/0" 8 | // 9 | // function checkPath (path) { 10 | // // 深度大于1,小于10,目前规范是5 11 | // if (path.split('/').length < 2 || path.split('/').length > 10) { 12 | // throw new ImkeyException(Messages.IMKEY_PATH_ILLEGAL) 13 | // } 14 | // 15 | // // 长度不超过100个字符 16 | // if (path.length() > 100) { 17 | // throw new ImkeyException(Messages.IMKEY_PATH_ILLEGAL) 18 | // } 19 | // 20 | // // 以m/开头 21 | // const regEx = "^m/[0-9'/]+$" 22 | // if (!Pattern.matches(regEx, path)) { 23 | // throw new ImkeyException(Messages.IMKEY_PATH_ILLEGAL) 24 | // } 25 | // } 26 | 27 | module.exports = { 28 | BTC_PATH_PREFIX, 29 | BITCOIN_TESTNET_PATH, 30 | BTC_SEGWIT_PATH_PREFIX, 31 | BITCOIN_SEGWIT_TESTNET_PATH, 32 | ETH_LEDGER, 33 | EOS_LEDGER, 34 | COSMOS_LEDGER 35 | } 36 | -------------------------------------------------------------------------------- /src/api/loadFail.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

访问出错,请检查你的网络连接

7 | 8 | -------------------------------------------------------------------------------- /src/renderer/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 47 | -------------------------------------------------------------------------------- /src/renderer/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | 4 | Vue.use(VueRouter) 5 | 6 | const routes = [ 7 | { 8 | path: '/', 9 | name: '/', 10 | component: require('@/views/appStart').default 11 | }, 12 | { 13 | path: '/appStart', 14 | name: 'appStart', 15 | component: require('@/views/appStart').default 16 | }, 17 | { 18 | path: '/connectDevice', 19 | name: 'connectDevice', 20 | component: require('@/views/connectDevice').default 21 | }, 22 | { 23 | path: '/imKeySetting', 24 | name: 'imKeySetting', 25 | component: require('@/views/imKeySetting').default 26 | }, 27 | { 28 | path: '/home', 29 | name: 'home', 30 | component: require('@/views/home').default, 31 | children: [ 32 | { 33 | path: 'welcomeHome', 34 | name: 'welcomeHome', 35 | component: require('@/views/welcomeHome').default 36 | }, 37 | { 38 | path: 'manager', 39 | name: 'manager', 40 | component: require('@/views/manager').default 41 | }, 42 | { 43 | path: 'setting', 44 | name: 'setting', 45 | component: require('@/views/setting').default 46 | } 47 | ] 48 | } 49 | ] 50 | const router = new VueRouter({ 51 | routes 52 | }) 53 | 54 | export default router 55 | -------------------------------------------------------------------------------- /.electron-vue/dev-client.js: -------------------------------------------------------------------------------- 1 | const hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true') 2 | 3 | hotClient.subscribe(event => { 4 | /** 5 | * Reload browser when HTMLWebpackPlugin emits a new index.html 6 | * 7 | * Currently disabled until jantimon/html-webpack-plugin#680 is resolved. 8 | * https://github.com/SimulatedGREG/electron-vue/issues/437 9 | * https://github.com/jantimon/html-webpack-plugin/issues/680 10 | */ 11 | // if (event.action === 'reload') { 12 | // window.location.reload() 13 | // } 14 | 15 | /** 16 | * Notify `mainWindow` when `main` process is compiling, 17 | * giving notice for an expected reload of the `electron` process 18 | */ 19 | if (event.action === 'compiling') { 20 | document.body.innerHTML += ` 21 | 34 | 35 |
36 | Compiling Main Process... 37 |
38 | ` 39 | } 40 | }) 41 | -------------------------------------------------------------------------------- /test/unit/karma.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('path') 4 | const merge = require('webpack-merge') 5 | const webpack = require('webpack') 6 | 7 | const baseConfig = require('../../.electron-vue/webpack.renderer.config') 8 | const projectRoot = path.resolve(__dirname, '../../src/renderer') 9 | 10 | // Set BABEL_ENV to use proper preset config 11 | process.env.BABEL_ENV = 'test' 12 | 13 | let webpackConfig = merge(baseConfig, { 14 | devtool: '#inline-source-map', 15 | plugins: [ 16 | new webpack.DefinePlugin({ 17 | 'process.env.NODE_ENV': '"testing"' 18 | }) 19 | ] 20 | }) 21 | 22 | // don't treat dependencies as externals 23 | delete webpackConfig.entry 24 | delete webpackConfig.externals 25 | delete webpackConfig.output.libraryTarget 26 | 27 | // apply vue option to apply isparta-loader on js 28 | webpackConfig.module.rules 29 | .find(rule => rule.use.loader === 'vue-loader').use.options.loaders.js = 'babel-loader' 30 | 31 | module.exports = config => { 32 | config.set({ 33 | browsers: ['visibleElectron'], 34 | client: { 35 | useIframe: false 36 | }, 37 | coverageReporter: { 38 | dir: './coverage', 39 | reporters: [ 40 | { type: 'lcov', subdir: '.' }, 41 | { type: 'text-summary' } 42 | ] 43 | }, 44 | customLaunchers: { 45 | 'visibleElectron': { 46 | base: 'Electron', 47 | flags: ['--show'] 48 | } 49 | }, 50 | frameworks: ['mocha', 'chai'], 51 | files: ['./index.js'], 52 | preprocessors: { 53 | './index.js': ['webpack', 'sourcemap'] 54 | }, 55 | reporters: ['spec', 'coverage'], 56 | singleRun: true, 57 | webpack: webpackConfig, 58 | webpackMiddleware: { 59 | noInfo: true 60 | } 61 | }) 62 | } 63 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. 4 | 5 | We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality. 6 | 7 | Examples of unacceptable behavior by participants include: 8 | 9 | - The use of sexualized language or imagery 10 | - Personal attacks 11 | - Trolling or insulting/derogatory comments 12 | - Public or private harassment 13 | - Publishing other's private information, such as physical or electronic addresses, without explicit permission 14 | - Other unethical or unprofessional conduct. 15 | 16 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team. 17 | 18 | This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. 19 | 20 | This Code of Conduct is adapted from the Contributor Covenant ([http://contributor-covenant.org](http://contributor-covenant.org/)), version 1.2.0, available at [http://contributor-covenant.org/version/1/2/0](http://contributor-covenant.org/version/1/2/0/) 21 | 22 | Instances of abusive, harassing, or otherwise unacceptable behavior in imKeyCore maybe reported by contacting maintainer via support@imkey.im -------------------------------------------------------------------------------- /src/api/crypto.js: -------------------------------------------------------------------------------- 1 | 2 | import crypto from 'crypto' 3 | 4 | // /!\ changing those presets would lock out users with already encrypted databases due to breaking changes. 5 | const ENCRYPTION_ALGORITHM = 'aes-256-cbc' 6 | const IV_LENGTH = 16 7 | const PBKDF2_ITERATIONS = 10000 8 | const PBKDF2_KEY_LENGTH = 32 9 | const PBKDF2_DIGEST = 'sha512' 10 | 11 | export const encryptData = (data, encryptionKey) => { 12 | // in any case, we save new data using an initialization vector 13 | const initializationVector = crypto.randomBytes(IV_LENGTH) 14 | const password = crypto.pbkdf2Sync( 15 | encryptionKey, 16 | initializationVector.toString(), 17 | PBKDF2_ITERATIONS, 18 | PBKDF2_KEY_LENGTH, 19 | PBKDF2_DIGEST 20 | ) 21 | const cipher = crypto.createCipheriv(ENCRYPTION_ALGORITHM, password, initializationVector) 22 | return Buffer.concat([ 23 | initializationVector, 24 | Buffer.from(':'), 25 | cipher.update(data, 'utf8'), 26 | cipher.final() 27 | ]).toString('base64') 28 | } 29 | 30 | export const decryptData = (raw, encryptionKey) => { 31 | const data = Buffer.from(raw, 'base64') 32 | 33 | // We check if the data include an initialization vector 34 | if (data.slice(IV_LENGTH, IV_LENGTH + 1).toString() === ':') { 35 | const initializationVector = data.slice(0, IV_LENGTH) 36 | const password = crypto.pbkdf2Sync( 37 | encryptionKey, 38 | initializationVector.toString(), 39 | PBKDF2_ITERATIONS, 40 | PBKDF2_KEY_LENGTH, 41 | PBKDF2_DIGEST 42 | ) 43 | const decipher = crypto.createDecipheriv(ENCRYPTION_ALGORITHM, password, initializationVector) 44 | return Buffer.concat([decipher.update(data.slice(IV_LENGTH + 1)), decipher.final()]).toString( 45 | 'utf8' 46 | ) 47 | } 48 | 49 | // if not, then we fallback to the deprecated API 50 | // eslint-disable-next-line node/no-deprecated-api 51 | const decipher = crypto.createDecipher(ENCRYPTION_ALGORITHM, encryptionKey) 52 | return Buffer.concat([decipher.update(data), decipher.final()]).toString('utf8') 53 | } 54 | -------------------------------------------------------------------------------- /src/api/callimkeycore.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 通过此方法来调用connector库文件的api函数 3 | * @param protobufStr 4 | * @returns {*} 5 | */ 6 | 7 | const ref = require('ref-napi') 8 | const ffi = require('ffi-napi') 9 | const path = require('path') 10 | 11 | let libraryName 12 | 13 | if (process.platform === 'win32') { 14 | if (process.env.NODE_ENV === 'production') { 15 | libraryName = path.resolve(__dirname, 'connector').replace('\\resources\\app.asar\\dist\\electron', '') 16 | } else { 17 | libraryName = path.resolve('connector') 18 | } 19 | } else if (process.platform === 'darwin') { 20 | if (process.env.NODE_ENV === 'production') { 21 | libraryName = path.resolve(__dirname, 'connector').replace('/app.asar/dist/electron', '') 22 | } else { 23 | libraryName = path.resolve('connector') 24 | } 25 | } else { 26 | console.log('不支持此平台') 27 | } 28 | let lib 29 | let winlib 30 | if (process.platform === 'win32') { 31 | // eslint-disable-next-line no-unused-vars 32 | winlib = ffi.Library(libraryName, { 33 | // [a, [b,c....]] a是函数出参类型,[b,c]是dll函数的入参类型 34 | imkey_clear_err_win: [ref.types.void, ['String']], 35 | call_imkey_api: ['String', ['String']], 36 | imkey_get_last_err_message_win: ['String', ['String']] 37 | }) 38 | } else { 39 | lib = ffi.Library(libraryName, { 40 | // [a, [b,c....]] a是函数出参类型,[b,c]是dll函数的入参类型 41 | imkey_clear_err: [ref.types.void, [ref.types.void]], 42 | call_imkey_api: ['String', ['String']], 43 | imkey_get_last_err_message: ['String', [ref.types.void]] 44 | }) 45 | } 46 | export function callImKeyApi (protobufStr) { 47 | if (process.platform === 'win32') { 48 | winlib.imkey_clear_err_win('')// 清空之前的error 49 | return winlib.call_imkey_api(protobufStr) 50 | } else { 51 | lib.imkey_clear_err('')// 清空之前的error 52 | return lib.call_imkey_api(protobufStr) 53 | } 54 | } 55 | 56 | export function getLastErrorMessage () { 57 | if (process.platform === 'win32') { 58 | return winlib.imkey_get_last_err_message_win('')// 获取错误的信息 59 | } else { 60 | return lib.imkey_get_last_err_message('')// 获取错误的信息 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/api/ethereumdapp_imkey_web3.js: -------------------------------------------------------------------------------- 1 | 2 | const { contextBridge, ipcRenderer } = require('electron') 3 | 4 | // https://chainid.network/ 5 | // const MAINNET_RPC_URL = "https://mainnet.infura.io/v3/819049aeadbe494c80bdb815cf41242e" 6 | const MAINNET_RPC_URL = 'https://mainnet-eth.token.im' 7 | const MAINNET_CHAIN_ID = 1 8 | // const KOVAN_RPC_URL = 'https://kovan.infura.io/v3/e35ac016a10548f1b4a835a1cd72d17a' 9 | // const KOVAN_CHAIN_ID = 42 10 | 11 | contextBridge.exposeInMainWorld('imKeyManager', { 12 | accounts: () => { 13 | const res = ipcRenderer.sendSync('message-from-get-address') 14 | const allAccounts = res.result 15 | const account = allAccounts.find((x) => x.chain === 'Ethereum') 16 | return { 17 | accounts: [account.address], 18 | chainId: account.chainId, 19 | rpcUrl: account.rpcUrl, 20 | headers: { 21 | agent: 'ios:2.4.2:2' 22 | }, 23 | symbol: account.symbol 24 | } 25 | }, 26 | callNativeApi: async (data) => { 27 | // const ret = ipcRenderer.sendSync('showMessageBoxSync', JSON.stringify(data)) 28 | const ret = ipcRenderer.sendSync('showMessageBoxSync', '') 29 | if (ret === 0) { 30 | return await ipcRenderer.sendSync('message-from-get-api', data) 31 | } else { 32 | 33 | } 34 | } 35 | }) 36 | 37 | const scriptContent = ipcRenderer.sendSync('read-file') 38 | 39 | process.on('document-start', () => { 40 | const script = document.createElement('script') 41 | script.type = 'text/javascript' 42 | script.text = scriptContent 43 | document.documentElement.appendChild(script) 44 | }) 45 | 46 | // process.on('document-start', () => { 47 | // var script = document.createElement("script"); 48 | // script.type = "text/javascript"; 49 | // script.src = "http://localhost:8000/main.imkey-web3-provider.js"; 50 | // document.documentElement.appendChild(script); 51 | // var script = document.createElement("script"); 52 | // script.type = "text/javascript"; 53 | // script.src = "http://localhost:8000/imkey-web3-provider.js"; 54 | // document.documentElement.appendChild(script); 55 | // }) 56 | -------------------------------------------------------------------------------- /.electron-vue/webpack.main.config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | process.env.BABEL_ENV = 'main' 4 | 5 | const path = require('path') 6 | const {dependencies} = require('../package.json') 7 | const webpack = require('webpack') 8 | 9 | // const BabiliWebpackPlugin = require('babili-webpack-plugin') 10 | const BabiliWebpackPlugin = require('babel-minify-webpack-plugin') 11 | 12 | let mainConfig = { 13 | entry: { 14 | main: path.join(__dirname, '../src/main/index.js') 15 | }, 16 | externals: [ 17 | ...Object.keys(dependencies || {}) 18 | ], 19 | // 为了方便调试chrome://inspect,本地调试用,此代码不能提交 20 | // devtool:`inline-source-map`, 21 | module: { 22 | rules: [ 23 | { 24 | test: /\.js$/, 25 | use: 'babel-loader', 26 | exclude: /node_modules/ 27 | }, 28 | { 29 | test: /\.node$/, 30 | use: 'node-loader' 31 | } 32 | ] 33 | }, 34 | node: { 35 | __dirname: process.env.NODE_ENV !== 'production', 36 | __filename: process.env.NODE_ENV !== 'production' 37 | }, 38 | output: { 39 | filename: '[name].js', 40 | libraryTarget: 'commonjs2', 41 | path: path.join(__dirname, '../dist/electron'), 42 | publicPath: './' 43 | }, 44 | plugins: [ 45 | new webpack.NoEmitOnErrorsPlugin() 46 | ], 47 | resolve: { 48 | extensions: ['.js', '.json', '.node'] 49 | }, 50 | target: 'electron-main' 51 | } 52 | 53 | /** 54 | * Adjust mainConfig for development settings 55 | */ 56 | if (process.env.NODE_ENV !== 'production') { 57 | mainConfig.plugins.push( 58 | new webpack.DefinePlugin({ 59 | '__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"` 60 | }) 61 | ) 62 | } 63 | 64 | /** 65 | * Adjust mainConfig for production settings 66 | */ 67 | if (process.env.NODE_ENV === 'production') { 68 | mainConfig.plugins.push( 69 | new BabiliWebpackPlugin(), 70 | new webpack.DefinePlugin({ 71 | 'process.env.NODE_ENV': '"production"' 72 | }) 73 | ) 74 | } 75 | 76 | module.exports = mainConfig 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Report Bug 4 | 5 | While bugs are unfortunate, they're a reality in software. We can't fix what we don't know about, so please report liberally. If you're not sure if something is a bug or not, feel free to file a bug anyway. 6 | 7 | ``` 8 | 9 | 10 | I tried this code: 11 | 12 | 13 | 14 | I expected to see this happen: 15 | 16 | Instead, this happened: 17 | 18 | ## Meta 19 | 20 | `rustc --version --verbose`: 21 | 22 | Backtrace: 23 | ``` 24 | 25 | ## Submit Pull Request 26 | 27 | Pull requests are major way to contribute an opensource project. Github has a detailed explaination about [Pull Request](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests). 28 | 29 | ## Code Style 30 | 31 | We expect run `cargo fmt` before code commit. Follow rust community [code style guide](https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/guide.md). 32 | 33 | CI server already setup to check style on CI build. We recommand you turn on code auto format for you IDE or editor plugin, follow [this](https://github.com/rust-lang/rustfmt#running-rustfmt-from-your-editor) rustfmt document. 34 | 35 | ## CI 36 | 37 | Code submitted must pass all unit tests and static analysis ("lint") checks. We use Travis CI to test code on Linux, macOS. 38 | 39 | For failing CI builds, the issue may not be related to the PR itself. Such failures are usually related to flaky tests. These failures can be ignored (authors don't need to fix unrelated issues), but please file a GH issue so the test gets fixed eventually. 40 | 41 | ## Commit Message 42 | 43 | We follow a rough convention for commit message writing. 44 | 45 | First line is the subject line, in around 50 charactors or less to describe what changed. And the body of the commit should describe why changed. 46 | 47 | Template: 48 | 49 | ``` 50 | Changes in around 50 charactors or less 51 | 52 | Longer explanation of the change in the commit. You can use 53 | multiple sentences here. It's usually best to include content 54 | from the PR description in the final commit message. 55 | 56 | issue notices, e.g. "Fixes #42, Resolve #123, See also #456". 57 | ``` 58 | 59 | -------------------------------------------------------------------------------- /src/proto/device.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package deviceapi; 3 | 4 | message AppDownloadReq { 5 | string app_name = 1; 6 | } 7 | 8 | message AppDownloadRes { 9 | repeated string address_register_list = 1; 10 | } 11 | 12 | message AppUpdateReq { 13 | string app_name = 1; 14 | } 15 | 16 | message AppUpdateRes { 17 | repeated string address_register_list = 1; 18 | } 19 | 20 | message AppDeleteReq { 21 | string app_name = 1; 22 | } 23 | 24 | // check_update api 25 | message CheckUpdateRes { 26 | string se_id = 1; 27 | string sn = 2; 28 | string status = 3; 29 | string sdk_mode = 4; 30 | repeated AvailableAppBean available_app_list = 5; 31 | } 32 | 33 | message AvailableAppBean { 34 | string app_name = 1; 35 | string app_logo = 2; 36 | string installed_version = 3; 37 | string last_updated = 4; 38 | string latest_version = 5; 39 | string install_mode = 6; 40 | } 41 | 42 | message BindCheckRes { 43 | string bind_status = 1; 44 | } 45 | 46 | message BindAcquireReq { 47 | string bind_code = 1; 48 | } 49 | 50 | message BindAcquireRes { 51 | string bind_result = 1; 52 | } 53 | 54 | message GetSeidRes { 55 | string seid = 1; 56 | } 57 | 58 | message GetSnRes { 59 | string sn = 1; 60 | } 61 | 62 | message GetRamSizeRes { 63 | string ram_size = 1; 64 | } 65 | 66 | message GetFirmwareVersionRes { 67 | string firmware_version = 1; 68 | } 69 | 70 | message GetBatteryPowerRes { 71 | string battery_power = 1; 72 | } 73 | 74 | message GetLifeTimeRes { 75 | string life_time = 1; 76 | } 77 | 78 | message GetBleNameRes { 79 | string ble_name = 1; 80 | } 81 | 82 | message SetBleNameReq { 83 | string ble_name = 1; 84 | } 85 | 86 | message GetBleVersionRes { 87 | string ble_version = 1; 88 | } 89 | 90 | message GetSdkInfoRes { 91 | string sdk_version = 1; 92 | } 93 | 94 | message DeviceConnectReq { 95 | string device_model_name = 1; 96 | } 97 | 98 | message CosCheckUpdateRes { 99 | string seid = 1; 100 | bool is_latest = 2; 101 | string latest_cos_version = 3; 102 | string latest_ble_version = 4; 103 | string update_type = 5; 104 | string description = 6; 105 | bool is_update_success = 7; 106 | } 107 | 108 | message IsBlStatusRes { 109 | bool check_result = 1; 110 | } -------------------------------------------------------------------------------- /src/proto/api.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package api; 3 | 4 | import "google/protobuf/any.proto"; 5 | 6 | // Action Wrapper 7 | // There is a `call_imkey_api` method in tcx which act as a endpoint like RPC. It accepts a `ImkeyAction` param which method field is 8 | // the real action and param field is the real param of that method. 9 | // When an error occurred, the `call_imkey_api` will return a `Response` which isSuccess field be false and error field is the reason 10 | // which cause the error. 11 | message ImkeyAction { 12 | string method = 1; 13 | google.protobuf.Any param = 2; 14 | } 15 | 16 | // A common response when error occurred. 17 | message ErrorResponse { 18 | bool isSuccess = 1; 19 | string error = 2; 20 | } 21 | 22 | //A commonresponse when successfully ended. 23 | message CommonResponse{ 24 | string result = 1; 25 | } 26 | 27 | message AddressParam { 28 | string chainType = 1; 29 | string path = 2; 30 | string network = 3; 31 | bool isSegWit = 4; 32 | } 33 | 34 | message AddressResult { 35 | string path = 1; 36 | string chainType = 2; 37 | string address = 3; 38 | } 39 | 40 | message PubKeyParam { 41 | string chainType = 1; 42 | string path = 2; 43 | string network = 3; 44 | string isSegWit = 4; 45 | } 46 | 47 | message PubKeyResult { 48 | string path = 1; 49 | string chainType = 2; 50 | string pubKey = 3; 51 | string derivedMode = 4; 52 | } 53 | 54 | message ExternalAddress { 55 | string address = 1; 56 | string derivedPath = 2; 57 | string type = 3; 58 | } 59 | 60 | 61 | message BitcoinWallet { 62 | string path = 1; 63 | string chainType = 2; 64 | string address = 3; 65 | string encXPub = 4; 66 | ExternalAddress externalAddress = 5; 67 | } 68 | 69 | 70 | message EosWallet { 71 | string chainType = 1; 72 | string address = 2; 73 | message PubKeyInfo { 74 | string path = 1; 75 | string derivedMode = 2; 76 | string publicKey = 3; 77 | } 78 | repeated PubKeyInfo publicKeys = 3; 79 | } 80 | 81 | 82 | message ExternalAddressParam { 83 | string path = 1; 84 | string chainType = 2; 85 | string network = 3; 86 | string segWit = 4; 87 | int32 externalIdx = 5; 88 | } 89 | 90 | message InitImKeyCoreXParam { 91 | string fileDir = 1; 92 | string xpubCommonKey = 2; 93 | string xpubCommonIv = 3; 94 | bool isDebug = 4; 95 | string system = 5; 96 | string terminalType = 6; 97 | string sdkVersion = 7; 98 | string serverUrl = 8; 99 | } 100 | 101 | message BtcForkWallet { 102 | string path = 1; 103 | string chainType = 2; 104 | string address = 3; 105 | string encXPub = 4; 106 | } -------------------------------------------------------------------------------- /src/renderer/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import router from './router' 4 | import store from './store' 5 | import ElementUI from 'element-ui' 6 | import 'element-ui/lib/theme-chalk/index.css' 7 | import VueI18n from 'vue-i18n' 8 | import '@fortawesome/fontawesome-free/css/all.css' // Ensure you are using css-loader 9 | const { ipcRenderer } = require('electron') // Renderer process modules 10 | // const { screen } = remote // Main process modules 11 | // const devInnerHeight = 1080.0 // 开发时的InnerHeight 12 | // const devDevicePixelRatio = 1.0// 开发时的devicepixelratio 13 | // const devScaleFactor = 1.3 // 开发时的ScaleFactor 14 | // const scaleFactor = screen.getPrimaryDisplay().scaleFactor 15 | // const zoomFactor = (window.innerHeight / devInnerHeight) * (window.devicePixelRatio / devDevicePixelRatio) * (devScaleFactor / scaleFactor) 16 | // ipcRenderer.send('zoomIn', zoomFactor) 17 | let callbackCache 18 | Vue.prototype.$ipcRenderer = { 19 | send: (msgType, msgData) => { 20 | ipcRenderer.send('message-from-renderer', { 21 | type: msgType, 22 | data: msgData 23 | }) 24 | }, 25 | on: (type, callback) => { 26 | callbackCache = { 27 | type, 28 | callback 29 | } 30 | } 31 | } 32 | ipcRenderer.on('message-to-renderer', (sender, msg) => { 33 | if (callbackCache.type === msg.type) { 34 | callbackCache.callback(msg.data) 35 | } 36 | }) // 监听主进程的消息 37 | Vue.config.productionTip = false 38 | Vue.prototype.$store = store 39 | Vue.prototype.router = router 40 | Vue.use(ElementUI) 41 | Vue.use(VueI18n) 42 | // 弹出框禁止滑动 43 | Vue.prototype.noScroll = function () { 44 | const mo = function (e) { 45 | e.preventDefault() 46 | } 47 | document.body.style.overflow = 'hidden' 48 | // 禁止页面滑动 49 | document.addEventListener('touchmove', mo, false) 50 | } 51 | 52 | // 弹出框可以滑动 53 | Vue.prototype.canScroll = function () { 54 | const mo = function (e) { 55 | e.preventDefault() 56 | } 57 | // 出现滚动条 58 | document.body.style.overflow = '' 59 | document.removeEventListener('touchmove', mo, false) 60 | } 61 | // 获取系统语言,根据系统语言来切换语言 62 | const app = require('electron').remote.app 63 | let sysLocale = app.getLocale() 64 | if (sysLocale !== 'zh-CN') { 65 | sysLocale = 'en-US' 66 | } 67 | 68 | // VueI18n 69 | const i18n = new VueI18n({ 70 | // 默认中文 71 | // locale: 'zh-CN', 72 | // locale: 'en-US', 73 | // this.$i18n.locale // 通过切换locale的值来实现语言切换 74 | // 获取系统语言,根据系统语言来切换语言 75 | locale: sysLocale, 76 | messages: { 77 | // 语言包路径 78 | 'zh-CN': require('./common/lang/zh'), 79 | 'en-US': require('./common/lang/en') 80 | } 81 | }) 82 | 83 | new Vue({ 84 | i18n, 85 | router, 86 | store, 87 | render: h => h(App) 88 | }).$mount('#app') 89 | // vue 项目在路由切换的时候调用 90 | router.beforeEach(function (to, from, next) { 91 | next() 92 | let eventName = '' 93 | let toName = '' 94 | if (from.name === 'welcomeHome') { 95 | if (to.name === 'manager') { 96 | eventName = 'im_homepage$manage' 97 | toName = 'im_manage' 98 | } 99 | if (to.name === 'setting') { 100 | eventName = 'im_homepage$setting' 101 | toName = 'im_setting' 102 | } 103 | } 104 | if (from.name === 'manager') { 105 | if (to.name === 'welcomeHome') { 106 | eventName = 'im_manage$homepage' 107 | toName = 'im_homepage' 108 | } 109 | if (to.name === 'setting') { 110 | eventName = 'im_manage$setting' 111 | toName = 'im_setting' 112 | } 113 | } 114 | if (from.name === 'setting') { 115 | if (to.name === 'welcomeHome') { 116 | eventName = 'im_setting$homepage' 117 | toName = 'im_homepage' 118 | } 119 | if (to.name === 'manager') { 120 | eventName = 'im_setting$manage' 121 | toName = 'im_manage' 122 | } 123 | } 124 | }) 125 | -------------------------------------------------------------------------------- /.electron-vue/build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | process.env.NODE_ENV = 'production' 4 | 5 | const {say} = require('cfonts') 6 | const chalk = require('chalk') 7 | const del = require('del') 8 | const {spawn} = require('child_process') 9 | const webpack = require('webpack') 10 | const Multispinner = require('multispinner') 11 | 12 | 13 | const mainConfig = require('./webpack.main.config') 14 | const rendererConfig = require('./webpack.renderer.config') 15 | const webConfig = require('./webpack.web.config') 16 | 17 | const doneLog = chalk.bgGreen.white(' DONE ') + ' ' 18 | const errorLog = chalk.bgRed.white(' ERROR ') + ' ' 19 | const okayLog = chalk.bgBlue.white(' OKAY ') + ' ' 20 | const isCI = process.env.CI || false 21 | 22 | if (process.env.BUILD_TARGET === 'clean') clean() 23 | else if (process.env.BUILD_TARGET === 'web') web() 24 | else build() 25 | 26 | function clean() { 27 | del.sync(['build/*', '!build/icons', '!build/icons/icon.*']) 28 | console.log(`\n${doneLog}\n`) 29 | process.exit() 30 | } 31 | 32 | function build() { 33 | greeting() 34 | 35 | del.sync(['dist/electron/*', '!.gitkeep']) 36 | 37 | const tasks = ['main', 'renderer'] 38 | const m = new Multispinner(tasks, { 39 | preText: 'building', 40 | postText: 'process' 41 | }) 42 | 43 | let results = '' 44 | 45 | m.on('success', () => { 46 | process.stdout.write('\x1B[2J\x1B[0f') 47 | console.log(`\n\n${results}`) 48 | console.log(`${okayLog}take it away ${chalk.yellow('`electron-builder`')}\n`) 49 | process.exit() 50 | }) 51 | 52 | pack(mainConfig).then(result => { 53 | results += result + '\n\n' 54 | m.success('main') 55 | }).catch(err => { 56 | m.error('main') 57 | console.log(`\n ${errorLog}failed to build main process`) 58 | console.error(`\n${err}\n`) 59 | process.exit(1) 60 | }) 61 | 62 | pack(rendererConfig).then(result => { 63 | results += result + '\n\n' 64 | m.success('renderer') 65 | }).catch(err => { 66 | m.error('renderer') 67 | console.log(`\n ${errorLog}failed to build renderer process`) 68 | console.error(`\n${err}\n`) 69 | process.exit(1) 70 | }) 71 | } 72 | 73 | function pack(config) { 74 | return new Promise((resolve, reject) => { 75 | config.mode = 'production' 76 | webpack(config, (err, stats) => { 77 | if (err) reject(err.stack || err) 78 | else if (stats.hasErrors()) { 79 | let err = '' 80 | 81 | stats.toString({ 82 | chunks: false, 83 | colors: true 84 | }) 85 | .split(/\r?\n/) 86 | .forEach(line => { 87 | err += ` ${line}\n` 88 | }) 89 | 90 | reject(err) 91 | } else { 92 | resolve(stats.toString({ 93 | chunks: false, 94 | colors: true 95 | })) 96 | } 97 | }) 98 | }) 99 | } 100 | 101 | function web() { 102 | del.sync(['dist/web/*', '!.gitkeep']) 103 | webConfig.mode = 'production' 104 | webpack(webConfig, (err, stats) => { 105 | if (err || stats.hasErrors()) console.log(err) 106 | 107 | console.log(stats.toString({ 108 | chunks: false, 109 | colors: true 110 | })) 111 | 112 | process.exit() 113 | }) 114 | } 115 | 116 | function greeting() { 117 | const cols = process.stdout.columns 118 | let text = '' 119 | 120 | if (cols > 85) text = 'lets-build' 121 | else if (cols > 60) text = 'lets-|build' 122 | else text = false 123 | 124 | if (text && !isCI) { 125 | say(text, { 126 | colors: ['yellow'], 127 | font: 'simple3d', 128 | space: false 129 | }) 130 | } else console.log(chalk.yellow.bold('\n lets-build')) 131 | console.log() 132 | } 133 | -------------------------------------------------------------------------------- /src/renderer/views/appStart.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 54 | 104 | -------------------------------------------------------------------------------- /.electron-vue/dev-runner.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const chalk = require('chalk') 4 | const electron = require('electron') 5 | const path = require('path') 6 | const fs = require('fs') 7 | const {say} = require('cfonts') 8 | const {spawn} = require('child_process') 9 | const webpack = require('webpack') 10 | const WebpackDevServer = require('webpack-dev-server') 11 | const webpackHotMiddleware = require('webpack-hot-middleware') 12 | 13 | const mainConfig = require('./webpack.main.config') 14 | const rendererConfig = require('./webpack.renderer.config') 15 | 16 | let electronProcess = null 17 | let manualRestart = false 18 | let hotMiddleware 19 | 20 | function logStats(proc, data) { 21 | let log = '' 22 | 23 | log += chalk.yellow.bold(`┏ ${proc} Process ${new Array((19 - proc.length) + 1).join('-')}`) 24 | log += '\n\n' 25 | 26 | if (typeof data === 'object') { 27 | data.toString({ 28 | colors: true, 29 | chunks: false 30 | }).split(/\r?\n/).forEach(line => { 31 | log += ' ' + line + '\n' 32 | }) 33 | } else { 34 | log += ` ${data}\n` 35 | } 36 | 37 | log += '\n' + chalk.yellow.bold(`┗ ${new Array(28 + 1).join('-')}`) + '\n' 38 | 39 | console.log(log) 40 | } 41 | 42 | function startRenderer() { 43 | return new Promise((resolve, reject) => { 44 | rendererConfig.entry.renderer = [path.join(__dirname, 'dev-client')].concat(rendererConfig.entry.renderer) 45 | rendererConfig.mode = 'development' 46 | const compiler = webpack(rendererConfig) 47 | hotMiddleware = webpackHotMiddleware(compiler, { 48 | log: false, 49 | heartbeat: 2500 50 | }) 51 | 52 | // compiler.hooks.compilation.tap('compilation', compilation => { 53 | // compilation.hooks.htmlWebpackPluginAfterEmit.tapAsync('html-webpack-plugin-after-emit', (data, cb) => { 54 | // hotMiddleware.publish({action: 'reload'}) 55 | // cb() 56 | // }) 57 | // }) 58 | compiler.hooks.compilation.tap('html-webpack-plugin-after-emit', () => { 59 | hotMiddleware.publish({ 60 | action: 'reload' 61 | }); 62 | }); 63 | compiler.hooks.done.tap('done', stats => { 64 | logStats('Renderer', stats) 65 | }) 66 | 67 | const server = new WebpackDevServer( 68 | compiler, 69 | { 70 | contentBase: path.join(__dirname, '../'), 71 | quiet: true, 72 | before(app, ctx) { 73 | app.use(hotMiddleware) 74 | ctx.middleware.waitUntilValid(() => { 75 | resolve() 76 | }) 77 | } 78 | } 79 | ) 80 | 81 | server.listen(9080) 82 | }) 83 | } 84 | 85 | function startMain() { 86 | return new Promise((resolve, reject) => { 87 | mainConfig.entry.main = [path.join(__dirname, '../src/main/index.dev.js')].concat(mainConfig.entry.main) 88 | mainConfig.mode = 'development' 89 | const compiler = webpack(mainConfig) 90 | 91 | compiler.hooks.watchRun.tapAsync('watch-run', (compilation, done) => { 92 | logStats('Main', chalk.white.bold('compiling...')) 93 | hotMiddleware.publish({action: 'compiling'}) 94 | done() 95 | }) 96 | 97 | compiler.watch({}, (err, stats) => { 98 | if (err) { 99 | console.log(err) 100 | return 101 | } 102 | 103 | logStats('Main', stats) 104 | 105 | if (electronProcess && electronProcess.kill) { 106 | manualRestart = true 107 | process.kill(electronProcess.pid) 108 | electronProcess = null 109 | startElectron() 110 | 111 | setTimeout(() => { 112 | manualRestart = false 113 | }, 5000) 114 | } 115 | 116 | resolve() 117 | }) 118 | }) 119 | } 120 | 121 | function startElectron() { 122 | var args = [ 123 | // 为了方便调试chrome://inspect,本地调试用,此代码不能提交 124 | // '--inspect-brk=5858', 125 | path.join(__dirname, '../dist/electron/main.js') 126 | ] 127 | 128 | // detect yarn or npm and process commandline args accordingly 129 | if (process.env.npm_execpath.endsWith('yarn.js')) { 130 | args = args.concat(process.argv.slice(3)) 131 | } else if (process.env.npm_execpath.endsWith('npm-cli.js')) { 132 | args = args.concat(process.argv.slice(2)) 133 | } 134 | 135 | electronProcess = spawn(electron, args) 136 | 137 | electronProcess.stdout.on('data', data => { 138 | electronLog(data, 'blue') 139 | }) 140 | electronProcess.stderr.on('data', data => { 141 | electronLog(data, 'red') 142 | }) 143 | 144 | electronProcess.on('close', () => { 145 | if (!manualRestart) process.exit() 146 | }) 147 | } 148 | 149 | function electronLog(data, color) { 150 | let log = '' 151 | data = data.toString().split(/\r?\n/) 152 | data.forEach(line => { 153 | log += ` ${line}\n` 154 | }) 155 | if (/[0-9A-z]+/.test(log)) { 156 | console.log( 157 | chalk[color].bold('┏ Electron -------------------') + 158 | '\n\n' + 159 | log + 160 | chalk[color].bold('┗ ----------------------------') + 161 | '\n' 162 | ) 163 | } 164 | } 165 | 166 | 167 | function greeting() { 168 | const cols = process.stdout.columns 169 | let text = '' 170 | 171 | if (cols > 104) text = 'electron-vue' 172 | else if (cols > 76) text = 'electron-|vue' 173 | else text = false 174 | 175 | if (text) { 176 | say(text, { 177 | colors: ['yellow'], 178 | font: 'simple3d', 179 | space: false 180 | }) 181 | } else console.log(chalk.yellow.bold('\n electron-vue')) 182 | console.log(chalk.blue(' getting ready...') + '\n') 183 | } 184 | 185 | function init() { 186 | greeting() 187 | // example 188 | fs.createReadStream(path.join(__dirname,'../connector.dylib')).pipe(fs.createWriteStream(path.join(__dirname, '../dist/electron/connector.dylib'))); 189 | fs.createReadStream(path.join(__dirname,'../connector.dll')).pipe(fs.createWriteStream(path.join(__dirname, '../dist/electron/connector.dll'))); 190 | Promise.all([startRenderer(), startMain()]) 191 | .then(() => { 192 | startElectron() 193 | }) 194 | .catch(err => { 195 | console.error(err) 196 | }) 197 | } 198 | 199 | init() 200 | -------------------------------------------------------------------------------- /src/common/constants.js: -------------------------------------------------------------------------------- 1 | // sdk version 2 | const sdkVersion = '2.10.3' 3 | // serverurl 4 | // const serverUrl = 'https://imkeyserver.com:10444/imkey' 5 | const serverUrl = 'https://imkey.online:1000/imkey' 6 | // Terminaltype 7 | const terminalType = 'PC' 8 | // Battery is charging sign 9 | const BATTERY_CHARGING_SIGN = 'FF' 10 | 11 | // 设备初始化 80 12 | const LIFE_TIME_DEVICE_INITED = 'life_time_device_inited' 13 | // 已激活 89 14 | const LIFE_TIME_DEVICE_ACTIVATED = 'life_time_device_activated' 15 | // pin未设置 81 16 | const LIFE_TIME_UNSET_PIN = 'life_time_unset_pin' 17 | // 钱包unready 83 18 | const LIFE_TIME_WALLET_UNREADY = 'life_time_wallet_unready' 19 | // 钱包创建中 84 20 | const LIFE_TIME_WALLET_CREATTING = 'life_time_wallet_creatting' 21 | // 钱包恢复中 85 22 | const LIFE_TIME_WALLET_RECOVERING = 'life_time_wallet_recovering' 23 | // 钱包创建完成 86 24 | const LIFE_TIME_WALLET_READY = 'life_time_wallet_ready' 25 | // 未知 26 | const LIFE_TIME_UNKNOWN = 'life_time_unknown' 27 | 28 | // 设备绑定状态 29 | const BIND_STATUS_UNBOUND = '00' 30 | const BIND_STATUS_BOUND_THIS = '55' 31 | const BIND_STATUS_BOUND_OTHER = 'AA' 32 | const BIND_STATUS_STRING_UNBOUND = 'unbound' 33 | const BIND_STATUS_STRING_BOUND_THIS = 'bound_this' 34 | const BIND_STATUS_STRING_BOUND_OTHER = 'bound_other' 35 | // network 36 | const MAINNET = 'MAINNET' 37 | const TESTNET = 'TESTNET' 38 | // imKey device name 39 | const DEVICE_NAME_IMKEY_PRO = 'imKey Pro' 40 | // 成功状态 41 | const RESULT_STATUS_SUCCESS = 'success' 42 | // apiName 43 | const API_NAME_TRANSACTION_BTC = 'btc.signTransaction' 44 | const API_NAME_TRANSACTION_BTC_SEGWIT = 'btc.signSegWitTransaction' 45 | const API_NAME_TRANSACTION_BTC_USDT = 'btc.signUsdtTransaction' 46 | const API_NAME_TRANSACTION_BTC_USDT_SEGWIT = 'btc.signUsdtSegWitTransaction' 47 | const API_NAME_TRANSACTION_BCH = 'bch.signTransaction' 48 | const API_NAME_TRANSACTION_LTC = 'ltc.signTransaction' 49 | const API_NAME_TRANSACTION_SIGNTX_ETH = 'eth.signTransaction' 50 | const API_NAME_TRANSACTION_SIGNMSG_ETH = 'eth.signMessage' 51 | const API_NAME_TRANSACTION_SIGNTX_EOS = 'eos.signTransaction' 52 | const API_NAME_TRANSACTION_SIGNMSG_EOS = 'eos.signMessage' 53 | const API_NAME_TRANSACTION_SIGNTX_COSMOS = 'cosmos.signTransaction' 54 | const API_NAME_TRANSACTION_SIGNTX_FILECOIN = 'filecoin.signTransaction' 55 | const API_NAME_TRANSACTION_SIGNTX_POLKADOT = 'dot.signTransaction' 56 | const API_NAME_TRANSACTION_SIGNTX_KUSAMA = 'ksm.signTransaction' 57 | const API_NAME_TRANSACTION_SIGNTX_TRON = 'tron.signTransaction' 58 | const API_NAME_TRANSACTION_SIGNMSG_TRON = 'tron.signMessage' 59 | const API_NAME_TRANSACTION_SIGNTX_XTZ = 'xtz.signTransaction' 60 | const API_NAME_GET_BTC_XPUB = 'btc.getXpub' 61 | const API_NAME_GET_ADDRESS_BTC = 'btc.getAddress' 62 | const API_NAME_GET_ADDRESS_BTC_SEGWIT = 'btc.getSegWitAddress' 63 | const API_NAME_REGISTER_ADDRESS_BTC = 'btc.registerAddress' 64 | const API_NAME_REGISTER_ADDRESS_BTC_SEGWIT = 'btc.registerSegWitAddress' 65 | const API_NAME_GET_ADDRESS_BCH = 'bch.getAddress' 66 | const API_NAME_REGISTER_ADDRESS_BCH = 'bch.registerAddress' 67 | const API_NAME_GET_ADDRESS_LTC = 'ltc.getAddress' 68 | const API_NAME_REGISTER_ADDRESS_LTC = 'ltc.registerAddress' 69 | const API_NAME_GET_ADDRESS_LTC_SEGWIT = 'ltc.getSegWitAddress' 70 | const API_NAME_REGISTER_ADDRESS_LTC_SEGWIT = 'ltc.registerSegWitAddress' 71 | const API_NAME_GET_ADDRESS_ETH = 'eth.getAddress' 72 | const API_NAME_REGISTER_ADDRESS_ETH = 'eth.registerAddress' 73 | const API_NAME_GET_PUBKEY_EOS = 'eos.getPubKey' 74 | const API_NAME_REGISTER_PUBKEY_EOS = 'eos.registerPubKey' 75 | const API_NAME_GET_ADDRESS_COSMOS = 'cosmos.getAddress' 76 | const API_NAME_REGISTER_ADDRESS_COSMOS = 'cosmos.registerAddress' 77 | const API_NAME_GET_ADDRESS_FILECOIN = 'filecoin.getAddress' 78 | const API_NAME_REGISTER_ADDRESS_FILECOIN = 'filecoin.registerAddress' 79 | const API_NAME_GET_ADDRESS_POLKADOT = 'dot.getAddress' 80 | const API_NAME_REGISTER_ADDRESS_POLKADOT = 'dot.registerAddress' 81 | const API_NAME_GET_ADDRESS_KUSAMA = 'ksm.getAddress' 82 | const API_NAME_REGISTER_ADDRESS_KUSAMA = 'ksm.registerAddress' 83 | const API_NAME_GET_ADDRESS_TRON = 'tron.getAddress' 84 | const API_NAME_REGISTER_ADDRESS_TRON = 'tron.registerAddress' 85 | const API_NAME_GET_ADDRESS_XTZ = 'tezos.getAddress' 86 | const API_NAME_REGISTER_ADDRESS_XTZ = 'tezos.registerAddress' 87 | const XPubCommonKey128 = 'B888D25EC8C12BD5043777B1AC49F872' 88 | const XPubCommonIv = '9C0C30889CBCC5E01AB5B2BB88715799' 89 | 90 | module.exports = { 91 | sdkVersion, 92 | serverUrl, 93 | terminalType, 94 | BATTERY_CHARGING_SIGN, 95 | LIFE_TIME_DEVICE_INITED, 96 | LIFE_TIME_DEVICE_ACTIVATED, 97 | LIFE_TIME_UNSET_PIN, 98 | LIFE_TIME_WALLET_UNREADY, 99 | LIFE_TIME_WALLET_CREATTING, 100 | LIFE_TIME_WALLET_RECOVERING, 101 | LIFE_TIME_WALLET_READY, 102 | LIFE_TIME_UNKNOWN, 103 | BIND_STATUS_UNBOUND, 104 | BIND_STATUS_BOUND_THIS, 105 | BIND_STATUS_BOUND_OTHER, 106 | BIND_STATUS_STRING_UNBOUND, 107 | BIND_STATUS_STRING_BOUND_THIS, 108 | BIND_STATUS_STRING_BOUND_OTHER, 109 | MAINNET, 110 | TESTNET, 111 | DEVICE_NAME_IMKEY_PRO, 112 | RESULT_STATUS_SUCCESS, 113 | API_NAME_TRANSACTION_BTC, 114 | API_NAME_TRANSACTION_BTC_SEGWIT, 115 | API_NAME_TRANSACTION_BTC_USDT, 116 | API_NAME_TRANSACTION_BTC_USDT_SEGWIT, 117 | API_NAME_TRANSACTION_BCH, 118 | API_NAME_TRANSACTION_LTC, 119 | API_NAME_TRANSACTION_SIGNTX_ETH, 120 | API_NAME_TRANSACTION_SIGNMSG_ETH, 121 | API_NAME_TRANSACTION_SIGNTX_EOS, 122 | API_NAME_TRANSACTION_SIGNMSG_EOS, 123 | API_NAME_TRANSACTION_SIGNTX_COSMOS, 124 | API_NAME_TRANSACTION_SIGNTX_FILECOIN, 125 | API_NAME_TRANSACTION_SIGNTX_POLKADOT, 126 | API_NAME_TRANSACTION_SIGNTX_KUSAMA, 127 | API_NAME_TRANSACTION_SIGNTX_TRON, 128 | API_NAME_TRANSACTION_SIGNMSG_TRON, 129 | API_NAME_TRANSACTION_SIGNTX_XTZ, 130 | API_NAME_GET_BTC_XPUB, 131 | API_NAME_GET_ADDRESS_BTC, 132 | API_NAME_GET_ADDRESS_BTC_SEGWIT, 133 | API_NAME_REGISTER_ADDRESS_BTC, 134 | API_NAME_REGISTER_ADDRESS_BTC_SEGWIT, 135 | API_NAME_GET_ADDRESS_BCH, 136 | API_NAME_REGISTER_ADDRESS_BCH, 137 | API_NAME_GET_ADDRESS_LTC, 138 | API_NAME_REGISTER_ADDRESS_LTC, 139 | API_NAME_GET_ADDRESS_LTC_SEGWIT, 140 | API_NAME_REGISTER_ADDRESS_LTC_SEGWIT, 141 | API_NAME_GET_ADDRESS_ETH, 142 | API_NAME_REGISTER_ADDRESS_ETH, 143 | API_NAME_GET_PUBKEY_EOS, 144 | API_NAME_REGISTER_PUBKEY_EOS, 145 | API_NAME_GET_ADDRESS_COSMOS, 146 | API_NAME_REGISTER_ADDRESS_COSMOS, 147 | API_NAME_GET_ADDRESS_FILECOIN, 148 | API_NAME_REGISTER_ADDRESS_FILECOIN, 149 | API_NAME_GET_ADDRESS_POLKADOT, 150 | API_NAME_REGISTER_ADDRESS_POLKADOT, 151 | API_NAME_GET_ADDRESS_KUSAMA, 152 | API_NAME_REGISTER_ADDRESS_KUSAMA, 153 | API_NAME_GET_ADDRESS_TRON, 154 | API_NAME_REGISTER_ADDRESS_TRON, 155 | API_NAME_GET_ADDRESS_XTZ, 156 | API_NAME_REGISTER_ADDRESS_XTZ, 157 | XPubCommonKey128, 158 | XPubCommonIv 159 | } 160 | -------------------------------------------------------------------------------- /src/api/polkadotdapp.js: -------------------------------------------------------------------------------- 1 | const { 2 | web3Enable 3 | } = require('@polkadot/extension-dapp') 4 | 5 | const { Decimal } = require('decimal.js') 6 | const { u8aToHex, hexToU8a } = require('@polkadot/util') 7 | const { TypeRegistry } = require('@polkadot/types/create') 8 | const { GenericAccountId, UInt } = require('@polkadot/types') 9 | const { encodeAddress } = require('@polkadot/util-crypto') 10 | const { ipcRenderer } = require('electron') 11 | 12 | const PolkadotGenesisHash = '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3' 13 | const KusamaGenesisHash = '0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe' 14 | const PolkadotPath = "m/44'/354'/0'/0'/0'" 15 | const KusamaPath = "m/44'/434'/0'/0'/0'" 16 | 17 | const REGISTRY = (() => { 18 | // kusama runtime is 28 19 | const ADDRESS_TYPES_KUSAMA = { 20 | Address: 'MultiAddress', 21 | LookupSource: 'MultiAddress' 22 | } 23 | const ADDRESS_TYPES_POLKADOT = ADDRESS_TYPES_KUSAMA 24 | 25 | const registryKSM = new TypeRegistry() 26 | const registryDOT = new TypeRegistry() 27 | registryKSM.register(ADDRESS_TYPES_KUSAMA) 28 | registryDOT.register(ADDRESS_TYPES_POLKADOT) 29 | return { 30 | KUSAMA: registryKSM, 31 | POLKADOT: registryDOT 32 | } 33 | })() 34 | 35 | let id = 0 36 | 37 | class Signer { 38 | constructor () { 39 | this.registry = REGISTRY.KUSAMA 40 | } 41 | 42 | async signPayload (payload) { 43 | // const registry = REGISTRY[params.chainType] 44 | // let extrinsicPayload = this.registry.createType('ExtrinsicPayload', payload, {version: payload.version}); 45 | this.registry.setSignedExtensions(payload.signedExtensions) 46 | const extrinsicPayload = this.registry 47 | .createType('ExtrinsicPayload', payload, { version: payload.version }) 48 | const rawdata = u8aToHex(extrinsicPayload.toU8a(true)) 49 | console.log(rawdata) 50 | const encodedAddress = new GenericAccountId(this.registry, hexToU8a('0x' + payload.method.substring(6, 70))).toString() 51 | const toAddress = encodeAddress(encodedAddress, 0) 52 | let payment = '0' 53 | 54 | if (payload.method.length === 72) { 55 | console.log('none') 56 | } else { 57 | payment = new UInt(this.registry, hexToU8a('0x' + payload.method.substring(72, payload.method.length))).toString() 58 | payment = new Decimal(payment).dividedBy(10000000000) 59 | } 60 | let json = { 61 | jsonrpc: '2.0', 62 | method: 'ksm.signTransaction', 63 | params: { 64 | rawdata: rawdata.substring(2, rawdata.length), 65 | path: KusamaPath, 66 | preview: { 67 | payment: payment + ' KSM', 68 | receiver: toAddress, 69 | sender: payload.address 70 | // fee: '15.4000 milli DOT' 71 | } 72 | }, 73 | id: 24 74 | } 75 | if (payload.address === addressDOT) { 76 | json = { 77 | jsonrpc: '2.0', 78 | method: 'dot.signTransaction', 79 | params: { 80 | rawdata: rawdata.substring(2, rawdata.length), 81 | path: PolkadotPath, 82 | preview: { 83 | payment: payment + ' DOT', 84 | receiver: toAddress, 85 | sender: payload.address 86 | // fee: '15.4000 milli DOT' 87 | } 88 | }, 89 | id: 24 90 | } 91 | } 92 | const signature = sign(json) 93 | return { 94 | id: ++id, 95 | signature: signature 96 | } 97 | } 98 | 99 | async signRaw ({ address, data }) { 100 | console.log(address) 101 | console.log(data) 102 | 103 | let json = { 104 | jsonrpc: '2.0', 105 | method: 'ksm.signTransaction', 106 | params: { 107 | rawdata: data, 108 | path: KusamaPath 109 | }, 110 | id: 24 111 | } 112 | if (address === addressDOT) { 113 | json = { 114 | jsonrpc: '2.0', 115 | method: 'dot.signTransaction', 116 | params: { 117 | rawdata: data, 118 | path: PolkadotPath 119 | }, 120 | id: 24 121 | } 122 | } 123 | const signature = sign(json) 124 | return { 125 | id: ++id, 126 | signature 127 | } 128 | } 129 | } 130 | 131 | function sign (json) { 132 | // const ret = ipcRenderer.sendSync('showMessageBoxSync', JSON.stringify(json)) 133 | const ret = ipcRenderer.sendSync('showMessageBoxSync', '') 134 | if (ret === 0) { 135 | const result = ipcRenderer.sendSync('message-from-get-api', json) 136 | return result.result.signature 137 | } else { 138 | 139 | } 140 | } 141 | let addressKSM 142 | let addressDOT 143 | function getAddress () { 144 | const res = ipcRenderer.sendSync('message-from-get-address') 145 | const walletAddressArray = res.result 146 | for (let i = 0; i < walletAddressArray.length; i++) { 147 | if (walletAddressArray[i].chain === 'Polkadot') { 148 | addressDOT = walletAddressArray[i].address 149 | } 150 | if (walletAddressArray[i].chain === 'Kusama') { 151 | addressKSM = walletAddressArray[i].address 152 | } 153 | } 154 | console.log(addressKSM) 155 | console.log(addressDOT) 156 | return transformAccounts([{ 157 | address: addressKSM, 158 | name: 'imKey KSM', 159 | genesisHash: KusamaGenesisHash 160 | }, { 161 | address: addressDOT, 162 | name: 'imKey DOT', 163 | genesisHash: PolkadotGenesisHash 164 | }]) 165 | } 166 | const accounts = getAddress() 167 | 168 | window.injectedWeb3 = { 169 | imkey: { 170 | version: '0.1.0', 171 | enable: async origin => ({ 172 | accounts: { 173 | get: () => { 174 | console.log('accounts:' + accounts) 175 | return accounts 176 | } 177 | }, 178 | signer: new Signer() 179 | }) 180 | } 181 | } 182 | 183 | function transformAccounts (accounts) { 184 | return accounts.map(({ 185 | address, 186 | name, 187 | genesisHash 188 | }) => ({ 189 | address, 190 | name, 191 | genesisHash 192 | })) 193 | } 194 | 195 | // returns an array of all the injected sources 196 | // (this needs to be called first, before other requests) 197 | web3Enable('imkey') 198 | 199 | // returns an array of { address, meta: { name, source } } 200 | // meta.source contains the name of the extension that provides this account 201 | // const allAccounts = web3Accounts() 202 | 203 | // the address we use to use for signing, as injected 204 | // const SENDER = '5DgzZQE9FS7G5CJLrLgVq2YNa4LM3oYdPh4DFfpp4cMRcWeM' 205 | 206 | // finds an injector for an address 207 | // const injector = web3FromAddress(SENDER) 208 | 209 | // sign and send our transaction - notice here that the address of the account 210 | // (as retrieved injected) is passed through as the param to the `signAndSend`, 211 | // the API then calls the extension to present to the user and get it signed. 212 | // Once complete, the api sends the tx + signature via the normal process 213 | // api.tx.balances 214 | // .transfer('5EAKqFv3izjcjDdNuMLbrEk6Wzo1KEJsUAtWLyrB2zn4kMpT', 123456) 215 | // .signAndSend(SENDER, { SingleAccountSigner }, (status) => { }); 216 | -------------------------------------------------------------------------------- /.electron-vue/webpack.web.config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | process.env.BABEL_ENV = 'web' 4 | 5 | const path = require('path') 6 | const webpack = require('webpack') 7 | 8 | // const BabiliWebpackPlugin = require('babili-webpack-plugin') 9 | const BabiliWebpackPlugin = require('babel-minify-webpack-plugin') 10 | const CopyWebpackPlugin = require('copy-webpack-plugin') 11 | const MiniCssExtractPlugin = require('mini-css-extract-plugin') 12 | const HtmlWebpackPlugin = require('html-webpack-plugin') 13 | const {VueLoaderPlugin} = require('vue-loader') 14 | 15 | let webConfig = { 16 | // devtool: '#cheap-module-eval-source-map', 17 | entry: { 18 | web: path.join(__dirname, '../src/renderer/main.js'), 19 | worker: path.join(__dirname, '../src/worker/worker.js'), 20 | polkadotdapp: path.join(__dirname, '../src/api/polkadotdapp.js'), 21 | ethereumdapp: path.join(__dirname, '../src/api/ethereumdapp.js'), 22 | // imkey_web3_provider: path.join(__dirname, '../src/api/imkey_web3_provider.js') 23 | }, 24 | module: { 25 | rules: [ 26 | { 27 | test: /\.vue$/, 28 | use: { 29 | loader: 'vue-loader', 30 | options: { 31 | extractCSS: true, 32 | loaders: { 33 | sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1', 34 | scss: 'vue-style-loader!css-loader!sass-loader', 35 | less: 'vue-style-loader!css-loader!less-loader' 36 | } 37 | } 38 | } 39 | }, 40 | { 41 | test: /\.scss$/, 42 | use: ['vue-style-loader', 'css-loader', 'sass-loader'] 43 | }, 44 | { 45 | test: /\.sass$/, 46 | use: ['vue-style-loader', 'css-loader', 'sass-loader?indentedSyntax'] 47 | }, 48 | { 49 | test: /\.less$/, 50 | use: ['vue-style-loader', 'css-loader', 'less-loader'] 51 | }, 52 | { 53 | test: /\.css$/, 54 | use: ['vue-style-loader', 'css-loader'] 55 | }, 56 | { 57 | test: /\.html$/, 58 | use: 'vue-html-loader' 59 | }, 60 | { 61 | test: /\.js$/, 62 | use: 'babel-loader', 63 | include: [path.resolve(__dirname, '../src/renderer')], 64 | exclude: /node_modules/ 65 | }, 66 | 67 | { 68 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 69 | use: { 70 | loader: 'url-loader', 71 | options: { 72 | limit: 10000, 73 | name: 'imgs/[name].[ext]' 74 | } 75 | } 76 | }, 77 | { 78 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 79 | use: { 80 | loader: 'url-loader', 81 | options: { 82 | limit: 10000, 83 | name: 'fonts/[name].[ext]' 84 | } 85 | } 86 | } 87 | ] 88 | }, 89 | plugins: [ 90 | new VueLoaderPlugin(), 91 | new MiniCssExtractPlugin({filename: 'styles.css'}), 92 | new HtmlWebpackPlugin({ 93 | filename: 'index.html', 94 | template: path.resolve(__dirname, '../src/index.ejs'), 95 | chunks: ['renderer', 'vendor'], 96 | templateParameters(compilation, assets, options) { 97 | return { 98 | compilation: compilation, 99 | webpack: compilation.getStats().toJson(), 100 | webpackConfig: compilation.options, 101 | htmlWebpackPlugin: { 102 | files: assets, 103 | options: options 104 | }, 105 | process, 106 | }; 107 | }, 108 | minify: { 109 | collapseWhitespace: true, 110 | removeAttributeQuotes: true, 111 | removeComments: true 112 | }, 113 | nodeModules: false 114 | }), 115 | new webpack.DefinePlugin({ 116 | 'process.env.IS_WEB': 'true' 117 | }), 118 | new HtmlWebpackPlugin({ 119 | filename: 'worker.html', 120 | template: path.resolve(__dirname, '../src/worker.ejs'), 121 | chunks: ['worker', 'vendor'], 122 | minify: { 123 | collapseWhitespace: true, 124 | removeAttributeQuotes: true, 125 | removeComments: true 126 | }, 127 | templateParameters(compilation, assets, options) { 128 | return { 129 | compilation: compilation, 130 | webpack: compilation.getStats().toJson(), 131 | webpackConfig: compilation.options, 132 | htmlWebpackPlugin: { 133 | files: assets, 134 | options: options 135 | }, 136 | process, 137 | }; 138 | }, 139 | nodeModules: process.env.NODE_ENV !== 'production' 140 | ? path.resolve(__dirname, '../node_modules') 141 | : false 142 | }), 143 | new webpack.HotModuleReplacementPlugin(), 144 | new webpack.NoEmitOnErrorsPlugin() 145 | ], 146 | output: { 147 | filename: '[name].js', 148 | path: path.join(__dirname, '../dist/web'), 149 | publicPath: './' 150 | }, 151 | resolve: { 152 | alias: { 153 | '@': path.join(__dirname, '../src/renderer'), 154 | 'vue$': 'vue/dist/vue.esm.js' 155 | }, 156 | extensions: ['.js', '.vue', '.json', '.css'] 157 | }, 158 | target: 'web' 159 | } 160 | 161 | /** 162 | * Adjust webConfig for production settings 163 | */ 164 | if (process.env.NODE_ENV === 'production') { 165 | // webConfig.devtool = '' 166 | 167 | webConfig.plugins.push( 168 | new BabiliWebpackPlugin(), 169 | new CopyWebpackPlugin( 170 | { 171 | patterns: [ 172 | { from: path.join(__dirname, '../static'), 173 | to: path.join(__dirname, '../dist/web/static')}, 174 | { from: path.join(__dirname, '../src/api/imkey_web3_provider.js'), 175 | to: path.join(__dirname, '../dist/electron/imkey_web3_provider.js')}, 176 | ] 177 | }), 178 | new webpack.DefinePlugin({ 179 | 'process.env.NODE_ENV': '"production"' 180 | }), 181 | new webpack.LoaderOptionsPlugin({ 182 | minimize: true 183 | }) 184 | ) 185 | } 186 | 187 | module.exports = webConfig 188 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "imKeyManager", 3 | "version": "2.1.6-beta", 4 | "author": "imkey ", 5 | "description": "imKey Manager @2020", 6 | "license": "MIT", 7 | "main": "./dist/electron/main.js", 8 | "scripts": { 9 | "build": "node .electron-vue/build.js && electron-builder", 10 | "build:dir": "node .electron-vue/build.js && electron-builder --dir", 11 | "build:clean": "cross-env BUILD_TARGET=clean node .electron-vue/build.js", 12 | "build:web": "cross-env BUILD_TARGET=web node .electron-vue/build.js", 13 | "lint": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter src test", 14 | "lint:fix": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter --fix src test", 15 | "dev": "node .electron-vue/dev-runner.js", 16 | "e2e": "mocha test/e2e", 17 | "pack": "npm run pack:main && npm run pack:renderer", 18 | "pack:main": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.main.config.js", 19 | "pack:renderer": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.renderer.config.js", 20 | "test": "npm run unit && npm run e2e", 21 | "unit": "karma start test/unit/karma.conf.js", 22 | "postinstall": "npm run lint:fix && electron-builder install-app-deps", 23 | "release": "node .electron-vue/build.js && electron-builder" 24 | }, 25 | "build": { 26 | "afterSign": "build/notarize.js", 27 | "productName": "imKey Manager", 28 | "appId": "com.imkey.imkey-manager", 29 | "copyright": "Copyright © 2020 imkey", 30 | "directories": { 31 | "output": "build" 32 | }, 33 | "publish": { 34 | "provider": "generic", 35 | "url": "https://files.imkey.im/downloads/imKeyManager" 36 | }, 37 | "releaseInfo": { 38 | "releaseNotes": "[{id:1,info:\"新增蓝牙固件更新功能\"},{id:2,info:\"增加蓝牙固件版本显示\"},{id:3,info:\"删除DApp选项卡\"}]" 39 | }, 40 | "files": [ 41 | "dist/electron/**/*" 42 | ], 43 | "dmg": { 44 | "sign": false, 45 | "contents": [ 46 | { 47 | "x": 410, 48 | "y": 150, 49 | "type": "link", 50 | "path": "/Applications" 51 | }, 52 | { 53 | "x": 130, 54 | "y": 150, 55 | "type": "file" 56 | } 57 | ] 58 | }, 59 | "mac": { 60 | "hardenedRuntime": true, 61 | "gatekeeperAssess": false, 62 | "entitlements": "build/entitlements.mac.plist", 63 | "entitlementsInherit": "build/entitlements.mac.plist", 64 | "icon": "build/icons/icon.icns", 65 | "extraResources": [ 66 | { 67 | "from": "./connector.dylib", 68 | "to": "./" 69 | }, 70 | { 71 | "from": "./key.env", 72 | "to": "./" 73 | } 74 | ] 75 | }, 76 | "win": { 77 | "icon": "build/icons/icon.ico", 78 | "requestedExecutionLevel": "highestAvailable", 79 | "target": "nsis", 80 | "extraResources": [ 81 | { 82 | "from": "./connector.dll", 83 | "to": "../" 84 | }, 85 | { 86 | "from": "./key.env", 87 | "to": "../" 88 | } 89 | ] 90 | }, 91 | "nsis": { 92 | "oneClick": false, 93 | "perMachine": true, 94 | "allowElevation": true, 95 | "allowToChangeInstallationDirectory": true, 96 | "createDesktopShortcut": true, 97 | "runAfterFinish": true 98 | }, 99 | "linux": { 100 | "icon": "build/icons/icon.icns" 101 | } 102 | }, 103 | "dependencies": { 104 | "@fortawesome/fontawesome-free": "^5.15.2", 105 | "@polkadot/api": "^4.2.1", 106 | "@polkadot/extension-dapp": "^0.37.2", 107 | "@polkadot/types": "^4.2.1", 108 | "@polkadot/util": "^6.0.5", 109 | "@polkadot/util-crypto": "^6.0.5", 110 | "body-parser": "^1.19.0", 111 | "copy-to-clipboard": "^3.3.1", 112 | "cors": "^2.8.5", 113 | "decimal.js": "^10.2.1", 114 | "dotenv": "^8.2.0", 115 | "electron-updater": "^4.3.8", 116 | "electron-localshortcut": "^3.2.1", 117 | "element-ui": "^2.15.1", 118 | "express": "^4.17.1", 119 | "ffi-napi": "^4.0.3", 120 | "fs-extra": "^9.1.0", 121 | "is-online": "^9.0.0", 122 | "lodash": "^4.17.21", 123 | "lodash-id": "^0.14.0", 124 | "ref-napi": "^3.0.2", 125 | "vue": "^2.6.12", 126 | "vue-cookies": "^1.7.4", 127 | "vue-electron": "^1.0.6", 128 | "vue-i18n": "^8.24.0", 129 | "vue-router": "^3.5.1", 130 | "vuex": "^3.6.2", 131 | "vuex-electron": "^1.0.3", 132 | "web3": "^1.3.4", 133 | "write-file-atomic": "^3.0.3" 134 | }, 135 | "devDependencies": { 136 | "@babel/core": "^7.13.10", 137 | "@babel/plugin-transform-runtime": "^7.13.10", 138 | "@babel/preset-env": "^7.13.10", 139 | "@babel/register": "^7.13.8", 140 | "@babel/runtime": "^7.13.10", 141 | "@polkadot/api-cli": "^0.30.1", 142 | "@vue/devtools": "^6.0.0-beta.6", 143 | "ajv": "^7.2.1", 144 | "babel-eslint": "^10.1.0", 145 | "babel-loader": "^8.2.2", 146 | "babel-minify-webpack-plugin": "^0.3.1", 147 | "babel-plugin-istanbul": "^6.0.0", 148 | "bufferutil": "^4.0.3", 149 | "cfonts": "^2.9.1", 150 | "chai": "^4.3.3", 151 | "chai-http": "^4.3.0", 152 | "chalk": "^4.1.0", 153 | "copy-webpack-plugin": "^8.0.0", 154 | "cross-env": "^7.0.3", 155 | "css-loader": "^5.1.1", 156 | "del": "^6.0.0", 157 | "devtron": "^1.4.0", 158 | "electron": "^11.3.0", 159 | "electron-builder": "24.6.4", 160 | "electron-debug": "^3.2.0", 161 | "electron-devtools-installer": "^3.2.0", 162 | "electron-download": "^4.1.1", 163 | "electron-notarize": "^1.1.1", 164 | "eslint": "^7.21.0", 165 | "eslint-config-standard": "^16.0.2", 166 | "eslint-friendly-formatter": "^4.0.1", 167 | "eslint-loader": "^4.0.2", 168 | "eslint-plugin-html": "^6.1.2", 169 | "eslint-plugin-import": "^2.22.1", 170 | "eslint-plugin-node": "^11.1.0", 171 | "eslint-plugin-promise": "^4.3.1", 172 | "eslint-plugin-standard": "^5.0.0", 173 | "eslint-plugin-vue": "^7.7.0", 174 | "file-loader": "^6.2.0", 175 | "google-protobuf": "3.15.5", 176 | "html-webpack-plugin": "^5.3.0", 177 | "inject-loader": "^4.0.1", 178 | "karma": "^6.1.2", 179 | "karma-chai": "^0.1.0", 180 | "karma-coverage": "^2.0.3", 181 | "karma-electron": "^6.3.3", 182 | "karma-mocha": "^2.0.1", 183 | "karma-sourcemap-loader": "^0.3.8", 184 | "karma-spec-reporter": "^0.0.32", 185 | "karma-webpack": "^5.0.0", 186 | "less": "^4.1.1", 187 | "less-loader": "^8.0.0", 188 | "mini-css-extract-plugin": "^1.3.9", 189 | "mocha": "^8.3.1", 190 | "multispinner": "^0.2.1", 191 | "node-loader": "^1.0.2", 192 | "node-sass": "^5.0.0", 193 | "require-dir": "^1.2.0", 194 | "sass-loader": "^11.0.1", 195 | "should": "^13.2.3", 196 | "spectron": "^14.0.0", 197 | "style-loader": "^2.0.0", 198 | "url-loader": "^4.1.1", 199 | "utf-8-validate": "^5.0.4", 200 | "vue-html-loader": "^1.2.4", 201 | "vue-loader": "^15.9.6", 202 | "vue-style-loader": "^4.1.3", 203 | "vue-template-compiler": "^2.6.12", 204 | "webpack": "^5.24.4", 205 | "webpack-cli": "^4.5.0", 206 | "webpack-dev-server": "^3.11.2", 207 | "webpack-hot-middleware": "^2.25.0", 208 | "webpack-merge": "^5.7.3" 209 | }, 210 | "repository": { 211 | "type": "git", 212 | "url": "git+https://github.com/consenlabs/imkey-manager.git" 213 | }, 214 | "bugs": { 215 | "url": "https://github.com/consenlabs/imkey-manager/issues" 216 | }, 217 | "homepage": "https://github.com/consenlabs/imkey-manager#readme" 218 | } 219 | -------------------------------------------------------------------------------- /test/e2e/specs/deviceManager.spec.js: -------------------------------------------------------------------------------- 1 | // const chai = require('chai') 2 | const deviceManager = require('../../../src/api/devicemanagerapi') 3 | describe('Api', () => { 4 | describe('test initImKeyCore()', () => { 5 | it('it should show initImKeyCore() result', (done) => { 6 | const response = deviceManager.initImKeyCore() 7 | if (response.isSuccess) { 8 | expect(response.result).to.eq('success') 9 | done() 10 | } else { 11 | console.error(response.result) 12 | } 13 | }).timeout(500000) 14 | }) 15 | describe('test connect()', () => { 16 | it('it should show connect() result', (done) => { 17 | const response = deviceManager.connect() 18 | if (response.isSuccess) { 19 | expect(response.result).to.eq('success') 20 | done() 21 | } else { 22 | console.error(response.result) 23 | } 24 | }).timeout(500000) 25 | }) 26 | 27 | describe('test getSn()', () => { 28 | it('it should show getSn() result', (done) => { 29 | const response = deviceManager.getSn() 30 | if (response.isSuccess) { 31 | expect(response.result).to.eq('imKey01200100010') 32 | done() 33 | } else { 34 | console.error(response.result) 35 | } 36 | }).timeout(500000) 37 | }) 38 | describe('test getSeid()', () => { 39 | it('it should show getSeid result', (done) => { 40 | const response = deviceManager.getSeid() 41 | if (response.isSuccess) { 42 | expect(response.result).to.eq('18080000000000860001010000000106') 43 | done() 44 | } else { 45 | console.error(response.result) 46 | } 47 | }).timeout(500000) 48 | }) 49 | 50 | describe('test getRamSize()', () => { 51 | it('it should show getRamSize result', (done) => { 52 | const response = deviceManager.getRamSize() 53 | if (response.isSuccess) { 54 | expect(response.result).to.eq(3159) 55 | done() 56 | } else { 57 | console.error(response.result) 58 | } 59 | }).timeout(500000) 60 | }) 61 | 62 | describe('test getFirmwareVersion()', () => { 63 | it('it should show getFirmwareVersion result', (done) => { 64 | const response = deviceManager.getFirmwareVersion() 65 | if (response.isSuccess) { 66 | expect(response.result).to.eq('1.5.08') 67 | done() 68 | } else { 69 | console.error(response.result) 70 | } 71 | }).timeout(500000) 72 | }) 73 | describe('test isBLStatus()', () => { 74 | it('it should show isBLStatus result', (done) => { 75 | const response = deviceManager.isBLStatus() 76 | if (response.isSuccess) { 77 | expect(response.result).to.eq(false) 78 | done() 79 | } else { 80 | console.error(response.result) 81 | } 82 | }).timeout(500000) 83 | }) 84 | describe('test getSdkInfo()', () => { 85 | it('it should show getSdkInfo result', (done) => { 86 | const response = deviceManager.getSdkInfo() 87 | if (response.isSuccess) { 88 | expect(response.result).to.eq('1.2.10') 89 | done() 90 | } else { 91 | console.error(response.result) 92 | } 93 | }).timeout(500000) 94 | }) 95 | describe('test activeDevice()', () => { 96 | it('it should show activeDevice result', (done) => { 97 | const response = deviceManager.activeDevice() 98 | if (response.isSuccess) { 99 | expect(response.result).to.eq('success') 100 | done() 101 | } else { 102 | console.error(response.result) 103 | } 104 | }).timeout(500000) 105 | }) 106 | describe('test checkDevice()', () => { 107 | it('it should show checkDevice result', (done) => { 108 | const response = deviceManager.checkDevice() 109 | if (response.isSuccess) { 110 | expect(response.result).to.eq('success') 111 | done() 112 | } else { 113 | console.error(response.result) 114 | } 115 | }).timeout(500000) 116 | }) 117 | describe('test cosCheckUpdate()', () => { 118 | it('it should show cosCheckUpdate result', (done) => { 119 | const response = deviceManager.cosCheckUpdate() 120 | if (response.isSuccess) { 121 | expect(response.result).to.have.a.property('seid') 122 | done() 123 | } else { 124 | console.error(response.result) 125 | } 126 | }).timeout(500000) 127 | }) 128 | 129 | describe('test checkUpdate()', () => { 130 | it('it should show checkUpdate result', (done) => { 131 | const response = deviceManager.checkUpdate() 132 | if (response.isSuccess) { 133 | expect(response.result).to.have.a.property('sn') 134 | done() 135 | } else { 136 | console.error(response.result) 137 | } 138 | }).timeout(500000) 139 | }) 140 | describe('test checkUpdateAppList()', () => { 141 | it('it should show checkUpdateAppList result', (done) => { 142 | const response = deviceManager.checkUpdateAppList() 143 | if (response.isSuccess) { 144 | expect(response.result).to.have.a.property('list') 145 | done() 146 | } else { 147 | console.error(response.result) 148 | } 149 | }).timeout(500000) 150 | }) 151 | const appletNmae = 'ETH' 152 | describe('test deleteApplet()', () => { 153 | it('it should show deleteApplet result', (done) => { 154 | const response = deviceManager.deleteApplet(appletNmae) 155 | if (response.isSuccess) { 156 | expect(response.result).to.eq('success') 157 | done() 158 | } else { 159 | console.error(response.result) 160 | } 161 | }).timeout(500000) 162 | }) 163 | 164 | describe('test downloadApplet()', () => { 165 | it('it should show downloadApplet result', (done) => { 166 | const response = deviceManager.downloadApplet(appletNmae) 167 | if (response.isSuccess) { 168 | expect(response.result).to.eq('success') 169 | done() 170 | } else { 171 | console.error(response.result) 172 | } 173 | }).timeout(500000) 174 | }) 175 | 176 | describe('test updateApplet()', () => { 177 | it('it should show updateApplet result', (done) => { 178 | const response = deviceManager.updateApplet(appletNmae) 179 | if (response.isSuccess) { 180 | expect(response.result).to.eq('success') 181 | done() 182 | } else { 183 | console.error(response.result) 184 | } 185 | }).timeout(500000) 186 | }) 187 | 188 | describe('test deviceBindCheck()', () => { 189 | it('it should show deviceBindCheck result', (done) => { 190 | const filePath = require('path').resolve('./') 191 | const response = deviceManager.deviceBindCheck(filePath) 192 | if (response.isSuccess) { 193 | expect(response.result).to.contain('bound') 194 | done() 195 | } else { 196 | console.error(response.result) 197 | } 198 | }).timeout(500000) 199 | }) 200 | 201 | describe('test deviceBindAcquire()', () => { 202 | it('it should show deviceBindAcquire result', (done) => { 203 | const bindCode = 'T52MKBA3' 204 | const response = deviceManager.deviceBindAcquire(bindCode) 205 | if (response.isSuccess) { 206 | expect(response.result).to.contain('success') 207 | done() 208 | } else { 209 | console.error(response.result) 210 | } 211 | }).timeout(500000) 212 | }) 213 | describe('test deviceBindDisplay()', () => { 214 | it('it should show deviceBindDisplay result', (done) => { 215 | const response = deviceManager.deviceBindDisplay() 216 | if (response.isSuccess) { 217 | expect(response.result).to.contain('success') 218 | done() 219 | } else { 220 | console.error(response.result) 221 | } 222 | }).timeout(500000) 223 | }) 224 | 225 | describe('test cosUpdate()', () => { 226 | it('it should show cosUpdate result', (done) => { 227 | const response = deviceManager.cosUpdate() 228 | if (response.isSuccess) { 229 | expect(response.result).to.contain('success') 230 | done() 231 | } else { 232 | console.error(response.result) 233 | } 234 | }).timeout(500000) 235 | }) 236 | }) 237 | -------------------------------------------------------------------------------- /.electron-vue/webpack.renderer.config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | process.env.BABEL_ENV = 'renderer' 4 | 5 | const path = require('path') 6 | const {dependencies} = require('../package.json') 7 | const webpack = require('webpack') 8 | 9 | // const BabiliWebpackPlugin = require('babili-webpack-plugin') 10 | const BabiliWebpackPlugin = require('babel-minify-webpack-plugin') 11 | const CopyWebpackPlugin = require('copy-webpack-plugin') 12 | const MiniCssExtractPlugin = require('mini-css-extract-plugin') 13 | const HtmlWebpackPlugin = require('html-webpack-plugin') 14 | const {VueLoaderPlugin} = require('vue-loader') 15 | 16 | /** 17 | * List of node_modules to include in webpack bundle 18 | * 19 | * Required for specific packages like Vue UI libraries 20 | * that provide pure *.vue files that need compiling 21 | * https://simulatedgreg.gitbooks.io/electron-vue/content/en/webpack-configurations.html#white-listing-externals 22 | */ 23 | let whiteListedModules = ['vue', 'element-ui'] 24 | 25 | let rendererConfig = { 26 | // devtool: '#cheap-module-eval-source-map', 27 | entry: { 28 | renderer: path.join(__dirname, '../src/renderer/main.js'), 29 | worker: path.join(__dirname, '../src/worker/worker.js'), 30 | polkadotdapp: path.join(__dirname, '../src/api/polkadotdapp.js'), 31 | ethereumdapp: path.join(__dirname, '../src/api/ethereumdapp.js'), 32 | ethereumdapp_imkey_web3: path.join(__dirname, '../src/api/ethereumdapp_imkey_web3.js'), 33 | // imkey_web3_provider: path.join(__dirname, '../src/api/imkey_web3_provider.js') 34 | }, 35 | externals: [ 36 | ...Object.keys(dependencies || {}).filter(d => !whiteListedModules.includes(d)) 37 | ], 38 | module: { 39 | rules: [ 40 | { 41 | test: /\.vue$/, 42 | use: { 43 | loader: 'vue-loader', 44 | options: { 45 | extractCSS: process.env.NODE_ENV === 'production', 46 | loaders: { 47 | sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1', 48 | scss: 'vue-style-loader!css-loader!sass-loader', 49 | less: 'vue-style-loader!css-loader!less-loader' 50 | } 51 | } 52 | } 53 | }, 54 | { 55 | test: /\.scss$/, 56 | use: ['vue-style-loader', 'css-loader', 'sass-loader'] 57 | }, 58 | { 59 | test: /\.sass$/, 60 | use: ['vue-style-loader', 'css-loader', 'sass-loader?indentedSyntax'] 61 | }, 62 | { 63 | test: /\.less$/, 64 | use: ['vue-style-loader', 'css-loader', 'less-loader'] 65 | }, 66 | { 67 | test: /\.css$/, 68 | use: ['vue-style-loader', 'css-loader'] 69 | }, 70 | { 71 | test: /\.html$/, 72 | use: 'vue-html-loader' 73 | }, 74 | { 75 | test: /\.js$/, 76 | use: 'babel-loader', 77 | exclude: /node_modules/ 78 | }, 79 | { 80 | test: /\.node$/, 81 | use: 'node-loader' 82 | }, 83 | 84 | { 85 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 86 | use: { 87 | loader: 'url-loader', 88 | options: { 89 | limit: 10000, 90 | name: 'imgs/[name]--[folder].[ext]' 91 | } 92 | } 93 | }, 94 | { 95 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, 96 | loader: 'url-loader', 97 | options: { 98 | limit: 10000, 99 | name: 'media/[name]--[folder].[ext]' 100 | } 101 | }, 102 | { 103 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 104 | use: { 105 | loader: 'url-loader', 106 | options: { 107 | limit: 10000, 108 | name: 'fonts/[name]--[folder].[ext]' 109 | } 110 | } 111 | } 112 | ] 113 | }, 114 | node: { 115 | __dirname: process.env.NODE_ENV !== 'production', 116 | __filename: process.env.NODE_ENV !== 'production' 117 | }, 118 | plugins: [ 119 | new VueLoaderPlugin(), 120 | new MiniCssExtractPlugin({filename: 'styles.css'}), 121 | new HtmlWebpackPlugin({ 122 | filename: 'index.html', 123 | template: path.resolve(__dirname, '../src/index.ejs'), 124 | chunks: ['renderer', 'vendor'], 125 | templateParameters(compilation, assets, options) { 126 | return { 127 | compilation: compilation, 128 | webpack: compilation.getStats().toJson(), 129 | webpackConfig: compilation.options, 130 | htmlWebpackPlugin: { 131 | files: assets, 132 | options: options 133 | }, 134 | process, 135 | }; 136 | }, 137 | minify: { 138 | collapseWhitespace: true, 139 | removeAttributeQuotes: true, 140 | removeComments: true 141 | }, 142 | nodeModules: process.env.NODE_ENV !== 'production' 143 | ? path.resolve(__dirname, '../node_modules') 144 | : false 145 | }), 146 | new HtmlWebpackPlugin({ 147 | filename: 'worker.html', 148 | template: path.resolve(__dirname, '../src/worker.ejs'), 149 | chunks: ['worker', 'vendor'], 150 | minify: { 151 | collapseWhitespace: true, 152 | removeAttributeQuotes: true, 153 | removeComments: true 154 | }, 155 | templateParameters(compilation, assets, options) { 156 | return { 157 | compilation: compilation, 158 | webpack: compilation.getStats().toJson(), 159 | webpackConfig: compilation.options, 160 | htmlWebpackPlugin: { 161 | files: assets, 162 | options: options 163 | }, 164 | process, 165 | }; 166 | }, 167 | nodeModules: process.env.NODE_ENV !== 'production' 168 | ? path.resolve(__dirname, '../node_modules') 169 | : false 170 | }), 171 | new webpack.HotModuleReplacementPlugin(), 172 | new webpack.NoEmitOnErrorsPlugin() 173 | ], 174 | output: { 175 | filename: '[name].js', 176 | libraryTarget: 'commonjs2', 177 | path: path.join(__dirname, '../dist/electron'), 178 | publicPath: './' 179 | }, 180 | resolve: { 181 | alias: { 182 | '@': path.join(__dirname, '../src/renderer'), 183 | 'vue$': 'vue/dist/vue.esm.js', 184 | '~': path.join(__dirname, '../src'), 185 | }, 186 | extensions: ['.js', '.vue', '.json', '.css', '.node'] 187 | }, 188 | target: 'electron-renderer' 189 | } 190 | 191 | /** 192 | * Adjust rendererConfig for development settings 193 | */ 194 | if (process.env.NODE_ENV !== 'production') { 195 | rendererConfig.plugins.push( 196 | new webpack.DefinePlugin({ 197 | '__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"` 198 | }) 199 | ) 200 | } 201 | 202 | /** 203 | * Adjust rendererConfig for production settings 204 | */ 205 | if (process.env.NODE_ENV === 'production') { 206 | // rendererConfig.devtool = '' 207 | 208 | rendererConfig.plugins.push( 209 | new BabiliWebpackPlugin(), 210 | new CopyWebpackPlugin( 211 | { 212 | patterns: [ 213 | { from: path.join(__dirname, '../static'), 214 | to: path.join(__dirname, '../dist/electron/static')}, 215 | { from: path.join(__dirname, '../src/api/imkey_web3_provider.js'), 216 | to: path.join(__dirname, '../dist/electron/imkey_web3_provider.js')}, 217 | ] 218 | }), 219 | new webpack.DefinePlugin({ 220 | 'process.env.NODE_ENV': '"production"' 221 | }), 222 | new webpack.LoaderOptionsPlugin({ 223 | minimize: true 224 | }) 225 | ) 226 | } 227 | 228 | module.exports = rendererConfig 229 | -------------------------------------------------------------------------------- /src/renderer/store/dapps.js: -------------------------------------------------------------------------------- 1 | export const DAPPS_ETH = [ 2 | { 3 | id: 1, 4 | urlType: 'Tokenlon', 5 | url: 'https://tokenlon.im/instant', 6 | title: 'Tokenlon', 7 | iconUrl: 'https://aws-v2-cdn.token.im/app-production/dapps/app_icons/285/LON-Dashboard.png', 8 | desc: 'm.dapp.tokenlon_desc' 9 | }, 10 | { 11 | id: 2, 12 | urlType: 'Sushiswap', 13 | url: 'https://app.sushi.com', 14 | title: 'Sushiswap', 15 | iconUrl: 'https://aws-v2-cdn.token.im/app-production/dapps/app_icons/277/WechatIMG3.png', 16 | desc: 'm.dapp.sushiswap_desc' 17 | }, 18 | { 19 | id: 3, 20 | urlType: 'Uniswap', 21 | url: 'https://app.uniswap.org', 22 | title: 'Uniswap', 23 | iconUrl: 'https://aws-v2-cdn.token.im/app-production/dapps/app_icons/338/Uniswap-ipfs.png', 24 | desc: 'm.dapp.uniswap_desc' 25 | }, 26 | { 27 | id: 4, 28 | urlType: 'Multisender', 29 | url: 'https://multisender.app', 30 | title: 'Multisender', 31 | iconUrl: 'https://multisender.app/favicon/favicon.ico', 32 | desc: 'm.dapp.multisender_desc' 33 | }, 34 | { 35 | id: 5, 36 | urlType: 'Compound', 37 | url: 'https://app.compound.finance/', 38 | title: 'Compound', 39 | iconUrl: 'https://compound.finance/favicon.ico', 40 | desc: 'm.dapp.compound_desc' 41 | }, 42 | { 43 | id: 6, 44 | urlType: 'AAVE', 45 | url: 'https://app.aave.com/', 46 | title: 'AAVE', 47 | iconUrl: 'https://defigogo.s3-ap-northeast-1.amazonaws.com/images/b7d8f173-de3f-4dc3-9316-00268fc402a9.jpg', 48 | desc: 'm.dapp.aave_desc' 49 | }, 50 | { 51 | id: 7, 52 | urlType: 'Rarible', 53 | url: 'https://rarible.com', 54 | title: 'Rarible', 55 | iconUrl: 'https://rarible.com/favicon.png', 56 | desc: 'm.dapp.rarible_desc' 57 | }, 58 | { 59 | id: 8, 60 | urlType: 'Murall', 61 | url: 'https://murall.art', 62 | title: 'Murall', 63 | iconUrl: 'https://murall.art/images/murall_top_logo_mobile.svg', 64 | desc: 'm.dapp.murall_desc' 65 | }, 66 | // { 67 | // id: 9, 68 | // urlType: 'zkSync', 69 | // url: 'https://wallet.zksync.io/accounts', 70 | // title: 'zkSync', 71 | // iconUrl: 'https://zksync.io/favicon.ico', 72 | // desc: 'm.dapp.zksync_desc' 73 | // }, 74 | { 75 | id: 9, 76 | urlType: 'ZKSwap', 77 | url: 'https://zks.app/zh/wallet', 78 | title: 'ZKSwap', 79 | iconUrl: 'https://zks.app/favicon.ico', 80 | desc: 'm.dapp.zkswap_desc' 81 | }, 82 | { 83 | id: 10, 84 | urlType: 'Etherscan', 85 | url: 'https://cn.etherscan.com', 86 | title: 'Etherscan', 87 | iconUrl: 'https://aws-v2-cdn.token.im/app-production/dapps/app_icons/276/etherscanDapp_3x.png', 88 | desc: 'm.dapp.Etherscan_desc' 89 | }, 90 | { 91 | id: 11, 92 | urlType: 'OpenSea', 93 | url: 'https://opensea.io/?ref=0xb9e29984fe50602e7a619662ebed4f90d93824c7"', 94 | title: 'OpenSea', 95 | iconUrl: 'https://aws-v2-cdn.token.im/app-production/dapps/app_icons/78/OpenSea_2_162-162.png', 96 | desc: 'm.dapp.OpenSea_desc' 97 | }, 98 | { 99 | id: 12, 100 | urlType: 'Nifty Gateway', 101 | url: 'https://niftygateway.com/', 102 | title: 'Nifty Gateway', 103 | iconUrl: 'https://aws-v2-cdn.token.im/app-production/dapps/app_icons/382/nifty.jpg', 104 | desc: 'm.dapp.Nifty_Gateway_desc' 105 | }, 106 | { 107 | id: 13, 108 | urlType: 'SuperRare', 109 | url: 'https://superrare.co/', 110 | title: 'SuperRare', 111 | iconUrl: 'https://aws-v2-cdn.token.im/app-production/dapps/app_icons/293/SuperRare126.jpg', 112 | desc: 'm.dapp.SuperRare_desc' 113 | } 114 | 115 | ] 116 | export const DAPPS_BSC = [ 117 | // { 118 | // id: 1, 119 | // urlType: 'Venus', 120 | // url: 'https://app.venus.io/dashboard', 121 | // title: 'Venus', 122 | // iconUrl: 'https://www.defibox.com/media/20210308115849-566d86dd8aca.jpg', 123 | // desc: 'm.dapp.Venus_desc' 124 | // }, 125 | { 126 | id: 1, 127 | urlType: 'pancake', 128 | url: 'https://pancakeswap.finance/', 129 | title: 'pancake', 130 | iconUrl: 'https://www.defibox.com/media/20210308122716-ce4f305bb61c.jpg', 131 | desc: 'm.dapp.pancake_desc' 132 | }, 133 | { 134 | id: 2, 135 | urlType: 'Autofarm', 136 | url: 'https://autofarm.network/', 137 | title: 'Autofarm', 138 | iconUrl: 'https://www.defibox.com/media/20210308131736-b7b86d2e94d2.png', 139 | desc: 'm.dapp.Autofarm_desc' 140 | }, 141 | { 142 | id: 3, 143 | urlType: 'PancakeBunny', 144 | url: 'https://pancakebunny.finance/', 145 | title: 'PancakeBunny', 146 | iconUrl: 'https://www.defibox.com/media/20210309172437-ffb7821a88a4.png', 147 | desc: 'm.dapp.PancakeBunny_desc' 148 | }, 149 | { 150 | id: 4, 151 | urlType: 'Belt Finance', 152 | url: 'https://belt.fi/', 153 | title: 'Belt Finance', 154 | iconUrl: 'https://www.defibox.com/media/20210329113710-5d8c36afd079.png', 155 | desc: 'm.dapp.Belt_Finance_desc' 156 | }, 157 | { 158 | id: 5, 159 | urlType: 'beefy.finance', 160 | url: 'https://app.beefy.finance/', 161 | title: 'beefy.finance', 162 | iconUrl: 'https://www.defibox.com/media/20210308130841-da93a82b0f97.png', 163 | desc: 'm.dapp.beefy_finance_desc' 164 | }, 165 | { 166 | id: 6, 167 | urlType: 'Bscex LaunchpoolX', 168 | url: 'https://launchpoolx.bscex.org/', 169 | title: 'Bscex LaunchpoolX', 170 | iconUrl: 'https://www.defibox.com/media/20210309165630-9754c86ed206.png', 171 | desc: 'm.dapp.Bscex_LaunchpoolX_desc' 172 | }, 173 | { 174 | id: 7, 175 | urlType: 'ACryptoS', 176 | url: 'https://app.acryptos.com/', 177 | title: 'ACryptoS', 178 | iconUrl: 'https://www.defibox.com/media/20210308131222-ab768b768a61.jpg', 179 | desc: 'm.dapp.ACryptoS_desc' 180 | }, 181 | { 182 | id: 8, 183 | urlType: 'Alpaca Finance', 184 | url: 'https://app.alpacafinance.org/lend', 185 | title: 'Alpaca Finance', 186 | iconUrl: 'https://www.defibox.com/media/20210309162132-19d0ef32c55a.png', 187 | desc: 'm.dapp.Alpaca_Finance_desc' 188 | }, 189 | { 190 | id: 9, 191 | urlType: 'Goose Finance', 192 | url: 'https://www.goosedefi.com/', 193 | title: 'Goose Finance', 194 | iconUrl: 'https://www.defibox.com/media/20210309164915-48abe3f2fc60.png', 195 | desc: 'm.dapp.Goose_Finance_desc' 196 | }, 197 | { 198 | id: 10, 199 | urlType: 'JulSwap', 200 | url: 'https://julswap.com/#/swap', 201 | title: 'JulSwap', 202 | iconUrl: 'https://aws-v2-cdn.token.im/app-production/dapps/app_icons/416/julswap.jpg', 203 | desc: 'm.dapp.JulSwap_desc' 204 | } 205 | ] 206 | export const DAPPS_HECO = [ 207 | { 208 | id: 1, 209 | urlType: 'MDEX', 210 | url: 'https://ht.mdex.com/#/swap', 211 | title: 'MDEX', 212 | iconUrl: 'https://www.defibox.com/media/3664bcb5-9e37-4602-b8e0-0d53cc3fa20d.png', 213 | desc: 'm.dapp.MDEX_desc' 214 | }, 215 | { 216 | id: 2, 217 | urlType: 'BXH', 218 | url: 'https://bxh.com', 219 | title: 'BXH', 220 | iconUrl: 'https://www.defibox.com/media/20210330145517-0f974e8ebf4e.png', 221 | desc: 'm.dapp.BXH_desc' 222 | }, 223 | { 224 | id: 3, 225 | urlType: 'CoinWind', 226 | url: 'https://www.coinwind.com/', 227 | title: 'CoinWind', 228 | iconUrl: 'https://www.defibox.com/media/20210222114531-d0dcdedc1f9c.png', 229 | desc: 'm.dapp.CoinWind_desc' 230 | }, 231 | { 232 | id: 4, 233 | urlType: 'Lendhub', 234 | url: 'https://www.lendhub.org/', 235 | title: 'Lendhub', 236 | iconUrl: 'https://defigogo.s3-ap-northeast-1.amazonaws.com/media/960f016e-98ac-404a-8541-9f7989086b30.jpeg', 237 | desc: 'm.dapp.Lendhub_desc' 238 | }, // , 239 | // { 240 | // id: 5, 241 | // urlType: 'Pilot Protocol', 242 | // url: 'https://p.td/', 243 | // title: 'Pilot Protocol', 244 | // iconUrl: 'https://www.defibox.com/media/20210311140656-af0715d5f927.png', 245 | // desc: 'm.dapp.sushiswap_desc' 246 | // } 247 | 248 | { 249 | id: 5, 250 | urlType: 'FilDA', 251 | url: 'https://filda.io/', 252 | title: 'FilDA', 253 | iconUrl: 'https://www.defibox.com/media/900edc7c-bea6-4051-a73b-02d71a1c84da.jpeg', 254 | desc: 'm.dapp.FilDA_desc' 255 | }, 256 | { 257 | id: 6, 258 | urlType: 'EarnDefi', 259 | url: 'https://heco.earndefi.finance/', 260 | title: 'EarnDefi', 261 | iconUrl: 'https://www.defibox.com/media/3ed20046-0a5d-4a93-bcbe-8823d483147e.png', 262 | desc: 'm.dapp.EarnDefi_desc' 263 | }, 264 | { 265 | id: 7, 266 | urlType: 'Channels', 267 | url: 'https://channels.finance/', 268 | title: 'Channels', 269 | iconUrl: 'https://defigogo.s3-ap-northeast-1.amazonaws.com/media/f7938d8a-df16-4936-9c0e-9d430512787a.png', 270 | desc: 'm.dapp.Channels_desc' 271 | }, 272 | { 273 | id: 8, 274 | urlType: 'HFI.one', 275 | url: 'https://hfi.one', 276 | title: 'HFI.one', 277 | iconUrl: 'https://www.defibox.com/media/20210212183358-d5414d9f1240.jpg', 278 | desc: 'm.dapp.HFI_one_desc' 279 | }, 280 | { 281 | id: 9, 282 | urlType: 'Pippi Shrimp Swap', 283 | url: 'https://app.pippi.finance/', 284 | title: 'Pippi Shrimp Swap', 285 | iconUrl: 'https://www.defibox.com/media/20210317165637-ae1c5eab7535.png', 286 | desc: 'm.dapp.Pippi_Shrimp_Swap_desc' 287 | }, 288 | { 289 | id: 10, 290 | urlType: 'HashBridge', 291 | url: 'https://hboracle.org/#/', 292 | title: 'HashBridge', 293 | iconUrl: 'https://www.defibox.com/media/20210218175140-2346bca2ef51.png', 294 | desc: 'm.dapp.HashBridge_desc' 295 | } 296 | 297 | ] 298 | export const DAPPS_Polkadot = [ 299 | { 300 | id: 1, 301 | urlType: 'Polkadot', 302 | url: 'https://polkadot.js.org/apps/#/accounts', 303 | title: 'Polkadot', 304 | iconUrl: 'https://polkadot.js.org/favicon.ico', 305 | desc: 'm.dapp.polkadotJS_desc' 306 | } 307 | ] 308 | export const DAPPS_Config = { 309 | ETH: { 310 | chainId: 1, 311 | rpcUrl: 'https://mainnet-eth.token.im', 312 | symbol: 'ETH' 313 | }, 314 | BSC: { 315 | chainId: 56, 316 | rpcUrl: 'https://bsc-dataseed1.ninicoin.io', 317 | symbol: 'BNB' 318 | }, 319 | HECO: { 320 | chainId: 128, 321 | rpcUrl: 'https://http-mainnet.Hecochain.com', 322 | symbol: 'HT' 323 | }, 324 | Polkadot: { 325 | chainId: '', 326 | rpcUrl: '', 327 | symbol: '' 328 | } 329 | } 330 | -------------------------------------------------------------------------------- /src/proto/substrate_pb.js: -------------------------------------------------------------------------------- 1 | // source: substrate.proto 2 | /** 3 | * @fileoverview 4 | * @enhanceable 5 | * @suppress {messageConventions} JS Compiler reports an error if a variable or 6 | * field starts with 'MSG_' and isn't a translatable message. 7 | * @public 8 | */ 9 | // GENERATED CODE -- DO NOT EDIT! 10 | 11 | var jspb = require('google-protobuf'); 12 | var goog = jspb; 13 | var global = Function('return this')(); 14 | 15 | goog.exportSymbol('proto.substrateapi.SubstrateRawTxIn', null, global); 16 | goog.exportSymbol('proto.substrateapi.SubstrateTxOut', null, global); 17 | /** 18 | * Generated by JsPbCodeGenerator. 19 | * @param {Array=} opt_data Optional initial data array, typically from a 20 | * server response, or constructed directly in Javascript. The array is used 21 | * in place and becomes part of the constructed object. It is not cloned. 22 | * If no data is provided, the constructed object will be empty, but still 23 | * valid. 24 | * @extends {jspb.Message} 25 | * @constructor 26 | */ 27 | proto.substrateapi.SubstrateRawTxIn = function(opt_data) { 28 | jspb.Message.initialize(this, opt_data, 0, -1, null, null); 29 | }; 30 | goog.inherits(proto.substrateapi.SubstrateRawTxIn, jspb.Message); 31 | if (goog.DEBUG && !COMPILED) { 32 | /** 33 | * @public 34 | * @override 35 | */ 36 | proto.substrateapi.SubstrateRawTxIn.displayName = 'proto.substrateapi.SubstrateRawTxIn'; 37 | } 38 | /** 39 | * Generated by JsPbCodeGenerator. 40 | * @param {Array=} opt_data Optional initial data array, typically from a 41 | * server response, or constructed directly in Javascript. The array is used 42 | * in place and becomes part of the constructed object. It is not cloned. 43 | * If no data is provided, the constructed object will be empty, but still 44 | * valid. 45 | * @extends {jspb.Message} 46 | * @constructor 47 | */ 48 | proto.substrateapi.SubstrateTxOut = function(opt_data) { 49 | jspb.Message.initialize(this, opt_data, 0, -1, null, null); 50 | }; 51 | goog.inherits(proto.substrateapi.SubstrateTxOut, jspb.Message); 52 | if (goog.DEBUG && !COMPILED) { 53 | /** 54 | * @public 55 | * @override 56 | */ 57 | proto.substrateapi.SubstrateTxOut.displayName = 'proto.substrateapi.SubstrateTxOut'; 58 | } 59 | 60 | 61 | 62 | if (jspb.Message.GENERATE_TO_OBJECT) { 63 | /** 64 | * Creates an object representation of this proto. 65 | * Field names that are reserved in JavaScript and will be renamed to pb_name. 66 | * Optional fields that are not set will be set to undefined. 67 | * To access a reserved field use, foo.pb_, eg, foo.pb_default. 68 | * For the list of reserved names please see: 69 | * net/proto2/compiler/js/internal/generator.cc#kKeyword. 70 | * @param {boolean=} opt_includeInstance Deprecated. whether to include the 71 | * JSPB instance for transitional soy proto support: 72 | * http://goto/soy-param-migration 73 | * @return {!Object} 74 | */ 75 | proto.substrateapi.SubstrateRawTxIn.prototype.toObject = function(opt_includeInstance) { 76 | return proto.substrateapi.SubstrateRawTxIn.toObject(opt_includeInstance, this); 77 | }; 78 | 79 | 80 | /** 81 | * Static version of the {@see toObject} method. 82 | * @param {boolean|undefined} includeInstance Deprecated. Whether to include 83 | * the JSPB instance for transitional soy proto support: 84 | * http://goto/soy-param-migration 85 | * @param {!proto.substrateapi.SubstrateRawTxIn} msg The msg instance to transform. 86 | * @return {!Object} 87 | * @suppress {unusedLocalVariables} f is only used for nested messages 88 | */ 89 | proto.substrateapi.SubstrateRawTxIn.toObject = function(includeInstance, msg) { 90 | var f, obj = { 91 | rawdata: jspb.Message.getFieldWithDefault(msg, 1, "") 92 | }; 93 | 94 | if (includeInstance) { 95 | obj.$jspbMessageInstance = msg; 96 | } 97 | return obj; 98 | }; 99 | } 100 | 101 | 102 | /** 103 | * Deserializes binary data (in protobuf wire format). 104 | * @param {jspb.ByteSource} bytes The bytes to deserialize. 105 | * @return {!proto.substrateapi.SubstrateRawTxIn} 106 | */ 107 | proto.substrateapi.SubstrateRawTxIn.deserializeBinary = function(bytes) { 108 | var reader = new jspb.BinaryReader(bytes); 109 | var msg = new proto.substrateapi.SubstrateRawTxIn; 110 | return proto.substrateapi.SubstrateRawTxIn.deserializeBinaryFromReader(msg, reader); 111 | }; 112 | 113 | 114 | /** 115 | * Deserializes binary data (in protobuf wire format) from the 116 | * given reader into the given message object. 117 | * @param {!proto.substrateapi.SubstrateRawTxIn} msg The message object to deserialize into. 118 | * @param {!jspb.BinaryReader} reader The BinaryReader to use. 119 | * @return {!proto.substrateapi.SubstrateRawTxIn} 120 | */ 121 | proto.substrateapi.SubstrateRawTxIn.deserializeBinaryFromReader = function(msg, reader) { 122 | while (reader.nextField()) { 123 | if (reader.isEndGroup()) { 124 | break; 125 | } 126 | var field = reader.getFieldNumber(); 127 | switch (field) { 128 | case 1: 129 | var value = /** @type {string} */ (reader.readString()); 130 | msg.setRawdata(value); 131 | break; 132 | default: 133 | reader.skipField(); 134 | break; 135 | } 136 | } 137 | return msg; 138 | }; 139 | 140 | 141 | /** 142 | * Serializes the message to binary data (in protobuf wire format). 143 | * @return {!Uint8Array} 144 | */ 145 | proto.substrateapi.SubstrateRawTxIn.prototype.serializeBinary = function() { 146 | var writer = new jspb.BinaryWriter(); 147 | proto.substrateapi.SubstrateRawTxIn.serializeBinaryToWriter(this, writer); 148 | return writer.getResultBuffer(); 149 | }; 150 | 151 | 152 | /** 153 | * Serializes the given message to binary data (in protobuf wire 154 | * format), writing to the given BinaryWriter. 155 | * @param {!proto.substrateapi.SubstrateRawTxIn} message 156 | * @param {!jspb.BinaryWriter} writer 157 | * @suppress {unusedLocalVariables} f is only used for nested messages 158 | */ 159 | proto.substrateapi.SubstrateRawTxIn.serializeBinaryToWriter = function(message, writer) { 160 | var f = undefined; 161 | f = message.getRawdata(); 162 | if (f.length > 0) { 163 | writer.writeString( 164 | 1, 165 | f 166 | ); 167 | } 168 | }; 169 | 170 | 171 | /** 172 | * optional string rawData = 1; 173 | * @return {string} 174 | */ 175 | proto.substrateapi.SubstrateRawTxIn.prototype.getRawdata = function() { 176 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); 177 | }; 178 | 179 | 180 | /** 181 | * @param {string} value 182 | * @return {!proto.substrateapi.SubstrateRawTxIn} returns this 183 | */ 184 | proto.substrateapi.SubstrateRawTxIn.prototype.setRawdata = function(value) { 185 | return jspb.Message.setProto3StringField(this, 1, value); 186 | }; 187 | 188 | 189 | 190 | 191 | 192 | if (jspb.Message.GENERATE_TO_OBJECT) { 193 | /** 194 | * Creates an object representation of this proto. 195 | * Field names that are reserved in JavaScript and will be renamed to pb_name. 196 | * Optional fields that are not set will be set to undefined. 197 | * To access a reserved field use, foo.pb_, eg, foo.pb_default. 198 | * For the list of reserved names please see: 199 | * net/proto2/compiler/js/internal/generator.cc#kKeyword. 200 | * @param {boolean=} opt_includeInstance Deprecated. whether to include the 201 | * JSPB instance for transitional soy proto support: 202 | * http://goto/soy-param-migration 203 | * @return {!Object} 204 | */ 205 | proto.substrateapi.SubstrateTxOut.prototype.toObject = function(opt_includeInstance) { 206 | return proto.substrateapi.SubstrateTxOut.toObject(opt_includeInstance, this); 207 | }; 208 | 209 | 210 | /** 211 | * Static version of the {@see toObject} method. 212 | * @param {boolean|undefined} includeInstance Deprecated. Whether to include 213 | * the JSPB instance for transitional soy proto support: 214 | * http://goto/soy-param-migration 215 | * @param {!proto.substrateapi.SubstrateTxOut} msg The msg instance to transform. 216 | * @return {!Object} 217 | * @suppress {unusedLocalVariables} f is only used for nested messages 218 | */ 219 | proto.substrateapi.SubstrateTxOut.toObject = function(includeInstance, msg) { 220 | var f, obj = { 221 | signature: jspb.Message.getFieldWithDefault(msg, 1, "") 222 | }; 223 | 224 | if (includeInstance) { 225 | obj.$jspbMessageInstance = msg; 226 | } 227 | return obj; 228 | }; 229 | } 230 | 231 | 232 | /** 233 | * Deserializes binary data (in protobuf wire format). 234 | * @param {jspb.ByteSource} bytes The bytes to deserialize. 235 | * @return {!proto.substrateapi.SubstrateTxOut} 236 | */ 237 | proto.substrateapi.SubstrateTxOut.deserializeBinary = function(bytes) { 238 | var reader = new jspb.BinaryReader(bytes); 239 | var msg = new proto.substrateapi.SubstrateTxOut; 240 | return proto.substrateapi.SubstrateTxOut.deserializeBinaryFromReader(msg, reader); 241 | }; 242 | 243 | 244 | /** 245 | * Deserializes binary data (in protobuf wire format) from the 246 | * given reader into the given message object. 247 | * @param {!proto.substrateapi.SubstrateTxOut} msg The message object to deserialize into. 248 | * @param {!jspb.BinaryReader} reader The BinaryReader to use. 249 | * @return {!proto.substrateapi.SubstrateTxOut} 250 | */ 251 | proto.substrateapi.SubstrateTxOut.deserializeBinaryFromReader = function(msg, reader) { 252 | while (reader.nextField()) { 253 | if (reader.isEndGroup()) { 254 | break; 255 | } 256 | var field = reader.getFieldNumber(); 257 | switch (field) { 258 | case 1: 259 | var value = /** @type {string} */ (reader.readString()); 260 | msg.setSignature(value); 261 | break; 262 | default: 263 | reader.skipField(); 264 | break; 265 | } 266 | } 267 | return msg; 268 | }; 269 | 270 | 271 | /** 272 | * Serializes the message to binary data (in protobuf wire format). 273 | * @return {!Uint8Array} 274 | */ 275 | proto.substrateapi.SubstrateTxOut.prototype.serializeBinary = function() { 276 | var writer = new jspb.BinaryWriter(); 277 | proto.substrateapi.SubstrateTxOut.serializeBinaryToWriter(this, writer); 278 | return writer.getResultBuffer(); 279 | }; 280 | 281 | 282 | /** 283 | * Serializes the given message to binary data (in protobuf wire 284 | * format), writing to the given BinaryWriter. 285 | * @param {!proto.substrateapi.SubstrateTxOut} message 286 | * @param {!jspb.BinaryWriter} writer 287 | * @suppress {unusedLocalVariables} f is only used for nested messages 288 | */ 289 | proto.substrateapi.SubstrateTxOut.serializeBinaryToWriter = function(message, writer) { 290 | var f = undefined; 291 | f = message.getSignature(); 292 | if (f.length > 0) { 293 | writer.writeString( 294 | 1, 295 | f 296 | ); 297 | } 298 | }; 299 | 300 | 301 | /** 302 | * optional string signature = 1; 303 | * @return {string} 304 | */ 305 | proto.substrateapi.SubstrateTxOut.prototype.getSignature = function() { 306 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); 307 | }; 308 | 309 | 310 | /** 311 | * @param {string} value 312 | * @return {!proto.substrateapi.SubstrateTxOut} returns this 313 | */ 314 | proto.substrateapi.SubstrateTxOut.prototype.setSignature = function(value) { 315 | return jspb.Message.setProto3StringField(this, 1, value); 316 | }; 317 | 318 | 319 | goog.object.extend(exports, proto.substrateapi); 320 | -------------------------------------------------------------------------------- /src/renderer/views/welcomeHome.vue: -------------------------------------------------------------------------------- 1 | 64 | 65 | 87 | 88 | 209 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/proto/common_pb.js: -------------------------------------------------------------------------------- 1 | // source: common.proto 2 | /** 3 | * @fileoverview 4 | * @enhanceable 5 | * @suppress {messageConventions} JS Compiler reports an error if a variable or 6 | * field starts with 'MSG_' and isn't a translatable message. 7 | * @public 8 | */ 9 | // GENERATED CODE -- DO NOT EDIT! 10 | 11 | var jspb = require('google-protobuf'); 12 | var goog = jspb; 13 | var global = Function('return this')(); 14 | 15 | var google_protobuf_any_pb = require('google-protobuf/google/protobuf/any_pb.js'); 16 | goog.object.extend(proto, google_protobuf_any_pb); 17 | goog.exportSymbol('proto.common.SignParam', null, global); 18 | /** 19 | * Generated by JsPbCodeGenerator. 20 | * @param {Array=} opt_data Optional initial data array, typically from a 21 | * server response, or constructed directly in Javascript. The array is used 22 | * in place and becomes part of the constructed object. It is not cloned. 23 | * If no data is provided, the constructed object will be empty, but still 24 | * valid. 25 | * @extends {jspb.Message} 26 | * @constructor 27 | */ 28 | proto.common.SignParam = function(opt_data) { 29 | jspb.Message.initialize(this, opt_data, 0, -1, null, null); 30 | }; 31 | goog.inherits(proto.common.SignParam, jspb.Message); 32 | if (goog.DEBUG && !COMPILED) { 33 | /** 34 | * @public 35 | * @override 36 | */ 37 | proto.common.SignParam.displayName = 'proto.common.SignParam'; 38 | } 39 | 40 | 41 | 42 | if (jspb.Message.GENERATE_TO_OBJECT) { 43 | /** 44 | * Creates an object representation of this proto. 45 | * Field names that are reserved in JavaScript and will be renamed to pb_name. 46 | * Optional fields that are not set will be set to undefined. 47 | * To access a reserved field use, foo.pb_, eg, foo.pb_default. 48 | * For the list of reserved names please see: 49 | * net/proto2/compiler/js/internal/generator.cc#kKeyword. 50 | * @param {boolean=} opt_includeInstance Deprecated. whether to include the 51 | * JSPB instance for transitional soy proto support: 52 | * http://goto/soy-param-migration 53 | * @return {!Object} 54 | */ 55 | proto.common.SignParam.prototype.toObject = function(opt_includeInstance) { 56 | return proto.common.SignParam.toObject(opt_includeInstance, this); 57 | }; 58 | 59 | 60 | /** 61 | * Static version of the {@see toObject} method. 62 | * @param {boolean|undefined} includeInstance Deprecated. Whether to include 63 | * the JSPB instance for transitional soy proto support: 64 | * http://goto/soy-param-migration 65 | * @param {!proto.common.SignParam} msg The msg instance to transform. 66 | * @return {!Object} 67 | * @suppress {unusedLocalVariables} f is only used for nested messages 68 | */ 69 | proto.common.SignParam.toObject = function(includeInstance, msg) { 70 | var f, obj = { 71 | chaintype: jspb.Message.getFieldWithDefault(msg, 1, ""), 72 | path: jspb.Message.getFieldWithDefault(msg, 2, ""), 73 | network: jspb.Message.getFieldWithDefault(msg, 3, ""), 74 | input: (f = msg.getInput()) && google_protobuf_any_pb.Any.toObject(includeInstance, f), 75 | payment: jspb.Message.getFieldWithDefault(msg, 5, ""), 76 | receiver: jspb.Message.getFieldWithDefault(msg, 6, ""), 77 | sender: jspb.Message.getFieldWithDefault(msg, 7, ""), 78 | fee: jspb.Message.getFieldWithDefault(msg, 8, "") 79 | }; 80 | 81 | if (includeInstance) { 82 | obj.$jspbMessageInstance = msg; 83 | } 84 | return obj; 85 | }; 86 | } 87 | 88 | 89 | /** 90 | * Deserializes binary data (in protobuf wire format). 91 | * @param {jspb.ByteSource} bytes The bytes to deserialize. 92 | * @return {!proto.common.SignParam} 93 | */ 94 | proto.common.SignParam.deserializeBinary = function(bytes) { 95 | var reader = new jspb.BinaryReader(bytes); 96 | var msg = new proto.common.SignParam; 97 | return proto.common.SignParam.deserializeBinaryFromReader(msg, reader); 98 | }; 99 | 100 | 101 | /** 102 | * Deserializes binary data (in protobuf wire format) from the 103 | * given reader into the given message object. 104 | * @param {!proto.common.SignParam} msg The message object to deserialize into. 105 | * @param {!jspb.BinaryReader} reader The BinaryReader to use. 106 | * @return {!proto.common.SignParam} 107 | */ 108 | proto.common.SignParam.deserializeBinaryFromReader = function(msg, reader) { 109 | while (reader.nextField()) { 110 | if (reader.isEndGroup()) { 111 | break; 112 | } 113 | var field = reader.getFieldNumber(); 114 | switch (field) { 115 | case 1: 116 | var value = /** @type {string} */ (reader.readString()); 117 | msg.setChaintype(value); 118 | break; 119 | case 2: 120 | var value = /** @type {string} */ (reader.readString()); 121 | msg.setPath(value); 122 | break; 123 | case 3: 124 | var value = /** @type {string} */ (reader.readString()); 125 | msg.setNetwork(value); 126 | break; 127 | case 4: 128 | var value = new google_protobuf_any_pb.Any; 129 | reader.readMessage(value,google_protobuf_any_pb.Any.deserializeBinaryFromReader); 130 | msg.setInput(value); 131 | break; 132 | case 5: 133 | var value = /** @type {string} */ (reader.readString()); 134 | msg.setPayment(value); 135 | break; 136 | case 6: 137 | var value = /** @type {string} */ (reader.readString()); 138 | msg.setReceiver(value); 139 | break; 140 | case 7: 141 | var value = /** @type {string} */ (reader.readString()); 142 | msg.setSender(value); 143 | break; 144 | case 8: 145 | var value = /** @type {string} */ (reader.readString()); 146 | msg.setFee(value); 147 | break; 148 | default: 149 | reader.skipField(); 150 | break; 151 | } 152 | } 153 | return msg; 154 | }; 155 | 156 | 157 | /** 158 | * Serializes the message to binary data (in protobuf wire format). 159 | * @return {!Uint8Array} 160 | */ 161 | proto.common.SignParam.prototype.serializeBinary = function() { 162 | var writer = new jspb.BinaryWriter(); 163 | proto.common.SignParam.serializeBinaryToWriter(this, writer); 164 | return writer.getResultBuffer(); 165 | }; 166 | 167 | 168 | /** 169 | * Serializes the given message to binary data (in protobuf wire 170 | * format), writing to the given BinaryWriter. 171 | * @param {!proto.common.SignParam} message 172 | * @param {!jspb.BinaryWriter} writer 173 | * @suppress {unusedLocalVariables} f is only used for nested messages 174 | */ 175 | proto.common.SignParam.serializeBinaryToWriter = function(message, writer) { 176 | var f = undefined; 177 | f = message.getChaintype(); 178 | if (f.length > 0) { 179 | writer.writeString( 180 | 1, 181 | f 182 | ); 183 | } 184 | f = message.getPath(); 185 | if (f.length > 0) { 186 | writer.writeString( 187 | 2, 188 | f 189 | ); 190 | } 191 | f = message.getNetwork(); 192 | if (f.length > 0) { 193 | writer.writeString( 194 | 3, 195 | f 196 | ); 197 | } 198 | f = message.getInput(); 199 | if (f != null) { 200 | writer.writeMessage( 201 | 4, 202 | f, 203 | google_protobuf_any_pb.Any.serializeBinaryToWriter 204 | ); 205 | } 206 | f = message.getPayment(); 207 | if (f.length > 0) { 208 | writer.writeString( 209 | 5, 210 | f 211 | ); 212 | } 213 | f = message.getReceiver(); 214 | if (f.length > 0) { 215 | writer.writeString( 216 | 6, 217 | f 218 | ); 219 | } 220 | f = message.getSender(); 221 | if (f.length > 0) { 222 | writer.writeString( 223 | 7, 224 | f 225 | ); 226 | } 227 | f = message.getFee(); 228 | if (f.length > 0) { 229 | writer.writeString( 230 | 8, 231 | f 232 | ); 233 | } 234 | }; 235 | 236 | 237 | /** 238 | * optional string chainType = 1; 239 | * @return {string} 240 | */ 241 | proto.common.SignParam.prototype.getChaintype = function() { 242 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); 243 | }; 244 | 245 | 246 | /** 247 | * @param {string} value 248 | * @return {!proto.common.SignParam} returns this 249 | */ 250 | proto.common.SignParam.prototype.setChaintype = function(value) { 251 | return jspb.Message.setProto3StringField(this, 1, value); 252 | }; 253 | 254 | 255 | /** 256 | * optional string path = 2; 257 | * @return {string} 258 | */ 259 | proto.common.SignParam.prototype.getPath = function() { 260 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); 261 | }; 262 | 263 | 264 | /** 265 | * @param {string} value 266 | * @return {!proto.common.SignParam} returns this 267 | */ 268 | proto.common.SignParam.prototype.setPath = function(value) { 269 | return jspb.Message.setProto3StringField(this, 2, value); 270 | }; 271 | 272 | 273 | /** 274 | * optional string network = 3; 275 | * @return {string} 276 | */ 277 | proto.common.SignParam.prototype.getNetwork = function() { 278 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, "")); 279 | }; 280 | 281 | 282 | /** 283 | * @param {string} value 284 | * @return {!proto.common.SignParam} returns this 285 | */ 286 | proto.common.SignParam.prototype.setNetwork = function(value) { 287 | return jspb.Message.setProto3StringField(this, 3, value); 288 | }; 289 | 290 | 291 | /** 292 | * optional google.protobuf.Any input = 4; 293 | * @return {?proto.google.protobuf.Any} 294 | */ 295 | proto.common.SignParam.prototype.getInput = function() { 296 | return /** @type{?proto.google.protobuf.Any} */ ( 297 | jspb.Message.getWrapperField(this, google_protobuf_any_pb.Any, 4)); 298 | }; 299 | 300 | 301 | /** 302 | * @param {?proto.google.protobuf.Any|undefined} value 303 | * @return {!proto.common.SignParam} returns this 304 | */ 305 | proto.common.SignParam.prototype.setInput = function(value) { 306 | return jspb.Message.setWrapperField(this, 4, value); 307 | }; 308 | 309 | 310 | /** 311 | * Clears the message field making it undefined. 312 | * @return {!proto.common.SignParam} returns this 313 | */ 314 | proto.common.SignParam.prototype.clearInput = function() { 315 | return this.setInput(undefined); 316 | }; 317 | 318 | 319 | /** 320 | * Returns whether this field is set. 321 | * @return {boolean} 322 | */ 323 | proto.common.SignParam.prototype.hasInput = function() { 324 | return jspb.Message.getField(this, 4) != null; 325 | }; 326 | 327 | 328 | /** 329 | * optional string payment = 5; 330 | * @return {string} 331 | */ 332 | proto.common.SignParam.prototype.getPayment = function() { 333 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 5, "")); 334 | }; 335 | 336 | 337 | /** 338 | * @param {string} value 339 | * @return {!proto.common.SignParam} returns this 340 | */ 341 | proto.common.SignParam.prototype.setPayment = function(value) { 342 | return jspb.Message.setProto3StringField(this, 5, value); 343 | }; 344 | 345 | 346 | /** 347 | * optional string receiver = 6; 348 | * @return {string} 349 | */ 350 | proto.common.SignParam.prototype.getReceiver = function() { 351 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 6, "")); 352 | }; 353 | 354 | 355 | /** 356 | * @param {string} value 357 | * @return {!proto.common.SignParam} returns this 358 | */ 359 | proto.common.SignParam.prototype.setReceiver = function(value) { 360 | return jspb.Message.setProto3StringField(this, 6, value); 361 | }; 362 | 363 | 364 | /** 365 | * optional string sender = 7; 366 | * @return {string} 367 | */ 368 | proto.common.SignParam.prototype.getSender = function() { 369 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 7, "")); 370 | }; 371 | 372 | 373 | /** 374 | * @param {string} value 375 | * @return {!proto.common.SignParam} returns this 376 | */ 377 | proto.common.SignParam.prototype.setSender = function(value) { 378 | return jspb.Message.setProto3StringField(this, 7, value); 379 | }; 380 | 381 | 382 | /** 383 | * optional string fee = 8; 384 | * @return {string} 385 | */ 386 | proto.common.SignParam.prototype.getFee = function() { 387 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 8, "")); 388 | }; 389 | 390 | 391 | /** 392 | * @param {string} value 393 | * @return {!proto.common.SignParam} returns this 394 | */ 395 | proto.common.SignParam.prototype.setFee = function(value) { 396 | return jspb.Message.setProto3StringField(this, 8, value); 397 | }; 398 | 399 | 400 | goog.object.extend(exports, proto.common); 401 | -------------------------------------------------------------------------------- /src/api/ethereumdapp.js: -------------------------------------------------------------------------------- 1 | // const KOVAN_RPC_URL = 'https://kovan.infura.io' 2 | // const ETHEREUM_MAIN_NET = 'https://kovan.infura.io' 3 | 4 | const { contextBridge, ipcRenderer } = require('electron') 5 | 6 | // Expose protected methods that allow the renderer process to use 7 | // the ipcRenderer without exposing the entire object 8 | contextBridge.exposeInMainWorld( 9 | 'imToken', 10 | { 11 | callPromisifyAPI: async (data) => { 12 | console.log('call Native Api before ipcRender') 13 | return await ipcRenderer.invoke('imkey-api', data) 14 | } 15 | } 16 | ) 17 | /** 18 | * EIP 1102 and EIP 1193 19 | */ 20 | 21 | const createEthereumProviderScript = function () { 22 | const imToken = window.top.imToken 23 | 24 | if (window.__inTokenSDKInjecked) return 25 | 26 | function createJsonrpcResponse (payload, result) { 27 | const response = payload 28 | response.result = result 29 | return response 30 | } 31 | 32 | // function createJsonrpcError (payload, error) { 33 | // const response = payload 34 | // response.error = error 35 | // response.result = null 36 | // return response 37 | // } 38 | 39 | function inherits (ctor, superCtor) { 40 | ctor.super_ = superCtor 41 | ctor.prototype = Object.create(superCtor.prototype, { 42 | constructor: { 43 | value: ctor, 44 | enumerable: false, 45 | writable: true, 46 | configurable: true 47 | } 48 | }) 49 | } 50 | 51 | // only alert one time 52 | _alerted = false 53 | 54 | function _showEnableTip () { 55 | // !_alerted && imToken.callAPI('native.toastInfo', 'you should call ethereum.enable() first to access web3.eth.defaultAccount and web3.eth.accounts') 56 | // _alerted = true 57 | } 58 | 59 | let accounts = __accounts__ 60 | 61 | function EthereumProvider () { 62 | EventEmitter.call(this) 63 | /** 64 | * https://metamask.github.io/metamask-docs/API_Reference/Ethereum_Provider#properties 65 | */ 66 | this.networkVersion = __netVersion__ 67 | this.chainId = __chainId__ 68 | this.selectedAddress = accounts[0] 69 | this.isImToken = true 70 | this._isConnected = true 71 | this._nextJsonrpcId = 10000 72 | 73 | this._connect() 74 | } 75 | 76 | /** 77 | * https://github.com/ethereum/web3.js/blob/v1.0.0-beta.11/packages/web3-core-requestmanager/src/givenProvider.js#L38-L40 78 | * Note: We must use prototype inherit here, cannot use ES6 class extends, otherwise sendAsync method will be delete in web3.js 1.0 version 79 | */ 80 | inherits(EthereumProvider, EventEmitter) 81 | 82 | // https://github.com/ensdomains/ens-app/pull/296 83 | EthereumProvider.prototype.supportsSubscriptions = () => false 84 | 85 | EthereumProvider.prototype.enable = function (payload) { 86 | if (imToken.addressAccessDenied) { 87 | return Promise.reject(new Error('user_canceled')) 88 | } 89 | return imToken.callPromisifyAPI('ethereum.enable', payload).then(_accounts => { 90 | accounts = _accounts 91 | window.ethereum.selectedAddress = accounts[0] 92 | return _accounts 93 | }).catch(err => { 94 | imToken.addressAccessDenied = true 95 | throw err 96 | }) 97 | } 98 | 99 | EthereumProvider.prototype._connect = function () { 100 | this.emit('connect', { 101 | chainId: __chainId__ 102 | }) 103 | this.once('close', this._connect.bind(this)) 104 | } 105 | 106 | EthereumProvider.prototype.isConnected = function () { 107 | return this._isConnected 108 | } 109 | 110 | /** 111 | * Events 112 | */ 113 | EthereumProvider.prototype._emitNotification = function (result) { 114 | this.emit('notification', result) 115 | } 116 | 117 | EthereumProvider.prototype._emitConnect = function () { 118 | this._isConnected = true 119 | this.emit('connect') 120 | } 121 | 122 | EthereumProvider.prototype._emitClose = function (code, reason) { 123 | this._isConnected = false 124 | this.emit('close', code, reason) 125 | } 126 | 127 | EthereumProvider.prototype._emitNetworkChanged = function (networkId) { 128 | this.emit('networkChanged', networkId) 129 | } 130 | 131 | EthereumProvider.prototype._emitAccountsChanged = function (accounts) { 132 | this.emit('accountsChanged', accounts) 133 | } 134 | 135 | /** 136 | * EIP1103 send 137 | * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1193.md#send-1 138 | */ 139 | EthereumProvider.prototype._eip1193Send = function (method, params) { 140 | const id = this._nextJsonrpcId++ 141 | 142 | const payload = { 143 | jsonrpc: '2.0', 144 | id, 145 | method, 146 | params 147 | } 148 | 149 | if (payload.method === 'eth_requestAccounts') { 150 | return this.enable() 151 | } 152 | 153 | // https://tower.im/teams/349901/todos/16653/ 154 | if (payload.method === 'eth_subscribe') { 155 | // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1193.md#error-object-and-codes 156 | // https://github.com/ethereum/web3.js/blob/a5a739b7d1834b53b7268c57f791db5d07b720da/packages/web3-core-method/src/index.js#L559-L560 157 | // https://github.com/ethereum/web3.js/issues/3669 158 | const error = new Error('eth_subscribe is not supported') 159 | error.code = 4200 160 | return Promise.reject(error) 161 | } 162 | 163 | return imToken.callPromisifyAPI('ethereum.sendAsync', payload) 164 | .then(response => { 165 | /** 166 | * return jsonrpc result.result 167 | */ 168 | if (response.jsonrpc && response.hasOwnProperty('result')) { 169 | return response.result 170 | } 171 | 172 | if (!response.id) { 173 | if (response.method && response.method.indexOf('_subscription') > -1) { 174 | // Emit subscription notification 175 | this._emitNotification(message.params) 176 | } 177 | } 178 | 179 | return response 180 | }) 181 | } 182 | 183 | /** 184 | * EIP1193 request 185 | */ 186 | EthereumProvider.prototype.request = function (payload) { 187 | /** 188 | * because a web3.js bug https://github.com/ethereum/web3.js/pull/3649#issuecomment-695200876 189 | * we can't use `this._eip1193Send` here. 190 | * use `prototype` instead `this` can't solve this miss of `this` context. 191 | * it only fixed the problem that can't found `this._eip1193Send` method. 192 | * luckly we didn't use any `this` inside the `_eip1193Send` method. 193 | */ 194 | return EthereumProvider.prototype._eip1193Send(payload.method, payload.params) 195 | } 196 | 197 | /** 198 | * case1: eip1193 send 199 | * case2: web3.js 0.1x send 200 | * case3 web3.js 1.x send 201 | */ 202 | EthereumProvider.prototype.send = function (method, params) { 203 | return this._send(method, params) 204 | } 205 | 206 | EthereumProvider.prototype.sendAsync = function (payload, callback) { 207 | return this._sendAsync(payload, callback) 208 | } 209 | 210 | EthereumProvider.prototype._send = function (method, params) { 211 | if (!method) { 212 | return new Error('method is null') 213 | } 214 | 215 | /** 216 | * web3 httpProvider sync send, need return jsonrpc response 217 | */ 218 | if (typeof method === 'object' && typeof method.method === 'string') { 219 | const payload = method 220 | cb = params 221 | 222 | /** 223 | * treat as web3 sendAsync, return by callback 224 | */ 225 | if (typeof cb === 'function') { 226 | return this._sendAsync(payload, cb) 227 | } else { 228 | /** 229 | * return result 230 | */ 231 | return this._sendSync(payload) 232 | } 233 | } else if (typeof method === 'string') { 234 | return this._eip1193Send(method, params) 235 | } 236 | } 237 | 238 | /** 239 | * web3.js Provider send Backwards Compatibility 240 | */ 241 | EthereumProvider.prototype._sendSync = function (payload) { 242 | switch (payload.method) { 243 | case 'net_version': 244 | return createJsonrpcResponse(payload, __netVersion__) 245 | case 'eth_chainId': 246 | return createJsonrpcResponse(payload, __chainId__) 247 | 248 | case 'eth_accounts': 249 | if (!accounts[0]) { 250 | _showEnableTip() 251 | } 252 | return createJsonrpcResponse(payload, accounts) 253 | 254 | case 'eth_uninstallFilter': 255 | this._sendAsync(payload) 256 | return createJsonrpcResponse(payload, true) 257 | 258 | case 'eth_coinbase': 259 | return createJsonrpcResponse(payload, accounts[0]) 260 | default: 261 | console.error('The imToken Web3 object does not support synchronous methods like ' + payload.method + ' without a callback parameter.') 262 | return this._eip1193Send(payload.method, payload.params) 263 | } 264 | } 265 | 266 | /** 267 | * web3.js Provider sendAsync Backwards Compatibility 268 | */ 269 | EthereumProvider.prototype._sendAsync = function (payload, callback) { 270 | if (imToken.addressAccessDenied) { 271 | return Promise.reject(new Error('user_canceled')) 272 | } 273 | /** 274 | * https://github.com/ethereum/web3.js/blob/v1.0.0-beta.11/packages/web3-core-requestmanager/src/givenProvider.js#L38-L40 275 | * cause web3.js 1.0 delete sendAsync and set sendAsync to send, this is incompatible with EIP1193 276 | */ 277 | if (typeof payload === 'string') { 278 | return this._eip1193Send(payload, callback) 279 | } 280 | 281 | // for batch payload 282 | if (Array.isArray(payload)) { 283 | return imToken.callPromisifyAPI('ethereum.sendAsync', payload) 284 | .then((response) => { 285 | return callback(null, response) 286 | }).catch((err) => { 287 | callback(err, null) 288 | console.error('Error from EthereumProvider sendAsync ' + JSON.stringify(payload) + ': ' + (err.message || err.toString())) 289 | }) 290 | } 291 | 292 | /** 293 | * normal web3 sendAsync 294 | * payload: {method, params, id, jsonrpc} 295 | */ 296 | return this._eip1193Send(payload.method, payload.params).then(r => { 297 | callback(null, createJsonrpcResponse(payload, r)) 298 | }).catch(err => { 299 | const approveMethods = ['eth_requestAccounts', 'personal_listAccounts', 'eth_accounts'] 300 | if (approveMethods.indexOf(payload.method) !== -1) { 301 | imToken.addressAccessDenied = true 302 | } 303 | callback(err, null) 304 | console.error('Error from EthereumProvider sendAsync ' + JSON.stringify(payload) + ': ' + (err.message || err.toString())) 305 | }) 306 | } 307 | 308 | const web3EthProxyHandler = { 309 | get: function (obj, prop) { 310 | switch (prop) { 311 | case 'defaultAccount': 312 | if (accounts[0]) { 313 | return accounts[0] 314 | } else { 315 | _showEnableTip() 316 | return undefined 317 | } 318 | case 'accounts': 319 | // https://web3js.readthedocs.io/en/v1.2.0/web3-eth-accounts.html 320 | if (typeof obj[prop] === 'object' && obj[prop].create) { 321 | return obj[prop] 322 | // https://github.com/ethereum/wiki/wiki/JavaScript-API#web3ethaccounts 323 | } else { 324 | if (accounts[0]) { 325 | return accounts 326 | } else { 327 | _showEnableTip() 328 | return [] 329 | } 330 | } 331 | default: 332 | return obj[prop] 333 | } 334 | } 335 | } 336 | 337 | // https://github.com/ethereum/web3.js/blob/v1.2.6/packages/web3-core-requestmanager/src/givenProvider.js#L36 338 | window.ethereum = window.ethereumProvider = new EthereumProvider() 339 | 340 | // inject web3 as default 341 | if (typeof Web3 !== 'undefined') { 342 | window.web3 = new Web3(window.ethereum) 343 | 344 | window.web3.eth.defaultAccount = accounts[0] 345 | 346 | // for web3.js 1.0 givenProvider 347 | window.web3.givenProvider = window.ethereum 348 | window.web3.eth.givenProvider = window.ethereum 349 | 350 | const _oldWe3Eth = window.web3.eth 351 | window.web3.eth = new Proxy(_oldWe3Eth, web3EthProxyHandler) 352 | } 353 | 354 | window.__inTokenSDKInjecked = true 355 | } 356 | 357 | export default (dappUrl, needInjectWeb3) => { 358 | const isApproved = needInjectWeb3 || selectIsApproved(dappUrl) 359 | 360 | const _accounts = isApproved ? getEthereumAccounts() : [] 361 | // https://eips.ethereum.org/EIPS/eip-695 hex string 362 | const _chainId = `0x${parseInt(16)}` 363 | // number 364 | const _netVersion = parseInt(10) 365 | 366 | const template = `;(${createEthereumProviderScript.toString()}());` 367 | return template 368 | .replace(/__accounts__/g, JSON.stringify(_accounts)) 369 | .replace(/__netVersion__/g, JSON.stringify(_netVersion)) 370 | .replace(/__chainId__/g, JSON.stringify(_chainId)) 371 | } 372 | -------------------------------------------------------------------------------- /test/e2e/specs/api.spec.js: -------------------------------------------------------------------------------- 1 | // Require the dev-dependencies 2 | const chai = require('chai') 3 | const chaiHttp = require('chai-http') 4 | const fs = require('fs') 5 | const path = require('path') 6 | const testJsonPath = path.resolve('./test/e2e/specs/test.json') 7 | chai.use(chaiHttp) 8 | 9 | describe('Api', () => { 10 | const testObj = readFileToJsonObj(testJsonPath) 11 | const testJsonArr = [] 12 | for (const json in testObj) { 13 | testJsonArr.push(json) 14 | console.log('json:' + json) 15 | } 16 | it('test ' + testJsonArr[0], (done) => { 17 | const reqJson = testObj[testJsonArr[0]] 18 | console.log('request:' + JSON.stringify(reqJson)) 19 | chai.request('127.0.0.1:8081') 20 | .post('/api/imKey') 21 | .send(reqJson) 22 | .end((err, res) => { 23 | if (err) { 24 | console.log(err.stack) 25 | } 26 | console.log('response:' + JSON.stringify(res.body)) 27 | expect(res.status).to.equal(200) 28 | expect(JSON.stringify(res.body.result)).to.equal(JSON.stringify(testObj[testJsonArr[1]].result)) 29 | done() 30 | }) 31 | }).timeout(30000) 32 | 33 | it('test ' + testJsonArr[2], (done) => { 34 | const reqJson = testObj[testJsonArr[2]] 35 | console.log('request:' + JSON.stringify(reqJson)) 36 | chai.request('127.0.0.1:8081') 37 | .post('/api/imKey') 38 | .send(reqJson) 39 | .end((err, res) => { 40 | if (err) { 41 | console.log(err.stack) 42 | } 43 | console.log('response:' + JSON.stringify(res.body)) 44 | expect(res.status).to.equal(200) 45 | expect(JSON.stringify(res.body.result)).to.equal(JSON.stringify(testObj[testJsonArr[3]].result)) 46 | done() 47 | }) 48 | }).timeout(30000) 49 | it('test ' + testJsonArr[4], (done) => { 50 | const reqJson = testObj[testJsonArr[4]] 51 | console.log('request:' + JSON.stringify(reqJson)) 52 | chai.request('127.0.0.1:8081') 53 | .post('/api/imKey') 54 | .send(reqJson) 55 | .end((err, res) => { 56 | if (err) { 57 | console.log(err.stack) 58 | } 59 | console.log('response:' + JSON.stringify(res.body)) 60 | expect(res.status).to.equal(200) 61 | expect(JSON.stringify(res.body.result)).to.equal(JSON.stringify(testObj[testJsonArr[5]].result)) 62 | done() 63 | }) 64 | }).timeout(30000) 65 | it('test ' + testJsonArr[6], (done) => { 66 | const reqJson = testObj[testJsonArr[6]] 67 | console.log('request:' + JSON.stringify(reqJson)) 68 | chai.request('127.0.0.1:8081') 69 | .post('/api/imKey') 70 | .send(reqJson) 71 | .end((err, res) => { 72 | if (err) { 73 | console.log(err.stack) 74 | } 75 | console.log('response:' + JSON.stringify(res.body)) 76 | expect(res.status).to.equal(200) 77 | expect(JSON.stringify(res.body.result)).to.equal(JSON.stringify(testObj[testJsonArr[7]].result)) 78 | done() 79 | }) 80 | }).timeout(30000) 81 | it('test ' + testJsonArr[8], (done) => { 82 | const reqJson = testObj[testJsonArr[8]] 83 | console.log('request:' + JSON.stringify(reqJson)) 84 | chai.request('127.0.0.1:8081') 85 | .post('/api/imKey') 86 | .send(reqJson) 87 | .end((err, res) => { 88 | if (err) { 89 | console.log(err.stack) 90 | } 91 | console.log('response:' + JSON.stringify(res.body)) 92 | expect(res.status).to.equal(200) 93 | expect(JSON.stringify(res.body.result)).to.equal(JSON.stringify(testObj[testJsonArr[9]].result)) 94 | done() 95 | }) 96 | }).timeout(30000) 97 | it('test ' + testJsonArr[10], (done) => { 98 | const reqJson = testObj[testJsonArr[10]] 99 | console.log('request:' + JSON.stringify(reqJson)) 100 | chai.request('127.0.0.1:8081') 101 | .post('/api/imKey') 102 | .send(reqJson) 103 | .end((err, res) => { 104 | if (err) { 105 | console.log(err.stack) 106 | } 107 | console.log('response:' + JSON.stringify(res.body)) 108 | expect(res.status).to.equal(200) 109 | expect(JSON.stringify(res.body.result)).to.equal(JSON.stringify(testObj[testJsonArr[11]].result)) 110 | done() 111 | }) 112 | }).timeout(30000) 113 | it('test ' + testJsonArr[12], (done) => { 114 | const reqJson = testObj[testJsonArr[12]] 115 | console.log('request:' + JSON.stringify(reqJson)) 116 | chai.request('127.0.0.1:8081') 117 | .post('/api/imKey') 118 | .send(reqJson) 119 | .end((err, res) => { 120 | if (err) { 121 | console.log(err.stack) 122 | } 123 | console.log('response:' + JSON.stringify(res.body)) 124 | expect(res.status).to.equal(200) 125 | expect(JSON.stringify(res.body.result)).to.equal(JSON.stringify(testObj[testJsonArr[13]].result)) 126 | done() 127 | }) 128 | }).timeout(30000) 129 | it('test ' + testJsonArr[14], (done) => { 130 | const reqJson = testObj[testJsonArr[14]] 131 | console.log('request:' + JSON.stringify(reqJson)) 132 | chai.request('127.0.0.1:8081') 133 | .post('/api/imKey') 134 | .send(reqJson) 135 | .end((err, res) => { 136 | if (err) { 137 | console.log(err.stack) 138 | } 139 | console.log('response:' + JSON.stringify(res.body)) 140 | expect(res.status).to.equal(200) 141 | expect(JSON.stringify(res.body.result)).to.equal(JSON.stringify(testObj[testJsonArr[15]].result)) 142 | done() 143 | }) 144 | }).timeout(30000) 145 | it('test ' + testJsonArr[16], (done) => { 146 | const reqJson = testObj[testJsonArr[16]] 147 | console.log('request:' + JSON.stringify(reqJson)) 148 | chai.request('127.0.0.1:8081') 149 | .post('/api/imKey') 150 | .send(reqJson) 151 | .end((err, res) => { 152 | if (err) { 153 | console.log(err.stack) 154 | } 155 | console.log('response:' + JSON.stringify(res.body)) 156 | expect(res.status).to.equal(200) 157 | expect(JSON.stringify(res.body.result)).to.equal(JSON.stringify(testObj[testJsonArr[17]].result)) 158 | done() 159 | }) 160 | }).timeout(30000) 161 | it('test ' + testJsonArr[18], (done) => { 162 | const reqJson = testObj[testJsonArr[18]] 163 | console.log('request:' + JSON.stringify(reqJson)) 164 | chai.request('127.0.0.1:8081') 165 | .post('/api/imKey') 166 | .send(reqJson) 167 | .end((err, res) => { 168 | if (err) { 169 | console.log(err.stack) 170 | } 171 | console.log('response:' + JSON.stringify(res.body)) 172 | expect(res.status).to.equal(200) 173 | expect(JSON.stringify(res.body.result)).to.equal(JSON.stringify(testObj[testJsonArr[19]].result)) 174 | done() 175 | }) 176 | }).timeout(30000) 177 | it('test ' + testJsonArr[20], (done) => { 178 | const reqJson = testObj[testJsonArr[20]] 179 | console.log('request:' + JSON.stringify(reqJson)) 180 | chai.request('127.0.0.1:8081') 181 | .post('/api/imKey') 182 | .send(reqJson) 183 | .end((err, res) => { 184 | if (err) { 185 | console.log(err.stack) 186 | } 187 | console.log('response:' + JSON.stringify(res.body)) 188 | expect(res.status).to.equal(200) 189 | expect(JSON.stringify(res.body.result)).to.equal(JSON.stringify(testObj[testJsonArr[21]].result)) 190 | done() 191 | }) 192 | }).timeout(30000) 193 | it('test ' + testJsonArr[22], (done) => { 194 | const reqJson = testObj[testJsonArr[22]] 195 | console.log('request:' + JSON.stringify(reqJson)) 196 | chai.request('127.0.0.1:8081') 197 | .post('/api/imKey') 198 | .send(reqJson) 199 | .end((err, res) => { 200 | if (err) { 201 | console.log(err.stack) 202 | } 203 | console.log('response:' + JSON.stringify(res.body)) 204 | expect(res.status).to.equal(200) 205 | expect(JSON.stringify(res.body.result)).to.equal(JSON.stringify(testObj[testJsonArr[23]].result)) 206 | done() 207 | }) 208 | }).timeout(30000) 209 | it('test ' + testJsonArr[24], (done) => { 210 | const reqJson = testObj[testJsonArr[24]] 211 | console.log('request:' + JSON.stringify(reqJson)) 212 | chai.request('127.0.0.1:8081') 213 | .post('/api/imKey') 214 | .send(reqJson) 215 | .end((err, res) => { 216 | if (err) { 217 | console.log(err.stack) 218 | } 219 | console.log('response:' + JSON.stringify(res.body)) 220 | expect(res.status).to.equal(200) 221 | expect(JSON.stringify(res.body.result)).to.equal(JSON.stringify(testObj[testJsonArr[25]].result)) 222 | done() 223 | }) 224 | }).timeout(30000) 225 | 226 | it('test ' + testJsonArr[26], (done) => { 227 | const reqJson = testObj[testJsonArr[26]] 228 | console.log('request:' + JSON.stringify(reqJson)) 229 | chai.request('127.0.0.1:8081') 230 | .post('/api/imKey') 231 | .send(reqJson) 232 | .end((err, res) => { 233 | if (err) { 234 | console.log(err.stack) 235 | } 236 | console.log('response:' + JSON.stringify(res.body)) 237 | expect(res.status).to.equal(200) 238 | expect(JSON.stringify(res.body.result)).to.equal(JSON.stringify(testObj[testJsonArr[27]].result)) 239 | done() 240 | }) 241 | }).timeout(30000) 242 | it('test ' + testJsonArr[28], (done) => { 243 | const reqJson = testObj[testJsonArr[28]] 244 | console.log('request:' + JSON.stringify(reqJson)) 245 | chai.request('127.0.0.1:8081') 246 | .post('/api/imKey') 247 | .send(reqJson) 248 | .end((err, res) => { 249 | if (err) { 250 | console.log(err.stack) 251 | } 252 | console.log('response:' + JSON.stringify(res.body)) 253 | expect(res.status).to.equal(200) 254 | expect(JSON.stringify(res.body.result)).to.equal(JSON.stringify(testObj[testJsonArr[29]].result)) 255 | done() 256 | }) 257 | }).timeout(30000) 258 | 259 | it('test ' + testJsonArr[30], (done) => { 260 | const reqJson = testObj[testJsonArr[30]] 261 | console.log('request:' + JSON.stringify(reqJson)) 262 | chai.request('127.0.0.1:8081') 263 | .post('/api/imKey') 264 | .send(reqJson) 265 | .end((err, res) => { 266 | if (err) { 267 | console.log(err.stack) 268 | } 269 | console.log('response:' + JSON.stringify(res.body)) 270 | expect(res.status).to.equal(200) 271 | expect(JSON.stringify(res.body.result)).to.equal(JSON.stringify(testObj[testJsonArr[31]].result)) 272 | done() 273 | }) 274 | }).timeout(30000) 275 | it('test ' + testJsonArr[32], (done) => { 276 | const reqJson = testObj[testJsonArr[32]] 277 | console.log('request:' + JSON.stringify(reqJson)) 278 | chai.request('127.0.0.1:8081') 279 | .post('/api/imKey') 280 | .send(reqJson) 281 | .end((err, res) => { 282 | if (err) { 283 | console.log(err.stack) 284 | } 285 | console.log('response:' + JSON.stringify(res.body)) 286 | expect(res.status).to.equal(200) 287 | expect(JSON.stringify(res.body.result)).to.equal(JSON.stringify(testObj[testJsonArr[33]].result)) 288 | done() 289 | }) 290 | }).timeout(30000) 291 | it('test ' + testJsonArr[34], (done) => { 292 | const reqJson = testObj[testJsonArr[34]] 293 | console.log('request:' + JSON.stringify(reqJson)) 294 | chai.request('127.0.0.1:8081') 295 | .post('/api/imKey') 296 | .send(reqJson) 297 | .end((err, res) => { 298 | if (err) { 299 | console.log(err.stack) 300 | } 301 | console.log('response:' + JSON.stringify(res.body)) 302 | expect(res.status).to.equal(200) 303 | expect(JSON.stringify(res.body.result)).to.equal(JSON.stringify(testObj[testJsonArr[35]].result)) 304 | done() 305 | }) 306 | }).timeout(30000) 307 | it('test ' + testJsonArr[36], (done) => { 308 | const reqJson = testObj[testJsonArr[36]] 309 | console.log('request:' + JSON.stringify(reqJson)) 310 | chai.request('127.0.0.1:8081') 311 | .post('/api/imKey') 312 | .send(reqJson) 313 | .end((err, res) => { 314 | if (err) { 315 | console.log(err.stack) 316 | } 317 | console.log('response:' + JSON.stringify(res.body)) 318 | expect(res.status).to.equal(200) 319 | expect(JSON.stringify(res.body.result)).to.equal(JSON.stringify(testObj[testJsonArr[37]].result)) 320 | done() 321 | }) 322 | }).timeout(30000) 323 | it('test ' + testJsonArr[38], (done) => { 324 | const reqJson = testObj[testJsonArr[38]] 325 | console.log('request:' + JSON.stringify(reqJson)) 326 | chai.request('127.0.0.1:8081') 327 | .post('/api/imKey') 328 | .send(reqJson) 329 | .end((err, res) => { 330 | if (err) { 331 | console.log(err.stack) 332 | } 333 | console.log('response:' + JSON.stringify(res.body)) 334 | expect(res.status).to.equal(200) 335 | expect(JSON.stringify(res.body.result)).to.equal(JSON.stringify(testObj[testJsonArr[39]].result)) 336 | done() 337 | }) 338 | }).timeout(30000) 339 | 340 | // describe('test btc.getXpub', () => { 341 | // it('it should show sign result', (done) => { 342 | // const reqJson = { 343 | // jsonrpc: '2.0', 344 | // method: 'btc.getXpub', 345 | // params: { 346 | // network: 'MAINNET', 347 | // path: "m/44'/0'/0'/0/0" 348 | // }, 349 | // id: 5 350 | // } 351 | // 352 | // chai.request('127.0.0.1:8081') 353 | // .post('/api/imKey') 354 | // .send(reqJson 355 | // ) 356 | // .end((err, res) => { 357 | // if (err) { 358 | // console.log(err.stack) 359 | // } 360 | // console.log(res.body) 361 | // expect(res.status).to.equal(200) 362 | // expect(res.body.result.xpub).to.equal('xpub6FuzpGNBc46EfvmcvECyqXjrzGcKErQgpQcpvhw1tiC5yXvi1jUkzudMpdg5AaguiFstdVR5ASDbSceBswKRy6cAhpTgozmgxMUayPDrLLX') 363 | // done() 364 | // }) 365 | // }).timeout(30000) 366 | // 367 | // 368 | // it('test btc.getAddress', (done) => { 369 | // chai.request('127.0.0.1:8081') 370 | // .post('/api/imKey') 371 | // .send({ 372 | // jsonrpc: '2.0', 373 | // method: 'btc.getAddress', 374 | // params: { 375 | // network: 'MAINNET', 376 | // path: "m/44'/0'/0'/0/0" 377 | // }, 378 | // id: 6 379 | // } 380 | // ) 381 | // .end((err, res) => { 382 | // if (err) { 383 | // console.log(err.stack) 384 | // } 385 | // console.log(res.body) 386 | // expect(res.status).to.equal(200) 387 | // expect(res.body.result.address).to.equal('12z6UzsA3tjpaeuvA2Zr9jwx19Azz74D6g') 388 | // done() 389 | // }) 390 | // }).timeout(30000) 391 | // }) 392 | }) 393 | 394 | function readFileToJsonObj (filePath) { 395 | console.log(filePath) 396 | // 现将json文件读出来 397 | const data = fs.readFileSync(filePath, 'utf-8') 398 | const dataString = data.toString()// 将二进制的数据转换为字符串 399 | const jsonObj = JSON.parse(dataString)// 将字符串转换为json对象 400 | // console.log(jsonObj); 401 | return jsonObj 402 | // var str = JSON.stringify(jsonObj);//因为nodejs的写入文件只认识字符串或者二进制数,所以把json对象转换成字符串重新写入json文件中 403 | } 404 | -------------------------------------------------------------------------------- /src/renderer/common/lang/zh.js: -------------------------------------------------------------------------------- 1 | // zh-CN 2 | export const m = { 3 | imKeyManager: { 4 | imKey_manager: 'imKey Manager', 5 | your_imKey_manager: '你的 imKey Pro 桌面管理工具', 6 | use_now: '立刻使用', 7 | no_have_imKey_pro: '还未拥有 imKey Pro ?', 8 | use_imKey_connected: '使用 imKey Pro 完成连接', 9 | use_imKey_connect_computer: '1. 使用 USB 将 imKey Pro 连接至电脑', 10 | click_connect_button: '2. 点击「连接」按钮', 11 | enter_pin_on_imKey: '3. 在 imKey Pro 中输入 PIN 码', 12 | if_no_set_pin_can_jump_step3: '* 如果你尚未在 imKey Pro 中设置 PIN 码,可以跳过第 3 步', 13 | if_bind_device_disconnect_bluetooth: '* 请确保当前处于imKey Pro首页,并断开蓝牙连接', 14 | connect: '连接', 15 | imKey_connecting_wait: 'imKey 连接中,请耐心等待', 16 | check_firmware_version_update: '检查固件版本升级', 17 | check_device_bind_code: '检查设备绑定码', 18 | check_pin_wallet: '检查 PIN 码及钱包', 19 | check_secure_test: '检查安全测试', 20 | usb_connect_error: 'USB 连接异常', 21 | check_usb_connect: '请拔出 USB 重试或检查 USB 接口是否松动,确认无误后再次点击「连接」', 22 | ok: '确定', 23 | firmware_update_fail: '固件升级未完成', 24 | find_firmware_update_fail_continue: '检测到因应用异常退出导致固件升级未完成,请继续升级,完成后可正常使用 imKey Manager', 25 | imKey_pro_firmware_update_wait: 'imKey Pro 固件版本升级中,请耐心等待', 26 | imKey_pro_firmware_update_no_disconnect: '注意:升级完成后 imKey Pro 将自动重启升级过程中请勿断开 USB 连接,同时中止 imKey 操作', 27 | notice: '升级提示', 28 | imKey_pro_ble_update_prompt_message: '升级成功后需删除蓝牙配对信息重新配对', 29 | enter_bind_code: '请输入设备绑定码', 30 | bind_code_error_please_check: ' 绑定码错误,请仔细核对,或通过导入助记词', 31 | reset_imKey: ' 重置 imKey ', 32 | retrieved: '找回', 33 | enter_pin_imKey_pro: '请在 imKey Pro 中输入 PIN 码', 34 | pin_code_error_please_check: 'PIN 码错误 / 遗忘可以通过', 35 | imKey_setting: 'imKey 设置', 36 | following_operations_on_imKey: '请在 imKey 上完成以下操作:', 37 | bin_code: '绑定码', 38 | create_restore_Wallet: '创建 / 恢复钱包', 39 | complete_setup: '完成设置', 40 | 41 | generate_bind_code: '生成设备绑定码', 42 | first_bind_device_see_to_imKey: '初次绑定将生成设备绑定码,请在 imKey Pro 中查看并妥善备份', 43 | operating_tutorial: '新手教程', 44 | precautions: '注意事项:', 45 | bind_code_used_check_device_please_save: '绑定码用于 imKey 与客户端一对一绑定,请仔细抄写并保管', 46 | bind_code_export_on_setting_or_rest_imKey: '绑定码在「设置」页面可导出,或通过导入助记词重置 imKey 找回', 47 | same_imKey_bind_different_PC_used_same_bind_code: '同一台 imKey Pro 若在不同 PC 端绑定,将使用相同的绑定码', 48 | next: '下一步', 49 | // enter_bind_code:'输入设备绑定码', 50 | enter_bind_code_for_use_on_the_current_device: '请输入设备绑定码,以便在当前设备上使用', 51 | // bind_code_error_please_check:'绑定码错误,请仔细核对,或通过导入助记词重置 imKey 找回', 52 | 53 | disconnect_usb: '断开USB连接', 54 | setting_pin: '设置 PIN 码', 55 | disconnect_usb_use_imKey_setting: '请断开 USB 连接,使用 imKey 进行设置,设置完成后重新连接并选择下一步', 56 | use_pin_unlock_imKey: 'PIN 码用于解锁 imKey', 57 | setting_6_8_bit_pin: '设置 6-8 位 PIN 码', 58 | do_not_enter_the_same_or_consecutive_pin: '请勿输入相同或连续数字的 PIN 码', 59 | // create_restore_Wallet:'创建 / 恢复钱包', 60 | Mnemonic_control_ownership_please_private: '助记词掌控资产所有权,请妥善私密保管', 61 | Replace_reset_device_use_mnemonic_restore_wallet: '更换或重置设备,可以使用助记词恢复钱包资产', 62 | Create_restore_wallet_2_minutes_wait: '创建 / 恢复钱包约 2 分钟,请耐心等待', 63 | choose_create_wallet_mnemonic_never_be_online: '建议选择创建钱包,保障助记词永不联网', 64 | // complete_setup:'完成设置', 65 | congratulations_complete_setup_final_security_check: '恭喜你已完成 imKey 设置,进行最后一步安全检查吧', 66 | confirm_pi_code_set_for_me_backup_completed: '确认为本人设置 PIN 码,并已完成备份', 67 | mnemonics_backup_offline_importance_of_mnemonics: '已离线备份助记词,并知晓助记词的重要性', 68 | binding_code_backup_known: '已备份设备绑定码,并知晓其作用', 69 | safety_your_assets_please_complete_all_security_checks: '为了你的资产安全,请完成所有安全检查项', 70 | previous: '上一步', 71 | done: '完成', 72 | home: '主页', 73 | manager: '管理', 74 | setting: '设置', 75 | welcome_used_imkKey_manager: '欢迎使用 imKey Manager :)', 76 | imKey_manager_is_desktop_manager: 'imKey Manager 是一款硬件钱包桌面管理程序,在这里你可以实现:', 77 | imKey_wallet_version_manager: 'imKey 硬件钱包版本管理', 78 | imKey_APP_version_manager: 'imKey App 版本管理', 79 | fast_connect_desktop_APP: '快速连接桌面端应用', 80 | used_this_desktop_APP: '尝试使用这些桌面端应用吧', 81 | view_imKey_Pro_support_coin_list_install_update_Apps: '查看 imKey Pro 币种支持列表,升级或安装新增币种', 82 | App_list: '币种列表', 83 | imKey_soft: 'imKey 软件', 84 | search: '搜索', 85 | install: '安装', 86 | upgrade: '升级', 87 | installed: '已安装', 88 | delete: '删除', 89 | APP_installing_do_not_disconnect_usb: '币种程序安装中,请勿断开 USB 连接,并中止 imKey 操作', 90 | APP_deleting_do_not_disconnect_usb: '币种程序删除中,请勿断开 USB 连接,并中止 imKey 操作', 91 | APP_upgrading_do_not_disconnect_usb: '币种程序升级中,请勿断开 USB 连接,并中止 imKey 操作', 92 | install_uninstall_upgrade_delete_Apps_fail: '币种安装/升级失败', 93 | check_usb_or_internet_connect: '请检查 USB 连接或网络连接情况,确认无误后重试', 94 | imKey_pro_setting_and_info: 'imKey Pro 基本设置及相关信息 ', 95 | device: '设备', 96 | current_least_cos_version: '已为最新固件版本', 97 | found_new_cos_version: '发现新固件版本', 98 | found_new_soft_version: 'imKey Manager 新版本提示', 99 | device_SN: '设备编号: ', 100 | device_bind_code: '设备绑定码:', 101 | export: '导出', 102 | understand_more: '了解更多', 103 | restart_automatically_after_upgrade: '注意:升级完成后 imKey Pro 将自动重启', 104 | upgrading_do_not_disconnect_usb_operating: '升级过程中请勿断开 USB 连接,同时中止 imKey 操作', 105 | upgrade_done: '升级完成', 106 | can_used_imKey_manager: '当前可以正常使用 imKey Manager', 107 | upgrade_fail: '升级失败', 108 | check_usb_internet_connect_click_upgrade_retry: '请检查 USB 连接或网络连接情况,确认无误后点击升级重试', 109 | bind_code_8_bit_world_uppercase: '8 位绑定码,英文字母均为大写', 110 | loading_fail: '加载失败', 111 | check_usb_internet_connect_retry: '请检查 USB 连接或网络连接情况,确认无误后点击重试', 112 | found_imKey_manager_new_version: 'imKey Manager 新版本提示', 113 | update: '更新', 114 | access_error_please_check_your_network_connection: '访问出错,请检查你的网络/USB连接', 115 | loading_please_wait: '加载中,请耐心等待', 116 | new_version_is: '新版本为 ', 117 | current_version_is: ',当前版本为 ', 118 | update_later: '稍后更新', 119 | update_now: '立刻更新', 120 | cancel: '取消', 121 | copy_success: '复制成功', 122 | verifying: ' 正在验证', 123 | verified_successfully: ' 验证成功', 124 | done_setting_select_next: ' 请完成上述设置再选择下一步', 125 | 126 | beginner_guide: '新手引导', 127 | learn_use_imKey: '学习如何配对和使用 imKey,或前往帮助中心浏览更多文章', 128 | buy: '快速购买', 129 | visit_Youzan_Mall_to_buy: '访问有赞商城购买 imKey 及其周边吧', 130 | user_support: '用户支持', 131 | email: '邮件:support@imkey.im', 132 | wechat: '微信:imKey_Official', 133 | tokenlon_info: '安全快速的去中心化交易所', 134 | compound_info: '存币生息,支持 USDT/USDC/Dai等 ', 135 | aave_info: '存/借贷生息,支持 17 个币种', 136 | operating_tutorial_url: 'https://imkey.im/get-started?locale=zh-cn', 137 | update_tip_wait: '更新完成后 App 将自动重启,请耐心等待', 138 | coming_soon: ' 敬请期待 ', 139 | found_dapp_access: 'imKey Manager 提示', 140 | installing_dot_ksm_coin: '请前往管理界面安装币种程序DOT和KSM' 141 | }, 142 | appStart: { 143 | imKey_manager: 'imKey Manager', 144 | your_imKey_manager: '你的 imKey Pro 桌面管理工具', 145 | no_have_imKey_pro: '还未拥有 imKey Pro?', 146 | use_now: '立即使用' 147 | }, 148 | connectDevice: { 149 | get_start_imKey: '开始使用你的imKey Pro', 150 | info_connect_imKey: '将imKey连接到你的电脑上', 151 | info_click_button: '点击连接按钮', 152 | info_enter_pin: '在imKey上输入PIN码 ', 153 | info_if_pin: '如果没有PIN就不用输入', 154 | connect: '连接', 155 | connect_success: '连接成功', 156 | connecting: '连接中', 157 | check_BL: '检查BL', 158 | upgrading_firmware: '升级固件中', 159 | check_active: '检查激活', 160 | active_success: '激活成功', 161 | check_bind: '检查绑定', 162 | check_create_wallet: '检查创建钱包' 163 | }, 164 | step: { 165 | connect: '连接', 166 | active_bind: '激活&绑定', 167 | set_pin_create_wallet: '设置PIN&创建钱包' 168 | }, 169 | stepOne: { 170 | connect_imKey: '连接你的imKey' 171 | }, 172 | stepTwo: { 173 | active_bind: '激活&绑定', 174 | please_active_bind: '请激活并绑定你的imKey', 175 | active_imKey: '激活你的imKey', 176 | bind_imKey: '绑定你的imKey', 177 | start_active_bind_imKey: '开始激活并绑定你的imKey', 178 | start: '开始', 179 | activating_imKey: '正在激活你的imKey', 180 | binding_imKey: '正在绑定你的imKey', 181 | bind: '绑定', 182 | bind_code_is_null: '绑定码不能为空', 183 | bind_code_is_correct: '绑定码格式不正确', 184 | enter_bind_code: '输入绑定码' 185 | }, 186 | stepThree: { 187 | set_pin_create_wallet: '设置PIN&创建钱包', 188 | please_disconnect: '请断开USB,设置PIN并创建钱包', 189 | set_modify_pin: '设置或修改PIN', 190 | create_wallet: '创建或恢复钱包', 191 | next: '下一步', 192 | operation_guide: '操作指南', 193 | how_to_set_pin: '设置方式:', 194 | set_pin_1: 'imKey 的 PIN 码设置要求为 6 - 8 位数字,不支持相同或连续的数字组合。', 195 | set_pin_2: 'imKey 上显示「设置 PIN 」进入设置流程。', 196 | set_pin_3: '设置每一位的数字,上下按钮切换,点击「OK」确认,点击「C」取消', 197 | set_pin_4: '重复此过程,直到选择了 PIN 码的所有数字。(如果你想设置 6 位 PIN 码,在第 7 位出现「✔️」符号时点击「OK」按钮即可完成设置。如果你想设置 7 位或者 8 位 PIN\n' + 198 | ' 码,在显示「✔️」符号时,通过切换按钮切换成数字并确认即可。)', 199 | set_pin_5: '再次输入 PIN 码进行确认', 200 | security_reminders: '安全提示:', 201 | security_info_1: 'PIN 码用于解锁 imKey,丢失无法找回,请妥善保管;', 202 | security_info_2: '不支持相同或连续的数字组合。', 203 | security_info_3: '切勿使用随机 PIN 码和第三方工具提供的 PIN 码。', 204 | how_to_change_PIN: '修改 PIN 码:', 205 | change_pin: '输入 PIN 开启 imKey,点击「OK」进入钱包设置界面,选择「设置」—「修改 PIN」,输入原 PIN 码进行身份确认,然后重新设置 PIN 码即可。', 206 | special_notes: '特别提示:为保证钱包资产安全,连续五次输入错误的 PIN 码,imKey 会被强制重置。请用户务必记牢设置的 PIN 码,PIN 码一旦遗失没有任何办法能够重置或者找回。', 207 | create_a_wallet: '创建钱包:', 208 | create_a_wallet_1: '根据 imKey 界面显示,使用上下按钮移动光标至「创建钱包」,点击 OK。', 209 | create_a_wallet_2: '阅读提醒后点击 OK 确认', 210 | create_a_wallet_3: '准确按顺序抄写单词', 211 | create_a_wallet_4: '重复此过程直到 12 个单词全部保存(助记词是恢复钱包的唯一方式,只能在创建钱包的时候备份备份,请务必离线妥善保管)', 212 | create_a_wallet_5: '备份助记词结束后 imKey 会显示「请确认助记词」', 213 | create_a_wallet_6: '上下按钮选择对应位置正确的单词,点击 OK 确认」', 214 | create_a_wallet_7: '重复此过程直到 12 个单词全部验证 ', 215 | create_a_wallet_8: '验证完成后提示「钱包创建中,预计 2 分钟完成」。创建完成后,即可开始使用。', 216 | recover_wallet: '恢复钱包:', 217 | recover_wallet_1: '根据 imKey 界面显示,使用上下按钮移动光标至「恢复钱包」,点击 OK。', 218 | recover_wallet_2: '选择要导入助记词的单词数量,imKey 目前支持导入 12、18、24 个单词的助记词。', 219 | recover_wallet_3: '选择助记词对应位置单词的字母,上下按钮切换,OK 确认,C 取消。', 220 | recover_wallet_4: '重复此操作,直到 imKey 显示可供选择的建议单词,上下按钮移动光标,点击 OK 选择正确的单词。(因为 BIP39 词库的 2048\n' + 221 | ' 个单词各不相同。每个单词只需要前几位字母即可确定,所以输入前几位字母后即可跳转到选择备选单词界面)', 222 | recover_wallet_5: '完成助记词导入后,点击 OK 。开始导入钱包后进入提示界面「钱包恢复中,预计 2 分钟完成」。', 223 | recover_wallet_6: '导入完成,即可开始使用。' 224 | }, 225 | home: { 226 | buy_now: '现在购买', 227 | use_imToken_to_buy: '请使用 imToken 2.0 扫码购买', 228 | use_WeChat_AliPay_to_buy: '请使用微信/支付宝扫码购买', 229 | WeChat_AliPay: '微信/支付宝' 230 | }, 231 | connect: { 232 | connect_imKey: '连接你的imKey', 233 | follow_steps: '执行下面步骤进入管理界面', 234 | connect_imKey_to_computer: '将你的imKey连接到电脑上', 235 | click_connect: '点击连接按钮', 236 | enter_pin_imKey: '在你的imKey上输入PIN码', 237 | connect: '连接' 238 | }, 239 | manager: { 240 | manager: '管理', 241 | install_uninstall_apps: '给imKey安装或者下载应用', 242 | firmware_version: '固件版本', 243 | firmware_is: '有新的固件 ', 244 | available: '可用', 245 | update: '更新', 246 | app_catalog: '应用目录', 247 | search_app: '搜索应用', 248 | install: '安装' 249 | }, 250 | setting: { 251 | info: '提示', 252 | is_update: '是否要更新?', 253 | cancel: '取消', 254 | ok: '确定', 255 | is_quit_update: '是否立刻退出更新?', 256 | setting: '设置', 257 | setting_imKey_manager: '设置imKey-manager', 258 | need_help: '帮助?', 259 | imKey_manager_version: 'imKey-manager版本', 260 | imKey_manager_version_is: 'imKey-manager有新的版本', 261 | available: '可用' 262 | }, 263 | menu: { 264 | home: '主页', 265 | manager: '管理', 266 | setting: '设置' 267 | }, 268 | noticeDialog: { 269 | info: '提示' 270 | }, 271 | dapp: { 272 | newest_launched: '最新上架', 273 | tokenlon_desc: '安全快速的去中心化交易所', 274 | sushiswap_desc: '锁定 SushiSwap LP 代币,领取属于您的美味 SUSHI!', 275 | uniswap_desc: '保证数百万用户和数百个 Ethereum 应用的流动性', 276 | polkadotJS_desc: '加密货币交易所|简单的硬币兑换方式', 277 | multisender_desc: '将 ERC20 代币或 ETH 发送到成千上万的地址,只需一次交易......', 278 | rarible_desc: 'Create and sell digital collectibles', 279 | murall_desc: '在线创作加密艺术作品', 280 | zksync_desc: '安全、去信任化的以太坊二层网络', 281 | zkswap_desc: '基于 zkRollup Layer2 并采用 AMM 的去中心化交易所', 282 | compound_desc: '能够同时管理 MakerDAO 与 Compound 的理财神器', 283 | aave_desc: '去中心化理财, 抵押 DAI 获取稳定收益', 284 | Venus_desc: '去中心化的算法货币市场和稳定币协议,使借款人和供应商能够按需获得流动性和收益', 285 | pancake_desc: '允许在 Binance 智能链上交换两个代币。它速度快,价格便宜,并且任何人都可以参加', 286 | Autofarm_desc: 'AutoFarm 聚集了 BSC 上最好的保管库,并实施了最佳策略来最大化用户的收益', 287 | PancakeBunny_desc: '用于 PancakeSwap 的新型且快速增长的 DeFi 挖矿聚合器', 288 | Belt_Finance_desc: '具有低费用/滑点,还通过 vault 复合,借贷和收益率生成提供聚合', 289 | beefy_finance_desc: '收益更高,且不必担心不断地提高APY所必需的相互作用量', 290 | Bscex_LaunchpoolX_desc: '构建 Binance 交易所 Launchpool 的链上版本', 291 | ACryptoS_desc: '通过自动收益策略来增加您的资产', 292 | Alpaca_Finance_desc: '基于 Binance Smart Chain 的杠杆式收益挖矿协议', 293 | Goose_Finance_desc: 'Binance Smart Chain 上第一个删除迁移器代码的收益挖矿项目', 294 | MDEX_desc: '融合多种基础公链的差异化优势,打造高性能复合型 DEX 生态', 295 | BXH_desc: 'DEX 创新交易平台,是⼀个社区⾃治、⾃动做 市(AMM)的去中⼼化交易所', 296 | CoinWind_desc: 'DeFi 智能挖矿金融平台', 297 | Lendhub_desc: '基于火币生态链的去中心化借贷平台', 298 | FilDA_desc: '全球首个基于 HECO 的跨链借贷 DeFi 项目', 299 | EarnDefi_desc: '一站式 DEFI 协议,专挖头矿,具有聚合理财、交易、跨链等功能', 300 | Channels_desc: '专注于主流借贷币种 HUSD、USDT、ETH、HBTC 和 HT 等', 301 | HFI_one_desc: '基于 Heco 生态链的去中心化聚合挖矿平台', 302 | Pippi_Shrimp_Swap_desc: '基于火币智能链(Heco)的去中心化交易平台', 303 | HashBridge_desc: '提供一整套完善的数据服务方案', 304 | JulSwap_desc: '币安智能链上的 AMM', 305 | Etherscan_desc: '以太坊交易浏览器', 306 | OpenSea_desc: '全球知名加密收藏品交易平台', 307 | Nifty_Gateway_desc: '独一无二的 NFT 交易管理平台', 308 | SuperRare_desc: '收集超级珍稀(Super Rare)的数字艺术品' 309 | }, 310 | imKeyCoreErrorInfo: { 311 | decoding_failed: '解析失败', 312 | imkey_publickey_mismatch_with_path: '公钥路径不匹配', 313 | imkey_illegal_param: '参数错误', 314 | get_seid_error: '获取seid失败', 315 | get_sn_error: '获取sn失败', 316 | get_ram_size_error: '获取内存空间失败', 317 | get_firmware_version_error: '获取固件版本失败', 318 | get_battery_power_error: '获取电量失败', 319 | get_life_time_error: '获取生命周期失败', 320 | get_ble_name_error: '获取蓝牙名称失败', 321 | get_ble_version_error: '获取蓝牙版本失败', 322 | parse_arguments_to_str: '解析api传入的字符失败', 323 | decode_imkey_api: 'api数据解析失败', 324 | encode_error: '编码错误', 325 | device_connect_interface_not_called: '没有调用api连接接口', 326 | device_data_read_time_out: '设备读取数据超时', 327 | imkey_device_not_connect: '没有连接imkey', 328 | Failed_opening_hid_device: '连接失败,如果已连接,请断开重连', 329 | hidapi_error_hid_error_is_not_implemented_yet: '连接失败,请断开重连', 330 | imkey_device_reconnect_fail: '重新连接失败,请断开重连', 331 | imkey_path_illegal: '路径错误', 332 | imkey_user_not_confirmed: '用户没有确认', 333 | imkey_conditions_not_satisfied: '安全条件不满足', 334 | imkey_command_format_error: '指令格式错误', 335 | imkey_command_data_error: '指令错误', 336 | imkey_applet_not_exist: '应用不存在', 337 | imkey_apdu_wrong_length: '指令长度错误', 338 | imkey_signature_verify_fail: '签名校验失败', 339 | imkey_bluetooth_channel_error: '蓝牙通道错误', 340 | imkey_applet_function_not_supported: '该应用不支持这个方法', 341 | imkey_exceeded_max_utxo_number: '超过了最大utxo数量', 342 | imkey_command_execute_fail: '指令执行失败', 343 | imkey_wallet_not_created: '钱包没有创建', 344 | imkey_in_menu_page: 'imkey在菜单页面', 345 | imkey_pin_not_verified: 'PIN码没有认证通过', 346 | imkey_address_mismatch_with_path: '地址不匹配', 347 | imkey_insufficient_funds: '资金不足', 348 | imkey_sdk_illegal_argument: 'sdk参数错误', 349 | imkey_amount_less_than_minimum: '金额少于最小值', 350 | get_xpub_error: '获取xpub错误', 351 | address_type_mismatch: '地址类型不匹配', 352 | imkey_tsm_device_authenticity_check_fail: '验证真伪失败', 353 | imkey_tsm_device_not_activated: '设备未激活', 354 | imkey_tsm_device_illegal: '非法设备', 355 | imkey_tsm_device_stop_using: '该设备已停用', 356 | imkey_tsm_server_error: '服务器错误', 357 | imkey_se_cert_invalid: 'imkey SE证书无效', 358 | imkey_tsm_device_update_check_fail: '设备更新检查失败l', 359 | imkey_tsm_device_active_fail: 'i设备激活失败', 360 | imkey_tsm_receipt_check_fail: 'receipt检查失败', 361 | imkey_tsm_app_download_fail: '应用下载失败', 362 | imkey_tsm_app_update_fail: '应用更新失败', 363 | imkey_tsm_app_delete_fail: '应用删除失败', 364 | imkey_tsm_oce_cert_check_fail: '公钥验证失败', 365 | imkey_tsm_cos_info_no_conf: 'COS 信息未配置', 366 | imkey_tsm_cos_upgrade_fail: 'COS升级失败', 367 | imkey_tsm_upload_cos_version_is_null: '上传的COS版本为空', 368 | imkey_tsm_switch_bl_status_fail: '切换BL状态失败', 369 | imkey_tsm_write_wallet_address_fail: '写wallet地址失败', 370 | imkey_tsm_check_update_fail: '检查更新失败', 371 | imkey_auth_code_ciphertext_storage_fail: '绑定码密文存储失败', 372 | imkey_keyfile_io_error: '创建key文件错误', 373 | imkey_encrypt_auth_code_fail: '加密绑定码失败', 374 | imkey_save_key_file_fail: '保存key文件失败' 375 | } 376 | 377 | } 378 | --------------------------------------------------------------------------------