Skip to content
Merged
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
4 changes: 2 additions & 2 deletions modules/sdk-core/src/bitgo/safe/safe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/
import * as t from 'io-ts';
import { FreezeSafeBody, SafeData, SafeShareData, SafeShareState, type RootKeyType } from '@bitgo/public-types';
import { KeyCurve } from '@bitgo/statics';
import { coins, KeyCurve } from '@bitgo/statics';
import { IBaseCoin } from '../baseCoin';
import { BitGoBase } from '../bitgoBase';
import { IncorrectPasswordError } from '../errors';
Expand Down Expand Up @@ -49,7 +49,7 @@ function onchainSlotForCoin(coin: IBaseCoin): Extract<RootKeyType, 'secp256k1Mul
if (coin.getDefaultMultisigType() === 'tss') {
throw new Error('MPC safe wallet minting is not yet implemented; use a slot-1 onchain coin');
}
const curve = coin.getConfig().primaryKeyCurve;
const curve = coins.get(coin.getChain()).primaryKeyCurve;
if (curve === KeyCurve.Secp256k1) {
return 'secp256k1Multisig';
}
Expand Down
2 changes: 1 addition & 1 deletion modules/sdk-core/src/bitgo/safe/safeDerivation.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* @prettier
*
* Shared safe child derivation for mint and sign.
* @experimental Shared safe child derivation for mint and sign.
*
* User child: hardened `m/<index>'` from the sequential `safe.derivationIndex[slot]`.
* Backup / BitGo children are soft-derived server-side at `m/<index>` (not here).
Expand Down
5 changes: 3 additions & 2 deletions modules/sdk-core/src/bitgo/wallet/iWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1016,8 +1016,8 @@ export interface WalletData {
evmKeyRingReferenceWalletId?: string;
isParent?: boolean;
enabledChildChains?: string[];
/** Set on child wallets that belong to a safe. */
safeId?: string;
/** @experimental Public id of the parent safe this wallet was minted in */
safe?: string;
/**
* @deprecated Read from `coinSpecific.userKeySigningRequired` instead. Retained
* temporarily as a fallback while the field migrates from the top level to the OFC
Expand Down Expand Up @@ -1187,6 +1187,7 @@ export interface IWallet {
subType(): SubWalletType | undefined;
multisigType(): 'onchain' | 'tss';
multisigTypeVersion(): 'MPCv2' | undefined;
/** @experimental Public id of the parent safe this wallet was minted in */
safeId(): string | undefined;
label(): string;
keyIds(): string[];
Expand Down
6 changes: 5 additions & 1 deletion modules/sdk-core/src/bitgo/wallet/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,8 +385,12 @@ export class Wallet implements IWallet {
return this._wallet.multisigTypeVersion;
}

/**
* Public id of the parent safe, if this wallet was minted in one.
* @experimental
*/
safeId(): string | undefined {
return this._wallet.safeId;
return this._wallet.safe;
}

subType(): SubWalletType | undefined {
Expand Down
13 changes: 6 additions & 7 deletions modules/sdk-core/test/unit/bitgo/safe/safe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ describe('Safe', function () {
let derivationQuery: sinon.SinonStub;
let mintSend: sinon.SinonStub;

function stubCoin(primaryKeyCurve: string, opts: { getDefaultMultisigType?: string } = {}) {
function stubCoin(chain: string, opts: { getDefaultMultisigType?: string } = {}) {
keychainsGet = sinon.stub().resolves({
id: 'user-root-id',
source: 'user',
Expand All @@ -143,16 +143,15 @@ describe('Safe', function () {
});
keychainsAdd = sinon.stub().resolves({ id: 'child-key-id', pub: childAt0.pub, type: 'independent' });
mockBitGo.coin = sinon.stub().returns({
getChain: sinon.stub().returns('tbtc'),
getConfig: sinon.stub().returns({ primaryKeyCurve }),
getChain: sinon.stub().returns(chain),
getDefaultMultisigType: sinon.stub().returns(opts.getDefaultMultisigType),
supportsTss: sinon.stub().returns(false),
keychains: sinon.stub().returns({ get: keychainsGet, add: keychainsAdd }),
});
}

beforeEach(function () {
stubCoin('secp256k1');
stubCoin('tbtc');
mockBitGo.decrypt = sinon.stub().callsFake(({ input, password }: { input: string; password: string }) => {
if (password !== 'pw') {
throw new Error('bad password');
Expand All @@ -173,7 +172,7 @@ describe('Safe', function () {
type: 'hot',
multisigType: 'onchain',
enterprise: 'test-enterprise-id',
safeId: 'test-safe-id',
safe: 'test-safe-id',
});
mintSend = sinon.stub().returns({ result: mintResult });
mockBitGo.post.returns({ send: mintSend });
Expand Down Expand Up @@ -219,7 +218,7 @@ describe('Safe', function () {
});

it('rejects a TSS-default coin even without multisigType tss', async function () {
stubCoin('secp256k1', { getDefaultMultisigType: 'tss' });
stubCoin('hteth', { getDefaultMultisigType: 'tss' });
await safe
.createWallet({ coin: 'hteth', label: 'evm', passphrase: 'pw' })
.should.be.rejectedWith(/MPC safe wallet minting is not yet implemented/);
Expand All @@ -235,7 +234,7 @@ describe('Safe', function () {
});

it('rejects ed25519 onchain coins', async function () {
stubCoin('ed25519');
stubCoin('txlm');
await safe
.createWallet({ coin: 'txlm', label: 'xlm', passphrase: 'pw' })
.should.be.rejectedWith(/ed25519 coin safe wallet minting is not yet supported/);
Expand Down
22 changes: 11 additions & 11 deletions modules/sdk-core/test/unit/bitgo/wallet/safeGetUserPrv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ describe('WCN-1200 safe child getUserPrv root-fetch detour', function () {
});

it('throws for a safe wallet when keychain has no parent', async function () {
const wallet = makeWallet({ safeId: 'safe-id-1' });
const wallet = makeWallet({ safe: 'safe-id-1' });
await wallet
.getUserPrv({
keychain: {
Expand All @@ -228,7 +228,7 @@ describe('WCN-1200 safe child getUserPrv root-fetch detour', function () {
});

it('fetches root and hardened-derives child key for safe owner', async function () {
const wallet = makeWallet({ safeId: 'safe-id-1' });
const wallet = makeWallet({ safe: 'safe-id-1' });
keychainsGetStub.resolves({
id: rootKeyId,
source: 'user',
Expand All @@ -254,7 +254,7 @@ describe('WCN-1200 safe child getUserPrv root-fetch detour', function () {
});

it('fails closed for TSS safe owner instead of returning the root prv', async function () {
const wallet = makeWallet({ safeId: 'safe-id-1', multisigType: 'tss' });
const wallet = makeWallet({ safe: 'safe-id-1', multisigType: 'tss' });

await wallet
.getUserPrv({
Expand All @@ -275,7 +275,7 @@ describe('WCN-1200 safe child getUserPrv root-fetch detour', function () {

it('fails closed for ed25519 onchain safe owner instead of BIP32-deriving', async function () {
mockBaseCoin.getFamily.returns('xlm');
const wallet = makeWallet({ safeId: 'safe-id-1', coin: 'txlm' });
const wallet = makeWallet({ safe: 'safe-id-1', coin: 'txlm' });

await wallet
.getUserPrv({
Expand All @@ -294,7 +294,7 @@ describe('WCN-1200 safe child getUserPrv root-fetch detour', function () {
});

it('aborts locally when derived pub does not match registered child pub', async function () {
const wallet = makeWallet({ safeId: 'safe-id-1' });
const wallet = makeWallet({ safe: 'safe-id-1' });
keychainsGetStub.resolves({
id: rootKeyId,
source: 'user',
Expand All @@ -319,7 +319,7 @@ describe('WCN-1200 safe child getUserPrv root-fetch detour', function () {

it('decrypts child encryptedPrv as-is for wallet sharee (hardened child prv)', async function () {
const childPrv = 'child-level-prv';
const wallet = makeWallet({ safeId: 'safe-id-1' });
const wallet = makeWallet({ safe: 'safe-id-1' });

const result = await wallet.getUserPrv({
keychain: {
Expand All @@ -340,7 +340,7 @@ describe('WCN-1200 safe child getUserPrv root-fetch detour', function () {

it('does not auto-populate coldDerivationSeed when explicit prv and encryptedPrv are present', async function () {
const childPrv = 'child-level-prv';
const wallet = makeWallet({ safeId: 'safe-id-1' });
const wallet = makeWallet({ safe: 'safe-id-1' });

const result = await wallet.getUserPrv({
prv: childPrv,
Expand Down Expand Up @@ -410,7 +410,7 @@ describe('WCN-1200 safe child getUserPrv root-fetch detour', function () {

describe('getEncryptedUserKeychain', function () {
it('still fails for a safe owner so wallet sharing cannot obtain root material', async function () {
const wallet = makeWallet({ safeId: 'safe-id-1' });
const wallet = makeWallet({ safe: 'safe-id-1' });
keychainsGetStub.resolves({
id: 'user-key',
pub: 'child-pub',
Expand All @@ -427,7 +427,7 @@ describe('WCN-1200 safe child getUserPrv root-fetch detour', function () {
describe('signing guards', function () {
it('getUserKeyAndSignTssTransaction allows safe child keychain without encryptedPrv', async function () {
const wallet = makeWallet({
safeId: 'safe-id-1',
safe: 'safe-id-1',
multisigType: 'tss',
type: 'hot',
});
Expand Down Expand Up @@ -464,7 +464,7 @@ describe('WCN-1200 safe child getUserPrv root-fetch detour', function () {

it('getUserKeyAndSignTssTransaction rejects wrong passphrase early for safe child wallets', async function () {
const wallet = makeWallet({
safeId: 'safe-id-1',
safe: 'safe-id-1',
multisigType: 'tss',
type: 'hot',
});
Expand Down Expand Up @@ -501,7 +501,7 @@ describe('WCN-1200 safe child getUserPrv root-fetch detour', function () {

it('signTransaction does not pass the root prv into TSS signing for a safe owner', async function () {
const wallet = makeWallet({
safeId: 'safe-id-1',
safe: 'safe-id-1',
multisigType: 'tss',
type: 'hot',
});
Expand Down
Loading