import { expect } from "chai" ; import * as ABIDecoder from "abi-decoder" ; import { compact } from "lodash" ; import { BigNumber } from "bignumber.js" ; import * as Web3 from "web3" ; import { CollateralizerContract } from "types/generated/collateralizer" ; import { MockDebtRegistryContract } from "types/generated/mock_debt_registry" ; import { MockERC20TokenContract } from "types/generated/mock_e_r_c20_token" ; import { MockTokenRegistryContract } from "types/generated/mock_token_registry" ; import { MockTokenTransferProxyContract } from "types/generated/mock_token_transfer_proxy" ; import { MockCollateralizedTermsContractContract } from "../../../../../types/generated/mock_collateralized_terms_contract" ; import { SeizeCollateralScenario , TestAccounts , TestContracts } from "./" ; import { NULL_ADDRESS , REVERT_ERROR } from "../../../test_utils/constants" ; import { Web3Utils } from "../../../../../utils/web3_utils" ; import { CollateralSeized } from "../../../logs/collateralized_contract" ; import { CollateralizedSimpleInterestTermsParameters } from "../../../factories/terms_contract_parameters" ; export class SeizeCollateralRunner { private web3Utils : Web3Utils ; private contracts : TestContracts ; private accounts : TestAccounts ; constructor ( web3 ) { this . web3Utils = new Web3Utils ( web3 ) ; this . testScenario = this . testScenario . bind ( this ) ; } public initialize ( contracts , accounts ) { this . contracts = contracts ; this . accounts = accounts ; } public testScenario ( scenario ) { let COLLATERALIZER ; let NON_COLLATERALIZER ; let BENEFICIARY_1 ; let BENEFICIARY_2 ; let ATTACKER ; let collateralizer ; let mockDebtRegistry ; let mockCollateralToken ; let mockTokenRegistry ; let mockTokenTransferProxy ; let mockTermsContract ; let txHash ; describe ( scenario . description , ( ) => { before ( async ( ) => { collateralizer = this . contracts . collateralizer ; mockDebtRegistry = this . contracts . mockDebtRegistry ; mockCollateralToken = this . contracts . mockCollateralToken ; mockTokenRegistry = this . contracts . mockTokenRegistry ; mockTokenTransferProxy = this . contracts . mockTokenTransferProxy ; mockTermsContract = this . contracts . mockTermsContract ; BENEFICIARY_1 = this . accounts . BENEFICIARY_1 ; BENEFICIARY_2 = this . accounts . BENEFICIARY_2 ; ATTACKER = this . accounts . ATTACKER ; COLLATERALIZER = this . accounts . COLLATERALIZER ; NON_COLLATERALIZER = this . accounts . NON_COLLATERALIZER ; await mockDebtRegistry . reset . sendTransactionAsync ( ) ; await mockCollateralToken . reset . sendTransactionAsync ( ) ; await mockTokenRegistry . reset . sendTransactionAsync ( ) ; const latestBlockTime = await this . web3Utils . getLatestBlockTime ( ) ; await mockTokenRegistry . mockGetTokenAddressByIndex . sendTransactionAsync ( new BigNumber ( 0 ) , mockCollateralToken . address ) ; const termsContractParameters = CollateralizedSimpleInterestTermsParameters . pack ( { collateralTokenIndex : new BigNumber ( 0 ) , collateralAmount : scenario . collateralAmount , gracePeriodInDays : scenario . gracePeriodInDays , } ) ; if ( scenario . debtAgreementExists ) { await mockDebtRegistry . mockGetTermsReturnValueFor . sendTransactionAsync ( scenario . agreementId , scenario . termsContract ( mockTermsContract . address , ATTACKER ) , termsContractParameters ) ; } if ( scenario . debtAgreementExists ) { await mockDebtRegistry . mockGetBeneficiaryReturnValueFor . sendTransactionAsync ( scenario . agreementId , BENEFICIARY_1 ) ; } await mockCollateralToken . mockBalanceOfFor . sendTransactionAsync ( COLLATERALIZER , scenario . collateralAmount ) ; await mockCollateralToken . mockAllowanceFor . sendTransactionAsync ( COLLATERALIZER , mockTokenTransferProxy . address , scenario . collateralAmount ) ; if ( scenario . debtAgreementExists && scenario . debtAgreementCollateralized && scenario . validTermsContract ) { await mockTermsContract . mockCallCollateralize . sendTransactionAsync ( collateralizer . address , scenario . agreementId , COLLATERALIZER ) ; } for ( const repaymentDate of scenario . expectedRepaymentValueSchedule ) { await mockTermsContract . mockExpectedRepaymentValue . sendTransactionAsync ( scenario . agreementId , new BigNumber ( repaymentDate . timestamp ( latestBlockTime ) ) , repaymentDate . expectedRepaymentValue ) ; } await mockTermsContract . mockDummyValueRepaid . sendTransactionAsync ( scenario . agreementId , scenario . valueRepaidToDate ) ; if ( typeof scenario . before !== "undefined" ) { await scenario . before ( collateralizer , mockTermsContract ) ; } ABIDecoder . addABI ( collateralizer . abi ) ; } ) ; after ( ( ) => { ABIDecoder . addABI ( collateralizer . abi ) ; } ) ; if ( scenario . succeeds ) { it ( "should not throw" , async ( ) => { txHash = await collateralizer . seizeCollateral . sendTransactionAsync ( scenario . agreementId , { from : scenario . from ( BENEFICIARY_1 , BENEFICIARY_2 ) } ) ; expect ( txHash . length ) . to . equal ( 66 ) ; } ) ; it ( "should erase record of current collateralization" , async ( ) => { await expect ( collateralizer . agreementToCollateralizer . callAsync ( scenario . agreementId ) ) . to . eventually . equal ( NULL_ADDRESS ) ; } ) ; it ( "should emit log that collateral has been seized" , async ( ) => { const receipt = await web3 . eth . getTransactionReceipt ( txHash ) ; const [ CollateralSeizedLog ] = compact ( ABIDecoder . decodeLogs ( receipt . logs ) ) ; expect ( CollateralSeizedLog ) . to . deep . equal ( CollateralSeized ( collateralizer . address , scenario . agreementId , scenario . beneficiary ( BENEFICIARY_1 , BENEFICIARY_2 ) , mockCollateralToken . address , scenario . collateralAmount ) ) ; } ) ; it ( "should transfer collateral from terms contract to current beneficiary" , async ( ) => { await expect ( mockCollateralToken . wasTransferCalledWith . callAsync ( scenario . beneficiary ( BENEFICIARY_1 , BENEFICIARY_2 ) , scenario . collateralAmount ) ) . to . eventually . be . true ; } ) ; } else { it ( "should throw" , async ( ) => { await expect ( collateralizer . seizeCollateral . sendTransactionAsync ( scenario . agreementId , { from : scenario . from ( BENEFICIARY_1 , ATTACKER ) , } ) ) . to . eventually . be . rejectedWith ( REVERT_ERROR ) ; } ) ; } } ) ; } }