Skip to content
This repository has been archived by the owner on Apr 15, 2021. It is now read-only.

Commit

Permalink
refactor: separate builder function for signed and unsigned transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
reedrosenbluth committed Aug 14, 2020
1 parent 3ee4ed2 commit 694b2dc
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 38 deletions.
102 changes: 70 additions & 32 deletions src/builders.ts
@@ -1,3 +1,5 @@
import * as _ from 'lodash';

import { StacksTransaction } from './transaction';

import { StacksNetwork, StacksMainnet, StacksTestnet } from './network';
Expand Down Expand Up @@ -232,7 +234,6 @@ export interface MultiSigOptions {
* @param {String|PrincipalCV} recipientAddress - the c32check address of the recipient or a
* principal clarity value
* @param {BigNum} amount - number of tokens to transfer in microstacks
* @param {String} senderKey - hex string sender private key used to sign transaction
* @param {BigNum} fee - transaction fee in microstacks
* @param {BigNum} nonce - a nonce must be increased monotonically with each new transaction
* @param {StacksNetwork} network - the Stacks blockchain network this transaction is destined for
Expand All @@ -244,13 +245,10 @@ export interface MultiSigOptions {
* @param {PostCondition[]} postConditions - an array of post conditions to add to the
* transaction
* @param {Boolean} sponsored - true if another account is sponsoring the transaction fees
*
* @param {MultiSigOptions} multiSig - options for a multi-sig transaction
*/
export interface TokenTransferOptions {
recipient: string | PrincipalCV;
amount: BigNum;
senderKey?: string;
fee?: BigNum;
nonce?: BigNum;
network?: StacksNetwork;
Expand All @@ -259,20 +257,38 @@ export interface TokenTransferOptions {
postConditionMode?: PostConditionMode;
postConditions?: PostCondition[];
sponsored?: boolean;
multiSig?: MultiSigOptions;
}

export interface UnsignedTokenTransferOptions extends TokenTransferOptions {
publicKey: string;
}

export interface SignedTokenTransferOptions extends TokenTransferOptions {
senderKey: string;
}

export interface UnsignedMultiSigTokenTransferOptions extends TokenTransferOptions {
numSignatures: number;
publicKeys: string[];
}

export interface SignedMultiSigTokenTransferOptions extends TokenTransferOptions {
numSignatures: number;
publicKeys: string[];
signerKeys: string[];
}

/**
* Generates a Stacks token transfer transaction
* Generates an unsigned Stacks token transfer transaction
*
* Returns a signed Stacks token transfer transaction.
* Returns a Stacks token transfer transaction.
*
* @param {TokenTransferOptions} txOptions - an options object for the token transfer
* @param {UnsignedTokenTransferOptions | UnsignedMultiSigTokenTransferOptions} txOptions - an options object for the token transfer
*
* @return {StacksTransaction}
*/
export async function makeSTXTokenTransfer(
txOptions: TokenTransferOptions
export async function makeUnsignedSTXTokenTransfer(
txOptions: UnsignedTokenTransferOptions | UnsignedMultiSigTokenTransferOptions
): Promise<StacksTransaction> {
const defaultOptions = {
fee: new BigNum(0),
Expand All @@ -291,25 +307,23 @@ export async function makeSTXTokenTransfer(
let authorization = null;
let spendingCondition = null;

if (options.multiSig) {
spendingCondition = createMultiSigSpendingCondition(
AddressHashMode.SerializeP2SH,
options.multiSig.numSignatures,
options.multiSig.publicKeys,
options.nonce,
options.fee
);
} else if (options.senderKey) {
const privKey = createStacksPrivateKey(options.senderKey);
const pubKey = getPublicKey(privKey);
if ('publicKey' in options) {
// multi-sig
spendingCondition = createSingleSigSpendingCondition(
AddressHashMode.SerializeP2PKH,
publicKeyToString(pubKey),
options.publicKey,
options.nonce,
options.fee
);
} else {
throw new Error('Transaction options must include either senderKey or multiSig options');
// multi-sig
spendingCondition = createMultiSigSpendingCondition(
AddressHashMode.SerializeP2SH,
options.numSignatures,
options.publicKeys,
options.nonce,
options.fee
);
}

if (options.sponsored) {
Expand Down Expand Up @@ -351,11 +365,39 @@ export async function makeSTXTokenTransfer(
transaction.setNonce(txNonce);
}

if (options.multiSig?.signerKeys) {
return transaction;
}

/**
* Generates a signed Stacks token transfer transaction
*
* Returns a signed Stacks token transfer transaction.
*
* @param {SignedTokenTransferOptions | SignedMultiSigTokenTransferOptions} txOptions - an options object for the token transfer
*
* @return {StacksTransaction}
*/
export async function makeSTXTokenTransfer(
txOptions: SignedTokenTransferOptions | SignedMultiSigTokenTransferOptions
): Promise<StacksTransaction> {
if ('senderKey' in txOptions) {
const publicKey = publicKeyToString(getPublicKey(createStacksPrivateKey(txOptions.senderKey)));
const options = _.omit(txOptions, 'senderKey');
const transaction = await makeUnsignedSTXTokenTransfer({ publicKey, ...options });

const privKey = createStacksPrivateKey(txOptions.senderKey);
const signer = new TransactionSigner(transaction);
let pubKeys = options.multiSig.publicKeys;
signer.signOrigin(privKey);

return transaction;
} else {
const transaction = await makeUnsignedSTXTokenTransfer(
_.omit(txOptions, 'signerKeys') as UnsignedMultiSigTokenTransferOptions
);

for (const key of options.multiSig.signerKeys) {
const signer = new TransactionSigner(transaction);
let pubKeys = txOptions.publicKeys;
for (const key of txOptions.signerKeys) {
const pubKey = pubKeyfromPrivKey(key);
pubKeys = pubKeys.filter(pk => pk !== pubKey.data.toString('hex'));
signer.signOrigin(createStacksPrivateKey(key));
Expand All @@ -364,13 +406,9 @@ export async function makeSTXTokenTransfer(
for (const key of pubKeys) {
signer.appendOrigin(publicKeyFromBuffer(Buffer.from(key, 'hex')));
}
} else if (options.senderKey) {
const privKey = createStacksPrivateKey(options.senderKey);
const signer = new TransactionSigner(transaction);
signer.signOrigin(privKey);
}

return transaction;
return transaction;
}
}

/**
Expand Down
11 changes: 5 additions & 6 deletions tests/src/builder-tests.ts
@@ -1,7 +1,7 @@
import * as fs from 'fs';

import {
makeSTXTokenTransfer,
makeUnsignedSTXTokenTransfer,
makeContractDeploy,
makeContractCall,
makeStandardSTXPostCondition,
Expand All @@ -18,6 +18,7 @@ import {
TxBroadcastResultRejected,
callReadOnlyFunction,
sponsorTransaction,
makeSTXTokenTransfer,
} from '../../src/builders';

import { deserializeTransaction } from '../../src/transaction';
Expand Down Expand Up @@ -209,16 +210,14 @@ test('Make Multi-Sig STX token transfer', async () => {
const pubKeys = privKeyStrings.map(pubKeyfromPrivKey);
const pubKeyStrings = pubKeys.map(publicKeyToString);

const transaction = await makeSTXTokenTransfer({
const transaction = await makeUnsignedSTXTokenTransfer({
recipient,
amount,
fee,
nonce,
memo: memo,
multiSig: {
numSignatures: 2,
publicKeys: pubKeyStrings,
},
numSignatures: 2,
publicKeys: pubKeyStrings,
});

const serializedTx = transaction.serialize();
Expand Down

0 comments on commit 694b2dc

Please sign in to comment.