1- // TODO: Only for folder structure. Adjust QuickStarterPrivateStateId based on your implementation.
2- // Common types and constants
3- export const QuickStarterPrivateStateId = 'quick-starter-private-state' ;
1+ import { Observable } from "rxjs" ;
2+ import { ContractAddress } from "@midnight-ntwrk/compact-runtime" ;
3+ import { type Logger } from "pino" ;
4+ import { ledger , Contract } from "@midnight-setup/midnight-setup-contract" ;
5+ import { deployContract , findDeployedContract } from "@midnight-ntwrk/midnight-js-contracts" ;
6+ import * as utils from "./utils.js" ;
7+ import {
8+ MidnightSetupContractProviders ,
9+ MidnightSetupPrivateStateId ,
10+ DerivedMidnightSetupContractState ,
11+ ContractStateData ,
12+ LedgerStateData ,
13+ } from "./common-types.js" ;
14+
15+ export interface DeployedMidnightSetupAPI {
16+ readonly deployedContractAddress : ContractAddress ;
17+ readonly state : Observable < DerivedMidnightSetupContractState > ;
18+ getContractState : ( ) => Promise < ContractStateData > ;
19+ getLedgerState : ( ) => Promise < LedgerStateData > ;
20+ }
21+ /**
22+ * NB: Declaring a class implements a given type, means it must contain all defined properties and methods, then take on other extra properties or class
23+ */
24+
25+ export class MidnightSetupAPI implements DeployedMidnightSetupAPI {
26+ deployedContractAddress : string ;
27+ state : Observable < DerivedMidnightSetupContractState > ;
28+
29+ private constructor (
30+ private providers : MidnightSetupContractProviders ,
31+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
32+ public readonly allReadyDeployedContract : any ,
33+ private logger ?: Logger
34+ ) {
35+ this . deployedContractAddress = allReadyDeployedContract . deployTxData . public . contractAddress ;
36+
37+ // Real state observable
38+ this . state = new Observable ( subscriber => {
39+ subscriber . next ( {
40+ protocolTVL : [ ] ,
41+ projects : [ ] ,
42+ } ) ;
43+ } ) ;
44+ }
45+
46+ async getContractState ( ) : Promise < ContractStateData > {
47+ try {
48+ this . logger ?. info ( "Getting contract state..." , { address : this . deployedContractAddress } ) ;
49+
50+ // Try to get contract state from public data provider
51+ const contractState = await this . providers . publicDataProvider . queryContractState ( this . deployedContractAddress ) ;
52+
53+ if ( contractState ) {
54+ this . logger ?. info ( "Contract state retrieved successfully" ) ;
55+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
56+ const stateAny = contractState as any ;
57+ return {
58+ address : this . deployedContractAddress ,
59+ data : contractState . data ,
60+ blockHeight : stateAny . blockHeight ?. toString ( ) ,
61+ blockHash : stateAny . blockHash ?. toString ( ) ,
62+ } ;
63+ } else {
64+ this . logger ?. warn ( "No contract state found" ) ;
65+ return {
66+ address : this . deployedContractAddress ,
67+ data : null ,
68+ message : "No contract state found at this address"
69+ } ;
70+ }
71+ } catch ( error ) {
72+ this . logger ?. error ( "Failed to get contract state" , { error : error instanceof Error ? error . message : error } ) ;
73+ return {
74+ address : this . deployedContractAddress ,
75+ data : null ,
76+ error : error instanceof Error ? error . message : "Failed to get contract state"
77+ } ;
78+ }
79+ }
80+
81+ async getLedgerState ( ) : Promise < LedgerStateData > {
82+ try {
83+ this . logger ?. info ( "Getting ledger state..." , { address : this . deployedContractAddress } ) ;
84+
85+ const contractState = await this . getContractState ( ) ;
86+
87+ if ( contractState . data ) {
88+ // Try to parse the ledger state
89+ try {
90+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
91+ const ledgerState = ledger ( contractState . data as any ) ;
92+ this . logger ?. info ( "Ledger state parsed successfully" ) ;
93+ return {
94+ address : this . deployedContractAddress ,
95+ ledgerState : {
96+ message : ledgerState . message ? new TextDecoder ( ) . decode ( ledgerState . message ) : null ,
97+ } ,
98+ blockHeight : contractState . blockHeight ,
99+ blockHash : contractState . blockHash ,
100+ } ;
101+ } catch ( parseError ) {
102+ this . logger ?. warn ( "Failed to parse ledger state" , { error : parseError } ) ;
103+ return {
104+ address : this . deployedContractAddress ,
105+ rawData : contractState . data ,
106+ parseError : parseError instanceof Error ? parseError . message : "Failed to parse ledger state"
107+ } ;
108+ }
109+ } else {
110+ return {
111+ address : this . deployedContractAddress ,
112+ error : contractState . error || contractState . message
113+ } ;
114+ }
115+ } catch ( error ) {
116+ this . logger ?. error ( "Failed to get ledger state" , { error : error instanceof Error ? error . message : error } ) ;
117+ return {
118+ address : this . deployedContractAddress ,
119+ error : error instanceof Error ? error . message : "Failed to get ledger state"
120+ } ;
121+ }
122+ }
123+
124+ static async deployMidnightSetupContract (
125+ providers : MidnightSetupContractProviders ,
126+ logger ?: Logger
127+ ) : Promise < MidnightSetupAPI > {
128+ logger ?. info ( "Deploying contract..." ) ;
129+
130+ try {
131+ // Get or create initial private state
132+ const initialPrivateState = await MidnightSetupAPI . getPrivateState ( providers ) ;
133+
134+ // Create contract instance with constructor arguments
135+ const contractInstance = new Contract ( { } ) ;
136+
137+ // Real contract deployment using the wallet
138+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
139+ const deployedContract = await deployContract (
140+ providers as any ,
141+ {
142+ contract : contractInstance ,
143+ initialPrivateState : initialPrivateState ,
144+ privateStateId : MidnightSetupPrivateStateId ,
145+ }
146+ ) ;
147+
148+ logger ?. info ( "Contract deployed successfully" , {
149+ address : deployedContract . deployTxData . public . contractAddress
150+ } ) ;
151+ return new MidnightSetupAPI ( providers , deployedContract , logger ) ;
152+ } catch ( error ) {
153+ logger ?. error ( "Failed to deploy contract" , {
154+ error : error instanceof Error ? error . message : String ( error ) ,
155+ stack : error instanceof Error ? error . stack : undefined
156+ } ) ;
157+ throw error ;
158+ }
159+ }
160+
161+ static async joinMidnightSetupContract (
162+ providers : MidnightSetupContractProviders ,
163+ contractAddress : string ,
164+ logger ?: Logger
165+ ) : Promise < MidnightSetupAPI > {
166+ logger ?. info ( "Joining contract..." , { contractAddress } ) ;
167+
168+ try {
169+ // Get or create initial private state
170+ const initialPrivateState = await MidnightSetupAPI . getPrivateState ( providers ) ;
171+
172+ // Create contract instance
173+ const contractInstance = new Contract ( { } ) ;
174+
175+ // Real contract join using the wallet
176+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
177+ const existingContract = await findDeployedContract (
178+ providers as any ,
179+ {
180+ contract : contractInstance ,
181+ contractAddress : contractAddress ,
182+ privateStateId : MidnightSetupPrivateStateId ,
183+ initialPrivateState : initialPrivateState ,
184+ }
185+ ) ;
186+
187+ logger ?. info ( "Successfully joined contract" , {
188+ address : existingContract . deployTxData . public . contractAddress
189+ } ) ;
190+ return new MidnightSetupAPI ( providers , existingContract , logger ) ;
191+ } catch ( error ) {
192+ logger ?. error ( "Failed to join contract" , {
193+ error : error instanceof Error ? error . message : String ( error ) ,
194+ contractAddress
195+ } ) ;
196+ throw error ;
197+ }
198+ }
199+
200+ private static async getPrivateState (
201+ providers : MidnightSetupContractProviders
202+ ) : Promise < Record < string , unknown > > {
203+ try {
204+ const existingPrivateState = await providers . privateStateProvider . get (
205+ MidnightSetupPrivateStateId
206+ ) ;
207+
208+ // If no existing state, return empty object (the contract will initialize it)
209+ return existingPrivateState ?? { } ;
210+ } catch ( error ) {
211+ console . warn ( "Error getting private state, returning empty object:" , error ) ;
212+ return { } ;
213+ }
214+ }
215+ }
216+
217+ export * as utils from "./utils.js" ;
218+
219+ export * from "./common-types.js" ;
220+
221+ // Re-export types for external use
222+ export type { ContractStateData , LedgerStateData } from "./common-types.js" ;
0 commit comments