Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions modules/abstract-utxo/src/abstractUtxoCoin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
AddressCoinSpecific,
BaseCoin,
BitGoBase,
ColdTransactionPrebuild,
CreateAddressFormat,
ExtraPrebuildParamsOptions,
HalfSignedUtxoTransaction,
Expand All @@ -17,6 +18,7 @@ import {
IRequestTracer,
isTriple,
IWallet,
Keychain,
KeychainsTriplet,
KeyIndices,
MismatchedRecipient,
Expand Down Expand Up @@ -1068,6 +1070,17 @@ export abstract class AbstractUtxoCoin extends BaseCoin implements Musig2Partici
return false;
}

prepareColdTransaction<T extends BaseTransactionPrebuild>(
txPrebuild: T,
keychains: Triple<Keychain>
): T & ColdTransactionPrebuild {
// PSBTs already carry BIP-32 derivation info inline, so offline signers do not need the extra information
if (typeof txPrebuild.txHex === 'string' && hasPsbtMagic(stringToBufferTryFormats(txPrebuild.txHex, ['hex']))) {
return txPrebuild as T & ColdTransactionPrebuild;
}
return super.prepareColdTransaction(txPrebuild, keychains);
}

getRecoveryProvider(apiToken?: string): RecoveryProvider {
return forCoin(this.getChain(), apiToken);
}
Expand Down
42 changes: 41 additions & 1 deletion modules/sdk-core/src/bitgo/baseCoin/baseCoin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { signMessage } from '../bip32util';
import { NotImplementedError } from '../../account-lib';
import { BitGoBase } from '../bitgoBase';
import { Enterprises } from '../enterprise';
import { Keychains, KeyIndices } from '../keychain';
import { ApiKeyShare, Keychain, Keychains, KeyIndices } from '../keychain';
import { Markets } from '../market';
import { PendingApprovals } from '../pendingApproval';
import { IWallet, Wallet, Wallets } from '../wallet';
Expand All @@ -22,6 +22,7 @@ import {
BaseBroadcastTransactionOptions,
BaseBroadcastTransactionResult,
BuildNftTransferDataOptions,
ColdTransactionPrebuild,
DeriveKeyWithSeedOptions,
ExtraPrebuildParamsOptions,
FeeEstimateOptions,
Expand Down Expand Up @@ -57,6 +58,7 @@ import {
PopulatedIntent,
PrebuildTransactionWithIntentOptions,
TokenTransferRecipientParams,
Triple,
} from '../utils';

export abstract class BaseCoin implements IBaseCoin {
Expand Down Expand Up @@ -244,6 +246,44 @@ export abstract class BaseCoin implements IBaseCoin {
return false;
}

/**
* Prepare a built transaction for cold (offline) signing by attaching the
* xpubs / xpub derivation paths the offline signer (OVC) needs in order to
* sign it. Coins whose tx format already carries this information inline
* (e.g. PSBT) can override this to skip attaching it.
* @param txPrebuild The built transaction JSON returned by the platform
* @param keychains The user/backup/bitgo keychains for this wallet, in that order
* @returns The txPrebuild JSON, augmented with `pubs` and `xpubsWithDerivationPath`
*/
prepareColdTransaction<T extends TransactionPrebuild>(
Comment thread
davidkaplanbitgo marked this conversation as resolved.
txPrebuild: T,
keychains: Triple<Keychain>
): T & ColdTransactionPrebuild {
const [user, backup, bitgo] = keychains;
const sources: Array<[ApiKeyShare['from'], Keychain]> = [
['user', user],
['backup', backup],
['bitgo', bitgo],
];
const pubs: string[] = [];
const xpubsWithDerivationPath: NonNullable<ColdTransactionPrebuild['xpubsWithDerivationPath']> = {};
for (const [source, keychain] of sources) {
// MPC keychains only have a commonKeychain, not a pub - skip those.
if (keychain.pub) {
pubs.push(keychain.pub);
xpubsWithDerivationPath[source] = {
xpub: keychain.pub,
derivedFromParentWithSeed: keychain.derivedFromParentWithSeed,
};
}
}
return {
...txPrebuild,
pubs,
xpubsWithDerivationPath,
};
}
Comment thread
davidkaplanbitgo marked this conversation as resolved.

/**
* Returns the factor between the base unit and its smallest subdivison
* @return {number}
Expand Down
18 changes: 16 additions & 2 deletions modules/sdk-core/src/bitgo/baseCoin/iBaseCoin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { BaseTokenConfig, BaseCoin as StaticsBaseCoin } from '@bitgo/statics';
import BigNumber from 'bignumber.js';
import { IRequestTracer } from '../../api';
import { IEnterprises } from '../enterprise';
import { IKeychains, Keychain } from '../keychain';
import { ApiKeyShare, IKeychains, Keychain } from '../keychain';
import { IMarkets } from '../market';
import { IPendingApprovals } from '../pendingApproval';
import { InitiateRecoveryOptions } from '../recovery';
Expand All @@ -14,7 +14,7 @@ import { TokenEnablement } from '@bitgo/public-types';
import { Hash } from 'crypto';
import { TransactionType } from '../../account-lib';
import { IInscriptionBuilder } from '../inscriptionBuilder';
import { MessageStandardType, MPCTx, PopulatedIntent, TokenTransferRecipientParams, TokenType } from '../utils';
import { MessageStandardType, MPCTx, PopulatedIntent, TokenTransferRecipientParams, TokenType, Triple } from '../utils';
import type { SignableTransaction } from '../utils/tss/baseTypes';
import { IWebhooks } from '../webhook/iWebhooks';

Expand Down Expand Up @@ -442,6 +442,16 @@ export interface TransactionPrebuild extends BaseSignable {
txInfo?: unknown;
}

export interface XpubWithDerivationPath {
xpub: string;
derivedFromParentWithSeed?: string;
}

export interface ColdTransactionPrebuild {
pubs?: string[];
xpubsWithDerivationPath?: Partial<Record<ApiKeyShare['from'], XpubWithDerivationPath>>;
}

export interface Message extends BaseSignable {
messageRaw: string;
messageEncoded?: string;
Expand Down Expand Up @@ -672,6 +682,10 @@ export interface IBaseCoin {
supportsDeriveKeyWithSeed(): boolean;
isEVM(): boolean;
supportsBlsDkg(): boolean;
prepareColdTransaction<T extends TransactionPrebuild>(
txPrebuild: T,
keychains: Triple<Keychain>
): T & ColdTransactionPrebuild;
getBaseFactor(): number | string;
baseUnitsToBigUnits(baseUnits: string | number): string;
bigUnitsToBaseUnits(bigUnits: string | number): string;
Expand Down
Loading