UNPKG

aion-gql-webcomponents

Version:
720 lines (719 loc) 33.2 kB
import '../../global/global.js'; import { Constant } from "../../common/Constant"; import { Transaction } from "../../common/Transaction"; import { TxnResponse } from "../../common/TxnResponse"; import { CryptoUtil } from "../../providers/util/CryptoUtil"; import { LedgerProvider } from "../../providers/impl/ledger/LedgerProvider"; import PrivateKeyWalletProvider from "../../providers/impl/PrivateKeyWalletProvider"; import KeystoreWalletProvider from "../../providers/impl/KeystoreWalletProvider"; import AionPayService from "./AionPayService"; import { TransactionUtil } from "../../providers/util/TransactionUtil"; export class AionPay { constructor() { this.default_button_text = "Pay"; this.to_readonly = false; this.readonly = false; this.unlockBy = "ledger"; this.visible = false; this.inputDialogEnable = false; this.txnInProgress = false; this.txnDone = false; this.showConfirm = false; this.isNotification = false; this.isError = false; this.errors = []; this.keystoreLoadingPercentage = 0; this.gas = TransactionUtil.defaultNrgLimit; this.gasPrice = TransactionUtil.defaultNrgPrice; this.txnResponse = new TxnResponse(); this.handleHidePaymentDialog = this.handleHidePaymentDialog.bind(this); this.handleShowPaymentDialog = this.handleShowPaymentDialog.bind(this); this.handleHideTransactionInprogressDialog = this.handleHideTransactionInprogressDialog.bind(this); this.handleCloseConfirmDialog = this.handleCloseConfirmDialog.bind(this); this.handleHideError = this.handleHideError.bind(this); this.handleHideNotification = this.handleHideNotification.bind(this); this.handleResetData = this.handleResetData.bind(this); this.handleShowInputDialog = this.handleShowInputDialog.bind(this); this.handleHideInputDialog = this.handleHideInputDialog.bind(this); this.signPayment = this.signPayment.bind(this); this.confirmPayment = this.confirmPayment.bind(this); this.handleUnlockBy = this.handleUnlockBy.bind(this); this.handleFromInput = this.handleFromInput.bind(this); this.handleToInput = this.handleToInput.bind(this); this.handleValueInput = this.handleValueInput.bind(this); this.handleNrgInput = this.handleNrgInput.bind(this); this.handleNrgPriceInput = this.handleNrgPriceInput.bind(this); this.handleMessageInput = this.handleMessageInput.bind(this); this.handlePrivateKeyInput = this.handlePrivateKeyInput.bind(this); this.handleKeyStoreFileSelected = this.handleKeyStoreFileSelected.bind(this); this.handleKeystorePasswordInput = this.handleKeystorePasswordInput.bind(this); this.handleUnlockKeystore = this.handleUnlockKeystore.bind(this); this.handleDerivePublicKey = this.handleDerivePublicKey.bind(this); this.submitRawTransansaction = this.submitRawTransansaction.bind(this); this.handleLedgerConnect = this.handleLedgerConnect.bind(this); } componentWillLoad() { if (this.to) this._to = this.to.toLowerCase(); if (this.to) this.to_readonly = true; this.service = new AionPayService(this.gqlUrl); } refreshAndShow() { this.handleResetData(); this.handleShowPaymentDialog(); } showWithData(refId, to, value, data) { this.handleResetData(); this._to = to; this.value = value; this.message = data; this.readonly = true; this.refId = refId; this.handleShowPaymentDialog(); } handleShowPaymentDialog() { this.visible = true; } handleHidePaymentDialog() { this.visible = false; this.inputDialogEnable = false; this.handleResetData(); } handleShowInputDialog() { if (!this.from) { this.isError = true; this.errors.push("Please select a wallet provider and unlock it."); return; } this.visible = false; this.inputDialogEnable = true; } handleHideInputDialog() { this.inputDialogEnable = false; } handleHideTransactionInprogressDialog() { this.txnInProgress = false; this.txnDone = false; this.visible = false; this.handleResetData(); } handleCloseConfirmDialog() { this.showConfirm = false; this.handleResetData(); } handleHideError() { this.isError = false; this.errors.length = 0; } handleHideNotification() { this.isNotification = false; this.notification = null; } handleResetData() { this.txnInProgress = false; this.showConfirm = false; this.txnDone = false; this.visible = false; this.resetFromAddressData(); if (!this.to_readonly) { this._to = ''; } this.value = 0; this.message = ''; this.gas = TransactionUtil.defaultNrgLimit; this.gasPrice = TransactionUtil.defaultNrgPrice; this.isError = false; this.errors.length = 0; this.isNotification = false; this.notification = ''; this.resetTxnResponse(); } resetFromAddressData() { this.from = ''; this.fromBalance = null; this.privateKey = ''; this.privateKey = ''; this.keystore_password = ''; this.keystore = null; this.keystoreLoadingPercentage = 0; } resetTxnResponse() { this.txnResponse.txHash = ''; this.txnResponse.msgHash = ''; this.txnResponse.error = ''; this.txnResponse.txResult = ''; this.txnResponse.status = ''; } handleUnlockBy(event) { let oldValue = this.unlockBy; this.unlockBy = event.target.value; if (oldValue != this.unlockBy) this.resetFromAddressData(); } handleToInput(event) { this._to = event.target.value; if (event.target.validity.typeMismatch) { console.log('this element is not valid'); } } handleFromInput(event) { this.from = event.target.value; if (event.target.validity.typeMismatch) { console.log('this element is not valid'); } } handleValueInput(event) { this.value = event.target.value; if (event.target.validity.typeMismatch) { console.log('this element is not valid'); } } handleNrgInput(event) { this.gas = event.target.value; } handleNrgPriceInput(event) { this.gasPrice = event.target.value; } handleMessageInput(event) { this.message = event.target.value; if (this.unlockBy == 'ledger') { if (this.message.length > 50) { if (!this.isError) { this.isError = true; this.errors.length = 0; this.errors.push("Please keep your message within 50 characters while using ledger. Or, your transaction may fail."); } } else { if (this.isError) { this.isError = false; this.errors.length = 0; } } } } handlePrivateKeyInput(event) { this.privateKey = event.target.value; if (event.target.validity.typeMismatch) { console.log('this element is not valid'); } } async handleDerivePublicKey() { if (!this.privateKey || this.privateKey.trim().length == 0) return; try { this.handleHideError(); this.provider = null; this.provider = new PrivateKeyWalletProvider(this.privateKey); let [address] = await this.provider.unlock(null); if (address) this.from = address; this.updateBalance(); } catch (error) { console.log(error); this.from = ''; this.fromBalance = null; this.isError = true; this.errors.push("Public Key derivation failed: " + error.toString()); throw error; } } async updateBalance() { try { let [balance, nrgPrice] = await this.service.fetchBalance(this.from); if (balance) this.fromBalance = CryptoUtil.convertnAmpBalanceToAION(balance); if (nrgPrice) this.gasPrice = nrgPrice; } catch (error) { this.isError = true; this.errors.push("Error getting balance for the address"); this.errors.push("[Reason] " + error); return; } } handleKeyStoreFileSelected(event) { this.resetFromAddressData(); this.keystore = event.srcElement.files[0]; } handleKeystorePasswordInput(event) { this.keystore_password = event.target.value; } async handleUnlockKeystore() { let reader = new FileReader(); try { reader.readAsArrayBuffer(this.keystore); } catch (error) { console.error("Error loading keystore file" + error); this.isError = true; this.errors.push("Invalid keystore file. " + error.toString()); } let me = this; reader.onload = async function () { let content = reader.result; me.handleHideError(); me.provider = new KeystoreWalletProvider(content, me.keystore_password); try { let [address] = await me.provider.unlock((progress) => { me.keystoreLoadingPercentage = Math.round(progress); }); me.from = address; me.updateBalance(); } catch (error) { console.log("Error in opening keystore fie. " + error); me.isError = true; me.errors.push("Could not open the keystore file. " + error.toString()); } }; } async handleLedgerConnect() { this.provider = new LedgerProvider(); try { let [address] = await this.provider.unlock(null); this.from = address; this.updateBalance(); } catch (e) { this.isError = true; this.errors.push(e.toString()); } } validateInput() { this.handleHideError(); if (isNaN(this.value)) { this.isError = true; this.errors.push("Amount is not valid"); } if (this.unlockBy == 'private_key') { if (!this.privateKey || this.privateKey.trim().length == 0) { this.isError = true; this.errors.push("Private key can not be empty"); } } if (!this._to || this._to.trim().length == 0) { this.isError = true; this.errors.push("To address can not be empty"); } return !this.isError; } async signPayment(e) { e.preventDefault(); if (!this.validateInput()) { console.log('not a valid input'); return; } console.log("All valid input"); if (!this._to) { this.handleDerivePublicKey(); } console.log("lets do signing first.."); this.amount = CryptoUtil.convertAIONTonAmpBalance(this.value); let txn = new Transaction(); txn.to = this._to; txn.value = this.amount; if (this.message) { txn.data = this.message; } txn.gas = this.gas + ''; txn.gasPrice = this.gasPrice + ''; let retVal = null; try { retVal = await this.service.fetchNonceNrg(this.from, txn); } catch (e) { this.isError = true; this.errors.push("Error to get nonce for the address"); this.errors.push("[Reason] " + e); return; } if (!retVal) { this.isError = true; this.errors.push("Unable to get nonce and nrgPrice from AION kernel"); return; } txn.nonce = retVal[0]; let estimatedNrg = retVal[1]; if (estimatedNrg && estimatedNrg > 0) { if (Number(txn.gas) < estimatedNrg) { let r = confirm("Estimated Nrg : " + estimatedNrg + "\nDefault Nrg Limit : " + txn.gas + "\n\nDo you want to update the Nrg Limit to " + estimatedNrg + "?"); if (r == true) { this.gas = estimatedNrg; return; } else { } } } console.log("Fetching current nonce " + txn.nonce); if (this.unlockBy == 'ledger') { this.isNotification = true; this.notification = "Please check your ledger device to confirm the transaction."; } try { this.encodedTxn = await this.provider.sign(txn); } catch (error) { console.log(error); this.isError = true; this.errors.push("Error in signing transaction. Please refresh and try again"); this.errors.push("[Reason] " + error); throw error; } this.encodedTxn.input.from = this.from; this.showConfirm = true; this.visible = false; this.inputDialogEnable = false; console.log(this.encodedTxn); } async confirmPayment() { this.showConfirm = false; this.submitRawTransansaction(this.encodedTxn.rawTransaction); } async submitRawTransansaction(encodedTx) { this.txnInProgress = true; this.txnDone = false; try { this.transactionInProgress.emit({ refId: this.refId, data: encodedTx }); this.txnResponse = await this.service.sendRawTransaction(encodedTx); console.log(this.txnResponse); this.txnDone = true; this.transactionCompleted.emit({ refId: this.refId, data: this.txnResponse }); } catch (error) { this.txnDone = true; this.isError = true; this.errors.push("Error sending the transaction"); this.errors.push("[Reason] " + error); this.transactionFailed.emit({ refId: this.refId, data: error.toString() }); throw error; } finally { } } renderError() { return (h("div", { class: "error-section" }, this.isError ? h("div", { class: "notification is-warning is-small" }, h("button", { class: "delete", onClick: this.handleHideError }), h("ul", null, this.errors.map((msg) => h("li", null, msg)))) : null)); } renderNotification() { return (h("div", null, this.isNotification ? h("div", { class: "notification is-info error-section" }, h("button", { class: "delete", onClick: this.handleHideNotification }), this.notification) : null)); } renderSelectProvider() { return (h("div", null, h("div", { class: "modal is-active" }, h("div", { class: "modal-background" }), h("div", { class: "modal-card" }, h("header", { class: "modal-card-head" }, h("img", { src: Constant.aion_logo, class: "aion-image" }), h("p", { class: "modal-card-title" }, "Choose your wallet provider"), h("button", { class: "delete", "aria-label": "close", onClick: this.handleHidePaymentDialog }, "\u00D7")), h("section", { class: "modal-card-body" }, this.renderError(), h("div", { class: "field" }, h("div", { class: "control" }, h("label", { class: "label is-small" }, h("input", { type: "radio", name: "unlock_by", value: "ledger", checked: this.unlockBy === 'ledger', onClick: (event) => this.handleUnlockBy(event) }), "\u00A0Ledger"), h("label", { class: "label is-small" }, h("input", { type: "radio", name: "unlock_by", value: "keystore", checked: this.unlockBy === 'keystore', onClick: (event) => this.handleUnlockBy(event) }), "\u00A0Keystore File"), h("label", { class: "label is-small" }, h("input", { type: "radio", name: "unlock_by", value: "private_key", checked: this.unlockBy === 'private_key', onClick: (event) => this.handleUnlockBy(event) }), "\u00A0Private Key"))), h("hr", null), h("div", { class: "form" }, this.renderUnlockOptions(), h("div", { class: "field" }, h("label", { class: "label" }, "\u00A0\u00A0"), h("div", { class: "control" }, h("input", { id: "from", placeholder: "From Address", class: "input is-small", value: this.from, onInput: (e) => this.handleFromInput(e), readOnly: true, disabled: true }), this.fromBalance ? h("label", { class: "label is-small from-balance is-pulled-right" }, "Balance: ", this.fromBalance, " AION") : null)))), h("footer", { class: "modal-card-foot" }, h("button", { class: "button is-primary is-small is-rounded is-pulled-right", disabled: !this.from, onClick: this.handleShowInputDialog }, "Next"), h("button", { class: "button is-danger is-small is-rounded is-right", onClick: this.handleHidePaymentDialog }, "Cancel")))))); } renderUnlockOptions() { return (h("div", null, this.unlockBy == 'private_key' ? h("div", { class: "field" }, h("label", { class: "label is-small" }, "Private Key"), h("div", { class: "control" }, h("input", { id: "private_key", placeholder: "Private Key", class: "input is-small", value: this.privateKey, type: "password", onInput: (e) => this.handlePrivateKeyInput(e), onBlur: this.handleDerivePublicKey }))) : null, this.unlockBy == 'keystore' ? this._renderUnlockByKeystore() : null, this.unlockBy == 'ledger' ? h("div", { class: "field" }, h("div", { class: "control" }, h("button", { class: "button is-small is-danger is-focused is-rounded is-outlined", onClick: this.handleLedgerConnect }, "Connect To Ledger"))) : null)); } _renderUnlockByKeystore() { return (h("div", null, h("div", { class: "field is-small" }, h("label", { class: "label is-small", htmlFor: "private_key" }, "Key Store File"), h("div", { class: "control" }, h("input", { id: "file-upload", class: "is-small", type: "file", accept: "*", onChange: (e) => this.handleKeyStoreFileSelected(e) }))), h("div", { class: "field" }, h("label", { class: "label is-small", htmlFor: "keystore_password" }, "Passowrd"), h("div", { class: "control" }, h("input", { id: "keystore_password", type: "password", class: "input is-small", value: this.keystore_password, onInput: this.handleKeystorePasswordInput }))), h("div", { class: "field is-grouped" }, h("div", { class: "control is-pulled-right" }, h("button", { name: "unlock_button", class: "button is-small is-danger is-focused is-rounded is-outlined", onClick: this.handleUnlockKeystore }, "Unlock"))), this.keystoreLoadingPercentage && this.keystoreLoadingPercentage != 0 && this.keystoreLoadingPercentage != 100 ? h("div", { class: "modal is-active" }, h("div", { class: "modal-background " }), h("div", { class: "modal-card" }, h("label", { class: "color-white" }, "Unlocking keystore ... ", this.keystoreLoadingPercentage, "%"), h("progress", { class: "progress", value: this.keystoreLoadingPercentage, max: "100" }, this.keystoreLoadingPercentage, "%"))) : null)); } renderInputForm() { return (h("div", null, h("div", { class: "modal is-active" }, h("div", { class: "modal-background" }), h("div", { class: "modal-card" }, h("header", { class: "modal-card-head" }, h("img", { src: Constant.aion_logo, class: "aion-image" }), h("p", { class: "modal-card-title" }, "Transfer AION"), h("button", { class: "delete", "aria-label": "close", onClick: this.handleHidePaymentDialog }, "\u00D7")), h("section", { class: "modal-card-body form" }, this.renderError(), this.renderNotification(), h("div", { class: "field" }, h("label", { class: "label is-small", htmlFor: "from" }, "From"), h("div", { class: "control" }, h("input", { id: "from", placeholder: "From Address", class: "input is-small", value: this.from, onInput: (e) => this.handleFromInput(e), readOnly: true, disabled: true }), this.fromBalance ? h("label", { class: "label is-small is-pulled-right from-balance" }, "Balance: ", this.fromBalance, " AION") : null)), h("div", { class: "field" }, h("label", { class: "label is-small", htmlFor: "to" }, "To"), h("div", { class: "control" }, h("input", { id: "to", placeholder: "To Address", class: "input is-small", value: this._to, onInput: this.handleToInput, readOnly: this.to_readonly || this.readonly }))), h("div", { class: "field" }, h("label", { class: "label is-small", htmlFor: "value" }, "Amount"), h("div", { class: "control" }, h("input", { id: "value", placeholder: "Enter amount", class: "input is-small", value: this.value, type: "number", onInput: this.handleValueInput, readonly: this.readonly }))), h("div", { class: "columns" }, h("div", { class: "column" }, h("div", { class: "field" }, h("label", { class: "label is-small", htmlFor: "nrg" }, "Nrg Limit"), h("div", { class: "control" }, h("input", { id: "nrg", placeholder: "Nrg Limit", class: "input is-small", value: this.gas, type: "number", onInput: this.handleNrgInput })))), h("div", { class: "column" }, h("div", { class: "field" }, h("label", { class: "label is-small", htmlFor: "nrgPrice" }, "Nrg Price"), h("div", { class: "control" }, h("input", { id: "nrgPrice", placeholder: "Nrg Price", class: "input is-small", value: this.gasPrice, type: "number", onInput: this.handleNrgPriceInput }))))), h("div", { class: "field" }, h("label", { class: "label is-small", htmlFor: "message" }, "Message"), h("div", { class: "control" }, h("textarea", { id: "message", class: "textarea is-small", placeholder: "Optional message", onInput: this.handleMessageInput, readonly: this.readonly }, this.message)))), h("footer", { class: "modal-card-foot" }, h("button", { class: "button is-primary is-small is-rounded", onClick: this.signPayment }, "Next"), h("button", { class: "button is-danger is-small is-rounded", onClick: this.handleHidePaymentDialog }, "Cancel")))))); } renderShowConfirmation() { return (h("div", { class: "modal is-active" }, h("div", { class: "modal-background" }), h("div", { class: "modal-card" }, h("header", { class: "modal-card-head" }, h("img", { src: Constant.aion_logo, class: "aion-image" }), h("p", { class: "modal-card-title" }, "Confirm Transaction"), h("button", { class: "delete", "aria-label": "close", onClick: this.handleHidePaymentDialog }, "\u00D7")), h("section", { class: "modal-card-body form" }, this.renderError(), h("div", { class: "columns" }, h("div", { class: "column is-1" }, h("label", { class: "lable" }, "From")), h("div", { class: "column field" }, this.encodedTxn.input.from)), h("div", { class: "columns" }, h("div", { class: "column is-1" }, "To"), h("div", { class: "column field" }, this.encodedTxn.input.to)), h("div", { class: "columns" }, h("div", { class: "column is-1" }, "Value"), h("div", { class: "column field" }, CryptoUtil.convertnAmpBalanceToAION(this.encodedTxn.input.value), " AION")), h("div", { class: "columns" }, h("div", { class: "column is-1" }, "Nrg"), h("div", { class: "column field" }, this.encodedTxn.input.gas)), h("div", { class: "columns" }, h("div", { class: "column is-1" }, "Nrg Price"), h("div", { class: "column field" }, this.encodedTxn.input.gasPrice)), h("div", { class: "columns" }, h("div", { class: "column is-1" }, "Raw Transaction"), h("div", { class: "column field" }, h("textarea", { class: "input is-small", rows: 10, readOnly: true }, this.encodedTxn.rawTransaction)))), h("footer", { class: "modal-card-foot" }, h("button", { type: "button", class: "button is-primary is-small is-rounded", onClick: this.confirmPayment }, "Confirm"), h("button", { type: "button", class: "button is-danger is-small is-rounded", onClick: this.handleHidePaymentDialog }, "Close"))))); } renderTxnInProgress() { return (h("div", null, h("div", { class: "modal is-active" }, h("div", { class: "modal-background" }), h("div", { class: "modal-card" }, h("header", { class: "modal-card-head" }, h("img", { src: Constant.aion_logo, class: "aion-image" }), h("p", { class: "modal-card-title" }, "Sending AION"), h("button", { class: "delete", "aria-label": "close", onClick: this.handleHidePaymentDialog }, "\u00D7")), h("section", { class: "modal-card-body form" }, this.renderError(), !this.txnDone ? h("div", null, h("div", { class: "spinner" }, "Loading ..."), "\u00A0 ", h("i", null, "Sending transaction and waiting for at least one block confirmation. Please wait ...")) : h("div", null, this.txnResponse.txHash ? h("span", null, "Transaction Hash: ", h("a", { href: Constant.explorer_base_url + "transaction/" + this.txnResponse.txHash, target: "_blank" }, this.txnResponse.txHash)) : h("span", null, "Transaction could not be completed"))))))); } render() { return (h("div", { class: "aion-pay" }, this.visible ? this.renderSelectProvider() : null, this.inputDialogEnable ? this.renderInputForm() : null, this.txnInProgress ? this.renderTxnInProgress() : h("div", null), this.showConfirm ? this.renderShowConfirmation() : null, h("div", { id: "pay", onClick: this.handleShowPaymentDialog }, h("slot", null, h("button", { type: "button", class: "button pay-button is-primary" }, h("span", { class: "pay-button-text" }, h("img", { src: Constant.aion_logo, class: "img-valign" }), this.buttonText ? this.buttonText : this.default_button_text)))))); } static get is() { return "aion-pay"; } static get encapsulation() { return "shadow"; } static get properties() { return { "_to": { "state": true }, "buttonText": { "type": String, "attr": "button-text" }, "encodedTxn": { "state": true }, "errors": { "state": true }, "from": { "state": true }, "fromBalance": { "state": true }, "gas": { "state": true }, "gasPrice": { "state": true }, "gqlUrl": { "type": String, "attr": "gql-url" }, "inputDialogEnable": { "state": true }, "isError": { "state": true }, "isNotification": { "state": true }, "keystore_password": { "state": true }, "keystoreLoadingPercentage": { "state": true }, "message": { "state": true }, "notification": { "state": true }, "privateKey": { "state": true }, "refreshAndShow": { "method": true }, "showConfirm": { "state": true }, "showWithData": { "method": true }, "to": { "type": String, "attr": "to" }, "txnDone": { "state": true }, "txnInProgress": { "state": true }, "txnResponse": { "state": true }, "unlockBy": { "state": true }, "value": { "state": true }, "visible": { "state": true } }; } static get events() { return [{ "name": "TXN_COMPLETED", "method": "transactionCompleted", "bubbles": true, "cancelable": true, "composed": true }, { "name": "TXN_INPROGRESS", "method": "transactionInProgress", "bubbles": true, "cancelable": true, "composed": true }, { "name": "TXN_FAILED", "method": "transactionFailed", "bubbles": true, "cancelable": true, "composed": true }]; } static get style() { return "/**style-placeholder:aion-pay:**/"; } }