message: common: implement stake feasibility check
diff: @@ -103,6 +103,7 @@ export class AllocationManager {
  throw Error('Failed to populate batch transaction: no transactions supplied')
  }
  + await this.validateActionBatchFeasibilty(actions)
  const populateTransactionsResults = await this.prepareTransactions(actions)
  const failedTransactionPreparations = populateTransactionsResults
  @@ -113,6 +114,7 @@ export class AllocationManager {
  return failedTransactionPreparations
  }
  + // TODO: sort actions here. they should be sorted by their "intrinsic delta"
  const callData = populateTransactionsResults
  .map((tx) => tx as PopulatedTransaction)
  .filter((tx: PopulatedTransaction) => !!tx.data)
  @@ -1148,4 +1150,60 @@ export class AllocationManager {
  return isDeploymentWorthAllocatingTowards(logger, subgraphDeployment, indexingRules)
  .toAllocate
  }
  +
  + // Calculates the balance (GRT delta) of a single Action.
  + async resolveActionDelta(action: Action): Promise<BigNumber> {
  + let delta = BigNumber.from(0)
  +
  + // Delta increases when allocating
  + if (action.type === ActionType.ALLOCATE || action.type === ActionType.REALLOCATE) {
  + delta.add(BigNumber.from(action.amount))
  + }
  +
  + // Delta decreases when unallocating
  + if (action.type === ActionType.UNALLOCATE || action.type === ActionType.REALLOCATE) {
  + // ensure those action types have r
  + if (action.allocationID === null || action.allocationID === undefined) {
  + throw Error(
  + `SHOULD BE UNREACHABLE: Unallocate or Reallocate action must have an allocationID field: ${action}`,
  + )
  + }
  + // Fetch the allocation on chain to inspect its amount
  + const allocation = await this.networkMonitor.allocation(action.allocationID)
  + if (!allocation) {
  + throw indexerError(
  + IndexerErrorCode.IE063,
  + `Action validation failed: No active allocation found on chain with id '${action.allocationID}' found`,
  + )
  + }
  + /* We intentionally don't check if the allocation is active now because it will be checked
  + * when preparing the transaction */
  + delta.sub(allocation.allocatedTokens)
  + }
  +
  + return delta
  + }
  +
  + async validateActionBatchFeasibilty(batch: Action[]) {
  + const logger = this.logger.child({ function: 'validateActionBatch' })
  + logger.debug(`Validating action batch`, { size: batch.length })
  +
  + // Validate stake feasibility
  + const freeStake = await this.contracts.staking.getIndexerCapacity(this.indexer)
  + const mapper = async (action: Action) => this.resolveActionDelta(action)
  + const batchDelta: BigNumber = (await pMap(batch, mapper)).reduce((a, b) => a.add(b))
  + const newBalance = batchDelta.add(freeStake)
  + if (newBalance.isNegative()) {
  + {
  + throw indexerError(
  + IndexerErrorCode.IE013, // TODO: Should we create a new error code?
  + `Unfeasible action batch: Approved action batch GRT balance is ` +
  + `${formatGRT(batchDelta)} ` +
  + `but available stake equals ${formatGRT(freeStake)}.`,
  + )
  + }
  + }
  +
  + // TODO: checkGasLimit(batch)
  + }
  }
  