diff --git a/package-lock.json b/package-lock.json index ad172e37..86a93025 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "PowPeg", "version": "2.3.0", "dependencies": { + "@enkryptcom/types": "^0.0.5", "@leather.io/rpc": "^2.0.2", "@ledgerhq/devices": "6.27.1", "@ledgerhq/hw-app-btc": "6.27.1", @@ -2276,6 +2277,14 @@ "resolved": "https://registry.npmjs.org/@emurgo/cardano-serialization-lib-nodejs/-/cardano-serialization-lib-nodejs-11.5.0.tgz", "integrity": "sha512-IlVABlRgo9XaTR1NunwZpWcxnfEv04ba2l1vkUz4S1W7Jt36F4CtffP+jPeqBZGnAe+fnUwo0XjIJC3ZTNToNQ==" }, + "node_modules/@enkryptcom/types": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@enkryptcom/types/-/types-0.0.5.tgz", + "integrity": "sha512-GzSq2pu7LO1iuAcKFUORd+SyGm3907/ZHupAn/49sRbLXfJZ3HzTY65jeHG/EWPL4lyv2xyuhy7JEpo+x0tIUQ==", + "engines": { + "node": ">=14.15.0" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "dev": true, diff --git a/package.json b/package.json index 81380f67..e337f4c8 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "scanner": "npx sonar-scanner" }, "dependencies": { + "@enkryptcom/types": "^0.0.5", "@leather.io/rpc": "^2.0.2", "@ledgerhq/devices": "6.27.1", "@ledgerhq/hw-app-btc": "6.27.1", diff --git a/src/assets/exchange/enkrypt/connect_enkrypt.png b/src/assets/exchange/enkrypt/connect_enkrypt.png new file mode 100644 index 00000000..61a6a9b1 Binary files /dev/null and b/src/assets/exchange/enkrypt/connect_enkrypt.png differ diff --git a/src/assets/wallet-icons/enkrypt-black.svg b/src/assets/wallet-icons/enkrypt-black.svg new file mode 100644 index 00000000..e6f7afab --- /dev/null +++ b/src/assets/wallet-icons/enkrypt-black.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/wallet-icons/enkrypt-color.svg b/src/assets/wallet-icons/enkrypt-color.svg new file mode 100644 index 00000000..80d3e36d --- /dev/null +++ b/src/assets/wallet-icons/enkrypt-color.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/wallet-icons/enkrypt-white.svg b/src/assets/wallet-icons/enkrypt-white.svg new file mode 100644 index 00000000..29821e3f --- /dev/null +++ b/src/assets/wallet-icons/enkrypt-white.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/common/components/exchange/ConnectDevice.vue b/src/common/components/exchange/ConnectDevice.vue index 162d018f..ba5893ca 100644 --- a/src/common/components/exchange/ConnectDevice.vue +++ b/src/common/components/exchange/ConnectDevice.vue @@ -118,6 +118,10 @@ export default defineComponent({ // eslint-disable-next-line global-require, import/no-dynamic-require return require('@/assets/exchange/leather/connect_leather.png'); } + if (bitcoinWallet.value === constants.WALLET_NAMES.ENKRYPT.long_name) { + // eslint-disable-next-line global-require, import/no-dynamic-require + return require('@/assets/exchange/enkrypt/connect_enkrypt.png'); + } // eslint-disable-next-line global-require, import/no-dynamic-require return require('@/assets/exchange/wallet.png'); }); diff --git a/src/common/components/exchange/SelectBitcoinWallet.vue b/src/common/components/exchange/SelectBitcoinWallet.vue index 4a44919a..3ad4348e 100644 --- a/src/common/components/exchange/SelectBitcoinWallet.vue +++ b/src/common/components/exchange/SelectBitcoinWallet.vue @@ -89,6 +89,9 @@ export default { case constants.WALLET_NAMES.LEATHER.long_name: wallet = constants.WALLET_NAMES.LEATHER.short_name; break; + case constants.WALLET_NAMES.ENKRYPT.long_name: + wallet = constants.WALLET_NAMES.ENKRYPT.short_name; + break; default: wallet = ''; break; diff --git a/src/common/services/EnkryptService.ts b/src/common/services/EnkryptService.ts new file mode 100644 index 00000000..6f8d2083 --- /dev/null +++ b/src/common/services/EnkryptService.ts @@ -0,0 +1,91 @@ +/* eslint-disable class-methods-use-this, @typescript-eslint/no-explicit-any */ +import * as bitcoin from 'bitcoinjs-lib'; +import { + BtcAccount, + WalletAddress, + SignedTx, +} from '@/common/types'; +import { WalletService } from '@/common/services/index'; +import { EnkryptTx } from '@/pegin/middleware/TxBuilder/EnkryptTxBuilder'; +import * as constants from '@/common/store/constants'; +import ProviderError from '@enkryptcom/types'; + +export default class EnkryptService extends WalletService { + private btcProvider; + + constructor() { + super(); + if (this.network === 'test') window.enkrypt.providers.bitcoin.switchNetwork('testnet'); + this.btcProvider = window.enkrypt.providers.bitcoin; + } + + name(): Record<'formal_name' | 'short_name' | 'long_name', string> { + return constants.WALLET_NAMES.ENKRYPT; + } + + getAccountAddresses(): Promise { + return new Promise((resolve, reject) => { + this.btcProvider.getAccounts() + .then((addresses: string[]) => { + const walletAddresses = addresses + .map((address) => ({ address, derivationPath: '', publicKey: '' } as WalletAddress)); + resolve(walletAddresses); + }) + .catch((e: typeof ProviderError) => reject(e)); + }); + } + + availableAccounts(): BtcAccount[] { + return [constants.BITCOIN_NATIVE_SEGWIT_ADDRESS]; + } + + isConnected(): Promise { + return new Promise((resolve) => { + resolve(this.btcProvider.isConnected()); + }); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getXpub(accountType: BtcAccount, accountNumber: number): Promise { + return Promise.reject(new Error()); + } + + /** Needed to load account balance */ + executed = false; + + areEnoughUnusedAddresses(): boolean { + if (!this.executed) { + this.executed = true; + return !this.executed; + } + return this.executed; + } + + sign(tx: EnkryptTx): Promise { + return new Promise((resolve, reject) => { + this.btcProvider?.signPsbt(tx.hex, { autoFinalized: false }) + .then((hex: string) => { + const signedPsbt = bitcoin.Psbt.fromHex(hex) + .finalizeAllInputs() + .extractTransaction() + .toHex(); + resolve({ signedTx: signedPsbt }); + }) + .catch((e: typeof ProviderError) => { + reject(e); + }); + }); + } + + async reconnect(): Promise { + return new Promise((resolve, reject) => { + try { + if (this.network === 'test') window.enkrypt.providers.bitcoin.switchNetwork('testnet'); + this.btcProvider = window.enkrypt.providers.bitcoin; + resolve(); + } catch (e: any) { + reject(e); + } + }); + } +} diff --git a/src/common/services/LeatherService.ts b/src/common/services/LeatherService.ts index 21979dcb..ff5ed834 100644 --- a/src/common/services/LeatherService.ts +++ b/src/common/services/LeatherService.ts @@ -2,7 +2,6 @@ import * as bitcoin from 'bitcoinjs-lib'; import { BtcAccount, WalletAddress, - Step, SignedTx, } from '@/common/types'; import { WalletService } from '@/common/services/index'; @@ -40,32 +39,6 @@ export default class LeatherService extends WalletService { ]; } - // eslint-disable-next-line class-methods-use-this - confirmationSteps(): Step[] { - return [ - { - title: 'Transaction information', - subtitle: '', - outputsToshow: { - opReturn: { - value: false, - amount: true, - }, - change: { - address: false, - amount: true, - }, - federation: { - address: true, - amount: true, - }, - }, - fee: true, - fullAmount: false, - }, - ]; - } - // eslint-disable-next-line class-methods-use-this isConnected(): Promise { return new Promise((resolve) => { diff --git a/src/common/services/LedgerService.ts b/src/common/services/LedgerService.ts index 24b0ba8f..0c1f5bac 100644 --- a/src/common/services/LedgerService.ts +++ b/src/common/services/LedgerService.ts @@ -4,7 +4,7 @@ import * as bitcoin from 'bitcoinjs-lib'; import * as constants from '@/common/store/constants'; import { BtcAccount, WalletAddress } from '@/common/types/pegInTx'; import { - LedgerjsTransaction, LedgerSignedTx, LedgerTx, Step, Tx, + LedgerjsTransaction, LedgerSignedTx, LedgerTx, Tx, } from '@/common/types'; import { EnvironmentAccessorService } from '@/common/services/enviroment-accessor.service'; import { WalletService } from '@/common/services/index'; @@ -33,92 +33,6 @@ export default class LedgerService extends WalletService { return constants.WALLET_NAMES.LEDGER; } - // eslint-disable-next-line class-methods-use-this - confirmationSteps(): Step[] { - return [ - { - title: 'Confirm transaction', - subtitle: 'Please check your Ledger device', - outputsToshow: { - opReturn: { - value: true, - amount: true, - }, - change: { - address: false, - amount: false, - }, - federation: { - address: false, - amount: false, - }, - }, - fee: false, - fullAmount: false, - }, - { - title: 'Confirm funds transfer', - subtitle: 'Confirm sending', - outputsToshow: { - opReturn: { - value: false, - amount: false, - }, - change: { - address: false, - amount: false, - }, - federation: { - address: true, - amount: true, - }, - }, - fee: false, - fullAmount: false, - }, - { - title: 'Confirm change address', - subtitle: 'Confirm sending', - outputsToshow: { - opReturn: { - value: false, - amount: false, - }, - change: { - address: true, - amount: true, - }, - federation: { - address: false, - amount: false, - }, - }, - fee: false, - fullAmount: false, - }, - { - title: 'Confirm transaction fee', - subtitle: 'Confirm transaction', - outputsToshow: { - opReturn: { - value: false, - amount: false, - }, - change: { - address: false, - amount: false, - }, - federation: { - address: false, - amount: false, - }, - }, - fee: true, - fullAmount: false, - }, - ]; - } - // eslint-disable-next-line class-methods-use-this public reconnect(): Promise { return new Promise((resolve, reject) => { diff --git a/src/common/services/TrezorService.ts b/src/common/services/TrezorService.ts index 0f7d6d4f..d711aa1d 100644 --- a/src/common/services/TrezorService.ts +++ b/src/common/services/TrezorService.ts @@ -6,7 +6,6 @@ import * as constants from '@/common/store/constants'; import { GetAddress, SignedTx, - Step, TrezorTx, Tx, } from '@/common/types'; import { WalletService } from '@/common/services/index'; @@ -64,92 +63,6 @@ export default class TrezorService extends WalletService { ]; } - // eslint-disable-next-line class-methods-use-this - confirmationSteps(): Step[] { - return [ - { - title: 'Confirm transaction', - subtitle: 'Please check your Trezor device', - outputsToshow: { - opReturn: { - value: true, - amount: true, - }, - change: { - address: false, - amount: false, - }, - federation: { - address: false, - amount: false, - }, - }, - fee: false, - fullAmount: false, - }, - { - title: 'Confirm funds transfer', - subtitle: 'Confirm sending', - outputsToshow: { - opReturn: { - value: false, - amount: false, - }, - change: { - address: false, - amount: false, - }, - federation: { - address: true, - amount: true, - }, - }, - fee: false, - fullAmount: false, - }, - { - title: 'Confirm change address', - subtitle: 'Confirm sending', - outputsToshow: { - opReturn: { - value: false, - amount: false, - }, - change: { - address: true, - amount: true, - }, - federation: { - address: false, - amount: false, - }, - }, - fee: false, - fullAmount: false, - }, - { - title: 'Confirm transaction fee', - subtitle: 'Really send', - outputsToshow: { - opReturn: { - value: false, - amount: false, - }, - change: { - address: false, - amount: false, - }, - federation: { - address: false, - amount: false, - }, - }, - fee: true, - fullAmount: true, - }, - ]; - } - // eslint-disable-next-line class-methods-use-this isConnected(): Promise { return new Promise((resolve) => { diff --git a/src/common/services/WalletService.ts b/src/common/services/WalletService.ts index fc0bced3..39169541 100644 --- a/src/common/services/WalletService.ts +++ b/src/common/services/WalletService.ts @@ -1,7 +1,7 @@ import * as constants from '@/common/store/constants'; import SatoshiBig from '@/common/types/SatoshiBig'; import { - Purpose, SignedTx, WalletCount, Step, + Purpose, SignedTx, WalletCount, } from '@/common/types/Wallets'; import { AccountBalance, AddressStatus, AppNetwork, BtcAccount, Tx, UtxoListPerAccount, WalletAddress, @@ -91,8 +91,6 @@ export default abstract class WalletService { abstract availableAccounts(): Array; - abstract confirmationSteps(): Array; - get isLoadingBalances(): boolean { return this.loadingBalances; } diff --git a/src/common/services/index.ts b/src/common/services/index.ts index 17c10254..89feb54a 100644 --- a/src/common/services/index.ts +++ b/src/common/services/index.ts @@ -4,3 +4,4 @@ export { default as TrezorService } from './TrezorService'; export { default as LedgerService } from './LedgerService'; export { default as LeatherService } from './LeatherService'; export { default as FlyoverService } from './FlyoverService'; +export { default as EnkryptService } from './EnkryptService'; diff --git a/src/common/store/constants.ts b/src/common/store/constants.ts index a7796c31..1f87ad8e 100644 --- a/src/common/store/constants.ts +++ b/src/common/store/constants.ts @@ -3,6 +3,7 @@ export const WALLET_NAMES = { TREZOR: { formal_name: 'Trezor', short_name: 'trezor', long_name: 'WALLET_TREZOR' }, METAMASK: { formal_name: 'Metamask', short_name: 'metamask', long_name: 'WALLET_METAMASK' }, LEATHER: { formal_name: 'Leather', short_name: 'leather', long_name: 'WALLET_LEATHER' }, + ENKRYPT: { formal_name: 'Enkrypt', short_name: 'enkrypt', long_name: 'WALLET_ENKRYPT' }, } as const; export const OPERATION_TYPE = 'OPERATION_TYPE'; diff --git a/src/common/types/pegInTx.ts b/src/common/types/pegInTx.ts index 601ecc39..2b1d3a13 100644 --- a/src/common/types/pegInTx.ts +++ b/src/common/types/pegInTx.ts @@ -7,7 +7,7 @@ export type BtcAccount = 'BITCOIN_LEGACY_ADDRESS' | 'BITCOIN_SEGWIT_ADDRESS' | 'BITCOIN_NATIVE_SEGWIT_ADDRESS'; -export type BtcWallet = 'WALLET_LEDGER' | 'WALLET_TREZOR' | 'WALLET_LEATHER'; +export type BtcWallet = 'WALLET_LEDGER' | 'WALLET_TREZOR' | 'WALLET_LEATHER' | 'WALLET_ENKRYPT'; export type MiningSpeedFee = 'BITCOIN_SLOW_FEE_LEVEL' | 'BITCOIN_AVERAGE_FEE_LEVEL' | diff --git a/src/common/walletConf.json b/src/common/walletConf.json index 0f2d66c0..f2111f7f 100644 --- a/src/common/walletConf.json +++ b/src/common/walletConf.json @@ -35,6 +35,18 @@ "btnClass": "btn-leather", "kind": "Software Wallet", "installation": "https://leather.io/install-extension" + }, + { + "name": "Enkrypt", + "icon": "wallet-icons/enkrypt-black.svg", + "iconWhite": "wallet-icons/enkrypt-white.svg", + "constant": "WALLET_ENKRYPT", + "pegin": true, + "pegout": false, + "hover": false, + "btnClass": "btn-enkrypt", + "kind": "Software Wallet", + "installation": "https://www.enkrypt.com/" } ] } diff --git a/src/pegin/components/create/PegInAccountSelect.vue b/src/pegin/components/create/PegInAccountSelect.vue index f4fa9726..5ee9a1ea 100644 --- a/src/pegin/components/create/PegInAccountSelect.vue +++ b/src/pegin/components/create/PegInAccountSelect.vue @@ -126,6 +126,7 @@ export default defineComponent({ const onlyNativeSegwit = computed(() => { const wallets = [ constants.WALLET_NAMES.LEATHER.long_name, + constants.WALLET_NAMES.ENKRYPT.long_name, ]; return wallets.some((wallet) => wallet === bitcoinWallet.value); }); diff --git a/src/pegin/components/create/SendBitcoin.vue b/src/pegin/components/create/SendBitcoin.vue index 2a41262f..089a477f 100644 --- a/src/pegin/components/create/SendBitcoin.vue +++ b/src/pegin/components/create/SendBitcoin.vue @@ -50,6 +50,7 @@ import ConnectDevice from '@/common/components/exchange/ConnectDevice.vue'; import TxErrorDialog from '@/common/components/exchange/TxErrorDialog.vue'; import { TrezorError } from '@/common/types/exception/TrezorError'; import LeatherTxBuilder from '@/pegin/middleware/TxBuilder/LeatherTxBuilder'; +import EnkryptTxBuilder from '@/pegin/middleware/TxBuilder/EnkryptTxBuilder'; import PeginTxService from '@/pegin/services/PeginTxService'; export default defineComponent({ @@ -193,6 +194,10 @@ export default defineComponent({ txBuilder.value = new LeatherTxBuilder(); currentWallet.value = constants.WALLET_NAMES.LEATHER.short_name; break; + case constants.WALLET_NAMES.ENKRYPT.long_name: + txBuilder.value = new EnkryptTxBuilder(); + currentWallet.value = constants.WALLET_NAMES.ENKRYPT.short_name; + break; default: txBuilder.value = new TrezorTxBuilder(); break; diff --git a/src/pegin/middleware/TxBuilder/EnkryptTxBuilder.ts b/src/pegin/middleware/TxBuilder/EnkryptTxBuilder.ts new file mode 100644 index 00000000..32c7fb62 --- /dev/null +++ b/src/pegin/middleware/TxBuilder/EnkryptTxBuilder.ts @@ -0,0 +1,80 @@ +/* eslint-disable class-methods-use-this */ +import { ApiService } from '@/common/services'; +import { + Tx, + NormalizedTx, + NormalizedInput, + PsbtExtendedInput, +} from '@/common/types'; +import * as bitcoin from 'bitcoinjs-lib'; +import TxBuilder from './TxBuilder'; + +export interface EnkryptTx extends Tx { + hex: string; +} + +export default class EnkryptTxBuilder extends TxBuilder { + buildTx(normalizedTx: NormalizedTx): Promise { + return new Promise((resolve, reject) => { + const psbt = new bitcoin.Psbt({ network: this.network }); + EnkryptTxBuilder.getExtendedInputs(normalizedTx.inputs) + .then((extendedInputs) => { + psbt.addInputs(extendedInputs); + normalizedTx.outputs.forEach((normalizedOutput) => { + if (normalizedOutput.op_return_data) { + const buffer = Buffer.from(normalizedOutput.op_return_data, 'hex'); + const script: bitcoin.Payment = bitcoin.payments.embed({ data: [buffer] }); + if (script.output) { + psbt.addOutput({ + script: script.output, + value: 0, + }); + } + } else if (normalizedOutput.address) { + psbt.addOutput({ + address: normalizedOutput.address, + value: Number(normalizedOutput.amount), + }); + } + }); + const inputs = normalizedTx.inputs + .map((input) => ({ + address: input.address, + idx: input.prev_index, + })); + resolve({ + coin: this.coin, + inputs, + outputs: normalizedTx.outputs, + hex: psbt.toHex(), + }); + }) + .catch(reject); + }); + } + + private static getExtendedInputs(normalizedInputs: Array) + :Promise> { + return new Promise>((resolve, reject) => { + const psbtExtendedInputs: Array = []; + const hexUtxoPromises = normalizedInputs + .map((input) => ApiService.getTxHex(input.prev_hash)); + Promise.all(hexUtxoPromises) + .then((hexUtxos) => { + normalizedInputs.forEach((normalizedInput, idx) => { + const utxo = bitcoin.Transaction.fromHex(hexUtxos[idx]); + psbtExtendedInputs.push({ + hash: normalizedInput.prev_hash, + index: normalizedInput.prev_index, + witnessUtxo: { + value: utxo.outs[normalizedInput.prev_index].value, + script: utxo.outs[normalizedInput.prev_index].script, + }, + }); + }); + resolve(psbtExtendedInputs); + }) + .catch(reject); + }); + } +} diff --git a/src/pegin/store/PeginTx/actions.ts b/src/pegin/store/PeginTx/actions.ts index be1efdc8..ce7e1512 100644 --- a/src/pegin/store/PeginTx/actions.ts +++ b/src/pegin/store/PeginTx/actions.ts @@ -5,6 +5,7 @@ import * as constants from '@/common/store/constants'; import { ApiService, LedgerService, TrezorService, LeatherService, + EnkryptService, } from '@/common/services'; import SatoshiBig from '@/common/types/SatoshiBig'; import { EnvironmentAccessorService } from '@/common/services/enviroment-accessor.service'; @@ -45,6 +46,9 @@ export const actions: ActionTree = { case constants.WALLET_NAMES.LEATHER.long_name: commit(constants.PEGIN_TX_SET_WALLET_SERVICE, new LeatherService()); break; + case constants.WALLET_NAMES.ENKRYPT.long_name: + commit(constants.PEGIN_TX_SET_WALLET_SERVICE, new EnkryptService()); + break; default: commit(constants.PEGIN_TX_SET_WALLET_SERVICE, undefined); break; diff --git a/src/pegin/store/PeginTx/getters.ts b/src/pegin/store/PeginTx/getters.ts index e2dd8b72..3e1f4a89 100644 --- a/src/pegin/store/PeginTx/getters.ts +++ b/src/pegin/store/PeginTx/getters.ts @@ -17,6 +17,9 @@ export const getters: GetterTree = { case constants.WALLET_NAMES.LEATHER.long_name: { return constants.WALLET_NAMES.LEATHER.formal_name; } + case constants.WALLET_NAMES.ENKRYPT.long_name: { + return constants.WALLET_NAMES.ENKRYPT.formal_name; + } default: { return 'wallet'; } @@ -185,6 +188,9 @@ export const getters: GetterTree = { case constants.WALLET_NAMES.LEATHER.long_name: isSfWallet = true; break; + case constants.WALLET_NAMES.ENKRYPT.long_name: + isSfWallet = true; + break; default: isSfWallet = false; break; diff --git a/src/pegout/components/FlyoverPegout.vue b/src/pegout/components/FlyoverPegout.vue index 501c0d95..25a52c1d 100644 --- a/src/pegout/components/FlyoverPegout.vue +++ b/src/pegout/components/FlyoverPegout.vue @@ -307,7 +307,6 @@ export default defineComponent({ })); async function send() { - console.log('Sending pegout'); const quoteHash = selectedOption.value; const type = quoteHash ? TxStatusType.FLYOVER_PEGOUT.toLowerCase() diff --git a/src/shims-tsx.d.ts b/src/shims-tsx.d.ts index 6a9387be..90646609 100644 --- a/src/shims-tsx.d.ts +++ b/src/shims-tsx.d.ts @@ -1,5 +1,6 @@ import Vue, { VNode } from 'vue'; import { LeatherProvider } from '@leather.io/rpc'; +import EnkryptWindow from '@enkryptcom/types'; declare global { namespace JSX { @@ -21,6 +22,7 @@ declare global { interface Window { ethereum: Ethereum, LeatherProvider?: LeatherProvider, + enkrypt?: EnkryptWindow, grecaptcha: { ready: (cb: () => void) => void, execute: () => Promise, @@ -28,4 +30,7 @@ declare global { }, onRecaptchaSuccess: () => Promise, } + interface EnkryptProvider { + getAccounts(): Promise; + } } diff --git a/tests/unit/SatoshiBig.spec.ts b/tests/unit/SatoshiBig.spec.ts index bb18a811..71d8b8e0 100644 --- a/tests/unit/SatoshiBig.spec.ts +++ b/tests/unit/SatoshiBig.spec.ts @@ -58,7 +58,7 @@ describe('SatoshiBig', () => { expect(sb1.toBTCString()).toEqual('0.00000000'); expect(sb1.toBTCStringNotZeroPadded()).toEqual('0'); }); - + it('should return an instance of SatoshiBig from a WeiBig instance rounded up', () => { const weiToTest = new WeiBig('5301364444000000', 'wei'); const weiToTest2 = new WeiBig('8101341211956000', 'wei'); diff --git a/tests/unit/common/services/EnkryptService.spec.ts b/tests/unit/common/services/EnkryptService.spec.ts new file mode 100644 index 00000000..c59b239d --- /dev/null +++ b/tests/unit/common/services/EnkryptService.spec.ts @@ -0,0 +1,92 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import sinon from 'sinon'; +import * as constants from '@/common/store/constants'; +import { EnvironmentAccessorService } from '@/common/services/enviroment-accessor.service'; +import { EnkryptService, WalletService } from '@/common/services'; + +function setEnvironment() { + const defaultEnvironmentVariables = { + vueAppCoin: constants.BTC_NETWORK_TESTNET, + vueAppRskNodeHost: '', + vueAppApiBaseUrl: 'https://2wp-api.testnet.rsk.co', + }; + EnvironmentAccessorService.initializeEnvironmentVariables(defaultEnvironmentVariables); +} +describe('Enkrypt Service: ', () => { + let mockBitcoinProvider: any; + let enkryptService: EnkryptService; + + beforeEach(() => { + setEnvironment(); + mockBitcoinProvider = { + switchNetwork: jest.fn(), + getAccounts: jest.fn().mockResolvedValue(['testAddress']), + isConnected: jest.fn().mockResolvedValue(true), + signPsbt: jest.fn().mockResolvedValue('70736274ff0100960200000001617bfa33c63e4d6c1dff9f90b116371b298a8f08084802a85bb5db956e8c96a60000000000ffffffff0300000000000000001b6a1952534b5401cd3fb9fdd6035e3da5a997efe5b3d895cbc39ed120a107000000000017a9143b004aa2b568c97f80ccccc5130226d0e98bd588872246010000000000160014dfc8d4318aea9a5a945be8e2eba3cafb94fe2569000000000001011fc027090000000000160014dfc8d4318aea9a5a945be8e2eba3cafb94fe2569220203ad6e44f7982ddcd5626539da854a1337b85be787248d42f9d7a638991464494d47304402205d735b7c295fd0d200c2824b504dc71dd54a45f9e2c43a782b3f67e8b759f48402207937eabfd56d9d177e3dd187ebd3e219eb7c44758f9a05cddd46d35d01e712be0100000000'), + }; + (window as any).enkrypt = { + providers: { + bitcoin: mockBitcoinProvider, + }, + }; + enkryptService = new EnkryptService(); + }); + afterEach(() => { + jest.restoreAllMocks(); + sinon.restore(); + }); + it('should create a EnkryptService instance', () => { + expect(enkryptService).toBeInstanceOf(WalletService); + expect(enkryptService).toBeInstanceOf(EnkryptService); + expect(mockBitcoinProvider.switchNetwork).toHaveBeenCalledWith('testnet'); + }); + it('should return the wallet name', () => { + expect(enkryptService.name()).toEqual(constants.WALLET_NAMES.ENKRYPT); + }); + it('should return native segwit as available accounts', () => { + expect(enkryptService.availableAccounts()[0]).toEqual(constants.BITCOIN_NATIVE_SEGWIT_ADDRESS); + }); + it('should return a single wallet address', () => { + enkryptService.getAccountAddresses().then((addresses) => { + expect(addresses.length).toBe(1); + expect(addresses[0].address).toBe('testAddress'); + }); + }); + it('should return native segwit as available accounts', () => { + expect(enkryptService.availableAccounts()[0]).toEqual(constants.BITCOIN_NATIVE_SEGWIT_ADDRESS); + }); + it('should return true if enkrypt is connected', () => { + enkryptService.isConnected().then((isConnected) => { + expect(isConnected).toBe(true); + }); + }); + it('should return a sign psbt', () => { + const tx = { + coin: "test", + inputs: [ + { + "address": "tb1qmlydgvv2a2d949zmar3whg72lw20uftftg4vr8", + "idx": 0 + } + ], + outputs: [ + { + amount: "0", + op_return_data: "52534b5401cd3Fb9fdd6035E3dA5A997EfE5b3D895CbC39ed1" + }, + { + address: "2MxdCCrmUaEG1Tk8dshdcTGKiA9LewNDVCb", + amount: "500000" + }, + { + address: "tb1qmlydgvv2a2d949zmar3whg72lw20uftftg4vr8", + amount: "83490" + } + ], + hex: "70736274ff0100960200000001617bfa33c63e4d6c1dff9f90b116371b298a8f08084802a85bb5db956e8c96a60000000000ffffffff0300000000000000001b6a1952534b5401cd3fb9fdd6035e3da5a997efe5b3d895cbc39ed120a107000000000017a9143b004aa2b568c97f80ccccc5130226d0e98bd588872246010000000000160014dfc8d4318aea9a5a945be8e2eba3cafb94fe2569000000000001011fc027090000000000160014dfc8d4318aea9a5a945be8e2eba3cafb94fe256900000000" + }; + enkryptService.sign(tx).then((res) => { + expect(res.signedTx).toEqual('02000000000101617bfa33c63e4d6c1dff9f90b116371b298a8f08084802a85bb5db956e8c96a60000000000ffffffff0300000000000000001b6a1952534b5401cd3fb9fdd6035e3da5a997efe5b3d895cbc39ed120a107000000000017a9143b004aa2b568c97f80ccccc5130226d0e98bd588872246010000000000160014dfc8d4318aea9a5a945be8e2eba3cafb94fe25690247304402205d735b7c295fd0d200c2824b504dc71dd54a45f9e2c43a782b3f67e8b759f48402207937eabfd56d9d177e3dd187ebd3e219eb7c44758f9a05cddd46d35d01e712be012103ad6e44f7982ddcd5626539da854a1337b85be787248d42f9d7a638991464494d00000000'); + }); + }); +}); diff --git a/tests/unit/pegin/services/EnkryptTxBuilder.spec.ts b/tests/unit/pegin/services/EnkryptTxBuilder.spec.ts new file mode 100644 index 00000000..94dc1b01 --- /dev/null +++ b/tests/unit/pegin/services/EnkryptTxBuilder.spec.ts @@ -0,0 +1,69 @@ +import { NormalizedTx } from '@/common/types'; +import EnkryptTxBuilder from '@/pegin/middleware/TxBuilder/EnkryptTxBuilder'; +import * as constants from '@/common/store/constants'; +import ApiService from '@/common/services/ApiService'; +import { EnvironmentAccessorService } from '@/common/services/enviroment-accessor.service'; +import sinon from 'sinon'; + +describe('EnkryptTxBuilder', () => { + let enkryptTxBuilder: EnkryptTxBuilder; + + function setEnvironment() { + const defaultEnvironmentVariables = { + vueAppCoin: constants.BTC_NETWORK_TESTNET, + vueAppRskNodeHost: '', + vueAppApiBaseUrl: 'https://2wp-api.testnet.rsk.co', + }; + EnvironmentAccessorService.initializeEnvironmentVariables(defaultEnvironmentVariables); + } + + beforeEach(() => { + setEnvironment(); + enkryptTxBuilder = new EnkryptTxBuilder(); + sinon.stub(ApiService, 'getTxHex') + .resolves('020000000001010366d8862273dc1afe60c26d3449ae248a7e15a50f97242d57fd9dcaeca5d96400000000000000000002c027090000000000160014dfc8d4318aea9a5a945be8e2eba3cafb94fe256975910500000000001600149b6d476d887db413ed0a59fbb1ea80ed41641e70024730440220535da8f53c535ed68b5a30b2adc87e424467351f2e867ae0ee4472668ef3a20a02202d1713a3bbed41e26b9fd19f08cdf665fdeba00b4295f858daa5ab69f1457ae401210296b60d2b92e4ba3f1948e00412d5fdc4ec0586830660c806ffe2214daa25fce900000000'); + }); + + afterEach(() => { + sinon.restore(); + }); + + it('builds a transaction', async () => { + const inputs = [ + { + address: 'tb1qmlydgvv2a2d949zmar3whg72lw20uftftg4vr8', + amount: '600000', + prev_hash: 'd4e1c03847c3c732893453704f94659501a2e7e37389d2d72cc562d00ca0eff1', + prev_index: 0, + }, + ]; + const outputs = [ + { + amount: '0', + op_return_data: '52534b5401cd3Fb9fdd6035E3dA5A997EfE5b3D895CbC39ed1', + }, + { + address: '2MxdCCrmUaEG1Tk8dshdcTGKiA9LewNDVCb', + amount: '500000', + }, + { + address: 'tb1qmlydgvv2a2d949zmar3whg72lw20uftftg4vr8', + amount: '74346', + }, + ]; + const normalizedTx: NormalizedTx = { + inputs, + outputs, + coin: constants.BTC_NETWORK_TESTNET, + }; + expect(normalizedTx.coin).toBe(constants.BTC_NETWORK_TESTNET); + expect(normalizedTx.inputs).toBe(constants.BTC_NETWORK_TESTNET); + expect(normalizedTx.outputs).toBe(constants.BTC_NETWORK_TESTNET); + + const tx = await enkryptTxBuilder.buildTx(normalizedTx); + + expect(tx.coin).toBe(constants.BTC_NETWORK_TESTNET); + expect(tx.inputs.length).toEqual(inputs.length); + expect(tx.outputs.length).toEqual(outputs.length); + }); +});