@@ -1150,29 +1150,79 @@ function loadRepoSchema(relPath: string): unknown {
11501150 return JSON . parse ( fs . readFileSync ( found , 'utf-8' ) ) ;
11511151}
11521152
1153- function compileSolidity ( sourcePath : string , contents : string , contractName : string ) : { abi : unknown ; bytecode : string ; deployedBytecode : string } {
1154- const input = {
1155- language : 'Solidity' ,
1156- sources : {
1157- [ sourcePath ] : { content : contents }
1158- } ,
1159- settings : {
1160- optimizer : { enabled : true , runs : 200 } ,
1161- outputSelection : {
1162- '*' : {
1163- '*' : [ 'abi' , 'evm.bytecode.object' , 'evm.deployedBytecode.object' ]
1153+ type CompileProfile = 'default' | 'large-app' | 'auto' ;
1154+ const MAX_EVM_RUNTIME_CODE_BYTES = 24576 ;
1155+
1156+ function shouldRetryWithViaIR ( errors : any [ ] ) : boolean {
1157+ const rendered = errors
1158+ . map ( ( e : any ) => String ( e ?. formattedMessage || e ?. message || '' ) )
1159+ . join ( '\n' ) ;
1160+ return / S t a c k t o o d e e p | Y u l E x c e p t i o n / i. test ( rendered ) ;
1161+ }
1162+
1163+ function deployedCodeBytes ( output : any , sourcePath : string , contractName : string ) : number | null {
1164+ const object = output ?. contracts ?. [ sourcePath ] ?. [ contractName ] ?. evm ?. deployedBytecode ?. object ;
1165+ if ( typeof object !== 'string' || object . length === 0 ) return null ;
1166+ return object . length / 2 ;
1167+ }
1168+
1169+ function compileSolidity ( sourcePath : string , contents : string , contractName : string , options : { profile ?: CompileProfile } = { } ) : { abi : unknown ; bytecode : string ; deployedBytecode : string ; viaIR : boolean } {
1170+ const profile = options . profile ?? 'auto' ;
1171+ const compileOnce = ( viaIR : boolean ) => {
1172+ const input = {
1173+ language : 'Solidity' ,
1174+ sources : {
1175+ [ sourcePath ] : { content : contents }
1176+ } ,
1177+ settings : {
1178+ optimizer : { enabled : true , runs : 200 } ,
1179+ viaIR,
1180+ outputSelection : {
1181+ '*' : {
1182+ '*' : [ 'abi' , 'evm.bytecode.object' , 'evm.deployedBytecode.object' ]
1183+ }
11641184 }
11651185 }
1166- }
1186+ } ;
1187+
1188+ const output = JSON . parse ( solc . compile ( JSON . stringify ( input ) ) ) ;
1189+ const errors = ( output . errors || [ ] ) . filter ( ( e : any ) => e . severity === 'error' ) ;
1190+ return { output, errors, viaIR } ;
11671191 } ;
11681192
1169- const output = JSON . parse ( solc . compile ( JSON . stringify ( input ) ) ) ;
1170- const errors = ( output . errors || [ ] ) . filter ( ( e : any ) => e . severity === 'error' ) ;
1171- if ( errors . length > 0 ) {
1172- const msg = errors . map ( ( e : any ) => e . formattedMessage || e . message ) . join ( '\n' ) ;
1193+ const attempts =
1194+ profile === 'default'
1195+ ? [ compileOnce ( false ) ]
1196+ : profile === 'large-app'
1197+ ? [ compileOnce ( true ) ]
1198+ : ( ( ) => {
1199+ const first = compileOnce ( false ) ;
1200+ const firstBytes = first . errors . length === 0 ? deployedCodeBytes ( first . output , sourcePath , contractName ) : null ;
1201+ if ( first . errors . length > 0 && shouldRetryWithViaIR ( first . errors ) ) {
1202+ return [ first , compileOnce ( true ) ] ;
1203+ }
1204+ if ( first . errors . length === 0 && firstBytes !== null && firstBytes > MAX_EVM_RUNTIME_CODE_BYTES ) {
1205+ return [ first , compileOnce ( true ) ] ;
1206+ }
1207+ return [ first ] ;
1208+ } ) ( ) ;
1209+
1210+ const successful = ( ( ) => {
1211+ if ( attempts . length === 2 && attempts [ 0 ] ! . errors . length === 0 ) {
1212+ const fallback = attempts [ 1 ] ! ;
1213+ if ( fallback . errors . length === 0 ) {
1214+ return fallback ;
1215+ }
1216+ }
1217+ return attempts . find ( ( attempt ) => attempt . errors . length === 0 ) ;
1218+ } ) ( ) ;
1219+ if ( ! successful ) {
1220+ const last = attempts [ attempts . length - 1 ] ! ;
1221+ const msg = last . errors . map ( ( e : any ) => e . formattedMessage || e . message ) . join ( '\n' ) ;
11731222 throw new Error ( `Solidity compile failed:\n${ msg } ` ) ;
11741223 }
11751224
1225+ const output = successful . output ;
11761226 const compiled = output . contracts ?. [ sourcePath ] ?. [ contractName ] ;
11771227 if ( ! compiled ) {
11781228 throw new Error ( `Solidity compile output missing ${ contractName } in ${ sourcePath } ` ) ;
@@ -1181,7 +1231,22 @@ function compileSolidity(sourcePath: string, contents: string, contractName: str
11811231 const abi = compiled . abi ;
11821232 const bytecode = `0x${ compiled . evm . bytecode . object } ` ;
11831233 const deployedBytecode = `0x${ compiled . evm . deployedBytecode . object } ` ;
1184- return { abi, bytecode, deployedBytecode } ;
1234+ return { abi, bytecode, deployedBytecode, viaIR : successful . viaIR } ;
1235+ }
1236+
1237+ function normalizeCompileProfile ( value ?: string ) : CompileProfile {
1238+ const normalized = String ( value ?? 'auto' ) . trim ( ) . toLowerCase ( ) ;
1239+ if ( normalized === 'default' || normalized === 'large-app' || normalized === 'auto' ) {
1240+ return normalized ;
1241+ }
1242+ throw new Error ( `Invalid compiler profile "${ value } ". Supported: auto, default, large-app` ) ;
1243+ }
1244+
1245+ function compileProfileForLog ( profile : CompileProfile , viaIR : boolean ) : string {
1246+ if ( profile === 'auto' ) {
1247+ return viaIR ? 'auto(viaIR)' : 'auto' ;
1248+ }
1249+ return viaIR ? `${ profile } (viaIR)` : profile ;
11851250}
11861251
11871252function titleFromSlug ( slug : string ) : string {
@@ -1911,7 +1976,15 @@ function startUiSiteServer(args: {
19111976function buildFromSchema (
19121977 schema : ThsSchema ,
19131978 outDir : string ,
1914- opts : { ui : boolean ; quiet ?: boolean ; schemaPathForHints ?: string ; txMode ?: string ; relayBaseUrl ?: string ; targetChainId ?: number }
1979+ opts : {
1980+ ui : boolean ;
1981+ quiet ?: boolean ;
1982+ schemaPathForHints ?: string ;
1983+ txMode ?: string ;
1984+ relayBaseUrl ?: string ;
1985+ targetChainId ?: number ;
1986+ compileProfile ?: CompileProfile ;
1987+ }
19151988) : { outDir : string ; uiBundleDir : string | null ; uiSiteDir : string | null } {
19161989 const resolvedOutDir = path . resolve ( outDir ) ;
19171990 ensureDir ( resolvedOutDir ) ;
@@ -1923,12 +1996,14 @@ function buildFromSchema(
19231996
19241997 // 2) Compile (solc-js)
19251998 const sourceRelPath = appSol . path . replace ( / \\ \\ / g, '/' ) ;
1926- const compiled = compileSolidity ( sourceRelPath , appSol . contents , 'App' ) ;
1999+ const compileProfile = normalizeCompileProfile ( opts . compileProfile ) ;
2000+ const compiled = compileSolidity ( sourceRelPath , appSol . contents , 'App' , { profile : compileProfile } ) ;
19272001 const compiledArtifact = {
19282002 contractName : 'App' ,
19292003 abi : compiled . abi ,
19302004 bytecode : compiled . bytecode ,
1931- deployedBytecode : compiled . deployedBytecode
2005+ deployedBytecode : compiled . deployedBytecode ,
2006+ compilerProfile : compileProfileForLog ( compileProfile , compiled . viaIR )
19322007 } ;
19332008 const compiledJson = JSON . stringify ( compiledArtifact , null , 2 ) ;
19342009 const compiledOutPath = path . join ( resolvedOutDir , 'compiled' , 'App.json' ) ;
@@ -2026,7 +2101,8 @@ function buildFromSchema(
20262101 generatorVersion : '0.0.0' ,
20272102 toolchain : {
20282103 node : process . version . replace ( / ^ v / , '' ) ,
2029- solc : solc . version ( )
2104+ solc : solc . version ( ) ,
2105+ compilerProfile : compileProfileForLog ( compileProfile , compiled . viaIR )
20302106 } ,
20312107 release : {
20322108 releaseId : `rel_local_${ Date . now ( ) } ` ,
@@ -2743,8 +2819,9 @@ program
27432819 . argument ( '<schema>' , 'Path to THS schema JSON file' )
27442820 . option ( '--out <dir>' , 'Output directory' , 'artifacts' )
27452821 . option ( '--no-ui' , 'Do not generate UI output' )
2822+ . option ( '--compiler-profile <profile>' , 'Compiler profile (auto|default|large-app)' , 'auto' )
27462823 . option ( '--with-tests' , 'Emit generated app test scaffold' , false )
2747- . action ( ( schemaPath : string , opts : { out : string ; ui : boolean ; withTests : boolean } ) => {
2824+ . action ( ( schemaPath : string , opts : { out : string ; ui : boolean ; compilerProfile ?: string ; withTests : boolean } ) => {
27482825 const input = readJsonFile ( schemaPath ) ;
27492826 const structural = validateThsStructural ( input ) ;
27502827 if ( ! structural . ok ) {
@@ -2768,6 +2845,21 @@ program
27682845 ensureDir ( contractsDir ) ;
27692846 fs . writeFileSync ( path . join ( outDir , appSol . path ) , appSol . contents ) ;
27702847
2848+ const sourceRelPath = appSol . path . replace ( / \\ \\ / g, '/' ) ;
2849+ const compileProfile = normalizeCompileProfile ( opts . compilerProfile ) ;
2850+ const compiled = compileSolidity ( sourceRelPath , appSol . contents , 'App' , { profile : compileProfile } ) ;
2851+ const compiledArtifact = {
2852+ contractName : 'App' ,
2853+ abi : compiled . abi ,
2854+ bytecode : compiled . bytecode ,
2855+ deployedBytecode : compiled . deployedBytecode ,
2856+ compilerProfile : compileProfileForLog ( compileProfile , compiled . viaIR )
2857+ } ;
2858+ const compiledJson = JSON . stringify ( compiledArtifact , null , 2 ) ;
2859+ const compiledOutPath = path . join ( outDir , 'compiled' , 'App.json' ) ;
2860+ ensureDir ( path . dirname ( compiledOutPath ) ) ;
2861+ fs . writeFileSync ( compiledOutPath , compiledJson ) ;
2862+
27712863 // Also persist an immutable copy of the schema input alongside the artifacts.
27722864 ensureDir ( outDir ) ;
27732865 fs . writeFileSync ( path . join ( outDir , 'schema.json' ) , JSON . stringify ( schema , null , 2 ) ) ;
@@ -2781,6 +2873,10 @@ program
27812873 ensureDir ( path . dirname ( thsTsPath ) ) ;
27822874 fs . writeFileSync ( thsTsPath , renderThsTs ( schema ) ) ;
27832875
2876+ const compiledPublicPath = path . join ( uiDir , 'public' , 'compiled' , 'App.json' ) ;
2877+ ensureDir ( path . dirname ( compiledPublicPath ) ) ;
2878+ fs . writeFileSync ( compiledPublicPath , compiledJson ) ;
2879+
27842880 if ( opts . withTests ) {
27852881 addGeneratedUiTestScaffold ( uiDir , templateDir ) ;
27862882 console . log ( `Wrote ui/tests/ (generated app test scaffold)` ) ;
@@ -2789,6 +2885,7 @@ program
27892885 console . log ( `Wrote ui/ (Next.js static export template)` ) ;
27902886 }
27912887
2888+ console . log ( `Wrote compiled/App.json` ) ;
27922889 console . log ( `Wrote ${ appSol . path } ` ) ;
27932890 } ) ;
27942891
@@ -2797,14 +2894,16 @@ program
27972894 . argument ( '<schema>' , 'Path to THS schema JSON file' )
27982895 . option ( '--out <dir>' , 'Output directory' , 'artifacts' )
27992896 . option ( '--no-ui' , 'Do not generate/build UI bundle' )
2897+ . option ( '--compiler-profile <profile>' , 'Compiler profile (auto|default|large-app)' , 'auto' )
28002898 . option ( '--tx-mode <mode>' , 'Transaction mode (auto|userPays|sponsored)' , 'auto' )
28012899 . option ( '--relay-base-url <url>' , 'Relay base URL for sponsored mode' , '/__tokenhost/relay' )
2802- . action ( ( schemaPath : string , opts : { out : string ; ui : boolean ; txMode ?: string ; relayBaseUrl ?: string } ) => {
2900+ . action ( ( schemaPath : string , opts : { out : string ; ui : boolean ; compilerProfile ?: string ; txMode ?: string ; relayBaseUrl ?: string } ) => {
28032901 try {
28042902 const schema = loadThsSchemaOrThrow ( schemaPath ) ;
28052903 buildFromSchema ( schema , opts . out , {
28062904 ui : opts . ui ,
28072905 schemaPathForHints : schemaPath ,
2906+ compileProfile : normalizeCompileProfile ( opts . compilerProfile ) ,
28082907 txMode : opts . txMode ,
28092908 relayBaseUrl : opts . relayBaseUrl
28102909 } ) ;
@@ -2835,6 +2934,7 @@ program
28352934 . option ( '--port <n>' , 'Preview port' , '3000' )
28362935 . option ( '--interactive' , 'Prompt for missing values' , false )
28372936 . option ( '--dry-run' , 'Print what would run and exit' , false )
2937+ . option ( '--compiler-profile <profile>' , 'Compiler profile (auto|default|large-app)' , 'auto' )
28382938 . option ( '--tx-mode <mode>' , 'Transaction mode (auto|userPays|sponsored)' , 'auto' )
28392939 . option ( '--relay-base-url <url>' , 'Relay base URL for sponsored mode' , '/__tokenhost/relay' )
28402940 . option ( '--no-start-anvil' , 'Do not start anvil automatically (anvil chain only)' )
@@ -2856,6 +2956,7 @@ program
28562956 port : string ;
28572957 interactive : boolean ;
28582958 dryRun : boolean ;
2959+ compilerProfile ?: string ;
28592960 txMode ?: string ;
28602961 relayBaseUrl ?: string ;
28612962 startAnvil : boolean ;
@@ -2939,11 +3040,13 @@ program
29393040 const { chainName, chain } = resolveKnownChain ( opts . chain ) ;
29403041 const rpcUrl = resolveRpcUrl ( chainName , chain , opts . rpc ) ;
29413042 const resolvedTxMode = resolveTxMode ( opts . txMode , chain . id ) ;
3043+ const compileProfile = normalizeCompileProfile ( opts . compilerProfile ) ;
29423044
29433045 if ( opts . dryRun ) {
29443046 console . log ( 'Plan:' ) ;
29453047 console . log ( ` - validate: ${ resolvedSchemaPath } ` ) ;
29463048 console . log ( ` - build: ${ outDir } ` ) ;
3049+ console . log ( ` - compile: ${ compileProfile } ` ) ;
29473050 if ( chainName === 'anvil' ) {
29483051 console . log ( ` - anvil: ${ opts . startAnvil ? `ensure running at ${ rpcUrl } ` : `SKIP (rpc=${ rpcUrl } )` } ` ) ;
29493052 }
@@ -2969,6 +3072,7 @@ program
29693072 console . log ( `Out: ${ path . relative ( process . cwd ( ) , outDir ) } ` ) ;
29703073 console . log ( `Chain: ${ chainName } (${ rpcUrl } )` ) ;
29713074 console . log ( `Tx: ${ resolvedTxMode } ` ) ;
3075+ console . log ( `Compile:${ compileProfile } ` ) ;
29723076 if ( opts . preview ) console . log ( `UI: ${ previewUrl } ` ) ;
29733077 console . log ( '' ) ;
29743078
@@ -2987,6 +3091,7 @@ program
29873091 ui : true ,
29883092 quiet : true ,
29893093 schemaPathForHints : resolvedSchemaPath ,
3094+ compileProfile,
29903095 txMode : opts . txMode ,
29913096 relayBaseUrl : opts . relayBaseUrl ,
29923097 targetChainId : chain . id
0 commit comments