@@ -147,6 +147,7 @@ type DependencyInstaller struct {
147147 installCount int // Track number of dependencies installed
148148 pendingPrompts []pendingPrompt // Dependencies that need prompts after tree display
149149 prompter Prompter // Optional: for testing. If nil, uses real prompts
150+ blockHeightCache map [string ]uint64 // Cache of latest block heights per network for consistent pinning
150151}
151152
152153type Prompter interface {
@@ -204,6 +205,7 @@ func NewDependencyInstaller(logger output.Logger, state *flowkit.State, saveStat
204205 accountAliases : make (map [string ]map [string ]flowsdk.Address ),
205206 pendingPrompts : make ([]pendingPrompt , 0 ),
206207 prompter : prompter {},
208+ blockHeightCache : make (map [string ]uint64 ),
207209 }, nil
208210}
209211
@@ -459,16 +461,60 @@ func (di *DependencyInstaller) processDependency(dependency config.Dependency) e
459461 return di .processDependencies (dependency )
460462}
461463
464+ // getLatestBlockHeight returns the current block height for a given network.
465+ // Results are cached per network to ensure all dependencies in a single install
466+ // operation get pinned to the same block height for consistency.
467+ func (di * DependencyInstaller ) getLatestBlockHeight (network string ) (uint64 , error ) {
468+ // Check cache first
469+ if height , ok := di .blockHeightCache [network ]; ok {
470+ return height , nil
471+ }
472+
473+ gw , ok := di .Gateways [network ]
474+ if ! ok {
475+ return 0 , fmt .Errorf ("gateway for network %s not found" , network )
476+ }
477+
478+ ctx := context .Background ()
479+ latestBlock , err := gw .GetLatestBlock (ctx )
480+ if err != nil {
481+ return 0 , fmt .Errorf ("failed to get latest block: %w" , err )
482+ }
483+
484+ // Cache the result
485+ di .blockHeightCache [network ] = latestBlock .Height
486+ return latestBlock .Height , nil
487+ }
488+
462489func (di * DependencyInstaller ) getContracts (network string , address flowsdk.Address ) (map [string ][]byte , error ) {
490+ return di .getContractsAtBlockHeight (network , address , 0 )
491+ }
492+
493+ // getContractsAtBlockHeight retrieves contracts at a specific block height.
494+ // If blockHeight is 0, it fetches the latest version.
495+ // Uses GetAccountAtBlockHeight from flowkit Gateway interface for historical queries.
496+ func (di * DependencyInstaller ) getContractsAtBlockHeight (network string , address flowsdk.Address , blockHeight uint64 ) (map [string ][]byte , error ) {
463497 gw , ok := di .Gateways [network ]
464498 if ! ok {
465499 return nil , fmt .Errorf ("gateway for network %s not found" , network )
466500 }
467501
468502 ctx := context .Background ()
469- acct , err := gw .GetAccount (ctx , address )
470- if err != nil {
471- return nil , fmt .Errorf ("failed to get account at %s on %s: %w" , address , network , err )
503+ var acct * flowsdk.Account
504+ var err error
505+
506+ if blockHeight > 0 {
507+ // Query at specific block height (historical)
508+ acct , err = gw .GetAccountAtBlockHeight (ctx , address , blockHeight )
509+ if err != nil {
510+ return nil , fmt .Errorf ("failed to get account at block height %d on %s: %w" , blockHeight , network , err )
511+ }
512+ } else {
513+ // Query latest version
514+ acct , err = gw .GetAccount (ctx , address )
515+ if err != nil {
516+ return nil , fmt .Errorf ("failed to get account at %s on %s: %w" , address , network , err )
517+ }
472518 }
473519
474520 if acct == nil {
@@ -533,7 +579,19 @@ func (di *DependencyInstaller) fetchDependenciesWithDepth(dependency config.Depe
533579 return fmt .Errorf ("error adding dependency: %w" , err )
534580 }
535581
536- accountContracts , err := di .getContracts (networkName , address )
582+ // Determine which block height to use for querying
583+ // If the dependency already exists in flow.json with a block height, use it
584+ // This allows us to fetch historical versions when local deps are frozen
585+ var blockHeight uint64
586+ existingDep := di .State .Dependencies ().ByName (dependency .Name )
587+ if existingDep != nil && existingDep .BlockHeight > 0 {
588+ blockHeight = existingDep .BlockHeight
589+ } else {
590+ // For new dependencies or those without block height, use latest (0)
591+ blockHeight = 0
592+ }
593+
594+ accountContracts , err := di .getContractsAtBlockHeight (networkName , address , blockHeight )
537595 if err != nil {
538596 return fmt .Errorf ("error fetching contracts: %w" , err )
539597 }
@@ -916,13 +974,20 @@ func (di *DependencyInstaller) updateDependencyAlias(contractName, aliasNetwork
916974}
917975
918976func (di * DependencyInstaller ) updateDependencyState (originalDependency config.Dependency , contractHash string ) error {
977+ // Get current block height for the source network to pin the dependency
978+ blockHeight , err := di .getLatestBlockHeight (originalDependency .Source .NetworkName )
979+ if err != nil {
980+ return fmt .Errorf ("failed to get block height for pinning dependency %s: %w" , originalDependency .Name , err )
981+ }
982+
919983 // Create the dependency to save, preserving aliases and canonical from the original
920984 dep := config.Dependency {
921- Name : originalDependency .Name ,
922- Source : originalDependency .Source ,
923- Hash : contractHash ,
924- Aliases : originalDependency .Aliases ,
925- Canonical : originalDependency .Canonical ,
985+ Name : originalDependency .Name ,
986+ Source : originalDependency .Source ,
987+ Hash : contractHash ,
988+ BlockHeight : blockHeight ,
989+ Aliases : originalDependency .Aliases ,
990+ Canonical : originalDependency .Canonical ,
926991 }
927992
928993 isNewDep := di .State .Dependencies ().ByName (dep .Name ) == nil
0 commit comments