import { DatabaseService } from '@energyweb/origin-backend-utils' ; import { UserStatus } from '@energyweb/origin-backend-core' ; import { HttpStatus , INestApplication } from '@nestjs/common' ; import { expect } from 'chai' ; import { ethers } from 'ethers' ; import moment from 'moment' ; import request from 'supertest' ; import { AccountBalanceDTO , AccountService , CreateAskDTO , Order , RequestWithdrawalDTO , RequestClaimDTO , TransferDirection , TransferStatus , Transfer , TransferService , DB_TABLE_PREFIX , testUtils , RequestBatchClaimDTO , RequestSendDTO , AccountBalanceService } from '@energyweb/exchange' ; import { TestProduct } from '@energyweb/exchange/test/product/get-product.handler' ; import { getProviderWithFallback } from '@energyweb/utils-general' ; import { RegistryExtended } from '@energyweb/issuer/dist/js/src/ethers/RegistryExtended' ; import { Issuer } from '@energyweb/issuer/dist/js/src/ethers/Issuer' ; import { ConfigService } from '@nestjs/config' ; import { Certificate } from '@energyweb/issuer' ; import { ExchangeErc1888Module } from '../src' ; const web3 = 'http://localhost:8590' ; const { authenticatedUser , createDepositAddress , depositToken , issueToken , bootstrapTestInstance , MWh } = testUtils ; const sleep = ( ms ) => new Promise ( ( resolve ) => setTimeout ( resolve , ms ) ) ; describe ( 'Deposits using deployed registry' , ( ) => { let app ; let databaseService ; let accountService ; let accountBalanceService ; let transferService ; let configService ; const user1Id = authenticatedUser . organization . id ; let registry ; let issuer ; let depositAddress ; let tokenId ; let token2Id ; const tokenReceiverPrivateKey = '0xca77c9b06fde68bcbcc09f603c958620613f4be79f3abb4b2032131d0229462e' ; const tokenReceiver = new ethers . Wallet ( tokenReceiverPrivateKey , getProviderWithFallback ( web3 ) ) ; const generationFrom = moment ( '2020-01-01' ) . unix ( ) ; const generationTo = moment ( '2020-01-31' ) . unix ( ) ; const startExchange = async ( ) => { ( { accountService , accountBalanceService , databaseService , transferService , configService , registry , issuer , app } = await bootstrapTestInstance ( web3 , null , [ ExchangeErc1888Module ] ) ) ; await app . init ( ) ; tokenId = await issueToken ( issuer , tokenReceiver . address , `${ 1000 * MWh }` , generationFrom , generationTo ) ; token2Id = await issueToken ( issuer , tokenReceiver . address , `${ 500 * MWh }` , generationFrom , generationTo ) ; } ; const depositToExchangeAddress = async ( amount , id ? ) : Promise < string > => { await depositToken ( registry , tokenReceiver , depositAddress , amount , id ?? tokenId ) ; await sleep ( 5000 ) ; const { body : [ transfer ] } = await request ( app . getHttpServer ( ) ) . get ( '/transfer/all' ) ; expect ( transfer . id ) . to . be . ok ; expect ( transfer . asset . tokenId ) . equals ( tokenId . toString ( ) ) ; return transfer . asset . id ; } ; before ( startExchange ) ; after ( async ( ) => { await databaseService . cleanUp ( ) ; await app . close ( ) ; } ) ; beforeEach ( async ( ) => { await databaseService . truncate ( `${ DB_TABLE_PREFIX }_order` , `${ DB_TABLE_PREFIX }_transfer` ) ; depositAddress = await createDepositAddress ( accountService , user1Id ) ; } ) ; const getBalance = async ( address , id ) : Promise < ethers . BigNumber > => registry . balanceOf ( address , id ) ; const getClaimedBalance = async ( address , id ) : Promise < ethers . BigNumber > => registry . claimedBalanceOf ( address , id ) ; it ( 'should be able to discover token deposit and post the ask' , async ( ) => { const depositAmount = `${ 10 * MWh }` ; await depositToken ( registry , tokenReceiver , depositAddress , depositAmount , tokenId ) ; await sleep ( 5000 ) ; await request ( app . getHttpServer ( ) ) . get ( '/transfer/all' ) . expect ( HttpStatus . OK ) . expect ( ( res ) => { const transfers = res . body as Transfer [ ] ; const [ tokenDeposit ] = transfers ; expect ( transfers ) . to . have . length ( 1 ) ; expect ( tokenDeposit . userId ) . equals ( user1Id ) ; expect ( tokenDeposit . direction ) . equals ( TransferDirection . Deposit ) ; expect ( tokenDeposit . amount ) . equals ( depositAmount ) ; expect ( tokenDeposit . address ) . equals ( depositAddress ) ; } ) ; let assetId ; await request ( app . getHttpServer ( ) ) . get ( '/account-balance' ) . expect ( HttpStatus . OK ) . expect ( ( res ) => { const { available : [ balance ] } = res . body as AccountBalanceDTO ; expect ( balance . amount ) . equals ( depositAmount ) ; expect ( new Date ( balance . asset . generationFrom ) ) . deep . equals ( moment . unix ( generationFrom ) . toDate ( ) ) ; expect ( new Date ( balance . asset . generationTo ) ) . deep . equals ( moment . unix ( generationTo ) . toDate ( ) ) ; assetId = balance . asset . id ; } ) ; const createAsk = { assetId , volume : `${ 10 * MWh }` , price : 100 , validFrom : new Date ( ) } ; await request ( app . getHttpServer ( ) ) . post ( '/orders/ask' ) . send ( createAsk ) . expect ( HttpStatus . CREATED ) . expect ( ( res ) => { const order = res . body as Order ; expect ( order . price ) . equals ( 100 ) ; expect ( order . startVolume ) . equals ( `${ 10 * MWh }` ) ; expect ( order . assetId ) . equals ( assetId ) ; expect ( order . product ) . equals ( TestProduct ) ; } ) ; } ) ; it ( 'should withdraw to requested address' , async ( ) => { const withdrawalAddress = ethers . Wallet . createRandom ( ) . address ; const withdrawalAmount = '5' ; const depositAmount = '10' ; const assetId = await depositToExchangeAddress ( depositAmount ) ; const withdrawal = { assetId , amount : withdrawalAmount , address : withdrawalAddress } ; const startBalance = await getBalance ( withdrawalAddress , tokenId ) ; await request ( app . getHttpServer ( ) ) . post ( '/transfer/withdrawal' ) . send ( withdrawal ) . expect ( HttpStatus . CREATED ) ; await sleep ( 5000 ) ; const endBalance = await getBalance ( withdrawalAddress , tokenId ) ; expect ( endBalance . gt ( startBalance ) ) . to . equal ( true ) ; } ) ; it ( 'should re-test unconfirmed withdrawal on start' , async ( ) => { const withdrawalAddress = ethers . Wallet . createRandom ( ) . address ; const amount = '5' ; const assetId = await depositToExchangeAddress ( amount ) ; const withdrawal = { assetId , amount , address : withdrawalAddress } ; await request ( app . getHttpServer ( ) ) . post ( '/transfer/withdrawal' ) . send ( withdrawal ) . expect ( HttpStatus . CREATED ) ; await sleep ( 5000 ) ; const [ confirmed ] = await transferService . getByStatus ( TransferStatus . Confirmed , TransferDirection . Withdrawal ) ; await transferService . setAsUnconfirmed ( confirmed . id , confirmed . transactionHash ) ; await app . close ( ) ; await startExchange ( ) ; const transfer = await transferService . findOne ( confirmed . id ) ; expect ( transfer . status ) . to . be . equal ( TransferStatus . Confirmed ) ; expect ( transfer . confirmationBlock ) . to . be . equal ( confirmed . confirmationBlock ) ; } ) ; it ( 'should not allow withdrawing to more than available assets' , async ( ) => { const withdrawalAddress = ethers . Wallet . createRandom ( ) . address ; const withdrawalAmount = '10' ; const depositAmount = '10' ; const depositAddress2 = await createDepositAddress ( accountService , 'user2' ) ; await depositToken ( registry , tokenReceiver , depositAddress2 , depositAmount , tokenId ) ; const assetId = await depositToExchangeAddress ( depositAmount ) ; const withdrawal = { assetId , amount : withdrawalAmount , address : withdrawalAddress } ; for ( let i = 0 ; i < 10 ; i ++ ) { request ( app . getHttpServer ( ) ) . post ( '/transfer/withdrawal' ) . send ( withdrawal ) . end ( ( ) => { } ) ; } await sleep ( 5000 ) ; const endBalance = await getBalance ( withdrawalAddress , tokenId ) ; expect ( endBalance . toString ( ) ) . to . be . equal ( withdrawalAmount ) ; } ) ; it ( 'should claim from the exchange' , async ( ) => { const tokenAmount = '10' ; const exchangeAddress = configService . get ( 'EXCHANGE_WALLET_PUB' ) ; const assetId = await depositToExchangeAddress ( tokenAmount ) ; const claim = { assetId , amount : tokenAmount , claimData : { beneficiary : 'EnergyWeb' , location : '133 N. St, Orange County, CA 19444' , countryCode : 'GB' , periodStartDate : '2021-11-08T17:11:11.883Z' , periodEndDate : '2021-11-08T17:11:11.883Z' , purpose : 'No idea' } } ; const balance = await getBalance ( exchangeAddress , tokenId ) ; expect ( balance . toNumber ( ) ) . to . be . above ( Number ( tokenAmount ) ) ; await request ( app . getHttpServer ( ) ) . post ( '/transfer/claim' ) . send ( claim ) . expect ( HttpStatus . CREATED ) ; await sleep ( 5000 ) ; const claimedBalance = await getClaimedBalance ( exchangeAddress , tokenId ) ; expect ( claimedBalance . toString ( ) ) . to . equal ( tokenAmount ) ; const certificate = await new Certificate ( Number ( tokenId ) , { registry , issuer , web3 : getProviderWithFallback ( web3 ) } ) . sync ( ) ; const [ claimDetails ] = await certificate . getClaimedData ( ) ; expect ( claimDetails . claimData ) . to . deep . equal ( claim . claimData ) ; const { body : transfers } = await request ( app . getHttpServer ( ) ) . get ( '/transfer/all/claimed' ) . expect ( HttpStatus . OK ) ; const [ claimed ] = transfers ; expect ( transfers . length ) . to . be . gte ( 1 ) ; expect ( claimed . userId ) . equals ( user1Id ) ; expect ( claimed . direction ) . equals ( TransferDirection . Claim ) ; } ) ; it ( 'should batch claim from the exchange' , async ( ) => { const tokenAmount = '10' ; const token2Amount = '5' ; const exchangeAddress = configService . get ( 'EXCHANGE_WALLET_PUB' ) ; const assetId = await depositToExchangeAddress ( tokenAmount ) ; const asset2Id = await depositToExchangeAddress ( token2Amount , token2Id ) ; const batchClaim = { assetIds : [ assetId , asset2Id ] , claimData : { beneficiary : 'EnergyWeb' , location : '133 N. St, Orange County, CA 19444' , countryCode : 'GB' , periodStartDate : '2021-11-08T17:11:11.883Z' , periodEndDate : '2021-11-08T17:11:11.883Z' , purpose : 'No idea' } } ; const balance = await getBalance ( exchangeAddress , tokenId ) ; const balance2 = await getBalance ( exchangeAddress , token2Id ) ; expect ( balance . toNumber ( ) ) . to . be . least ( Number ( tokenAmount ) ) ; expect ( balance2 . toNumber ( ) ) . to . be . least ( Number ( token2Amount ) ) ; await request ( app . getHttpServer ( ) ) . post ( '/transfer/claim/batch' ) . send ( batchClaim ) . expect ( HttpStatus . CREATED ) ; await sleep ( 5000 ) ; const claimedBalance = await getClaimedBalance ( exchangeAddress , tokenId ) ; expect ( claimedBalance . toNumber ( ) ) . to . be . least ( Number ( tokenAmount ) + Number ( token2Amount ) ) ; } ) ; it ( 'should send to requested address' , async ( ) => { const recipient = { id : 2 , organization : { id : '2000' } , status : UserStatus . Active } ; const destinationAddress = await createDepositAddress ( accountService , recipient . organization . id ) ; const sendAmount = '3' ; const depositAmount = '10' ; const assetId = await depositToExchangeAddress ( depositAmount ) ; const send = { assetId , amount : sendAmount , address : destinationAddress } ; const startBalance = await getBalance ( destinationAddress , tokenId ) ; expect ( startBalance . isZero ( ) ) . equals ( true ) ; await request ( app . getHttpServer ( ) ) . post ( '/transfer/send' ) . send ( send ) . expect ( HttpStatus . CREATED ) ; await sleep ( 6000 ) ; const destinationBalance = await accountBalanceService . getAccountBalance ( recipient . organization . id ) ; expect ( destinationBalance . available [ 0 ] . amount . toString ( ) ) . equals ( sendAmount ) ; } ) ; } ) ;