Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
551c570
chore: add catalog-info.yaml for Backstage developer portal
bitgo-backstage[bot] Mar 24, 2026
285b90d
feat: add new tokens from AMS API
asset-metadata-bot[bot] Jun 2, 2026
a9e2dd9
feat(express): add EdDSA MPCv2 external signer routing
vibhavgo May 28, 2026
7e6555a
feat: add encryptAsync to encrypt.ts
bdesoky Jun 2, 2026
60d9ab8
Merge pull request #8921 from BitGo/WCN-715
bdesoky Jun 2, 2026
f7a0736
fix(statics): sdk bump
shobhit565 Jun 2, 2026
3aa4218
Merge pull request #8922 from BitGo/hfix-slx-decimals
shobhit565 Jun 2, 2026
ead9d12
feat: add new tokens from AMS API
asset-metadata-bot[bot] Jun 3, 2026
bbe7ebb
Merge pull request #8882 from BitGo/WCI-380
vibhavgo Jun 3, 2026
b183180
feat(statics): exclude Singapore custody for CECHO-1183 HoodETH and X…
manojkumar138 Jun 3, 2026
a54b75e
feat: add new tokens from AMS API
asset-metadata-bot[bot] Jun 3, 2026
4b09564
Merge pull request #8914 from BitGo/ams-bot-tokens
manas-at-bitgo Jun 3, 2026
7fac7f8
Merge pull request #8329 from BitGo/add-catalog-info-yaml
roshan-bitgo Jun 3, 2026
3ca5247
Merge pull request #8928 from BitGo/feat/sg-trust-gate-xtzevm-hoodeth
nvrakesh06 Jun 3, 2026
216825c
feat(utxo-lib): add ZEC NU6.2 consensus branch id
OttoAllmendinger Jun 3, 2026
715cafe
chore: bump @bitgo/wasm-utxo to 4.16.0
lcovar Jun 3, 2026
f9604a0
Merge pull request #8932 from BitGo/otto/T1-3519-zec-nu62-branch-id
davidkaplanbitgo Jun 3, 2026
10b7ff0
Merge pull request #8933 from BitGo/BTC-0.bump-wasm-utxo-4.16.0
davidkaplanbitgo Jun 3, 2026
80b9e0f
Merge remote-tracking branch 'origin/master' into backmerge/master-in…
alextse-bg Jun 3, 2026
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
15 changes: 15 additions & 0 deletions catalog-info.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Backstage catalog descriptor. Update this file to enrich your service's entry in the developer portal.
# Full field reference: https://backstage.io/docs/features/software-catalog/descriptor-format/
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: BitGoJS
annotations:
github.com/project-slug: BitGo/BitGoJS
spec:
# The primary purpose of this component. If this repo serves multiple purposes
# (e.g. both a deployable service and a published library), set this to whichever
# role is most significant. Valid values: service, library, website, documentation, other.
type: other
lifecycle: production
owner: group:coins
2 changes: 1 addition & 1 deletion modules/abstract-utxo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
"@bitgo/utxo-descriptors": "^1.3.1",
"@bitgo/utxo-lib": "^11.23.0",
"@bitgo/utxo-ord": "^1.32.1",
"@bitgo/wasm-utxo": "^4.14.1",
"@bitgo/wasm-utxo": "^4.16.0",
"@types/lodash": "^4.14.121",
"@types/superagent": "4.1.15",
"bignumber.js": "^9.0.2",
Expand Down
132 changes: 111 additions & 21 deletions modules/express/src/clientRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import {
CustomCommitmentGeneratingFunction,
CustomGShareGeneratingFunction,
CustomKShareGeneratingFunction,
CustomEddsaMPCv2SigningRound1GeneratingFunction,
CustomEddsaMPCv2SigningRound2GeneratingFunction,
CustomEddsaMPCv2SigningRound3GeneratingFunction,
CustomMPCv2SigningRound1GeneratingFunction,
CustomMPCv2SigningRound2GeneratingFunction,
CustomMPCv2SigningRound3GeneratingFunction,
Expand All @@ -17,6 +20,7 @@ import {
CustomSShareGeneratingFunction,
EcdsaMPCv2Utils,
EcdsaUtils,
EddsaMPCv2Utils,
EddsaUtils,
EncryptedSignerShareRecord,
encryptRsaWithAesGcm,
Expand Down Expand Up @@ -455,18 +459,41 @@ export async function handleV2GenerateShareTSS(
req.body.walletPassphrase = walletPw;
try {
if (coin.getMPCAlgorithm() === MPCType.EDDSA) {
const eddsaUtils = new EddsaUtils(bitgo, coin);
switch (req.decoded.sharetype) {
case ShareType.Commitment:
return await eddsaUtils.createCommitmentShareFromTxRequest(req.body);
case ShareType.R:
return await eddsaUtils.createRShareFromTxRequest(req.body);
case ShareType.G:
return await eddsaUtils.createGShareFromTxRequest(req.body);
default:
throw new Error(
`Share type ${req.decoded.sharetype} not supported, only commitment, G and R share generation is supported.`
);
const isMPCv2 =
[
ShareType.EddsaMPCv2Round1.toString(),
ShareType.EddsaMPCv2Round2.toString(),
ShareType.EddsaMPCv2Round3.toString(),
].includes(req.decoded.sharetype) || req.decoded.sharetype.startsWith('EddsaMPCv2');

if (isMPCv2) {
const eddsaMPCv2Utils = new EddsaMPCv2Utils(bitgo, coin);
switch (req.decoded.sharetype) {
case ShareType.EddsaMPCv2Round1:
return await eddsaMPCv2Utils.createOfflineRound1Share(req.body);
case ShareType.EddsaMPCv2Round2:
return await eddsaMPCv2Utils.createOfflineRound2Share(req.body);
case ShareType.EddsaMPCv2Round3:
return await eddsaMPCv2Utils.createOfflineRound3Share(req.body);
default:
throw new Error(
`Share type ${req.decoded.sharetype} not supported for EdDSA MPCv2, only EddsaMPCv2Round1, EddsaMPCv2Round2 and EddsaMPCv2Round3 is supported.`
);
}
} else {
const eddsaUtils = new EddsaUtils(bitgo, coin);
switch (req.decoded.sharetype) {
case ShareType.Commitment:
return await eddsaUtils.createCommitmentShareFromTxRequest(req.body);
case ShareType.R:
return await eddsaUtils.createRShareFromTxRequest(req.body);
case ShareType.G:
return await eddsaUtils.createGShareFromTxRequest(req.body);
default:
throw new Error(
`Share type ${req.decoded.sharetype} not supported, only commitment, G and R share generation is supported.`
);
}
}
} else if (coin.getMPCAlgorithm() === MPCType.ECDSA) {
const isMPCv2 = [
Expand Down Expand Up @@ -895,15 +922,33 @@ function createTSSSendParams(req: express.Request, wallet: Wallet) {
if (req.config?.externalSignerUrl !== undefined) {
const coin = req.bitgo.coin(req.params.coin);
if (coin.getMPCAlgorithm() === MPCType.EDDSA) {
return {
...req.body,
customCommitmentGeneratingFunction: createCustomCommitmentGenerator(
req.config.externalSignerUrl,
req.params.coin
),
customRShareGeneratingFunction: createCustomRShareGenerator(req.config.externalSignerUrl, req.params.coin),
customGShareGeneratingFunction: createCustomGShareGenerator(req.config.externalSignerUrl, req.params.coin),
};
if (wallet._wallet.multisigTypeVersion === 'MPCv2') {
return {
...req.body,
customEddsaMPCv2SigningRound1GenerationFunction: createCustomEddsaMPCv2SigningRound1Generator(
req.config.externalSignerUrl,
req.params.coin
),
customEddsaMPCv2SigningRound2GenerationFunction: createCustomEddsaMPCv2SigningRound2Generator(
req.config.externalSignerUrl,
req.params.coin
),
customEddsaMPCv2SigningRound3GenerationFunction: createCustomEddsaMPCv2SigningRound3Generator(
req.config.externalSignerUrl,
req.params.coin
),
};
} else {
return {
...req.body,
customCommitmentGeneratingFunction: createCustomCommitmentGenerator(
req.config.externalSignerUrl,
req.params.coin
),
customRShareGeneratingFunction: createCustomRShareGenerator(req.config.externalSignerUrl, req.params.coin),
customGShareGeneratingFunction: createCustomGShareGenerator(req.config.externalSignerUrl, req.params.coin),
};
}
} else if (coin.getMPCAlgorithm() === MPCType.ECDSA) {
if (wallet._wallet.multisigTypeVersion === 'MPCv2') {
return {
Expand Down Expand Up @@ -1776,6 +1821,51 @@ export function createCustomMPCv2SigningRound3Generator(
};
}

export function createCustomEddsaMPCv2SigningRound1Generator(
externalSignerUrl: string,
coin: string
): CustomEddsaMPCv2SigningRound1GeneratingFunction {
return async function (params) {
const { body: result } = await retryPromise(
() => superagent.post(`${externalSignerUrl}/api/v2/${coin}/tssshare/EddsaMPCv2Round1`).type('json').send(params),
(err, tryCount) => {
debug(`failed to connect to external signer (attempt ${tryCount}, error: ${err.message})`);
}
);
return result;
};
}

export function createCustomEddsaMPCv2SigningRound2Generator(
externalSignerUrl: string,
coin: string
): CustomEddsaMPCv2SigningRound2GeneratingFunction {
return async function (params) {
const { body: result } = await retryPromise(
() => superagent.post(`${externalSignerUrl}/api/v2/${coin}/tssshare/EddsaMPCv2Round2`).type('json').send(params),
(err, tryCount) => {
debug(`failed to connect to external signer (attempt ${tryCount}, error: ${err.message})`);
}
);
return result;
};
}

export function createCustomEddsaMPCv2SigningRound3Generator(
externalSignerUrl: string,
coin: string
): CustomEddsaMPCv2SigningRound3GeneratingFunction {
return async function (params) {
const { body: result } = await retryPromise(
() => superagent.post(`${externalSignerUrl}/api/v2/${coin}/tssshare/EddsaMPCv2Round3`).type('json').send(params),
(err, tryCount) => {
debug(`failed to connect to external signer (attempt ${tryCount}, error: ${err.message})`);
}
);
return result;
};
}

export function setupAPIRoutes(app: express.Application, config: Config): void {
// When adding new routes to BitGo Express make sure that you also add the exact same routes to the server. Since
// some customers were confused when calling a BitGo Express route on the BitGo server, we now handle all BitGo
Expand Down
23 changes: 17 additions & 6 deletions modules/express/src/typedRoutes/api/v2/generateShareTSS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export const GenerateShareTSSParams = {
/**
* The type of share to generate. Valid values depend on the MPC algorithm:
* - EDDSA: 'commitment', 'R', 'G'
* - EDDSA MPCv2: 'EddsaMPCv2Round1', 'EddsaMPCv2Round2', 'EddsaMPCv2Round3'
* - ECDSA: 'PaillierModulus', 'K', 'MuDelta', 'S'
* - ECDSA MPCv2: 'MPCv2Round1', 'MPCv2Round2', 'MPCv2Round3'
*/
Expand Down Expand Up @@ -471,8 +472,8 @@ export const EcdsaMuDeltaShareResponse = t.type({
/** ECDSA S share generation response (Step 3) with final signature components */
export const EcdsaSShareResponse = SShare;

/** ECDSA MPCv2 Round 1 response with initial signature share and encrypted session state */
export const EcdsaMPCv2Round1Response = t.type({
/** MPCv2 Round 1 response with initial signature share and encrypted session state (shared by ECDSA and EdDSA) */
export const MPCv2Round1Response = t.type({
/** First round signature share for MPCv2 protocol */
signatureShareRound1: SignatureShareRecord,
/** User's GPG public key for Round 2 communication */
Expand All @@ -483,20 +484,27 @@ export const EcdsaMPCv2Round1Response = t.type({
encryptedUserGpgPrvKey: t.string,
});

/** ECDSA MPCv2 Round 2 response with second signature share and session state */
export const EcdsaMPCv2Round2Response = t.type({
/** MPCv2 Round 2 response with second signature share and session state (shared by ECDSA and EdDSA) */
export const MPCv2Round2Response = t.type({
/** Second round signature share for MPCv2 protocol */
signatureShareRound2: SignatureShareRecord,
/** Encrypted session state to continue to Round 3 */
encryptedRound2Session: t.string,
});

/** ECDSA MPCv2 Round 3 response with final signature share */
export const EcdsaMPCv2Round3Response = t.type({
/** MPCv2 Round 3 response with final signature share (shared by ECDSA and EdDSA) */
export const MPCv2Round3Response = t.type({
/** Signature share for round 3 (final signature) */
signatureShareRound3: SignatureShareRecord,
});

/** @deprecated Use MPCv2Round1Response */
export const EcdsaMPCv2Round1Response = MPCv2Round1Response;
/** @deprecated Use MPCv2Round2Response */
export const EcdsaMPCv2Round2Response = MPCv2Round2Response;
/** @deprecated Use MPCv2Round3Response */
export const EcdsaMPCv2Round3Response = MPCv2Round3Response;

/** Union of all TSS share responses - EDDSA (Commitment/R/G), ECDSA (PaillierModulus/K/MuDelta/S), or MPCv2 (Round1/2/3) */
export const GenerateShareTSSResponse = {
/** Successfully generated TSS share (type depends on MPC algorithm and sharetype parameter) */
Expand All @@ -511,6 +519,9 @@ export const GenerateShareTSSResponse = {
EcdsaMPCv2Round1Response, // ECDSA MPCv2 Round 1
EcdsaMPCv2Round2Response, // ECDSA MPCv2 Round 2
EcdsaMPCv2Round3Response, // ECDSA MPCv2 Round 3
MPCv2Round1Response, // EdDSA MPCv2 Round 1
MPCv2Round2Response, // EdDSA MPCv2 Round 2
MPCv2Round3Response, // EdDSA MPCv2 Round 3
]),
/** Invalid request parameters, missing configuration, or share generation validation failure */
400: BitgoExpressError,
Expand Down
Loading
Loading