|
8 | 8 | * --coin tbtc \ |
9 | 9 | * --walletId <walletId> \ |
10 | 10 | * --passphrase <walletPassphrase> \ |
11 | | - * --accessToken <bearerToken> \ |
| 11 | + * --accessToken <sessionToken> \ |
12 | 12 | * [--otp <code>] \ |
13 | 13 | * [--boxD <ciphertext>] \ |
14 | 14 | * [--boxA <ciphertext>] \ |
@@ -157,9 +157,9 @@ function parseArgs() { |
157 | 157 | .option('passphrase', { type: 'string', demandOption: true, description: 'Current wallet passphrase' }) |
158 | 158 | .option('accessToken', { type: 'string', demandOption: true, description: 'Short-lived BitGo access token' }) |
159 | 159 | .option('otp', { type: 'string', description: 'OTP for session unlock' }) |
160 | | - .option('boxD', { type: 'string', description: 'Box D ciphertext from the original keycard' }) |
161 | | - .option('boxA', { type: 'string', description: 'Box A ciphertext from the original keycard (MPCv2)' }) |
162 | | - .option('boxB', { type: 'string', description: 'Box B ciphertext from the original keycard' }) |
| 160 | + .option('boxD', { type: 'string', description: 'Box D ciphertext from the original keycard', coerce: (v: string) => v?.replace(/\s/g, '') }) |
| 161 | + .option('boxA', { type: 'string', description: 'Box A ciphertext from the original keycard (MPCv2)', coerce: (v: string) => v?.replace(/\s/g, '') }) |
| 162 | + .option('boxB', { type: 'string', description: 'Box B ciphertext from the original keycard', coerce: (v: string) => v?.replace(/\s/g, '') }) |
163 | 163 | .option('passcodeEncryptionCode', { |
164 | 164 | type: 'string', |
165 | 165 | description: 'Passcode encryption code (fetched automatically if omitted)', |
@@ -204,16 +204,33 @@ async function main() { |
204 | 204 | if (dryRun) console.log('[dry-run] No changes will be persisted.'); |
205 | 205 |
|
206 | 206 | const bitgo = new BitGo({ env }); |
| 207 | + |
207 | 208 | bitgo.authenticateWithAccessToken({ accessToken }); |
208 | 209 |
|
209 | 210 | if (!dryRun) { |
210 | | - await bitgo.unlock({ otp: otp ?? '0000000', duration: 600 }); |
211 | | - console.log('Session unlocked.'); |
| 211 | + try { |
| 212 | + await bitgo.unlock({ otp: otp ?? '0000000', duration: 600 }); |
| 213 | + console.log('Session unlocked.'); |
| 214 | + } catch (err: unknown) { |
| 215 | + const msg = err instanceof Error ? err.message : String(err); |
| 216 | + if (msg.includes('already unlocked longer')) { |
| 217 | + console.log('Session already unlocked (longer duration) — proceeding.'); |
| 218 | + } else { |
| 219 | + throw err; |
| 220 | + } |
| 221 | + } |
212 | 222 | } |
213 | 223 |
|
214 | 224 | const wallet = await bitgo.coin(coin).wallets().get({ id: walletId }); |
215 | 225 | console.log(`Wallet: ${wallet.label()} (${walletId})`); |
216 | 226 |
|
| 227 | + if (wallet.multisigTypeVersion() === 'MPCv2' && (!boxA || !boxB)) { |
| 228 | + throw new Error( |
| 229 | + 'This is an MPCv2 wallet. --boxA and --boxB are required to re-encrypt the reducedEncryptedPrv ' + |
| 230 | + 'key shares stored on the keycard. Without them the new keycard will be missing Box A and Box B.' |
| 231 | + ); |
| 232 | + } |
| 233 | + |
217 | 234 | // Fetch passcodeEncryptionCode — needed to decrypt Box D (when provided) and to generate Box D |
218 | 235 | // in the new keycard. Always fetched when boxD is provided; otherwise skipped in dry-run. |
219 | 236 | const needsPec = boxD || !dryRun; |
|
0 commit comments