@@ -1050,6 +1050,34 @@ export function buildRootObject(
10501050 maxFeePerGas ?: Hex | undefined ;
10511051 maxPriorityFeePerGas ?: Hex | undefined ;
10521052 } ) : Promise < Hex > {
1053+ // Check the relay path first — it forwards raw delegations/execution to
1054+ // the home wallet and does not need local chain ID or SDK calldata.
1055+ if ( ! bundlerConfig && ! smartAccountConfig ) {
1056+ if ( peerWallet ) {
1057+ try {
1058+ return await E ( peerWallet ) . handleRedemptionRequest ( {
1059+ type : 'single' ,
1060+ delegations : options . delegations ,
1061+ execution : options . execution ,
1062+ maxFeePerGas : options . maxFeePerGas ,
1063+ maxPriorityFeePerGas : options . maxPriorityFeePerGas ,
1064+ } ) ;
1065+ } catch ( relayError ) {
1066+ const detail =
1067+ relayError instanceof Error
1068+ ? relayError . message
1069+ : String ( relayError ) ;
1070+ throw new Error (
1071+ `Failed to relay delegation redemption to home wallet: ${ detail } ` ,
1072+ { cause : relayError } ,
1073+ ) ;
1074+ }
1075+ }
1076+ throw new Error (
1077+ 'Bundler not configured and no peer wallet available for relay' ,
1078+ ) ;
1079+ }
1080+
10531081 const sender =
10541082 smartAccountConfig ?. address ?? options . delegations [ 0 ] . delegate ;
10551083
@@ -1070,25 +1098,8 @@ export function buildRootObject(
10701098 }
10711099
10721100 if ( ! bundlerConfig ) {
1073- if ( peerWallet ) {
1074- try {
1075- return await E ( peerWallet ) . handleRedemptionRequest ( {
1076- type : 'single' ,
1077- delegations : options . delegations ,
1078- execution : options . execution ,
1079- maxFeePerGas : options . maxFeePerGas ,
1080- maxPriorityFeePerGas : options . maxPriorityFeePerGas ,
1081- } ) ;
1082- } catch ( relayError ) {
1083- throw new Error (
1084- 'Failed to relay delegation redemption to home wallet — ' +
1085- 'ensure the home device is online and try again' ,
1086- { cause : relayError } ,
1087- ) ;
1088- }
1089- }
10901101 throw new Error (
1091- 'Bundler not configured and no peer wallet available for relay ' ,
1102+ 'Bundler not configured (required for hybrid smart account redemption) ' ,
10921103 ) ;
10931104 }
10941105
@@ -1122,6 +1133,32 @@ export function buildRootObject(
11221133 delegations : Delegation [ ] ;
11231134 executions : Execution [ ] ;
11241135 } ) : Promise < Hex > {
1136+ // Check the relay path first — it forwards raw delegations/executions to
1137+ // the home wallet and does not need local chain ID or SDK calldata.
1138+ if ( ! bundlerConfig && ! smartAccountConfig ) {
1139+ if ( peerWallet ) {
1140+ try {
1141+ return await E ( peerWallet ) . handleRedemptionRequest ( {
1142+ type : 'batch' ,
1143+ delegations : options . delegations ,
1144+ executions : options . executions ,
1145+ } ) ;
1146+ } catch ( relayError ) {
1147+ const detail =
1148+ relayError instanceof Error
1149+ ? relayError . message
1150+ : String ( relayError ) ;
1151+ throw new Error (
1152+ `Failed to relay batch delegation redemption to home wallet: ${ detail } ` ,
1153+ { cause : relayError } ,
1154+ ) ;
1155+ }
1156+ }
1157+ throw new Error (
1158+ 'Bundler not configured and no peer wallet available for relay' ,
1159+ ) ;
1160+ }
1161+
11251162 const sender =
11261163 smartAccountConfig ?. address ?? options . delegations [ 0 ] ?. delegate ;
11271164 if ( ! sender ) {
@@ -1140,23 +1177,8 @@ export function buildRootObject(
11401177 }
11411178
11421179 if ( ! bundlerConfig ) {
1143- if ( peerWallet ) {
1144- try {
1145- return await E ( peerWallet ) . handleRedemptionRequest ( {
1146- type : 'batch' ,
1147- delegations : options . delegations ,
1148- executions : options . executions ,
1149- } ) ;
1150- } catch ( relayError ) {
1151- throw new Error (
1152- 'Failed to relay batch delegation redemption to home wallet — ' +
1153- 'ensure the home device is online and try again' ,
1154- { cause : relayError } ,
1155- ) ;
1156- }
1157- }
11581180 throw new Error (
1159- 'Bundler not configured and no peer wallet available for relay ' ,
1181+ 'Bundler not configured (required for hybrid smart account batch redemption) ' ,
11601182 ) ;
11611183 }
11621184
@@ -2767,12 +2789,14 @@ export function buildRootObject(
27672789 throw new Error ( 'Missing or empty delegations in redemption request' ) ;
27682790 }
27692791
2770- // Guard against infinite relay loops: if this wallet also has no
2771- // bundler and no direct 7702, it cannot fulfill the request and
2772- // must not relay it back to its own peer.
2773- const sender =
2774- smartAccountConfig ?. address ?? request . delegations [ 0 ] . delegate ;
2775- if ( ! bundlerConfig && ! ( await useDirect7702Tx ( sender ) ) ) {
2792+ // Guard against infinite relay loops: if this wallet cannot fulfill
2793+ // the request locally, it must not relay it back to its own peer.
2794+ // Uses the same config-based check as the relay entry condition in
2795+ // submitDelegationUserOp/submitBatchDelegationUserOp — keep in sync.
2796+ const canFulfillLocally =
2797+ bundlerConfig !== undefined ||
2798+ smartAccountConfig ?. implementation === 'stateless7702' ;
2799+ if ( ! canFulfillLocally ) {
27762800 throw new Error (
27772801 'Cannot fulfill relayed redemption: no bundler or direct 7702 configured' ,
27782802 ) ;
@@ -2838,7 +2862,7 @@ export function buildRootObject(
28382862 cachedPeerSigningMode = signingMode ;
28392863 persistBaggage ( 'cachedPeerSigningMode' , cachedPeerSigningMode ) ;
28402864 } catch ( error ) {
2841- logger . warn ( 'peer getCapabilities timed out , using cache' , error ) ;
2865+ logger . warn ( 'peer getCapabilities failed , using cache' , error ) ;
28422866 signingMode = cachedPeerSigningMode ?? 'peer:unknown' ;
28432867 }
28442868 } else if ( externalSigner ) {
@@ -2867,8 +2891,12 @@ export function buildRootObject(
28672891 let autonomy : string ;
28682892 const canRedeemLocally =
28692893 bundlerConfig !== undefined ||
2870- smartAccountConfig ?. implementation === 'stateless7702' ;
2871- const canRedeemViaRelay = ! canRedeemLocally && peerWallet !== undefined ;
2894+ ( smartAccountConfig ?. implementation === 'stateless7702' &&
2895+ providerVat !== undefined ) ;
2896+ // Relay requires no smartAccountConfig — mirrors the entry condition
2897+ // in submitDelegationUserOp/submitBatchDelegationUserOp.
2898+ const canRedeemViaRelay =
2899+ ! canRedeemLocally && ! smartAccountConfig && peerWallet !== undefined ;
28722900 const canRedeemDelegationsOnChain =
28732901 activeDelegations . length > 0 && ( canRedeemLocally || canRedeemViaRelay ) ;
28742902 if ( canRedeemDelegationsOnChain ) {
0 commit comments