const MoneroIncomingTransfer = require("./MoneroIncomingTransfer");
const MoneroOutgoingTransfer = require("./MoneroOutgoingTransfer");
const MoneroTransfer = require("./MoneroTransfer");
const MoneroError = require("../../common/MoneroError")
/**
* Configuration to query wallet transfers.
*
* @extends {MoneroTransfer}
*/
class MoneroTransferQuery extends MoneroTransfer {
/**
* <p>Construct the transfer query.</p>
*
* <p>Example:</p>
*
* <code>
* // get incoming transfers to account 0, subaddress 1<br>
* let transfers = await wallet.getTransfers({<br>
* accountIndex: 0,<br>
* subaddressIndex: 0<br>
* });
* </code>
*
* <p>All configuration is optional. All transfers are returned except those that don't meet criteria defined in this query.</p>
*
* @param {object} config - transfer query configuration (optional)
* @param {BigInteger} config.amount - get transfers with this amount
* @param {int} config.accountIndex - get transfers to/from this account index
* @param {int} config.subaddressIndex - get transfers to/from this subaddress index
* @param {int[]} config.subaddressIndices - get transfers to/from these subaddress indices
* @param {string} config.address - get transfers to/from this wallet address
* @param {string[]} config.addresses - get transfers to/from these wallet addresses
* @param {boolean} config.isIncoming - get transfers which are incoming if true
* @param {boolean} config.isOutgoing - get transfers which are outgoing if true
* @param {boolean} config.hasDestinations - get transfers with known destinations if true (destinations are only stored locally with the wallet)
* @param {object|MoneroTxQuery} config.txQuery - get transfers whose tx match this tx query
*/
constructor(config) {
super(config);
// deserialize if necessary
const MoneroTxQuery = require("./MoneroTxQuery");
if (this.state.txQuery && !(this.state.txQuery instanceof MoneroTxQuery)) this.state.txQuery = new MoneroTxQuery(this.state.txQuery);
if (this.state.txQuery) this.state.txQuery.setTransferQuery(this);
// alias isOutgoing to isIncoming
if (this.state.isOutgoing !== undefined) this.state.isIncoming = !this.state.isOutgoing;
// validate state
this._validate();
}
copy() {
return new MoneroTransferQuery(this);
}
toJson() {
let json = Object.assign({}, this.state, super.toJson());
delete json.txQuery;
return json;
}
getTxQuery() {
return this.state.txQuery;
}
setTxQuery(txQuery) {
this.state.txQuery = txQuery;
if (txQuery) txQuery.state.transferQuery = this;
return this;
}
isIncoming() {
return this.state.isIncoming;
}
setIsIncoming(isIncoming) {
this.state.isIncoming = isIncoming;
return this;
}
isOutgoing() {
return this.state.isIncoming === undefined ? undefined : !this.state.isIncoming;
}
setIsOutgoing(isOutgoing) {
this.state.isIncoming = isOutgoing === undefined ? undefined : !isOutgoing;
return this;
}
getAddress() {
return this.state.address;
}
setAddress(address) {
this.state.address = address;
return this;
}
getAddresses() {
return this.state.addresses;
}
setAddresses(addresses) {
this.state.addresses = addresses;
return this;
}
getSubaddressIndex() {
return this.state.subaddressIndex;
}
setSubaddressIndex(subaddressIndex) {
this.state.subaddressIndex = subaddressIndex;
this._validate();
return this;
}
getSubaddressIndices() {
return this.state.subaddressIndices;
}
setSubaddressIndices(subaddressIndices) {
this.state.subaddressIndices = subaddressIndices;
this._validate();
return this;
}
getDestinations() {
return this.state.destinations;
}
setDestinations(destinations) {
this.state.destinations = destinations;
return this;
}
hasDestinations() {
return this.state.hasDestinations;
}
setHasDestinations(hasDestinations) {
this.state.hasDestinations = hasDestinations;
return this;
}
/**
* Convenience method to query outputs by the locked state of their tx.
*
* @param isLocked specifies if the output's tx must be locked or unlocked (optional)
* @return {MoneroOutputQuery} this query for chaining
*/
setIsLocked(isLocked) {
if (this.state.txQuery === undefined) this.state.txQuery = new MoneroTxQuery();
this.state.txQuery.setIsLocked(isLocked);
return this;
}
meetsCriteria(transfer, queryParent) {
if (!(transfer instanceof MoneroTransfer)) throw new Error("Transfer not given to MoneroTransferQuery.meetsCriteria(transfer)");
if (queryParent === undefined) queryParent = true;
// filter on common fields
if (this.isIncoming() !== undefined && this.isIncoming() !== transfer.isIncoming()) return false;
if (this.isOutgoing() !== undefined && this.isOutgoing() !== transfer.isOutgoing()) return false;
if (this.getAmount() !== undefined && this.getAmount().compare(transfer.getAmount()) !== 0) return false;
if (this.getAccountIndex() !== undefined && this.getAccountIndex() !== transfer.getAccountIndex()) return false;
// filter on incoming fields
if (transfer instanceof MoneroIncomingTransfer) {
if (this.hasDestinations()) return false;
if (this.getAddress() !== undefined && this.getAddress() !== transfer.getAddress()) return false;
if (this.getAddresses() !== undefined && !this.getAddresses().includes(transfer.getAddress())) return false;
if (this.getSubaddressIndex() !== undefined && this.getSubaddressIndex() !== transfer.getSubaddressIndex()) return false;
if (this.getSubaddressIndices() !== undefined && !this.getSubaddressIndices().includes(transfer.getSubaddressIndex())) return false;
}
// filter on outgoing fields
else if (transfer instanceof MoneroOutgoingTransfer) {
// filter on addresses which must have overlap
if (this.getAddress() !== undefined && (transfer.getAddresses() === undefined || !transfer.getAddresses().includes(this.getAddress()))) return false; // TODO: will filter all transfers that don't contain addresses (outgoing txs might not have this field initialized)
if (this.getAddresses() !== undefined) {
if (!transfer.getAddresses()) return false;
if (!this.getAddresses().some(address => transfer.getAddresses().includes(address))) return false;
}
// filter on subaddress indices
if (this.getSubaddressIndex() !== undefined && (transfer.getSubaddressIndices() === undefined || !transfer.getSubaddressIndices().includes(this.getSubaddressIndex()))) return false;
if (this.getSubaddressIndices() !== undefined) {
if (!transfer.getSubaddressIndices()) return false;
if (!this.getSubaddressIndices().some(subaddressIdx => transfer.getSubaddressIndices().includes(subaddressIdx))) return false;
}
// filter on having destinations
if (this.hasDestinations() !== undefined) {
if (this.hasDestinations() && transfer.getDestinations() === undefined) return false;
if (!this.hasDestinations() && transfer.getDestinations() !== undefined) return false;
}
// filter on destinations TODO: start with test for this
// if (this.getDestionations() !== undefined && this.getDestionations() !== transfer.getDestionations()) return false;
}
// otherwise invalid type
else throw new Error("Transfer must be MoneroIncomingTransfer or MoneroOutgoingTransfer");
// filter with tx filter
if (queryParent && this.getTxQuery() !== undefined && !this.getTxQuery().meetsCriteria(transfer.getTx())) return false;
return true;
}
_validate() {
if (this.getSubaddressIndex() !== undefined && this.getSubaddressIndex() < 0) throw new MoneroError("Subaddress index must be >= 0");
if (this.getSubaddressIndices() !== undefined) for (let subaddressIdx of this.getSubaddressIndices()) if (subaddressIdx < 0) throw new MoneroError("Subaddress indices must be >= 0");
}
}
module.exports = MoneroTransferQuery;