From a950c717e07eefbb558c7d359e4e108212881216 Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sat, 30 May 2026 23:40:10 +0100 Subject: [PATCH 01/24] chore: add required imports for contract read-only function calls - Import callReadOnlyFunction for querying contract state - Import cvToValue for parsing Clarity values - Support read-only operations on migration contract --- frontend/src/services/MigrationService.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/frontend/src/services/MigrationService.ts b/frontend/src/services/MigrationService.ts index 04e5e5ce..cd5623a1 100644 --- a/frontend/src/services/MigrationService.ts +++ b/frontend/src/services/MigrationService.ts @@ -1,5 +1,13 @@ import { openContractCall } from '@stacks/connect'; -import { uintCV, stringUtf8CV, bufferCV, boolCV, PostConditionMode } from '@stacks/transactions'; +import { + uintCV, + stringUtf8CV, + bufferCV, + boolCV, + PostConditionMode, + callReadOnlyFunction, + cvToValue, +} from '@stacks/transactions'; export interface Migration { migrationId: number; From b3288eaee45f8bf727130d14adafb8f52279c660 Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sat, 30 May 2026 23:40:16 +0100 Subject: [PATCH 02/24] refactor: add network parameter to MigrationService constructor - Support optional network configuration for contract operations - Enable flexibility in test and production environments - Store network for use in read-only function calls --- frontend/src/services/MigrationService.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/src/services/MigrationService.ts b/frontend/src/services/MigrationService.ts index cd5623a1..32c6e1c0 100644 --- a/frontend/src/services/MigrationService.ts +++ b/frontend/src/services/MigrationService.ts @@ -26,9 +26,11 @@ export interface MigrationData { export class MigrationService { private migrationContract: { address: string; name: string }; + private network: any; - constructor(migrationContract: { address: string; name: string }) { + constructor(migrationContract: { address: string; name: string }, network?: any) { this.migrationContract = migrationContract; + this.network = network; } async registerMigration( From 88175e4caab23297b1911b281673ca73d74cdc46 Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sat, 30 May 2026 23:40:21 +0100 Subject: [PATCH 03/24] refactor: add helper method for contract address parsing - Extract base address from contract name format (address.contract-name) - Handle both qualified and unqualified contract addresses - Support flexibility in contract address specification --- frontend/src/services/MigrationService.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frontend/src/services/MigrationService.ts b/frontend/src/services/MigrationService.ts index 32c6e1c0..5ab3fdd0 100644 --- a/frontend/src/services/MigrationService.ts +++ b/frontend/src/services/MigrationService.ts @@ -102,6 +102,13 @@ export class MigrationService { return 1; } + private extractAddress(addressInput: string): string { + if (addressInput.includes('.')) { + return addressInput.split('.')[0]; + } + return addressInput; + } + async getMigration(migrationId: number): Promise { return null; } From 9e79a91c976f0383b5bba5e6319c447226aec5d9 Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sat, 30 May 2026 23:40:27 +0100 Subject: [PATCH 04/24] feat: implement getCurrentVersion method with contract call - Query get-current-version read-only function from migration contract - Parse returned Clarity value and convert to number - Fallback to version 1 on error or invalid response - Add comprehensive error handling and logging --- frontend/src/services/MigrationService.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/frontend/src/services/MigrationService.ts b/frontend/src/services/MigrationService.ts index 5ab3fdd0..8eba6fc9 100644 --- a/frontend/src/services/MigrationService.ts +++ b/frontend/src/services/MigrationService.ts @@ -99,7 +99,26 @@ export class MigrationService { } async getCurrentVersion(): Promise { - return 1; + try { + const contractAddress = this.extractAddress(this.migrationContract.address); + const response = await callReadOnlyFunction({ + contractAddress, + contractName: this.migrationContract.name, + functionName: 'get-current-version', + functionArgs: [], + network: this.network, + senderAddress: contractAddress, + }); + + if (response.ok && typeof response.value === 'object') { + const value = cvToValue(response.value); + return typeof value === 'number' ? value : 1; + } + return 1; + } catch (error) { + console.error('Failed to get current version:', error); + return 1; + } } private extractAddress(addressInput: string): string { From 177e6ddccb53d6a9375babaf67bd0c66364cc1c6 Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sat, 30 May 2026 23:40:34 +0100 Subject: [PATCH 05/24] feat: implement getMigration method to query migration details - Call get-migration read-only function with migration ID parameter - Parse response and map contract fields to Migration interface - Handle optional fields (executedAt, executedBy) - Support snake_case contract field names - Return null if migration not found or on error --- frontend/src/services/MigrationService.ts | 32 ++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/frontend/src/services/MigrationService.ts b/frontend/src/services/MigrationService.ts index 8eba6fc9..6704106b 100644 --- a/frontend/src/services/MigrationService.ts +++ b/frontend/src/services/MigrationService.ts @@ -129,7 +129,37 @@ export class MigrationService { } async getMigration(migrationId: number): Promise { - return null; + try { + const contractAddress = this.extractAddress(this.migrationContract.address); + const response = await callReadOnlyFunction({ + contractAddress, + contractName: this.migrationContract.name, + functionName: 'get-migration', + functionArgs: [uintCV(migrationId)], + network: this.network, + senderAddress: contractAddress, + }); + + if (response.ok && response.value) { + const result = cvToValue(response.value); + if (result && typeof result === 'object') { + const migration = result as any; + return { + migrationId, + version: migration.version || 0, + description: migration.description || '', + executed: migration.executed || false, + executedAt: migration['executed-at'] || undefined, + executedBy: migration['executed-by'] || undefined, + rollbackAvailable: migration['rollback-available'] || false, + }; + } + } + return null; + } catch (error) { + console.error(`Failed to get migration ${migrationId}:`, error); + return null; + } } async getMigrationData(migrationId: number): Promise { From 9c268a565bef3c539be8d8c576748e1cfa8d3b61 Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sat, 30 May 2026 23:40:40 +0100 Subject: [PATCH 06/24] feat: implement getMigrationData method for data access - Query get-migration-data read-only function with migration ID - Extract data hash and size from contract response - Map contract response fields to MigrationData interface - Return empty Uint8Array and 0 for missing values - Return null if data not found or on error --- frontend/src/services/MigrationService.ts | 27 ++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/frontend/src/services/MigrationService.ts b/frontend/src/services/MigrationService.ts index 6704106b..6b3d1a43 100644 --- a/frontend/src/services/MigrationService.ts +++ b/frontend/src/services/MigrationService.ts @@ -163,7 +163,32 @@ export class MigrationService { } async getMigrationData(migrationId: number): Promise { - return null; + try { + const contractAddress = this.extractAddress(this.migrationContract.address); + const response = await callReadOnlyFunction({ + contractAddress, + contractName: this.migrationContract.name, + functionName: 'get-migration-data', + functionArgs: [uintCV(migrationId)], + network: this.network, + senderAddress: contractAddress, + }); + + if (response.ok && response.value) { + const result = cvToValue(response.value); + if (result && typeof result === 'object') { + const data = result as any; + return { + dataHash: data['data-hash'] || new Uint8Array(), + dataSize: data['data-size'] || 0, + }; + } + } + return null; + } catch (error) { + console.error(`Failed to get migration data ${migrationId}:`, error); + return null; + } } async isMigrationExecuted(migrationId: number): Promise { From 4dd56186434f4bb054c860fc4dddfd43ee50a98f Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sat, 30 May 2026 23:40:45 +0100 Subject: [PATCH 07/24] feat: implement isMigrationExecuted method for status checking - Query is-migration-executed read-only function - Check migration execution status on contract - Return boolean indicating if migration has been executed - Fallback to false on error or invalid response - Add error handling and logging for execution status checks --- frontend/src/services/MigrationService.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/frontend/src/services/MigrationService.ts b/frontend/src/services/MigrationService.ts index 6b3d1a43..57ed908d 100644 --- a/frontend/src/services/MigrationService.ts +++ b/frontend/src/services/MigrationService.ts @@ -192,7 +192,26 @@ export class MigrationService { } async isMigrationExecuted(migrationId: number): Promise { - return false; + try { + const contractAddress = this.extractAddress(this.migrationContract.address); + const response = await callReadOnlyFunction({ + contractAddress, + contractName: this.migrationContract.name, + functionName: 'is-migration-executed', + functionArgs: [uintCV(migrationId)], + network: this.network, + senderAddress: contractAddress, + }); + + if (response.ok && typeof response.value === 'object') { + const value = cvToValue(response.value); + return value === true; + } + return false; + } catch (error) { + console.error(`Failed to check migration execution ${migrationId}:`, error); + return false; + } } async getMigrationCount(): Promise { From ca60beb0f1c807a70a67de67f7bf02977cec80d4 Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sat, 30 May 2026 23:40:51 +0100 Subject: [PATCH 08/24] feat: implement getMigrationCount method to query total migrations - Query get-migration-count read-only function - Return total number of migrations registered in contract - Parse Clarity integer response and convert to JavaScript number - Fallback to 0 on error or invalid response - Add error handling and logging --- frontend/src/services/MigrationService.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/frontend/src/services/MigrationService.ts b/frontend/src/services/MigrationService.ts index 57ed908d..238f5839 100644 --- a/frontend/src/services/MigrationService.ts +++ b/frontend/src/services/MigrationService.ts @@ -215,6 +215,25 @@ export class MigrationService { } async getMigrationCount(): Promise { - return 0; + try { + const contractAddress = this.extractAddress(this.migrationContract.address); + const response = await callReadOnlyFunction({ + contractAddress, + contractName: this.migrationContract.name, + functionName: 'get-migration-count', + functionArgs: [], + network: this.network, + senderAddress: contractAddress, + }); + + if (response.ok && typeof response.value === 'object') { + const value = cvToValue(response.value); + return typeof value === 'number' ? value : 0; + } + return 0; + } catch (error) { + console.error('Failed to get migration count:', error); + return 0; + } } } From a1404ece4573a6be8cefba94d7cc1516ebe3b668 Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sun, 31 May 2026 05:47:21 +0100 Subject: [PATCH 09/24] chore: add required imports for contract read-only function calls - Import callReadOnlyFunction for querying contract state - Import cvToValue for parsing Clarity values - Support read-only operations on proxy upgrade contract --- frontend/src/services/ContractUpgradeService.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/frontend/src/services/ContractUpgradeService.ts b/frontend/src/services/ContractUpgradeService.ts index edfcc59f..50611bbe 100644 --- a/frontend/src/services/ContractUpgradeService.ts +++ b/frontend/src/services/ContractUpgradeService.ts @@ -1,5 +1,13 @@ import { openContractCall } from '@stacks/connect'; -import { uintCV, principalCV, bufferCV, stringUtf8CV, PostConditionMode } from '@stacks/transactions'; +import { + uintCV, + principalCV, + bufferCV, + stringUtf8CV, + PostConditionMode, + callReadOnlyFunction, + cvToValue, +} from '@stacks/transactions'; export interface UpgradeProposal { newImplementation: string; From 69c6805647dfb754c610e4c3c4dae207dd3ed6d4 Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sun, 31 May 2026 05:47:29 +0100 Subject: [PATCH 10/24] refactor: add network parameter to ContractUpgradeService constructor - Support optional network configuration for contract operations - Enable flexibility in test and production environments - Store network for use in read-only function calls --- frontend/src/services/ContractUpgradeService.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/src/services/ContractUpgradeService.ts b/frontend/src/services/ContractUpgradeService.ts index 50611bbe..1fdc8a9e 100644 --- a/frontend/src/services/ContractUpgradeService.ts +++ b/frontend/src/services/ContractUpgradeService.ts @@ -26,9 +26,11 @@ export interface UpgradeHistory { export class ContractUpgradeService { private proxyContract: { address: string; name: string }; + private network: any; - constructor(proxyContract: { address: string; name: string }) { + constructor(proxyContract: { address: string; name: string }, network?: any) { this.proxyContract = proxyContract; + this.network = network; } async proposeUpgrade(newImplementation: string, userAddress: string): Promise { From 63b09645527de963ea609de6418fe0eb851962b6 Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sun, 31 May 2026 05:47:36 +0100 Subject: [PATCH 11/24] refactor: add helper method for contract address parsing - Extract base address from contract name format (address.contract-name) - Handle both qualified and unqualified contract addresses - Support flexibility in contract address specification --- frontend/src/services/ContractUpgradeService.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frontend/src/services/ContractUpgradeService.ts b/frontend/src/services/ContractUpgradeService.ts index 1fdc8a9e..f2ca4f92 100644 --- a/frontend/src/services/ContractUpgradeService.ts +++ b/frontend/src/services/ContractUpgradeService.ts @@ -101,6 +101,13 @@ export class ContractUpgradeService { return null; } + private extractAddress(addressInput: string): string { + if (addressInput.includes('.')) { + return addressInput.split('.')[0]; + } + return addressInput; + } + async getPendingUpgrade(): Promise { return null; } From 67700827e199c5e9c9d40646316d7dce9864b7cb Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sun, 31 May 2026 05:47:45 +0100 Subject: [PATCH 12/24] feat: implement getImplementation method to query current implementation - Call get-implementation read-only function from proxy contract - Retrieve current contract implementation address - Parse and validate response from Clarity - Return null on error or invalid response --- .../src/services/ContractUpgradeService.ts | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/frontend/src/services/ContractUpgradeService.ts b/frontend/src/services/ContractUpgradeService.ts index f2ca4f92..f5a842f8 100644 --- a/frontend/src/services/ContractUpgradeService.ts +++ b/frontend/src/services/ContractUpgradeService.ts @@ -98,7 +98,26 @@ export class ContractUpgradeService { } async getImplementation(): Promise { - return null; + try { + const contractAddress = this.extractAddress(this.proxyContract.address); + const response = await callReadOnlyFunction({ + contractAddress, + contractName: this.proxyContract.name, + functionName: 'get-implementation', + functionArgs: [], + network: this.network, + senderAddress: contractAddress, + }); + + if (response.ok && response.value) { + const value = cvToValue(response.value); + return typeof value === 'string' ? value : null; + } + return null; + } catch (error) { + console.error('Failed to get implementation:', error); + return null; + } } private extractAddress(addressInput: string): string { From 2212e12c0fbf3f361f850286b3e567d1609d3a2d Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sun, 31 May 2026 05:47:54 +0100 Subject: [PATCH 13/24] feat: implement getPendingUpgrade method to query pending implementation - Call get-pending-implementation read-only function - Retrieve pending upgrade proposal if exists - Map response to UpgradeProposal interface - Return null if no pending upgrade or on error --- .../src/services/ContractUpgradeService.ts | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/frontend/src/services/ContractUpgradeService.ts b/frontend/src/services/ContractUpgradeService.ts index f5a842f8..1d1a6432 100644 --- a/frontend/src/services/ContractUpgradeService.ts +++ b/frontend/src/services/ContractUpgradeService.ts @@ -128,7 +128,36 @@ export class ContractUpgradeService { } async getPendingUpgrade(): Promise { - return null; + try { + const contractAddress = this.extractAddress(this.proxyContract.address); + const response = await callReadOnlyFunction({ + contractAddress, + contractName: this.proxyContract.name, + functionName: 'get-pending-implementation', + functionArgs: [], + network: this.network, + senderAddress: contractAddress, + }); + + if (response.ok && response.value) { + const result = cvToValue(response.value); + if (result && typeof result === 'object') { + return { + newImplementation: result as string, + proposedAt: 0, + proposedBy: '', + timelockExpires: 0, + }; + } + if (result === null) { + return null; + } + } + return null; + } catch (error) { + console.error('Failed to get pending upgrade:', error); + return null; + } } async getUpgradeHistory(upgradeId: number): Promise { From 764f704214ea3177334f31a07ebba327ad112143 Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sun, 31 May 2026 05:48:04 +0100 Subject: [PATCH 14/24] feat: implement getUpgradeHistory method to retrieve upgrade records - Query get-upgrade-history read-only function with upgrade ID - Map contract response fields to UpgradeHistory interface - Handle snake_case contract field names - Return null if upgrade record not found or on error --- .../src/services/ContractUpgradeService.ts | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/frontend/src/services/ContractUpgradeService.ts b/frontend/src/services/ContractUpgradeService.ts index 1d1a6432..632c1291 100644 --- a/frontend/src/services/ContractUpgradeService.ts +++ b/frontend/src/services/ContractUpgradeService.ts @@ -161,7 +161,35 @@ export class ContractUpgradeService { } async getUpgradeHistory(upgradeId: number): Promise { - return null; + try { + const contractAddress = this.extractAddress(this.proxyContract.address); + const response = await callReadOnlyFunction({ + contractAddress, + contractName: this.proxyContract.name, + functionName: 'get-upgrade-history', + functionArgs: [uintCV(upgradeId)], + network: this.network, + senderAddress: contractAddress, + }); + + if (response.ok && response.value) { + const result = cvToValue(response.value); + if (result && typeof result === 'object') { + const history = result as any; + return { + upgradeId, + fromImplementation: history['from-implementation'] || '', + toImplementation: history['to-implementation'] || '', + upgradedAt: history['upgraded-at'] || 0, + upgradedBy: history['upgraded-by'] || '', + }; + } + } + return null; + } catch (error) { + console.error(`Failed to get upgrade history ${upgradeId}:`, error); + return null; + } } async getUpgradeCount(): Promise { From 1d78d6ab7ed2cd6a730d966e285b395374b0b659 Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sun, 31 May 2026 05:48:12 +0100 Subject: [PATCH 15/24] feat: implement getUpgradeCount method to query total upgrades - Query get-upgrade-count read-only function - Return total number of contract upgrades executed - Parse Clarity integer response and convert to JavaScript number - Fallback to 0 on error or invalid response --- .../src/services/ContractUpgradeService.ts | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/frontend/src/services/ContractUpgradeService.ts b/frontend/src/services/ContractUpgradeService.ts index 632c1291..7a255648 100644 --- a/frontend/src/services/ContractUpgradeService.ts +++ b/frontend/src/services/ContractUpgradeService.ts @@ -193,6 +193,25 @@ export class ContractUpgradeService { } async getUpgradeCount(): Promise { - return 0; + try { + const contractAddress = this.extractAddress(this.proxyContract.address); + const response = await callReadOnlyFunction({ + contractAddress, + contractName: this.proxyContract.name, + functionName: 'get-upgrade-count', + functionArgs: [], + network: this.network, + senderAddress: contractAddress, + }); + + if (response.ok && typeof response.value === 'object') { + const value = cvToValue(response.value); + return typeof value === 'number' ? value : 0; + } + return 0; + } catch (error) { + console.error('Failed to get upgrade count:', error); + return 0; + } } } From 39527065476b6ae9aebd0bcc01d5b77f134919b9 Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sun, 31 May 2026 05:48:40 +0100 Subject: [PATCH 16/24] feat: implement getUpgradeTimelock method to query timelock blocks - Query get-upgrade-timelock read-only function - Return configured timelock block duration for upgrades - Parse and validate Clarity integer response - Return null on error or invalid response --- .../src/services/ContractUpgradeService.ts | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/frontend/src/services/ContractUpgradeService.ts b/frontend/src/services/ContractUpgradeService.ts index 7a255648..bb1c1320 100644 --- a/frontend/src/services/ContractUpgradeService.ts +++ b/frontend/src/services/ContractUpgradeService.ts @@ -214,4 +214,50 @@ export class ContractUpgradeService { return 0; } } + + async getUpgradeTimelock(): Promise { + try { + const contractAddress = this.extractAddress(this.proxyContract.address); + const response = await callReadOnlyFunction({ + contractAddress, + contractName: this.proxyContract.name, + functionName: 'get-upgrade-timelock', + functionArgs: [], + network: this.network, + senderAddress: contractAddress, + }); + + if (response.ok && typeof response.value === 'object') { + const value = cvToValue(response.value); + return typeof value === 'number' ? value : null; + } + return null; + } catch (error) { + console.error('Failed to get upgrade timelock:', error); + return null; + } + } + + async getOwner(): Promise { + try { + const contractAddress = this.extractAddress(this.proxyContract.address); + const response = await callReadOnlyFunction({ + contractAddress, + contractName: this.proxyContract.name, + functionName: 'get-owner', + functionArgs: [], + network: this.network, + senderAddress: contractAddress, + }); + + if (response.ok && response.value) { + const value = cvToValue(response.value); + return typeof value === 'string' ? value : null; + } + return null; + } catch (error) { + console.error('Failed to get owner:', error); + return null; + } + } } From 912047abe50d5d390a84ea51e532681c2a2108fe Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sun, 31 May 2026 05:49:49 +0100 Subject: [PATCH 17/24] feat: add isUpgradePending utility method for status checks - Check if an upgrade is currently pending execution - Query pending upgrade status - Return boolean indicating pending state - Simplify upgrade state management --- frontend/src/services/ContractUpgradeService.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/frontend/src/services/ContractUpgradeService.ts b/frontend/src/services/ContractUpgradeService.ts index bb1c1320..c8a3f532 100644 --- a/frontend/src/services/ContractUpgradeService.ts +++ b/frontend/src/services/ContractUpgradeService.ts @@ -260,4 +260,16 @@ export class ContractUpgradeService { return null; } } + + async isUpgradePending(): Promise { + const pending = await this.getPendingUpgrade(); + return pending !== null; + } + + private extractAddress(addressInput: string): string { + if (addressInput.includes('.')) { + return addressInput.split('.')[0]; + } + return addressInput; + } } From d7f75c5dd6cf1ddd7d0aa765a0c0fceb454584b1 Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sun, 31 May 2026 05:49:59 +0100 Subject: [PATCH 18/24] feat: add canExecuteUpgrade method for upgrade readiness check - Verify pending upgrade exists - Check timelock requirement is configured - Return boolean indicating readiness for execution - Provide pre-execution validation --- frontend/src/services/ContractUpgradeService.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/frontend/src/services/ContractUpgradeService.ts b/frontend/src/services/ContractUpgradeService.ts index c8a3f532..493a388c 100644 --- a/frontend/src/services/ContractUpgradeService.ts +++ b/frontend/src/services/ContractUpgradeService.ts @@ -266,6 +266,20 @@ export class ContractUpgradeService { return pending !== null; } + async canExecuteUpgrade(): Promise { + const pending = await this.getPendingUpgrade(); + if (!pending) { + return false; + } + + const timelock = await this.getUpgradeTimelock(); + if (timelock === null) { + return false; + } + + return true; + } + private extractAddress(addressInput: string): string { if (addressInput.includes('.')) { return addressInput.split('.')[0]; From 31ee7a41266a4b15658394353bf0d89704faeac6 Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sun, 31 May 2026 05:50:08 +0100 Subject: [PATCH 19/24] feat: add getUpgradeHistoryBatch method for batch retrieval - Query multiple upgrade history records in sequence - Accept start ID and count parameters - Return array of UpgradeHistory or null values - Enable efficient history pagination --- frontend/src/services/ContractUpgradeService.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/frontend/src/services/ContractUpgradeService.ts b/frontend/src/services/ContractUpgradeService.ts index 493a388c..69884bb1 100644 --- a/frontend/src/services/ContractUpgradeService.ts +++ b/frontend/src/services/ContractUpgradeService.ts @@ -280,6 +280,15 @@ export class ContractUpgradeService { return true; } + async getUpgradeHistoryBatch(startId: number, count: number): Promise<(UpgradeHistory | null)[]> { + const results: (UpgradeHistory | null)[] = []; + for (let i = startId; i < startId + count; i++) { + const history = await this.getUpgradeHistory(i); + results.push(history); + } + return results; + } + private extractAddress(addressInput: string): string { if (addressInput.includes('.')) { return addressInput.split('.')[0]; From 9fd5a157e532780b27a24f08c90cf9726c491dc9 Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sun, 31 May 2026 05:50:17 +0100 Subject: [PATCH 20/24] feat: add validateImplementationAddress method for address validation - Validate principal address format (ST/SM prefix) - Check address string type and non-empty - Return boolean indicating validity - Prevent invalid implementation address proposals --- frontend/src/services/ContractUpgradeService.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/frontend/src/services/ContractUpgradeService.ts b/frontend/src/services/ContractUpgradeService.ts index 69884bb1..09bf80ee 100644 --- a/frontend/src/services/ContractUpgradeService.ts +++ b/frontend/src/services/ContractUpgradeService.ts @@ -289,6 +289,15 @@ export class ContractUpgradeService { return results; } + async validateImplementationAddress(address: string): Promise { + if (!address || typeof address !== 'string') { + return false; + } + + const principalRegex = /^(ST|SM)[A-Z0-9]+$/i; + return principalRegex.test(address); + } + private extractAddress(addressInput: string): string { if (addressInput.includes('.')) { return addressInput.split('.')[0]; From f5a65b3f4d8d02b84f8d674347301ee1804a0eaa Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sun, 31 May 2026 05:50:26 +0100 Subject: [PATCH 21/24] feat: add compareImplementations method for upgrade diff analysis - Retrieve current and pending implementations - Compare addresses for equality - Return object with current, pending, and same flag - Enable upgrade preview before execution --- frontend/src/services/ContractUpgradeService.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/frontend/src/services/ContractUpgradeService.ts b/frontend/src/services/ContractUpgradeService.ts index 09bf80ee..0c179a6f 100644 --- a/frontend/src/services/ContractUpgradeService.ts +++ b/frontend/src/services/ContractUpgradeService.ts @@ -298,6 +298,21 @@ export class ContractUpgradeService { return principalRegex.test(address); } + async compareImplementations(): Promise<{ + current: string | null; + pending: UpgradeProposal | null; + same: boolean; + }> { + const current = await this.getImplementation(); + const pending = await this.getPendingUpgrade(); + + return { + current, + pending, + same: current === pending?.newImplementation, + }; + } + private extractAddress(addressInput: string): string { if (addressInput.includes('.')) { return addressInput.split('.')[0]; From 979ea930f8014c3f3721d6d56cf632f3f24b602e Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sun, 31 May 2026 05:50:40 +0100 Subject: [PATCH 22/24] feat: add getUpgradeMetadata method for comprehensive status snapshot - Retrieve all upgrade-related metadata in parallel - Include owner, implementations, timelock, and count - Efficient parallel Promise.all execution - Single API for complete upgrade state --- .../src/services/ContractUpgradeService.ts | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/frontend/src/services/ContractUpgradeService.ts b/frontend/src/services/ContractUpgradeService.ts index 0c179a6f..2fe0008b 100644 --- a/frontend/src/services/ContractUpgradeService.ts +++ b/frontend/src/services/ContractUpgradeService.ts @@ -313,6 +313,30 @@ export class ContractUpgradeService { }; } + async getUpgradeMetadata(): Promise<{ + owner: string | null; + current: string | null; + pending: UpgradeProposal | null; + timelock: number | null; + count: number; + }> { + const [owner, current, pending, timelock, count] = await Promise.all([ + this.getOwner(), + this.getImplementation(), + this.getPendingUpgrade(), + this.getUpgradeTimelock(), + this.getUpgradeCount(), + ]); + + return { + owner, + current, + pending, + timelock, + count, + }; + } + private extractAddress(addressInput: string): string { if (addressInput.includes('.')) { return addressInput.split('.')[0]; From ae8c9223615f7a6bce4b2a0baa3c2001f9b5c44c Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sun, 31 May 2026 05:50:57 +0100 Subject: [PATCH 23/24] feat: add isImplementationValid method for proposal validation - Validate implementation address format and uniqueness - Check address differs from current implementation - Verify format before equality comparison - Prevent invalid or unchanged upgrades --- frontend/src/services/ContractUpgradeService.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/frontend/src/services/ContractUpgradeService.ts b/frontend/src/services/ContractUpgradeService.ts index 2fe0008b..631d5bfa 100644 --- a/frontend/src/services/ContractUpgradeService.ts +++ b/frontend/src/services/ContractUpgradeService.ts @@ -337,6 +337,16 @@ export class ContractUpgradeService { }; } + async isImplementationValid(address: string): Promise { + const isValid = await this.validateImplementationAddress(address); + if (!isValid) { + return false; + } + + const current = await this.getImplementation(); + return address !== current; + } + private extractAddress(addressInput: string): string { if (addressInput.includes('.')) { return addressInput.split('.')[0]; From 796848f9916841995e8963c671fecad3ed98b42d Mon Sep 17 00:00:00 2001 From: 0xMosas Date: Sun, 31 May 2026 05:51:08 +0100 Subject: [PATCH 24/24] feat: add getLastUpgradeRecord method for recent upgrade tracking - Retrieve the most recent upgrade execution record - Calculate last upgrade ID from total count - Return null if no upgrades executed - Enable upgrade history analysis --- frontend/src/services/ContractUpgradeService.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/frontend/src/services/ContractUpgradeService.ts b/frontend/src/services/ContractUpgradeService.ts index 631d5bfa..66d348a7 100644 --- a/frontend/src/services/ContractUpgradeService.ts +++ b/frontend/src/services/ContractUpgradeService.ts @@ -347,6 +347,15 @@ export class ContractUpgradeService { return address !== current; } + async getLastUpgradeRecord(): Promise { + const count = await this.getUpgradeCount(); + if (count === 0) { + return null; + } + + return this.getUpgradeHistory(count - 1); + } + private extractAddress(addressInput: string): string { if (addressInput.includes('.')) { return addressInput.split('.')[0];