import { getLogger , logError , SignatureProvider , serializeObject , DefaultSignatureProvider , SimpleClient , } from '@pigi/core-utils' ; import { SignedByDBInterface , SignedByDB , SignedByDecider , MerkleInclusionProofDecider , } from '@pigi/ovm' ; import { DB } from '@pigi/core-db' ; import { Address , Balances , FaucetRequest , isFaucetTransaction , RollupStateSolver , RollupTransaction , SignatureError , SignedStateReceipt , StateReceipt , Swap , TokenType , Transfer , } from '../types' ; import { DefaultRollupStateSolver } from './rollup-state-solver' ; import { RollupClient } from './rollup-client' ; import { EMPTY_AGGREGATOR_SIGNATURE , UNISWAP_ADDRESS } from './utils' ; const log = getLogger ( 'unipig-transitioner' ) ; interface KnownState { [ pubKey ] : StateReceipt ; } export class UnipigTransitioner { private knownState : KnownState ; public static async new ( db , aggregatorAddress , signatureProvider ? , aggregatorURL = 'http://127.0.0.1:3000' ) { if ( ! signatureProvider ) { signatureProvider = new DefaultSignatureProvider ( ) ; } const signedByDB = new SignedByDB ( db ) ; const decider = new SignedByDecider ( signedByDB , await signatureProvider . getAddress ( ) ) ; const stateSolver = new DefaultRollupStateSolver ( signedByDB , decider , new MerkleInclusionProofDecider ( ) ) ; const rollupclient = new RollupClient ( db , aggregatorAddress ) ; rollupclient . connect ( new SimpleClient ( aggregatorURL ) ) ; return new UnipigTransitioner ( db , stateSolver , rollupclient , signatureProvider , aggregatorAddress ) ; } constructor ( private readonly db , private readonly stateSolver , private readonly rollupClient , private readonly signatureProvider , private readonly aggregatorAddress ) { this . knownState = { } ; } public async sign ( message ) { log . debug ( 'Address:' , await this . getAddress ( ) , 'signing message:' , message ) ; return this . signatureProvider . sign ( message ) ; } public async getAddress ( ) { return this . signatureProvider . getAddress ( ) ; } public async getUniswapBalances ( ) { return this . getBalances ( UNISWAP_ADDRESS ) ; } public async getUniswapState ( ) { return this . getState ( UNISWAP_ADDRESS ) ; } public async getBalances ( account ) { const stateReceipt = await this . getState ( account ) ; return ! ! stateReceipt && ! ! stateReceipt . state ? stateReceipt . state . balances : undefined ; } public async getState ( account ) { log . debug ( `Fetching state for ${ account }` ) ; const signedState = await this . rollupClient . getState ( account ) ; log . debug ( `State for ${ account }: ${ JSON . stringify ( signedState . stateReceipt ) }` ) ; if ( signedState . signature === EMPTY_AGGREGATOR_SIGNATURE ) { return signedState . stateReceipt ; } log . debug ( `Storing state for ${ account }: ${ JSON . stringify ( signedState . stateReceipt ) }` ) ; await this . stateSolver . storeSignedStateReceipt ( signedState ) ; this . knownState [ account ] = signedState . stateReceipt ; return account in this . knownState ? this . knownState [ account ] : undefined ; } public async send ( tokenType , to , amount ) { const transaction = { sender : await this . getAddress ( ) , recipient : to , tokenType , amount , } ; await this . submitTransaction ( transaction ) ; } public async swap ( tokenType , inputAmount , minOutputAmount , timeoutMillis ) { const transaction = { sender : await this . getAddress ( ) , tokenType , inputAmount , minOutputAmount , timeout : timeoutMillis , } ; await this . submitTransaction ( transaction ) ; } public async requestFaucetFunds ( amount = 10 ) { const faucetRequest = { sender : await this . getAddress ( ) , amount , } ; await this . submitTransaction ( faucetRequest ) ; } private async submitTransaction ( transaction ) { let receipts = [ ] ; try { if ( isFaucetTransaction ( transaction ) ) { receipts = [ await this . rollupClient . requestFaucetFunds ( transaction , this . signatureProvider ) , ] ; } else { receipts = await this . rollupClient . sendTransaction ( transaction , this . signatureProvider ) ; } } catch ( e ) { if ( e instanceof SignatureError ) { logError ( log , `Transaction was sent and a response was received, indicating it probably went through, but the signature on the response was invalid, so we cannot be sure. Tx: ${ serializeObject ( transaction ) }` , e ) ; } else { logError ( log , `An error occurred sending transaction: Tx: ${ serializeObject ( transaction ) }.` , e ) ; } throw e ; } try { await Promise . all ( receipts . map ( ( r ) => this . stateSolver . storeSignedStateReceipt ( r ) ) ) ; } catch ( e ) { logError ( log , `An error persisting transaction receipt for Tx: ${ serializeObject ( transaction ) }.` , e ) ; } } }