Skip to content

Commit cee918b

Browse files
committed
fix(sdk-coin-avaxp): update utxo selection to also check STAKEABLE_LOCK_OUT
noticed that some utxo will be of type 22 with locktim 0 those are eligible to be used for transactions TICKET: SC-3257
1 parent 50f69f6 commit cee918b

File tree

7 files changed

+32
-43
lines changed

7 files changed

+32
-43
lines changed

modules/sdk-coin-avaxp/src/lib/atomicTransactionBuilder.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
} from 'avalanche/dist/apis/platformvm';
1212
import { Credential } from 'avalanche/dist/common';
1313
import { BuildTransactionError } from '@bitgo/sdk-core';
14-
import { SECP256K1_Transfer_Output } from './iface';
14+
import { SECP256K1_STAKEABLE_LOCK_OUT, SECP256K1_Transfer_Output } from './iface';
1515

1616
/**
1717
* Cross-chain transactions (export and import) are atomic operations.
@@ -104,7 +104,10 @@ export abstract class AtomicTransactionBuilder extends DeprecatedTransactionBuil
104104
});
105105

106106
this.transaction._utxos.forEach((utxo, i) => {
107-
if (utxo.outputID === SECP256K1_Transfer_Output) {
107+
if (
108+
utxo.outputID === SECP256K1_Transfer_Output ||
109+
(utxo.outputID === SECP256K1_STAKEABLE_LOCK_OUT && utxo.locktime === '0')
110+
) {
108111
const txidBuf = utils.cb58Decode(utxo.txid);
109112
const amt: BN = new BN(utxo.amount);
110113
const outputidx = utils.outputidxNumberToBuffer(utxo.outputidx);

modules/sdk-coin-avaxp/src/lib/delegatorTxBuilder.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
UnsignedTx,
1717
} from 'avalanche/dist/apis/platformvm';
1818
import { BinTools, BN } from 'avalanche';
19-
import { SECP256K1_Transfer_Output, DeprecatedTx, DeprecatedBaseTx } from './iface';
19+
import { SECP256K1_Transfer_Output, DeprecatedTx, DeprecatedBaseTx, SECP256K1_STAKEABLE_LOCK_OUT } from './iface';
2020
import utils from './utils';
2121
import { Credential } from 'avalanche/dist/common';
2222
import { deprecatedRecoverUtxos } from './utxoEngine';
@@ -309,7 +309,10 @@ export class DelegatorTxBuilder extends DeprecatedTransactionBuilder {
309309
const buildOutputs = this.transaction._utxos[0].addresses.length !== 0;
310310

311311
this.transaction._utxos.forEach((utxo, i) => {
312-
if (utxo.outputID === SECP256K1_Transfer_Output) {
312+
if (
313+
utxo.outputID === SECP256K1_Transfer_Output ||
314+
(utxo.outputID === SECP256K1_STAKEABLE_LOCK_OUT && utxo.locktime === '0')
315+
) {
313316
const txidBuf = utils.cb58Decode(utxo.txid);
314317
const amt: BN = new BN(utxo.amount);
315318
const outputidx = utils.outputidxNumberToBuffer(utxo.outputidx);

modules/sdk-coin-avaxp/src/lib/iface.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export interface TxData {
5050
*/
5151
export type DecodedUtxoObj = {
5252
outputID: number;
53+
locktime?: string;
5354
amount: string;
5455
txid: string;
5556
outputidx: string;
@@ -61,9 +62,14 @@ export type DecodedUtxoObj = {
6162
/**
6263
* TypeId value for SECP256K1 Transfer Output
6364
*
64-
* {@link https://docs.avax.network/specs/platform-transaction-serialization#secp256k1-transfer-output-example }
65+
* {@link https://build.avax.network/docs/api-reference/p-chain/txn-format#secp256k1-transfer-output }
6566
*/
6667
export const SECP256K1_Transfer_Output = 7;
68+
/**
69+
* TypeId value for Stakeable Lock Output
70+
* {@link https://build.avax.network/docs/api-reference/p-chain/txn-format#stakeablelockout }
71+
*/
72+
export const SECP256K1_STAKEABLE_LOCK_OUT = 22;
6773

6874
export const ADDRESS_SEPARATOR = '~';
6975
export const INPUT_SEPARATOR = ':';

modules/sdk-coin-avaxp/src/lib/permissionlessValidatorTxBuilder.ts

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import {
3030
import { BaseCoin as CoinConfig } from '@bitgo/statics';
3131
import { Buffer as BufferAvax } from 'avalanche';
3232
import BigNumber from 'bignumber.js';
33-
import { DecodedUtxoObj, SECP256K1_Transfer_Output, Tx } from './iface';
33+
import { DecodedUtxoObj, SECP256K1_STAKEABLE_LOCK_OUT, SECP256K1_Transfer_Output, Tx } from './iface';
3434
import { KeyPair } from './keyPair';
3535
import { Transaction } from './transaction';
3636
import { TransactionBuilder } from './transactionBuilder';
@@ -291,9 +291,6 @@ export class PermissionlessValidatorTxBuilder extends TransactionBuilder {
291291
this.transaction._stakeAmount = permissionlessValidatorTx.stake[0].output.amount();
292292
this.stakeAmount(this.transaction._stakeAmount);
293293
this.transaction._utxos = recoverUtxos(permissionlessValidatorTx.getInputs());
294-
// TODO(CR-1073): remove log
295-
console.log('utxos: ', this.transaction._utxos);
296-
console.log('fromAddresses: ', this.transaction.fromAddresses);
297294
return this;
298295
}
299296

@@ -338,8 +335,6 @@ export class PermissionlessValidatorTxBuilder extends TransactionBuilder {
338335
const bitgoAddresses = this.transaction._fromAddresses.map((b) =>
339336
avaxUtils.format(this.transaction._network.alias, this.transaction._network.hrp, b)
340337
);
341-
// TODO(CR-1073): remove log
342-
console.log(`bitgoAddress: ${bitgoAddresses}`);
343338

344339
// if we are in OVC, none of the utxos will have addresses since they come from
345340
// deserialized inputs (which don't have addresses), not the IMS
@@ -371,12 +366,12 @@ export class PermissionlessValidatorTxBuilder extends TransactionBuilder {
371366
utxo.addresses.forEach((a) => {
372367
bitgoIndexToOnChainIndex.set(bitgoAddresses.indexOf(a), utxo.addresses.indexOf(a));
373368
});
374-
// TODO(CR-1073): remove log
375-
console.log(`utxo.addresses: ${utxo.addresses}`);
376-
console.log(`bitgoIndexToOnChainIndex: ${Array.from(bitgoIndexToOnChainIndex)}`);
377369
// in OVC, output.addressesIndex is defined correctly from the previous iteration
378370

379-
if (utxo.outputID === SECP256K1_Transfer_Output) {
371+
if (
372+
utxo.outputID === SECP256K1_Transfer_Output ||
373+
(utxo.outputID === SECP256K1_STAKEABLE_LOCK_OUT && utxo.locktime === '0')
374+
) {
380375
const utxoAmount = BigInt(utxo.amount);
381376
// either user (0) or recovery (2)
382377
// On regular mode: [user, bitgo] (i.e. [0, 1])
@@ -400,8 +395,6 @@ export class PermissionlessValidatorTxBuilder extends TransactionBuilder {
400395
new BigIntPr(utxoAmount),
401396
new Input([...addressesIndex].sort().map((num) => new Int(num)))
402397
);
403-
// TODO(CR-1073): remove log
404-
console.log(`using addressesIndex sorted: ${[...addressesIndex].sort()}`);
405398

406399
const input = new avaxSerial.TransferableInput(utxoId, assetId, transferInputs);
407400
utxos.push(new Utxo(utxoId, assetId, transferInputs));
@@ -413,12 +406,7 @@ export class PermissionlessValidatorTxBuilder extends TransactionBuilder {
413406
// For the user/backup signature we store the address that matches the key
414407
// if bitgo address comes before < user/backup address
415408

416-
// TODO(CR-1073): remove log
417-
console.log(`bitgo index on chain: ${utxo.addressesIndex[bitgoIndex]}`);
418-
console.log(`user Or Backup Index: ${utxo.addressesIndex[userOrBackupIndex]}`);
419409
if (utxo.addressesIndex[bitgoIndex] < utxo.addressesIndex[userOrBackupIndex]) {
420-
// TODO(CR-1073): remove log
421-
console.log(`user or backup credentials after bitgo`);
422410
credentials.push(
423411
new Credential([
424412
utils.createNewSig(BufferAvax.from('').toString('hex')),
@@ -428,8 +416,6 @@ export class PermissionlessValidatorTxBuilder extends TransactionBuilder {
428416
])
429417
);
430418
} else {
431-
// TODO(CR-1073): remove log
432-
console.log(`user or backup credentials before bitgo`);
433419
credentials.push(
434420
new Credential([
435421
utils.createNewSig(
@@ -440,7 +426,6 @@ export class PermissionlessValidatorTxBuilder extends TransactionBuilder {
440426
);
441427
}
442428
} else {
443-
// TODO(CR-1073): verify this else case for OVC
444429
credentials.push(
445430
new Credential(
446431
addressesIndex.map((i) =>
@@ -449,9 +434,6 @@ export class PermissionlessValidatorTxBuilder extends TransactionBuilder {
449434
)
450435
);
451436
}
452-
} else {
453-
// TODO(CR-1073): remove log
454-
console.log(`reusing credentials from transaction`);
455437
}
456438
}
457439
});
@@ -542,9 +524,8 @@ export class PermissionlessValidatorTxBuilder extends TransactionBuilder {
542524
.map((a) => Address.fromBytes(a)[0])
543525
);
544526

545-
// TODO(CR-1073): check this value
546-
// Shares 10,000 times percentage of reward taken from delegators
547-
// https://docs.avax.network/reference/avalanchego/p-chain/txn-format#unsigned-add-validator-tx
527+
// Shares 10,000 times percentage of reward taken from delegators
528+
// https://docs.avax.network/reference/avalanchego/p-chain/txn-format#unsigned-add-validator-tx
548529
const shares = new Int(1e4 * 2);
549530

550531
const addressMaps = [...this.transaction._fromAddresses]

modules/sdk-coin-avaxp/src/lib/transactionBuilder.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -131,21 +131,12 @@ export abstract class TransactionBuilder extends BaseTransactionBuilder {
131131
return this;
132132
}
133133

134-
// TODO(CR-1073):
135-
// Implement:
136-
// buildImplementation
137-
// signImplementation
138-
// get transaction
139-
// set transaction
140-
// validateRawTransaction
141-
142134
/** @inheritdoc */
143135
protected fromImplementation(rawTransaction: string): Transaction {
144136
const [tx] = pvmSerial.AddPermissionlessValidatorTx.fromBytes(
145137
Buffer.from(rawTransaction, 'hex'),
146138
avmSerial.getAVMManager().getDefaultCodec()
147139
);
148-
// TODO(CR-1073): check if initBuilder can only use UnsignedTx and pvmSerial.BaseTx is not required
149140
this.initBuilder(tx);
150141
return this._transaction;
151142
}

modules/sdk-coin-avaxp/src/lib/utxoEngine.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { DecodedUtxoObj, SECP256K1_Transfer_Output } from './iface';
1+
import { DecodedUtxoObj, SECP256K1_STAKEABLE_LOCK_OUT, SECP256K1_Transfer_Output } from './iface';
22
import { BN, Buffer as BufferAvax } from 'avalanche';
33
import { Signature } from 'avalanche/dist/common';
44
import utils from './utils';
@@ -98,7 +98,12 @@ export function utxoToInput(
9898
let currentTotal: BN = new BN(0);
9999

100100
const inputs = utxos
101-
.filter((utxo) => utxo && utxo.outputID === SECP256K1_Transfer_Output)
101+
.filter(
102+
(utxo) =>
103+
utxo &&
104+
(utxo.outputID === SECP256K1_Transfer_Output ||
105+
(utxo.outputID === SECP256K1_STAKEABLE_LOCK_OUT && utxo.locktime === '0'))
106+
)
102107
.map((utxo) => {
103108
// validate the utxos
104109
const utxoAddresses: BufferAvax[] = utxo.addresses.map((a) => utils.parseAddress(a));

modules/sdk-coin-avaxp/test/resources/avaxp.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,7 @@ export const BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE = {
622622
'0xa94d6182edbd953516b262f17565a65d98f5741549cd70d2423abff750bb4b8d982d482376b189142ff8aa4705615fee14be6174610860e9c003aa4aeaa613b1732abf3cd0c9c42fa5856345644068c0d1f9fa1d9af32e20b14fca02983260bc',
623623
utxos: [
624624
{
625-
outputID: 7,
625+
outputID: 22,
626626
amount: '98000000',
627627
txid: 's92SjoZQemgG97HocX9GgyFy6ZKmapgcgqQ3y5J2uwP3qWBUy',
628628
threshold: 2,

0 commit comments

Comments
 (0)