Skip to content

Commit a54becd

Browse files
committed
fix(sdk-coin-flrp): update AddressMap creation to match UTXO address order
Ticket: WIN-8279
1 parent 53166e8 commit a54becd

File tree

2 files changed

+378
-6
lines changed

2 files changed

+378
-6
lines changed

modules/sdk-coin-flrp/src/lib/ImportInPTxBuilder.ts

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,6 @@ export class ImportInPTxBuilder extends AtomicTransactionBuilder {
9292
}
9393

9494
// Create proper UnsignedTx wrapper with credentials
95-
const sortedAddresses = [...this.transaction._fromAddresses].sort((a, b) => Buffer.compare(a, b));
96-
const addressMaps = sortedAddresses.map((a, i) => new FlareUtils.AddressMap([[new Address(a), i]]));
97-
98-
// When credentials were extracted, use them directly to preserve existing signatures
9995
// Match avaxp behavior: dynamic ordering based on addressesIndex from UTXO
10096
const txCredentials =
10197
credentials.length > 0
@@ -142,6 +138,42 @@ export class ImportInPTxBuilder extends AtomicTransactionBuilder {
142138
}
143139
});
144140

141+
// Create AddressMaps based on signature slot order (matching credential order), not sorted addresses
142+
// This matches the approach used in credentials: addressesIndex determines signature order
143+
// AddressMaps should map addresses to signature slots in the same order as credentials
144+
const addressMaps = this.transaction._utxos.map((utxo, utxoIdx) => {
145+
const addressMap = new FlareUtils.AddressMap();
146+
147+
// If UTXO has addresses, compute addressesIndex to determine signature order
148+
if (utxo && utxo.addresses && utxo.addresses.length > 0) {
149+
const utxoAddresses = utxo.addresses.map((a) => utils.parseAddress(a));
150+
const addressesIndex = this.transaction._fromAddresses.map((a) =>
151+
utxoAddresses.findIndex((u) => Buffer.compare(Buffer.from(u), Buffer.from(a)) === 0)
152+
);
153+
154+
const firstIndex = this.recoverSigner ? 2 : 0;
155+
const bitgoIndex = 1;
156+
157+
// Determine signature slot order based on addressesIndex (same logic as credentials)
158+
if (addressesIndex[bitgoIndex] < addressesIndex[firstIndex]) {
159+
// Bitgo comes first: slot 0 = bitgo, slot 1 = firstIndex
160+
addressMap.set(new Address(this.transaction._fromAddresses[bitgoIndex]), 0);
161+
addressMap.set(new Address(this.transaction._fromAddresses[firstIndex]), 1);
162+
} else {
163+
// User/recovery comes first: slot 0 = firstIndex, slot 1 = bitgo
164+
addressMap.set(new Address(this.transaction._fromAddresses[firstIndex]), 0);
165+
addressMap.set(new Address(this.transaction._fromAddresses[bitgoIndex]), 1);
166+
}
167+
} else {
168+
// Fallback: map addresses sequentially if no UTXO addresses available
169+
this.transaction._fromAddresses.slice(0, this.transaction._threshold).forEach((addr, i) => {
170+
addressMap.set(new Address(addr), i);
171+
});
172+
}
173+
174+
return addressMap;
175+
});
176+
145177
const unsignedTx = new UnsignedTx(importTx, [], new FlareUtils.AddressMaps(addressMaps), txCredentials);
146178

147179
this.transaction.setTransaction(unsignedTx);
@@ -200,8 +232,42 @@ export class ImportInPTxBuilder extends AtomicTransactionBuilder {
200232
inputs // importedInputs (ins)
201233
);
202234

203-
// Create address maps for signing
204-
const addressMaps = this.transaction._fromAddresses.map((a, i) => new FlareUtils.AddressMap([[new Address(a), i]]));
235+
// Create AddressMaps based on signature slot order (matching credential order), not sorted addresses
236+
// This matches the approach used in credentials: addressesIndex determines signature order
237+
// AddressMaps should map addresses to signature slots in the same order as credentials
238+
const addressMaps = credentials.map((credential, credIdx) => {
239+
const addressMap = new FlareUtils.AddressMap();
240+
const utxo = this.transaction._utxos[credIdx];
241+
242+
// If UTXO has addresses, compute addressesIndex to determine signature order
243+
if (utxo && utxo.addresses && utxo.addresses.length > 0) {
244+
const utxoAddresses = utxo.addresses.map((a) => utils.parseAddress(a));
245+
const addressesIndex = this.transaction._fromAddresses.map((a) =>
246+
utxoAddresses.findIndex((u) => Buffer.compare(Buffer.from(u), Buffer.from(a)) === 0)
247+
);
248+
249+
const firstIndex = this.recoverSigner ? 2 : 0;
250+
const bitgoIndex = 1;
251+
252+
// Determine signature slot order based on addressesIndex (same logic as credentials)
253+
if (addressesIndex[bitgoIndex] < addressesIndex[firstIndex]) {
254+
// Bitgo comes first: slot 0 = bitgo, slot 1 = firstIndex
255+
addressMap.set(new Address(this.transaction._fromAddresses[bitgoIndex]), 0);
256+
addressMap.set(new Address(this.transaction._fromAddresses[firstIndex]), 1);
257+
} else {
258+
// User/recovery comes first: slot 0 = firstIndex, slot 1 = bitgo
259+
addressMap.set(new Address(this.transaction._fromAddresses[firstIndex]), 0);
260+
addressMap.set(new Address(this.transaction._fromAddresses[bitgoIndex]), 1);
261+
}
262+
} else {
263+
// Fallback: map addresses sequentially if no UTXO addresses available
264+
this.transaction._fromAddresses.slice(0, this.transaction._threshold).forEach((addr, i) => {
265+
addressMap.set(new Address(addr), i);
266+
});
267+
}
268+
269+
return addressMap;
270+
});
205271

206272
// Create unsigned transaction
207273
const unsignedTx = new UnsignedTx(

0 commit comments

Comments
 (0)