@bsv/message-box-client 1.4.1 → 1.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/src/MessageBoxClient.js +371 -29
- package/dist/cjs/src/MessageBoxClient.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/MessageBoxClient.js +369 -29
- package/dist/esm/src/MessageBoxClient.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/MessageBoxClient.d.ts +11 -3
- package/dist/types/src/MessageBoxClient.d.ts.map +1 -1
- package/dist/types/src/types/permissions.d.ts +46 -1
- package/dist/types/src/types/permissions.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/dist/umd/bundle.js +1 -1
- package/package.json +2 -2
- package/src/MessageBoxClient.ts +432 -20
- package/src/types/permissions.ts +41 -1
package/dist/cjs/package.json
CHANGED
|
@@ -903,6 +903,143 @@ class MessageBoxClient {
|
|
|
903
903
|
throw new Error(`Failed to send message: ${errorMessage}`);
|
|
904
904
|
}
|
|
905
905
|
}
|
|
906
|
+
/**
|
|
907
|
+
* Multi-recipient sender. Uses the multi-quote route to:
|
|
908
|
+
* - identify blocked recipients
|
|
909
|
+
* - compute per-recipient payment
|
|
910
|
+
* Then sends to the allowed recipients with payment attached.
|
|
911
|
+
*/
|
|
912
|
+
async sendMesagetoRecepients(params, overrideHost) {
|
|
913
|
+
var _a, _b, _c;
|
|
914
|
+
await this.assertInitialized();
|
|
915
|
+
const { recipients, messageBox, body, skipEncryption } = params;
|
|
916
|
+
if (!Array.isArray(recipients) || recipients.length === 0) {
|
|
917
|
+
throw new Error('You must provide at least one recipient!');
|
|
918
|
+
}
|
|
919
|
+
if (!messageBox || messageBox.trim() === '') {
|
|
920
|
+
throw new Error('You must provide a messageBox to send this message into!');
|
|
921
|
+
}
|
|
922
|
+
if (body == null || (typeof body === 'string' && body.trim().length === 0)) {
|
|
923
|
+
throw new Error('Every message must have a body!');
|
|
924
|
+
}
|
|
925
|
+
// 1) Multi-quote for all recipients
|
|
926
|
+
const quoteResponse = await this.getMessageBoxQuote({
|
|
927
|
+
recipient: recipients,
|
|
928
|
+
messageBox
|
|
929
|
+
}, overrideHost);
|
|
930
|
+
const quotesByRecipient = Array.isArray(quoteResponse === null || quoteResponse === void 0 ? void 0 : quoteResponse.quotesByRecipient)
|
|
931
|
+
? quoteResponse.quotesByRecipient : [];
|
|
932
|
+
const blocked = ((_a = quoteResponse === null || quoteResponse === void 0 ? void 0 : quoteResponse.blockedRecipients) !== null && _a !== void 0 ? _a : []);
|
|
933
|
+
const totals = quoteResponse === null || quoteResponse === void 0 ? void 0 : quoteResponse.totals;
|
|
934
|
+
// 2) Filter allowed recipients
|
|
935
|
+
const allowedRecipients = recipients.filter(r => !blocked.includes(r));
|
|
936
|
+
if (allowedRecipients.length === 0) {
|
|
937
|
+
return {
|
|
938
|
+
status: 'error',
|
|
939
|
+
description: `All ${recipients.length} recipients are blocked.`,
|
|
940
|
+
sent: [],
|
|
941
|
+
blocked,
|
|
942
|
+
failed: recipients.map(r => ({ recipient: r, error: 'blocked' })),
|
|
943
|
+
totals
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
// 3) Map recipient -> fees
|
|
947
|
+
const perRecipientQuotes = new Map();
|
|
948
|
+
for (const q of quotesByRecipient) {
|
|
949
|
+
perRecipientQuotes.set(q.recipient, { recipientFee: q.recipientFee, deliveryFee: q.deliveryFee });
|
|
950
|
+
}
|
|
951
|
+
// 4) One delivery agent only (batch goes to one server)
|
|
952
|
+
const { deliveryAgentIdentityKeyByHost } = quoteResponse;
|
|
953
|
+
if (!deliveryAgentIdentityKeyByHost || Object.keys(deliveryAgentIdentityKeyByHost).length === 0) {
|
|
954
|
+
throw new Error('Missing delivery agent identity keys in quote response.');
|
|
955
|
+
}
|
|
956
|
+
if (Object.keys(deliveryAgentIdentityKeyByHost).length > 1 && !overrideHost) {
|
|
957
|
+
// To keep the single-POST invariant, we require all recipients to share a host
|
|
958
|
+
throw new Error('Recipients resolve to multiple hosts. Use overrideHost to force a single server or split by host.');
|
|
959
|
+
}
|
|
960
|
+
// pick the host to POST to
|
|
961
|
+
const finalHost = (overrideHost !== null && overrideHost !== void 0 ? overrideHost : await this.resolveHostForRecipient(allowedRecipients[0])).replace(/\/+$/, '');
|
|
962
|
+
const singleDeliveryKey = (_b = deliveryAgentIdentityKeyByHost[finalHost]) !== null && _b !== void 0 ? _b : Object.values(deliveryAgentIdentityKeyByHost)[0];
|
|
963
|
+
if (!singleDeliveryKey) {
|
|
964
|
+
throw new Error('Could not determine server delivery agent identity key.');
|
|
965
|
+
}
|
|
966
|
+
// 5) Identity key (sender)
|
|
967
|
+
if (!this.myIdentityKey) {
|
|
968
|
+
const keyResult = await this.walletClient.getPublicKey({ identityKey: true }, this.originator);
|
|
969
|
+
this.myIdentityKey = keyResult.publicKey;
|
|
970
|
+
}
|
|
971
|
+
// 6) Build per-recipient messageIds (HMAC), same order as allowedRecipients
|
|
972
|
+
const messageIds = [];
|
|
973
|
+
for (const r of allowedRecipients) {
|
|
974
|
+
const hmac = await this.walletClient.createHmac({
|
|
975
|
+
data: Array.from(new TextEncoder().encode(JSON.stringify(body))),
|
|
976
|
+
protocolID: [1, 'messagebox'],
|
|
977
|
+
keyID: '1',
|
|
978
|
+
counterparty: r
|
|
979
|
+
}, this.originator);
|
|
980
|
+
const mid = Array.from(hmac.hmac).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
981
|
+
messageIds.push(mid);
|
|
982
|
+
}
|
|
983
|
+
// 7) Body: for batch route the server expects a single shared body
|
|
984
|
+
// NOTE: If you need per-recipient encryption, we must change the server payload shape.
|
|
985
|
+
let finalBody;
|
|
986
|
+
if (skipEncryption === true) {
|
|
987
|
+
finalBody = typeof body === 'string' ? body : JSON.stringify(body);
|
|
988
|
+
}
|
|
989
|
+
else {
|
|
990
|
+
// safest for now: send plaintext; the recipients can decrypt payload fields client-side if needed
|
|
991
|
+
finalBody = typeof body === 'string' ? body : JSON.stringify(body);
|
|
992
|
+
}
|
|
993
|
+
// 8) ONE batch payment with server output at index 0
|
|
994
|
+
const paymentData = await this.createMessagePaymentBatch(allowedRecipients, perRecipientQuotes, singleDeliveryKey);
|
|
995
|
+
// 9) Single POST to /sendMessage with recipients[] + messageId[]
|
|
996
|
+
const requestBody = {
|
|
997
|
+
message: {
|
|
998
|
+
recipients: allowedRecipients,
|
|
999
|
+
messageBox,
|
|
1000
|
+
messageId: messageIds, // aligned by index with recipients
|
|
1001
|
+
body: finalBody
|
|
1002
|
+
},
|
|
1003
|
+
payment: paymentData
|
|
1004
|
+
};
|
|
1005
|
+
Logger.log('[MB CLIENT] Sending HTTP request to:', `${finalHost}/sendMessage`);
|
|
1006
|
+
Logger.log('[MB CLIENT] Request Body (batch):', JSON.stringify({ ...requestBody, payment: { ...paymentData, tx: '<omitted>' } }, null, 2));
|
|
1007
|
+
try {
|
|
1008
|
+
const response = await this.authFetch.fetch(`${finalHost}/sendMessage`, {
|
|
1009
|
+
method: 'POST',
|
|
1010
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1011
|
+
body: JSON.stringify(requestBody)
|
|
1012
|
+
});
|
|
1013
|
+
const parsed = await response.json().catch(() => ({}));
|
|
1014
|
+
if (!response.ok || parsed.status !== 'success') {
|
|
1015
|
+
const msg = !response.ok ? `HTTP ${response.status} - ${response.statusText}` : ((_c = parsed.description) !== null && _c !== void 0 ? _c : 'Unknown server error');
|
|
1016
|
+
throw new Error(msg);
|
|
1017
|
+
}
|
|
1018
|
+
// server returns { results: [{ recipient, messageId }] }
|
|
1019
|
+
const sent = Array.isArray(parsed.results) ? parsed.results : [];
|
|
1020
|
+
const failed = []; // handled server-side now
|
|
1021
|
+
const status = sent.length === allowedRecipients.length ? 'success'
|
|
1022
|
+
: sent.length > 0 ? 'partial'
|
|
1023
|
+
: 'error';
|
|
1024
|
+
const description = status === 'success'
|
|
1025
|
+
? `Sent to ${sent.length} recipients.`
|
|
1026
|
+
: status === 'partial'
|
|
1027
|
+
? `Sent to ${sent.length} recipients; ${allowedRecipients.length - sent.length} failed; ${blocked.length} blocked.`
|
|
1028
|
+
: `Failed to send to ${allowedRecipients.length} allowed recipients. ${blocked.length} blocked.`;
|
|
1029
|
+
return { status, description, sent, blocked, failed, totals };
|
|
1030
|
+
}
|
|
1031
|
+
catch (err) {
|
|
1032
|
+
const msg = err instanceof Error ? err.message : 'Unknown error';
|
|
1033
|
+
return {
|
|
1034
|
+
status: 'error',
|
|
1035
|
+
description: `Batch send failed: ${msg}`,
|
|
1036
|
+
sent: [],
|
|
1037
|
+
blocked,
|
|
1038
|
+
failed: allowedRecipients.map(r => ({ recipient: r, error: msg })),
|
|
1039
|
+
totals
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
906
1043
|
/**
|
|
907
1044
|
* @method anointHost
|
|
908
1045
|
* @async
|
|
@@ -1561,31 +1698,145 @@ class MessageBoxClient {
|
|
|
1561
1698
|
*/
|
|
1562
1699
|
async getMessageBoxQuote(params, overrideHost) {
|
|
1563
1700
|
var _a;
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1701
|
+
// ---------- SINGLE RECIPIENT (back-compat) ----------
|
|
1702
|
+
if (!Array.isArray(params.recipient)) {
|
|
1703
|
+
const finalHost = overrideHost !== null && overrideHost !== void 0 ? overrideHost : await this.resolveHostForRecipient(params.recipient);
|
|
1704
|
+
const queryParams = new URLSearchParams({
|
|
1705
|
+
recipient: params.recipient,
|
|
1706
|
+
messageBox: params.messageBox
|
|
1707
|
+
});
|
|
1708
|
+
Logger.log('[MB CLIENT] Getting messageBox quote (single)...');
|
|
1709
|
+
console.log("HELP IM QUOTING", `${finalHost}/permissions/quote?${queryParams.toString()}`);
|
|
1710
|
+
const response = await this.authFetch.fetch(`${finalHost}/permissions/quote?${queryParams.toString()}`, { method: 'GET' });
|
|
1711
|
+
console.log("server response from getquote]", response);
|
|
1712
|
+
if (!response.ok) {
|
|
1713
|
+
const errorData = await response.json().catch(() => ({}));
|
|
1714
|
+
throw new Error(`Failed to get quote: HTTP ${response.status} - ${(_a = String(errorData.description)) !== null && _a !== void 0 ? _a : response.statusText}`);
|
|
1715
|
+
}
|
|
1716
|
+
const { status, description, quote } = await response.json();
|
|
1717
|
+
if (status === 'error') {
|
|
1718
|
+
throw new Error(description !== null && description !== void 0 ? description : 'Failed to get quote');
|
|
1719
|
+
}
|
|
1720
|
+
const deliveryAgentIdentityKey = response.headers.get('x-bsv-auth-identity-key');
|
|
1721
|
+
console.log("deliveryAgentIdentityKey", deliveryAgentIdentityKey);
|
|
1722
|
+
if (deliveryAgentIdentityKey == null) {
|
|
1723
|
+
throw new Error('Failed to get quote: Delivery agent did not provide their identity key');
|
|
1724
|
+
}
|
|
1725
|
+
return {
|
|
1726
|
+
recipientFee: quote.recipientFee,
|
|
1727
|
+
deliveryFee: quote.deliveryFee,
|
|
1728
|
+
deliveryAgentIdentityKey
|
|
1729
|
+
};
|
|
1584
1730
|
}
|
|
1731
|
+
// ---------- MULTI RECIPIENTS ----------
|
|
1732
|
+
const recipients = params.recipient;
|
|
1733
|
+
if (recipients.length === 0) {
|
|
1734
|
+
throw new Error('At least one recipient is required.');
|
|
1735
|
+
}
|
|
1736
|
+
Logger.log('[MB CLIENT] Getting messageBox quotes (multi)...');
|
|
1737
|
+
console.log("[MB CLIENT] Getting messageBox quotes (multi)...");
|
|
1738
|
+
// Resolve host per recipient (unless caller forces overrideHost)
|
|
1739
|
+
// Group recipients by host so we call each overlay once.
|
|
1740
|
+
const hostGroups = new Map();
|
|
1741
|
+
for (const r of recipients) {
|
|
1742
|
+
const host = overrideHost !== null && overrideHost !== void 0 ? overrideHost : await this.resolveHostForRecipient(r);
|
|
1743
|
+
const list = hostGroups.get(host);
|
|
1744
|
+
if (list)
|
|
1745
|
+
list.push(r);
|
|
1746
|
+
else
|
|
1747
|
+
hostGroups.set(host, [r]);
|
|
1748
|
+
}
|
|
1749
|
+
const deliveryAgentIdentityKeyByHost = {};
|
|
1750
|
+
const quotesByRecipient = [];
|
|
1751
|
+
const blockedRecipients = [];
|
|
1752
|
+
let totalDeliveryFees = 0;
|
|
1753
|
+
let totalRecipientFees = 0;
|
|
1754
|
+
// Helper to fetch one host group
|
|
1755
|
+
const fetchGroup = async (host, groupRecipients) => {
|
|
1756
|
+
var _a;
|
|
1757
|
+
const qp = new URLSearchParams();
|
|
1758
|
+
for (const r of groupRecipients)
|
|
1759
|
+
qp.append('recipient', r);
|
|
1760
|
+
qp.set('messageBox', params.messageBox);
|
|
1761
|
+
const url = `${host}/permissions/quote?${qp.toString()}`;
|
|
1762
|
+
Logger.log('[MB CLIENT] Multi-quote GET:', url);
|
|
1763
|
+
const resp = await this.authFetch.fetch(url, { method: 'GET' });
|
|
1764
|
+
if (!resp.ok) {
|
|
1765
|
+
const errorData = await resp.json().catch(() => ({}));
|
|
1766
|
+
throw new Error(`Failed to get quote (host ${host}): HTTP ${resp.status} - ${(_a = String(errorData.description)) !== null && _a !== void 0 ? _a : resp.statusText}`);
|
|
1767
|
+
}
|
|
1768
|
+
const deliveryAgentKey = resp.headers.get('x-bsv-auth-identity-key');
|
|
1769
|
+
if (!deliveryAgentKey) {
|
|
1770
|
+
throw new Error(`Failed to get quote (host ${host}): missing delivery agent identity key`);
|
|
1771
|
+
}
|
|
1772
|
+
deliveryAgentIdentityKeyByHost[host] = deliveryAgentKey;
|
|
1773
|
+
const payload = await resp.json();
|
|
1774
|
+
// Server supports both shapes. For multi we expect:
|
|
1775
|
+
// { quotesByRecipient, totals, blockedRecipients }
|
|
1776
|
+
if (Array.isArray(payload === null || payload === void 0 ? void 0 : payload.quotesByRecipient)) {
|
|
1777
|
+
// merge quotes
|
|
1778
|
+
for (const q of payload.quotesByRecipient) {
|
|
1779
|
+
quotesByRecipient.push({
|
|
1780
|
+
recipient: q.recipient,
|
|
1781
|
+
messageBox: q.messageBox,
|
|
1782
|
+
deliveryFee: q.deliveryFee,
|
|
1783
|
+
recipientFee: q.recipientFee,
|
|
1784
|
+
status: q.status
|
|
1785
|
+
});
|
|
1786
|
+
// aggregate client-side totals as well (in case we hit multiple hosts)
|
|
1787
|
+
totalDeliveryFees += q.deliveryFee;
|
|
1788
|
+
if (q.recipientFee === -1) {
|
|
1789
|
+
if (!blockedRecipients.includes(q.recipient))
|
|
1790
|
+
blockedRecipients.push(q.recipient);
|
|
1791
|
+
}
|
|
1792
|
+
else {
|
|
1793
|
+
totalRecipientFees += q.recipientFee;
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
// Also merge server totals if present (they are per-host); we already aggregated above,
|
|
1797
|
+
// so we don’t need to use payload.totals except for sanity/logging.
|
|
1798
|
+
if (Array.isArray(payload === null || payload === void 0 ? void 0 : payload.blockedRecipients)) {
|
|
1799
|
+
for (const br of payload.blockedRecipients) {
|
|
1800
|
+
if (!blockedRecipients.includes(br))
|
|
1801
|
+
blockedRecipients.push(br);
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
else if (payload === null || payload === void 0 ? void 0 : payload.quote) {
|
|
1806
|
+
// Defensive: if an overlay still returns single-quote shape for multi (shouldn’t),
|
|
1807
|
+
// we map it to each recipient in the group uniformly.
|
|
1808
|
+
for (const r of groupRecipients) {
|
|
1809
|
+
const { deliveryFee, recipientFee } = payload.quote;
|
|
1810
|
+
const status = recipientFee === -1 ? 'blocked' : recipientFee === 0 ? 'always_allow' : 'payment_required';
|
|
1811
|
+
quotesByRecipient.push({
|
|
1812
|
+
recipient: r,
|
|
1813
|
+
messageBox: params.messageBox,
|
|
1814
|
+
deliveryFee,
|
|
1815
|
+
recipientFee,
|
|
1816
|
+
status
|
|
1817
|
+
});
|
|
1818
|
+
totalDeliveryFees += deliveryFee;
|
|
1819
|
+
if (recipientFee === -1)
|
|
1820
|
+
blockedRecipients.push(r);
|
|
1821
|
+
else
|
|
1822
|
+
totalRecipientFees += recipientFee;
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
else {
|
|
1826
|
+
throw new Error(`Unexpected quote response shape from host ${host}`);
|
|
1827
|
+
}
|
|
1828
|
+
};
|
|
1829
|
+
// Run all host groups (in parallel, but you can limit if needed)
|
|
1830
|
+
await Promise.all(Array.from(hostGroups.entries()).map(([host, group]) => fetchGroup(host, group)));
|
|
1585
1831
|
return {
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1832
|
+
quotesByRecipient,
|
|
1833
|
+
totals: {
|
|
1834
|
+
deliveryFees: totalDeliveryFees,
|
|
1835
|
+
recipientFees: totalRecipientFees,
|
|
1836
|
+
totalForPayableRecipients: totalDeliveryFees + totalRecipientFees
|
|
1837
|
+
},
|
|
1838
|
+
blockedRecipients,
|
|
1839
|
+
deliveryAgentIdentityKeyByHost
|
|
1589
1840
|
};
|
|
1590
1841
|
}
|
|
1591
1842
|
/**
|
|
@@ -1742,13 +1993,20 @@ class MessageBoxClient {
|
|
|
1742
1993
|
*/
|
|
1743
1994
|
async sendNotification(recipient, body, overrideHost) {
|
|
1744
1995
|
await this.assertInitialized();
|
|
1745
|
-
//
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1996
|
+
// Single recipient → keep original flow
|
|
1997
|
+
if (!Array.isArray(recipient)) {
|
|
1998
|
+
return await this.sendMessage({
|
|
1999
|
+
recipient,
|
|
2000
|
+
messageBox: 'notifications',
|
|
2001
|
+
body,
|
|
2002
|
+
checkPermissions: true
|
|
2003
|
+
}, overrideHost);
|
|
2004
|
+
}
|
|
2005
|
+
// Multiple recipients → new flow
|
|
2006
|
+
return await this.sendMesagetoRecepients({
|
|
2007
|
+
recipients: recipient,
|
|
1749
2008
|
messageBox: 'notifications',
|
|
1750
|
-
body
|
|
1751
|
-
checkPermissions: true
|
|
2009
|
+
body
|
|
1752
2010
|
}, overrideHost);
|
|
1753
2011
|
}
|
|
1754
2012
|
/**
|
|
@@ -1895,6 +2153,7 @@ class MessageBoxClient {
|
|
|
1895
2153
|
const derivationPrefix = sdk_1.Utils.toBase64((0, sdk_1.Random)(32));
|
|
1896
2154
|
const derivationSuffix = sdk_1.Utils.toBase64((0, sdk_1.Random)(32));
|
|
1897
2155
|
// Get host's derived public key
|
|
2156
|
+
console.log('delivery agent:', quote.deliveryAgentIdentityKey);
|
|
1898
2157
|
const { publicKey: derivedKeyResult } = await this.walletClient.getPublicKey({
|
|
1899
2158
|
protocolID: [2, '3241645161d8'],
|
|
1900
2159
|
keyID: `${derivationPrefix} ${derivationSuffix}`,
|
|
@@ -1975,6 +2234,89 @@ class MessageBoxClient {
|
|
|
1975
2234
|
// labels
|
|
1976
2235
|
};
|
|
1977
2236
|
}
|
|
2237
|
+
async createMessagePaymentBatch(recipients, perRecipientQuotes,
|
|
2238
|
+
// server (delivery agent) identity key to pay the delivery fee to
|
|
2239
|
+
serverIdentityKey, description = 'MessageBox delivery payment (batch)') {
|
|
2240
|
+
var _a;
|
|
2241
|
+
const outputs = [];
|
|
2242
|
+
const createActionOutputs = [];
|
|
2243
|
+
// figure out the per-request delivery fee (take it from any quoted recipient)
|
|
2244
|
+
const deliveryFeeOnce = (_a = recipients.reduce((acc, r) => {
|
|
2245
|
+
const q = perRecipientQuotes.get(r);
|
|
2246
|
+
return q ? (acc !== null && acc !== void 0 ? acc : q.deliveryFee) : acc;
|
|
2247
|
+
}, undefined)) !== null && _a !== void 0 ? _a : 0;
|
|
2248
|
+
const senderIdentityKey = await this.getIdentityKey();
|
|
2249
|
+
let outputIndex = 0;
|
|
2250
|
+
// index 0: server delivery fee (if any)
|
|
2251
|
+
if (deliveryFeeOnce > 0) {
|
|
2252
|
+
const derivationPrefix = sdk_1.Utils.toBase64((0, sdk_1.Random)(32));
|
|
2253
|
+
const derivationSuffix = sdk_1.Utils.toBase64((0, sdk_1.Random)(32));
|
|
2254
|
+
const { publicKey: agentDerived } = await this.walletClient.getPublicKey({
|
|
2255
|
+
protocolID: [2, '3241645161d8'],
|
|
2256
|
+
keyID: `${derivationPrefix} ${derivationSuffix}`,
|
|
2257
|
+
counterparty: serverIdentityKey
|
|
2258
|
+
}, this.originator);
|
|
2259
|
+
const lockingScript = new sdk_1.P2PKH().lock(sdk_1.PublicKey.fromString(agentDerived).toAddress()).toHex();
|
|
2260
|
+
createActionOutputs.push({
|
|
2261
|
+
satoshis: deliveryFeeOnce,
|
|
2262
|
+
lockingScript,
|
|
2263
|
+
outputDescription: 'MessageBox server delivery fee (batch)',
|
|
2264
|
+
customInstructions: JSON.stringify({
|
|
2265
|
+
derivationPrefix,
|
|
2266
|
+
derivationSuffix,
|
|
2267
|
+
recipientIdentityKey: serverIdentityKey
|
|
2268
|
+
})
|
|
2269
|
+
});
|
|
2270
|
+
outputs.push({
|
|
2271
|
+
outputIndex: outputIndex++,
|
|
2272
|
+
protocol: 'wallet payment',
|
|
2273
|
+
paymentRemittance: { derivationPrefix, derivationSuffix, senderIdentityKey }
|
|
2274
|
+
});
|
|
2275
|
+
}
|
|
2276
|
+
// recipient outputs start at index 1 (or 0 if no delivery fee)
|
|
2277
|
+
const anyoneWallet = new sdk_1.ProtoWallet('anyone');
|
|
2278
|
+
const anyoneIdKey = (await anyoneWallet.getPublicKey({ identityKey: true })).publicKey;
|
|
2279
|
+
for (const r of recipients) {
|
|
2280
|
+
const q = perRecipientQuotes.get(r);
|
|
2281
|
+
if (!q || q.recipientFee <= 0)
|
|
2282
|
+
continue;
|
|
2283
|
+
const derivationPrefix = sdk_1.Utils.toBase64((0, sdk_1.Random)(32));
|
|
2284
|
+
const derivationSuffix = sdk_1.Utils.toBase64((0, sdk_1.Random)(32));
|
|
2285
|
+
const { publicKey: recipientDerived } = await anyoneWallet.getPublicKey({
|
|
2286
|
+
protocolID: [2, '3241645161d8'],
|
|
2287
|
+
keyID: `${derivationPrefix} ${derivationSuffix}`,
|
|
2288
|
+
counterparty: r
|
|
2289
|
+
});
|
|
2290
|
+
const lockingScript = new sdk_1.P2PKH().lock(sdk_1.PublicKey.fromString(recipientDerived).toAddress()).toHex();
|
|
2291
|
+
createActionOutputs.push({
|
|
2292
|
+
satoshis: q.recipientFee,
|
|
2293
|
+
lockingScript,
|
|
2294
|
+
outputDescription: `Recipient message fee (${r.slice(0, 8)}…)`,
|
|
2295
|
+
customInstructions: JSON.stringify({
|
|
2296
|
+
derivationPrefix,
|
|
2297
|
+
derivationSuffix,
|
|
2298
|
+
recipientIdentityKey: r
|
|
2299
|
+
})
|
|
2300
|
+
});
|
|
2301
|
+
outputs.push({
|
|
2302
|
+
outputIndex: outputIndex++,
|
|
2303
|
+
protocol: 'wallet payment',
|
|
2304
|
+
paymentRemittance: {
|
|
2305
|
+
derivationPrefix,
|
|
2306
|
+
derivationSuffix,
|
|
2307
|
+
senderIdentityKey: anyoneIdKey
|
|
2308
|
+
}
|
|
2309
|
+
});
|
|
2310
|
+
}
|
|
2311
|
+
const { tx } = await this.walletClient.createAction({
|
|
2312
|
+
description,
|
|
2313
|
+
outputs: createActionOutputs,
|
|
2314
|
+
options: { randomizeOutputs: false, acceptDelayedBroadcast: false }
|
|
2315
|
+
}, this.originator);
|
|
2316
|
+
if (!tx)
|
|
2317
|
+
throw new Error('Failed to create payment transaction');
|
|
2318
|
+
return { tx, outputs, description };
|
|
2319
|
+
}
|
|
1978
2320
|
}
|
|
1979
2321
|
exports.MessageBoxClient = MessageBoxClient;
|
|
1980
2322
|
//# sourceMappingURL=MessageBoxClient.js.map
|