Commit 3a1ca9cd authored by Allan Juma's avatar Allan Juma

0.7.8

parent 4c8579a4
......@@ -17474,6 +17474,16 @@
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
},
"python": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/python/-/python-0.0.4.tgz",
"integrity": "sha1-MJTomO8Xozqpw+lzs4SKOOR9GBg="
},
"python-shell": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/python-shell/-/python-shell-3.0.1.tgz",
"integrity": "sha512-TWeotuxe1auhXa5bGRScxnc2J+0r41NBntSa6RYZtMBLtAEsvCboKrEbW6DvASosWQepVkhZZlT3B5Ei766G+Q=="
},
"q": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
......@@ -20646,6 +20656,14 @@
"dev": true,
"optional": true
},
"tinyman-ts-sdk": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/tinyman-ts-sdk/-/tinyman-ts-sdk-1.0.1.tgz",
"integrity": "sha512-uaZ5wEptwpPAqNeqOOrI6JmW6BVGi3kaZUR9T5s3ouM6snzzF9LLW8T+lkW4BXXjiy6r1xEAIQO6bQhz/ypUEQ==",
"requires": {
"algosdk": "^1.12.0"
}
},
"tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
......@@ -20777,6 +20795,11 @@
"resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.1.2.tgz",
"integrity": "sha512-f5Knjh7XCyRIzoC/z1Su1yLLRrPrFCgtUAh/9fCSP6NKbATwpOL1+idQVXQokK9GRFURn/jYPGPfegIctwunoA=="
},
"tsc": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/tsc/-/tsc-2.0.4.tgz",
"integrity": "sha512-fzoSieZI5KKJVBYGvwbVZs/J5za84f2lSTLPYf6AGiIf43tZ3GNrI1QzTLcjtyDDP4aLxd46RTZq1nQxe7+k5Q=="
},
"tslib": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
......@@ -20855,6 +20878,12 @@
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
},
"typescript": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz",
"integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==",
"dev": true
},
"ua-parser-js": {
"version": "0.7.20",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.20.tgz",
......
......@@ -47,6 +47,8 @@
"mobx": "^4.9.4",
"mobx-react": "^5.4.3",
"npm": "^8.1.0",
"python": "0.0.4",
"python-shell": "^3.0.1",
"qr-image": "^3.2.0",
"react": "^16.10.0",
"react-art": "^16.8.6",
......@@ -54,7 +56,9 @@
"react-dom": "17.0.2",
"react-native-web": "^0.11.2",
"react-scripts": "4.0.3",
"svgs": "4.0.0"
"svgs": "4.0.0",
"tinyman-ts-sdk": "^1.0.1",
"tsc": "^2.0.4"
},
"devDependencies": {
"@babel/cli": "^7.2.3",
......@@ -90,7 +94,7 @@
"protobufjs": "^6.8.8",
"react-scripts": "^3.0.0",
"sinon": "^6.0.0",
"typescript": "4.4.2",
"typescript": "^4.4.2",
"unexpected": "^10.37.2",
"unexpected-sinon": "^10.10.1",
"wait-on": "^2.1.0",
......
const { app, BrowserWindow, ipcMain, dialog, Menu } = require('electron');
const { contextBridge, ipcRenderer, app, BrowserWindow, ipcMain, dialog, Menu } = require('electron');
const { autoUpdater } = require('electron-updater');
const os = require('os');
const path = require('path');
......@@ -6,6 +6,7 @@ const url = require('url');
const isDev = require('electron-is-dev');
const log = require('electron-log');
const { startLndProcess, startBtcdProcess } = require('./lnd-child-process');
const { initPythonProcess } = require('./python-child-process');
const grcpClient = require('./grpc-client');
const {
PREFIX_NAME,
......@@ -47,6 +48,7 @@ const lndArgs = process.argv.filter(a =>
let win;
let lndProcess;
let btcdProcess;
let pythonProcess;
log.transports.console.level = 'info';
log.transports.file.level = 'info';
......@@ -99,7 +101,7 @@ console.log(path.join(__dirname, 'preload.js'));
nodeIntegration: true, // is default value after Electron v5
contextIsolation: true, // protect against prototype pollution
enableRemoteModule: false, // turn off remote
allowRunningInsecureContent: true,
//allowRunningInsecureContent: true,
sandbox: false,
preload: path.join(__dirname, 'preload.js'),
},
......@@ -163,9 +165,50 @@ ipcMain.on('lnd-restart-process', async event => {
event.sender.send('lnd-restart-error', { restartError });
});
const startLnd = async () => {
ipcMain.on('pyFace', async function(event, args) {
console.log(args.body);
var ranID = args.body[0];
//args.body = args.body.shift(0);
args.body = args.body.filter(function(v){
console.log(typeof(v));
if(typeof(v) != 'string') return true;
return !(v.includes('pyFace:'));
});
console.log(args.body, ranID);
try {
/*
res = await initPythonProcess({args: args.body,logger: Logger,});
} catch (err) {
console.log(err);
res = err
}
/*
pythonProcess = "{'address': 'TTNTINGI6AKN6JBHCAOXVFZN72BAMDNBTHZ3ZRQC3ILAMWONKN7XRRQ6GM', 'amount': 0, 'amount-without-pending-rewards': 0, 'apps-local-state': [], 'apps-total-schema': {'num-byte-slice': 0, 'num-uint': 0}, 'assets': [], 'created-apps': [], 'created-assets': [], 'pending-rewards': 0, 'reward-base': 27521, 'rewards': 0, 'round': 19891151, 'status': 'Offline'}";
*/
var res = JSON.stringify(res);
event.sender.send(ranID, {response:res});
});
const startLnd = async () => {
try {
/*
pythonProcess = await initPythonProcess({args: ['/data/lightning-bits/src/chains/algo/pools.py',
'',
'',
'',
'',
'',
'TTNTINGI6AKN6JBHCAOXVFZN72BAMDNBTHZ3ZRQC3ILAMWONKN7XRRQ6GM',
],logger: Logger,});
btcdProcess = await startBtcdProcess({
isDev,
logger: Logger,
......@@ -237,6 +280,7 @@ app.on('activate', () => {
app.on('quit', () => {
lndProcess && lndProcess.kill('SIGINT');
btcdProcess && btcdProcess.kill('SIGINT');
pythonProcess && pythonProcess.kill('SIGINT');
});
// Prevent multiple instances of the application.
......
......@@ -51,6 +51,7 @@ function startBlockingProcess(name, args, logger) {
});
}
module.exports.startLndProcess = async function({
isDev,
lndSettingsDir,
......@@ -86,7 +87,7 @@ module.exports.startLndProcess = async function({
];
// set development or production settings
if (!isDev) {
if (isDev) {
args = args.concat([
'--bitcoin.node=neutrino',
......@@ -109,7 +110,7 @@ module.exports.startLndProcess = async function({
// set default production settings if no custom flags
//if (!isDev && !lndArgs.length) {
if (isDev) {
if (!isDev) {
args = args.concat([
'--bitcoin.node=bitcoind',
'--bitcoind.rpchost=btc-p2p.bitsoko.org',
......@@ -160,3 +161,4 @@ module.exports.mineBlocks = async function({ blocks, logger }) {
};
......@@ -6,7 +6,7 @@ var { contextBridge, ipcRenderer } = require("electron");
const filter = event => {
if (
!/^(lnd)|(unlock)|(log)|(locale)|(open-url)[a-zA-Z_-]{0,20}$/.test(event)
!/^(lnd)|(unlock)|(pyFace)|(log)|(locale)|(open-url)[a-zA-Z_-]{0,20}$/.test(event)
) {
throw new Error(`Invalid IPC: ${event}`);
}
......
const os = require('os');
const fs = require('fs');
const path = require('path');
const cp = require('child_process');
var cmdQueue = new Array();
function handleStdout(data) {
var datastr = data.toString('utf8');
var finished = false;
if (datastr.match(/Command Start\n/)) {
datastr = datastr.replace(/Command Start\n/,'');
}
if (datastr.match(/Command End\n/)) {
datastr = datastr.replace(/Command End\n/,'');
finished = true;
}
if (cmdQueue.length > 0) {
cmdQueue[0].data+=datastr;
}
if (finished) {
cmd = cmdQueue.shift();
if (cmd && cmd.command) {
if (undefined != typeof cmd.callback) {
cmd.callback(null, cmd.data);
processQueue();
}
}
}
return datastr;
};
function handleStderr(data) {
processQueue();
};
function processQueue() {
if (cmdQueue.length > 0 && cmdQueue[0].state === 'pending') {
cmdQueue[0].state = 'processing';
child.stdin.write(cmdQueue[0].command, encoding='utf8');
}
};
function handleExit(code) {
console.log('child process exited with code ' + code);
//process.exit();
};
function getProcessName(binName) {
const filename = os.platform() === 'win32' ? `${binName}.exe` : binName;
const filePath = __dirname.includes('asar')
? path.join(__dirname, '..', '..', 'assets', 'bin', os.platform(), filename)
: path.join(__dirname, '..', 'assets', 'bin', os.platform(), filename);
console.log(fs.existsSync(filePath),path.join(__dirname, '..', 'assets', 'bin', os.platform(), filename));
return fs.existsSync(filePath) ? filePath : filename;
}
async function startChildProcess(name, args, logger) {
return new Promise((resolve, reject) => {
const processName = getProcessName(name);
logger.info(`Using ${name} in path ${processName}`);
const childProcess = cp.spawn(processName, args);
childProcess.stdout.on('data', data => {
logger.info(`${name}: ${data}`);
//resolve(childProcess);
resolve(data);
});
childProcess.stderr.on('data', data => {
logger.error(`${name} Error: ${data}`);
reject(new Error(data));
});
childProcess.on('error', reject);
});
}
function startBlockingProcess(name, args, logger) {
return new Promise((resolve, reject) => {
const processName = getProcessName(name);
logger.info(`Using ${name} in path ${processName}`);
const childProcess = cp.spawn(processName, args);
childProcess.stdout.on('data', data => {
logger.info(`${name}: ${data}`);
});
childProcess.stderr.on('data', data => {
logger.error(`${name} Error: ${data}`);
reject(new Error(data));
});
childProcess.on('exit', resolve);
childProcess.on('error', reject);
});
}
module.exports.initPythonProcess = async function({
args,logger
}) {
console.log('RUNNING Python');
//logger.error(`started python`);
const processName = 'python3';
data = await startChildProcess(processName, args, logger);
return handleStdout(data);
//child.stdout.on('data', handleStdout);
//child.stderr.on('data', handleStderr);
//child.on('exit', handleExit);
};
......@@ -17,6 +17,32 @@ class GrpcAction {
// WalletUnlocker grpc client
//
/**
* The first GRPC api that is called to initialize the wallet unlocker.
* Once `unlockerReady` is set to true on the store GRPC calls can be
* made to the client.
* @return {Promise<undefined>}
*/
async initPyface(body) {
log.info('python starting');
try{
var ranID = 'pyFace:'+JSON.stringify(Math.floor(Math.random() * 10000));
body = [ranID].concat(body);
var res = await this._ipc.send('pyFace', ranID, { body });
return res;
}catch(err){
console.log(err);
}
}
//
// WalletUnlocker grpc client
//
/**
* The first GRPC api that is called to initialize the wallet unlocker.
* Once `unlockerReady` is set to true on the store GRPC calls can be
......
......@@ -29,9 +29,8 @@ import PoolRedeemLiqAction from './pool-redeem-liquid';
import InvoiceAction from './invoice';
import SettingAction from './setting';
import AtplAction from './autopilot';
import Algorand from '../chains/algo.js';
import Solana from '../chains/sol.js';
import Algorand from '../chains/algo/algo.js';
import Solana from '../chains/sol/sol.js';
//
// Inject dependencies
......@@ -98,7 +97,7 @@ when(
await grpc.closeUnlocker();
await grpc.initLnd();
await grpc.initAutopilot();
}
}
);
/**
......@@ -173,6 +172,24 @@ when(
console.log('old algo account! '+store.settings.chains.algo.account.address);
}
//var poolStats = await grpc.initPyface(['/data/lightning-bits/src/chains/algo/pools.py', 'bootstrap', 70232824, 0, '289:BITS', 'ALGO', 'TTNTINGI6AKN6JBHCAOXVFZN72BAMDNBTHZ3ZRQC3ILAMWONKN7XRRQ6GM' ]);
var poolStats = await grpc.initPyface(['/data/lightning-bits/src/chains/algo/pools.py', 'accState', 'TTNTINGI6AKN6JBHCAOXVFZN72BAMDNBTHZ3ZRQC3ILAMWONKN7XRRQ6GM' ]);
poolStats = poolStats.replace(/'/g, '"').replace(/"{/g, "{").slice(0, -3);
console.log(poolStats);
store.settings.chains.algo.account.balance = JSON.parse(poolStats).amount/1000000+' ALGOS'
/*
if(!store.chains.sol.account){
......
......@@ -40,8 +40,9 @@ class IpcAction {
* @return {Promise<Object>}
*/
send(event, listen, payload) {
return new Promise((resolve, reject) => {
return new Promise(async (resolve, reject) => {
this._ipcRenderer.send(event, payload);
if (!listen) return resolve();
this._ipcRenderer.once(listen, (e, arg) => {
if (arg.err) {
......
......@@ -101,6 +101,10 @@ class NavAction {
this._store.route = 'PayLightningConfirm';
}
goPayPoolDone() {
this._store.route = 'PayPoolDone';
}
goPayLightningDone() {
this._store.route = 'PayLightningDone';
}
......
......@@ -338,19 +338,19 @@ class PaymentAction {
this._nav.goWait();
const invoice = this._store.payment.address;
const stream = this._grpc.sendStreamCommand('sendPayment');
await new Promise((resolve, reject) => {
return await new Promise((resolve, reject) => {
stream.on('data', data => {
if (data.paymentError) {
reject(new Error(`Lightning payment error: ${data.paymentError}`));
} else {
resolve();
resolve(data);
}
});
stream.on('error', reject);
stream.write(JSON.stringify({ paymentRequest: invoice }), 'utf8');
});
if (failed) return;
this._nav.goPayLightningDone();
this._notification.display({ msg: 'Lightning sent to swap!' });
} catch (err) {
if (failed) return;
//this._nav.goPayLightningConfirm();
......
......@@ -116,6 +116,8 @@ class PoolAddAction {
this._nav.goPoolAddLiquid();
}
/**
* Set the address input for the payment view. This can either be
* an on-chain bitcoin addres or an encoded lightning invoice.
......@@ -138,10 +140,21 @@ class PoolAddAction {
const { settings } = this._store;
var amount = toSatoshis(amount, settings);
//check if contract has enough balance first
console.log(parseFloat(this._store.settings.chains.algo.account.balance)*100, (amount*1.1));
if(parseFloat(this._store.settings.chains.algo.account.balance)*100 > (amount*1.1)){
console.log('INFO! contract has sufficient balance');
}else{
//get new pool quotation from server
var escrowInvoice = await sendToEscrow(amount, '{"state":"pool", "action":"add", "account":"'+this._store.settings.chains.algo.account.address+'", "pool":"'+this._store.selectedPool.id+'", "type":"float"}');
console.log(escrowInvoice);
this.setAddress({ address: escrowInvoice });
}
}
......@@ -172,10 +185,18 @@ class PoolAddAction {
* @return {Promise<undefined>}
*/
async checkType() {
if (!this._store.payment.address) {
const { settings } = this._store;
var amount = toSatoshis(this._store.payment.amount, settings);
if(parseFloat(this._store.settings.chains.algo.account.balance)*1000000 > (amount*1.01)){
console.log('INFO! sufficient BTC in contract. skipping lightning swap...');
} else if (!this._store.payment.address) {
return this._notification.display({ msg: 'Enter an invoice or address' });
}
/*
//if not enough contract BTC then swap from lightning
//
if (await this.decodeInvoice({ invoice: this._store.payment.address })) {
this._nav.goPayLightningConfirm();
......@@ -184,7 +205,10 @@ class PoolAddAction {
} else {
this._notification.display({ msg: 'Invalid invoice or address' });
}
*/
this._nav.goPayPoolConfirm();
}
......@@ -306,6 +330,8 @@ class PoolAddAction {
});
}
}
/**
* Send the specified amount as an on-chain transaction to the provided
......
......@@ -4,7 +4,9 @@
*/
import * as log from './log';
import { parseDate, toHex, checkHttpStatus } from '../helper';
import { parseDate, toHex, checkHttpStatus, toAmountLabel, addAsaQuote } from '../helper';
var hii;
class PoolAction {
constructor(store, grpc, nav, notification) {
......@@ -12,6 +14,7 @@ class PoolAction {
this._grpc = grpc;
this._nav = nav;
this._notification = notification;
hii = this;
}
/**
......@@ -51,7 +54,32 @@ class PoolAction {
//this.getPayments(),
]);
}
async poolsToPool(pool){
var grpc = hii._grpc;
var store = hii._store;
var ret = {
id: pool.id,
cat: 'micro',
type: 'algorand',
icon: pool.bannerPath,
quote: await addAsaQuote(grpc,store,75685483, 0),
banner: pool.bannerPath,
url: 'https://demo1.bitsoko.org',
title: pool.name,
desc: pool.description,
amount: 0,
fee: 0,
confirmations: 5,
status: pool.active ? 'open' : 'closed',
date: parseDate(1604646525),
};
return ret;
}
/**
* List the on-chain transactions by calling the respective grpc api and updating
* the transactions array in the global store.
......@@ -68,23 +96,9 @@ class PoolAction {
var pools = svs.services;
//const pools = (await response.json()).tickers;
//const rate = tickers.find(t => t.ticker.toLowerCase() === fiat).rate;
var quote = await this.addQuote;
this._store.pools = pools.map(pool => ({
id: pool.id,
cat: 'micro',
type: 'algorand',
icon: pool.bannerPath,
banner: pool.bannerPath,
url: 'https://demo1.bitsoko.org',
title: pool.name,
desc: pool.description,
amount: 0,
fee: 0,
confirmations: 5,
status: pool.active ? 'open' : 'closed',
date: parseDate(1604646525),
}));
this._store.pools = await Promise.all(pools.map(await this.poolsToPool));
} catch (err) {
log.error('Listing transactions failed', err);
......
import React from 'react';
import Svg, { Path } from '../../component/svg';
const Bits = props => (
<Svg viewBox="0 0 512 512" width="1em" height="1em" {...props}><Path d="M252.2 10.2c-.5 1.8-4.4 17.6-8.8 35.1l-7.9 31.8-3-.7c-1.6-.3-20.9-5-42.7-10.5-21.8-5.4-40.1-9.9-40.6-9.9-1 0-12.5 46-11.7 46.8.2.2 8.3 2.4 18 4.8 19.6 4.8 25.1 7.3 29.2 13.5 5.2 7.9 5.7 5.2-20.6 111.4-18.1 72.5-24.7 97.9-26.5 100.6-2.4 3.6-7.8 6.9-11.3 6.9-1.1 0-10-2-19.7-4.4l-17.8-4.4-1.3 3.2c-.7 1.7-5.6 13-10.9 25.1-5.2 12.1-9.4 22-9.3 22.2.1.1 19.4 5 42.7 10.8 23.4 5.8 42.8 10.9 43.2 11.3.4.4-3.3 16.6-8.2 36-5.9 23.9-8.4 35.5-7.6 35.7 2 .8 41.5 10.5 42.3 10.5.5 0 4.7-15.3 9.3-34.1 4.7-18.7 8.8-34.6 9.2-35.2.4-.7 5.8.3 16.3 3.1 8.7 2.2 16 4.3 16.2 4.6.3.3-3.3 16.1-8.1 35.2s-8.5 34.8-8.4 35c.8.7 41.3 10.5 42.6 10.2 1.2-.2 3.8-9.3 10.3-35.1 4.8-19.2 8.8-35 9-35.1.2-.2 6.9.7 14.9 2 18.2 3 46.7 4.2 59.5 2.5 34.9-4.6 56.7-19.4 71.3-48.6 15.1-30.3 17.5-63.2 6.2-84.3-6.5-12.2-18.8-24.5-32-31.9-2.7-1.5-5-3-5-3.4 0-.3 3-1.5 6.8-2.7 28.2-9.1 46.2-37.4 46.4-72.7.1-13.8-1.4-21.2-6.8-32-9.7-19.8-31.5-36.6-64.7-50.1-7.3-3-13.7-5.4-14.3-5.4-.7 0 2.2-13.6 7.7-35.3 4.8-19.3 8.5-35.5 8.1-35.8-.9-.8-43.2-11.3-43.5-10.8-.2.2-4 15.7-8.7 34.4l-8.4 34.1-16.5-3.9c-9.1-2.1-16.7-4-16.8-4.1-.1-.1 3.6-15.2 8.2-33.6 4.7-18.4 8.5-34 8.5-34.6 0-1-38.7-11.4-42.4-11.4-.8 0-1.9 1.5-2.4 3.2zm55.4 189.9c-5.7 26.6-10.4 48.5-10.3 48.6.1 0 14.5 3.2 31.9 7 17.5 3.7 31.8 7.1 31.8 7.5 0 .4-27 25.7-60 56.3-33 30.5-60 54.9-60 54.2 0-.8 4.5-22.2 10-47.7 5.5-25.4 10-46.9 10-47.8 0-1.4-5.4-2.8-30.3-8.1-16.6-3.6-31-6.7-32-7.1-1.5-.5 10.8-12.3 58.3-56.4 33.1-30.6 60.4-55.5 60.6-55.3.3.2-4.3 22.2-10 48.8z" fill="#FFF"/></Svg>
);
export default Bits;
......@@ -3,6 +3,8 @@ import * as algosdk from "algosdk";
import * as config from "./algo-config.js";
import store from '../store';
const Algorand = {
status: {
PENDING: 0,
......@@ -135,6 +137,37 @@ const Algorand = {
} catch (err) {
return await Algorand.checkTxStatus(network, txId);
}
},
checkPoolStatus: async (network, txId, aID1, aID2, addr) => {
try {
var poolObject = await Algorand.getClient(network).pendingTransactionInformation(
txId
);
console.log(poolObject);
return poolObject;
} catch (err) {
return await Algorand.checkTxStatus(network, txId);
}
},
checkAccountStatus: async (network, txId, aID1, aID2, addr) => {
try {
var poolObject = await Algorand.getClient(network).pendingTransactionInformation(
txId
);
console.log(poolObject);
return poolObject;
} catch (err) {
return await Algorand.checkTxStatus(network, txId);
}
}
};
......
import * as algosdk from "algosdk";
import * as config from "./algo-config.js";
import store from '../../store';
const Algorand = {
status: {
PENDING: 0,
SUCCESS: 1,
FAILED: 2
},
isValidAddress: address => {
return algosdk.isValidAddress(address);
},
getClient: network => {
let token = {
"X-Algo-API-Token": store.chains.algo.rpc[network].key
};