import { BN } from '@statechannels/wallet-core' ; import { Model , TransactionOrKnex } from 'objection' ; import { Uint256 , Destination , Bytes32 } from '../type-aliases' ; export type LedgerRequestStatus = 'queued' | 'cancelled' | 'pending' | 'succeeded' | 'insufficient-funds' | 'inconsistent' | 'failed' ; export interface LedgerRequestType { ledgerChannelId : Destination ; channelToBeFunded : Destination ; status : LedgerRequestStatus ; amountA : Uint256 ; amountB : Uint256 ; lastSeenAgreedState : number | null ; missedOpportunityCount : number ; type : 'fund' | 'defund' ; } export interface RichLedgerRequest extends LedgerRequestType { isPending : boolean ; isQueued : boolean ; isFund : boolean ; isDefund : boolean ; totalAmount : Uint256 ; $id : any ; } export class LedgerRequest extends Model implements LedgerRequestType { channelToBeFunded ! : LedgerRequestType [ 'channelToBeFunded' ] ; ledgerChannelId ! : LedgerRequestType [ 'ledgerChannelId' ] ; status ! : LedgerRequestType [ 'status' ] ; type ! : LedgerRequestType [ 'type' ] ; amountA ! : Uint256 ; amountB ! : Uint256 ; missedOpportunityCount ! : number ; lastSeenAgreedState ! : number | null ; static tableName = 'ledger_requests' ; static get idColumn ( ) : string [ ] { return [ 'channelToBeFunded' , 'type' ] ; } static async getRequest ( channelToBeFunded , type , tx ) { return LedgerRequest . query ( tx ) . findById ( [ channelToBeFunded , type ] ) ; } static async setRequest ( request , tx ) { return LedgerRequest . query ( tx ) . insert ( { ... request , missedOpportunityCount : request . missedOpportunityCount || 0 , lastSeenAgreedState : request . lastSeenAgreedState || null , } ) ; } async markAsFailed ( tx ) { await LedgerRequest . query ( tx ) . findById ( [ this . channelToBeFunded , this . type ] ) . patch ( { status : 'failed' } ) ; } static async setRequestStatus ( channelToBeFunded , type , status , tx ) { await LedgerRequest . query ( tx ) . findById ( [ channelToBeFunded , type ] ) . patch ( { status } ) ; } static async getActiveRequests ( ledgerChannelId , tx ) { return LedgerRequest . query ( tx ) . select ( ) . where ( { ledgerChannelId , status : 'pending' } ) . orWhere ( { ledgerChannelId , status : 'queued' } ) ; } static async ledgersWithNewRequestsIds ( tx ) { return ( await LedgerRequest . query ( tx ) . column ( 'ledgerChannelId' ) . whereNull ( 'lastSeenAgreedState' ) ) . map ( lr => lr . ledgerChannelId ) ; } static async saveAll ( requests , tx ) { for ( const request of requests ) { await LedgerRequest . query ( tx ) . findById ( [ request . channelToBeFunded , request . type ] ) . update ( request ) ; } } static async requestLedgerFunding ( channelToBeFunded , ledgerChannelId , amountA , amountB , tx ) { await this . setRequest ( { ledgerChannelId , status : 'queued' , channelToBeFunded , amountA , amountB , type : 'fund' , } , tx ) ; } static async requestLedgerDefunding ( channelToBeFunded , ledgerChannelId , amountA , amountB , tx ) { await this . setRequest ( { ledgerChannelId , status : 'queued' , channelToBeFunded , amountA , amountB , type : 'defund' , } , tx ) ; } public get isFund ( ) : boolean { return this . type === 'fund' ; } public get isDefund ( ) : boolean { return this . type === 'defund' ; } public get isQueued ( ) : boolean { return this . status === 'queued' ; } public get isPending ( ) : boolean { return this . status === 'pending' ; } public get isActive ( ) : boolean { return this . isQueued || this . isPending ; } public get isTerminal ( ) : boolean { return ! this . isActive ; } static async markLedgerRequestsSuccessful ( requests , type , tx ) { await Promise . all ( requests . map ( req => LedgerRequest . setRequestStatus ( req , type , 'succeeded' , tx ) ) ) ; } public get totalAmount ( ) : Uint256 { return BN . add ( this . amountA , this . amountB ) ; } }