From 568b7d43ddb6183c3341a9a2a7b5b03c811c8752 Mon Sep 17 00:00:00 2001 From: tamir Date: Wed, 24 Dec 2025 09:45:53 +0200 Subject: [PATCH 1/7] Updated solver schemas --- .../migrations/schema-changes-migration.js | 70 +++++++++++++++++ .../solver-bonding-pool/schema.json | 14 ++-- .../solver-network/lifecycles.ts | 53 +++++++++++++ .../content-types/solver-network/schema.json | 14 +++- .../solver/content-types/solver/lifecycles.ts | 78 +++++++++++++++++++ .../solver/content-types/solver/schema.json | 8 ++ 6 files changed, 229 insertions(+), 8 deletions(-) create mode 100644 database/migrations/schema-changes-migration.js create mode 100644 src/api/solver-network/content-types/solver-network/lifecycles.ts create mode 100644 src/api/solver/content-types/solver/lifecycles.ts diff --git a/database/migrations/schema-changes-migration.js b/database/migrations/schema-changes-migration.js new file mode 100644 index 0000000..9edd693 --- /dev/null +++ b/database/migrations/schema-changes-migration.js @@ -0,0 +1,70 @@ +async function up(knex) { + // 1. Update Solver Bonding Pool: rename bonding_pool to name + // First, get all solver bonding pools with their bonding_pool relation + const solverBondingPools = await knex('solver_bonding_pools') + .select('id', 'bonding_pool_id') + .whereNotNull('bonding_pool_id'); + + // For each solver bonding pool, get the name from bonding_pool and update the solver bonding pool + for (const sbp of solverBondingPools) { + if (sbp.bonding_pool_id) { + // Get the name from bonding_pool + const bondingPool = await knex('bonding_pools') + .select('name') + .where('id', sbp.bonding_pool_id) + .first(); + + if (bondingPool && bondingPool.name) { + // Update the solver bonding pool with the name + await knex('solver_bonding_pools') + .where('id', sbp.id) + .update({ + name: bondingPool.name + }); + } + } + } + + // 2. Calculate activeNetworks for all solvers + const solvers = await knex('solvers').select('id'); + + for (const solver of solvers) { + // Get all active networks for this solver + const activeNetworks = await knex('solver_networks') + .join('networks', 'solver_networks.network_id', 'networks.id') + .where({ + 'solver_networks.solver_id': solver.id, + 'solver_networks.active': true + }) + .select('networks.name'); + + // Extract network names + const networkNames = activeNetworks.map(n => n.name); + + // Update the solver with activeNetworks and hasActiveNetworks + await knex('solvers') + .where('id', solver.id) + .update({ + activeNetworks: JSON.stringify(networkNames), + hasActiveNetworks: networkNames.length > 0 + }); + } + + // 3. Ensure all required fields are set for Solver Network + // Make a list of solver networks that have isVouched=true but no vouchedBy + const vouchedNetworks = await knex('solver_networks') + .where('isVouched', true) + .whereNull('vouchedBy'); + + console.log(`Found ${vouchedNetworks.length} solver networks with isVouched=true but no vouchedBy`); + + return; +} + +async function down(knex) { + // This migration cannot be reversed automatically + console.log('This migration cannot be reversed automatically'); + return; +} + +module.exports = { up, down }; diff --git a/src/api/solver-bonding-pool/content-types/solver-bonding-pool/schema.json b/src/api/solver-bonding-pool/content-types/solver-bonding-pool/schema.json index b116b8d..e8705d9 100644 --- a/src/api/solver-bonding-pool/content-types/solver-bonding-pool/schema.json +++ b/src/api/solver-bonding-pool/content-types/solver-bonding-pool/schema.json @@ -21,11 +21,9 @@ "type": "datetime", "required": true }, - "bonding_pool": { - "type": "relation", - "relation": "manyToOne", - "target": "api::bonding-pool.bonding-pool", - "inversedBy": "solver_bonding_pools" + "name": { + "type": "string", + "required": true }, "solvers": { "type": "relation", @@ -37,6 +35,12 @@ "type": "boolean", "required": true, "default": false + }, + "solver_networks": { + "type": "relation", + "relation": "oneToMany", + "target": "api::solver-network.solver-network", + "mappedBy": "vouchedBy" } } } diff --git a/src/api/solver-network/content-types/solver-network/lifecycles.ts b/src/api/solver-network/content-types/solver-network/lifecycles.ts new file mode 100644 index 0000000..2060a61 --- /dev/null +++ b/src/api/solver-network/content-types/solver-network/lifecycles.ts @@ -0,0 +1,53 @@ +import { calculateActiveNetworksForSolver } from '../../../solver/content-types/solver/lifecycles'; + +export default { + async beforeCreate(event) { + await validateVouchedBy(event); + }, + + async beforeUpdate(event) { + await validateVouchedBy(event); + }, + + async afterCreate(event) { + await updateParentSolver(event); + }, + + async afterUpdate(event) { + await updateParentSolver(event); + }, + + async afterDelete(event) { + await updateParentSolver(event); + }, +}; + +async function validateVouchedBy(event) { + const { data } = event.params; + + // Check if isVouched is true and vouchedBy is not set + if (data.isVouched === true && !data.vouchedBy) { + throw new Error('vouchedBy relationship must be set when isVouched is true'); + } +} + +async function updateParentSolver(event) { + try { + const { result } = event; + + // If this is a delete operation, result might not be available + if (!result || !result.solver) { + return; + } + + // Get the solver ID + const solverId = result.solver.id || result.solver; + + if (solverId) { + // Update the solver's activeNetworks field + await calculateActiveNetworksForSolver(solverId); + } + } catch (error) { + console.error('Error updating parent solver:', error); + } +} diff --git a/src/api/solver-network/content-types/solver-network/schema.json b/src/api/solver-network/content-types/solver-network/schema.json index fe21831..0fd3d68 100644 --- a/src/api/solver-network/content-types/solver-network/schema.json +++ b/src/api/solver-network/content-types/solver-network/schema.json @@ -22,11 +22,13 @@ "type": "relation", "relation": "manyToOne", "target": "api::network.network", - "inversedBy": "solver_networks" + "inversedBy": "solver_networks", + "required": true }, "address": { "type": "string", - "regex": "^(0x)?[0-9a-fA-F]{40}$" + "regex": "^(0x)?[0-9a-fA-F]{40}$", + "required": true }, "payoutAddress": { "type": "string", @@ -40,7 +42,8 @@ "environment": { "type": "relation", "relation": "oneToOne", - "target": "api::environment.environment" + "target": "api::environment.environment", + "required": true }, "isWhiteListed": { "type": "boolean", @@ -54,6 +57,11 @@ "type": "boolean", "default": false, "required": true + }, + "vouchedBy": { + "type": "relation", + "relation": "manyToOne", + "target": "api::solver-bonding-pool.solver-bonding-pool" } } } diff --git a/src/api/solver/content-types/solver/lifecycles.ts b/src/api/solver/content-types/solver/lifecycles.ts new file mode 100644 index 0000000..b56a3af --- /dev/null +++ b/src/api/solver/content-types/solver/lifecycles.ts @@ -0,0 +1,78 @@ +export default { + async beforeCreate(event) { + await updateActiveNetworks(event); + }, + + async beforeUpdate(event) { + await updateActiveNetworks(event); + }, +}; + +async function updateActiveNetworks(event) { + const { data, where } = event.params; + + // If this is an update operation and we're not updating solver_networks relation + if (where && !data.solver_networks) { + // Get the current solver data to check if we need to update activeNetworks + const solver = await strapi.entityService.findOne( + 'api::solver.solver', + where.id, + { populate: ['solver_networks.network'] } + ); + + if (solver) { + // Calculate active networks + await calculateActiveNetworks(solver, data); + } + } else if (data.solver_networks) { + // For create or when solver_networks is being updated + // We'll need to fetch the networks after the operation in afterCreate/afterUpdate + // as the relations might not be fully established yet + } +} + +// This function will be called after create/update to ensure relations are established +export async function calculateActiveNetworksForSolver(solverId) { + try { + // Get the solver with its networks + const solver = await strapi.entityService.findOne( + 'api::solver.solver', + solverId, + { populate: ['solver_networks.network'] } + ); + + if (solver) { + // Calculate active networks + const data = {}; + await calculateActiveNetworks(solver, data); + + // Update the solver with the calculated values + if (data.activeNetworks || data.hasActiveNetworks !== undefined) { + await strapi.entityService.update( + 'api::solver.solver', + solverId, + { data } + ); + } + } + } catch (error) { + console.error(`Error calculating active networks for solver ${solverId}:`, error); + } +} + +async function calculateActiveNetworks(solver, data) { + if (!solver.solver_networks) { + data.activeNetworks = []; + data.hasActiveNetworks = false; + return; + } + + // Filter active networks and extract their names + const activeNetworkNames = solver.solver_networks + .filter(network => network.active) + .map(network => network.network?.name) + .filter(Boolean); // Remove any undefined values + + data.activeNetworks = activeNetworkNames; + data.hasActiveNetworks = activeNetworkNames.length > 0; +} diff --git a/src/api/solver/content-types/solver/schema.json b/src/api/solver/content-types/solver/schema.json index 5dda4ca..c1dd525 100644 --- a/src/api/solver/content-types/solver/schema.json +++ b/src/api/solver/content-types/solver/schema.json @@ -69,6 +69,14 @@ ], "required": true, "default": "No" + }, + "activeNetworks": { + "type": "json", + "private": false + }, + "hasActiveNetworks": { + "type": "boolean", + "default": false } } } From c7d8b72c5d6cd0ce51c491e7ba62b978f2e08bce Mon Sep 17 00:00:00 2001 From: tamir Date: Wed, 24 Dec 2025 10:49:16 +0200 Subject: [PATCH 2/7] Fixed lifecycle --- src/api/solver/content-types/solver/lifecycles.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/api/solver/content-types/solver/lifecycles.ts b/src/api/solver/content-types/solver/lifecycles.ts index b56a3af..e790042 100644 --- a/src/api/solver/content-types/solver/lifecycles.ts +++ b/src/api/solver/content-types/solver/lifecycles.ts @@ -10,6 +10,7 @@ export default { async function updateActiveNetworks(event) { const { data, where } = event.params; + const solverData: SolverData = data; // If this is an update operation and we're not updating solver_networks relation if (where && !data.solver_networks) { @@ -22,7 +23,7 @@ async function updateActiveNetworks(event) { if (solver) { // Calculate active networks - await calculateActiveNetworks(solver, data); + await calculateActiveNetworks(solver, solverData); } } else if (data.solver_networks) { // For create or when solver_networks is being updated @@ -31,6 +32,12 @@ async function updateActiveNetworks(event) { } } +// Define interface for the data object +interface SolverData { + activeNetworks?: string[]; + hasActiveNetworks?: boolean; +} + // This function will be called after create/update to ensure relations are established export async function calculateActiveNetworksForSolver(solverId) { try { @@ -43,7 +50,7 @@ export async function calculateActiveNetworksForSolver(solverId) { if (solver) { // Calculate active networks - const data = {}; + const data: SolverData = {}; await calculateActiveNetworks(solver, data); // Update the solver with the calculated values @@ -60,7 +67,7 @@ export async function calculateActiveNetworksForSolver(solverId) { } } -async function calculateActiveNetworks(solver, data) { +async function calculateActiveNetworks(solver, data: SolverData) { if (!solver.solver_networks) { data.activeNetworks = []; data.hasActiveNetworks = false; From 8c3b8274760391edb17c392547259504fa99c07f Mon Sep 17 00:00:00 2001 From: tamir Date: Wed, 24 Dec 2025 16:54:20 +0200 Subject: [PATCH 3/7] Update service fee flag based on bonding pool status --- .../solver/content-types/solver/lifecycles.ts | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/src/api/solver/content-types/solver/lifecycles.ts b/src/api/solver/content-types/solver/lifecycles.ts index e790042..c599c0b 100644 --- a/src/api/solver/content-types/solver/lifecycles.ts +++ b/src/api/solver/content-types/solver/lifecycles.ts @@ -1,10 +1,12 @@ export default { async beforeCreate(event) { await updateActiveNetworks(event); + await updateServiceFeeEnabled(event); }, async beforeUpdate(event) { await updateActiveNetworks(event); + await updateServiceFeeEnabled(event); }, }; @@ -36,6 +38,7 @@ async function updateActiveNetworks(event) { interface SolverData { activeNetworks?: string[]; hasActiveNetworks?: boolean; + isServiceFeeEnabled?: boolean; } // This function will be called after create/update to ensure relations are established @@ -67,6 +70,41 @@ export async function calculateActiveNetworksForSolver(solverId) { } } +// This function will be called to update the service fee enabled status for a solver +export async function updateServiceFeeEnabledForSolver(solverId) { + try { + // Get the solver with its bonding pools + const solver = await strapi.entityService.findOne( + 'api::solver.solver', + solverId, + { + populate: { + solver_bonding_pools: { + fields: ['name', 'joinedOn'] + } + } + } + ); + + if (solver) { + // Calculate service fee enabled status + const data: SolverData = {}; + await calculateServiceFeeEnabled(solver, data); + + // Update the solver with the calculated values + if (data.isServiceFeeEnabled !== undefined) { + await strapi.entityService.update( + 'api::solver.solver', + solverId, + { data } + ); + } + } + } catch (error) { + console.error(`Error updating service fee enabled for solver ${solverId}:`, error); + } +} + async function calculateActiveNetworks(solver, data: SolverData) { if (!solver.solver_networks) { data.activeNetworks = []; @@ -83,3 +121,76 @@ async function calculateActiveNetworks(solver, data: SolverData) { data.activeNetworks = activeNetworkNames; data.hasActiveNetworks = activeNetworkNames.length > 0; } + +async function updateServiceFeeEnabled(event) { + const { data, where } = event.params; + const solverData: SolverData = data; + + // For create operation or update operation + if (where) { + // Get the current solver data with bonding pools + const solver = await strapi.entityService.findOne( + 'api::solver.solver', + where.id, + { + populate: { + solver_bonding_pools: { + fields: ['name', 'joinedOn'] + } + } + } + ); + + if (solver) { + // Calculate service fee enabled status + await calculateServiceFeeEnabled(solver, solverData); + } + } + // For create operation, we'll handle it in afterCreate since we need the ID +} + +async function calculateServiceFeeEnabled(solver, data: SolverData) { + // Default to false + data.isServiceFeeEnabled = false; + + if (!solver.solver_bonding_pools || solver.solver_bonding_pools.length === 0) { + return; + } + + // Get current date for comparison + const currentDate = new Date(); + + // Check each bonding pool + for (const bondingPool of solver.solver_bonding_pools) { + // Skip if joinedOn is not set + if (!bondingPool.joinedOn) { + continue; + } + + const joinedDate = new Date(bondingPool.joinedOn); + const monthsDifference = getMonthsDifference(joinedDate, currentDate); + + // CoW bonding pool (name is "CoW" and not colocated) + if (bondingPool.name === "CoW" && solver.isColocated === "No") { + if (monthsDifference >= 6) { + data.isServiceFeeEnabled = true; + return; // Exit early once we find a qualifying pool + } + } + // Colocated bonding pool + else if (solver.isColocated === "Yes") { + if (monthsDifference >= 3) { + data.isServiceFeeEnabled = true; + return; // Exit early once we find a qualifying pool + } + } + // For partial colocated, we'll treat it as not colocated + } +} + +// Helper function to calculate months difference between two dates +function getMonthsDifference(startDate, endDate) { + const years = endDate.getFullYear() - startDate.getFullYear(); + const months = endDate.getMonth() - startDate.getMonth(); + return years * 12 + months; +} From a9888bf2ff1a96ef769f9b122f3a1f2c5082dca3 Mon Sep 17 00:00:00 2001 From: tamir Date: Thu, 25 Dec 2025 08:19:57 +0200 Subject: [PATCH 4/7] Update service fee flag based on bonding pool status --- .../solver-bonding-pool/lifecycles.ts | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 src/api/solver-bonding-pool/content-types/solver-bonding-pool/lifecycles.ts diff --git a/src/api/solver-bonding-pool/content-types/solver-bonding-pool/lifecycles.ts b/src/api/solver-bonding-pool/content-types/solver-bonding-pool/lifecycles.ts new file mode 100644 index 0000000..35852de --- /dev/null +++ b/src/api/solver-bonding-pool/content-types/solver-bonding-pool/lifecycles.ts @@ -0,0 +1,106 @@ +import { updateServiceFeeEnabledForSolver } from '../../../solver/content-types/solver/lifecycles'; + +export default { + async afterCreate(event) { + await updateRelatedSolvers(event); + }, + + async afterUpdate(event) { + await updateRelatedSolvers(event); + }, + + async beforeDelete(event) { + await storeRelatedSolversForUpdate(event); + }, + + async afterDelete(event) { + await updateStoredSolvers(event); + }, +}; + +// Store for temporarily keeping solver IDs between beforeDelete and afterDelete +const solverIdsToUpdate = new Map(); + +async function updateRelatedSolvers(event) { + try { + const { result } = event; + + // If this is a delete operation, result might not be available + if (!result || !result.solvers) { + return; + } + + // Get the solver IDs + const solverIds = Array.isArray(result.solvers) + ? result.solvers.map(solver => solver.id || solver) + : []; + + // If no solvers are directly available in the result, fetch them + if (solverIds.length === 0 && result.id) { + const bondingPool = await strapi.entityService.findOne( + 'api::solver-bonding-pool.solver-bonding-pool', + result.id, + { populate: ['solvers'] } + ); + + if (bondingPool && bondingPool.solvers) { + bondingPool.solvers.forEach(solver => { + solverIds.push(solver.id); + }); + } + } + + // Update each solver's service fee enabled status + for (const solverId of solverIds) { + if (solverId) { + await updateServiceFeeEnabledForSolver(solverId); + } + } + } catch (error) { + console.error('Error updating related solvers:', error); + } +} + +async function storeRelatedSolversForUpdate(event) { + try { + const { where } = event.params; + const { id } = where; + + // Get the bonding pool with its solvers + const bondingPool = await strapi.entityService.findOne( + 'api::solver-bonding-pool.solver-bonding-pool', + id, + { populate: ['solvers'] } + ); + + if (bondingPool && bondingPool.solvers) { + // Store the solver IDs for later use in afterDelete + const solverIds = bondingPool.solvers.map(solver => solver.id); + solverIdsToUpdate.set(id, solverIds); + } + } catch (error) { + console.error('Error storing related solvers for update:', error); + } +} + +async function updateStoredSolvers(event) { + try { + const { where } = event.params; + const { id } = where; + + // Get the stored solver IDs + const solverIds = solverIdsToUpdate.get(id) || []; + + // Update each solver's service fee enabled status + for (const solverId of solverIds) { + if (solverId) { + await updateServiceFeeEnabledForSolver(solverId); + } + } + + // Clean up the stored solver IDs + solverIdsToUpdate.delete(id); + } catch (error) { + console.error('Error updating stored solvers:', error); + } +} From 2b712d1fb63537f2e37bf9e5b3bd71daf239487b Mon Sep 17 00:00:00 2001 From: tamir Date: Mon, 5 Jan 2026 10:33:39 +0200 Subject: [PATCH 5/7] Updated lifecycles to handle errors and commented out usage to start off as disabled --- .../solver-bonding-pool/lifecycles.ts | 23 ++++++++++--------- .../solver-network/lifecycles.ts | 19 +++++++-------- .../solver/content-types/solver/lifecycles.ts | 22 ++++++++++-------- 3 files changed, 34 insertions(+), 30 deletions(-) diff --git a/src/api/solver-bonding-pool/content-types/solver-bonding-pool/lifecycles.ts b/src/api/solver-bonding-pool/content-types/solver-bonding-pool/lifecycles.ts index 35852de..f70d875 100644 --- a/src/api/solver-bonding-pool/content-types/solver-bonding-pool/lifecycles.ts +++ b/src/api/solver-bonding-pool/content-types/solver-bonding-pool/lifecycles.ts @@ -1,27 +1,28 @@ +import { Strapi } from '@strapi/strapi'; import { updateServiceFeeEnabledForSolver } from '../../../solver/content-types/solver/lifecycles'; -export default { +export default ({ strapi }: { strapi: Strapi }) => ({ async afterCreate(event) { - await updateRelatedSolvers(event); + // await updateRelatedSolvers(event, strapi); }, async afterUpdate(event) { - await updateRelatedSolvers(event); + // await updateRelatedSolvers(event, strapi); }, async beforeDelete(event) { - await storeRelatedSolversForUpdate(event); + // await storeRelatedSolversForUpdate(event, strapi); }, async afterDelete(event) { - await updateStoredSolvers(event); + // await updateStoredSolvers(event, strapi); }, -}; +}); // Store for temporarily keeping solver IDs between beforeDelete and afterDelete const solverIdsToUpdate = new Map(); -async function updateRelatedSolvers(event) { +async function updateRelatedSolvers(event, strapi) { try { const { result } = event; @@ -53,7 +54,7 @@ async function updateRelatedSolvers(event) { // Update each solver's service fee enabled status for (const solverId of solverIds) { if (solverId) { - await updateServiceFeeEnabledForSolver(solverId); + await updateServiceFeeEnabledForSolver(solverId, strapi); } } } catch (error) { @@ -61,7 +62,7 @@ async function updateRelatedSolvers(event) { } } -async function storeRelatedSolversForUpdate(event) { +async function storeRelatedSolversForUpdate(event, strapi) { try { const { where } = event.params; const { id } = where; @@ -83,7 +84,7 @@ async function storeRelatedSolversForUpdate(event) { } } -async function updateStoredSolvers(event) { +async function updateStoredSolvers(event, strapi) { try { const { where } = event.params; const { id } = where; @@ -94,7 +95,7 @@ async function updateStoredSolvers(event) { // Update each solver's service fee enabled status for (const solverId of solverIds) { if (solverId) { - await updateServiceFeeEnabledForSolver(solverId); + await updateServiceFeeEnabledForSolver(solverId, strapi); } } diff --git a/src/api/solver-network/content-types/solver-network/lifecycles.ts b/src/api/solver-network/content-types/solver-network/lifecycles.ts index 2060a61..b4f6194 100644 --- a/src/api/solver-network/content-types/solver-network/lifecycles.ts +++ b/src/api/solver-network/content-types/solver-network/lifecycles.ts @@ -1,26 +1,27 @@ +import { Strapi } from '@strapi/strapi'; import { calculateActiveNetworksForSolver } from '../../../solver/content-types/solver/lifecycles'; -export default { +export default ({ strapi }: { strapi: Strapi }) => ({ async beforeCreate(event) { - await validateVouchedBy(event); + // await validateVouchedBy(event); }, async beforeUpdate(event) { - await validateVouchedBy(event); + // await validateVouchedBy(event); }, async afterCreate(event) { - await updateParentSolver(event); + // await updateParentSolver(event, strapi); }, async afterUpdate(event) { - await updateParentSolver(event); + // await updateParentSolver(event, strapi); }, async afterDelete(event) { - await updateParentSolver(event); + // await updateParentSolver(event, strapi); }, -}; +}); async function validateVouchedBy(event) { const { data } = event.params; @@ -31,7 +32,7 @@ async function validateVouchedBy(event) { } } -async function updateParentSolver(event) { +async function updateParentSolver(event, strapi) { try { const { result } = event; @@ -45,7 +46,7 @@ async function updateParentSolver(event) { if (solverId) { // Update the solver's activeNetworks field - await calculateActiveNetworksForSolver(solverId); + await calculateActiveNetworksForSolver(solverId, strapi); } } catch (error) { console.error('Error updating parent solver:', error); diff --git a/src/api/solver/content-types/solver/lifecycles.ts b/src/api/solver/content-types/solver/lifecycles.ts index c599c0b..9c069cd 100644 --- a/src/api/solver/content-types/solver/lifecycles.ts +++ b/src/api/solver/content-types/solver/lifecycles.ts @@ -1,16 +1,18 @@ -export default { +import { Strapi } from '@strapi/strapi'; + +export default ({ strapi }: { strapi: Strapi }) => ({ async beforeCreate(event) { - await updateActiveNetworks(event); - await updateServiceFeeEnabled(event); + // await updateActiveNetworks(event, strapi); + // await updateServiceFeeEnabled(event, strapi); }, async beforeUpdate(event) { - await updateActiveNetworks(event); - await updateServiceFeeEnabled(event); + // await updateActiveNetworks(event, strapi); + // await updateServiceFeeEnabled(event, strapi); }, -}; +}); -async function updateActiveNetworks(event) { +async function updateActiveNetworks(event, strapi) { const { data, where } = event.params; const solverData: SolverData = data; @@ -42,7 +44,7 @@ interface SolverData { } // This function will be called after create/update to ensure relations are established -export async function calculateActiveNetworksForSolver(solverId) { +export async function calculateActiveNetworksForSolver(solverId, strapi) { try { // Get the solver with its networks const solver = await strapi.entityService.findOne( @@ -71,7 +73,7 @@ export async function calculateActiveNetworksForSolver(solverId) { } // This function will be called to update the service fee enabled status for a solver -export async function updateServiceFeeEnabledForSolver(solverId) { +export async function updateServiceFeeEnabledForSolver(solverId, strapi) { try { // Get the solver with its bonding pools const solver = await strapi.entityService.findOne( @@ -122,7 +124,7 @@ async function calculateActiveNetworks(solver, data: SolverData) { data.hasActiveNetworks = activeNetworkNames.length > 0; } -async function updateServiceFeeEnabled(event) { +async function updateServiceFeeEnabled(event, strapi) { const { data, where } = event.params; const solverData: SolverData = data; From d2ba4568cecb3eacab188b82e0874591730e02d9 Mon Sep 17 00:00:00 2001 From: tamir Date: Mon, 5 Jan 2026 15:35:54 +0200 Subject: [PATCH 6/7] Removed lifecycles --- .../solver-bonding-pool/lifecycles.ts | 107 ---------- .../solver-network/lifecycles.ts | 54 ----- .../solver/content-types/solver/lifecycles.ts | 198 ------------------ 3 files changed, 359 deletions(-) delete mode 100644 src/api/solver-bonding-pool/content-types/solver-bonding-pool/lifecycles.ts delete mode 100644 src/api/solver-network/content-types/solver-network/lifecycles.ts delete mode 100644 src/api/solver/content-types/solver/lifecycles.ts diff --git a/src/api/solver-bonding-pool/content-types/solver-bonding-pool/lifecycles.ts b/src/api/solver-bonding-pool/content-types/solver-bonding-pool/lifecycles.ts deleted file mode 100644 index f70d875..0000000 --- a/src/api/solver-bonding-pool/content-types/solver-bonding-pool/lifecycles.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { Strapi } from '@strapi/strapi'; -import { updateServiceFeeEnabledForSolver } from '../../../solver/content-types/solver/lifecycles'; - -export default ({ strapi }: { strapi: Strapi }) => ({ - async afterCreate(event) { - // await updateRelatedSolvers(event, strapi); - }, - - async afterUpdate(event) { - // await updateRelatedSolvers(event, strapi); - }, - - async beforeDelete(event) { - // await storeRelatedSolversForUpdate(event, strapi); - }, - - async afterDelete(event) { - // await updateStoredSolvers(event, strapi); - }, -}); - -// Store for temporarily keeping solver IDs between beforeDelete and afterDelete -const solverIdsToUpdate = new Map(); - -async function updateRelatedSolvers(event, strapi) { - try { - const { result } = event; - - // If this is a delete operation, result might not be available - if (!result || !result.solvers) { - return; - } - - // Get the solver IDs - const solverIds = Array.isArray(result.solvers) - ? result.solvers.map(solver => solver.id || solver) - : []; - - // If no solvers are directly available in the result, fetch them - if (solverIds.length === 0 && result.id) { - const bondingPool = await strapi.entityService.findOne( - 'api::solver-bonding-pool.solver-bonding-pool', - result.id, - { populate: ['solvers'] } - ); - - if (bondingPool && bondingPool.solvers) { - bondingPool.solvers.forEach(solver => { - solverIds.push(solver.id); - }); - } - } - - // Update each solver's service fee enabled status - for (const solverId of solverIds) { - if (solverId) { - await updateServiceFeeEnabledForSolver(solverId, strapi); - } - } - } catch (error) { - console.error('Error updating related solvers:', error); - } -} - -async function storeRelatedSolversForUpdate(event, strapi) { - try { - const { where } = event.params; - const { id } = where; - - // Get the bonding pool with its solvers - const bondingPool = await strapi.entityService.findOne( - 'api::solver-bonding-pool.solver-bonding-pool', - id, - { populate: ['solvers'] } - ); - - if (bondingPool && bondingPool.solvers) { - // Store the solver IDs for later use in afterDelete - const solverIds = bondingPool.solvers.map(solver => solver.id); - solverIdsToUpdate.set(id, solverIds); - } - } catch (error) { - console.error('Error storing related solvers for update:', error); - } -} - -async function updateStoredSolvers(event, strapi) { - try { - const { where } = event.params; - const { id } = where; - - // Get the stored solver IDs - const solverIds = solverIdsToUpdate.get(id) || []; - - // Update each solver's service fee enabled status - for (const solverId of solverIds) { - if (solverId) { - await updateServiceFeeEnabledForSolver(solverId, strapi); - } - } - - // Clean up the stored solver IDs - solverIdsToUpdate.delete(id); - } catch (error) { - console.error('Error updating stored solvers:', error); - } -} diff --git a/src/api/solver-network/content-types/solver-network/lifecycles.ts b/src/api/solver-network/content-types/solver-network/lifecycles.ts deleted file mode 100644 index b4f6194..0000000 --- a/src/api/solver-network/content-types/solver-network/lifecycles.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Strapi } from '@strapi/strapi'; -import { calculateActiveNetworksForSolver } from '../../../solver/content-types/solver/lifecycles'; - -export default ({ strapi }: { strapi: Strapi }) => ({ - async beforeCreate(event) { - // await validateVouchedBy(event); - }, - - async beforeUpdate(event) { - // await validateVouchedBy(event); - }, - - async afterCreate(event) { - // await updateParentSolver(event, strapi); - }, - - async afterUpdate(event) { - // await updateParentSolver(event, strapi); - }, - - async afterDelete(event) { - // await updateParentSolver(event, strapi); - }, -}); - -async function validateVouchedBy(event) { - const { data } = event.params; - - // Check if isVouched is true and vouchedBy is not set - if (data.isVouched === true && !data.vouchedBy) { - throw new Error('vouchedBy relationship must be set when isVouched is true'); - } -} - -async function updateParentSolver(event, strapi) { - try { - const { result } = event; - - // If this is a delete operation, result might not be available - if (!result || !result.solver) { - return; - } - - // Get the solver ID - const solverId = result.solver.id || result.solver; - - if (solverId) { - // Update the solver's activeNetworks field - await calculateActiveNetworksForSolver(solverId, strapi); - } - } catch (error) { - console.error('Error updating parent solver:', error); - } -} diff --git a/src/api/solver/content-types/solver/lifecycles.ts b/src/api/solver/content-types/solver/lifecycles.ts deleted file mode 100644 index 9c069cd..0000000 --- a/src/api/solver/content-types/solver/lifecycles.ts +++ /dev/null @@ -1,198 +0,0 @@ -import { Strapi } from '@strapi/strapi'; - -export default ({ strapi }: { strapi: Strapi }) => ({ - async beforeCreate(event) { - // await updateActiveNetworks(event, strapi); - // await updateServiceFeeEnabled(event, strapi); - }, - - async beforeUpdate(event) { - // await updateActiveNetworks(event, strapi); - // await updateServiceFeeEnabled(event, strapi); - }, -}); - -async function updateActiveNetworks(event, strapi) { - const { data, where } = event.params; - const solverData: SolverData = data; - - // If this is an update operation and we're not updating solver_networks relation - if (where && !data.solver_networks) { - // Get the current solver data to check if we need to update activeNetworks - const solver = await strapi.entityService.findOne( - 'api::solver.solver', - where.id, - { populate: ['solver_networks.network'] } - ); - - if (solver) { - // Calculate active networks - await calculateActiveNetworks(solver, solverData); - } - } else if (data.solver_networks) { - // For create or when solver_networks is being updated - // We'll need to fetch the networks after the operation in afterCreate/afterUpdate - // as the relations might not be fully established yet - } -} - -// Define interface for the data object -interface SolverData { - activeNetworks?: string[]; - hasActiveNetworks?: boolean; - isServiceFeeEnabled?: boolean; -} - -// This function will be called after create/update to ensure relations are established -export async function calculateActiveNetworksForSolver(solverId, strapi) { - try { - // Get the solver with its networks - const solver = await strapi.entityService.findOne( - 'api::solver.solver', - solverId, - { populate: ['solver_networks.network'] } - ); - - if (solver) { - // Calculate active networks - const data: SolverData = {}; - await calculateActiveNetworks(solver, data); - - // Update the solver with the calculated values - if (data.activeNetworks || data.hasActiveNetworks !== undefined) { - await strapi.entityService.update( - 'api::solver.solver', - solverId, - { data } - ); - } - } - } catch (error) { - console.error(`Error calculating active networks for solver ${solverId}:`, error); - } -} - -// This function will be called to update the service fee enabled status for a solver -export async function updateServiceFeeEnabledForSolver(solverId, strapi) { - try { - // Get the solver with its bonding pools - const solver = await strapi.entityService.findOne( - 'api::solver.solver', - solverId, - { - populate: { - solver_bonding_pools: { - fields: ['name', 'joinedOn'] - } - } - } - ); - - if (solver) { - // Calculate service fee enabled status - const data: SolverData = {}; - await calculateServiceFeeEnabled(solver, data); - - // Update the solver with the calculated values - if (data.isServiceFeeEnabled !== undefined) { - await strapi.entityService.update( - 'api::solver.solver', - solverId, - { data } - ); - } - } - } catch (error) { - console.error(`Error updating service fee enabled for solver ${solverId}:`, error); - } -} - -async function calculateActiveNetworks(solver, data: SolverData) { - if (!solver.solver_networks) { - data.activeNetworks = []; - data.hasActiveNetworks = false; - return; - } - - // Filter active networks and extract their names - const activeNetworkNames = solver.solver_networks - .filter(network => network.active) - .map(network => network.network?.name) - .filter(Boolean); // Remove any undefined values - - data.activeNetworks = activeNetworkNames; - data.hasActiveNetworks = activeNetworkNames.length > 0; -} - -async function updateServiceFeeEnabled(event, strapi) { - const { data, where } = event.params; - const solverData: SolverData = data; - - // For create operation or update operation - if (where) { - // Get the current solver data with bonding pools - const solver = await strapi.entityService.findOne( - 'api::solver.solver', - where.id, - { - populate: { - solver_bonding_pools: { - fields: ['name', 'joinedOn'] - } - } - } - ); - - if (solver) { - // Calculate service fee enabled status - await calculateServiceFeeEnabled(solver, solverData); - } - } - // For create operation, we'll handle it in afterCreate since we need the ID -} - -async function calculateServiceFeeEnabled(solver, data: SolverData) { - // Default to false - data.isServiceFeeEnabled = false; - - if (!solver.solver_bonding_pools || solver.solver_bonding_pools.length === 0) { - return; - } - - // Get current date for comparison - const currentDate = new Date(); - - // Check each bonding pool - for (const bondingPool of solver.solver_bonding_pools) { - // Skip if joinedOn is not set - if (!bondingPool.joinedOn) { - continue; - } - - const joinedDate = new Date(bondingPool.joinedOn); - const monthsDifference = getMonthsDifference(joinedDate, currentDate); - - // CoW bonding pool (name is "CoW" and not colocated) - if (bondingPool.name === "CoW" && solver.isColocated === "No") { - if (monthsDifference >= 6) { - data.isServiceFeeEnabled = true; - return; // Exit early once we find a qualifying pool - } - } - // Colocated bonding pool - else if (solver.isColocated === "Yes") { - if (monthsDifference >= 3) { - data.isServiceFeeEnabled = true; - return; // Exit early once we find a qualifying pool - } - } - // For partial colocated, we'll treat it as not colocated - } -} - -// Helper function to calculate months difference between two dates -function getMonthsDifference(startDate, endDate) { - const years = endDate.getFullYear() - startDate.getFullYear(); - const months = endDate.getMonth() - startDate.getMonth(); - return years * 12 + months; -} From f3d51f628422f0d654ffe16d64d9a30ce297a08d Mon Sep 17 00:00:00 2001 From: tamir Date: Mon, 5 Jan 2026 16:32:17 +0200 Subject: [PATCH 7/7] Removed migration script --- .../migrations/schema-changes-migration.js | 70 ------------------- 1 file changed, 70 deletions(-) delete mode 100644 database/migrations/schema-changes-migration.js diff --git a/database/migrations/schema-changes-migration.js b/database/migrations/schema-changes-migration.js deleted file mode 100644 index 9edd693..0000000 --- a/database/migrations/schema-changes-migration.js +++ /dev/null @@ -1,70 +0,0 @@ -async function up(knex) { - // 1. Update Solver Bonding Pool: rename bonding_pool to name - // First, get all solver bonding pools with their bonding_pool relation - const solverBondingPools = await knex('solver_bonding_pools') - .select('id', 'bonding_pool_id') - .whereNotNull('bonding_pool_id'); - - // For each solver bonding pool, get the name from bonding_pool and update the solver bonding pool - for (const sbp of solverBondingPools) { - if (sbp.bonding_pool_id) { - // Get the name from bonding_pool - const bondingPool = await knex('bonding_pools') - .select('name') - .where('id', sbp.bonding_pool_id) - .first(); - - if (bondingPool && bondingPool.name) { - // Update the solver bonding pool with the name - await knex('solver_bonding_pools') - .where('id', sbp.id) - .update({ - name: bondingPool.name - }); - } - } - } - - // 2. Calculate activeNetworks for all solvers - const solvers = await knex('solvers').select('id'); - - for (const solver of solvers) { - // Get all active networks for this solver - const activeNetworks = await knex('solver_networks') - .join('networks', 'solver_networks.network_id', 'networks.id') - .where({ - 'solver_networks.solver_id': solver.id, - 'solver_networks.active': true - }) - .select('networks.name'); - - // Extract network names - const networkNames = activeNetworks.map(n => n.name); - - // Update the solver with activeNetworks and hasActiveNetworks - await knex('solvers') - .where('id', solver.id) - .update({ - activeNetworks: JSON.stringify(networkNames), - hasActiveNetworks: networkNames.length > 0 - }); - } - - // 3. Ensure all required fields are set for Solver Network - // Make a list of solver networks that have isVouched=true but no vouchedBy - const vouchedNetworks = await knex('solver_networks') - .where('isVouched', true) - .whereNull('vouchedBy'); - - console.log(`Found ${vouchedNetworks.length} solver networks with isVouched=true but no vouchedBy`); - - return; -} - -async function down(knex) { - // This migration cannot be reversed automatically - console.log('This migration cannot be reversed automatically'); - return; -} - -module.exports = { up, down };