Skip to content

chore: build message sample script #6371

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
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
40 changes: 40 additions & 0 deletions examples/ts/build-message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Pre-build a message from the wallet
*
* This tool will help you see how to use the BitGo API to easily build
* a message from a wallet.
*
* Copyright 2025, BitGo, Inc. All Rights Reserved.
*/

import {BitGoAPI} from '@bitgo/sdk-api';
import {Hteth} from "@bitgo/sdk-coin-eth";
import {MessageStandardType} from "@bitgo/sdk-core"; // Replace with your given coin (e.g. Ltc, Tltc)
require('dotenv').config({ path: '../../.env' });

const bitgo = new BitGoAPI({
accessToken: process.env.TESTNET_ACCESS_TOKEN,
env: 'test', // Change this to env: 'production' when you are ready for production
});

// Set the coin name to match the blockchain and network
// doge = dogecoin, tdoge = testnet dogecoin
const coin = 'hteth';
bitgo.register(coin, Hteth.createInstance);

const id = '';

async function main() {
const wallet = await bitgo.coin(coin).wallets().get({ id });
console.log(`Wallet label: ${wallet.label()}`);

const txRequest = await wallet.buildSignMessageRequest({
message: {
messageRaw: 'Hello, BitGo!',
messageStandardType: MessageStandardType.EIP191,
},
});
console.dir(txRequest);
}

main().catch((e) => console.log(e));
32 changes: 32 additions & 0 deletions examples/ts/sign-message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* Sign a Message from an MPC wallet at BitGo.
*
* Copyright 2025, BitGo, Inc. All Rights Reserved.
*/
import { BitGo } from 'bitgo';
import { MessageStandardType } from '@bitgo/sdk-core';

const bitgo = new BitGo({ env: 'test' });

const coin = 'hteth';
const basecoin = bitgo.coin(coin);
const accessToken = '';
const walletId = '';
const walletPassphrase = '';

async function signMessage(): Promise<void> {
await bitgo.authenticateWithAccessToken({ accessToken });
const walletInstance = await basecoin.wallets().get({ id: walletId });

const messageTxn = await walletInstance.signMessage({
message: {
messageRaw: 'Hello BitGo!',
messageStandardType: MessageStandardType.EIP191,
},
walletPassphrase,
});

console.log(messageTxn);
}

signMessage().catch(console.error);
80 changes: 53 additions & 27 deletions modules/bitgo/test/v2/unit/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,34 @@ import * as nock from 'nock';
import * as _ from 'lodash';

import {
BaseTssUtils,
common,
CustomSigningFunction,
Ecdsa,
ECDSAUtils,
EDDSAUtils,
GetUserPrvOptions,
Keychains,
KeyType,
ManageUnspentsOptions,
MessageStandardType,
MessageTypes,
PopulatedIntent,
PrebuildTransactionWithIntentOptions,
RequestTracer,
SendManyOptions,
SignatureShareType,
SignedMessage,
SignTypedDataVersion,
TokenType,
TssUtils,
TxRequest,
Wallet,
SignatureShareType,
Ecdsa,
Keychains,
TxRequestVersion,
TypedData,
TypedMessage,
MessageTypes,
SignTypedDataVersion,
GetUserPrvOptions,
ManageUnspentsOptions,
SignedMessage,
BaseTssUtils,
KeyType,
SendManyOptions,
PopulatedIntent,
TxRequestVersion,
Wallet,
WalletSignMessageOptions,
WalletSignTypedDataOptions,
PrebuildTransactionWithIntentOptions,
} from '@bitgo/sdk-core';

import { TestBitGo } from '@bitgo/sdk-test';
Expand Down Expand Up @@ -3467,14 +3468,27 @@ describe('V2 Wallet:', function () {
nock.cleanAll();
});

it('should throw error for unsupported coins', async function () {
await tssSolWallet
.signMessage({
reqId,
message: { messageRaw },
prv: 'secretKey',
})
.should.be.rejectedWith('Message signing not supported for Testnet Solana');
describe('should throw error for unsupported coins', function () {
it('sol signMessage', async function () {
await tssSolWallet
.signMessage({
reqId,
message: { messageRaw },
prv: 'secretKey',
})
.should.be.rejectedWith('Message signing not supported for Testnet Solana');
});

it('sol create signMessage tx request', async function () {
await tssSolWallet
.buildSignMessageRequest({
message: {
messageRaw,
messageStandardType: MessageStandardType.EIP191,
},
})
.should.be.rejectedWith('Message signing not supported for Testnet Solana');
});
});

messageSigningCoins.map((coinName) => {
Expand All @@ -3483,7 +3497,19 @@ describe('V2 Wallet:', function () {
tssEthWallet = new Wallet(bitgo, bitgo.coin(coinName), ethWalletData);
const txRequestId = txRequestForMessageSigning.txRequestId;

it('should sign message', async function () {
it('should create tx Request with signMessage intent', async function () {
nock(bgUrl).post(`/api/v2/wallet/${tssEthWallet.id()}/msgrequests`).reply(200, txRequestForMessageSigning);

const txRequest = await tssEthWallet.buildSignMessageRequest({
message: {
messageRaw,
messageStandardType: MessageStandardType.EIP191,
},
});
txRequest.should.deepEqual(txRequestForMessageSigning);
});

it(`[${coinName}] should sign message`, async function () {
const signMessageTssSpy = sinon.spy(tssEthWallet, 'signMessageTss' as any);
nock(bgUrl)
.get(
Expand All @@ -3495,7 +3521,7 @@ describe('V2 Wallet:', function () {

const signMessage = await tssEthWallet.signMessage({
reqId,
message: { messageRaw, txRequestId },
message: { messageRaw, txRequestId, messageStandardType: MessageStandardType.EIP191 },
prv: 'secretKey',
});
signMessage.should.deepEqual(expectedWithCoinField);
Expand All @@ -3505,7 +3531,7 @@ describe('V2 Wallet:', function () {
);
});

it('should sign message when custodianMessageId is provided', async function () {
it(`[${coinName}] should sign message when custodianMessageId is provided`, async function () {
const signMessageTssSpy = sinon.spy(tssEthWallet, 'signMessageTss' as any);
nock(bgUrl).post(`/api/v2/wallet/${tssEthWallet.id()}/txrequests`).reply(200, txRequestForMessageSigning);

Expand All @@ -3522,7 +3548,7 @@ describe('V2 Wallet:', function () {
);
});

it('should sign message when txRequestId not provided', async function () {
it(`[${coinName}] should sign message when txRequestId not provided`, async function () {
const signMessageTssSpy = sinon.spy(tssEthWallet, 'signMessageTss' as any);
nock(bgUrl).post(`/api/v2/wallet/${tssEthWallet.id()}/txrequests`).reply(200, txRequestForMessageSigning);

Expand Down
4 changes: 3 additions & 1 deletion modules/sdk-core/src/bitgo/baseCoin/iBaseCoin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { IWebhooks } from '../webhook/iWebhooks';
import { TransactionType } from '../../account-lib';
import { IInscriptionBuilder } from '../inscriptionBuilder';
import { Hash } from 'crypto';
import { MPCTx, PopulatedIntent, TokenType } from '../utils';
import { MessageStandardType, MPCTx, PopulatedIntent, TokenType } from '../utils';

export const multisigTypes = {
onchain: 'onchain',
Expand Down Expand Up @@ -301,6 +301,7 @@ export interface TransactionPrebuild extends BaseSignable {
export interface Message extends BaseSignable {
messageRaw: string;
messageEncoded?: string;
messageStandardType?: MessageStandardType;
}

export interface MessageTypeProperty {
Expand Down Expand Up @@ -418,6 +419,7 @@ export interface SignedMessage {
signature: string;
messageRaw: string;
messageEncoded?: string;
messageStandardType?: MessageStandardType;
txRequestId: string;
}

Expand Down
97 changes: 77 additions & 20 deletions modules/sdk-core/src/bitgo/utils/tss/baseTSSUtils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { IRequestTracer } from '../../../api';
import * as openpgp from 'openpgp';
import { Key, readKey, SerializedKeyPair } from 'openpgp';
import { IBaseCoin, KeychainsTriplet } from '../../baseCoin';
import { BitGoBase } from '../../bitgoBase';
Expand All @@ -10,39 +11,40 @@ import * as _ from 'lodash';
import {
BitgoGPGPublicKey,
BitgoHeldBackupKeyShare,
CommitmentShareRecord,
CreateBitGoKeychainParamsBase,
CreateKeychainParamsBase,
CustomCommitmentGeneratingFunction,
CustomGShareGeneratingFunction,
CustomKShareGeneratingFunction,
CustomMPCv2SigningRound1GeneratingFunction,
CustomMPCv2SigningRound2GeneratingFunction,
CustomMPCv2SigningRound3GeneratingFunction,
CustomMuDeltaShareGeneratingFunction,
CustomPaillierModulusGetterFunction,
CustomRShareGeneratingFunction,
CustomSShareGeneratingFunction,
EncryptedSignerShareRecord,
IntentOptionsForMessage,
IntentOptionsForTypedData,
ITssUtils,
PopulatedIntentForMessageSigning,
PopulatedIntentForTypedDataSigning,
PrebuildTransactionWithIntentOptions,
RequestType,
SignatureShareRecord,
TSSParams,
TxRequest,
TxRequestVersion,
CreateKeychainParamsBase,
IntentOptionsForMessage,
PopulatedIntentForMessageSigning,
IntentOptionsForTypedData,
PopulatedIntentForTypedDataSigning,
CreateBitGoKeychainParamsBase,
CommitmentShareRecord,
EncryptedSignerShareRecord,
CustomCommitmentGeneratingFunction,
TSSParamsForMessage,
RequestType,
CustomPaillierModulusGetterFunction,
CustomKShareGeneratingFunction,
CustomMuDeltaShareGeneratingFunction,
CustomSShareGeneratingFunction,
CustomMPCv2SigningRound1GeneratingFunction,
CustomMPCv2SigningRound2GeneratingFunction,
CustomMPCv2SigningRound3GeneratingFunction,
TSSParamsWithPrv,
TxRequest,
TxRequestVersion,
} from './baseTypes';
import { GShare, SignShare } from '../../../account-lib/mpc/tss';
import { RequestTracer } from '../util';
import * as openpgp from 'openpgp';
import { envRequiresBitgoPubGpgKeyConfig, getBitgoMpcGpgPubKey } from '../../tss/bitgoPubKeys';
import { getBitgoGpgPubKey } from '../opengpgUtils';
import assert from 'assert';
import { MessageStandardType } from '../messageTypes';

/**
* BaseTssUtil class which different signature schemes have to extend
Expand Down Expand Up @@ -355,6 +357,7 @@ export default class BaseTssUtils<KeyShare> extends MpcUtils implements ITssUtil

/**
* Create a tx request from params for message signing
* @deprecated Use createSignMessageRequest instead
*
* @param params
* @param apiVersion
Expand All @@ -379,6 +382,35 @@ export default class BaseTssUtils<KeyShare> extends MpcUtils implements ITssUtil
return this.createTxRequestBase(intentOptions, apiVersion, preview, params.reqId);
}

/**
* Create a sign message request
*
* @param params - the parameters for the sign message request
* @param apiVersion - the API version to use, defaults to 'full'
*/
async buildSignMessageRequest(
params: IntentOptionsForMessage,
apiVersion: TxRequestVersion = 'full'
): Promise<TxRequest> {
assert(
params.intentType === 'signMessage',
'Intent type must be signMessage for createMsgRequestWithSignMessageIntent'
);
const intent: PopulatedIntentForMessageSigning = {
custodianMessageId: params.custodianMessageId,
intentType: params.intentType,
sequenceId: params.sequenceId,
comment: params.comment,
memo: params.memo?.value,
isTss: params.isTss,
messageRaw: params.messageRaw,
messageStandardType: params.messageStandardType ?? MessageStandardType.UNKNOWN,
messageEncoded: params.messageEncoded ?? '',
};

return this.buildSignMessageRequestBase(intent, apiVersion, params.reqId);
}

/**
* Create a tx request from params for type data signing
*
Expand Down Expand Up @@ -432,6 +464,31 @@ export default class BaseTssUtils<KeyShare> extends MpcUtils implements ITssUtil
.result();
}

/**
* Calls Bitgo API to create msg request.
*
* @private
*/
private async buildSignMessageRequestBase(
intent: PopulatedIntentForMessageSigning,
apiVersion: TxRequestVersion,
reqId?: IRequestTracer
): Promise<TxRequest> {
const whitelistedParams = {
intent: {
...intent,
},
apiVersion,
};

const reqTracer = reqId || new RequestTracer();
this.bitgo.setRequestTracer(reqTracer);
return this.bitgo
.post(this.bitgo.url(`/wallet/${this.wallet.id()}/msgrequests`, 2))
.send(whitelistedParams)
.result();
}

/**
* Call delete signature shares for a txRequest, the endpoint delete the signatures and return them
*
Expand Down
3 changes: 3 additions & 0 deletions modules/sdk-core/src/bitgo/utils/tss/baseTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { KeyShare } from './ecdsa';
import { EcdsaTypes } from '@bitgo/sdk-lib-mpc';
import { TssEcdsaStep1ReturnMessage, TssEcdsaStep2ReturnMessage, TxRequestChallengeResponse } from '../../tss/types';
import { AShare, DShare, SShare } from '../../tss/ecdsa/types';
import { MessageStandardType } from '../messageTypes';

export type TxRequestVersion = 'full' | 'lite';
export interface HopParams {
Expand Down Expand Up @@ -172,6 +173,7 @@ interface IntentOptionsBase {
export interface IntentOptionsForMessage extends IntentOptionsBase {
messageRaw: string;
messageEncoded?: string;
messageStandardType?: MessageStandardType;
}

export interface IntentOptionsForTypedData extends IntentOptionsBase {
Expand Down Expand Up @@ -226,6 +228,7 @@ export interface PopulatedIntentForMessageSigning extends PopulatedIntentBase {
messageRaw: string;
messageEncoded: string;
custodianMessageId?: string;
messageStandardType?: MessageStandardType;
}

export interface PopulatedIntentForTypedDataSigning extends PopulatedIntentBase {
Expand Down
Loading
Loading