Skip to content
This repository has been archived by the owner on Nov 13, 2020. It is now read-only.

Commit

Permalink
SwapRequest flow (#6)
Browse files Browse the repository at this point in the history
* Fix tests; Add contributors and files in pkg.json

* Add comments for prepare server stubs integration

* Mock creation of Tx

* Add --verbose to swap command; add real createTx in WalletInterface
  • Loading branch information
tiero authored Mar 29, 2020
1 parent 7aab722 commit a12f463
Show file tree
Hide file tree
Showing 8 changed files with 221 additions and 51 deletions.
4 changes: 2 additions & 2 deletions src/actions/connectAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ export default function (endpoint: string): void {
const markets = {
"LBTC-USDT": {
"LBTC": "5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225",
"USDT": "6554256e750fd6a4d4fd0e0ac1ad960ea8c4d774c9d7918ef84d1eb6db78ae72"
"USDT": "b66f2b61a8140ba67265c25f6a5d5c836e9c789ee7a172c1408eea37d9d17ab0"
},
"LBTC-EQUI": {
"LBTC": "5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225",
"EQUI": "88d6951f3103ec292bf91d1d8ad14bf62e22f31c0f00221f7371440e4f9016bf"
"EQUI": "1fd91e7be0f4cefd52853c33475d2947cf5173d5add03de8aa369352852a2b9c"
}
};

Expand Down
59 changes: 48 additions & 11 deletions src/actions/swapAction.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { info, log, error, success } from '../logger';
import { createTx } from '../wallet';
import { makeid } from '../helpers';
import State from '../state';
import Wallet, { fetchUtxos, WalletInterface, fromWIF } from '../wallet';
import { decrypt } from '../crypto';
const state = new State();

const { Toggle, NumberPrompt, Confirm } = require('enquirer');
const { Toggle, NumberPrompt, Confirm, Password } = require('enquirer');

// 1. Fetch utxos
// 2. CHeck if input amount is enough
Expand All @@ -13,7 +14,7 @@ const { Toggle, NumberPrompt, Confirm } = require('enquirer');
// 5. Sign the final psbt
// 6. Send SwapComplete back

export default function () {
export default function (cmdObj: any) {
info('=========*** Swap ***==========\n');


Expand Down Expand Up @@ -43,10 +44,14 @@ export default function () {
name: 'question',
message: 'Are you sure continue?'
});
const password = new Password({
type: 'password',
name: 'key',
message: 'Type your private key WIF (Wallet Import Format)'
});



let toBeSent: string,
let walletInstance: WalletInterface,
toBeSent: string,
toReceive: string,
amountToBeSent: number,
amountToReceive: number;
Expand All @@ -68,17 +73,48 @@ export default function () {
const OneOfTickerB = 6000;
const OneOfTickerA = 0.000167;
amountToReceive = amountToBeSent * (toBeSent === 'LBTC' ? OneOfTickerB : OneOfTickerA);
const amountToReceiveString = amountToReceive.toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: 8 })
log(`Gotcha! You will send ${toBeSent} ${amountToBeSent} and receive circa ${toReceive} ${amountToReceiveString} based on current market rate`);
amountToReceive = Number(
amountToReceive
.toLocaleString("en-US", { minimumFractionDigits: 0, maximumFractionDigits: 8 })
.replace(',','')
);
log(`Gotcha! You will send ${toBeSent} ${amountToBeSent} and receive circa ${toReceive} ${amountToReceive} based on current market rate`);

return confirm.run()
}).then((keepGoing: Boolean) => {
if (!keepGoing)
return log('Terminated');
throw 'Terminated';

const execute = wallet.keystore.type === "encrypted" ?
password.run :
() => Promise.resolve(wallet.keystore.value);

return execute()
}).then((passwordOrWif: string) => {

const wif = wallet.keystore.type === "encrypted" ?
decrypt(wallet.keystore.value, passwordOrWif) :
passwordOrWif;

walletInstance = fromWIF(wif, network.chain);

return fetchUtxos(walletInstance.address, network.explorer)
}).then((utxos: Array<any>) => {
// Clean up numbers from fractional to satoshis
amountToBeSent = Math.floor(amountToBeSent * Math.pow(10,8));
amountToReceive = Math.floor(amountToReceive * Math.pow(10,8));

return walletInstance.createTx(
utxos,
amountToBeSent,
amountToReceive,
(market.assets as any)[toBeSent],
(market.assets as any)[toReceive]
)
}).then((unsignedPsbt: string) => {

// Wait for the stream with either the SwapAccet or SwapFail message
// We are going to send somethin like
const unsignedPsbt = createTx(wallet.address, (market.assets as any), network.explorer);
const TradeRequest = {
SwapRequest: {
id: makeid(8),
Expand All @@ -90,7 +126,8 @@ export default function () {
}
}

info(JSON.stringify(TradeRequest, undefined, 2));
if (cmdObj.verbose)
info(JSON.stringify(TradeRequest, undefined, 2));
log(`\nSending SwapRequest to provider...\n`)
// client.Trade().then( stream => { })
setTimeout(() => {
Expand Down
18 changes: 9 additions & 9 deletions src/actions/walletAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,14 @@ export default function () : void {
password.run().then((password:string) => {

setWalletState(
walletFromScratch.publicKey,
walletFromScratch.address,
walletFromScratch,
storageType,
encrypt(walletFromScratch.keyPair.toWIF(), password)
);
}).catch(error);
else
setWalletState(
walletFromScratch.publicKey,
walletFromScratch.address,
walletFromScratch,
storageType,
walletFromScratch.keyPair.toWIF()
);
Expand All @@ -83,17 +81,15 @@ export default function () : void {
password.run().then((password:string) => {

setWalletState(
restoredWallet.publicKey,
restoredWallet.address,
restoredWallet,
storageType,
encrypt(wif, password)
);

}).catch(error);
else
setWalletState(
restoredWallet.publicKey,
restoredWallet.address,
restoredWallet,
storageType,
restoredWallet.keyPair.toWIF()
);
Expand All @@ -106,12 +102,16 @@ export default function () : void {
}


function setWalletState(pubkey:string, address:string, type:string, value:string):void {
function setWalletState(walletObject: WalletInterface, type:string, value:string):void {
const pubkey = walletObject.publicKey;
const address = walletObject.address;
const script = walletObject.script;
state.set({
wallet: {
selected: true,
pubkey,
address,
script,
keystore: {
type,
value
Expand Down
6 changes: 3 additions & 3 deletions src/actions/walletBalanceAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const state = new State();
export default function (): void {
info('=========*** Wallet ***==========\n');

const { wallet, network } = state.get();
const { wallet, network, provider } = state.get();

if (!wallet.selected)
return error('A wallet is required. Create or restoste with the wallet command');
Expand All @@ -17,14 +17,14 @@ export default function (): void {
fetchBalances(wallet.address, network.explorer)
.then(balances => {
const entries = Object.entries(balances);

if (entries.length === 0)
return log('No transactions found.');

entries.forEach(([asset, balance]) => {
let title = "Unknown"
if (asset === (networks as any)[network.chain].assetHash)
title = "Liquid Bitcoin";
title = "LBTC";

success(`*** ${title} ***`)
log(`Balance ${balance}`)
Expand Down
97 changes: 97 additions & 0 deletions src/coinselect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// @ts-nocheck
// baseline estimates, used to improve performance
const TX_VERSION = 4
const TX_LOCKTIME = 4
const TX_IN_COUNT = 1
const TX_OUT_COUNT = 1
const TX_FLAG = 1
const TX_EMPTY_SIZE = TX_VERSION + TX_LOCKTIME + TX_IN_COUNT + TX_OUT_COUNT + TX_FLAG

const TX_PREV_OUT_HASH = 32
const TX_PREV_OUT_INDEX = 4
const TX_INPUT_SEQUENCE = 4
const TX_INPUT_SCRIPT_LENGHT = 1
const TX_INPUT_BASE = TX_PREV_OUT_HASH + TX_PREV_OUT_INDEX + TX_INPUT_SCRIPT_LENGHT + TX_INPUT_SEQUENCE
const TX_INPUT_SCRIPT = 107

const TX_OUTPUT_ASSET = 32 + 1
const TX_OUTPUT_NONCE = 1
const TX_OUTPUT_VALUE = 8 + 1
const TX_OUTPUT_BASE = TX_OUTPUT_VALUE + TX_OUTPUT_ASSET + TX_OUTPUT_NONCE
const TX_OUTPUT_SCRIPT = 24


function transactionBytes (vin, vout) {
return TX_EMPTY_SIZE +
(vin * (TX_INPUT_BASE + TX_INPUT_SCRIPT)) +
(vout * (TX_OUTPUT_BASE + TX_OUTPUT_SCRIPT))
};



function calculateFees(vin, vout, opts = {}) {
const satPerByte = opts.satPerByte ? opts.satPerByte : 1;
const rate = opts.rate ? opts.rate : 10;

const size = transactionBytes(vin, vout);
const fees = parseInt(size * satPerByte);

const spread = parseInt(fees / 100 * rate);
const total = parseInt(fees + spread);

return {
size,
satPerByte,
fees,
spread,
rate,
total
}
};


function naiveCoinSelection(utxos, amount) {
let unspents = [];
let availableSat = 0;
let change = 0;


for (let i = 0; i < utxos.length; i++) {
const utxo = utxos[i]
unspents.push({ txid: utxo.txid, vout: utxo.vout, value: utxo.value, asset: utxo.asset })
availableSat += utxo.value

if (availableSat >= amount)
break;
};

if (availableSat < amount)
throw "You do not have enough in your wallet";

change = availableSat - amount;

return { unspents, change }
};

/* const changeWithoutFees = availableSat - amount;
//let fee = getTxSize(unspents.length, changeWithoutFees > 0 ? 2 : 1) * feePerByte;
if (amount < fee)
throw "Satoshis amount must be larger than the fee";
if (fee < MAGIC_MIN_RELAY_FEE)
fee = MAGIC_MIN_RELAY_FEE;
const change = changeWithoutFees - fee;
if (change < 0)
throw "You do not have enough in your wallet to pay for fees";
return { unspents, change, fee }
*/

export default naiveCoinSelection;
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ wallet
*/
program
.command('swap')
.option('-v, --verbose', "Show low-level message information", false)
.description('Make a swap proposal')
.action(swapAction);

Expand Down
1 change: 1 addition & 0 deletions src/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ const initialState = {
selected: false,
pubkey: "",
address: "",
script: Buffer.alloc(0),
keystore: {
type: "",
value: ""
Expand Down
Loading

0 comments on commit a12f463

Please sign in to comment.