import { BN , checkThat , isSimpleAllocation , OpenChannel } from '@statechannels/wallet-core' ; import _ from 'lodash' ; import { Transaction } from 'objection' ; import { Logger } from 'pino' ; import { Channel } from '../models/channel' ; import { LedgerRequest } from '../models/ledger-request' ; import { WalletObjective } from '../models/objective' ; import { Store } from '../engine/store' ; import { EngineResponse } from '../engine/engine-response' ; export class LedgerFunder { constructor ( private store , private logger , private timingMetrics = false ) { } public static create ( store , logger , timingMetrics = false ) { return new LedgerFunder ( store , logger , timingMetrics ) ; } /**
     * Runs the ledger funding protocol
     *
     * Returns true if the channel is funded, false otherwise
     */ public async crank ( objective , channel , response , tx ) { const ledgerId = objective . data . fundingLedgerChannelId ; if ( ! ledgerId ) { throw new Error ( `LedgerFunder called with objective without a fundingLedgerChannelId` ) ; } const ledger = await this . store . getChannel ( ledgerId , tx ) ; if ( ! ledger ) return false ; if ( await this . alreadyFunded ( channel , ledger , tx ) ) return true ; if ( await this . alreadyRequestedFunding ( channel . channelId , tx ) ) return false ; await this . requestLedgerFunding ( channel , ledger , tx ) ; return false ; } private async alreadyFunded ( channel , ledger , _tx ) { if ( ! ledger . isRunning ) return false ; return doesLedgerFundApp ( ledger , channel ) ; } private async requestLedgerFunding ( channel , ledger , tx ) { const myAmount = channel . myAmount ; const theirAmount = channel . opponentAmount ; const [ amountA , amountB ] = ledger . myIndex === 0 ? [ myAmount , theirAmount ] : [ theirAmount , myAmount ] ; await LedgerRequest . requestLedgerFunding ( channel . channelId , ledger . channelId , amountA , amountB , tx ) ; } private async alreadyRequestedFunding ( channelId , tx ) { const req = await this . store . getLedgerRequest ( channelId , 'fund' , tx ) ; return ! ! req ; } } function doesLedgerFundApp ( ledger , app ) { if ( ! ledger . supported ) return false ; if ( ! app . supported ) return false ; const { allocationItems : ledgerFunding } = checkThat ( ledger . supported . outcome , isSimpleAllocation ) ; const { allocationItems : appFunding } = checkThat ( app . supported . outcome , isSimpleAllocation ) ; const targetFunding = appFunding . map ( a => a . amount ) . reduce ( BN . add , BN . from ( 0 ) ) ; return _ . some ( ledgerFunding , ( { destination , amount } ) => destination === app . channelId && amount === targetFunding ) ; }