edge-currency-monero 1.4.2 → 1.5.1
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/CHANGELOG.md +9 -0
- package/lib/MoneroEngine.js +75 -32
- package/lib/MyMoneroApi.js +9 -13
- package/lib/react-native/edge-currency-monero.ts +372 -280
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 1.5.1 (2025-07-03)
|
|
6
|
+
|
|
7
|
+
- fixed: Fix `getFreshAddress` to return a non-empty `publicAddress` when calling immediately after creating a wallet.
|
|
8
|
+
- fixed: Handle insufficient funds errors from native library into `InsufficientFundsError`.
|
|
9
|
+
|
|
10
|
+
## 1.5.0 (2025-04-21)
|
|
11
|
+
|
|
12
|
+
- added: Support for multiple spend targets.
|
|
13
|
+
|
|
5
14
|
## 1.4.2 (2025-04-01)
|
|
6
15
|
|
|
7
16
|
- fixed: Small internal implementation cleanup on how transactions are saved.
|
package/lib/MoneroEngine.js
CHANGED
|
@@ -85,13 +85,14 @@ const PRIMARY_CURRENCY_TOKEN_ID = null
|
|
|
85
85
|
|
|
86
86
|
|
|
87
87
|
|
|
88
|
+
__init() {this.loginPromise = null}
|
|
88
89
|
|
|
89
90
|
constructor(
|
|
90
91
|
env,
|
|
91
92
|
tools,
|
|
92
93
|
walletInfo,
|
|
93
94
|
opts
|
|
94
|
-
) {
|
|
95
|
+
) {;MoneroEngine.prototype.__init.call(this);
|
|
95
96
|
const { callbacks, userSettings = {}, walletLocalDisklet } = opts
|
|
96
97
|
const initOptions = _moneroTypes.asMoneroInitOptions.call(void 0, _nullishCoalesce(env.initOptions, () => ( {})))
|
|
97
98
|
const { networkInfo } = tools
|
|
@@ -174,6 +175,20 @@ const PRIMARY_CURRENCY_TOKEN_ID = null
|
|
|
174
175
|
// Login to mymonero.com server
|
|
175
176
|
// **********************************************
|
|
176
177
|
async loginIfNewAddress(privateKeys) {
|
|
178
|
+
if (this.loginPromise != null) {
|
|
179
|
+
return await this.loginPromise
|
|
180
|
+
}
|
|
181
|
+
this.loginPromise = this.performLogin(privateKeys)
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
await this.loginPromise
|
|
185
|
+
} finally {
|
|
186
|
+
// Clear the promise once completed (success OR failure)
|
|
187
|
+
this.loginPromise = null
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async performLogin(privateKeys) {
|
|
177
192
|
try {
|
|
178
193
|
const result = await this.myMoneroApi.login({
|
|
179
194
|
address: this.walletInfo.keys.moneroAddress,
|
|
@@ -578,8 +593,18 @@ const PRIMARY_CURRENCY_TOKEN_ID = null
|
|
|
578
593
|
async getFreshAddress(
|
|
579
594
|
options
|
|
580
595
|
) {
|
|
596
|
+
// Wait for login to complete if it's in progress
|
|
597
|
+
if (this.loginPromise != null) {
|
|
598
|
+
await this.loginPromise
|
|
599
|
+
}
|
|
600
|
+
|
|
581
601
|
// Do not show the address before logging into my monero...
|
|
582
|
-
if (!this.walletLocalData.hasLoggedIn)
|
|
602
|
+
if (!this.walletLocalData.hasLoggedIn) {
|
|
603
|
+
this.log.warn(
|
|
604
|
+
'Attempted to retrieve `publicAddress` before successfully logging in'
|
|
605
|
+
)
|
|
606
|
+
return { publicAddress: '' }
|
|
607
|
+
}
|
|
583
608
|
return { publicAddress: this.walletInfo.keys.moneroAddress }
|
|
584
609
|
}
|
|
585
610
|
|
|
@@ -601,10 +626,14 @@ const PRIMARY_CURRENCY_TOKEN_ID = null
|
|
|
601
626
|
}
|
|
602
627
|
|
|
603
628
|
const options = {
|
|
604
|
-
amount: '0',
|
|
605
629
|
isSweepTx: true,
|
|
606
630
|
priority: translateFee(edgeSpendInfo.networkFeeOption),
|
|
607
|
-
|
|
631
|
+
targets: [
|
|
632
|
+
{
|
|
633
|
+
amount: '0',
|
|
634
|
+
targetAddress: publicAddress
|
|
635
|
+
}
|
|
636
|
+
]
|
|
608
637
|
}
|
|
609
638
|
|
|
610
639
|
const result = await this.createMyMoneroTransaction(options, privateKeys)
|
|
@@ -625,12 +654,20 @@ const PRIMARY_CURRENCY_TOKEN_ID = null
|
|
|
625
654
|
},
|
|
626
655
|
options
|
|
627
656
|
)
|
|
628
|
-
} catch (
|
|
657
|
+
} catch (error) {
|
|
658
|
+
if (!(error instanceof Error)) {
|
|
659
|
+
throw new Error(String(error))
|
|
660
|
+
}
|
|
661
|
+
if (error.message === 'Not enough spendables') {
|
|
662
|
+
throw new (0, _types.InsufficientFundsError)({
|
|
663
|
+
tokenId: PRIMARY_CURRENCY_TOKEN_ID
|
|
664
|
+
})
|
|
665
|
+
}
|
|
629
666
|
// This error is specific to mymonero-core-js: github.com/mymonero/mymonero-core-cpp/blob/a53e57f2a376b05bb0f4d851713321c749e5d8d9/src/monero_transfer_utils.hpp#L112-L162
|
|
630
|
-
this.log.error(
|
|
667
|
+
this.log.error(error.message)
|
|
631
668
|
const regex = / Have (\d*\.?\d+) XMR; need (\d*\.?\d+) XMR./gm
|
|
632
669
|
const subst = `\nHave: $1 XMR.\nNeed: $2 XMR.`
|
|
633
|
-
const msgFormatted =
|
|
670
|
+
const msgFormatted = error.message.replace(regex, subst)
|
|
634
671
|
throw new Error(msgFormatted)
|
|
635
672
|
}
|
|
636
673
|
}
|
|
@@ -642,39 +679,45 @@ const PRIMARY_CURRENCY_TOKEN_ID = null
|
|
|
642
679
|
const { memos = [] } = edgeSpendInfo
|
|
643
680
|
const privateKeys = _moneroTypes.asPrivateKeys.call(void 0, _optionalChain([opts, 'optionalAccess', _7 => _7.privateKeys]))
|
|
644
681
|
|
|
645
|
-
|
|
646
|
-
// TODO: The new SDK fixes this!
|
|
647
|
-
if (edgeSpendInfo.spendTargets.length !== 1) {
|
|
648
|
-
throw new Error('Error: only one output allowed')
|
|
649
|
-
}
|
|
682
|
+
const { spendTargets } = edgeSpendInfo
|
|
650
683
|
|
|
651
|
-
|
|
652
|
-
const
|
|
653
|
-
if (publicAddress == null) {
|
|
654
|
-
throw new TypeError('Missing destination address')
|
|
655
|
-
}
|
|
656
|
-
if (nativeAmount == null || _biggystring.eq.call(void 0, nativeAmount, '0')) {
|
|
657
|
-
throw new (0, _types.NoAmountSpecifiedError)()
|
|
658
|
-
}
|
|
684
|
+
let totalAmount = '0'
|
|
685
|
+
const targets = []
|
|
659
686
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
687
|
+
for (const spendTarget of spendTargets) {
|
|
688
|
+
const { publicAddress, nativeAmount } = spendTarget
|
|
689
|
+
if (publicAddress == null) {
|
|
690
|
+
throw new TypeError('Missing destination address')
|
|
691
|
+
}
|
|
692
|
+
if (nativeAmount == null || _biggystring.eq.call(void 0, nativeAmount, '0')) {
|
|
693
|
+
throw new (0, _types.NoAmountSpecifiedError)()
|
|
694
|
+
}
|
|
695
|
+
totalAmount = _biggystring.add.call(void 0, totalAmount, nativeAmount)
|
|
696
|
+
if (
|
|
697
|
+
_biggystring.gte.call(void 0,
|
|
698
|
+
totalAmount,
|
|
699
|
+
_nullishCoalesce(this.walletLocalData.totalBalances.get(PRIMARY_CURRENCY_TOKEN_ID), () => (
|
|
700
|
+
'0'))
|
|
701
|
+
)
|
|
702
|
+
) {
|
|
703
|
+
if (_biggystring.gte.call(void 0, this.walletLocalData.lockedXmrBalance, totalAmount)) {
|
|
704
|
+
throw new (0, _types.PendingFundsError)()
|
|
705
|
+
} else {
|
|
706
|
+
throw new (0, _types.InsufficientFundsError)({
|
|
707
|
+
tokenId: PRIMARY_CURRENCY_TOKEN_ID
|
|
708
|
+
})
|
|
709
|
+
}
|
|
670
710
|
}
|
|
711
|
+
targets.push({
|
|
712
|
+
amount: _biggystring.div.call(void 0, nativeAmount, '1000000000000', 12),
|
|
713
|
+
targetAddress: publicAddress
|
|
714
|
+
})
|
|
671
715
|
}
|
|
672
716
|
|
|
673
717
|
const options = {
|
|
674
|
-
amount: _biggystring.div.call(void 0, nativeAmount, '1000000000000', 12),
|
|
675
718
|
isSweepTx: false,
|
|
676
719
|
priority: translateFee(edgeSpendInfo.networkFeeOption),
|
|
677
|
-
|
|
720
|
+
targets
|
|
678
721
|
}
|
|
679
722
|
this.log(`Creating transaction: ${JSON.stringify(options, null, 1)}`)
|
|
680
723
|
|
package/lib/MyMoneroApi.js
CHANGED
|
@@ -48,6 +48,8 @@ var _ResponseParser = require('./mymonero-utils/ResponseParser'); var _ResponseP
|
|
|
48
48
|
|
|
49
49
|
|
|
50
50
|
|
|
51
|
+
|
|
52
|
+
|
|
51
53
|
|
|
52
54
|
|
|
53
55
|
|
|
@@ -238,13 +240,7 @@ const asGetAddressTxsResponse = _cleaners.asObject.call(void 0, {
|
|
|
238
240
|
opts
|
|
239
241
|
) {
|
|
240
242
|
const { address, privateSpendKey, privateViewKey, publicSpendKey } = keys
|
|
241
|
-
const {
|
|
242
|
-
amount,
|
|
243
|
-
isSweepTx = false,
|
|
244
|
-
paymentId,
|
|
245
|
-
priority = 1,
|
|
246
|
-
targetAddress
|
|
247
|
-
} = opts
|
|
243
|
+
const { isSweepTx = false, paymentId, priority = 1, targets } = opts
|
|
248
244
|
|
|
249
245
|
// Grab the UTXO set:
|
|
250
246
|
const unspentOuts = await this.fetchPostMyMonero('get_unspent_outs', {
|
|
@@ -268,14 +264,14 @@ const asGetAddressTxsResponse = _cleaners.asObject.call(void 0, {
|
|
|
268
264
|
})
|
|
269
265
|
}
|
|
270
266
|
|
|
267
|
+
const destinations = targets.map(t => ({
|
|
268
|
+
send_amount: t.amount,
|
|
269
|
+
to_address: t.targetAddress
|
|
270
|
+
}))
|
|
271
|
+
|
|
271
272
|
// Make the transaction:
|
|
272
273
|
return await this.cppBridge.createTransaction({
|
|
273
|
-
destinations
|
|
274
|
-
{
|
|
275
|
-
send_amount: amount,
|
|
276
|
-
to_address: targetAddress
|
|
277
|
-
}
|
|
278
|
-
],
|
|
274
|
+
destinations,
|
|
279
275
|
priority,
|
|
280
276
|
address,
|
|
281
277
|
paymentId,
|