UNPKG

raiden-ts

Version:

Raiden Light Client Typescript/Javascript SDK

114 lines 7.37 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.coopSettleEpic = exports.coopSettleWithdrawReplyEpic = void 0; const pick_1 = __importDefault(require("lodash/pick")); const rxjs_1 = require("rxjs"); const operators_1 = require("rxjs/operators"); const channels_1 = require("../../channels"); const actions_1 = require("../../channels/actions"); const utils_1 = require("../../channels/utils"); const messages_1 = require("../../messages"); const error_1 = require("../../utils/error"); const ethers_1 = require("../../utils/ethers"); const rx_1 = require("../../utils/rx"); const actions_2 = require("../actions"); const state_1 = require("../state"); const utils_2 = require("./utils"); /** * Upon valid [[WithdrawConfirmation]] for a [[WithdrawRequest]].coop_settle=true from partner, * also send a [[WithdrawRequest]] with whole balance * * @param action$ - Observable of withdrawMessage.success actions * @param state$ - Observable of RaidenStates * @param deps - Epics dependencies * @param deps.log - Logger instance * @param deps.getTokenNetworkContract - TokenNetwork contract getter * @returns Observable of withdraw.request(coop_settle=false) actions */ function coopSettleWithdrawReplyEpic(action$, state$, { log, getTokenNetworkContract }) { return action$.pipe((0, operators_1.filter)(actions_2.withdrawMessage.success.is), (0, operators_1.filter)((action) => action.meta.direction === state_1.Direction.RECEIVED), (0, operators_1.withLatestFrom)(state$), (0, operators_1.mergeMap)(([action, state]) => { const tokenNetworkContract = getTokenNetworkContract(action.meta.tokenNetwork); return (0, ethers_1.checkContractHasMethod$)(tokenNetworkContract, 'cooperativeSettle').pipe((0, operators_1.mergeMap)(() => { const channel = state.channels[(0, utils_1.channelKey)(action.meta)]; (0, error_1.assert)(channel?.state === channels_1.ChannelState.open, 'channel not open'); const req = channel.partner.pendingWithdraws.find((0, utils_2.matchWithdraw)(messages_1.MessageType.WITHDRAW_REQUEST, action.payload.message)); (0, error_1.assert)(req, 'no matching WithdrawRequest found'); // shouldn't happen // only reply if this is a coop settle request from partner if (!req.coop_settle) return rxjs_1.EMPTY; const { ownTotalWithdrawable } = (0, utils_1.channelAmounts)(channel); return (0, rxjs_1.of)(actions_2.withdraw.request({ coopSettle: false }, { ...action.meta, direction: state_1.Direction.SENT, totalWithdraw: ownTotalWithdrawable, })); }), (0, operators_1.catchError)((error) => { log.warn('Could not reply to CoopSettle request, ignoring', { action, error }); return rxjs_1.EMPTY; })); })); } exports.coopSettleWithdrawReplyEpic = coopSettleWithdrawReplyEpic; /** * When both valid [[WithdrawConfirmation]] for a [[WithdrawRequest]].coop_settle=true from us, * send a channelSettle.request * * @param action$ - Observable of withdrawMessage.success actions * @param state$ - Observable of RaidenStates * @param deps - Epics dependencies * @param deps.latest$ - Latest observable * @param deps.config$ - Config observable * @param deps.log - Logger instance * @returns Observable of channelSettle.request|withdraw.failure|success|withdrawBusy actions */ function coopSettleEpic(action$, {}, { latest$, config$, log }) { return action$.pipe((0, rx_1.dispatchRequestAndGetResponse)(actions_1.channelSettle, (requestSettle$) => action$.pipe((0, operators_1.filter)(actions_2.withdrawMessage.success.is), (0, operators_1.groupBy)((action) => (0, utils_1.channelKey)(action.meta)), (0, operators_1.mergeMap)((grouped$) => grouped$.pipe((0, operators_1.concatMap)((action) => // observable inside concatMap ensures the body is evaluated at subscription time (0, rxjs_1.combineLatest)([latest$, config$]).pipe((0, operators_1.first)(), (0, operators_1.mergeMap)(([{ state }, { revealTimeout }]) => { const channel = state.channels[(0, utils_1.channelKey)(action.meta)]; (0, error_1.assert)(channel?.state === channels_1.ChannelState.open, 'channel not open'); const { ownCapacity, partnerCapacity, ownTotalWithdrawable, partnerTotalWithdrawable, } = (0, utils_1.channelAmounts)(channel); // when both capacities are zero, both sides should be ready; before that, just // skip silently, a matching state may come later or withdraw will expire (0, error_1.assert)((!channel.own.locks.length && !channel.partner.locks.length && ownCapacity.isZero()) || partnerCapacity.isZero(), ''); const ownReq = channel.own.pendingWithdraws.find((msg) => msg.type === messages_1.MessageType.WITHDRAW_REQUEST && msg.expiration.gte(Math.floor(Date.now() / 1e3 + revealTimeout)) && msg.total_withdraw.eq(ownTotalWithdrawable) && !!msg.coop_settle); // not our request or expires too soon (0, error_1.assert)(ownReq, 'own request not found'); const ownConfirmation = channel.own.pendingWithdraws.find((0, utils_2.matchWithdraw)(messages_1.MessageType.WITHDRAW_CONFIRMATION, ownReq)); const partnerReq = channel.partner.pendingWithdraws.find((msg) => msg.type === messages_1.MessageType.WITHDRAW_REQUEST && msg.expiration.gte(Math.floor(Date.now() / 1e3 + revealTimeout)) && msg.total_withdraw.eq(partnerTotalWithdrawable)); (0, error_1.assert)(partnerReq, 'partner request not found'); // shouldn't happen const partnerConfirmation = channel.partner.pendingWithdraws.find((0, utils_2.matchWithdraw)(messages_1.MessageType.WITHDRAW_CONFIRMATION, partnerReq)); (0, error_1.assert)(ownConfirmation && partnerConfirmation, [ 'no matching WithdrawConfirmations found', { ownConfirmation, partnerConfirmation }, ]); const withdrawMeta = (0, utils_2.withdrawMetaFromRequest)(ownReq, channel); return requestSettle$(actions_1.channelSettle.request({ coopSettle: [ [ownReq, ownConfirmation], [partnerReq, partnerConfirmation], ], }, { tokenNetwork: withdrawMeta.tokenNetwork, partner: withdrawMeta.partner })).pipe((0, operators_1.map)((success) => actions_2.withdraw.success((0, pick_1.default)(success.payload, ['txBlock', 'txHash', 'confirmed']), withdrawMeta)), (0, operators_1.catchError)((err) => (0, rxjs_1.of)(actions_2.withdraw.failure(err, withdrawMeta))), // prevents this withdraw from expire-failing while we're trying to settle (0, operators_1.startWith)((0, actions_2.withdrawBusy)(undefined, withdrawMeta))); }), (0, operators_1.catchError)((err) => { if (err.message) log.info(err.message, err.details); // these errors are just the asserts, to be ignored; // []actual errors are only the catched inside requestSettle$'s pipe return rxjs_1.EMPTY; })))))))); } exports.coopSettleEpic = coopSettleEpic; //# sourceMappingURL=coopsettle.js.map