Skip to content
Open
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-coin-apechain/src/lib/transactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ export class TransactionBuilder extends AbstractTransactionBuilder {
}

/** @inheritdoc */
transfer(data?: string): TransferBuilder {
transfer(data?: string, isFirstSigner?: boolean): TransferBuilder {
if (this._type !== TransactionType.Send) {
throw new BuildTransactionError('Transfers can only be set for send transactions');
}
if (!this._transfer) {
this._transfer = new TransferBuilder(data);
this._transfer = new TransferBuilder(data, isFirstSigner);
}
return this._transfer;
}
Expand Down
4 changes: 2 additions & 2 deletions modules/sdk-coin-arbeth/src/lib/transactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ export class TransactionBuilder extends EthLikeTransactionBuilder {
}

/** @inheritdoc */
transfer(data?: string): TransferBuilder {
transfer(data?: string, isFirstSigner?: boolean): TransferBuilder {
if (this._type !== TransactionType.Send) {
throw new BuildTransactionError('Transfers can only be set for send transactions');
}
if (!this._transfer) {
this._transfer = new TransferBuilder(data);
this._transfer = new TransferBuilder(data, isFirstSigner);
}
return this._transfer;
}
Expand Down
4 changes: 2 additions & 2 deletions modules/sdk-coin-avaxc/src/lib/transactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ export class TransactionBuilder extends EthTransactionBuilder {
}

/** @inheritdoc */
transfer(data?: string): TransferBuilder {
transfer(data?: string, isFirstSigner?: boolean): TransferBuilder {
if (this._type !== TransactionType.Send) {
throw new BuildTransactionError('Transfers can only be set for send transactions');
}
if (!this._transfer) {
this._transfer = new TransferBuilder(data);
this._transfer = new TransferBuilder(data, isFirstSigner);
}
return this._transfer;
}
Expand Down
4 changes: 2 additions & 2 deletions modules/sdk-coin-bera/src/lib/transactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ export class TransactionBuilder extends EthLikeTransactionBuilder {
}

/** @inheritdoc */
transfer(data?: string): TransferBuilder {
transfer(data?: string, isFirstSigner?: boolean): TransferBuilder {
if (this._type !== TransactionType.Send) {
throw new BuildTransactionError('Transfers can only be set for send transactions');
}
if (!this._transfer) {
this._transfer = new TransferBuilder(data);
this._transfer = new TransferBuilder(data, isFirstSigner);
}
return this._transfer;
}
Expand Down
4 changes: 2 additions & 2 deletions modules/sdk-coin-bsc/src/lib/transactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ export class TransactionBuilder extends AbstractTransactionBuilder {
}

/** @inheritdoc */
transfer(data?: string): TransferBuilder {
transfer(data?: string, isFirstSigner?: boolean): TransferBuilder {
if (this._type !== TransactionType.Send) {
throw new BuildTransactionError('Transfers can only be set for send transactions');
}
if (!this._transfer) {
this._transfer = new TransferBuilder(data);
this._transfer = new TransferBuilder(data, isFirstSigner);
}
return this._transfer;
}
Expand Down
4 changes: 2 additions & 2 deletions modules/sdk-coin-celo/src/lib/transactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,12 +174,12 @@ export class TransactionBuilder extends EthTransactionBuilder {
}

/** @inheritdoc */
transfer(data?: string): TransferBuilder {
transfer(data?: string, isFirstSigner?: boolean): TransferBuilder {
if (this._type !== TransactionType.Send) {
throw new BuildTransactionError('Transfers can only be set for send transactions');
}
if (!this._transfer) {
this._transfer = new TransferBuilder(data);
this._transfer = new TransferBuilder(data, isFirstSigner);
}
return this._transfer;
}
Expand Down
4 changes: 2 additions & 2 deletions modules/sdk-coin-coredao/src/lib/transactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ export class TransactionBuilder extends AbstractTransactionBuilder {
}

/** @inheritdoc */
transfer(data?: string): TransferBuilder {
transfer(data?: string, isFirstSigner?: boolean): TransferBuilder {
if (this._type !== TransactionType.Send) {
throw new BuildTransactionError('Transfers can only be set for send transactions');
}
if (!this._transfer) {
this._transfer = new TransferBuilder(data);
this._transfer = new TransferBuilder(data, isFirstSigner);
}
return this._transfer;
}
Expand Down
4 changes: 2 additions & 2 deletions modules/sdk-coin-etc/src/lib/transactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ export class TransactionBuilder extends EthTransactionBuilder {
}

/** @inheritdoc */
transfer(data?: string): TransferBuilder {
transfer(data?: string, isFirstSigner?: boolean): TransferBuilder {
if (this._type !== TransactionType.Send) {
throw new BuildTransactionError('Transfers can only be set for send transactions');
}
if (!this._transfer) {
this._transfer = new TransferBuilder(data);
this._transfer = new TransferBuilder(data, isFirstSigner);
}
return this._transfer;
}
Expand Down
78 changes: 78 additions & 0 deletions modules/sdk-coin-etc/test/unit/transactionBuilder/send.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,81 @@ describe('should sign and build from serialized', () => {
should.equal(signedTx.toBroadcastFormat(), testData.SEND_TX_AMOUNT_ZERO_BROADCAST);
});
});

/**
* Regression tests for WCN-560: ETC TransactionBuilder.transfer() must pass isFirstSigner to the
* TransferBuilder so that from(txHex, true) correctly decodes first-signer calldata, and
* getHalfSignedTxByFirstSigner produces a valid second-signer half-signed tx (instead of treating
* the ABI string-offset 0xC0 as the recipient address).
*/
describe('ETC first-signer round-trip (WCN-560 regression)', () => {
const contractAddress = '0x7073b82be1d932c70afe505e1fe211916e978c34';
const recipient = testData.ACCOUNT_2; // '0x33ffaefff29455fbcb1f7ddabb6ef48f4dd87536'
const amount = '1000000000';
const expireTime = 1590066728;
const sequenceId = 5;
const key = testData.KEYPAIR_PRV.getKeys().prv as string;

/** Build an unsigned first-signer tx with known parameters and return its hex. */
async function buildFirstSignerTxHex(): Promise<string> {
const txBuilder = getBuilder('tetc') as TransactionBuilder;
txBuilder.fee({ fee: '1000000000', gasLimit: '12100000' });
txBuilder.counter(2);
txBuilder.type(TransactionType.Send);
txBuilder.contract(contractAddress);
const transfer = txBuilder.transfer() as any;
transfer.amount(amount).to(recipient).expirationTime(expireTime).contractSequenceId(sequenceId).key(key);
transfer.isFirstSigner(true);
const tx = await txBuilder.build();
return tx.toBroadcastFormat();
}

it('from(firstSignerTxHex, true) decodes recipient and amount correctly (not 0xC0)', async () => {
const firstSignerTxHex = await buildFirstSignerTxHex();

// This was broken before the fix: ETC's transfer() dropped isFirstSigner, so the
// TransferBuilder decoded the first-signer ABI with second-signer offsets, producing
// address=0x...00c0 (the ABI dynamic-string offset) instead of the real recipient.
const txBuilder = getBuilder('tetc') as TransactionBuilder;
txBuilder.from(firstSignerTxHex, true);
const tx = await txBuilder.build();

should.equal(tx.outputs.length, 1);
should.equal(tx.outputs[0].address.toLowerCase(), recipient.toLowerCase());
should.equal(tx.outputs[0].value, amount);

// Explicitly assert the old-bug value is absent
should.notEqual(tx.outputs[0].address.toLowerCase(), '0x00000000000000000000000000000000000000c0');
});

it('full round-trip: first-signer → add inner sig → second-signer half-signed → verify recipient', async () => {
const firstSignerTxHex = await buildFirstSignerTxHex();

// Simulate getHalfSignedTxByFirstSigner:
// 1. Parse the first-signer tx (Trust HSM input)
// 2. Inject the operationHashSig returned by Trust
// 3. Switch to second-signer encoding (removes the method-id prefix string)
const txBuilder = getBuilder('tetc') as TransactionBuilder;
txBuilder.from(firstSignerTxHex, true);
const transfer = txBuilder.transfer() as any;
const mockOperationHashSig = '0x' + '1b'.repeat(65); // 65-byte sig from Trust HSM
transfer.setSignature(mockOperationHashSig);
transfer.isFirstSigner(false);
const halfSignedTx = await txBuilder.build();
const halfSignedTxHex = halfSignedTx.toBroadcastFormat();

// Direct calldata decode must give the correct recipient and amount
const { to, amount: decodedAmount } = decodeTransferData(halfSignedTx.toJson().data);
should.equal(to.toLowerCase(), recipient.toLowerCase());
should.equal(decodedAmount, amount);
// Guard against the old bug: 0xC0 is the ABI string offset, NOT a valid recipient
should.notEqual(to.toLowerCase(), '0x00000000000000000000000000000000000000c0');

// Re-parsing the half-signed tx as second-signer must also yield correct outputs
const verifyBuilder = getBuilder('tetc') as TransactionBuilder;
verifyBuilder.from(halfSignedTxHex);
const verifiedTx = await verifyBuilder.build();
should.equal(verifiedTx.outputs[0].address.toLowerCase(), recipient.toLowerCase());
should.equal(verifiedTx.outputs[0].value, amount);
});
});
4 changes: 2 additions & 2 deletions modules/sdk-coin-ethlike/src/lib/transactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ export class EthLikeTransactionBuilder extends AbstractTransactionBuilder {
}

/** @inheritdoc */
transfer(data?: string): TransferBuilder {
transfer(data?: string, isFirstSigner?: boolean): TransferBuilder {
if (this._type !== TransactionType.Send) {
throw new BuildTransactionError('Transfers can only be set for send transactions');
}
if (!this._transfer) {
this._transfer = new TransferBuilder(data);
this._transfer = new TransferBuilder(data, isFirstSigner);
}
return this._transfer;
}
Expand Down
4 changes: 2 additions & 2 deletions modules/sdk-coin-evm/src/lib/transactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ export class TransactionBuilder extends AbstractTransactionBuilder {
}

/** @inheritdoc */
transfer(data?: string): TransferBuilder {
transfer(data?: string, isFirstSigner?: boolean): TransferBuilder {
if (this._type !== TransactionType.Send) {
throw new BuildTransactionError('Transfers can only be set for send transactions');
}
if (!this._transfer) {
this._transfer = new TransferBuilder(data);
this._transfer = new TransferBuilder(data, isFirstSigner);
}
return this._transfer;
}
Expand Down
4 changes: 2 additions & 2 deletions modules/sdk-coin-flr/src/lib/transactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ export class TransactionBuilder extends AbstractTransactionBuilder {
}

/** @inheritdoc */
transfer(data?: string): TransferBuilder {
transfer(data?: string, isFirstSigner?: boolean): TransferBuilder {
if (this._type !== TransactionType.Send) {
throw new BuildTransactionError('Transfers can only be set for send transactions');
}
if (!this._transfer) {
this._transfer = new TransferBuilder(data);
this._transfer = new TransferBuilder(data, isFirstSigner);
}
return this._transfer;
}
Expand Down
4 changes: 2 additions & 2 deletions modules/sdk-coin-mon/src/lib/transactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ export class TransactionBuilder extends AbstractTransactionBuilder {
}

/** @inheritdoc */
transfer(data?: string): TransferBuilder {
transfer(data?: string, isFirstSigner?: boolean): TransferBuilder {
if (this._type !== TransactionType.Send) {
throw new BuildTransactionError('Transfers can only be set for send transactions');
}
if (!this._transfer) {
this._transfer = new TransferBuilder(data);
this._transfer = new TransferBuilder(data, isFirstSigner);
}
return this._transfer;
}
Expand Down
4 changes: 2 additions & 2 deletions modules/sdk-coin-oas/src/lib/transactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ export class TransactionBuilder extends AbstractTransactionBuilder {
}

/** @inheritdoc */
transfer(data?: string): TransferBuilder {
transfer(data?: string, isFirstSigner?: boolean): TransferBuilder {
if (this._type !== TransactionType.Send) {
throw new BuildTransactionError('Transfers can only be set for send transactions');
}
if (!this._transfer) {
this._transfer = new TransferBuilder(data);
this._transfer = new TransferBuilder(data, isFirstSigner);
}
return this._transfer;
}
Expand Down
4 changes: 2 additions & 2 deletions modules/sdk-coin-opeth/src/lib/transactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ export class TransactionBuilder extends EthLikeTransactionBuilder {
}

/** @inheritdoc */
transfer(data?: string): TransferBuilder {
transfer(data?: string, isFirstSigner?: boolean): TransferBuilder {
if (this._type !== TransactionType.Send) {
throw new BuildTransactionError('Transfers can only be set for send transactions');
}
if (!this._transfer) {
this._transfer = new TransferBuilder(data);
this._transfer = new TransferBuilder(data, isFirstSigner);
}
return this._transfer;
}
Expand Down
4 changes: 2 additions & 2 deletions modules/sdk-coin-polygon/src/lib/transactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ export class TransactionBuilder extends EthLikeTransactionBuilder {
}

/** @inheritdoc */
transfer(data?: string): TransferBuilder {
transfer(data?: string, isFirstSigner?: boolean): TransferBuilder {
if (this._type !== TransactionType.Send) {
throw new BuildTransactionError('Transfers can only be set for send transactions');
}
if (!this._transfer) {
this._transfer = new TransferBuilder(data);
this._transfer = new TransferBuilder(data, isFirstSigner);
}
return this._transfer;
}
Expand Down
4 changes: 2 additions & 2 deletions modules/sdk-coin-rbtc/src/lib/transactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ export class TransactionBuilder extends EthTransactionBuilder {
}

/** @inheritdoc */
transfer(data?: string): TransferBuilder {
transfer(data?: string, isFirstSigner?: boolean): TransferBuilder {
if (this._type !== TransactionType.Send) {
throw new BuildTransactionError('Transfers can only be set for send transactions');
}
if (!this._transfer) {
this._transfer = new TransferBuilder(data);
this._transfer = new TransferBuilder(data, isFirstSigner);
}
return this._transfer;
}
Expand Down
4 changes: 2 additions & 2 deletions modules/sdk-coin-sgb/src/lib/transactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ export class TransactionBuilder extends AbstractTransactionBuilder {
}

/** @inheritdoc */
transfer(data?: string): TransferBuilder {
transfer(data?: string, isFirstSigner?: boolean): TransferBuilder {
if (this._type !== TransactionType.Send) {
throw new BuildTransactionError('Transfers can only be set for send transactions');
}
if (!this._transfer) {
this._transfer = new TransferBuilder(data);
this._transfer = new TransferBuilder(data, isFirstSigner);
}
return this._transfer;
}
Expand Down
4 changes: 2 additions & 2 deletions modules/sdk-coin-soneium/src/lib/transactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ export class TransactionBuilder extends AbstractTransactionBuilder {
}

/** @inheritdoc */
transfer(data?: string): TransferBuilder {
transfer(data?: string, isFirstSigner?: boolean): TransferBuilder {
if (this._type !== TransactionType.Send) {
throw new BuildTransactionError('Transfers can only be set for send transactions');
}
if (!this._transfer) {
this._transfer = new TransferBuilder(data);
this._transfer = new TransferBuilder(data, isFirstSigner);
}
return this._transfer;
}
Expand Down
4 changes: 2 additions & 2 deletions modules/sdk-coin-stt/src/lib/transactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ export class TransactionBuilder extends AbstractTransactionBuilder {
}

/** @inheritdoc */
transfer(data?: string): TransferBuilder {
transfer(data?: string, isFirstSigner?: boolean): TransferBuilder {
if (this._type !== TransactionType.Send) {
throw new BuildTransactionError('Transfers can only be set for send transactions');
}
if (!this._transfer) {
this._transfer = new TransferBuilder(data);
this._transfer = new TransferBuilder(data, isFirstSigner);
}
return this._transfer;
}
Expand Down
4 changes: 2 additions & 2 deletions modules/sdk-coin-wemix/src/lib/transactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ export class TransactionBuilder extends AbstractTransactionBuilder {
}

/** @inheritdoc */
transfer(data?: string): TransferBuilder {
transfer(data?: string, isFirstSigner?: boolean): TransferBuilder {
if (this._type !== TransactionType.Send) {
throw new BuildTransactionError('Transfers can only be set for send transactions');
}
if (!this._transfer) {
this._transfer = new TransferBuilder(data);
this._transfer = new TransferBuilder(data, isFirstSigner);
}
return this._transfer;
}
Expand Down
4 changes: 2 additions & 2 deletions modules/sdk-coin-world/src/lib/transactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ export class TransactionBuilder extends AbstractTransactionBuilder {
}

/** @inheritdoc */
transfer(data?: string): TransferBuilder {
transfer(data?: string, isFirstSigner?: boolean): TransferBuilder {
if (this._type !== TransactionType.Send) {
throw new BuildTransactionError('Transfers can only be set for send transactions');
}
if (!this._transfer) {
this._transfer = new TransferBuilder(data);
this._transfer = new TransferBuilder(data, isFirstSigner);
}
return this._transfer;
}
Expand Down
Loading
Loading