2626
2727import { parseArgs } from "node:util" ;
2828import { destroyConnection } from "../src/utils/connection.js" ;
29+ import { checkAllowance , ensureAllowance } from "../src/utils/account/allowance.js" ;
2930import { publishToPlayground , normalizeDomain } from "../src/utils/deploy/playground.js" ;
31+ import { getReadOnlyRegistryContract } from "../src/utils/registry.js" ;
3032import { resolveSigner } from "../src/utils/signer.js" ;
3133import { SIGNER , E2E_DOMAINS } from "../e2e/cli/fixtures/accounts.js" ;
32- import { destroyTestClient } from "../e2e/cli/helpers/chain.js" ;
34+ import { destroyTestClient , getTestClient } from "../e2e/cli/helpers/chain.js" ;
3335import { fundAccountIfLow } from "../e2e/cli/setup/fund.js" ;
3436
3537const DEFAULT_TEMPLATE_DOMAIN = "dot-cli-mod-fixture.dot" ;
3638const DEFAULT_TEMPLATE_REPO = "https://github.com/paritytech/Rock-Paper-Scissors" ;
39+ const REGISTRY_READBACK_ATTEMPTS = 5 ;
40+ const REGISTRY_READBACK_DELAY_MS = 2_000 ;
3741
3842interface Fixture {
3943 domain : string ;
@@ -88,6 +92,71 @@ function logPlan(fixtures: readonly Fixture[]): void {
8892 console . log ( ) ;
8993}
9094
95+ function errorText ( error : unknown ) : string {
96+ if ( error instanceof Error ) return `${ error . name } : ${ error . message } ` ;
97+ return String ( error ) ;
98+ }
99+
100+ function isBulletinTeardownNoise ( error : unknown ) : boolean {
101+ const text = error instanceof Error ? `${ error . name } : ${ error . message } \n${ error . stack ?? "" } ` : String ( error ) ;
102+ return (
103+ text . includes ( "ChainHead disjointed" ) ||
104+ ( text . includes ( "UnsubscriptionError" ) && text . includes ( "Not connected" ) )
105+ ) ;
106+ }
107+
108+ function suppressStandaloneBulletinTeardownNoise ( ) : ( ) => void {
109+ const onUncaught = ( error : unknown ) => {
110+ if ( isBulletinTeardownNoise ( error ) ) {
111+ console . warn ( `ignored Bulletin client teardown noise: ${ errorText ( error ) } ` ) ;
112+ return ;
113+ }
114+ process . off ( "uncaughtException" , onUncaught ) ;
115+ throw error ;
116+ } ;
117+
118+ process . prependListener ( "uncaughtException" , onUncaught ) ;
119+ return ( ) => process . off ( "uncaughtException" , onUncaught ) ;
120+ }
121+
122+ async function delay ( ms : number ) : Promise < void > {
123+ await new Promise ( ( resolve ) => setTimeout ( resolve , ms ) ) ;
124+ }
125+
126+ function formatAllowance ( status : Awaited < ReturnType < typeof checkAllowance > > ) : string {
127+ if ( ! status . authorized ) return "not authorized" ;
128+ return `${ status . remainingTxs } tx / ${ status . remainingBytes } bytes` ;
129+ }
130+
131+ async function ensureFixtureStorageAllowance ( address : string ) : Promise < void > {
132+ const client = await getTestClient ( ) ;
133+ const before = await checkAllowance ( client , address ) ;
134+ console . log ( `Bulletin allowance ${ formatAllowance ( before ) } ` ) ;
135+
136+ await ensureAllowance ( client , address ) ;
137+
138+ const after = await checkAllowance ( client , address ) ;
139+ if ( formatAllowance ( after ) !== formatAllowance ( before ) ) {
140+ console . log ( `Bulletin allowance ${ formatAllowance ( after ) } ` ) ;
141+ }
142+ console . log ( ) ;
143+ }
144+
145+ async function verifyRegistryEntry ( domain : string , metadataCid : string ) : Promise < void > {
146+ const client = await getTestClient ( ) ;
147+ const registry = await getReadOnlyRegistryContract ( client . raw . assetHub ) ;
148+
149+ for ( let attempt = 1 ; attempt <= REGISTRY_READBACK_ATTEMPTS ; attempt ++ ) {
150+ const result = await registry . getMetadataUri . query ( domain ) ;
151+ const value = result . value as { isSome ?: boolean ; value ?: string } | undefined ;
152+ if ( result . success && value ?. isSome && value . value === metadataCid ) return ;
153+
154+ if ( attempt < REGISTRY_READBACK_ATTEMPTS ) await delay ( REGISTRY_READBACK_DELAY_MS ) ;
155+ }
156+
157+ throw new Error ( `Registry readback failed for ${ domain } : expected metadata CID ${ metadataCid } ` ) ;
158+ }
159+
91160async function registerFixture (
92161 fixture : Fixture ,
93162 signer : Awaited < ReturnType < typeof resolveSigner > > ,
@@ -110,7 +179,10 @@ async function registerFixture(
110179 } ,
111180 } ) ;
112181
182+ await verifyRegistryEntry ( result . fullDomain , result . metadataCid ) ;
183+
113184 const elapsed = ( ( Date . now ( ) - start ) / 1000 ) . toFixed ( 1 ) ;
185+ console . log ( " verified registry readback" ) ;
114186 console . log ( ` published ${ result . metadataCid } (${ elapsed } s)` ) ;
115187 console . log ( ) ;
116188}
@@ -145,18 +217,21 @@ async function main(): Promise<number> {
145217 }
146218
147219 const signer = await resolveSigner ( { suri : values . suri ?? SIGNER . suri } ) ;
220+ const restoreErrorHandling = suppressStandaloneBulletinTeardownNoise ( ) ;
148221 try {
149222 logPlan ( fixtures ) ;
150223 console . log ( `signer ${ signer . address } (${ signer . source } )` ) ;
151224 await fundAccountIfLow ( { name : "fixture signer" , address : signer . address } ) ;
152225 console . log ( ) ;
226+ await ensureFixtureStorageAllowance ( signer . address ) ;
153227
154228 for ( const [ index , fixture ] of fixtures . entries ( ) ) {
155229 await registerFixture ( fixture , signer , index + 1 , fixtures . length ) ;
156230 }
157231 console . log ( `registered ${ fixtures . length } fixture(s)` ) ;
158232 return 0 ;
159233 } finally {
234+ restoreErrorHandling ( ) ;
160235 signer . destroy ( ) ;
161236 destroyTestClient ( ) ;
162237 destroyConnection ( ) ;
0 commit comments