UNPKG

@test-org122/hypernet-core

Version:

Hypernet Core. Represents the SDK for running the Hypernet Protocol.

390 lines 19 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PaymentRepository = void 0; const utils_1 = require("@test-org122/utils"); const objects_1 = require("@interfaces/objects"); const errors_1 = require("@interfaces/objects/errors"); const types_1 = require("@interfaces/types"); const EMessageTransferType_1 = require("@interfaces/types/EMessageTransferType"); const neverthrow_1 = require("neverthrow"); /** * Contains methods for creating push, pull, etc payments, * as well as retrieving them, and finalizing them. */ class PaymentRepository { /** * Returns an instance of PaymentRepository */ constructor(browserNodeProvider, vectorUtils, configProvider, contextProvider, paymentUtils, logUtils, timeUtils) { this.browserNodeProvider = browserNodeProvider; this.vectorUtils = vectorUtils; this.configProvider = configProvider; this.contextProvider = contextProvider; this.paymentUtils = paymentUtils; this.logUtils = logUtils; this.timeUtils = timeUtils; } createPullRecord(paymentId, amount) { let transfers; let browserNode; return utils_1.ResultUtils.combine([this._getTransfersByPaymentId(paymentId), this.browserNodeProvider.getBrowserNode()]) .andThen((vals) => { [transfers, browserNode] = vals; return this.paymentUtils.transfersToPayment(paymentId, transfers); }) .andThen((payment) => { const message = { messageType: EMessageTransferType_1.EMessageTransferType.PULLPAYMENT, paymentId: paymentId, to: payment.to, from: payment.from, paymentToken: payment.paymentToken, pullPaymentAmount: amount, }; return this.vectorUtils.createPullNotificationTransfer(payment.to, message); }) .andThen((transferResponse) => { // Get the newly minted transfer return browserNode.getTransfer(transferResponse.transferId); }) .andThen((newTransfer) => { // Add the new transfer to the list transfers.push(newTransfer); // Convert the list of transfers to a payment (again) return this.paymentUtils.transfersToPayment(paymentId, transfers); }); } createPullPayment(counterPartyAccount, maximumAmount, // TODO: amounts should be consistently use BigNumber deltaTime, deltaAmount, // TODO: amounts should be consistently use BigNumber expirationDate, requiredStake, // TODO: amounts should be consistently use BigNumber paymentToken, merchantUrl) { let browserNode; let context; let paymentId; return utils_1.ResultUtils.combine([ this.browserNodeProvider.getBrowserNode(), this.contextProvider.getInitializedContext(), this.paymentUtils.createPaymentId(types_1.EPaymentType.Pull), ]) .andThen((vals) => { [browserNode, context, paymentId] = vals; const message = { messageType: EMessageTransferType_1.EMessageTransferType.OFFER, paymentId, creationDate: this.timeUtils.getUnixNow(), to: counterPartyAccount, from: context.publicIdentifier, requiredStake, paymentAmount: maximumAmount, expirationDate, paymentToken, merchantUrl, rate: { deltaAmount, deltaTime, }, }; // Create a message transfer, with the terms of the payment in the metadata. return this.vectorUtils.createOfferTransfer(counterPartyAccount, message); }) .andThen((transferInfo) => { return browserNode.getTransfer(transferInfo.transferId); }) .andThen((transfer) => { // Return the payment return this.paymentUtils.transfersToPayment(paymentId, [transfer]); }); } /** * Creates a push payment and returns it. Nothing moves until * the payment is accepted; the payment will return with the * "PROPOSED" status. This function just creates an OfferTransfer. * @param counterPartyAccount the public identifier of the account to pay * @param amount the amount to pay the counterparty * @param expirationDate the date (in unix time) at which point the payment will expire & revert * @param requiredStake the amount of insurance the counterparty must put up for this payment * @param paymentToken the (Ethereum) address of the payment token * @param merchantUrl the registered URL for the merchant that will resolve any disputes. */ createPushPayment(counterPartyAccount, amount, expirationDate, requiredStake, paymentToken, merchantUrl) { let browserNode; let context; let paymentId; return utils_1.ResultUtils.combine([ this.browserNodeProvider.getBrowserNode(), this.contextProvider.getInitializedContext(), this.paymentUtils.createPaymentId(types_1.EPaymentType.Push), ]) .andThen((vals) => { [browserNode, context, paymentId] = vals; const message = { messageType: EMessageTransferType_1.EMessageTransferType.OFFER, paymentId, creationDate: this.timeUtils.getUnixNow(), to: counterPartyAccount, from: context.publicIdentifier, requiredStake: requiredStake.toString(), paymentAmount: amount.toString(), expirationDate: expirationDate, paymentToken, merchantUrl, }; // Create a message transfer, with the terms of the payment in the metadata. return this.vectorUtils.createOfferTransfer(counterPartyAccount, message); }) .andThen((transferInfo) => { return browserNode.getTransfer(transferInfo.transferId); }) .andThen((transfer) => { // Return the payment return this.paymentUtils.transfersToPayment(paymentId, [transfer]); }); } /** * Given a paymentId, return the component transfers. * @param paymentId the payment to get transfers for */ _getTransfersByPaymentId(paymentId) { let browserNode; let channelAddress; return utils_1.ResultUtils.combine([this.browserNodeProvider.getBrowserNode(), this.vectorUtils.getRouterChannelAddress()]) .andThen((vals) => { [browserNode, channelAddress] = vals; return browserNode.getActiveTransfers(channelAddress); }) .andThen((activeTransfers) => { // We also need to look for potentially resolved transfers const earliestDate = this.paymentUtils.getEarliestDateFromTransfers(activeTransfers); return browserNode.getTransfers(earliestDate, this.timeUtils.getUnixNow()); }) .andThen((transfers) => { // This new list is complete- it should include active and inactive transfers // after the earliest active transfer const transferTypeResults = new Array(); for (const transfer of transfers) { transferTypeResults.push(this.paymentUtils.getTransferTypeWithTransfer(transfer)); } return neverthrow_1.combine(transferTypeResults); }) .andThen((tranferTypesWithTransfers) => { // For each transfer, we are either just going to know it's relevant // from the data in the metadata, or we are going to check if it's an // insurance payment and we have more bulletproof ways to check const relevantTransfers = []; for (const transferTypeWithTransfer of tranferTypesWithTransfers) { const { transferType, transfer } = transferTypeWithTransfer; if (transferType === types_1.ETransferType.Offer) { const offerDetails = JSON.parse(transfer.transferState.message); if (offerDetails.paymentId === paymentId) { relevantTransfers.push(transfer); } } else { if (transferType === types_1.ETransferType.Insurance || transferType === types_1.ETransferType.Parameterized) { if (paymentId === transfer.transferState.UUID) { relevantTransfers.push(transfer); } else { this.logUtils.log(`Transfer not relevant in PaymentRepository, transferId: ${transfer.transferId}`); } } else { this.logUtils.log(`Unrecognized transfer in PaymentRepository, transferId: ${transfer.transferId}`); } } } return neverthrow_1.okAsync(relevantTransfers); }); } /** * Given a list of payment Ids, return the associated payments. * @param paymentIds the list of payments to get */ getPaymentsByIds(paymentIds) { let browserNode; let channelAddress; return utils_1.ResultUtils.combine([this.browserNodeProvider.getBrowserNode(), this.vectorUtils.getRouterChannelAddress()]) .andThen((vals) => { [browserNode, channelAddress] = vals; return browserNode.getActiveTransfers(channelAddress); }) .andThen((activeTransfers) => { // We also need to look for potentially resolved transfers const earliestDate = this.paymentUtils.getEarliestDateFromTransfers(activeTransfers); return browserNode.getTransfers(earliestDate, this.timeUtils.getUnixNow()); }) .andThen((transfers) => { const transferTypeResults = new Array(); for (const transfer of transfers) { transferTypeResults.push(this.paymentUtils.getTransferTypeWithTransfer(transfer)); } return neverthrow_1.combine(transferTypeResults); }) .andThen((tranferTypesWithTransfers) => { // For each transfer, we are either just going to know it's relevant // from the data in the metadata, or we are going to check if it's an // insurance payment and we have more bulletproof ways to check const relevantTransfers = []; for (const transferTypeWithTransfer of tranferTypesWithTransfers) { const { transferType, transfer } = transferTypeWithTransfer; if (transferType === types_1.ETransferType.Offer) { const offerDetails = JSON.parse(transfer.transferState.message); if (paymentIds.includes(offerDetails.paymentId)) { relevantTransfers.push(transfer); } } else { if (transferType === types_1.ETransferType.Insurance || transferType === types_1.ETransferType.Parameterized) { if (paymentIds.includes(transfer.transferState.UUID)) { relevantTransfers.push(transfer); } else { this.logUtils.log(`Transfer not relevant in PaymentRepository, transferId: ${transfer.transferId}`); } } else { this.logUtils.log(`Unrecognized transfer in PaymentRepository, transferId: ${transfer.transferId}`); } } } return this.paymentUtils.transfersToPayments(relevantTransfers); }) .map((payments) => { return payments.reduce((map, obj) => { map.set(obj.id, obj); return map; }, new Map()); }); } /** * Finalizes/confirms a payment * Internally, this is what actually calls resolve() on the Vector transfer - * be it a insurancePayments or parameterizedPayments. * @param paymentId the payment to finalize * @param amount the amount of the payment to finalize for */ finalizePayment(paymentId, amount) { let browserNode; let existingTransfers; let parameterizedTransferId; return utils_1.ResultUtils.combine([this.browserNodeProvider.getBrowserNode(), this._getTransfersByPaymentId(paymentId)]) .andThen((vals) => { [browserNode, existingTransfers] = vals; this.logUtils.log(`Finalizing payment ${paymentId}`); // get the transfer id from the paymentId // use payment utils for this return this.paymentUtils.sortTransfers(paymentId, existingTransfers); }) .andThen((sortedTransfers) => { if (sortedTransfers.parameterizedTransfer == null) { return neverthrow_1.errAsync(new errors_1.PaymentFinalizeError(`Cannot finalize payment ${paymentId}, no parameterized transfer exists for this!`)); } parameterizedTransferId = sortedTransfers.parameterizedTransfer.transferId; return this.vectorUtils.resolvePaymentTransfer(parameterizedTransferId, paymentId, amount); }) .andThen(() => { return browserNode.getTransfer(parameterizedTransferId); }) .andThen((transfer) => { // Remove the parameterized transfer, and replace it // with this latest transfer existingTransfers = existingTransfers.filter((obj) => obj.transferId !== parameterizedTransferId); existingTransfers.push(transfer); // Transfer has been resolved successfully; return the updated payment. const updatedPayment = this.paymentUtils.transfersToPayment(paymentId, existingTransfers); return updatedPayment; }); } /** * Provides stake for a given payment id * Internally, this is what actually creates the InsurancePayments with Vector. * @param paymentId the payment for which to provide stake for */ provideStake(paymentId, merchantPublicKey) { let browserNode; let config; let existingTransfers; return utils_1.ResultUtils.combine([ this.browserNodeProvider.getBrowserNode(), this.configProvider.getConfig(), this._getTransfersByPaymentId(paymentId), ]) .andThen((vals) => { [browserNode, config, existingTransfers] = vals; return this.paymentUtils.transfersToPayment(paymentId, existingTransfers); }) .andThen((payment) => { const paymentSender = payment.from; const paymentID = payment.id; const paymentStart = this.timeUtils.getUnixNow(); const paymentExpiration = paymentStart + config.defaultPaymentExpiryLength; // TODO: There are probably some logical times when you should not provide a stake if (false) { return neverthrow_1.errAsync(new errors_1.PaymentStakeError()); } this.logUtils.log(`PaymentRepository:provideStake: Creating insurance transfer for paymentId: ${paymentId}`); return this.vectorUtils.createInsuranceTransfer(paymentSender, merchantPublicKey, payment.requiredStake, paymentExpiration, paymentID); }) .andThen((transferInfoUnk) => { const transferInfo = transferInfoUnk; return browserNode.getTransfer(transferInfo.transferId); }) .andThen((transfer) => { const allTransfers = [transfer, ...existingTransfers]; // Transfer has been created successfully; return the updated payment. return this.paymentUtils.transfersToPayment(paymentId, allTransfers); }); } /** * Singular version of provideAssets * Internally, creates a parameterizedPayment with Vector, * and returns a payment of state 'Approved' * @param paymentId the payment for which to provide an asset for */ provideAsset(paymentId) { let browserNode; let config; let existingTransfers; return utils_1.ResultUtils.combine([ this.browserNodeProvider.getBrowserNode(), this.configProvider.getConfig(), this._getTransfersByPaymentId(paymentId), ]) .andThen((vals) => { [browserNode, config, existingTransfers] = vals; return this.paymentUtils.transfersToPayment(paymentId, existingTransfers); }) .andThen((payment) => { const paymentTokenAddress = payment.paymentToken; let paymentTokenAmount; if (payment instanceof objects_1.PushPayment) { paymentTokenAmount = payment.paymentAmount; } else if (payment instanceof objects_1.PullPayment) { paymentTokenAmount = payment.authorizedAmount; } else { this.logUtils.error(`Payment was not instance of push or pull payment!`); return neverthrow_1.errAsync(new errors_1.LogicalError()); } const paymentRecipient = payment.to; const paymentID = payment.id; const paymentStart = this.timeUtils.getUnixNow(); const paymentExpiration = paymentStart + config.defaultPaymentExpiryLength; this.logUtils.log(`Providing a payment amount of ${paymentTokenAmount}`); // Use vectorUtils to create the parameterizedPayment return this.vectorUtils.createPaymentTransfer(payment instanceof objects_1.PushPayment ? types_1.EPaymentType.Push : types_1.EPaymentType.Pull, paymentRecipient, paymentTokenAmount, paymentTokenAddress, paymentID, paymentStart, paymentExpiration, payment instanceof objects_1.PullPayment ? payment.deltaTime : undefined, payment instanceof objects_1.PullPayment ? payment.deltaAmount.toString() : undefined); }) .andThen((transferInfoUnk) => { const transferInfo = transferInfoUnk; return browserNode.getTransfer(transferInfo.transferId); }) .andThen((transfer) => { const allTransfers = [transfer, ...existingTransfers]; // Transfer has been created successfully; return the updated payment. return this.paymentUtils.transfersToPayment(paymentId, allTransfers); }); } } exports.PaymentRepository = PaymentRepository; //# sourceMappingURL=PaymentRepository.js.map