Skip to content

Commit

Permalink
feat: add coinselect and basic wallet functionality to sdk
Browse files Browse the repository at this point in the history
Signed-off-by: Gregory Hill <[email protected]>
  • Loading branch information
gregdhill committed Nov 1, 2023
1 parent b0379a3 commit c2d1dc9
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 2 deletions.
5 changes: 3 additions & 2 deletions sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"yargs": "^17.5.1"
},
"dependencies": {
"bitcoinjs-lib": "^6.1.5"
"bitcoinjs-lib": "^6.1.5",
"coinselect": "^3.1.13"
}
}
}
40 changes: 40 additions & 0 deletions sdk/src/@types/coinselect/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
declare module "coinselect" {
interface Input {
txId: string,
vout: number,
value: number,
// For use with PSBT:
// not needed for coinSelect, but will be passed on to inputs later
nonWitnessUtxo?: Buffer,
// OR
// if your utxo is a segwit output, you can use witnessUtxo instead
witnessUtxo?: {
script: Buffer,
value: number,
}
}

interface Output {
address?: string,
value: number
}

/**
* Blackjack, with Accumulative fallback
* @param inputs The UTXOs to spend from.
* @param outputs The transaction outputs.
* @param feeRate The current Bitcoin fee rate.
* @returns The fee paid to the network.
*/
function coinSelect(
inputs: Array<Input>,
outputs: Array<Output>,
feeRate: number // satoshis per byte
): {
inputs?: Array<Input>,
outputs?: Array<Output>,
fee: number,
};

export = coinSelect;
}
1 change: 1 addition & 0 deletions sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from "./electrs";
export * from "./relay";
export * from "./utils";
export * from "./ordinals";
export * from "./wallet";
61 changes: 61 additions & 0 deletions sdk/src/wallet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Psbt, Transaction, Network, TxOutput, address as addressUtils } from "bitcoinjs-lib";
import { ElectrsClient } from "./electrs";

import coinSelect = require('coinselect');

export interface Wallet {
getAddress(): Promise<string>;
getChangeAddress(): Promise<string>;
signPsbt(psbt: Psbt): Promise<Psbt>;
}

export async function createAndFundTransaction(electrsClient: ElectrsClient, wallet: Wallet, network: Network, txOutputs: Array<TxOutput>): Promise<Transaction> {
const address = await wallet.getAddress();
const utxos = await electrsClient.getAddressUtxos(address);

const { inputs, outputs } = coinSelect(
utxos.map(utxo => {
return {
txId: utxo.txid,
vout: utxo.vout,
value: utxo.value,
}
}),
txOutputs.map(txOut => {
return {
address: addressUtils.fromOutputScript(txOut.script, network),
value: txOut.value,
}
}),
1
);

let psbt = new Psbt({ network })

inputs.forEach(input =>
psbt.addInput({
hash: input.txId,
index: input.vout,
// TODO: determine these from electrs utxos
nonWitnessUtxo: input.nonWitnessUtxo,
witnessUtxo: input.witnessUtxo,
})
);

const changeAddress = await wallet.getChangeAddress();
outputs.forEach(output => {
// watch out, outputs may have been added that you need to provide
// an output address/script for
if (!output.address) {
output.address = changeAddress;
}

psbt.addOutput({
address: output.address,
value: output.value,
})
});

await wallet.signPsbt(psbt);
return psbt.extractTransaction();
}

0 comments on commit c2d1dc9

Please sign in to comment.