Skip to content

Commit 5641490

Browse files
Merge pull request #7701 from BitGo/WIN-8042-avaxc-usdc-signer-issue
fix(express): support legacy EIP1559 transaction in type valid…
2 parents 68a411b + 6c5e13f commit 5641490

File tree

2 files changed

+88
-4
lines changed

2 files changed

+88
-4
lines changed

modules/express/src/typedRoutes/api/v2/coinSignTx.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,19 @@ export const CoinSignTxParams = {
1414
/**
1515
* EIP1559 transaction parameters for Ethereum
1616
* Reference: modules/abstract-eth/src/abstractEthLikeNewCoins.ts:116-119
17-
* Note: Both fields are REQUIRED when EIP1559 object is provided
17+
*
18+
* Note: Changed to t.partial() to support multiple use cases:
19+
* 1. Full EIP1559 transactions: { maxFeePerGas, maxPriorityFeePerGas }
20+
* 2. Legacy token transactions: { isEip1559: false } (SDK marker for non-EIP1559)
21+
* 3. Legacy base coin transactions: undefined (field omitted entirely)
1822
*/
19-
export const EIP1559 = t.type({
20-
/** Maximum priority fee per gas (REQUIRED) */
23+
export const EIP1559 = t.partial({
24+
/** Maximum priority fee per gas */
2125
maxPriorityFeePerGas: t.union([t.string, t.number]),
22-
/** Maximum fee per gas (REQUIRED) */
26+
/** Maximum fee per gas */
2327
maxFeePerGas: t.union([t.string, t.number]),
28+
/** Flag indicating whether transaction uses EIP1559 fee model */
29+
isEip1559: t.boolean,
2430
});
2531

2632
/**

modules/express/test/unit/typedRoutes/coinSignTx.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,84 @@ describe('CoinSignTx codec tests', function () {
782782
assert.strictEqual(decoded.recipients[0].tokenName, 'USDC');
783783
assert.strictEqual(decoded.recipients[0].data, '0xabcdef');
784784
});
785+
786+
it('should validate prebuild with eip1559 as legacy transaction marker (token transactions)', function () {
787+
// This tests the fix for WP-6630 - SDK sends { isEip1559: false } for legacy token transactions
788+
const validPrebuild = {
789+
txHex: '0xf9010d81f88408ccd68c830f424094932bb3ec0a0cb9e8aafba8e2f2c7ecf83deacc5c80b8e40dcd7a6c',
790+
gasPrice: '147641996',
791+
gasLimit: 1000000,
792+
eip1559: {
793+
isEip1559: false, // Legacy transaction marker
794+
},
795+
coin: 'avaxc',
796+
token: 'avaxc:usdc',
797+
walletId: '6618f448f6c53303d38130bc60e9efe6',
798+
};
799+
800+
const decoded = assertDecode(TransactionPrebuild, validPrebuild);
801+
assert.deepStrictEqual(decoded.eip1559, validPrebuild.eip1559);
802+
assert.strictEqual(decoded.eip1559?.isEip1559, false);
803+
assert.strictEqual(decoded.gasPrice, validPrebuild.gasPrice);
804+
assert.strictEqual(decoded.gasLimit, validPrebuild.gasLimit);
805+
assert.strictEqual(decoded.coin, validPrebuild.coin);
806+
assert.strictEqual(decoded.token, validPrebuild.token);
807+
});
808+
809+
it('should validate prebuild without eip1559 field (base coin legacy transactions)', function () {
810+
// This tests base coin legacy transactions where eip1559 is undefined
811+
const validPrebuild = {
812+
txHex: '0xf9012e81f88408b66e168307a12094932bb3ec0a0cb9e8aafba8e2f2c7ecf83deacc5c80b90104',
813+
gasPrice: 146173462,
814+
gasLimit: 500000,
815+
coin: 'avaxc',
816+
walletId: '6618f448f6c53303d38130bc60e9efe6',
817+
// Note: No eip1559 field - it's undefined
818+
};
819+
820+
const decoded = assertDecode(TransactionPrebuild, validPrebuild);
821+
assert.strictEqual(decoded.eip1559, undefined);
822+
assert.strictEqual(decoded.gasPrice, validPrebuild.gasPrice);
823+
assert.strictEqual(decoded.gasLimit, validPrebuild.gasLimit);
824+
assert.strictEqual(decoded.coin, validPrebuild.coin);
825+
});
826+
827+
it('should validate prebuild with full EIP1559 parameters', function () {
828+
// This tests actual EIP1559 transactions with maxFeePerGas and maxPriorityFeePerGas
829+
const validPrebuild = {
830+
txHex: '0x02f87301808459682f008459682f0e8252089439c0f2000e39186af4b78b554eb96a2ea8dc5c3680',
831+
eip1559: {
832+
maxPriorityFeePerGas: '1500000000',
833+
maxFeePerGas: '2000000000',
834+
isEip1559: true,
835+
},
836+
coin: 'eth',
837+
walletId: '6618f448f6c53303d38130bc60e9efe6',
838+
};
839+
840+
const decoded = assertDecode(TransactionPrebuild, validPrebuild);
841+
assert.deepStrictEqual(decoded.eip1559, validPrebuild.eip1559);
842+
assert.strictEqual(decoded.eip1559?.maxPriorityFeePerGas, '1500000000');
843+
assert.strictEqual(decoded.eip1559?.maxFeePerGas, '2000000000');
844+
assert.strictEqual(decoded.eip1559?.isEip1559, true);
845+
});
846+
847+
it('should validate prebuild with partial EIP1559 fields', function () {
848+
// This tests that EIP1559 fields are optional (t.partial)
849+
const validPrebuild = {
850+
txHex: '0x02f87301808459682f008459682f0e8252089439c0f2000e39186af4b78b554eb96a2ea8dc5c3680',
851+
eip1559: {
852+
maxFeePerGas: '2000000000',
853+
// Only one field provided - tests that t.partial allows omitting fields
854+
},
855+
coin: 'eth',
856+
walletId: '6618f448f6c53303d38130bc60e9efe6',
857+
};
858+
859+
const decoded = assertDecode(TransactionPrebuild, validPrebuild);
860+
assert.deepStrictEqual(decoded.eip1559, validPrebuild.eip1559);
861+
assert.strictEqual(decoded.eip1559?.maxFeePerGas, '2000000000');
862+
});
785863
});
786864

787865
describe('CoinSignTxBody', function () {

0 commit comments

Comments
 (0)