├── js
├── config.js
├── background
│ ├── keychainify.js
│ ├── ops
│ │ ├── decode.js
│ │ ├── signBuffer.js
│ │ ├── signTx.js
│ │ ├── signedCall.js
│ │ ├── customJson.js
│ │ ├── broadcast.js
│ │ ├── vote.js
│ │ ├── witness.js
│ │ ├── createAccount.js
│ │ ├── tokens.js
│ │ ├── delegation.js
│ │ ├── post.js
│ │ ├── power.js
│ │ ├── transfer.js
│ │ ├── proposals.js
│ │ └── authority.js
│ ├── errors.js
│ ├── autolock.js
│ ├── dialog_lifecycle.js
│ ├── transactions.js
│ ├── context_menu.js
│ ├── init.js
│ └── auth.js
├── popup
│ ├── com.js
│ ├── preinit.js
│ ├── power.js
│ ├── rpc.js
│ ├── settings.js
│ ├── delegate.js
│ ├── witness.js
│ └── popup.js
├── libs
│ ├── globalProps.js
│ ├── hf21.js
│ ├── encrypt.js
│ ├── accountsList.js
│ ├── rpcs.js
│ ├── keychainify.js
│ └── account.js
├── import.js
├── keychainify_content.js
├── dialog
│ └── localize.js
├── web_interface.js
└── steem_keychain.js
├── images
├── eye.png
├── key.png
├── bg_rc.png
├── claim.png
├── clear.png
├── close.png
├── copy.png
├── delete.png
├── edit.png
├── hide.png
├── info.png
├── link.png
├── lock.png
├── logo.png
├── logo2.png
├── plus.png
├── submit.png
├── voted.png
├── accounts.png
├── arobase.png
├── autolock.png
├── delegate.png
├── history.png
├── loading.gif
├── password.png
├── plus_key.png
├── powerup.png
├── rewards.png
├── settings.png
├── squares.png
├── transfer.png
├── uparrow.png
├── witness.png
├── add_account.png
├── bg-steam@2x.png
├── bg_voting.png
├── btn-bg_send.png
├── downarrow.png
├── icon_white.png
├── left-arrow.png
├── lock_wallet.png
├── logo-white.png
├── powerdown.png
├── preferences.png
├── small_logo.png
├── white-link.png
├── arobase-light.png
├── arobase_white.png
├── btn-bg_tokens.png
├── keychain_icon.png
├── keychain_logo.png
├── btn-bg_history.png
├── btn-bg_witness.png
├── hr-lewis-keogh3.png
├── icon_info_blue.png
├── icon_info_white.png
├── keychain_icon2.png
├── hr-lewis-keogh3.2.png
├── keychain_icon_small.png
├── icon_witness-vote.svg
├── import.svg
├── icon_witness-vote_default.svg
└── error.svg
├── fonts
└── Futura-Boo.otf
├── .gitignore
├── decode_wrapper
├── README
├── package.json
└── decode_wrapper.js
├── css
├── import.css
└── dialog.css
├── html
├── import.html
└── dialog.html
├── LICENSE
├── manifest.json
├── vendor
├── md5.min.js
└── ssc.min.js
├── example
├── main.js
└── main.html
└── README.md
/js/config.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | mainNet: "ssc-mainnet1"
3 | };
4 |
--------------------------------------------------------------------------------
/images/eye.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/eye.png
--------------------------------------------------------------------------------
/images/key.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/key.png
--------------------------------------------------------------------------------
/images/bg_rc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/bg_rc.png
--------------------------------------------------------------------------------
/images/claim.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/claim.png
--------------------------------------------------------------------------------
/images/clear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/clear.png
--------------------------------------------------------------------------------
/images/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/close.png
--------------------------------------------------------------------------------
/images/copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/copy.png
--------------------------------------------------------------------------------
/images/delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/delete.png
--------------------------------------------------------------------------------
/images/edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/edit.png
--------------------------------------------------------------------------------
/images/hide.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/hide.png
--------------------------------------------------------------------------------
/images/info.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/info.png
--------------------------------------------------------------------------------
/images/link.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/link.png
--------------------------------------------------------------------------------
/images/lock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/lock.png
--------------------------------------------------------------------------------
/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/logo.png
--------------------------------------------------------------------------------
/images/logo2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/logo2.png
--------------------------------------------------------------------------------
/images/plus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/plus.png
--------------------------------------------------------------------------------
/images/submit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/submit.png
--------------------------------------------------------------------------------
/images/voted.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/voted.png
--------------------------------------------------------------------------------
/images/accounts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/accounts.png
--------------------------------------------------------------------------------
/images/arobase.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/arobase.png
--------------------------------------------------------------------------------
/images/autolock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/autolock.png
--------------------------------------------------------------------------------
/images/delegate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/delegate.png
--------------------------------------------------------------------------------
/images/history.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/history.png
--------------------------------------------------------------------------------
/images/loading.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/loading.gif
--------------------------------------------------------------------------------
/images/password.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/password.png
--------------------------------------------------------------------------------
/images/plus_key.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/plus_key.png
--------------------------------------------------------------------------------
/images/powerup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/powerup.png
--------------------------------------------------------------------------------
/images/rewards.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/rewards.png
--------------------------------------------------------------------------------
/images/settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/settings.png
--------------------------------------------------------------------------------
/images/squares.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/squares.png
--------------------------------------------------------------------------------
/images/transfer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/transfer.png
--------------------------------------------------------------------------------
/images/uparrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/uparrow.png
--------------------------------------------------------------------------------
/images/witness.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/witness.png
--------------------------------------------------------------------------------
/fonts/Futura-Boo.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/fonts/Futura-Boo.otf
--------------------------------------------------------------------------------
/images/add_account.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/add_account.png
--------------------------------------------------------------------------------
/images/bg-steam@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/bg-steam@2x.png
--------------------------------------------------------------------------------
/images/bg_voting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/bg_voting.png
--------------------------------------------------------------------------------
/images/btn-bg_send.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/btn-bg_send.png
--------------------------------------------------------------------------------
/images/downarrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/downarrow.png
--------------------------------------------------------------------------------
/images/icon_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/icon_white.png
--------------------------------------------------------------------------------
/images/left-arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/left-arrow.png
--------------------------------------------------------------------------------
/images/lock_wallet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/lock_wallet.png
--------------------------------------------------------------------------------
/images/logo-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/logo-white.png
--------------------------------------------------------------------------------
/images/powerdown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/powerdown.png
--------------------------------------------------------------------------------
/images/preferences.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/preferences.png
--------------------------------------------------------------------------------
/images/small_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/small_logo.png
--------------------------------------------------------------------------------
/images/white-link.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/white-link.png
--------------------------------------------------------------------------------
/images/arobase-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/arobase-light.png
--------------------------------------------------------------------------------
/images/arobase_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/arobase_white.png
--------------------------------------------------------------------------------
/images/btn-bg_tokens.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/btn-bg_tokens.png
--------------------------------------------------------------------------------
/images/keychain_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/keychain_icon.png
--------------------------------------------------------------------------------
/images/keychain_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/keychain_logo.png
--------------------------------------------------------------------------------
/images/btn-bg_history.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/btn-bg_history.png
--------------------------------------------------------------------------------
/images/btn-bg_witness.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/btn-bg_witness.png
--------------------------------------------------------------------------------
/images/hr-lewis-keogh3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/hr-lewis-keogh3.png
--------------------------------------------------------------------------------
/images/icon_info_blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/icon_info_blue.png
--------------------------------------------------------------------------------
/images/icon_info_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/icon_info_white.png
--------------------------------------------------------------------------------
/images/keychain_icon2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/keychain_icon2.png
--------------------------------------------------------------------------------
/images/hr-lewis-keogh3.2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/hr-lewis-keogh3.2.png
--------------------------------------------------------------------------------
/images/keychain_icon_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MattyIce/steem-keychain/HEAD/images/keychain_icon_small.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Dependency directory
2 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
3 | node_modules
4 |
5 |
--------------------------------------------------------------------------------
/decode_wrapper/README:
--------------------------------------------------------------------------------
1 | # Generating decode.min.js
2 | # Needs node/npm.
3 | # Needs browserify.
4 |
5 | npm install
6 | browserify decode_wrapper.js > decode.min.js
7 |
--------------------------------------------------------------------------------
/decode_wrapper/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "decode_wrapper",
3 | "version": "1.0.0",
4 | "description": "Small wrapper for key encryption methods.",
5 | "main": "decode_wrapper.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "license": "MIT",
10 | "dependencies": {
11 | "steem": "^0.7.1"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/js/background/keychainify.js:
--------------------------------------------------------------------------------
1 | chrome.webNavigation.onHistoryStateUpdated.addListener(function(details) {
2 | if (details.frameId === 0) {
3 | // Fires only when details.url === currentTab.url
4 | chrome.tabs.get(details.tabId, async function(tab) {
5 | if (await keychainify.isKeychainifyEnabled()) {
6 | keychainify.keychainifyUrl(tab);
7 | }
8 | });
9 | }
10 | });
11 |
--------------------------------------------------------------------------------
/js/background/ops/decode.js:
--------------------------------------------------------------------------------
1 | const decodeMessage = data => {
2 | let message = null;
3 | let decoded = null;
4 | let error = null;
5 | try {
6 | decoded = window.decodeMemo(key, data.message);
7 | } catch (err) {
8 | error = err;
9 | } finally {
10 | return createMessage(
11 | error,
12 | decoded,
13 | data,
14 | chrome.i18n.getMessage("bgd_ops_decode"),
15 | chrome.i18n.getMessage("bgd_ops_decode_err")
16 | );
17 | }
18 | };
19 |
--------------------------------------------------------------------------------
/js/background/ops/signBuffer.js:
--------------------------------------------------------------------------------
1 | const signBuffer = data => {
2 | let message = null;
3 | let signed = null;
4 | let error = null;
5 | try {
6 | signed = window.signBuffer(data.message, key);
7 | } catch (err) {
8 | error = err;
9 | } finally {
10 | return createMessage(
11 | error,
12 | signed,
13 | data,
14 | chrome.i18n.getMessage("bgd_ops_sign_success"),
15 | chrome.i18n.getMessage("bgd_ops_sign_error"),
16 | public
17 | );
18 | }
19 | };
20 |
--------------------------------------------------------------------------------
/js/background/ops/signTx.js:
--------------------------------------------------------------------------------
1 | const signTx = data => {
2 | return new Promise((resolve, reject) => {
3 | let result = null, err = null;
4 |
5 | try {
6 | result = steem.auth.signTransaction(data.tx, [key]);
7 | } catch (e) { err = e; }
8 |
9 | console.log(result, err);
10 | const err_message = beautifyErrorMessage(err);
11 |
12 | const message = createMessage(
13 | err,
14 | result,
15 | data,
16 | chrome.i18n.getMessage("bgd_ops_sign_tx"),
17 | err_message
18 | );
19 | resolve(message);
20 | });
21 | };
22 |
--------------------------------------------------------------------------------
/images/icon_witness-vote.svg:
--------------------------------------------------------------------------------
1 |
13 |
--------------------------------------------------------------------------------
/js/background/ops/signedCall.js:
--------------------------------------------------------------------------------
1 | const broadcastSignedCall = data => {
2 | return new Promise((resolve, reject) => {
3 | window.signedCall(
4 | data.method,
5 | data.params,
6 | data.username,
7 | key,
8 | (err, result) => {
9 | console.log(result, err);
10 | const err_message = beautifyErrorMessage(err);
11 | const message = createMessage(
12 | err,
13 | result,
14 | data,
15 | chrome.i18n.getMessage("bgd_ops_signed_call"),
16 | err_message
17 | );
18 | resolve(message);
19 | }
20 | );
21 | });
22 | };
23 |
--------------------------------------------------------------------------------
/images/import.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/js/background/errors.js:
--------------------------------------------------------------------------------
1 | // Send errors back to the content_script, it will forward it to website
2 | const sendErrors = (tab, error, message, display_msg, request) => {
3 | clearInterval(interval);
4 | interval = setInterval(() => {
5 | chrome.runtime.sendMessage({
6 | command: "sendDialogError",
7 | msg: {
8 | success: false,
9 | error: error,
10 | result: null,
11 | data: request,
12 | message: message,
13 | display_msg: display_msg,
14 | request_id: request_id
15 | },
16 | tab: tab
17 | });
18 | }, 200);
19 | setTimeout(() => {
20 | clearInterval(interval);
21 | }, 2000);
22 | key = null;
23 | accounts = new AccountsList();
24 | };
25 |
--------------------------------------------------------------------------------
/js/popup/com.js:
--------------------------------------------------------------------------------
1 | // Communication with the background
2 |
3 | // Send new autolock to background;
4 | function setAutolock(autolock) {
5 | chrome.runtime.sendMessage({
6 | command: "sendAutolock",
7 | autolock: autolock
8 | });
9 | }
10 |
11 | // get MK from background
12 | function getMK() {
13 | chrome.runtime.sendMessage({
14 | command: "getMk"
15 | });
16 | }
17 |
18 | // setRPC in the background
19 | function setRPC(rpc) {
20 | chrome.runtime.sendMessage({
21 | command: "setRPC",
22 | rpc: rpc
23 | });
24 | }
25 |
26 | setInterval(ping, 10000);
27 | // ping the background to show that the extension is not idle
28 | function ping() {
29 | chrome.runtime.sendMessage({
30 | command: "ping"
31 | });
32 | }
33 |
--------------------------------------------------------------------------------
/css/import.css:
--------------------------------------------------------------------------------
1 | #file {
2 | width: 0.1px;
3 | height: 0.1px;
4 | opacity: 0;
5 | overflow: hidden;
6 | position: absolute;
7 | z-index: -1;
8 | }
9 |
10 | #file+label {
11 | display: inline-block;
12 | border-radius: 5px;
13 | width: 100%;
14 | text-align: center;
15 | font-size: 20px;
16 | line-height: 28px;
17 | color: white;
18 | font-family: Futura;
19 | text-transform: uppercase;
20 | border: solid 3px rgba(0, 156, 156);
21 | cursor: pointer;
22 | }
23 |
24 | #file_span {
25 | display: inline-block;
26 | margin-bottom: 30px;
27 | }
28 |
29 | #file:focus+label,
30 | #file+label:hover {
31 | background-color: #917FC6;
32 | border: solid 3px #917FC6;
33 | }
34 |
35 | #import_result {
36 | display: none;
37 | }
--------------------------------------------------------------------------------
/js/background/ops/customJson.js:
--------------------------------------------------------------------------------
1 | const broadcastCustomJson = data => {
2 | return new Promise((resolve, reject) => {
3 | steem.broadcast.customJson(
4 | key,
5 | data.method.toLowerCase() == "active" ? [data.username] : null,
6 | data.method.toLowerCase() == "posting" ? [data.username] : null,
7 | data.id,
8 | data.json,
9 | (err, result) => {
10 | console.log(result, err);
11 | const err_message = beautifyErrorMessage(err);
12 |
13 | const message = createMessage(
14 | err,
15 | result,
16 | data,
17 | chrome.i18n.getMessage("bgd_ops_broadcast"),
18 | err_message
19 | );
20 | resolve(message);
21 | }
22 | );
23 | });
24 | };
25 |
--------------------------------------------------------------------------------
/js/background/ops/broadcast.js:
--------------------------------------------------------------------------------
1 | const broadcastData = data => {
2 | return new Promise((resolve, reject) => {
3 | const operations = data.operations;
4 | const broadcastKeys = {};
5 | broadcastKeys[data.typeWif] = key;
6 | steem.broadcast.send(
7 | {
8 | operations,
9 | extensions: []
10 | },
11 | broadcastKeys,
12 | (err, result) => {
13 | console.log(result, err);
14 | const err_message = beautifyErrorMessage(err);
15 |
16 | const message = createMessage(
17 | err,
18 | result,
19 | data,
20 | chrome.i18n.getMessage("bgd_ops_broadcast"),
21 | err_message
22 | );
23 | resolve(message);
24 | }
25 | );
26 | });
27 | };
28 |
--------------------------------------------------------------------------------
/js/background/ops/vote.js:
--------------------------------------------------------------------------------
1 | const broadcastVote = data => {
2 | return new Promise((resolve, reject) => {
3 | steem.broadcast.vote(
4 | key,
5 | data.username,
6 | data.author,
7 | data.permlink,
8 | parseInt(data.weight),
9 | (err, result) => {
10 | console.log(result, err);
11 | const err_message = beautifyErrorMessage(err);
12 | const message = createMessage(
13 | err,
14 | result,
15 | data,
16 | chrome.i18n.getMessage("bgd_ops_vote", [
17 | data.author,
18 | data.permlink,
19 | parseInt(data.weight) / 100
20 | ]),
21 | err_message
22 | );
23 | resolve(message);
24 | }
25 | );
26 | });
27 | };
28 |
--------------------------------------------------------------------------------
/js/background/ops/witness.js:
--------------------------------------------------------------------------------
1 | const broadcastWitnessVote = data => {
2 | return new Promise((resolve, reject) => {
3 | steem.broadcast.accountWitnessVote(
4 | key,
5 | data.username,
6 | data.witness,
7 | data.vote ? 1 : 0,
8 | (err, result) => {
9 | console.log(result, err);
10 | const err_message = beautifyErrorMessage(err);
11 | const message = createMessage(
12 | err,
13 | result,
14 | data,
15 | data.vote
16 | ? chrome.i18n.getMessage("bgd_ops_witness_voted", [data.witness])
17 | : chrome.i18n.getMessage("bgd_ops_witness_unvoted", [data.witness]),
18 | err_message
19 | );
20 | resolve(message);
21 | }
22 | );
23 | });
24 | };
25 |
--------------------------------------------------------------------------------
/js/background/ops/createAccount.js:
--------------------------------------------------------------------------------
1 | const broadcastCreateClaimedAccount = data => {
2 | return new Promise((resolve, reject) => {
3 | steem.broadcast.createClaimedAccount(
4 | key,
5 | data.username,
6 | data.new_account,
7 | JSON.parse(data.owner),
8 | JSON.parse(data.active),
9 | JSON.parse(data.posting),
10 | data.memo,
11 | {},
12 | [],
13 | (err, result) => {
14 | console.log(result, err);
15 | const err_message = beautifyErrorMessage(err);
16 | const message = createMessage(
17 | err,
18 | result,
19 | data,
20 | chrome.i18n.getMessage("bgd_ops_create_account", [data.new_account]),
21 | err_message
22 | );
23 | resolve(message);
24 | }
25 | );
26 | });
27 | };
28 |
--------------------------------------------------------------------------------
/decode_wrapper/decode_wrapper.js:
--------------------------------------------------------------------------------
1 | var signature = require("steem/lib/auth/ecc");
2 | var steem = require("steem");
3 |
4 | window.decodeMemo = (e, r) => steem.memo.decode(e, r);
5 | window.encodeMemo = (e, r, a) => steem.memo.encode(e, r, a);
6 | window.signBuffer = (e, r) => {
7 | // try to parse Buffer
8 | let buf = e;
9 | try {
10 | const o = JSON.parse(buf, (k, v) => {
11 | if (
12 | v !== null &&
13 | typeof v === "object" &&
14 | "type" in v &&
15 | v.type === "Buffer" &&
16 | "data" in v &&
17 | Array.isArray(v.data)
18 | ) {
19 | return new Buffer(v.data);
20 | }
21 | return v;
22 | });
23 | if (Buffer.isBuffer(o)) {
24 | buf = o;
25 | }
26 | } catch (e) {}
27 | return signature.Signature.signBuffer(buf, r).toHex();
28 | };
29 | window.signedCall = (m, p, a, k, c) => steem.api.signedCall(m, p, a, k, c);
30 |
--------------------------------------------------------------------------------
/js/background/ops/tokens.js:
--------------------------------------------------------------------------------
1 | const broadcastSendToken = data => {
2 | return new Promise((resolve, reject) => {
3 | const id = config.mainNet;
4 | const json = {
5 | contractName: "tokens",
6 | contractAction: "transfer",
7 | contractPayload: {
8 | symbol: data.currency,
9 | to: data.to,
10 | quantity: data.amount,
11 | memo: data.memo
12 | }
13 | };
14 | steem.broadcast.customJson(
15 | key,
16 | [data.username],
17 | null,
18 | id,
19 | JSON.stringify(json),
20 | (err, result) => {
21 | console.log(result, err);
22 | const message = createMessage(
23 | err,
24 | result,
25 | data,
26 | chrome.i18n.getMessage("bgd_ops_tokens"),
27 | chrome.i18n.getMessage("bgd_ops_error_broadcasting")
28 | );
29 | resolve(message);
30 | }
31 | );
32 | });
33 | };
34 |
--------------------------------------------------------------------------------
/js/background/autolock.js:
--------------------------------------------------------------------------------
1 | const startAutolock = async autoLock => {
2 | //Receive autolock from the popup (upon registration or unlocking)
3 | autolock = autoLock;
4 | console.log(autolock);
5 | if (mk == null) return;
6 | if (!autolock || autolock.type == "default") return;
7 | if (autolock.type == "locked") {
8 | chrome.idle.setDetectionInterval(parseInt(autolock.mn) * 60);
9 | chrome.idle.onStateChanged.addListener(state => {
10 | console.log(state, autolock.type);
11 | if (state === "locked") {
12 | mk = null;
13 | console.log("lock");
14 | }
15 | });
16 | } else if (autolock.type == "idle") {
17 | restartIdleCounter();
18 | }
19 | };
20 | //Create Custom Idle Function
21 | const restartIdleCounter = () => {
22 | console.log("idleCounter", new Date().toISOString());
23 | clearTimeout(timeoutIdle);
24 | timeoutIdle = setTimeout(() => {
25 | console.log("locked", new Date().toISOString());
26 | mk = null;
27 | }, autolock.mn * 60000);
28 | };
29 |
--------------------------------------------------------------------------------
/images/icon_witness-vote_default.svg:
--------------------------------------------------------------------------------
1 |
31 |
--------------------------------------------------------------------------------
/html/import.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/js/libs/globalProps.js:
--------------------------------------------------------------------------------
1 | class GlobalProps {
2 | constructor() {
3 | this.props = steem.api.getDynamicGlobalPropertiesAsync();
4 | this.median = steem.api.getCurrentMedianHistoryPriceAsync();
5 | this.fund = steem.api.getRewardFundAsync("post");
6 | this.prices = this.initGetPrice();
7 | }
8 | async getProp(key) {
9 | return (await this.props)[key];
10 | }
11 | async getMedian() {
12 | return await this.median;
13 | }
14 | async getFund(key) {
15 | return (await this.fund)[key];
16 | }
17 | async getSteemPrice() {
18 | const median = await this.getMedian();
19 | return (
20 | parseFloat(median.base.replace(" SBD", "")) /
21 | parseFloat(median.quote.replace(" STEEM", ""))
22 | );
23 | }
24 | async initGetPrice() {
25 | return await Promise.all([
26 | await getPriceSteemAsync(),
27 | await getPriceSBDAsync(),
28 | await getBTCPriceAsync()
29 | ]);
30 | }
31 | async getPrices() {
32 | const [steem, sbd, btc] = await this.prices;
33 | return [steem * btc, sbd * btc];
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Steem Monsters, LLC
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/images/error.svg:
--------------------------------------------------------------------------------
1 |
2 |
19 |
--------------------------------------------------------------------------------
/js/popup/preinit.js:
--------------------------------------------------------------------------------
1 | const rpcs = new Rpcs();
2 |
3 | window.sk_params = {
4 | page: "main"
5 | };
6 |
7 | function parseQueryString() {
8 | const queryString = window.location.search;
9 | const queryParamString = queryString.split("?").pop();
10 | const queryParams = queryParamString.split("&");
11 | for (let qi = 0; qi < queryParams.length; qi++) {
12 | const queryParam = queryParams[qi];
13 | const keyValue = queryParam.split("=");
14 | if (keyValue[0] && keyValue[1]) {
15 | let value = keyValue[1];
16 | value = value.replace(/\+/g, "%20");
17 | value = decodeURIComponent(value);
18 | window.sk_params[keyValue[0]] = value;
19 | }
20 | }
21 | }
22 |
23 | parseQueryString();
24 | initializeVisibility(true);
25 |
26 | // Check if we have mk or if accounts are stored to know if the wallet is locked unlocked or new.
27 | chrome.runtime.onMessage.addListener(function(msg, sender, sendResp) {
28 | if (msg.command == "sendBackMk") {
29 | chrome.storage.local.get(["accounts", "current_rpc"], function(items) {
30 | rpcs.setOptions(items.current_rpc || "DEFAULT");
31 | if (!msg.mk) {
32 | if (!items.accounts) {
33 | showRegister();
34 | } else {
35 | showUnlock();
36 | }
37 | } else {
38 | mk = msg.mk;
39 | initializeMainMenu();
40 | initializeVisibility();
41 | }
42 | });
43 | }
44 | });
45 |
--------------------------------------------------------------------------------
/js/background/ops/delegation.js:
--------------------------------------------------------------------------------
1 | const broadcastDelegation = data => {
2 | return new Promise((resolve, reject) => {
3 | steem.api.getDynamicGlobalPropertiesAsync().then(res => {
4 | let delegated_vest = null;
5 | if (data.unit == "SP") {
6 | const totalSteem = Number(res.total_vesting_fund_steem.split(" ")[0]);
7 | const totalVests = Number(res.total_vesting_shares.split(" ")[0]);
8 | delegated_vest = (parseFloat(data.amount) * totalVests) / totalSteem;
9 | delegated_vest = delegated_vest.toFixed(6);
10 | delegated_vest = delegated_vest.toString() + " VESTS";
11 | } else {
12 | delegated_vest = data.amount + " VESTS";
13 | }
14 | steem.broadcast.delegateVestingShares(
15 | key,
16 | data.username,
17 | data.delegatee,
18 | delegated_vest,
19 | (err, result) => {
20 | console.log(result, err);
21 | const err_message = beautifyErrorMessage(err);
22 | const message = createMessage(
23 | err,
24 | result,
25 | data,
26 | parseFloat(data.amount) === 0
27 | ? chrome.i18n.getMessage("bgd_ops_undelegate", [
28 | data.delegatee,
29 | data.username
30 | ])
31 | : chrome.i18n.getMessage("bgd_ops_delegate", [
32 | data.amount,
33 | data.delegatee,
34 | data.username
35 | ]),
36 | err_message
37 | );
38 | resolve(message);
39 | }
40 | );
41 | });
42 | });
43 | };
44 |
--------------------------------------------------------------------------------
/js/libs/hf21.js:
--------------------------------------------------------------------------------
1 | //HF21 wrapper
2 |
3 | const createProposal = (
4 | keys,
5 | creator,
6 | receiver,
7 | start_date,
8 | end_date,
9 | daily_pay,
10 | subject,
11 | permlink,
12 | extensions = "[]",
13 | callback
14 | ) => {
15 | let tx = {
16 | operations: [
17 | [
18 | "create_proposal",
19 | {
20 | creator,
21 | receiver,
22 | start_date,
23 | end_date,
24 | daily_pay,
25 | subject,
26 | permlink
27 | }
28 | ]
29 | ],
30 | extensions: JSON.parse(extensions)
31 | };
32 | return broadcast(tx, keys, callback);
33 | };
34 |
35 | const voteForProposal = (
36 | keys,
37 | voter,
38 | proposal_ids,
39 | approve,
40 | extensions = "[]",
41 | callback
42 | ) => {
43 | let tx = {
44 | operations: [
45 | [
46 | "update_proposal_votes",
47 | {
48 | voter,
49 | proposal_ids: JSON.parse(proposal_ids),
50 | approve
51 | }
52 | ]
53 | ],
54 | extensions: JSON.parse(extensions)
55 | };
56 | return broadcast(tx, keys, callback);
57 | };
58 |
59 | const removeProposal = (
60 | keys,
61 | proposal_owner,
62 | proposal_ids,
63 | extensions = "[]",
64 | callback
65 | ) => {
66 | let tx = {
67 | operations: [
68 | [
69 | "remove_proposal",
70 | {
71 | proposal_owner,
72 | proposal_ids: JSON.parse(proposal_ids)
73 | }
74 | ]
75 | ],
76 | extensions: JSON.parse(extensions)
77 | };
78 | return broadcast(tx, keys, callback);
79 | };
80 |
81 | const broadcast = (tx, keys, callback) => {
82 | steem.broadcast.send(tx, keys, (error, result) => {
83 | callback(error, result);
84 | });
85 | };
86 |
--------------------------------------------------------------------------------
/js/import.js:
--------------------------------------------------------------------------------
1 | $("#dialog_header").text(chrome.i18n.getMessage("popup_html_import_keys"));
2 | $("#text_import").html(chrome.i18n.getMessage("import_html_text"));
3 | $("#proceed").text(chrome.i18n.getMessage("popup_html_import"));
4 |
5 | $("#proceed").click(async () => {
6 | const file = $("input").prop("files")[0];
7 | if (file) {
8 | const base64 = await toBase64(file);
9 | const fileData = atob(base64);
10 | console.log(fileData);
11 | chrome.runtime.sendMessage({
12 | command: "importKeys",
13 | fileData
14 | });
15 | }
16 | });
17 | $("#file").on("change", () => {
18 | $("#file_span").text($("#file").prop("files")[0].name);
19 | });
20 | chrome.runtime.onMessage.addListener((msg, sender, sendResp) => {
21 | if (msg.command == "importResult") {
22 | $("#import_result").show();
23 | $("#mod_content").hide();
24 | if (msg.result) {
25 | $("#text_finish_import").html(
26 | chrome.i18n.getMessage("import_html_success")
27 | );
28 | $("#dialog_header_result").text(
29 | chrome.i18n.getMessage("dialog_header_success")
30 | );
31 | } else {
32 | $("#text_finish_import").html(
33 | chrome.i18n.getMessage("import_html_error")
34 | );
35 | $("#dialog_header_result").text(
36 | chrome.i18n.getMessage("dialog_header_error")
37 | );
38 | }
39 | $("#close").text(chrome.i18n.getMessage("dialog_ok"));
40 | $("#close").click(() => {
41 | window.close();
42 | });
43 | }
44 | });
45 |
46 | const toBase64 = file =>
47 | new Promise((resolve, reject) => {
48 | const reader = new FileReader();
49 | reader.readAsDataURL(file);
50 | reader.onload = () => resolve(reader.result.split(",")[1]);
51 | reader.onerror = error => reject(error);
52 | });
53 |
--------------------------------------------------------------------------------
/js/background/ops/post.js:
--------------------------------------------------------------------------------
1 | const broadcastPost = data => {
2 | console.log("broadcastPost");
3 | console.log(data);
4 | return new Promise((resolve, reject) => {
5 | if (data.comment_options == "") {
6 | steem.broadcast.comment(
7 | key,
8 | data.parent_username,
9 | data.parent_perm,
10 | data.username,
11 | data.permlink,
12 | data.title,
13 | data.body,
14 | data.json_metadata,
15 | (err, result) => {
16 | console.log(result, err);
17 | const err_message = beautifyErrorMessage(err);
18 | const message = createMessage(
19 | err,
20 | result,
21 | data,
22 | chrome.i18n.getMessage("bgd_ops_post"),
23 | err_message
24 | );
25 | resolve(message);
26 | }
27 | );
28 | } else {
29 | const operations = [
30 | [
31 | "comment",
32 | {
33 | parent_author: data.parent_username,
34 | parent_permlink: data.parent_perm,
35 | author: data.username,
36 | permlink: data.permlink,
37 | title: data.title,
38 | body: data.body,
39 | json_metadata: data.json_metadata
40 | }
41 | ],
42 | ["comment_options", JSON.parse(data.comment_options)]
43 | ];
44 | steem.broadcast.send(
45 | {
46 | operations,
47 | extensions: []
48 | },
49 | {
50 | posting: key
51 | },
52 | (err, result) => {
53 | console.log(result, err);
54 | const err_message = beautifyErrorMessage(err);
55 |
56 | const message = createMessage(
57 | err,
58 | result,
59 | data,
60 | chrome.i18n.getMessage("bgd_ops_post"),
61 | err_message
62 | );
63 | resolve(message);
64 | }
65 | );
66 | }
67 | });
68 | };
69 |
--------------------------------------------------------------------------------
/js/background/ops/power.js:
--------------------------------------------------------------------------------
1 | const broadcastPowerUp = data => {
2 | return new Promise((resolve, reject) => {
3 | steem.broadcast.transferToVesting(
4 | key,
5 | data.username,
6 | data.recipient,
7 | `${data.steem} STEEM`,
8 | (err, result) => {
9 | console.log(result, err);
10 | const err_message = beautifyErrorMessage(err);
11 | const message = createMessage(
12 | err,
13 | result,
14 | data,
15 | chrome.i18n.getMessage("bgd_ops_pu", [data.steem, data.recipient]),
16 | err_message
17 | );
18 | resolve(message);
19 | }
20 | );
21 | });
22 | };
23 |
24 | const broadcastPowerDown = data => {
25 | return new Promise((resolve, reject) => {
26 | steem.api.getDynamicGlobalPropertiesAsync().then(res => {
27 | let vestingShares = null;
28 | const totalSteem = Number(res.total_vesting_fund_steem.split(" ")[0]);
29 | const totalVests = Number(res.total_vesting_shares.split(" ")[0]);
30 | vestingShares = (parseFloat(data.steem_power) * totalVests) / totalSteem;
31 | vestingShares = vestingShares.toFixed(6);
32 | vestingShares = vestingShares.toString() + " VESTS";
33 |
34 | steem.broadcast.withdrawVesting(
35 | key,
36 | data.username,
37 | vestingShares,
38 | (err, result) => {
39 | console.log(result, err);
40 | const err_message = beautifyErrorMessage(err);
41 | const message = createMessage(
42 | err,
43 | result,
44 | data,
45 | parseFloat(data.steem_power) == 0
46 | ? chrome.i18n.getMessage("bgd_ops_pd_stop", [data.username])
47 | : chrome.i18n.getMessage("bgd_ops_pd", [
48 | data.steem_power,
49 | data.username
50 | ]),
51 | err_message
52 | );
53 | resolve(message);
54 | }
55 | );
56 | });
57 | });
58 | };
59 |
--------------------------------------------------------------------------------
/js/libs/encrypt.js:
--------------------------------------------------------------------------------
1 | // AES implementation using cryptojs
2 |
3 | var keySize = 256;
4 | var ivSize = 128;
5 | var iterations = 100;
6 |
7 | // We add an md5 hash to check if decryption is successful later on.
8 | function encryptJson(json, pwd) {
9 | json.hash = md5(json.list);
10 | var msg = encrypt(JSON.stringify(json), pwd);
11 | return msg;
12 | }
13 |
14 | // Decrypt and check the hash to confirm the decryption
15 | function decryptToJson(msg, pwd) {
16 | try {
17 | var decrypted = decrypt(msg, pwd).toString(CryptoJS.enc.Utf8);
18 | decrypted = JSON.parse(decrypted);
19 | if (decrypted.hash != null && decrypted.hash == md5(decrypted.list))
20 | return decrypted;
21 | else {
22 | return null;
23 | }
24 | } catch (e) {
25 | return null;
26 | }
27 | }
28 |
29 | // AES encryption with master password
30 | function encrypt(msg, pass) {
31 | var salt = CryptoJS.lib.WordArray.random(128 / 8);
32 | var key = CryptoJS.PBKDF2(pass, salt, {
33 | keySize: keySize / 32,
34 | iterations: iterations
35 | });
36 |
37 | var iv = CryptoJS.lib.WordArray.random(128 / 8);
38 |
39 | var encrypted = CryptoJS.AES.encrypt(msg, key, {
40 | iv: iv,
41 | padding: CryptoJS.pad.Pkcs7,
42 | mode: CryptoJS.mode.CBC
43 | });
44 | // salt, iv will be hex 32 in length
45 | // append them to the ciphertext for use in decryption
46 | var transitmessage = salt.toString() + iv.toString() + encrypted.toString();
47 | return transitmessage;
48 | }
49 |
50 | // AES decryption with master password
51 | function decrypt(transitmessage, pass) {
52 | var salt = CryptoJS.enc.Hex.parse(transitmessage.substr(0, 32));
53 | var iv = CryptoJS.enc.Hex.parse(transitmessage.substr(32, 32));
54 | var encrypted = transitmessage.substring(64);
55 |
56 | var key = CryptoJS.PBKDF2(pass, salt, {
57 | keySize: keySize / 32,
58 | iterations: iterations
59 | });
60 |
61 | var decrypted = CryptoJS.AES.decrypt(encrypted, key, {
62 | iv: iv,
63 | padding: CryptoJS.pad.Pkcs7,
64 | mode: CryptoJS.mode.CBC
65 | });
66 | return decrypted;
67 | }
68 |
--------------------------------------------------------------------------------
/js/background/ops/transfer.js:
--------------------------------------------------------------------------------
1 | const broadcastTransfer = data => {
2 | return new Promise(async (resolve, reject) => {
3 | let ac = accountsList.get(data.username);
4 | let memo = data.memo || "";
5 | let key_transfer = ac.getKey("active");
6 | if (data.memo && data.memo.length > 0 && data.memo[0] == "#") {
7 | try {
8 | const receiver = await steem.api.getAccountsAsync([data.to]);
9 | const memoReceiver = receiver["0"].memo_key;
10 | memo = window.encodeMemo(ac.keys.memo, memoReceiver, memo);
11 | } catch (e) {
12 | console.log(e);
13 | }
14 | }
15 | steem.broadcast.transfer(
16 | key_transfer,
17 | data.username,
18 | data.to,
19 | data.amount + " " + data.currency,
20 | memo,
21 | (err, result) => {
22 | console.log(result, err);
23 | let err_message = "";
24 | if (err) {
25 | console.log(err.data.stack[0].context.method);
26 | switch (err.data.stack[0].context.method) {
27 | case "adjust_balance":
28 | err_message = chrome.i18n.getMessage(
29 | "bgd_ops_transfer_adjust_balance",
30 | [data.currency]
31 | );
32 | break;
33 | case "get_account":
34 | err_message = chrome.i18n.getMessage(
35 | "bgd_ops_transfer_get_account",
36 | [data.to]
37 | );
38 | break;
39 | default:
40 | err_message = chrome.i18n.getMessage(
41 | "bgd_ops_error_broadcasting"
42 | );
43 | break;
44 | }
45 | }
46 | const message = createMessage(
47 | err,
48 | result,
49 | data,
50 | chrome.i18n.getMessage("bgd_ops_transfer_success", [
51 | data.amount,
52 | data.currency,
53 | data.username,
54 | data.to
55 | ]),
56 | err_message
57 | );
58 | resolve(message);
59 | }
60 | );
61 | });
62 | };
63 |
--------------------------------------------------------------------------------
/js/background/dialog_lifecycle.js:
--------------------------------------------------------------------------------
1 | const createPopup = (callback, popupHtml = "html/dialog.html") => {
2 | let width = 350;
3 | confirmed = false;
4 | //Ensuring only one window is opened by the extension at a time.
5 | if (id_win != null) {
6 | removeWindow(id_win);
7 | id_win = null;
8 | }
9 | //Create new window on the top right of the screen
10 | chrome.windows.getCurrent(w => {
11 | console.log(popupHtml);
12 | chrome.windows.create(
13 | {
14 | url: chrome.runtime.getURL(popupHtml),
15 | type: "popup",
16 | height: 566,
17 | width: width,
18 | left: w.width - width + w.left,
19 | top: w.top
20 | },
21 | win => {
22 | id_win = win.id;
23 | // Window create fails to take into account window size so it s updated afterwhile.
24 | chrome.windows.update(win.id, {
25 | height: 566,
26 | width: width,
27 | top: w.top,
28 | left: w.width - width + w.left
29 | });
30 |
31 | if (typeof callback === "function") {
32 | clearInterval(interval);
33 | interval = setInterval(callback, 200);
34 | setTimeout(() => {
35 | clearInterval(interval);
36 | }, 2000);
37 | }
38 | }
39 | );
40 | });
41 | };
42 |
43 | chrome.windows.onRemoved.addListener(id => {
44 | if (id == id_win && !confirmed) {
45 | console.log("error6");
46 | chrome.tabs.sendMessage(tab, {
47 | command: "answerRequest",
48 | msg: {
49 | success: false,
50 | error: "user_cancel",
51 | result: null,
52 | data: request,
53 | message: chrome.i18n.getMessage("bgd_lifecycle_request_canceled"),
54 | request_id: request_id
55 | }
56 | });
57 | }
58 | });
59 |
60 | // check if win exists before removing it
61 | const removeWindow = id_win => {
62 | console.log(id_win);
63 | chrome.windows.getAll(windows => {
64 | const hasWin = windows.filter(win => {
65 | return win.id == id_win;
66 | }).length;
67 | if (hasWin) {
68 | chrome.windows.remove(id_win);
69 | }
70 | });
71 | };
72 |
--------------------------------------------------------------------------------
/js/libs/accountsList.js:
--------------------------------------------------------------------------------
1 | class AccountsList {
2 | constructor() {
3 | this.accounts = {list: []};
4 | }
5 | init(accounts, last_account) {
6 | if (accounts) {
7 | this.accounts = accounts;
8 | this.accounts.list = this.accounts.list.map(e => new Account(e));
9 |
10 | // Sort the accounts by name
11 | this.accounts.list.sort((a, b) =>
12 | a.getName() < b.getName() ? -1 : a.getName() > b.getName() ? 1 : 0
13 | );
14 |
15 | // Add the last account selected to the front of the account list.
16 | if (last_account) {
17 | console.log(this.accounts.list, last_account);
18 | let last = this.accounts.list.find(a => a.account.name == last_account);
19 | console.log(last);
20 | if (last) {
21 | this.accounts.list.splice(this.accounts.list.indexOf(last), 1);
22 | this.accounts.list.unshift(last);
23 | }
24 | }
25 | }
26 | }
27 | getList() {
28 | return this.accounts.list || [];
29 | }
30 | get(name) {
31 | return this.getList().find(e => e.getName() === name);
32 | }
33 | getById(id) {
34 | return this.accounts.list[id];
35 | }
36 | save(mk) {
37 | chrome.storage.local.set({
38 | accounts: this.encrypt(mk)
39 | });
40 | }
41 | clear() {
42 | chrome.storage.local.clear();
43 | this.accounts = {};
44 | }
45 | isEmpty() {
46 | return !this.accounts.list || !this.accounts.list.length;
47 | }
48 | import(accounts, mk) {
49 | console.log(this.accounts.list);
50 | for (const account of accounts) {
51 | if (!this.accounts.list.find(acc => acc.getName() === account.name))
52 | this.accounts.list.push(new Account(account));
53 | }
54 | this.save(mk);
55 | }
56 | encrypt(mk) {
57 | const accounts = {
58 | ...this.accounts,
59 | list: this.accounts.list.map(e => e.getObj())
60 | };
61 | return encryptJson(accounts, mk);
62 | }
63 | add(account) {
64 | if (!this.accounts.list) this.accounts.list = [];
65 | this.accounts.list.push(account);
66 | return this;
67 | }
68 | delete(i) {
69 | this.accounts.list.splice(i, 1);
70 | return this;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "__MSG_keychain__",
3 | "description": "__MSG_description__",
4 | "default_locale": "en",
5 | "version": "1.7.6",
6 | "permissions": [
7 | "activeTab",
8 | "declarativeContent",
9 | "storage",
10 | "webNavigation",
11 | "tabs",
12 | "https://*/*",
13 | "notifications",
14 | "idle",
15 | "contextMenus"
16 | ],
17 | "browser_action": {
18 | "default_popup": "html/popup.html",
19 | "default_icon": "images/keychain_icon_small.png"
20 | },
21 | "icons": {
22 | "16": "images/keychain_icon_small.png",
23 | "32": "images/keychain_icon_small.png"
24 | },
25 | "background": {
26 | "scripts": [
27 | "vendor/crypto-js.js",
28 | "vendor/md5.min.js",
29 | "js/libs/encrypt.js",
30 | "vendor/steem.min.js",
31 | "vendor/decode.min.js",
32 | "vendor/jquery.min.js",
33 | "js/config.js",
34 | "js/libs/keychainify.js",
35 | "js/libs/account.js",
36 | "js/libs/accountsList.js",
37 | "js/libs/hf21.js",
38 | "js/libs/rpcs.js",
39 | "js/background/autolock.js",
40 | "js/background/init.js",
41 | "js/background/dialog_lifecycle.js",
42 | "js/background/errors.js",
43 | "js/background/keychainify.js",
44 | "js/background/auth.js",
45 | "js/background/ops/authority.js",
46 | "js/background/ops/broadcast.js",
47 | "js/background/ops/signTx.js",
48 | "js/background/ops/createAccount.js",
49 | "js/background/ops/customJson.js",
50 | "js/background/ops/decode.js",
51 | "js/background/ops/delegation.js",
52 | "js/background/ops/post.js",
53 | "js/background/ops/power.js",
54 | "js/background/ops/proposals.js",
55 | "js/background/ops/signBuffer.js",
56 | "js/background/ops/signedCall.js",
57 | "js/background/ops/tokens.js",
58 | "js/background/ops/transfer.js",
59 | "js/background/ops/vote.js",
60 | "js/background/ops/witness.js",
61 | "js/background/transactions.js",
62 | "js/background/context_menu.js"
63 | ],
64 | "persistent": true
65 | },
66 | "web_accessible_resources": [
67 | "/images/logo.png", "js/steem_keychain.js"
68 | ],
69 | "content_scripts": [
70 | {
71 | "matches": [
72 | "https://*/*", "http://0.0.0.0:1337/*", "http://*/*"
73 | ],
74 | "js": ["vendor/jquery.min.js", "js/web_interface.js", "js/libs/keychainify.js", "js/keychainify_content.js"]
75 | }
76 | ],
77 | "manifest_version": 2
78 | }
--------------------------------------------------------------------------------
/js/background/ops/proposals.js:
--------------------------------------------------------------------------------
1 | const broadcastCreateProposal = data => {
2 | return new Promise((resolve, reject) => {
3 | let keys = {};
4 | keys[data.typeWif] = key;
5 | createProposal(
6 | keys,
7 | data.username,
8 | data.receiver,
9 | data.start,
10 | data.end,
11 | data.daily_pay,
12 | data.subject,
13 | data.permlink,
14 | data.extensions,
15 | (err, result) => {
16 | console.log(result, err);
17 | const err_message = beautifyErrorMessage(err);
18 | const message = createMessage(
19 | err,
20 | result,
21 | data,
22 | chrome.i18n.getMessage("bgd_ops_proposal_create"),
23 | err_message
24 | );
25 | resolve(message);
26 | }
27 | );
28 | });
29 | };
30 |
31 | const broadcastUpdateProposalVote = data => {
32 | return new Promise((resolve, reject) => {
33 | let voteKeys = {};
34 | voteKeys[data.typeWif] = key;
35 | voteForProposal(
36 | voteKeys,
37 | data.username,
38 | data.proposal_ids,
39 | data.approve,
40 | data.extensions,
41 | (err, result) => {
42 | console.log(result, err);
43 | const err_message = beautifyErrorMessage(err);
44 | const message = createMessage(
45 | err,
46 | result,
47 | data,
48 | data.approve
49 | ? chrome.i18n.getMessage("bgd_ops_proposal_vote", [
50 | JSON.parse(data.proposal_ids)[0]
51 | ])
52 | : chrome.i18n.getMessage("bgd_ops_proposal_unvote", [
53 | JSON.parse(data.proposal_ids)[0]
54 | ]),
55 | err_message
56 | );
57 | resolve(message);
58 | }
59 | );
60 | });
61 | };
62 |
63 | const broadcastRemoveProposal = data => {
64 | return new Promise((resolve, reject) => {
65 | let removeProposalKeys = {};
66 | removeProposalKeys[data.typeWif] = key;
67 | removeProposal(
68 | removeProposalKeys,
69 | data.username,
70 | data.proposal_ids,
71 | data.extensions,
72 | (err, result) => {
73 | console.log(result, err);
74 | const err_message = beautifyErrorMessage(err);
75 | const message = createMessage(
76 | err,
77 | result,
78 | data,
79 | chrome.i18n.getMessage("bgd_ops_proposal_remove", [
80 | JSON.parse(data.proposal_ids)[0]
81 | ]),
82 | err_message
83 | );
84 | resolve(message);
85 | }
86 | );
87 | });
88 | };
89 |
--------------------------------------------------------------------------------
/js/popup/power.js:
--------------------------------------------------------------------------------
1 | async function preparePowerUpDown() {
2 | const SP = numberWithCommas(await activeAccount.getSP()) + " SP";
3 | const STEEM = numberWithCommas(await activeAccount.getSteem()) + " STEEM";
4 | $(".power_sp").html(SP);
5 | $(".power_steem").html(STEEM);
6 | const [
7 | withdrawn,
8 | total_withdrawing,
9 | next_vesting_withdrawal
10 | ] = await activeAccount.getPowerDown();
11 |
12 | if (total_withdrawing !== 0 && withdrawn !== total_withdrawing) {
13 | $("#powerdown_div .power")
14 | .eq(1)
15 | .show();
16 | $("#powering_down").html(withdrawn + " / " + total_withdrawing + " SP");
17 | $("#powering_down").attr(
18 | "title",
19 | chrome.i18n.getMessage("popup_next_powerdown", [next_vesting_withdrawal])
20 | );
21 | } else {
22 | $("#powerdown_div .power")
23 | .eq(1)
24 | .hide();
25 | }
26 |
27 | if (!activeAccount.hasKey("active")) {
28 | $("#power_up").addClass("disabled");
29 | $("#wrap_power_up").attr("title", chrome.i18n.getMessage("popup_pu_key"));
30 | $("#power_down").addClass("disabled");
31 | $("#wrap_power_down").attr("title", chrome.i18n.getMessage("popup_pd_key"));
32 | } else {
33 | $("#power_up").removeClass("disabled");
34 | $("#power_down").removeClass("disabled");
35 | $("#wrap_power_up").removeAttr("title");
36 | $("#wrap_power_down").removeAttr("title");
37 | }
38 | $("#power_up")
39 | .unbind("click")
40 | .click(function() {
41 | const amount = parseFloat($("#amt_pu").val()).toFixed(3) + " STEEM";
42 | $("#power_up").hide();
43 | $("#powerup_loading").show();
44 | activeAccount.powerUp(amount, $("#user_pu").val(), (err, result) => {
45 | console.log(err, result);
46 | $("#power_up").show();
47 | $("#powerup_loading").hide();
48 | if (err) {
49 | showError(chrome.i18n.getMessage("unknown_error"));
50 | } else {
51 | showConfirm(chrome.i18n.getMessage("popup_pu_success"));
52 | loadAccount(activeAccount.getName());
53 | }
54 | });
55 | });
56 |
57 | $("#power_down")
58 | .unbind("click")
59 | .click(function() {
60 | $("#power_down").hide();
61 | $("#powerdown_loading").show();
62 | activeAccount.powerDown($("#amt_pd").val(), function(err, result) {
63 | console.log(err, result);
64 | $("#power_down").show();
65 | $("#powerdown_loading").hide();
66 | if (err) {
67 | showError(chrome.i18n.getMessage("unknown_error"));
68 | } else {
69 | if ($("#amt_pd").val() !== "0")
70 | showConfirm(chrome.i18n.getMessage("popup_pd_success"));
71 | else showConfirm(chrome.i18n.getMessage("popup_pd_stop"));
72 | loadAccount(activeAccount.getName());
73 | }
74 | });
75 | });
76 | }
77 |
--------------------------------------------------------------------------------
/js/keychainify_content.js:
--------------------------------------------------------------------------------
1 | let contentScript = {
2 | init: function() {
3 | contentScript.process.checkAnchors();
4 | },
5 |
6 | /**
7 | * This contains the logic for actually processing the content on the page
8 | */
9 | process: {
10 | initialized: false,
11 | observer: null,
12 | observerConfig: {
13 | attributes: false,
14 | childList: true,
15 | subtree: true,
16 | characterData: false
17 | },
18 | observerTimer: null,
19 |
20 | /**
21 | * Initializing the MutationObserver to support pages with lazy-loading.
22 | * Dynamically reacts to page change instead of regular polling.
23 | */
24 | initObserver: function() {
25 | let body = document.body;
26 |
27 | // Using a MutationObserver to wait for a DOM change
28 | // This is to scan dynamically loaded content (lazyload of comments for example)
29 | contentScript.process.observer = new MutationObserver(
30 | (function(process) {
31 | return function(mutations) {
32 | mutations.forEach(function(mutation) {
33 | // Preventing multipl calls to checkAnchors()
34 | if (process.observerTimer) {
35 | window.clearTimeout(process.observerTimer);
36 | }
37 |
38 | // Lets wait for a DOM change
39 | process.observerTimer = window.setTimeout(function() {
40 | process.checkAnchors();
41 | }, 500);
42 | });
43 | };
44 | })(contentScript.process)
45 | );
46 |
47 | // Waiting for the DOM to be modified (lazy loading)
48 | contentScript.process.observer.observe(
49 | body,
50 | contentScript.process.observerConfig
51 | );
52 | },
53 |
54 | /**
55 | * Verify all anchors to find scammy links
56 | */
57 | checkAnchors: function() {
58 | let anchors = document.querySelectorAll(
59 | "a[href]:not(.steem-keychain-checked)"
60 | );
61 |
62 | for (let i = 0; i < anchors.length; i++) {
63 | let anchor = anchors[i];
64 |
65 | if (
66 | anchor.href &&
67 | !anchor.classList.contains("steem-keychain-checked") && // That was not checked before
68 | keychainify.isUrlSupported(anchor.href)
69 | ) {
70 | anchor.addEventListener("click", async function(e) {
71 | e.preventDefault();
72 |
73 | if (await keychainify.isKeychainifyEnabled()) {
74 | keychainify.keychainifyUrl(this.href);
75 | } else {
76 | window.location.href = this.href;
77 | }
78 | });
79 | }
80 |
81 | anchor.classList.add("steem-keychain-checked");
82 | }
83 | },
84 |
85 | /**
86 | * Initialize the scanning process
87 | */
88 | init: function() {
89 | contentScript.process.initObserver();
90 | contentScript.process.checkAnchors();
91 | }
92 | }
93 | };
94 |
95 | contentScript.init();
96 |
--------------------------------------------------------------------------------
/js/libs/rpcs.js:
--------------------------------------------------------------------------------
1 | class Rpcs {
2 | constructor() {
3 | console.log("build");
4 | this.currentRpc = "https://api.steemit.com";
5 | this.awaitRollback = false;
6 | this.DEFAULT_RPC_API = "https://api.steemkeychain.com/rpc";
7 | this.list = this.initList();
8 | }
9 |
10 | async initList() {
11 | let listRPC = [];
12 | const RPCs = ["DEFAULT", "https://api.steemit.com", "https://steemd.minnowsupportproject.org", "TESTNET"];
13 | return new Promise(resolve => {
14 | chrome.storage.local.get(["rpc"], items => {
15 | const local = items.rpc;
16 | listRPC = local != undefined ? JSON.parse(local).concat(RPCs) : RPCs;
17 | const currentrpc = this.current_rpc || "https://api.steemit.com";
18 | const list = [currentrpc].concat(
19 | listRPC.filter(e => {
20 | return e != currentrpc;
21 | })
22 | );
23 | resolve(list);
24 | });
25 | });
26 | }
27 |
28 | async getList() {
29 | return await this.list;
30 | }
31 |
32 | async setOptions(rpc, awaitRollback = false) {
33 | console.log("option:", rpc);
34 | if (rpc === this.currentRpc) {
35 | console.log("Same RPC");
36 | return;
37 | }
38 | const list = await this.getList();
39 | const newRpc = list.includes(rpc) ? rpc : this.currentRpc;
40 | if (newRpc === "TESTNET") {
41 | steem.api.setOptions({
42 | url: "https://testnet.steemitdev.com",
43 | transport: "http",
44 | useAppbaseApi: true
45 | });
46 | steem.config.set("address_prefix", "TST");
47 | steem.config.set(
48 | "chain_id",
49 | "46d82ab7d8db682eb1959aed0ada039a6d49afa1602491f93dde9cac3e8e6c32"
50 | );
51 | } else {
52 | if (newRpc === "DEFAULT") {
53 | let rpc;
54 | try {
55 | rpc = (await this.getDefaultRPC()).rpc || this.list[1];
56 | console.log(`Using ${rpc} as default.`);
57 | } catch (e) {
58 | rpc = this.currentRpc;
59 | }
60 | console.log("rpc", rpc);
61 | steem.api.setOptions({
62 | url: rpc,
63 | useAppbaseApi: true
64 | });
65 | } else {
66 | console.log("a", newRpc);
67 | steem.api.setOptions({
68 | url: newRpc,
69 | useAppbaseApi: true
70 | });
71 | }
72 | steem.config.set("address_prefix", "STM");
73 | steem.config.set(
74 | "chain_id",
75 | "0000000000000000000000000000000000000000000000000000000000000000"
76 | );
77 | }
78 | this.previousRpc = this.currentRpc;
79 | this.currentRpc = newRpc;
80 | console.log(`Now using ${this.currentRpc}, previous: ${this.previousRpc}`);
81 | this.awaitRollback = awaitRollback;
82 | return;
83 | }
84 |
85 | rollback() {
86 | if (this.awaitRollback) {
87 | console.log("Rolling back to user defined rpc");
88 | this.setOptions(this.previousRpc);
89 | this.awaitRollback = false;
90 | }
91 | return;
92 | }
93 |
94 | async getDefaultRPC() {
95 | return $.ajax({
96 | url: this.DEFAULT_RPC_API,
97 | type: "GET"
98 | });
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/vendor/md5.min.js:
--------------------------------------------------------------------------------
1 | !function(n){"use strict";function t(n,t){var r=(65535&n)+(65535&t);return(n>>16)+(t>>16)+(r>>16)<<16|65535&r}function r(n,t){return n<>>32-t}function e(n,e,o,u,c,f){return t(r(t(t(e,n),t(u,f)),c),o)}function o(n,t,r,o,u,c,f){return e(t&r|~t&o,n,t,u,c,f)}function u(n,t,r,o,u,c,f){return e(t&o|r&~o,n,t,u,c,f)}function c(n,t,r,o,u,c,f){return e(t^r^o,n,t,u,c,f)}function f(n,t,r,o,u,c,f){return e(r^(t|~o),n,t,u,c,f)}function i(n,r){n[r>>5]|=128<>>9<<4)]=r;var e,i,a,d,h,l=1732584193,g=-271733879,v=-1732584194,m=271733878;for(e=0;e>5]>>>t%32&255);return r}function d(n){var t,r=[];for(r[(n.length>>2)-1]=void 0,t=0;t>5]|=(255&n.charCodeAt(t/8))<16&&(o=i(o,8*n.length)),r=0;r<16;r+=1)u[r]=909522486^o[r],c[r]=1549556828^o[r];return e=i(u.concat(d(t)),512+8*t.length),a(i(c.concat(e),640))}function g(n){var t,r,e="";for(r=0;r>>4&15)+"0123456789abcdef".charAt(15&t);return e}function v(n){return unescape(encodeURIComponent(n))}function m(n){return h(v(n))}function p(n){return g(m(n))}function s(n,t){return l(v(n),v(t))}function C(n,t){return g(s(n,t))}function A(n,t,r){return t?r?s(t,n):C(t,n):r?m(n):p(n)}"function"==typeof define&&define.amd?define(function(){return A}):"object"==typeof module&&module.exports?module.exports=A:n.md5=A}(this);
2 | //# sourceMappingURL=md5.min.js.map
--------------------------------------------------------------------------------
/js/background/ops/authority.js:
--------------------------------------------------------------------------------
1 | const broadcastAddAccountAuthority = data => {
2 | return new Promise((resolve, reject) => {
3 | steem.broadcast.addAccountAuth(
4 | {
5 | signingKey: key,
6 | username: data.username,
7 | authorizedUsername: data.authorizedUsername,
8 | role: data.role.toLowerCase(),
9 | weight: parseInt(data.weight)
10 | },
11 | (err, result) => {
12 | console.log(result, err);
13 | const err_message = beautifyErrorMessage(err);
14 | const message = createMessage(
15 | err,
16 | result,
17 | data,
18 | chrome.i18n.getMessage("bgd_ops_add_auth", [
19 | data.role.toLowerCase(),
20 | data.authorizedUsername,
21 | data.username
22 | ]),
23 | err_message
24 | );
25 | resolve(message);
26 | }
27 | );
28 | });
29 | };
30 |
31 | const broadcastRemoveAccountAuthority = data => {
32 | return new Promise((resolve, reject) => {
33 | steem.broadcast.removeAccountAuth(
34 | {
35 | signingKey: key,
36 | username: data.username,
37 | authorizedUsername: data.authorizedUsername,
38 | role: data.role.toLowerCase()
39 | },
40 | (err, result) => {
41 | console.log(result, err);
42 | const err_message = beautifyErrorMessage(err);
43 | const message = createMessage(
44 | err,
45 | result,
46 | data,
47 | chrome.i18n.getMessage("bgd_ops_remove_auth", [
48 | data.role.toLowerCase(),
49 | data.authorizedUsername,
50 | data.username
51 | ]),
52 | err_message
53 | );
54 | resolve(message);
55 | }
56 | );
57 | });
58 | };
59 |
60 | const broadcastAddKeyAuthority = data => {
61 | return new Promise((resolve, reject) => {
62 | steem.broadcast.addKeyAuth(
63 | {
64 | signingKey: key,
65 | username: data.username,
66 | authorizedKey: data.authorizedKey,
67 | role: data.role.toLowerCase(),
68 | weight: parseInt(data.weight)
69 | },
70 | (err, result) => {
71 | console.log(result, err);
72 | const err_message = beautifyErrorMessage(err);
73 | const message = createMessage(
74 | err,
75 | result,
76 | data,
77 | chrome.i18n.getMessage("bgd_ops_add_key_auth", [
78 | data.authorizedKey,
79 | chrome.i18n.getMessage(data.role.toLowerCase()),
80 | data.username,
81 | data.weight
82 | ]),
83 | err_message
84 | );
85 | resolve(message);
86 | }
87 | );
88 | });
89 | };
90 |
91 | const broadcastRemoveKeyAuthority = data => {
92 | return new Promise((resolve, reject) => {
93 | steem.broadcast.removeKeyAuth(
94 | {
95 | signingKey: key,
96 | username: data.username,
97 | authorizedKey: data.authorizedKey,
98 | role: data.role.toLowerCase()
99 | },
100 | (err, result) => {
101 | console.log(result, err);
102 | const err_message = beautifyErrorMessage(err);
103 | const message = createMessage(
104 | err,
105 | result,
106 | data,
107 | chrome.i18n.getMessage("bgd_ops_remove_key_auth", [
108 | data.authorizedKey,
109 | chrome.i18n.getMessage(data.role.toLowerCase()),
110 | data.username
111 | ]),
112 | err_message
113 | );
114 | resolve(message);
115 | }
116 | );
117 | });
118 | };
119 |
--------------------------------------------------------------------------------
/js/popup/rpc.js:
--------------------------------------------------------------------------------
1 | async function loadRPC(current_rpc) {
2 | const listRPC = await rpcs.getList();
3 | console.log(listRPC);
4 | $("#custom_select_rpc").html("");
5 | $("#pref_div .usernames .select-selected").remove();
6 | $("#pref_div .usernames .select-items").remove();
7 |
8 | $("#custom_select_rpc select").html(
9 | listRPC.reduce((acc, val) => {
10 | return acc + "";
11 | }, "")
12 | );
13 | $("#custom_select_rpc select").append(
14 | ``
15 | );
16 | initiateCustomSelect();
17 |
18 | if (current_rpc === "TESTNET") {
19 | $("#currency_send select")
20 | .children("option:first")
21 | .text("TESTS");
22 | $("#currency_send select")
23 | .children("option:first")
24 | .val("TESTS");
25 | $("#currency_send select")
26 | .children("option:nth-child(2)")
27 | .text("TBD");
28 | $("#currency_send select")
29 | .children("option:nth-child(2)")
30 | .val("TBD");
31 | $("#wallet_currency .wallet_currency")
32 | .eq(0)
33 | .text("TESTS");
34 | $("#wallet_currency .wallet_currency")
35 | .eq(1)
36 | .text("TBD");
37 | $("#wallet_currency .wallet_currency")
38 | .eq(2)
39 | .text("TP");
40 | } else {
41 | $("#currency_send select")
42 | .children("option:first")
43 | .text("STEEM");
44 | $("#currency_send select")
45 | .children("option:first")
46 | .val("STEEM");
47 | $("#currency_send select")
48 | .children("option:nth-child(2)")
49 | .text("SBD");
50 | $("#currency_send select")
51 | .children("option:nth-child(2)")
52 | .val("SBD");
53 | $("#wallet_currency .wallet_currency")
54 | .eq(0)
55 | .text("STEEM");
56 | $("#wallet_currency .wallet_currency")
57 | .eq(1)
58 | .text("SBD");
59 | $("#wallet_currency .wallet_currency")
60 | .eq(2)
61 | .text("SP");
62 | }
63 | }
64 |
65 | function switchRPC(rpc) {
66 | rpcs.setOptions(rpc);
67 | setRPC(rpc);
68 | chrome.storage.local.set({
69 | current_rpc: rpc
70 | });
71 | }
72 |
73 | function addNewRPC(rpc) {
74 | chrome.storage.local.get(["rpc"], function(items) {
75 | let customRPCs = [];
76 | if (items.rpc != undefined) customRPCs = JSON.parse(items.rpc);
77 | customRPCs.push(rpc);
78 | chrome.storage.local.set(
79 | {
80 | rpc: JSON.stringify(customRPCs)
81 | },
82 | function() {
83 | $(".success_div")
84 | .html(chrome.i18n.getMessage("popup_rpc_added"))
85 | .show();
86 | showCustomRPC();
87 | setTimeout(function() {
88 | $(".success_div")
89 | .html("")
90 | .hide();
91 | }, 5000);
92 | }
93 | );
94 | });
95 | }
96 |
97 | function showCustomRPC() {
98 | $("#custom_rpc").empty();
99 | chrome.storage.local.get(["rpc"], function(items) {
100 | if (items.rpc) {
101 | let rpcs = JSON.parse(items.rpc);
102 | for (rpc of rpcs) {
103 | $("#custom_rpc").append(
104 | "" +
105 | rpc +
106 | "

"
107 | );
108 | }
109 | $(".deleteCustomRPC")
110 | .unbind("click")
111 | .click(function() {
112 | rpcs = rpcs.filter(e => {
113 | return (
114 | e !=
115 | $(this)
116 | .prev()
117 | .html()
118 | );
119 | });
120 | chrome.storage.local.set(
121 | {
122 | rpc: JSON.stringify(rpcs)
123 | },
124 | function() {
125 | showCustomRPC();
126 | }
127 | );
128 | });
129 | }
130 | });
131 | }
132 |
--------------------------------------------------------------------------------
/js/dialog/localize.js:
--------------------------------------------------------------------------------
1 | $("title").text(chrome.i18n.getMessage("keychain"));
2 |
3 | $("#error-ok").text(chrome.i18n.getMessage("dialog_ok"));
4 | $("#yes-unlock").text(chrome.i18n.getMessage("dialog_unlock"));
5 | $("#no-unlock").text(chrome.i18n.getMessage("dialog_cancel"));
6 |
7 | $("#tx_loading").prepend(chrome.i18n.getMessage("dialog_broadcasting"));
8 |
9 | // Info titles
10 | $("#username")
11 | .prev("h3")
12 | .text(chrome.i18n.getMessage("dialog_account"));
13 | $("#proposal_ids")
14 | .prev("h3")
15 | .text(chrome.i18n.getMessage("dialog_proposal_ids"));
16 | $("#approve")
17 | .prev("h3")
18 | .text(chrome.i18n.getMessage("dialog_approve"));
19 | $("#wif")
20 | .prev("h3")
21 | .text(chrome.i18n.getMessage("dialog_key"));
22 | $("#author")
23 | .prev("h3")
24 | .text(chrome.i18n.getMessage("dialog_author"));
25 | $("#receiver")
26 | .prev("h3")
27 | .text(chrome.i18n.getMessage("dialog_receiver"));
28 | $("#authorized_account")
29 | .prev("h3")
30 | .text(chrome.i18n.getMessage("dialog_auth_account"));
31 | $("#authorized_key")
32 | .prev("h3")
33 | .text(chrome.i18n.getMessage("dialog_auth_key"));
34 | $("#role")
35 | .prev("h3")
36 | .text(chrome.i18n.getMessage("dialog_role"));
37 | $("#perm")
38 | .prev("h3")
39 | .text(chrome.i18n.getMessage("dialog_permlink"));
40 | $("#weight")
41 | .prev("h3")
42 | .text(chrome.i18n.getMessage("dialog_weight"));
43 | $("#custom_key")
44 | .prev("h3")
45 | .text(chrome.i18n.getMessage("dialog_key"));
46 | $("#to")
47 | .prev("h3")
48 | .text(chrome.i18n.getMessage("dialog_to"));
49 | $("#amount")
50 | .prev("h3")
51 | .text(chrome.i18n.getMessage("dialog_amount"));
52 | $("#balance_title").text(chrome.i18n.getMessage("dialog_balance"));
53 | $("#balance_after_title").text(chrome.i18n.getMessage("dialog_future_balance"));
54 | $("#daily_pay")
55 | .prev("h3")
56 | .text(chrome.i18n.getMessage("dialog_daily_pay"));
57 | $("#memo")
58 | .prev("h3")
59 | .text(chrome.i18n.getMessage("dialog_memo"));
60 | $("#title")
61 | .prev("h3")
62 | .text(chrome.i18n.getMessage("dialog_title"));
63 | $("#permlink")
64 | .prev("h3")
65 | .text(chrome.i18n.getMessage("dialog_permlink"));
66 | $("#period_f")
67 | .prev("h3")
68 | .text(chrome.i18n.getMessage("dialog_period"));
69 | $("#extensions")
70 | .prev("h3")
71 | .text(chrome.i18n.getMessage("dialog_extensions"));
72 | $("#json_metadata")
73 | .prev("h3")
74 | .text(chrome.i18n.getMessage("dialog_meta"));
75 | $("#parent_username")
76 | .prev("h3")
77 | .text(chrome.i18n.getMessage("dialog_pu"));
78 | $("#parent_url")
79 | .prev("h3")
80 | .text(chrome.i18n.getMessage("dialog_pp"));
81 | $("#delegatee")
82 | .prev("h3")
83 | .text(chrome.i18n.getMessage("dialog_delegatee"));
84 | $("#amt_sp")
85 | .prev("h3")
86 | .text(chrome.i18n.getMessage("dialog_amount"));
87 | $("#message_sign")
88 | .prev("h3")
89 | .text(chrome.i18n.getMessage("dialog_message"));
90 | $("#witness")
91 | .prev("h3")
92 | .text(chrome.i18n.getMessage("dialog_witness"));
93 | $("#voteWit")
94 | .prev("h3")
95 | .text(chrome.i18n.getMessage("dialog_witness"));
96 |
97 | // Buttons
98 | $("#cancel").text(chrome.i18n.getMessage("dialog_cancel"));
99 | $("#proceed").text(chrome.i18n.getMessage("dialog_confirm"));
100 |
101 | //Togglable
102 | $("#body_toggle").html(chrome.i18n.getMessage("dialog_body_toggle"));
103 | $("#options_toggle").html(chrome.i18n.getMessage("dialog_options_toggle"));
104 | $("#custom_data").html(chrome.i18n.getMessage("dialog_data_toggle"));
105 |
106 | //options
107 | $("#max_accepted_payout").prepend(chrome.i18n.getMessage("dialog_max_payout"));
108 | $("#pct_sbd").prepend(chrome.i18n.getMessage("dialog_percentage_sbd"));
109 | $("#allow_votes_div").prepend(chrome.i18n.getMessage("dialog_allow_votes"));
110 | $("#curation_rewards").prepend(chrome.i18n.getMessage("dialog_allow_curation"));
111 | $("#beneficiaries_div").prepend(chrome.i18n.getMessage("dialog_beneficiaries"));
112 |
--------------------------------------------------------------------------------
/js/background/transactions.js:
--------------------------------------------------------------------------------
1 | const performTransaction = async (data, tab, no_confirm) => {
2 | let message = null;
3 | try {
4 | console.log('-- PERFORMING TRANSACTION --');
5 | console.log(data);
6 | if (data.rpc) rpc.setOptions(data.rpc, true);
7 | switch (data.type) {
8 | case "custom":
9 | message = await broadcastCustomJson(data);
10 | break;
11 | case "vote":
12 | message = await broadcastVote(data);
13 | break;
14 | case "transfer":
15 | message = await broadcastTransfer(data);
16 | break;
17 | case "post":
18 | console.log("post");
19 | message = await broadcastPost(data);
20 | break;
21 | case "addAccountAuthority":
22 | message = await broadcastAddAccountAuthority(data);
23 | break;
24 | case "removeAccountAuthority":
25 | message = await broadcastRemoveAccountAuthority(data);
26 | break;
27 | case "addKeyAuthority":
28 | message = await broadcastAddKeyAuthority(data);
29 | break;
30 | case "removeKeyAuthority":
31 | message = await broadcastRemoveKeyAuthority(data);
32 | break;
33 | case "broadcast":
34 | message = await broadcastData(data);
35 | break;
36 | case "createClaimedAccount":
37 | message = await broadcastCreateClaimedAccount(data);
38 | break;
39 | case "signedCall":
40 | message = await broadcastSignedCall(data);
41 | break;
42 | case "delegation":
43 | message = await broadcastDelegation(data);
44 | break;
45 | case "witnessVote":
46 | message = await broadcastWitnessVote(data);
47 | break;
48 | case "powerUp":
49 | message = await broadcastPowerUp(data);
50 | break;
51 | case "powerDown":
52 | message = await broadcastPowerDown(data);
53 | break;
54 | case "sendToken":
55 | message = await broadcastSendToken(data);
56 | break;
57 | case "createProposal":
58 | message = await broadcastCreateProposal(data);
59 | break;
60 | case "updateProposalVote":
61 | message = await broadcastUpdateProposalVote(data);
62 | break;
63 | case "removeProposal":
64 | message = await broadcastRemoveProposal(data);
65 | break;
66 | case "decode":
67 | message = await decodeMessage(data);
68 | break;
69 | case "signBuffer":
70 | message = await signBuffer(data);
71 | break;
72 | case "signTx":
73 | message = await signTx(data);
74 | break;
75 | }
76 |
77 | chrome.tabs.sendMessage(tab, message);
78 | } catch (e) {
79 | console.log("error", e);
80 | sendErrors(
81 | tab,
82 | e,
83 | chrome.i18n.getMessage("unknown_error"),
84 | chrome.i18n.getMessage("unknown_error"),
85 | data
86 | );
87 | } finally {
88 | if (no_confirm) {
89 | if (id_win != null) {
90 | removeWindow(id_win);
91 | }
92 | } else chrome.runtime.sendMessage(message);
93 | key = null;
94 | accounts = new AccountsList();
95 | rpc.rollback();
96 | }
97 | };
98 |
99 | const createMessage = (
100 | err,
101 | result,
102 | data,
103 | success_message,
104 | fail_message,
105 | publicKey
106 | ) => {
107 | return {
108 | command: "answerRequest",
109 | msg: {
110 | success: !err,
111 | error: err,
112 | result: result,
113 | data: data,
114 | message: !err ? success_message : fail_message,
115 | request_id,
116 | publicKey
117 | }
118 | };
119 | };
120 |
121 | const beautifyErrorMessage = err => {
122 | console.log(err);
123 | if (!err) return null;
124 | let error = err.message.split("xception:")[1].replace(".rethrow", ".");
125 | if (error.replace(" ", "") === "")
126 | return chrome.i18n.getMessage("unknown_error");
127 | return `${chrome.i18n.getMessage("bgd_ops_error")} : ${error}`;
128 | };
129 |
--------------------------------------------------------------------------------
/js/popup/settings.js:
--------------------------------------------------------------------------------
1 | // For accounts settings
2 |
3 | // Change master password
4 | $("#confirm_change_pwd").click(function() {
5 | if (mk === $("#old_pwd").val()) {
6 | if ($("#new_pwd").val() === $("#confirm_new_pwd").val()) {
7 | if (
8 | !$("#new_pwd")
9 | .val()
10 | .match(/^(.{0,7}|[^0-9]*|[^A-Z]*|[^a-z]*|[a-zA-Z0-9]*)$/)
11 | ) {
12 | mk = $("#new_pwd").val();
13 | accountsList.save(mk);
14 | sendMk(mk);
15 | initializeVisibility();
16 | showConfirm(chrome.i18n.getMessage("popup_master_changed"));
17 | } else showError(chrome.i18n.getMessage("popup_pwd_stronger"));
18 | } else showError(chrome.i18n.getMessage("popup_pwd_match"));
19 | } else showError(chrome.i18n.getMessage("popup_wrong_current_pwd"));
20 | });
21 |
22 | // Set "remember choice" Preferences
23 | function setPreferences(name) {
24 | chrome.storage.local.get(["no_confirm"], function(items) {
25 | try {
26 | const pref = JSON.parse(items.no_confirm);
27 | $("#pref").html("");
28 | if (!pref[name] || Object.keys(pref[name]).length == 0)
29 | $("#pref").html(chrome.i18n.getMessage("popup_html_no_pref"));
30 | for (let obj in pref[name]) {
31 | $("#pref").append(
32 | `${chrome.i18n.getMessage("popup_website")}: ${obj}
`
33 | );
34 | var display_names = {
35 | broadcast: chrome.i18n.getMessage("popup_broadcast"),
36 | signTx: chrome.i18n.getMessage("popup_sign_tx"),
37 | addAccountAuthority: chrome.i18n.getMessage("popup_add_auth"),
38 | removeAccountAuthority: chrome.i18n.getMessage("popup_remove_auth"),
39 | custom: chrome.i18n.getMessage("popup_custom"),
40 | decode: chrome.i18n.getMessage("popup_decode"),
41 | signBuffer: chrome.i18n.getMessage("popup_sign_buffer"),
42 | signedCall: chrome.i18n.getMessage("popup_signed_call"),
43 | post: chrome.i18n.getMessage("popup_post"),
44 | vote: chrome.i18n.getMessage("popup_vote")
45 | };
46 | var site_container = $(
47 | ''
48 | );
49 | for (let sub in pref[name][obj]) {
50 | site_container.append(
51 | "" +
52 | display_names[sub] +
53 | "

"
60 | );
61 | }
62 |
63 | $("#pref").append(site_container);
64 | }
65 |
66 | $(".deletePref").click(function() {
67 | const user = $(this)
68 | .attr("id")
69 | .split(",")[0];
70 | const domain = $(this)
71 | .attr("id")
72 | .split(",")[1];
73 | const type = $(this)
74 | .attr("id")
75 | .split(",")[2];
76 | delete pref[user][domain][type];
77 | if (Object.keys(pref[user][domain]).length == 0)
78 | delete pref[user][domain];
79 | chrome.storage.local.set(
80 | {
81 | no_confirm: JSON.stringify(pref)
82 | },
83 | function() {
84 | setPreferences(name);
85 | }
86 | );
87 | });
88 | } catch (e) {}
89 | });
90 | }
91 |
92 | $("#import_keys").click(() => {
93 | importKeys();
94 | });
95 |
96 | const importKeys = () => {
97 | chrome.windows.getCurrent(w => {
98 | chrome.windows.create(
99 | {
100 | url: chrome.runtime.getURL("html/import.html"),
101 | type: "popup",
102 | height: 566,
103 | focused: true,
104 | width: 350,
105 | left: w.width - 350 + w.left,
106 | top: w.top
107 | },
108 | w => w.update()
109 | );
110 | });
111 | };
112 |
113 | $("#export_keys").click(() => {
114 | var data = new Blob([accountsList.encrypt(mk)], {type: "text/plain"});
115 | var url = window.URL.createObjectURL(data);
116 | const a = document.createElement("a");
117 | a.href = url;
118 | a.download = "accounts.kc";
119 | a.click();
120 | });
121 |
--------------------------------------------------------------------------------
/js/background/context_menu.js:
--------------------------------------------------------------------------------
1 | const contextMenus = {
2 | menuActions: {
3 | contexts: {
4 | link: {
5 | transferToUser: {
6 | description: chrome.i18n.getMessage("bgd_context_transfer_user"),
7 | action: "transferToUser"
8 | },
9 | memoMessageUser: {
10 | description: chrome.i18n.getMessage("bgd_context_memo_user"),
11 | action: "memoMessageUser"
12 | },
13 | tipUser: {
14 | description: chrome.i18n.getMessage("bgd_context_tip_user"),
15 | action: "tipUser"
16 | }
17 | },
18 | page: {
19 | transferToUser: {
20 | description: chrome.i18n.getMessage("bgd_context_transfer_author"),
21 | action: "transferToUser"
22 | },
23 | memoMessageUser: {
24 | description: chrome.i18n.getMessage("bgd_context_memo_author"),
25 | action: "memoMessageUser"
26 | },
27 | tipUser: {
28 | description: chrome.i18n.getMessage("bgd_context_tip_author"),
29 | action: "tipUser"
30 | }
31 | }
32 | },
33 |
34 | actions: {
35 | transferToUser: function(url) {
36 | const user = url
37 | .split("@")
38 | .pop()
39 | .split("/")[0];
40 | createPopup(null, `html/popup.html?page=send_div&to=${user}&noback=1`);
41 | },
42 | memoMessageUser: function(url) {
43 | const user = url
44 | .split("@")
45 | .pop()
46 | .split("/")[0];
47 | createPopup(
48 | null,
49 | `html/popup.html?page=send_div&to=${user}&amount=0.001&noback=1`
50 | );
51 | },
52 | tipUser: function(url) {
53 | const user = url
54 | .split("@")
55 | .pop()
56 | .split("/")[0];
57 | console.log(url, user);
58 | createPopup(
59 | null,
60 | // @TODO make a settings UI for customizing default MEMO message and tipping amount
61 | `html/popup.html?page=send_div&to=${user}&amount=1&memo=I+appreciate+your+work+and+this+is+a+little+symbolic+tip+to+show+support.&noback=1`
62 | );
63 | }
64 | }
65 | },
66 |
67 | init: function() {
68 | console.log("initializing context menu");
69 | const contexts = ["link", "page"];
70 |
71 | for (let i = 0; i < contexts.length; i++) {
72 | const context = contexts[i];
73 |
74 | for (let menuActionName in contextMenus.menuActions.contexts[context]) {
75 | if (
76 | contextMenus.menuActions.contexts[context].hasOwnProperty(
77 | menuActionName
78 | )
79 | ) {
80 | const menuAction =
81 | contextMenus.menuActions.contexts[context][menuActionName];
82 | const title = menuAction.description;
83 |
84 | if (context === "link") {
85 | contextMenus.menuActions.contexts[context][
86 | menuActionName
87 | ].menuItemId = chrome.contextMenus.create({
88 | title: title,
89 | contexts: [context],
90 | onclick: contextMenus.genericOnClick,
91 | targetUrlPatterns: ["https://*/@*"] // @TODO make a better filter, maybe with a whitelist of known dApps
92 | });
93 | } else if (context === "page") {
94 | contextMenus.menuActions.contexts[context][
95 | menuActionName
96 | ].menuItemId = chrome.contextMenus.create({
97 | title: title,
98 | contexts: [context],
99 | onclick: contextMenus.genericOnClick,
100 | documentUrlPatterns: ["https://*/@*", "https://*/*/@*"] // @TODO make a better filter, maybe with a whitelist of known dApps
101 | });
102 | }
103 | }
104 | }
105 | }
106 | },
107 |
108 | genericOnClick: function(info, tab) {
109 | for (let context in contextMenus.menuActions.contexts) {
110 | if (contextMenus.menuActions.contexts.hasOwnProperty(context)) {
111 | for (let menuName in contextMenus.menuActions.contexts[context]) {
112 | if (
113 | contextMenus.menuActions.contexts[context].hasOwnProperty(menuName)
114 | ) {
115 | const menuAction =
116 | contextMenus.menuActions.contexts[context][menuName];
117 | if (menuAction.menuItemId === info.menuItemId) {
118 | const url = info[context + "Url"];
119 | contextMenus.menuActions.actions[menuAction.action](
120 | url,
121 | menuAction
122 | );
123 | }
124 | }
125 | }
126 | }
127 | }
128 | }
129 | };
130 | contextMenus.init();
131 |
--------------------------------------------------------------------------------
/js/background/init.js:
--------------------------------------------------------------------------------
1 | let mk = null;
2 | let id_win = null;
3 | let key = null;
4 | let confirmed = false;
5 | let tab = null;
6 | let request = null;
7 | let request_id = null;
8 | let accountsList = new AccountsList();
9 | let timeoutIdle = null;
10 | let autolock = null;
11 | let interval = null;
12 | let rpc = new Rpcs();
13 | // Lock after the browser is idle for more than 10 minutes
14 |
15 | chrome.storage.local.get(["current_rpc", "autolock"], function(items) {
16 | if (items.autolock) startAutolock(JSON.parse(items.autolock));
17 | rpc.setOptions(items.current_rpc || "DEFAULT");
18 | });
19 |
20 | //Listen to the other parts of the extension
21 |
22 | const chromeMessageHandler = (msg, sender, sendResp) => {
23 | // Send mk upon request from the extension popup.
24 | restartIdleCounterIfNeeded(autolock, msg);
25 | switch (msg.command) {
26 | case "getMk":
27 | chrome.runtime.sendMessage({
28 | command: "sendBackMk",
29 | mk: mk
30 | });
31 | break;
32 | case "stopInterval":
33 | clearInterval(interval);
34 | break;
35 | case "setRPC":
36 | rpc.setOptions(msg.rpc);
37 | break;
38 | case "sendMk":
39 | //Receive mk from the popup (upon registration or unlocking)
40 | mk = msg.mk;
41 | break;
42 | case "sendAutolock":
43 | startAutolock(JSON.parse(msg.autolock));
44 | break;
45 | case "sendRequest":
46 | // Receive request (website -> content_script -> background)
47 | // create a window to let users confirm the transaction
48 | tab = sender.tab.id;
49 | checkBeforeCreate(msg.request, tab, msg.domain);
50 | request = msg.request;
51 | request_id = msg.request_id;
52 | break;
53 | case "unlockFromDialog":
54 | // Receive unlock request from dialog
55 | unlockFromDialog(msg);
56 | break;
57 | case "acceptTransaction":
58 | if (msg.keep) saveNoConfirm(msg);
59 | confirmed = true;
60 | performTransaction(msg.data, msg.tab, false);
61 | // upon receiving the confirmation from user, perform the transaction and notify content_script. Content script will then notify the website.
62 | break;
63 | case "importKeys":
64 | try {
65 | chrome.storage.local.get(["accounts"], function(items) {
66 | const decrypt = decryptToJson(items.accounts, mk);
67 | if (!decrypt)
68 | chrome.runtime.sendMessage({
69 | command: "importResult",
70 | result: false
71 | });
72 | accountsList.init(decrypt);
73 | const accounts = decryptToJson(msg.fileData, mk);
74 | console.log(accounts);
75 | accountsList.import(accounts.list, mk);
76 | chrome.runtime.sendMessage({
77 | command: "importResult",
78 | result: true
79 | });
80 | });
81 | } catch (e) {
82 | console.log(e);
83 | chrome.runtime.sendMessage({
84 | command: "importResult",
85 | result: false
86 | });
87 | }
88 | break;
89 | }
90 | };
91 |
92 | const saveNoConfirm = msg => {
93 | chrome.storage.local.get(["no_confirm"], function(items) {
94 | let keep =
95 | items.no_confirm == null || items.no_confirm == undefined
96 | ? {}
97 | : JSON.parse(items.no_confirm);
98 | if (keep[msg.data.username] == undefined) {
99 | keep[msg.data.username] = {};
100 | }
101 | if (keep[msg.data.username][msg.domain] == undefined) {
102 | keep[msg.data.username][msg.domain] = {};
103 | }
104 | keep[msg.data.username][msg.domain][msg.data.type] = true;
105 | chrome.storage.local.set({
106 | no_confirm: JSON.stringify(keep)
107 | });
108 | });
109 | };
110 |
111 | const unlockFromDialog = msg => {
112 | chrome.storage.local.get(["accounts"], function(items) {
113 | if (!items.accounts) {
114 | sendErrors(
115 | msg.tab,
116 | "no_wallet",
117 | chrome.i18n.getMessage("bgd_init_no_wallet"),
118 | "",
119 | msg.data
120 | );
121 | } else {
122 | if (decryptToJson(items.accounts, msg.mk) != null) {
123 | mk = msg.mk;
124 | startAutolock(autolock);
125 | checkBeforeCreate(msg.data, msg.tab, msg.domain);
126 | } else {
127 | chrome.runtime.sendMessage({
128 | command: "wrongMk"
129 | });
130 | }
131 | }
132 | });
133 | };
134 |
135 | const restartIdleCounterIfNeeded = (autolock, msg) => {
136 | if (
137 | autolock != null &&
138 | autolock.type == "idle" &&
139 | (msg.command == "getMk" ||
140 | msg.command == "setRPC" ||
141 | msg.command == "sendMk" ||
142 | msg.command == "sendRequest" ||
143 | msg.command == "acceptTransaction" ||
144 | msg.command == "ping")
145 | )
146 | restartIdleCounter();
147 | };
148 |
149 | chrome.runtime.onMessage.addListener(chromeMessageHandler);
150 |
--------------------------------------------------------------------------------
/js/popup/delegate.js:
--------------------------------------------------------------------------------
1 | const prepareDelegationTab = async () => {
2 | $("#send_del")
3 | .unbind("click")
4 | .click(function() {
5 | $("#send_del").hide();
6 | $("#del_loading").show();
7 | activeAccount.delegateSP(
8 | $("#amt_del").val(),
9 | $("#username_del").val(),
10 | function(err, result) {
11 | console.log(err, result);
12 | $("#send_del").show();
13 | $("#del_loading").hide();
14 | if (err) {
15 | showError(chrome.i18n.getMessage("unknown_error"));
16 | } else {
17 | showConfirm(chrome.i18n.getMessage("popup_delegate_success"));
18 | loadAccount(activeAccount.getName());
19 | }
20 | }
21 | );
22 | });
23 | const [delegatees, delegators] = [
24 | await activeAccount.getDelegatees(),
25 | await activeAccount.getDelegators()
26 | ];
27 | console.log(delegatees, delegators);
28 | if (!activeAccount.hasKey("active")) {
29 | $("#send_del").addClass("disabled");
30 | $("#wrap_send_del").attr(
31 | "title",
32 | chrome.i18n.getMessage("popup_delegate_key")
33 | );
34 | $("#edit_del").addClass("disabled");
35 | $("#wrap_edit_del").attr(
36 | "title",
37 | chrome.i18n.getMessage("popup_delegate_key")
38 | );
39 | } else {
40 | $("#send_del").removeClass("disabled");
41 | $("#edit_del").removeClass("disabled");
42 | $("#wrap_edit_del").removeAttr("title");
43 | $("#wrap_send_del").removeAttr("title");
44 | }
45 |
46 | displayDelegationMain(delegators, delegatees);
47 | displayOutgoingDelegations(delegatees);
48 | displayIncomingDelegations(delegators);
49 | };
50 |
51 | function getSumOutgoing(delegatees) {
52 | return delegatees.reduce(function(total, elt) {
53 | return total + parseFloat(elt.sp);
54 | }, 0);
55 | }
56 |
57 | function getSumIncoming(delegators) {
58 | return delegators.reduce(function(total, elt) {
59 | return total + parseFloat(elt.sp);
60 | }, 0);
61 | }
62 |
63 | const displayIncomingDelegations = delegators => {
64 | const sumIncoming = getSumIncoming(delegators);
65 | delegators = delegators.sort(function(a, b) {
66 | return b.sp - a.sp;
67 | });
68 | $("#total_incoming span")
69 | .eq(1)
70 | .html(numberWithCommas(sumIncoming.toFixed(3)) + " SP");
71 | $("#list_incoming").empty();
72 | for (delegator of delegators) {
73 | $("#list_incoming").append(
74 | "@" +
75 | delegator.delegator +
76 | "" +
77 | numberWithCommas(delegator.sp) +
78 | "
"
79 | );
80 | }
81 | };
82 |
83 | const displayDelegationMain = async (delegators, delegatees) => {
84 | const sumIncoming = getSumIncoming(delegators);
85 | const sumOutgoing = getSumOutgoing(delegatees);
86 | $("#incoming_del").html("+ " + numberWithCommas(sumIncoming.toFixed(3)));
87 | $("#outgoing_del").html("- " + numberWithCommas(sumOutgoing.toFixed(3)));
88 | $("#available_del").html(
89 | numberWithCommas(
90 | ((await activeAccount.getSP()) - 5 - sumOutgoing).toFixed(3)
91 | )
92 | );
93 | };
94 |
95 | const displayOutgoingDelegations = delegatees => {
96 | const sumOutgoing = getSumOutgoing(delegatees);
97 | $("#total_outgoing span")
98 | .eq(1)
99 | .html("- " + numberWithCommas(sumOutgoing.toFixed(3)) + " SP");
100 | $("#list_outgoing").empty();
101 | for (delegatee of delegatees) {
102 | $("#list_outgoing").append(
103 | "@" +
104 | delegatee.delegatee +
105 | "" +
106 | numberWithCommas(delegatee.sp) +
107 | "
"
108 | );
109 | }
110 |
111 | $(".line_outgoing img")
112 | .unbind("click")
113 | .click(function() {
114 | $("#outgoing_del_div").hide();
115 | $("#edit_del_div").show();
116 | let that = $(this);
117 | let this_delegatee = delegatees.filter(function(elt) {
118 | return (
119 | elt.delegatee ==
120 | that
121 | .parent()
122 | .children()
123 | .eq(0)
124 | .html()
125 | .replace("@", "")
126 | );
127 | });
128 | showEditDiv(this_delegatee);
129 | });
130 | };
131 |
132 | const showEditDiv = async delegatees => {
133 | const delegatee = delegatees[0];
134 | $("#this_outgoing_del").html(
135 | numberWithCommas(parseFloat(delegatee.sp)) + " SP"
136 | );
137 | $("#this_available_del").html(
138 | numberWithCommas(
139 | (
140 | parseFloat(
141 | $("#available_del")
142 | .html()
143 | .replace(",", "")
144 | ) + parseFloat(delegatee.sp)
145 | ).toFixed(3)
146 | ) + " SP"
147 | );
148 | $("#username_del span").html(delegatee.delegatee);
149 | $("#edit_del")
150 | .unbind("click")
151 | .click(function() {
152 | $("#edit_del").hide();
153 | $("#edit_del_loading").show();
154 | activeAccount.delegateSP(
155 | $("#amt_edit_del").val(),
156 | delegatee.delegatee,
157 | function(err, result) {
158 | console.log(err, result);
159 | $("#edit_del").show();
160 | $("#edit_del_loading").hide();
161 | if (err) {
162 | showError(chrome.i18n.getMessage("unknown_error"));
163 | } else {
164 | showConfirm(
165 | chrome.i18n.getMessage("popup_delegate_change_success")
166 | );
167 | loadAccount(activeAccount.getName());
168 | $("#edit_del_div").hide();
169 | $("#outgoing_del_div").show();
170 | }
171 | }
172 | );
173 | });
174 | };
175 |
--------------------------------------------------------------------------------
/js/popup/witness.js:
--------------------------------------------------------------------------------
1 | let witness_ranks = null;
2 |
3 | async function prepareWitnessDiv(witness_votes, proxy) {
4 | witness_ranks = await getWitnessRanks();
5 | $("#votes_remaining span").html(30 - witness_votes.length);
6 | if (proxy != "") {
7 | $("#proxy div").html(`${chrome.i18n.getMessage("popup_proxy")}: @${proxy}`);
8 | $("#proxy").show();
9 | } else $("#proxy").hide();
10 | if (!activeAccount.hasKey("active")) $("#proxy div").addClass("no_active");
11 | else $("#proxy div").removeClass("no_active");
12 |
13 | $("#list_wit").empty();
14 |
15 | if (witness_votes) {
16 | for (wit of witness_votes) {
17 | const isActive =
18 | witness_ranks && witness_ranks.find(e => e.name == wit)
19 | ? "active"
20 | : "disabled";
21 | $("#list_wit").append(
22 | "@" +
23 | wit +
24 | "" +
25 | isActive +
26 | "
"
29 | );
30 | }
31 | }
32 |
33 | $("#top100_div").empty();
34 |
35 | if (witness_ranks) {
36 | let i = 0;
37 | for (wit of witness_ranks) {
38 | const isVoted = witness_votes.includes(wit.name)
39 | ? "wit-vote wit-voted"
40 | : "wit-vote wit-not-voted";
41 | i++;
42 | if (i <= 100)
43 | $("#top100_div").append(
44 | "" +
45 | wit.rank +
46 | "@" +
47 | wit.name +
48 | "
"
51 | );
52 | }
53 | }
54 |
55 | if (!activeAccount.hasKey("active")) $(".wit-vote").addClass("no_cursor");
56 | else $(".wit-vote").removeClass("no_cursor");
57 |
58 | $("#proxy div")
59 | .unbind("click")
60 | .click(function() {
61 | $("#proxy").hide();
62 | steem.broadcast.accountWitnessProxy(
63 | activeAccount.getKey("active"),
64 | activeAccount.getName(),
65 | "",
66 | function(err, result) {
67 | console.log(err, result);
68 | }
69 | );
70 | });
71 |
72 | $(".wit-vote")
73 | .unbind("click")
74 | .click(function() {
75 | const voted_wit = $(this).hasClass("wit-voted");
76 | const that = this;
77 | console.log(voted_wit);
78 | $(that).addClass("wit-loading");
79 | steem.broadcast.accountWitnessVote(
80 | activeAccount.getKey("active"),
81 | activeAccount.getName(),
82 | $(this)
83 | .prev()
84 | .html()
85 | .replace("@", ""),
86 | $(this).hasClass("wit-voted") ? 0 : 1,
87 | function(err, result) {
88 | console.log(err, result);
89 | $(that).removeClass("wit-loading");
90 | if (err == null) {
91 | if (voted_wit) {
92 | console.log("unvoted");
93 | $(that).removeClass("wit-voted");
94 | $(that).addClass("wit-not-voted");
95 | $("#votes_remaining span").html(
96 | parseInt($("#votes_remaining span").html()) + 1
97 | );
98 | } else {
99 | console.log("voted");
100 | $(that).removeClass("wit-not-voted");
101 | $(that).addClass("wit-voted");
102 | $("#votes_remaining span").html(
103 | parseInt($("#votes_remaining span").html()) - 1
104 | );
105 | }
106 | }
107 | }
108 | );
109 | });
110 |
111 | $(".witness-row img")
112 | .unbind("click")
113 | .click(function() {
114 | const acc = $(this)
115 | .parent()
116 | .find(".witName")
117 | .html()
118 | .replace("@", "");
119 | const that = this;
120 | $(that).attr("src", "../images/loading.gif");
121 | steem.broadcast.accountWitnessVote(
122 | activeAccount.getKey("active"),
123 | activeAccount.getName(),
124 | acc,
125 | 0,
126 | function(err, result) {
127 | $(that).attr("src", "../images/delete.png");
128 | if (err == null) {
129 | showConfirm(
130 | chrome.i18n.getMessage("popup_success_unvote_wit", [acc])
131 | );
132 | loadAccount(activeAccount.getName());
133 | } else showError(chrome.i18n.getMessage("unknown_error"));
134 | }
135 | );
136 | });
137 |
138 | $("#vote_wit")
139 | .unbind("click")
140 | .click(function() {
141 | $("#vote_wit").hide();
142 | $("#wit_loading").show();
143 | if ($("#witness_div select option:selected").val() === "Wit") {
144 | steem.broadcast.accountWitnessVote(
145 | activeAccount.getKey("active"),
146 | activeAccount.getName(),
147 | $("#wit-username").val(),
148 | 1,
149 | function(err, result) {
150 | $("#vote_wit").show();
151 | $("#wit_loading").hide();
152 | if (!err) {
153 | showConfirm(
154 | chrome.i18n.getMessage("popup_success_wit", [
155 | $("#wit-username").val()
156 | ])
157 | );
158 | loadAccount(activeAccount.getName());
159 | } else showError(chrome.i18n.getMessage("unknown_error"));
160 | }
161 | );
162 | } else {
163 | steem.broadcast.accountWitnessProxy(
164 | activeAccount.getKey("active"),
165 | activeAccount.getName(),
166 | $("#wit-username").val(),
167 | function(err, result) {
168 | $("#wit_loading").hide();
169 | $("#vote_wit").show();
170 | if (!err) {
171 | showConfirm(
172 | chrome.i18n.getMessage("popup_success_proxy", [
173 | $("#wit-username").val()
174 | ])
175 | );
176 | loadAccount(activeAccount.getName());
177 | } else {
178 | console.log(err);
179 | showError(chrome.i18n.getMessage("unknown_error"));
180 | }
181 | }
182 | );
183 | }
184 | });
185 | }
186 |
--------------------------------------------------------------------------------
/js/libs/keychainify.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * @type {{requestTransfer: keychainify.requestTransfer, initBackground: keychainify.initBackground, isKeychainifyEnabled: (function(): Promise), getVarsFromURL: (function(*)), requestWitnessVote: keychainify.requestWitnessVote, keychainifyUrl: keychainify.keychainifyUrl, requestDelegation: keychainify.requestDelegation, dispatchRequest: keychainify.dispatchRequest}}
4 | */
5 | const keychainify = {
6 | /**
7 | * Checks local storage for whether the feature has been disabled by the user
8 | * @returns {Promise}
9 | */
10 | isKeychainifyEnabled: function() {
11 | return new Promise(function(resolve, reject) {
12 | try {
13 | chrome.storage.local.get(["keychainify_enabled"], function(items) {
14 | const featureStatus =
15 | items.hasOwnProperty("keychainify_enabled") &&
16 | items.keychainify_enabled;
17 | resolve(featureStatus);
18 | });
19 | } catch (err) {
20 | reject(err);
21 | }
22 | });
23 | },
24 |
25 | isUrlSupported: function(url) {
26 | return url.includes("steemconnect.com/sign/transfer");
27 | },
28 |
29 | /**
30 | * Transform a known URL to a Keychain operation
31 | * @param tab
32 | */
33 | keychainifyUrl: function(tab) {
34 | let url;
35 | if (typeof tab === "string") {
36 | url = tab;
37 | tab = null;
38 | } else {
39 | url = tab.url;
40 | }
41 |
42 | const vars = keychainify.getVarsFromURL(url);
43 | let payload = {},
44 | defaults = {};
45 |
46 | switch (true) {
47 | /**
48 | * Transfer fund
49 | */
50 | case (url.includes("steemconnect.com/sign/transfer")):
51 | defaults = {
52 | from: null,
53 | to: null,
54 | amount: 0,
55 | memo: "",
56 | currency: "STEEM"
57 | };
58 |
59 | payload = Object.assign(defaults, vars);
60 |
61 | [payload.amount, payload.currency] = vars.amount.split(" ");
62 | keychainify.requestTransfer(
63 | tab,
64 | payload.from,
65 | payload.to,
66 | payload.amount,
67 | payload.memo,
68 | payload.currency
69 | );
70 | break;
71 |
72 | /**
73 | * Delegate Steem Power
74 | */
75 | case (url.includes("steemconnect.com/sign/delegate-vesting-shares")):
76 | // @TODO currently Steem Keychain does not allow null delegator account. Awaiting https://github.com/MattyIce/steem-keychain/issues/101 to continue
77 | //let [amount, unit] = vars.vesting_shares.split(' ');
78 | //keychainify.requestDelegation(null, vars.delegatee, amount, unit, null);
79 | window.location.href = url;
80 | break;
81 |
82 | case (url.includes("steemconnect.com/sign/account-witness-vote")):
83 | // @TODO currently Steem Keychain does not allow null voter account. Awaiting https://github.com/MattyIce/steem-keychain/issues/101 to continue
84 | //keychainify.requestWitnessVote(null, vars.witness, vars.approve);
85 | window.location.href = url;
86 | break;
87 | }
88 | },
89 |
90 | /**
91 | * Dispatch a Keychain operation
92 | * @param tab
93 | * @param request
94 | */
95 | dispatchRequest: function(tab, request) {
96 | const now = new Date().getTime();
97 |
98 | if (tab) {
99 | chromeMessageHandler(
100 | {
101 | command: "sendRequest",
102 | request: request,
103 | domain: window.location.hostname,
104 | request_id: now
105 | },
106 | {
107 | tab: tab
108 | }
109 | );
110 | } else {
111 | chrome.runtime.sendMessage({
112 | command: "sendRequest",
113 | request: request,
114 | domain: window.location.hostname,
115 | request_id: now
116 | });
117 | }
118 | },
119 |
120 | /**
121 | * Requesting a Keychain transfer operation
122 | * @param tab
123 | * @param account
124 | * @param to
125 | * @param amount
126 | * @param memo
127 | * @param currency
128 | * @param enforce
129 | */
130 | requestTransfer: function(
131 | tab,
132 | account,
133 | to,
134 | amount,
135 | memo,
136 | currency,
137 | enforce = false
138 | ) {
139 | const request = {
140 | type: "transfer",
141 | username: account,
142 | to: to,
143 | amount: amount,
144 | memo: memo,
145 | enforce: enforce,
146 | currency: currency
147 | };
148 |
149 | keychainify.dispatchRequest(tab, request);
150 | },
151 |
152 | /**
153 | * Requesting a Keychain witness vote operation
154 | * @param tab
155 | * @param username
156 | * @param witness
157 | * @param vote
158 | */
159 | requestWitnessVote: function(tab, username, witness, vote) {
160 | var request = {
161 | type: "witnessVote",
162 | username: username,
163 | witness: witness,
164 | vote: vote
165 | };
166 | keychainify.dispatchRequest(tab, equest);
167 | },
168 |
169 | /**
170 | * Requesting a Keychain delegation operation
171 | * @param tab
172 | * @param username
173 | * @param delegatee
174 | * @param amount
175 | * @param unit
176 | */
177 | requestDelegation: function(tab, username, delegatee, amount, unit) {
178 | const request = {
179 | type: "delegation",
180 | username: username,
181 | delegatee: delegatee,
182 | amount: amount,
183 | unit: unit
184 | };
185 |
186 | keychainify.dispatchRequest(tab, request);
187 | },
188 |
189 | /**
190 | * Parsing the query string
191 | * @param url
192 | */
193 | getVarsFromURL: function(url) {
194 | const argsParsed = {};
195 |
196 | if (url.indexOf("?") !== -1) {
197 | const query = url.split("?").pop();
198 | const args = query.split("&");
199 | let arg, kvp, key, value;
200 |
201 | for (let i = 0; i < args.length; i++) {
202 | arg = args[i];
203 | if (arg.indexOf("=") === -1) {
204 | argsParsed[decodeURIComponent(arg)] = true;
205 | } else {
206 | kvp = arg.split("=");
207 | key = decodeURIComponent(kvp[0]);
208 | value = decodeURIComponent(kvp[1]);
209 | argsParsed[key] = value;
210 | }
211 | }
212 | }
213 |
214 | return argsParsed;
215 | }
216 | };
217 |
--------------------------------------------------------------------------------
/html/dialog.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
28 |
29 |
30 |
31 |

32 |
33 |
34 |
35 |
36 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
70 |
71 |
72 |
73 |
74 |
75 |

76 |
77 |
78 |

79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
--------------------------------------------------------------------------------
/js/libs/account.js:
--------------------------------------------------------------------------------
1 | class Account {
2 | constructor(obj) {
3 | this.account = obj || {};
4 | }
5 | init() {
6 | this.info = steem.api.getAccountsAsync([this.account.name]);
7 | this.props = new GlobalProps();
8 | this.delegatees = getDelegatees(this.account.name);
9 | this.delegators = getDelegators(this.account.name);
10 | }
11 | getObj() {
12 | return this.account;
13 | }
14 | getName() {
15 | return this.account.name;
16 | }
17 | getKeys() {
18 | return this.account.keys;
19 | }
20 | getKey(key) {
21 | return this.account.keys[key];
22 | }
23 | hasKey(key) {
24 | return this.account.keys.hasOwnProperty(key);
25 | }
26 | setKey(key, val) {
27 | this.account.keys[key] = val;
28 | }
29 | deleteKey(key) {
30 | delete this.account.keys[key];
31 | delete this.account.keys[`${key}Pubkey`];
32 | }
33 | async getAccountInfos() {
34 | return (await this.info)[0];
35 | }
36 | async getAccountInfo(key) {
37 | const info = (await this.info)[0];
38 | return info[key];
39 | }
40 | async getAvailableRewards() {
41 | this.reward_sbd = await this.getAccountInfo("reward_sbd_balance");
42 | this.reward_vests = await this.getAccountInfo("reward_vesting_balance");
43 | const reward_sp = (await this.toSP(this.reward_vests)) + " SP";
44 | this.reward_steem = await this.getAccountInfo("reward_steem_balance");
45 | let rewardText = chrome.i18n.getMessage("popup_account_redeem") + ":
";
46 | if (getValFromString(reward_sp) != 0) rewardText += reward_sp + " / ";
47 | if (getValFromString(this.reward_sbd) != 0)
48 | rewardText += this.reward_sbd + " / ";
49 | if (getValFromString(this.reward_steem) != 0)
50 | rewardText += this.reward_steem + " / ";
51 | rewardText = rewardText.slice(0, -3);
52 | return [this.reward_sbd, reward_sp, this.reward_steem, rewardText];
53 | }
54 | async toSP(vests) {
55 | return steem.formatter
56 | .vestToSteem(
57 | vests,
58 | await this.props.getProp("total_vesting_shares"),
59 | await this.props.getProp("total_vesting_fund_steem")
60 | )
61 | .toFixed(3);
62 | }
63 |
64 | claimRewards(callback) {
65 | steem.broadcast.claimRewardBalance(
66 | this.getKey("posting"),
67 | this.getName(),
68 | this.reward_steem,
69 | this.reward_sbd,
70 | this.reward_vests,
71 | callback
72 | );
73 | }
74 |
75 | async getVotingMana() {
76 | const vm = await getVotingMana(await this.getAccountInfos());
77 | const full = getTimeBeforeFull(vm * 100);
78 | return [vm, full];
79 | }
80 |
81 | async getSteem() {
82 | return (await this.getAccountInfo("balance")).replace(" STEEM", "");
83 | }
84 |
85 | async getSBD() {
86 | return (await this.getAccountInfo("sbd_balance")).replace(" SBD", "");
87 | }
88 |
89 | async getSP() {
90 | return await this.toSP(
91 | (await this.getAccountInfo("vesting_shares")).replace(" VESTS", "")
92 | );
93 | }
94 |
95 | async getRC() {
96 | return await getRC(this.account.name);
97 | }
98 |
99 | async getVotingDollars(percentage) {
100 | return await getVotingDollarsPerAccount(
101 | percentage,
102 | await this.getAccountInfos(),
103 | (await this.props.getFund("reward_balance")).replace("STEEM", ""),
104 | (await this.props.getFund("recent_claims")).replace("STEEM", ""),
105 | await this.props.getSteemPrice(),
106 | await this.props.getProp("vote_power_reserve_rate"),
107 | false
108 | );
109 | }
110 |
111 | async getAccountValue() {
112 | const [steem, sbd] = await this.props.getPrices();
113 | return (
114 | numberWithCommas(
115 | "$ " +
116 | (
117 | sbd * parseInt(await this.getSBD()) +
118 | steem *
119 | (parseInt(await this.getSP()) + parseInt(await this.getSteem()))
120 | ).toFixed(2)
121 | ) + "\t USD"
122 | );
123 | }
124 |
125 | async getTransfers() {
126 | const result = await steem.api.getAccountHistoryAsync(
127 | this.getName(),
128 | -1,
129 | 1000
130 | );
131 | let transfers = result.filter(tx => tx[1].op[0] === "transfer");
132 | transfers = transfers.slice(-10).reverse();
133 | return transfers;
134 | }
135 |
136 | async getPowerDown() {
137 | const totalSteem = Number(
138 | (await this.props.getProp("total_vesting_fund_steem")).split(" ")[0]
139 | );
140 | const totalVests = Number(
141 | (await this.props.getProp("total_vesting_shares")).split(" ")[0]
142 | );
143 | const withdrawn = (
144 | (((await this.getAccountInfo("withdrawn")) / totalVests) * totalSteem) /
145 | 1000000
146 | ).toFixed(0);
147 | const total_withdrawing = (
148 | (((await this.getAccountInfo("to_withdraw")) / totalVests) * totalSteem) /
149 | 1000000
150 | ).toFixed(0);
151 | const next_vesting_withdrawal = await this.getAccountInfo(
152 | "next_vesting_withdrawal"
153 | );
154 | return [withdrawn, total_withdrawing, next_vesting_withdrawal];
155 | }
156 |
157 | async powerDown(sp, callback) {
158 | const totalSteem = Number(
159 | (await this.props.getProp("total_vesting_fund_steem")).split(" ")[0]
160 | );
161 | const totalVests = Number(
162 | (await this.props.getProp("total_vesting_shares")).split(" ")[0]
163 | );
164 | let vestingShares = (parseFloat(sp) * totalVests) / totalSteem;
165 | vestingShares = vestingShares.toFixed(6);
166 | vestingShares = vestingShares.toString() + " VESTS";
167 |
168 | steem.broadcast.withdrawVesting(
169 | this.getKey("active"),
170 | this.getName(),
171 | vestingShares,
172 | callback
173 | );
174 | }
175 |
176 | powerUp(amount, to, callback) {
177 | steem.broadcast.transferToVesting(
178 | this.getKey("active"),
179 | this.getName(),
180 | to,
181 | amount,
182 | callback
183 | );
184 | }
185 |
186 | async getDelegatees() {
187 | const that = this;
188 | let delegatees = await this.delegatees;
189 | delegatees = delegatees.filter(function(elt) {
190 | return elt.vesting_shares != 0;
191 | });
192 | if (delegatees.length > 0)
193 | delegatees = await Promise.all(
194 | delegatees.map(async elt => {
195 | elt.sp = parseFloat(
196 | await this.toSP(
197 | parseFloat(elt.vesting_shares.replace(" VESTS", ""))
198 | )
199 | ).toFixed(3);
200 | return elt;
201 | })
202 | );
203 | return delegatees;
204 | }
205 | async getDelegators() {
206 | const that = this;
207 | let delegators = (await this.delegators) || [];
208 | delegators = delegators.filter(function(elt) {
209 | return elt.vesting_shares != 0;
210 | });
211 | if (delegators.length > 0)
212 | delegators = await Promise.all(
213 | delegators.map(async elt => {
214 | const sp = await that.toSP(elt.vesting_shares + " VESTS");
215 | elt.sp = parseFloat(sp).toFixed(3);
216 | return elt;
217 | })
218 | );
219 | return delegators;
220 | }
221 | async delegateSP(amount, to, callback) {
222 | const totalSteem = Number(
223 | (await this.props.getProp("total_vesting_fund_steem")).split(" ")[0]
224 | );
225 | const totalVests = Number(
226 | (await this.props.getProp("total_vesting_shares")).split(" ")[0]
227 | );
228 | let delegated_vest = (parseFloat(amount) * totalVests) / totalSteem;
229 | delegated_vest = delegated_vest.toFixed(6);
230 | delegated_vest = delegated_vest.toString() + " VESTS";
231 | steem.broadcast.delegateVestingShares(
232 | activeAccount.getKey("active"),
233 | activeAccount.getName(),
234 | to,
235 | delegated_vest,
236 | callback
237 | );
238 | }
239 | }
240 |
--------------------------------------------------------------------------------
/js/background/auth.js:
--------------------------------------------------------------------------------
1 | const checkBeforeCreate = (request, tab, domain) => {
2 | if (mk == null) {
3 | // Check if locked
4 | const callback = () => {
5 | console.log("locked");
6 | chrome.runtime.sendMessage({
7 | command: "sendDialogError",
8 | msg: {
9 | success: false,
10 | error: "locked",
11 | result: null,
12 | data: request,
13 | message: chrome.i18n.getMessage("bgd_auth_locked"),
14 | display_msg: chrome.i18n.getMessage("bgd_auth_locked_desc")
15 | },
16 | tab: tab,
17 | domain: domain
18 | });
19 | };
20 | createPopup(callback);
21 | } else {
22 | chrome.storage.local.get(
23 | ["accounts", "no_confirm", "current_rpc"],
24 | function(items) {
25 | const {memo, username, type, enforce} = request;
26 | // Check user
27 | if (!items.accounts) {
28 | createPopup(() => {
29 | sendErrors(
30 | tab,
31 | "no_wallet",
32 | chrome.i18n.getMessage("bgd_init_no_wallet"),
33 | "",
34 | request
35 | );
36 | });
37 | } else {
38 | // Check that user and wanted keys are in the wallet
39 | accountsList.init(decryptToJson(items.accounts, mk));
40 | let account = null;
41 | if (type === "transfer") {
42 | let tr_accounts = accountsList
43 | .getList()
44 | .filter(e => e.hasKey("active"))
45 | .map(e => e.getName());
46 | console.log(tr_accounts, "a");
47 |
48 | const encode = memo && memo.length > 0 && memo[0] == "#";
49 | const enforced = enforce || encode;
50 | if (encode) account = accountsList.get(username);
51 | // If a username is specified, check that its active key has been added to the wallet
52 | if (
53 | enforced &&
54 | username &&
55 | !accountsList.get(username).hasKey("active")
56 | ) {
57 | createPopup(() => {
58 | console.log("error1");
59 | sendErrors(
60 | tab,
61 | "user_cancel",
62 | chrome.i18n.getMessage("bgd_auth_canceled"),
63 | chrome.i18n.getMessage("bgd_auth_transfer_no_active", [
64 | username
65 | ]),
66 | request
67 | );
68 | });
69 | } else if (encode && !account.hasKey("memo")) {
70 | createPopup(() => {
71 | console.log("error2");
72 | sendErrors(
73 | tab,
74 | "user_cancel",
75 | chrome.i18n.getMessage("bgd_auth_canceled"),
76 | chrome.i18n.getMessage("bgd_auth_transfer_no_memo", [
77 | username
78 | ]),
79 | request
80 | );
81 | });
82 | } else if (tr_accounts.length == 0) {
83 | createPopup(() => {
84 | console.log("error3");
85 | sendErrors(
86 | tab,
87 | "user_cancel",
88 | chrome.i18n.getMessage("bgd_auth_canceled"),
89 | chrome.i18n.getMessage("bgd_auth_transfer_no_active", [
90 | username
91 | ]),
92 | request
93 | );
94 | });
95 | } else {
96 | console.log("b", tr_accounts);
97 | const callback = () => {
98 | chrome.runtime.sendMessage({
99 | command: "sendDialogConfirm",
100 | data: request,
101 | domain,
102 | accounts: tr_accounts,
103 | tab,
104 | testnet: items.current_rpc === "TESTNET"
105 | });
106 | };
107 | createPopup(callback);
108 | }
109 | } else {
110 | if (!accountsList.get(username)) {
111 | const callback = () => {
112 | console.log("error4");
113 | sendErrors(
114 | tab,
115 | "user_cancel",
116 | chrome.i18n.getMessage("bgd_auth_canceled"),
117 | chrome.i18n.getMessage("bgd_auth_no_account", [username]),
118 | request
119 | );
120 | };
121 | createPopup(callback);
122 | } else {
123 | account = accountsList.get(username);
124 | let typeWif = getRequiredWifType(request);
125 | let req = request;
126 | req.key = typeWif;
127 |
128 | if (req.type == "custom") req.method = typeWif;
129 |
130 | if (req.type == "broadcast") {
131 | req.typeWif = typeWif;
132 | }
133 |
134 | if (!account.hasKey(typeWif)) {
135 | createPopup(() => {
136 | console.log("error5");
137 | sendErrors(
138 | tab,
139 | "user_cancel",
140 | chrome.i18n.getMessage("bgd_auth_canceled"),
141 | chrome.i18n.getMessage("bgd_auth_no_key", [
142 | username,
143 | typeWif
144 | ]),
145 | request
146 | );
147 | });
148 | } else {
149 | public = account.getKey(`${typeWif}Pubkey`);
150 | key = account.getKey(typeWif);
151 | if (
152 | !hasNoConfirm(
153 | items.no_confirm,
154 | req,
155 | domain,
156 | items.current_rpc
157 | )
158 | ) {
159 | const callback = () => {
160 | chrome.runtime.sendMessage({
161 | command: "sendDialogConfirm",
162 | data: req,
163 | domain,
164 | tab,
165 | testnet: items.current_rpc === "TESTNET"
166 | });
167 | };
168 | createPopup(callback);
169 | // Send the request to confirmation window
170 | } else {
171 | chrome.runtime.sendMessage({
172 | command: "broadcastingNoConfirm"
173 | });
174 | performTransaction(req, tab, true);
175 | }
176 | }
177 | }
178 | }
179 | }
180 | }
181 | );
182 | }
183 | };
184 |
185 | const hasNoConfirm = (arr, data, domain, current_rpc) => {
186 | try {
187 | if (
188 | data.method == "active" || data.method == "Active" ||
189 | arr == undefined ||
190 | current_rpc === "TESTNET" ||
191 | domain === "steemit.com"
192 | ) {
193 | return false;
194 | } else return JSON.parse(arr)[data.username][domain][data.type] == true;
195 | } catch (e) {
196 | console.log(e);
197 | return false;
198 | }
199 | };
200 |
201 | // Get the key needed for each type of transaction
202 | const getRequiredWifType = request => {
203 | switch (request.type) {
204 | case "decode":
205 | case "signBuffer":
206 | return request.method.toLowerCase();
207 | break;
208 | case "post":
209 | case "vote":
210 | return "posting";
211 | break;
212 | case "custom":
213 | return request.method == null || request.method == undefined
214 | ? "posting"
215 | : request.method.toLowerCase();
216 | break;
217 | case "addAccountAuthority":
218 | case "removeAccountAuthority":
219 | case "removeKeyAuthority":
220 | case "addKeyAuthority":
221 | case "broadcast":
222 | case "signTx":
223 | return request.method.toLowerCase();
224 | case "signedCall":
225 | return request.typeWif.toLowerCase();
226 | case "transfer":
227 | return "active";
228 | break;
229 | case "sendToken":
230 | return "active";
231 | break;
232 | case "delegation":
233 | return "active";
234 | break;
235 | case "witnessVote":
236 | return "active";
237 | break;
238 | case "powerUp":
239 | return "active";
240 | break;
241 | case "powerDown":
242 | return "active";
243 | break;
244 | case "createClaimedAccount":
245 | return "active";
246 | break;
247 | case "createProposal":
248 | return "active";
249 | break;
250 | case "removeProposal":
251 | return "active";
252 | break;
253 | case "updateProposalVote":
254 | return "active";
255 | break;
256 | }
257 | };
258 |
--------------------------------------------------------------------------------
/example/main.js:
--------------------------------------------------------------------------------
1 | // Send Handshake event
2 | $("#sw-handshake").click(function() {
3 | steem_keychain.requestHandshake(function() {
4 | console.log("Handshake received!");
5 | });
6 | });
7 |
8 | // All transactions are sent via a swRequest event.
9 |
10 | // Send decryption request
11 | $("#send_decode").click(function() {
12 | steem_keychain.requestVerifyKey(
13 | $("#decode_user").val(),
14 | $("#decode_message").val(),
15 | $("#decode_method option:selected").text(),
16 | function(response) {
17 | console.log("main js response - verify key");
18 | console.log(response);
19 | }
20 | );
21 | });
22 |
23 | // Send post request
24 | $("#send_post").click(function() {
25 | steem_keychain.requestPost(
26 | $("#post_username").val(),
27 | $("#post_title").val(),
28 | $("#post_body").val(),
29 | $("#post_pp").val(),
30 | $("#post_pu").val(),
31 | $("#post_json").val(),
32 | $("#post_perm").val(),
33 | $("#comment_options").val(),
34 | function(response) {
35 | console.log("main js response - post");
36 | console.log(response);
37 | }
38 | );
39 | });
40 |
41 | // Send vote request
42 | $("#send_vote").click(function() {
43 | steem_keychain.requestVote(
44 | $("#vote_username").val(),
45 | $("#vote_perm").val(),
46 | $("#vote_author").val(),
47 | $("#vote_weight").val(),
48 | function(response) {
49 | console.log("main js response - vote");
50 | console.log(response);
51 | }
52 | );
53 | });
54 |
55 | // Send Custom JSON request
56 | $("#send_custom").click(function() {
57 | console.log("click");
58 | steem_keychain.requestCustomJson(
59 | $("#custom_username").val(),
60 | $("#custom_id").val(),
61 | $("#custom_method option:selected").text(),
62 | $("#custom_json").val(),
63 | $("#custom_message").val(),
64 | function(response) {
65 | console.log("main js response - custom JSON");
66 | console.log(response);
67 | },
68 | $("#custom_rpc").val()
69 | );
70 | });
71 |
72 | // Send transfer request
73 | $("#send_tra").click(function() {
74 | console.log("transfer");
75 | steem_keychain.requestTransfer(
76 | $("#transfer_username").val(),
77 | $("#transfer_to").val(),
78 | $("#transfer_val").val(),
79 | $("#transfer_memo").val(),
80 | $("#transfer_currency option:selected").text(),
81 | function(response) {
82 | console.log("main js response - transfer");
83 | console.log(response);
84 | },
85 | $("#transfer_enforce").is(":checked")
86 | );
87 | });
88 |
89 | // Send tokens request
90 | $("#sendTokens").click(function() {
91 | steem_keychain.requestSendToken(
92 | $("#tokens_username").val(),
93 | $("#tokens_to").val(),
94 | $("#tokens_qt").val(),
95 | $("#tokens_memo").val(),
96 | $("#tokens_unit").val(),
97 | function(response) {
98 | console.log("main js response - tokens");
99 | console.log(response);
100 | }
101 | );
102 | });
103 |
104 | // Send delegation
105 | $("#send_delegation").click(function() {
106 | steem_keychain.requestDelegation(
107 | $("#delegation_username").val(),
108 | $("#delegation_delegatee").val(),
109 | $("#delegation_sp").val(),
110 | $("#delegation_unit option:selected").text(),
111 | function(response) {
112 | console.log("main js response - delegation");
113 | console.log(response);
114 | }
115 | );
116 | });
117 |
118 | $("#send_signature").click(function() {
119 | steem_keychain.requestSignBuffer(
120 | $("#sign_username").val(),
121 | $("#sign_message").val(),
122 | $("#sign_method option:selected").text(),
123 | function(response) {
124 | console.log("main js response - sign");
125 | console.log(response);
126 | }
127 | );
128 | });
129 |
130 | $("#send_addauth").click(function() {
131 | steem_keychain.requestAddAccountAuthority(
132 | $("#addauth_username").val(),
133 | $("#addauth_authorized_username").val(),
134 | $("#addauth_role option:selected").text(),
135 | $("#addauth_weight").val(),
136 | function(response) {
137 | console.log("main js response - add auth");
138 | console.log(response);
139 | }
140 | );
141 | });
142 |
143 | $("#send_removeauth").click(function() {
144 | steem_keychain.requestRemoveAccountAuthority(
145 | $("#removeauth_username").val(),
146 | $("#removeauth_authorized_username").val(),
147 | $("#removeauth_role option:selected").text(),
148 | function(response) {
149 | console.log("main js response - remove auth");
150 | console.log(response);
151 | }
152 | );
153 | });
154 |
155 | $("#send_addkey").click(function() {
156 | console.log("add key");
157 | steem_keychain.requestAddKeyAuthority(
158 | $("#addkey_username").val(),
159 | $("#addkey_authorized_key").val(),
160 | $("#addkey_role option:selected").text(),
161 | $("#addkey_weight").val(),
162 | function(response) {
163 | console.log("main js response - add auth key");
164 | console.log(response);
165 | }
166 | );
167 | });
168 |
169 | $("#send_removekey").click(function() {
170 | steem_keychain.requestRemoveKeyAuthority(
171 | $("#removekey_username").val(),
172 | $("#removekey_authorized_key").val(),
173 | $("#removekey_role option:selected").text(),
174 | function(response) {
175 | console.log("main js response - remove auth key");
176 | console.log(response);
177 | }
178 | );
179 | });
180 |
181 | $("#send_broadcast").click(function() {
182 | steem_keychain.requestBroadcast(
183 | $("#broadcast_username").val(),
184 | $("#broadcast_operations").val(),
185 | $("#broadcast_method option:selected").text(),
186 | function(response) {
187 | console.log("main js response - broadcast");
188 | console.log(response);
189 | }
190 | );
191 | });
192 |
193 | $("#send_signed_call").click(function() {
194 | steem_keychain.requestSignedCall(
195 | $("#signed_call_username").val(),
196 | $("#signed_call_method").val(),
197 | JSON.parse($("#signed_call_params").val()),
198 | $("#signed_call_key_type option:selected").text(),
199 | function(response) {
200 | console.log("main js response - signed call");
201 | console.log(response);
202 | }
203 | );
204 | });
205 |
206 | $("#send_witness_vote").click(function() {
207 | steem_keychain.requestWitnessVote(
208 | $("#witness_username").val(),
209 | $("#witness").val(),
210 | $("#vote_wit").is(":checked"),
211 | function(response) {
212 | console.log("main js response - witness vote");
213 | console.log(response);
214 | }
215 | );
216 | });
217 |
218 | $("#send_pu").click(function() {
219 | steem_keychain.requestPowerUp(
220 | $("#pu_username").val(),
221 | $("#pu_recipient").val(),
222 | $("#pu_steem").val(),
223 | function(response) {
224 | console.log("main js response - power up");
225 | console.log(response);
226 | }
227 | );
228 | });
229 |
230 | $("#send_pd").click(function() {
231 | steem_keychain.requestPowerDown(
232 | $("#pd_username").val(),
233 | $("#pd_sp").val(),
234 | function(response) {
235 | console.log("main js response - power down");
236 | console.log(response);
237 | }
238 | );
239 | });
240 |
241 | $("#send_create_claimed").click(function() {
242 | steem_keychain.requestCreateClaimedAccount(
243 | $("#create_claimed_username").val(),
244 | $("#create_claimed_new_username").val(),
245 | $("#create_claimed_owner").val(),
246 | $("#create_claimed_active").val(),
247 | $("#create_claimed_posting").val(),
248 | $("#create_claimed_memo").val(),
249 | function(response) {
250 | console.log("main js response - create claimed account");
251 | console.log(response);
252 | }
253 | );
254 | });
255 |
256 | $("#send_cp").click(function() {
257 | steem_keychain.requestCreateProposal(
258 | $("#cp_username").val(),
259 | $("#cp_receiver").val(),
260 | $("#cp_subject").val(),
261 | $("#cp_permlink").val(),
262 | $("#cp_daily_pay").val(),
263 | $("#cp_start").val(),
264 | $("#cp_end").val(),
265 | $("#cp_extensions").val(),
266 | function(response) {
267 | console.log("main js response - create proposal");
268 | console.log(response);
269 | }
270 | );
271 | });
272 |
273 | $("#send_rp").click(function() {
274 | steem_keychain.requestRemoveProposal(
275 | $("#rp_username").val(),
276 | $("#rp_proposal_ids").val(),
277 | $("#cp_extensions").val(),
278 | function(response) {
279 | console.log("main js response - remove proposal");
280 | console.log(response);
281 | }
282 | );
283 | });
284 |
285 | $("#send_vp").click(function() {
286 | steem_keychain.requestUpdateProposalVote(
287 | $("#vp_username").val(),
288 | $("#vp_proposal_ids").val(),
289 | $("#vp_approve").is(":checked"),
290 | $("#vp_extensions").val(),
291 | function(response) {
292 | console.log("main js response - update proposal votes");
293 | console.log(response);
294 | }
295 | );
296 | });
297 |
--------------------------------------------------------------------------------
/css/dialog.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: Futura;
3 | src: url("../fonts/Futura-Boo.otf");
4 | font-weight: 400;
5 | }
6 |
7 | html {
8 | width: 350px;
9 | height: 566px;
10 | overflow: hidden;
11 | border-radius: 1px;
12 | margin: 0 !important;
13 | padding: 0 !important;
14 | background-image: linear-gradient(328deg, #3df9b4, #00d3cc, #a779ff) !important;
15 | background-repeat: no-repeat;
16 | background-attachment: fixed;
17 | top: 0;
18 | left: 0;
19 | position: relative;
20 | word-wrap: break-word;
21 | }
22 |
23 | body {
24 | height: 100%;
25 | font-family: Futura !important;
26 | font-weight: 400;
27 | padding: 0 !important;
28 | width: 100%;
29 | margin: 0 !important;
30 | overflow: hidden;
31 | }
32 |
33 | .cloud {
34 | background: url("../images/bg-steam@2x.png") no-repeat;
35 | background-size: cover;
36 | width: 350px;
37 | height: 436px;
38 | position: absolute;
39 | bottom: 0;
40 | left: 0;
41 | z-index: 1;
42 | }
43 |
44 | .modal-content {
45 | width: 100%;
46 | height: 100%;
47 | box-sizing: border-box !important;
48 | position: absolute;
49 | margin: 0 !important;
50 | padding: 20px 32px !important;
51 | line-height: 22px;
52 | font-size: 16px;
53 | text-align: left;
54 | text-overflow: clip;
55 | white-space: normal;
56 | color: white;
57 | z-index: 2;
58 | }
59 |
60 | h2 {
61 | display: inline-block;
62 | margin: 10px 0 20px 0;
63 | width: 100%;
64 | font-size: 20px;
65 | line-height: 25px;
66 | text-transform: uppercase;
67 | }
68 |
69 | h2::before {
70 | content: "";
71 | width: 41.21px;
72 | height: 30px;
73 | opacity: 0.8;
74 | background-image: url("../images/keychain_icon2.png");
75 | background-repeat: no-repeat;
76 | background-size: 41.21px 30px;
77 | padding-right: 60px;
78 | vertical-align: 0%;
79 | background-size: contain;
80 | }
81 |
82 | h3.dialog-message {
83 | text-align: center;
84 | margin-top: 0;
85 | margin-bottom: 10px;
86 | background-color: rgba(0, 156, 156, 0.4);
87 | padding: 5px 0 10px 0;
88 | }
89 |
90 | h3 .small {
91 | text-transform: none;
92 | font-size: 80%;
93 | cursor: pointer;
94 | }
95 |
96 | #modal-body-msg .msg-data>div,
97 | #modal-body-msg .msg-data>h3,
98 | #modal-body-msg .msg-data>img,
99 | #transfer_acct_list {
100 | display: none;
101 | }
102 |
103 | #error_dialog {
104 | margin-bottom: 40px;
105 | }
106 |
107 | .modal-body-error {
108 | width: 100%;
109 | box-sizing: border-box;
110 | }
111 |
112 | .input_container {
113 | width: 100%;
114 | position: relative;
115 | padding: 0;
116 | margin: 0;
117 | }
118 |
119 | .input_img {
120 | position: absolute;
121 | bottom: 30.07px;
122 | left: 20.41px;
123 | width: 23.85px;
124 | height: 29.93px;
125 | }
126 |
127 | .input_container input {
128 | width: 100%;
129 | box-sizing: border-box;
130 | height: 48.43px;
131 | margin-bottom: 20px;
132 | background: rgba(255, 255, 255, 0.65);
133 | border: 1px solid rgba(255, 255, 255, 0.65);
134 | padding-left: 65px;
135 | border-radius: 5px;
136 | color: #009C9C;
137 | font-size: 20px;
138 | }
139 |
140 | button {
141 | border-radius: 5px;
142 | width: 100%;
143 | height: 48.43px;
144 | text-align: center;
145 | font-size: 20px;
146 | line-height: 28px;
147 | color: white;
148 | font-family: Futura;
149 | text-transform: uppercase;
150 | background-color: rgba(0, 156, 156);
151 | border: solid 1px rgba(0, 156, 156);
152 | cursor: pointer;
153 | }
154 |
155 | #yes-unlock {
156 | margin-bottom: 20px;
157 | }
158 |
159 | .unlock {
160 | display: none;
161 | }
162 |
163 | #modal-body-msg {}
164 |
165 | #modal-body-msg .msg-data {
166 | max-height: 285px;
167 | overflow-y: scroll;
168 | background-color: rgba(0, 156, 156, 0.8);
169 | padding: 10px;
170 | }
171 |
172 | ::-webkit-scrollbar {
173 | width: 0px;
174 | /* remove scrollbar space */
175 | background: transparent;
176 | /* optional: just make scrollbar invisible */
177 | }
178 |
179 | /* optional: show position indicator in red */
180 | ::-webkit-scrollbar-thumb {
181 | background: #FF0000;
182 | }
183 |
184 | #modal-body-msg h3 {
185 | font-size: 16px;
186 | line-height: 16px;
187 | text-transform: uppercase;
188 | margin: 0 0 5px 0;
189 | }
190 |
191 | #modal-body-msg .msg-data div {
192 | font-size: 16px;
193 | line-height: 16px;
194 | font-style: italic;
195 | margin-bottom: 20px;
196 | }
197 |
198 | .keep_checkbox {
199 | display: block;
200 | height: 37px;
201 | margin-bottom: 25px;
202 | }
203 |
204 | /* The container */
205 | .checkbox_container {
206 | display: block;
207 | position: relative;
208 | padding-left: 55px;
209 | font-size: 16px;
210 | line-height: 16px;
211 | cursor: pointer;
212 | -webkit-user-select: none;
213 | -moz-user-select: none;
214 | -ms-user-select: none;
215 | user-select: none;
216 | }
217 |
218 | .checkbox_container div {
219 | margin-top: 5px;
220 | color: #006060;
221 | }
222 |
223 | .checkbox_container div:first-of-type {
224 | color: white;
225 | margin-top: 0px;
226 | text-transform: uppercase;
227 | }
228 |
229 | /* Hide the browser's default checkbox */
230 | .checkbox_container input {
231 | position: absolute;
232 | opacity: 0;
233 | cursor: pointer;
234 | }
235 |
236 | /* Create a custom checkbox */
237 | .checkmark {
238 | position: absolute;
239 | top: 7px;
240 | left: 0;
241 | height: 32px;
242 | width: 32px;
243 | background-color: rgba(255, 255, 255, 0.65);
244 | }
245 |
246 | /* Create the checkmark/indicator (hidden when not checked) */
247 | .checkmark:after {
248 | content: "";
249 | position: absolute;
250 | display: none;
251 | }
252 |
253 | /* Show the checkmark when checked */
254 | .checkbox_container input:checked~.checkmark:after {
255 | display: block;
256 | }
257 |
258 | /* Style the checkmark/indicator */
259 | .checkbox_container .checkmark:after {
260 | left: 11.52px;
261 | top: 6.4px;
262 | width: 6.4px;
263 | height: 11.8px;
264 | border: solid #009C9C;
265 | border-width: 0 3px 3px 0;
266 | -webkit-transform: rotate(45deg);
267 | -ms-transform: rotate(45deg);
268 | transform: rotate(45deg);
269 | }
270 |
271 | #confirm_footer {
272 | display: none;
273 | width: 100%;
274 | min-height: 169px;
275 | position: absolute;
276 | box-sizing: border-box;
277 | bottom: 0;
278 | background-color: rgba(0, 156, 156, 0.8);
279 | margin-left: -32px;
280 | padding: 10px 32px 60px 32px;
281 | }
282 |
283 | #confirm_footer button {
284 | width: 138px;
285 | }
286 |
287 | #confirm_footer button:first-of-type {
288 | margin-right: 6px;
289 | background-color: #006060;
290 | }
291 |
292 | #proceed:hover {
293 | background-color: #917FC6;
294 | }
295 |
296 | #keep_label {
297 | font-size: 16px;
298 | line-height: 16px;
299 | text-transform: none;
300 | }
301 |
302 | #tx_loading {
303 | text-align: center;
304 | }
305 |
306 | #tx_loading img {
307 | width: 50px;
308 | }
309 |
310 | .custom-select {
311 | position: relative;
312 | width: 100% !important;
313 | line-height: 2;
314 | height: 48.43px;
315 | margin-bottom: 20px;
316 | border: 1px solid rgba(0, 96, 96, 0.29);
317 | border-radius: 5px;
318 | margin-top: 20px;
319 | }
320 |
321 | .custom-select select {
322 | display: none;
323 | /*hide original SELECT element:*/
324 | }
325 |
326 | .select-selected {
327 | background-color: rgba(0, 96, 96, 0.29);
328 | padding-left: 20px !important;
329 | }
330 |
331 | /*style the arrow inside the select element:*/
332 | .select-selected:after {
333 | position: absolute;
334 | content: "";
335 | top: 22px;
336 | right: 10px;
337 | width: 0;
338 | height: 0;
339 | border: 10px solid transparent;
340 | border-color: #88F7FB transparent transparent transparent;
341 | }
342 |
343 | /*point the arrow upwards when the select box is open (active):*/
344 | .select-selected.select-arrow-active:after {
345 | border-color: transparent transparent #88F7FB transparent;
346 | top: 11px;
347 | }
348 |
349 | /*style the items (options), including the selected item:*/
350 | .select-items div,
351 | .select-selected {
352 | box-sizing: border-box;
353 | color: #88F7FB;
354 | font-family: Futura;
355 | font-size: 20px;
356 | margin-bottom: 0px;
357 | line-height: 28px;
358 | padding: 9px 0px 8px 20px;
359 | border: 1px solid transparent;
360 | /*border-color: transparent transparent rgba(0, 0, 0, 0.1) transparent;*/
361 | cursor: pointer;
362 | border-radius: 3px;
363 | }
364 |
365 | /*style items (options):*/
366 | .select-items {
367 | position: absolute;
368 | background-color: rgba(0, 96, 96, 0.84);
369 | top: 100%;
370 | left: 0;
371 | margin-bottom: 0;
372 | right: 0;
373 | z-index: 99;
374 | }
375 |
376 | .select-items div {
377 | margin-bottom: 0;
378 | }
379 |
380 | /*hide the items when the select box is closed:*/
381 | .select-hide {
382 | display: none;
383 | }
384 |
385 | .select-items div:hover,
386 | .same-as-selected {
387 | background-color: rgba(0, 96, 96, 1);
388 | }
389 |
390 | .balance_loading {
391 | width: 15px
392 | }
393 |
394 | #steemit {
395 | display: none
396 | }
397 |
398 | #balance, #balance_after {
399 | display: none
400 | }
--------------------------------------------------------------------------------
/example/main.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Steem Keychain
6 |
7 |
8 |
9 | Handshake
10 |
11 | Decode memo
12 |
13 |
18 |
19 |
20 | Post
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | Vote
31 |
32 |
33 |
34 |
35 |
36 | Custom_JSON
37 |
38 |
39 |
43 |
44 |
45 |
46 |
47 | Transfer
48 |
49 |
50 |
51 |
52 |
53 |
57 |
58 | Sign message
59 |
60 |
61 |
66 |
67 | Add Account Authority
68 |
69 |
70 |
74 |
75 |
76 | Remove Account Authority
77 |
78 |
79 |
83 |
84 | Add Key Authority
85 |
86 |
87 |
91 |
92 |
93 | Remove Key Authority
94 |
95 |
96 |
100 |
101 | Broadcast
102 |
103 |
104 |
109 |
110 | Broadcast Create New claimed account
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 | Signed Call
119 |
120 |
121 |
122 |
127 |
128 |
129 | Delegate
130 |
131 |
132 |
133 |
137 |
138 | Send Tokens
139 |
140 |
141 |
142 |
143 |
144 |
145 | Witness
146 |
147 |
148 |
149 |
150 | Power Up
151 |
152 |
153 |
154 |
155 | Power Down
156 |
157 |
158 |
159 | Create Proposal
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 | Remove Proposal
170 |
171 |
172 |
173 |
174 | Update proposal votes
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
--------------------------------------------------------------------------------
/vendor/ssc.min.js:
--------------------------------------------------------------------------------
1 | !function(t){"use strict";var c,e=Object.prototype,s=e.hasOwnProperty,r="function"==typeof Symbol?Symbol:{},o=r.iterator||"@@iterator",n=r.asyncIterator||"@@asyncIterator",i=r.toStringTag||"@@toStringTag",a="object"==typeof module,u=t.regeneratorRuntime;if(u)a&&(module.exports=u);else{(u=t.regeneratorRuntime=a?module.exports:{}).wrap=w;var h="suspendedStart",f="suspendedYield",p="executing",d="completed",v={},l={};l[o]=function(){return this};var y=Object.getPrototypeOf,m=y&&y(y(I([])));m&&m!==e&&s.call(m,o)&&(l=m);var g=L.prototype=b.prototype=Object.create(l);k.prototype=g.constructor=L,L.constructor=k,L[i]=k.displayName="GeneratorFunction",u.isGeneratorFunction=function(t){var e="function"==typeof t&&t.constructor;return!!e&&(e===k||"GeneratorFunction"===(e.displayName||e.name))},u.mark=function(t){return Object.setPrototypeOf?Object.setPrototypeOf(t,L):(t.__proto__=L,i in t||(t[i]="GeneratorFunction")),t.prototype=Object.create(g),t},u.awrap=function(t){return{__await:t}},E(O.prototype),O.prototype[n]=function(){return this},u.AsyncIterator=O,u.async=function(t,e,r,n){var o=new O(w(t,e,r,n));return u.isGeneratorFunction(e)?o:o.next().then(function(t){return t.done?t.value:o.next()})},E(g),g[i]="Generator",g[o]=function(){return this},g.toString=function(){return"[object Generator]"},u.keys=function(r){var n=[];for(var t in r)n.push(t);return n.reverse(),function t(){for(;n.length;){var e=n.pop();if(e in r)return t.value=e,t.done=!1,t}return t.done=!0,t}},u.values=I,_.prototype={constructor:_,reset:function(t){if(this.prev=0,this.next=0,this.sent=this._sent=c,this.done=!1,this.delegate=null,this.method="next",this.arg=c,this.tryEntries.forEach(T),!t)for(var e in this)"t"===e.charAt(0)&&s.call(this,e)&&!isNaN(+e.slice(1))&&(this[e]=c)},stop:function(){this.done=!0;var t=this.tryEntries[0].completion;if("throw"===t.type)throw t.arg;return this.rval},dispatchException:function(r){if(this.done)throw r;var n=this;function t(t,e){return i.type="throw",i.arg=r,n.next=t,e&&(n.method="next",n.arg=c),!!e}for(var e=this.tryEntries.length-1;0<=e;--e){var o=this.tryEntries[e],i=o.completion;if("root"===o.tryLoc)return t("end");if(o.tryLoc<=this.prev){var a=s.call(o,"catchLoc"),u=s.call(o,"finallyLoc");if(a&&u){if(this.prev {
5 | try {
6 | var scriptTag = document.createElement("script");
7 | scriptTag.src = chrome.runtime.getURL("js/steem_keychain.js");
8 | var container = document.head || document.documentElement;
9 | container.insertBefore(scriptTag, container.children[0]);
10 | } catch (e) {
11 | console.error("Steem Keychain injection failed.", e);
12 | }
13 | };
14 | setupInjection();
15 |
16 | // Answering the handshakes
17 | document.addEventListener("swHandshake", function(request) {
18 | const req = JSON.stringify(request.detail);
19 | if (request.detail.extension)
20 | chrome.runtime.sendMessage(request.detail.extension, req);
21 | else
22 | window.postMessage(
23 | {
24 | type: "steem_keychain_handshake"
25 | },
26 | window.location.origin
27 | );
28 | });
29 |
30 | // Answering the requests
31 | document.addEventListener("swRequest", function(request) {
32 | const prevReq = req;
33 | req = request.detail;
34 | // If all information are filled, send the request to the background, if not notify an error
35 | if (validate()) {
36 | chrome.runtime.sendMessage({
37 | command: "sendRequest",
38 | request: req,
39 | domain: req.extensionName || window.location.hostname,
40 | request_id: req.request_id
41 | });
42 | if (prevReq) {
43 | const response = {
44 | success: false,
45 | error: "ignored",
46 | result: null,
47 | message: "User ignored this transaction",
48 | data: req,
49 | request_id: req.request_id
50 | };
51 | sendResponse(response);
52 | }
53 | } else {
54 | var response = {
55 | success: false,
56 | error: "incomplete",
57 | result: null,
58 | message: "Incomplete data or wrong format",
59 | data: req,
60 | request_id: req.request_id
61 | };
62 | sendResponse(response);
63 | req = prevReq;
64 | }
65 | });
66 |
67 | // Get notification from the background upon request completion and pass it to the website.
68 | chrome.runtime.onMessage.addListener(function(obj, sender, sendResp) {
69 | if (obj.command == "answerRequest") {
70 | sendResponse(obj.msg);
71 | req = null;
72 | }
73 | });
74 |
75 | const sendResponse = response => {
76 | if (response.data.extension && response.data.extensionName)
77 | chrome.runtime.sendMessage(
78 | response.data.extension,
79 | JSON.stringify(response)
80 | );
81 | else
82 | window.postMessage(
83 | {
84 | type: "steem_keychain_response",
85 | response
86 | },
87 | window.location.origin
88 | );
89 | };
90 |
91 | const validate = () => {
92 | console.log(req);
93 | return (
94 | req != null &&
95 | req != undefined &&
96 | req.type != undefined &&
97 | req.type != null &&
98 | ((req.type == "decode" &&
99 | isFilled(req.username) &&
100 | isFilled(req.message) &&
101 | req.message[0] == "#" &&
102 | isFilledKey(req.method)) ||
103 | (req.type == "signBuffer" &&
104 | isFilled(req.username) &&
105 | isFilled(req.message) &&
106 | isFilledKey(req.method)) ||
107 | (req.type == "vote" &&
108 | isFilled(req.username) &&
109 | isFilledWeight(req.weight) &&
110 | isFilled(req.permlink) &&
111 | isFilled(req.author)) ||
112 | (req.type == "post" &&
113 | isFilled(req.username) &&
114 | isFilled(req.body) &&
115 | ((isFilled(req.title) &&
116 | isFilledOrEmpty(req.permlink) &&
117 | !isFilled(req.parent_username) &&
118 | isFilled(req.parent_perm) &&
119 | isFilled(req.json_metadata)) ||
120 | (!isFilled(req.title) &&
121 | isFilledOrEmpty(req.permlink) &&
122 | isFilled(req.parent_username) &&
123 | isFilled(req.parent_perm) &&
124 | isFilledOrEmpty(req.json_metadata))) &&
125 | isCustomOptions(req)) ||
126 | (req.type == "custom" &&
127 | isFilled(req.username) &&
128 | isFilled(req.json) &&
129 | isFilled(req.id)) ||
130 | (req.type == "addAccountAuthority" &&
131 | isFilled(req.authorizedUsername) &&
132 | isFilled(req.role) &&
133 | isFilled(req.weight)) ||
134 | (req.type == "removeAccountAuthority" &&
135 | isFilled(req.authorizedUsername) &&
136 | isFilled(req.role)) ||
137 | (req.type == "addKeyAuthority" &&
138 | isFilled(req.authorizedKey) &&
139 | isFilled(req.role) &&
140 | isFilled(req.weight)) ||
141 | (req.type == "removeKeyAuthority" &&
142 | isFilled(req.authorizedKey) &&
143 | isFilled(req.role)) ||
144 | (req.type == "broadcast" &&
145 | isFilled(req.operations) &&
146 | isFilled(req.method)) ||
147 | (req.type == "signTx" &&
148 | isFilled(req.tx) &&
149 | isFilled(req.method)) ||
150 | (req.type == "signedCall" &&
151 | isFilled(req.method) &&
152 | isFilled(req.params) &&
153 | isFilled(req.typeWif)) ||
154 | (req.type == "witnessVote" &&
155 | isFilled(req.username) &&
156 | isFilled(req.witness) &&
157 | isBoolean(req.vote)) ||
158 | (req.type == "delegation" &&
159 | isFilled(req.username) &&
160 | isFilled(req.delegatee) &&
161 | isFilledAmtSP(req) &&
162 | isFilledDelegationMethod(req.unit)) ||
163 | (req.type == "transfer" &&
164 | isFilledAmt(req.amount) &&
165 | isFilled(req.to) &&
166 | isFilledCurrency(req.currency) &&
167 | hasTransferInfo(req)) ||
168 | (req.type == "sendToken" &&
169 | isFilledAmt(req.amount) &&
170 | isFilled(req.to) &&
171 | isFilled(req.currency)) ||
172 | (req.type == "powerUp" &&
173 | isFilled(req.username) &&
174 | isFilledAmt(req.steem) &&
175 | isFilled(req.recipient)) ||
176 | (req.type == "powerDown" &&
177 | isFilled(req.username) &&
178 | (isFilledAmt(req.steem_power) || req.steem_power == "0.000")) ||
179 | (req.type == "createClaimedAccount" &&
180 | isFilled(req.username) &&
181 | isFilled(req.new_account) &&
182 | isFilled(req.owner) &&
183 | isFilled(req.active) &&
184 | isFilled(req.posting) &&
185 | isFilled(req.memo)) ||
186 | (req.type == "createProposal" &&
187 | isFilled(req.username) &&
188 | isFilled(req.receiver) &&
189 | isFilledDate(req.start) &&
190 | isFilledDate(req.end) &&
191 | isFilled(req.subject) &&
192 | isFilled(req.permlink) &&
193 | isFilledAmtSBD(req.daily_pay)) ||
194 | (req.type == "removeProposal" &&
195 | isFilled(req.username) &&
196 | isProposalIDs(req.proposal_ids)) ||
197 | (req.type == "updateProposalVote" &&
198 | isFilled(req.username) &&
199 | isProposalIDs(req.proposal_ids) &&
200 | isBoolean(req.approve)))
201 | );
202 | };
203 |
204 | // Functions used to check the incoming data
205 |
206 | const hasTransferInfo = req => {
207 | if (req.enforce) return isFilled(req.username);
208 | else if (isFilled(req.memo) && req.memo[0] == "#")
209 | return isFilled(req.username);
210 | else return true;
211 | };
212 |
213 | const isFilled = obj => {
214 | return obj != undefined && obj != null && obj != "";
215 | };
216 |
217 | const isBoolean = obj => {
218 | return typeof obj == typeof true;
219 | };
220 |
221 | const isFilledOrEmpty = obj => {
222 | return obj || obj == "";
223 | };
224 |
225 | const isProposalIDs = obj => {
226 | const parsed = JSON.parse(obj);
227 | return Array.isArray(parsed) && !parsed.some(isNaN);
228 | };
229 |
230 | const isFilledDelegationMethod = obj => {
231 | return obj == "VESTS" || obj == "SP";
232 | };
233 |
234 | const isFilledJSON = obj => {
235 | try {
236 | return (
237 | isFilled(obj) &&
238 | JSON.parse(obj).hasOwnProperty("requiredAuths") &&
239 | JSON.parse(obj).hasOwnProperty("requiredPostingAuths") &&
240 | JSON.parse(obj).hasOwnProperty("id") &&
241 | JSON.parse(obj).hasOwnProperty("json")
242 | );
243 | } catch (e) {
244 | return false;
245 | }
246 | };
247 |
248 | const isFilledDate = date => {
249 | const regex = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d/;
250 | return regex.test(date);
251 | };
252 |
253 | const isFilledAmt = obj => {
254 | return isFilled(obj) && !isNaN(obj) && obj > 0 && countDecimals(obj) == 3;
255 | };
256 |
257 | const isFilledAmtSP = obj => {
258 | return (
259 | isFilled(obj.amount) &&
260 | !isNaN(obj.amount) &&
261 | ((countDecimals(obj.amount) == 3 && obj.unit == "SP") ||
262 | (countDecimals(obj.amount) == 6 && obj.unit == "VESTS"))
263 | );
264 | };
265 |
266 | const isFilledAmtSBD = amt => {
267 | return (
268 | amt &&
269 | amt.split(" ").length == 2 &&
270 | !isNaN(amt.split(" ")[0]) &&
271 | parseFloat(countDecimals(amt.split(" ")[0])) == 3 &&
272 | amt.split(" ")[1] == "SBD"
273 | );
274 | };
275 |
276 | const isFilledWeight = obj => {
277 | return (
278 | isFilled(obj) &&
279 | !isNaN(obj) &&
280 | obj >= -10000 &&
281 | obj <= 10000 &&
282 | countDecimals(obj) == 0
283 | );
284 | };
285 |
286 | const isFilledCurrency = obj => {
287 | return isFilled(obj) && (obj == "STEEM" || obj == "SBD");
288 | };
289 |
290 | const isFilledKey = obj => {
291 | return (
292 | isFilled(obj) && (obj == "Memo" || obj == "Active" || obj == "Posting")
293 | );
294 | };
295 |
296 | const isCustomOptions = obj => {
297 | if (obj.comment_options == "") return true;
298 | let comment_options = JSON.parse(obj.comment_options);
299 | if (
300 | comment_options.author != obj.username ||
301 | comment_options.permlink != obj.permlink
302 | )
303 | return false;
304 | return (
305 | comment_options.hasOwnProperty("max_accepted_payout") &&
306 | comment_options.hasOwnProperty("percent_steem_dollars") &&
307 | comment_options.hasOwnProperty("allow_votes") &&
308 | comment_options.hasOwnProperty("allow_curation_rewards") &&
309 | comment_options.hasOwnProperty("extensions")
310 | );
311 | };
312 |
313 | const countDecimals = nb => {
314 | return nb.toString().split(".")[1] == undefined
315 | ? 0
316 | : nb.toString().split(".")[1].length || 0;
317 | };
318 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 | ---
3 | Putting private keys directly into websites is not safe or secure. Even ones run by SteemIt, Inc. Yet this is currently how nearly every Steem-based site or service currently works. On top of that, most Steem users likely use their master password which is even worse
4 |
5 | The Vessel desktop wallet software is a secure alternative, but it is too difficult to use for the majority of Steem users and does not easily interact with websites - which is Steem's primary use case.
6 |
7 | On Ethereum, you never have to enter your private key into a website to use a dApp, you can just use a browser extension like Metamask, which dApp websites can interface with to securely store your keys and broadcast transactions to the blockchain.
8 |
9 | Steem Keychain aims to bring the security and ease-of-use of Metamask to the Steem blockchain platform.
10 |
11 | ## Installation
12 | You can download and install the latest published version of the extension for the following browsers:
13 |
14 | - Google Chrome (or Brave): [https://chrome.google.com/webstore/detail/steem-keychain/lkcjlnjfpbikmcmbachjpdbijejflpcm](https://chrome.google.com/webstore/detail/steem-keychain/lkcjlnjfpbikmcmbachjpdbijejflpcm)
15 | - Firefox: [https://addons.mozilla.org/en-US/firefox/addon/steem-keychain/](https://addons.mozilla.org/en-US/firefox/addon/steem-keychain/)
16 |
17 | ## Features
18 | The Steem Keychain extension includes the following features:
19 | - Store an unlimited number of Steem account keys, encrypted with AES
20 | - View balances, transaction history, voting power, and resource credits
21 | - Send STEEM and SBD transfers, manage witness votes, and update SP delegation right from the extension
22 | - Securely interact with Steem-based websites that have integrated with Steem Keychain
23 | - Manage transaction confirmation preferences by account and by website
24 | - Locks automatically on browser shutdown or manually using the lock button
25 |
26 | ## Website Integration
27 | Websites can currently request the Steem Keychain extension to perform the following functions / broadcast operations:
28 | - Send a handshake to make sure the extension is installed
29 | - Decrypt a message encrypted by a Steem account private key (commonly used for "logging in")
30 | - Post a comment (top level or reply)
31 | - Broadcast a vote
32 | - Broadcast a custom JSON operation
33 | - Send a transfer
34 | - Send Steem Engine tokens
35 | - Send Delegations
36 | - Power up/down
37 | - Vote for witnesses
38 |
39 | ## Example
40 |
41 | An example of a web page that interacts with the extension is included in the "example" folder in the repo. You can test it by running a local HTTP server and going to http://localhost:1337/main.html in your browser.
42 |
43 | `cd example`
44 | `python -m http.server 1337 //or any other method to run a static server`
45 |
46 | NOTE: On localhost, it will only run on port 1337.
47 |
48 | ## API Documentation
49 |
50 | The Steem Keychain extension will inject a "steem_keychain" JavaScript into all web pages opened in the browser while the extension is running. You can therefore check if the current user has the extension installed using the following code:
51 |
52 | ```
53 | if(window.steem_keychain) {
54 | // Steem Keychain extension installed...
55 | } else {
56 | // Steem Keychain extension not installed...
57 | }
58 | ```
59 |
60 | ### Handshake
61 |
62 | Additionally, you can request a "handshake" from the extension to further ensure it's installed and that your page is able to connect to it:
63 |
64 | ```
65 | steem_keychain.requestHandshake(function() {
66 | console.log('Handshake received!');
67 | });
68 | ```
69 |
70 | ### Transfer
71 |
72 | Sites can request that the extension sign and broadcast a transfer operation for STEEM or SBD. Note that a confirmation will always be shown to the user for transfer operations and they cannot be disabled.
73 |
74 | ```
75 | steem_keychain.requestTransfer(account_name, to_account, amount, memo, currency, function(response) {
76 | console.log(response);
77 | },enforce);
78 | ```
79 | where `memo` will be encrypted using Memo key if it is starting by `#`, and `enforce` doesn't allow the user to chose which account will make the transfer but rather enforce `account_name`.
80 |
81 | ### Decode Memo / Verify Key
82 |
83 | Sites can request that the extension decode a memo encrypted by the Memo, Posting, or Active key for a particular Steem account. This is messaged to the user as "Verify Key" since it is typically used to verify that they have access to the private key for an account in order to "log them in".
84 |
85 | ```
86 | steem_keychain.requestVerifyKey(account_name, encrypted_message, key_type, function(response) {
87 | console.log(response);
88 | });
89 | ```
90 |
91 | The values for "key_type" can be: "Memo", "Posting", or "Active".
92 |
93 | ### Comment Operation
94 |
95 | Sites can request that the extension sign and broadcast a "comment" operation (which can be a top-level post or a reply).
96 |
97 | ```
98 | steem_keychain.requestPost(account_name, title, body, parent_permlink, parent_author, json_metadata, permlink, function(response) {
99 | console.log(response);
100 | });
101 | ```
102 |
103 | ### Vote
104 |
105 | Sites can request that the extension sign and broadcast a "vote" operation:
106 |
107 | ```
108 | steem_keychain.requestVote(account_name, permlink, author, weight, function(response) {
109 | console.log(response);
110 | });
111 | ```
112 |
113 | ### Custom JSON
114 |
115 | Sites can request that the extension sign and broadcast a "custom_json" operation using either the posting or active key for the account:
116 |
117 | ```
118 | steem_keychain.requestCustomJson(account_name, custom_json_id, key_type, json, display_name, function(response) {
119 | console.log(response);
120 | });
121 | ```
122 |
123 | Where "key_type" can be "Posting" or "Active" and "display_name" is a user-friendly name of the operation to be shown to the user so they know what operation is being broadcast (ex. "Steem Monsters Card Transfer").
124 |
125 | ### Sign
126 |
127 | Sites can request that the extension sign messages:
128 |
129 | ```
130 | steem_keychain.requestSignBuffer(account_name, message, key_type, function(response) {
131 | console.log(response);
132 | });
133 | ```
134 |
135 | Where "message" is any string and "key_type" can be "Posting" or "Active". This is equivalent to
136 |
137 | ```Signature.signBufferSha256(hash.sha256(message), wif).toHex();```
138 |
139 | You can also pass in a JSON-stringified Node.js Buffer object. For example, if `buffer` is a Node.js Buffer
140 | to be signed, you can pass `JSON.stringify(buffer)` as `message`, then this method becomes equivalent to
141 |
142 | ```Signature.signBufferSha256(hash.sha256(buffer), wif).toHex();```
143 |
144 | ### Add Account Authority
145 |
146 | Sites can request that the extension add account authority for a given role:
147 |
148 | ```
149 | steem_keychain.requestAddAccountAuthority(account_name, authorized_account_name, role, weight, function(response) {
150 | console.log(response);
151 | });
152 | ```
153 |
154 | where "role" can be "Posting" or "Active".
155 |
156 | ### Remove Account Authority
157 |
158 | Sites can request that the extension remove account authority for a given role:
159 |
160 | ```
161 | steem_keychain.requestRemoveAccountAuthority(account_name, authorized_account_name, role, function(response) {
162 | console.log(response);
163 | });
164 | ```
165 |
166 | where "role" can be "Posting" or "Active".
167 |
168 | ### Broadcast
169 |
170 | Sites can request that the extension sign and broadcast general operations allowed by the `steem-js` library:
171 |
172 | ```
173 | steem_keychain.requestBroadcast(account_name, operations, key_type, function(response) {
174 | console.log(response);
175 | });
176 | ```
177 |
178 | Where "operations" is the list of operations and "key_type" can be "Posting" or "Active". This is
179 | roughly equivalent to
180 |
181 | ```
182 | broadcast.send({ extensions: [], operations }, keys, errorCallback);
183 | ```
184 |
185 | ### Signed Call
186 |
187 | Sites can request that per sign RPCs using steem authorities as specified in https://github.com/steemit/rpc-auth
188 | and implemented in the `steem-js` library method signedCall:
189 |
190 | ```
191 | steem_keychain.requestSignedCall(account_name, method, params, key_type, function(response) {
192 | console.log(response);
193 | });
194 | ```
195 |
196 | Where "method" is the method name, e.g. `conveyor.get_feature_flags`, "params" are the method parameters,
197 | and "key_type" can be "Posting" or "Active".
198 |
199 | ### Send Tokens
200 |
201 | Sites can request that Keychain broadcasts a JSON with active authority to transfer tokens to another user.
202 | This works with tokens generated using [Steem Engine](https://steem-engine.net).
203 |
204 | ```
205 | steem_keychain.requestSendToken(username, to,amount,memo, token, function(response) {
206 | console.log(response);
207 | });
208 | ```
209 |
210 | where `token` is the symbol of the said token.
211 |
212 | ### Delegate
213 |
214 | Sites can request a delegation via Keychain, using the active authority :
215 |
216 | ```
217 | steem_keychain.requestDelegation(username, delegatee, amount, unit, function(response) {
218 | console.log(response);
219 | });
220 | ```
221 |
222 | where `unit` can be either `VESTS` or `SP`. `amount` needs 6 decimals if the unit is `VESTS`, 3 if it is `SP`.
223 |
224 | ### Vote for a Witness
225 |
226 | Sites can request that the user votes for a particular witness :
227 |
228 | ```
229 | steem_keychain.requestWitnessVote(username, witness,vote, function(response) {
230 | console.log(response);
231 | });
232 | ```
233 |
234 | Where `vote` is a boolean, set to `true` for voting a witness, `false` for unvoting.
235 |
236 | ### Power Up
237 |
238 | Sites can request a Power Up:
239 |
240 | ```
241 | steem_keychain.requestPowerUp(username, to, amount, function(response) {
242 | console.log(response);
243 | });
244 | ```
245 |
246 | Where `to` is the recipient of the power up, and `amount` is expressed in STEEM (with 3 decimals).
247 |
248 | ### Power Down
249 |
250 | Sites can request a Power Down:
251 |
252 | ```
253 | steem_keychain.requestPowerDown(username, amount, function(response) {
254 | console.log(response);
255 | });
256 | ```
257 |
258 | Where `amount` is expressed in SP for more visibility for the user.
259 |
260 | ## Related Projects
261 |
262 | * [ngx-steem-keychain](https://github.com/steeveproject/ngx-steem-keychain) -
263 | Native [Angular](https://angular.io) framework integration.
264 |
--------------------------------------------------------------------------------
/js/steem_keychain.js:
--------------------------------------------------------------------------------
1 | // Content script interfacing the website and the extension
2 | var steem_keychain = {
3 | current_id: 1,
4 | requests: {},
5 | handshake_callback: null,
6 |
7 | requestHandshake: function(callback) {
8 | this.handshake_callback = callback;
9 | this.dispatchCustomEvent("swHandshake", "");
10 | },
11 |
12 | requestVerifyKey: function(account, message, key, callback, rpc) {
13 | var request = {
14 | type: "decode",
15 | username: account,
16 | message: message,
17 | method: key,
18 | rpc
19 | };
20 |
21 | this.dispatchCustomEvent("swRequest", request, callback);
22 | },
23 |
24 | requestSignBuffer: function(account, message, key, callback, rpc) {
25 | var request = {
26 | type: "signBuffer",
27 | username: account,
28 | message: message,
29 | method: key,
30 | rpc
31 | };
32 |
33 | this.dispatchCustomEvent("swRequest", request, callback);
34 | },
35 |
36 | requestAddAccountAuthority: function(
37 | account,
38 | authorizedUsername,
39 | role,
40 | weight,
41 | callback,
42 | rpc
43 | ) {
44 | var request = {
45 | type: "addAccountAuthority",
46 | username: account,
47 | authorizedUsername,
48 | role,
49 | weight,
50 | method: "Active",
51 | rpc
52 | };
53 |
54 | this.dispatchCustomEvent("swRequest", request, callback);
55 | },
56 |
57 | requestRemoveAccountAuthority: function(
58 | account,
59 | authorizedUsername,
60 | role,
61 | callback,
62 | rpc
63 | ) {
64 | var request = {
65 | type: "removeAccountAuthority",
66 | username: account,
67 | authorizedUsername,
68 | role,
69 | method: "Active",
70 | rpc
71 | };
72 |
73 | this.dispatchCustomEvent("swRequest", request, callback);
74 | },
75 | requestAddKeyAuthority: function(
76 | account,
77 | authorizedKey,
78 | role,
79 | weight,
80 | callback,
81 | rpc
82 | ) {
83 | var request = {
84 | type: "addKeyAuthority",
85 | username: account,
86 | authorizedKey,
87 | weight,
88 | role,
89 | method: "Active",
90 | rpc
91 | };
92 |
93 | this.dispatchCustomEvent("swRequest", request, callback);
94 | },
95 | requestRemoveKeyAuthority: function(
96 | account,
97 | authorizedKey,
98 | role,
99 | callback,
100 | rpc
101 | ) {
102 | var request = {
103 | type: "removeKeyAuthority",
104 | username: account,
105 | authorizedKey,
106 | role,
107 | method: "Active",
108 | rpc
109 | };
110 |
111 | this.dispatchCustomEvent("swRequest", request, callback);
112 | },
113 |
114 | requestBroadcast: function(account, operations, key, callback, rpc) {
115 | var request = {
116 | type: "broadcast",
117 | username: account,
118 | operations,
119 | method: key,
120 | rpc
121 | };
122 |
123 | this.dispatchCustomEvent("swRequest", request, callback);
124 | },
125 |
126 | requestSignTx: function(account, tx, key, callback, rpc) {
127 | var request = {
128 | type: "signTx",
129 | username: account,
130 | tx,
131 | method: key,
132 | rpc
133 | };
134 |
135 | this.dispatchCustomEvent("swRequest", request, callback);
136 | },
137 |
138 | requestSignedCall: function(account, method, params, key, callback, rpc) {
139 | console.log("getting request");
140 | var request = {
141 | type: "signedCall",
142 | username: account,
143 | method,
144 | params,
145 | typeWif: key,
146 | rpc
147 | };
148 | console.log(request);
149 | this.dispatchCustomEvent("swRequest", request, callback);
150 | },
151 |
152 | // Example comment_options: {"author":"stoodkev","permlink":"hi","max_accepted_payout":"100000.000 SBD","percent_steem_dollars":10000,"allow_votes":true,"allow_curation_rewards":true,"extensions":[[0,{"beneficiaries":[{"account":"yabapmatt","weight":1000},{"account":"steemplus-pay","weight":500}]}]]}
153 | requestPost: function(
154 | account,
155 | title,
156 | body,
157 | parent_perm,
158 | parent_account,
159 | json_metadata,
160 | permlink,
161 | comment_options,
162 | callback,
163 | rpc
164 | ) {
165 | var request = {
166 | type: "post",
167 | username: account,
168 | title,
169 | body,
170 | parent_perm,
171 | parent_username: parent_account,
172 | json_metadata,
173 | permlink,
174 | comment_options,
175 | rpc
176 | };
177 | this.dispatchCustomEvent("swRequest", request, callback);
178 | },
179 |
180 | requestVote: function(account, permlink, author, weight, callback, rpc) {
181 | var request = {
182 | type: "vote",
183 | username: account,
184 | permlink,
185 | author,
186 | weight,
187 | rpc
188 | };
189 |
190 | this.dispatchCustomEvent("swRequest", request, callback);
191 | },
192 |
193 | requestCustomJson: function(
194 | account,
195 | id,
196 | key,
197 | json,
198 | display_msg,
199 | callback,
200 | rpc
201 | ) {
202 | var request = {
203 | type: "custom",
204 | username: account,
205 | id: id, //can be "custom", "follow", "reblog" etc.
206 | method: key, // Posting key is used by default, active can be specified for id=custom .
207 | json: json, //content of your json
208 | display_msg: display_msg,
209 | rpc
210 | };
211 |
212 | this.dispatchCustomEvent("swRequest", request, callback);
213 | },
214 | requestTransfer: function(
215 | account,
216 | to,
217 | amount,
218 | memo,
219 | currency,
220 | callback,
221 | enforce = false,
222 | rpc
223 | ) {
224 | var request = {
225 | type: "transfer",
226 | username: account,
227 | to,
228 | amount,
229 | memo,
230 | enforce,
231 | currency,
232 | rpc
233 | };
234 | this.dispatchCustomEvent("swRequest", request, callback);
235 | },
236 | requestSendToken: function(
237 | account,
238 | to,
239 | amount,
240 | memo,
241 | currency,
242 | callback,
243 | rpc
244 | ) {
245 | var request = {
246 | type: "sendToken",
247 | username: account,
248 | to,
249 | amount,
250 | memo,
251 | currency,
252 | rpc
253 | };
254 | this.dispatchCustomEvent("swRequest", request, callback);
255 | },
256 | requestDelegation: function(
257 | username,
258 | delegatee,
259 | amount,
260 | unit,
261 | callback,
262 | rpc
263 | ) {
264 | var request = {
265 | type: "delegation",
266 | username,
267 | delegatee,
268 | amount,
269 | unit,
270 | rpc
271 | };
272 | this.dispatchCustomEvent("swRequest", request, callback);
273 | },
274 | requestWitnessVote: function(username, witness, vote, callback, rpc) {
275 | var request = {
276 | type: "witnessVote",
277 | username,
278 | witness,
279 | vote,
280 | rpc
281 | };
282 | this.dispatchCustomEvent("swRequest", request, callback);
283 | },
284 | requestPowerUp: function(username, recipient, steem, callback, rpc) {
285 | var request = {
286 | type: "powerUp",
287 | username,
288 | recipient,
289 | steem,
290 | rpc
291 | };
292 | this.dispatchCustomEvent("swRequest", request, callback);
293 | },
294 | requestPowerDown: function(username, steem_power, callback, rpc) {
295 | var request = {
296 | type: "powerDown",
297 | username,
298 | steem_power,
299 | rpc
300 | };
301 | this.dispatchCustomEvent("swRequest", request, callback);
302 | },
303 |
304 | requestCreateClaimedAccount: function(
305 | username,
306 | new_account,
307 | owner,
308 | active,
309 | posting,
310 | memo,
311 | callback,
312 | rpc
313 | ) {
314 | const request = {
315 | type: "createClaimedAccount",
316 | username,
317 | new_account,
318 | owner,
319 | active,
320 | posting,
321 | memo,
322 | rpc
323 | };
324 |
325 | this.dispatchCustomEvent("swRequest", request, callback);
326 | },
327 |
328 | //HF21
329 | requestCreateProposal: function(
330 | username,
331 | receiver,
332 | subject,
333 | permlink,
334 | daily_pay,
335 | start,
336 | end,
337 | extensions,
338 | callback,
339 | rpc
340 | ) {
341 | const request = {
342 | type: "createProposal",
343 | username,
344 | receiver,
345 | subject,
346 | permlink,
347 | start,
348 | end,
349 | daily_pay,
350 | extensions,
351 | rpc
352 | };
353 |
354 | this.dispatchCustomEvent("swRequest", request, callback);
355 | },
356 |
357 | requestRemoveProposal: function(
358 | username,
359 | proposal_ids,
360 | extensions,
361 | callback,
362 | rpc
363 | ) {
364 | const request = {
365 | type: "removeProposal",
366 | username,
367 | proposal_ids,
368 | extensions,
369 | rpc
370 | };
371 |
372 | this.dispatchCustomEvent("swRequest", request, callback);
373 | },
374 | requestUpdateProposalVote: function(
375 | username,
376 | proposal_ids,
377 | approve,
378 | extensions,
379 | callback,
380 | rpc
381 | ) {
382 | const request = {
383 | type: "updateProposalVote",
384 | username,
385 | proposal_ids,
386 | approve,
387 | extensions,
388 | rpc
389 | };
390 |
391 | this.dispatchCustomEvent("swRequest", request, callback);
392 | },
393 |
394 | // Send the customEvent
395 | dispatchCustomEvent: function(name, data, callback) {
396 | this.requests[this.current_id] = callback;
397 | data = Object.assign(
398 | {
399 | request_id: this.current_id
400 | },
401 | data
402 | );
403 | document.dispatchEvent(
404 | new CustomEvent(name, {
405 | detail: data
406 | })
407 | );
408 | this.current_id++;
409 | }
410 | };
411 |
412 | window.addEventListener(
413 | "message",
414 | function(event) {
415 | // We only accept messages from ourselves
416 | if (event.source != window) return;
417 |
418 | if (event.data.type && event.data.type == "steem_keychain_response") {
419 | const response = event.data.response;
420 | if (response && response.request_id) {
421 | if (steem_keychain.requests[response.request_id]) {
422 | steem_keychain.requests[response.request_id](response);
423 | delete steem_keychain.requests[response.request_id];
424 | }
425 | }
426 | } else if (
427 | event.data.type &&
428 | event.data.type == "steem_keychain_handshake"
429 | ) {
430 | if (steem_keychain.handshake_callback) {
431 | steem_keychain.handshake_callback();
432 | }
433 | }
434 | },
435 | false
436 | );
437 |
--------------------------------------------------------------------------------
/js/popup/popup.js:
--------------------------------------------------------------------------------
1 | let mk = null;
2 | let activeAccount;
3 | const STEEMIT_VOTE_REGENERATION_SECONDS = 5 * 60 * 60 * 24;
4 | let custom_created = false;
5 | let manageKey,
6 | getPref = false;
7 | let to_autocomplete = [];
8 | let accountsList = new AccountsList();
9 | //chrome.storage.local.remove("transfer_to");
10 |
11 | $("#copied").hide();
12 | $("#witness_votes").hide();
13 |
14 | // Ask background if it is unlocked
15 | getMK();
16 |
17 | // Check if autolock and set it to background
18 | function sendAutolock() {
19 | chrome.storage.local.get(["autolock"], function(items) {
20 | if (items.autolock != undefined) {
21 | $(".autolock input").prop("checked", false);
22 | $("#" + JSON.parse(items.autolock).type).prop("checked", true);
23 | $("#mn").val(JSON.parse(items.autolock).mn);
24 | setAutolock(items.autolock);
25 | $("#mn").css(
26 | "visibility",
27 | JSON.parse(items.autolock).type == "idle" ? "visible" : "hidden"
28 | );
29 | }
30 | });
31 | }
32 |
33 | function checkKeychainify() {
34 | chrome.storage.local.get(["keychainify_enabled"], function(items) {
35 | if (items.keychainify_enabled !== undefined) {
36 | $(".enable_keychainify input").prop("checked", items.keychainify_enabled);
37 | } else {
38 | $(".enable_keychainify input").prop("checked", false);
39 | }
40 | });
41 | }
42 |
43 | // Save autolock
44 | $(".autolock").click(function() {
45 | $(".autolock input").prop("checked", false);
46 | $(this)
47 | .find("input")
48 | .prop("checked", "true");
49 | $("#mn").css(
50 | "visibility",
51 | $(this)
52 | .find("input")
53 | .attr("id") == "idle"
54 | ? "visible"
55 | : "hidden"
56 | );
57 | });
58 |
59 | // Save enable_keychainify
60 | $(".enable_keychainify").click(function() {
61 | const enable_keychainify = $(this)
62 | .find("input")
63 | .prop("checked");
64 | $(this)
65 | .find("input")
66 | .prop("checked", !enable_keychainify);
67 | chrome.storage.local.set({
68 | keychainify_enabled: !enable_keychainify
69 | });
70 | });
71 |
72 | // Saving autolock options
73 | $("#save_autolock").click(function() {
74 | const autolock = JSON.stringify({
75 | type:
76 | $(".autolock input:checkbox:checked")
77 | .eq(0)
78 | .attr("id") || "default",
79 | mn: $("#mn").val() || 10
80 | });
81 | chrome.storage.local.set({
82 | autolock: autolock
83 | });
84 | console.log("set");
85 | setAutolock(autolock);
86 | initializeVisibility();
87 | initializeMainMenu();
88 | });
89 |
90 | // Lock the wallet and destroy traces of the mk
91 | $("#lock").click(function() {
92 | sendMk(null);
93 | accountsList.save(mk);
94 | $("#back_forgot_settings").attr("id", "back_forgot");
95 | mk = null;
96 | showUnlock();
97 | });
98 |
99 | const sendMk = mk => {
100 | chrome.runtime.sendMessage({
101 | command: "sendMk",
102 | mk
103 | });
104 | };
105 | // Unlock with masterkey and show the main menu
106 | $("#submit_unlock").click(function() {
107 | chrome.storage.local.get(["accounts"], function(items) {
108 | const pwd = $("#unlock_pwd").val();
109 | const accs = decryptToJson(items.accounts, pwd);
110 | console.log(accs);
111 | if (accs) {
112 | mk = pwd;
113 | sendMk(mk);
114 | $(".error_div").html("");
115 | $(".error_div").hide();
116 | $("#unlock_pwd").val("");
117 | initializeMainMenu();
118 | initializeVisibility();
119 | } else {
120 | showError(chrome.i18n.getMessage("wrong_password"));
121 | }
122 | });
123 | });
124 |
125 | // If user forgot Mk, he can reset the wallet
126 | $("#forgot_div button").click(function() {
127 | accountsList.clear();
128 | mk = null;
129 | $("#forgot_div").hide();
130 | $("#register").show();
131 | });
132 |
133 | // Registration confirmation
134 | $("#submit_master_pwd").click(function() {
135 | if (acceptMP($("#master_pwd").val())) {
136 | if ($("#master_pwd").val() == $("#confirm_master_pwd").val()) {
137 | mk = $("#master_pwd").val();
138 | sendMk(mk);
139 | initializeMainMenu();
140 | $(".error_div").hide();
141 | } else {
142 | showError(chrome.i18n.getMessage("popup_password_mismatch"));
143 | }
144 | } else {
145 | showError(chrome.i18n.getMessage("popup_password_regex"));
146 | }
147 | });
148 | function acceptMP(mp) {
149 | return (
150 | mp.length >= 16 ||
151 | (mp.length >= 8 &&
152 | mp.match(/.*[a-z].*/) &&
153 | mp.match(/.*[A-Z].*/) &&
154 | mp.match(/.*[0-9].*/))
155 | );
156 | }
157 | // Set visibilities back to normal when coming back to main menu
158 | function initializeMainMenu() {
159 | console.log("init");
160 | sendAutolock();
161 | checkKeychainify();
162 | manageKey = false;
163 | getPref = false;
164 | chrome.storage.local.get(
165 | ["accounts", "last_account", "rpc", "current_rpc", "transfer_to"],
166 | function(items) {
167 | to_autocomplete = items.transfer_to ? JSON.parse(items.transfer_to) : {};
168 | if (items.accounts)
169 | accountsList.init(
170 | decryptToJson(items.accounts, mk),
171 | items.last_account
172 | );
173 | loadRPC(items.current_rpc);
174 | console.log(accountsList.getList());
175 | $("#accounts").empty();
176 | if (!accountsList.isEmpty()) {
177 | $(".usernames").html("");
178 | for (account of accountsList.getList()) {
179 | $(".usernames select").append(
180 | ""
181 | );
182 | }
183 | $(".usernames select")
184 | .eq(0)
185 | .append(
186 | ``
189 | );
190 | initiateCustomSelect();
191 | } else {
192 | $("#main").hide();
193 | $("#register").hide();
194 | $("#add_account_types_div").show();
195 | $("#add_account_types_div .back_enabled").addClass("back_disabled");
196 | }
197 | }
198 | );
199 | }
200 | // Show Confirmation window before transfer
201 | $("#send_transfer").click(function() {
202 | confirmTransfer();
203 | });
204 |
205 | function confirmTransfer() {
206 | $("#confirm_send_div").show();
207 | $("#send_div").hide();
208 | const to = $("#recipient").val();
209 | const amount = $("#amt_send").val();
210 | const currency = $("#currency_send .select-selected").html();
211 | let memo = $("#memo_send").val();
212 | $("#from_conf_transfer").text("@" + activeAccount.getName());
213 | $("#to_conf_transfer").text("@" + to);
214 | $("#amt_conf_transfer").text(amount + " " + currency);
215 | $("#memo_conf_transfer").text(
216 | (memo == "" ? chrome.i18n.getMessage("popup_empty") : memo) +
217 | ((memo != "" && $("#encrypt_memo").prop("checked")) || memo[0] == "#"
218 | ? ` (${chrome.i18n.getMessage("popup_encrypted")})`
219 | : "")
220 | );
221 | }
222 |
223 | // Send STEEM or SBD to an user
224 | $("#confirm_send_transfer").click(function() {
225 | showLoader();
226 | sendTransfer();
227 | });
228 |
229 | // Vote for witnesses
230 | function voteFor(name) {
231 | if (activeAccount.hasKey("active")) {
232 | $("#" + name + " img").attr("src", "../images/loading.gif");
233 |
234 | steem.broadcast.accountWitnessVote(
235 | activeAccount.getKey("active"),
236 | activeAccount.getName(),
237 | name,
238 | true,
239 | function(err, result) {
240 | if (err == null) {
241 | setTimeout(function() {
242 | if ($(".witness_container:visible").length == 0)
243 | $("#witness_votes").animate(
244 | {
245 | opacity: 0
246 | },
247 | 500,
248 | function() {
249 | $("#witness_votes").hide();
250 | }
251 | );
252 | }, 1000);
253 |
254 | $("#" + name + " img").attr("src", "../images/icon_witness-vote.svg");
255 | }
256 | }
257 | );
258 | } else {
259 | $("#witness_votes").hide();
260 | $("#main").hide();
261 | $("#add_key_div").show();
262 | manageKey = true;
263 | manageKeys(
264 | $(".usernames .select-selected")
265 | .eq(0)
266 | .html()
267 | );
268 | showError(chrome.i18n.getMessage("popup_witness_key"));
269 | }
270 | }
271 |
272 | // Send a transfer
273 | async function sendTransfer() {
274 | const to = $("#recipient")
275 | .val()
276 | .replace(" ", "");
277 | const amount = $("#amt_send").val();
278 | const currency = $("#currency_send .select-selected").html();
279 | let memo = $("#memo_send").val();
280 | if ((memo != "" && $("#encrypt_memo").prop("checked")) || memo[0] == "#") {
281 | try {
282 | const receiver = await steem.api.getAccountsAsync([to]);
283 | const memoReceiver = receiver["0"].memo_key;
284 | memo = memo[0] == "#" ? memo : "#" + memo;
285 | memo = window.encodeMemo(
286 | activeAccount.getKey("memo"),
287 | memoReceiver,
288 | memo
289 | );
290 | } catch (e) {
291 | console.log(e);
292 | }
293 | }
294 | if (to != "" && amount != "" && amount >= 0.001) {
295 | steem.broadcast.transfer(
296 | activeAccount.getKey("active"),
297 | activeAccount.getName(),
298 | to,
299 | parseFloat(amount).toFixed(3) + " " + currency,
300 | memo,
301 | async function(err, result) {
302 | $("#send_loader").hide();
303 | $("#confirm_send_transfer").show();
304 | if (err == null) {
305 | const sender = await steem.api.getAccountsAsync([
306 | activeAccount.getName()
307 | ]);
308 | sbd = sender["0"].sbd_balance.replace("SBD", "");
309 | steem_p = sender["0"].balance.replace("STEEM", "");
310 | $("#confirm_send_div").hide();
311 | $("#send_div").show();
312 | if (currency == "SBD") {
313 | $(".transfer_balance div")
314 | .eq(1)
315 | .html(numberWithCommas(sbd));
316 | } else if (currency == "STEEM") {
317 | $(".transfer_balance div")
318 | .eq(1)
319 | .html(numberWithCommas(steem_p));
320 | }
321 | $(".error_div").hide();
322 | $(".success_div")
323 | .html(chrome.i18n.getMessage("popup_transfer_success"))
324 | .show();
325 | chrome.storage.local.get({transfer_to: JSON.stringify({})}, function(
326 | items
327 | ) {
328 | let transfer_to = JSON.parse(items.transfer_to);
329 | if (!transfer_to[activeAccount.getName()])
330 | transfer_to[activeAccount.getName()] = [];
331 | console.log(transfer_to);
332 | if (
333 | transfer_to[activeAccount.getName()].filter(elt => {
334 | return elt == to;
335 | }).length == 0
336 | )
337 | transfer_to[activeAccount.getName()].push(to);
338 | console.log(transfer_to);
339 |
340 | console.log(JSON.stringify(transfer_to));
341 | chrome.storage.local.set({
342 | transfer_to: JSON.stringify(transfer_to)
343 | });
344 | });
345 | setTimeout(function() {
346 | $(".success_div").hide();
347 | }, 5000);
348 | } else {
349 | $(".success_div").hide();
350 | showError(chrome.i18n.getMessage("unknown_error"));
351 | }
352 | $("#send_transfer").show();
353 | }
354 | );
355 | } else {
356 | showError(chrome.i18n.getMessage("popup_accounts_fill"));
357 | $("#send_loader").hide();
358 | $("#send_transfer").show();
359 | }
360 | }
361 |
--------------------------------------------------------------------------------