@@ -336,6 +336,77 @@ describe('SendCoins V2 codec tests', function () {
336336 assert . strictEqual ( callArgs . gasLimit , 21000 ) ;
337337 } ) ;
338338
339+ it ( 'should allow empty eip1559 object for backward compatibility' , async function ( ) {
340+ const requestBody = {
341+ address : '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb' ,
342+ amount : '1000000000000000000' ,
343+ walletPassphrase : 'test_passphrase_12345' ,
344+ eip1559 : { } ,
345+ } ;
346+
347+ const mockWallet = {
348+ send : sinon . stub ( ) . resolves ( mockSendResponse ) ,
349+ _wallet : { multisigType : 'onchain' } ,
350+ } ;
351+
352+ const walletsGetStub = sinon . stub ( ) . resolves ( mockWallet ) ;
353+ const mockCoin = {
354+ wallets : sinon . stub ( ) . returns ( {
355+ get : walletsGetStub ,
356+ } ) ,
357+ } ;
358+
359+ sinon . stub ( BitGo . prototype , 'coin' ) . returns ( mockCoin as any ) ;
360+
361+ const result = await agent
362+ . post ( `/api/v2/${ coin } /wallet/${ walletId } /sendcoins` )
363+ . set ( 'Authorization' , 'Bearer test_access_token_12345' )
364+ . set ( 'Content-Type' , 'application/json' )
365+ . send ( requestBody ) ;
366+
367+ assert . strictEqual ( result . status , 200 ) ;
368+
369+ // Verify empty eip1559 was passed to SDK (backend handles auto fee estimation)
370+ const callArgs = mockWallet . send . firstCall . args [ 0 ] ;
371+ assert . deepStrictEqual ( callArgs . eip1559 , { } ) ;
372+ } ) ;
373+
374+ it ( 'should reject partial eip1559 object with 400 error' , async function ( ) {
375+ const requestBody = {
376+ address : '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb' ,
377+ amount : '1000000000000000000' ,
378+ walletPassphrase : 'test_passphrase_12345' ,
379+ eip1559 : {
380+ maxFeePerGas : 100000000000 ,
381+ // maxPriorityFeePerGas intentionally missing
382+ } ,
383+ } ;
384+
385+ const mockWallet = {
386+ send : sinon . stub ( ) . resolves ( mockSendResponse ) ,
387+ _wallet : { multisigType : 'onchain' } ,
388+ } ;
389+
390+ const walletsGetStub = sinon . stub ( ) . resolves ( mockWallet ) ;
391+ const mockCoin = {
392+ wallets : sinon . stub ( ) . returns ( {
393+ get : walletsGetStub ,
394+ } ) ,
395+ } ;
396+
397+ sinon . stub ( BitGo . prototype , 'coin' ) . returns ( mockCoin as any ) ;
398+
399+ const result = await agent
400+ . post ( `/api/v2/${ coin } /wallet/${ walletId } /sendcoins` )
401+ . set ( 'Authorization' , 'Bearer test_access_token_12345' )
402+ . set ( 'Content-Type' , 'application/json' )
403+ . send ( requestBody ) ;
404+
405+ // Partial eip1559 should be rejected with 400 error
406+ assert . strictEqual ( result . status , 400 ) ;
407+ assert . ok ( result . body . error . includes ( 'eip1559 missing maxPriorityFeePerGas' ) ) ;
408+ } ) ;
409+
339410 it ( 'should successfully send with memo (XRP/Stellar)' , async function ( ) {
340411 const requestBody = {
341412 address : 'GDSAMPLE123456789' ,
@@ -787,10 +858,39 @@ describe('SendCoins V2 codec tests', function () {
787858
788859 const decoded = assertDecode ( t . type ( SendCoinsRequestBody ) , validBody ) ;
789860 assert . ok ( decoded . eip1559 ) ;
861+ assert . ok ( 'maxPriorityFeePerGas' in decoded . eip1559 ) ;
862+ assert . ok ( 'maxFeePerGas' in decoded . eip1559 ) ;
790863 assert . strictEqual ( decoded . eip1559 . maxPriorityFeePerGas , 2000000000 ) ;
791864 assert . strictEqual ( decoded . eip1559 . maxFeePerGas , 100000000000 ) ;
792865 } ) ;
793866
867+ it ( 'should allow empty eip1559 object for backward compatibility' , function ( ) {
868+ const validBody = {
869+ address : '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb' ,
870+ amount : '1000000000000000000' ,
871+ eip1559 : { } ,
872+ } ;
873+
874+ const decoded = assertDecode ( t . type ( SendCoinsRequestBody ) , validBody ) ;
875+ assert . ok ( decoded . eip1559 ) ;
876+ assert . deepStrictEqual ( decoded . eip1559 , { } ) ;
877+ } ) ;
878+
879+ it ( 'should pass schema validation for partial eip1559 (controller rejects)' , function ( ) {
880+ const partialBody = {
881+ address : '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb' ,
882+ amount : '1000000000000000000' ,
883+ eip1559 : {
884+ maxFeePerGas : 100000000000 ,
885+ } ,
886+ } ;
887+
888+ // Partial objects pass schema validation; controller validates and rejects
889+ const decoded = assertDecode ( t . type ( SendCoinsRequestBody ) , partialBody ) ;
890+ assert . ok ( decoded . eip1559 ) ;
891+ assert . strictEqual ( decoded . eip1559 . maxFeePerGas , 100000000000 ) ;
892+ } ) ;
893+
794894 it ( 'should validate body with memo' , function ( ) {
795895 const validBody = {
796896 address : 'GDSAMPLE' ,
@@ -860,21 +960,6 @@ describe('SendCoins V2 codec tests', function () {
860960 } ) ;
861961 } ) ;
862962
863- it ( 'should reject body with incomplete eip1559 params' , function ( ) {
864- const invalidBody = {
865- address : '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb' ,
866- amount : '1000000000000000000' ,
867- eip1559 : {
868- maxPriorityFeePerGas : 2000000000 ,
869- // Missing maxFeePerGas
870- } ,
871- } ;
872-
873- assert . throws ( ( ) => {
874- assertDecode ( t . type ( SendCoinsRequestBody ) , invalidBody ) ;
875- } ) ;
876- } ) ;
877-
878963 it ( 'should reject body with incomplete memo params' , function ( ) {
879964 const invalidBody = {
880965 address : 'GDSAMPLE' ,
0 commit comments