import { Node } from "@counterfactual/types" ; import { BigNumber } from "ethers/utils" ; import { jsonRpcMethod } from "rpc-server" ; import { CONVENTION_FOR_ETH_TOKEN_ADDRESS } from "../../../constants" ; import { Protocol , xkeyKthAddress } from "../../../engine" ; import { StateChannel } from "../../../models" ; import { RequestHandler } from "../../../request-handler" ; import { getCreate2MultisigAddress } from "../../../utils" ; import { NodeController } from "../../controller" ; import { INSUFFICIENT_FUNDS_IN_FREE_BALANCE_FOR_ASSET , NULL_INITIAL_STATE_FOR_PROPOSAL } from "../../errors" ; returns export default class ProposeInstallController extends NodeController { @ jsonRpcMethod ( Node . RpcMethodName . PROPOSE_INSTALL ) public executeMethod = super . executeMethod ; protected async getRequiredLockNames ( requestHandler , params ) { const { publicIdentifier , networkContext , store } = requestHandler ; const { proposedToIdentifier } = params ; const multisigAddress = await store . getMultisigAddressWithCounterparty ( publicIdentifier , proposedToIdentifier , networkContext . ProxyFactory , networkContext . MinimumViableMultisig ) ; return [ multisigAddress ] ; } protected async beforeExecution ( requestHandler , params ) { const { store , publicIdentifier , networkContext } = requestHandler ; const { initialState } = params ; if ( ! initialState ) { throw Error ( NULL_INITIAL_STATE_FOR_PROPOSAL ) ; } const { proposedToIdentifier , initiatorDeposit , responderDeposit , initiatorDepositTokenAddress : initiatorDepositTokenAddressParam , responderDepositTokenAddress : responderDepositTokenAddressParam } = params ; const myIdentifier = publicIdentifier ; const multisigAddress = await store . getMultisigAddressWithCounterparty ( publicIdentifier , proposedToIdentifier , networkContext . ProxyFactory , networkContext . MinimumViableMultisig ) ; const initiatorDepositTokenAddress = initiatorDepositTokenAddressParam || CONVENTION_FOR_ETH_TOKEN_ADDRESS ; const responderDepositTokenAddress = responderDepositTokenAddressParam || CONVENTION_FOR_ETH_TOKEN_ADDRESS ; const stateChannel = await store . getOrCreateStateChannelBetweenVirtualAppParticipants ( multisigAddress , myIdentifier , proposedToIdentifier ) ; assertSufficientFundsWithinFreeBalance ( stateChannel , myIdentifier , initiatorDepositTokenAddress , initiatorDeposit ) ; assertSufficientFundsWithinFreeBalance ( stateChannel , proposedToIdentifier , responderDepositTokenAddress , responderDeposit ) ; params . initiatorDepositTokenAddress = initiatorDepositTokenAddress ; params . responderDepositTokenAddress = responderDepositTokenAddress ; } protected async executeMethodImplementation ( requestHandler , params ) { const { store , publicIdentifier , networkContext , protocolRunner } = requestHandler ; const { proposedToIdentifier } = params ; const multisigAddress = await store . getMultisigAddressWithCounterparty ( publicIdentifier , proposedToIdentifier , networkContext . ProxyFactory , networkContext . MinimumViableMultisig ) ; await protocolRunner . initiateProtocol ( Protocol . Propose , { ... params , multisigAddress , initiatorXpub : publicIdentifier , responderXpub : proposedToIdentifier } ) ; return { appInstanceId : ( await store . getStateChannel ( multisigAddress ) ) . mostRecentlyProposedAppInstance ( ) . identityHash } ; } } function assertSufficientFundsWithinFreeBalance ( channel , publicIdentifier , tokenAddress , depositAmount ) { if ( ! channel . hasFreeBalance ) return ; const freeBalanceForToken = channel . getFreeBalanceClass ( ) . getBalance ( tokenAddress , xkeyKthAddress ( publicIdentifier , 0 ) ) ; if ( freeBalanceForToken . lt ( depositAmount ) ) { throw Error ( INSUFFICIENT_FUNDS_IN_FREE_BALANCE_FOR_ASSET ( publicIdentifier , channel . multisigAddress , tokenAddress , freeBalanceForToken , depositAmount ) ) ; } }