@test-org122/hypernet-core
Version:
Hypernet Core. Represents the SDK for running the Hypernet Protocol.
390 lines • 19 kB
JavaScript
"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