@bitpay-labs/bitcore-wallet-service 11.11.0 → 11.11.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/Dockerfile +1 -1
- package/package.json +5 -5
- package/src/config.ts +0 -20
- package/src/externalservices/banxa.ts +51 -10
- package/src/externalservices/changelly.ts +54 -6
- package/src/externalservices/moonpay.ts +85 -19
- package/src/externalservices/oneInch.ts +13 -5
- package/src/externalservices/ramp.ts +42 -8
- package/src/externalservices/sardine.ts +51 -10
- package/src/externalservices/simplex.ts +58 -11
- package/src/externalservices/thorswap.ts +46 -11
- package/src/externalservices/transak.ts +53 -11
- package/src/lib/chain/btc/index.ts +3 -2
- package/src/lib/chain/eth/index.ts +134 -117
- package/src/lib/chain/sol/index.ts +1 -1
- package/src/lib/chain/xrp/index.ts +1 -1
- package/src/lib/routes/services.ts +0 -12
- package/src/lib/routes/wallets.ts +3 -1
- package/src/lib/server.ts +136 -91
- package/src/lib/storage.ts +90 -20
- package/src/types/externalservices.d.ts +0 -1
- package/test/chain/eth.test.ts +70 -0
- package/test/chain/sol.test.ts +1 -1
- package/test/chain/xrp.test.ts +1 -1
- package/test/expressapp.test.ts +144 -0
- package/test/integration/addressReads.test.ts +503 -0
- package/test/integration/bcmonitor.test.ts +44 -0
- package/test/integration/externalservices/banxa.test.ts +95 -5
- package/test/integration/externalservices/changelly.test.ts +118 -6
- package/test/integration/externalservices/coinGecko.test.ts +4 -0
- package/test/integration/externalservices/moonpay.test.ts +178 -9
- package/test/integration/externalservices/oneInch.test.ts +28 -1
- package/test/integration/externalservices/ramp.test.ts +126 -4
- package/test/integration/externalservices/sardine.test.ts +95 -5
- package/test/integration/externalservices/simplex.test.ts +95 -5
- package/test/integration/externalservices/thorswap.test.ts +81 -5
- package/test/integration/externalservices/transak.test.ts +98 -6
- package/test/integration/helpers.ts +1 -1
- package/test/integration/server.test.ts +254 -136
- package/test/integration/walletReads.test.ts +488 -0
- package/ts_build/package.json +3 -3
- package/ts_build/src/config.d.ts.map +1 -1
- package/ts_build/src/config.js +0 -4
- package/ts_build/src/config.js.map +1 -1
- package/ts_build/src/externalservices/banxa.d.ts.map +1 -1
- package/ts_build/src/externalservices/banxa.js +51 -10
- package/ts_build/src/externalservices/banxa.js.map +1 -1
- package/ts_build/src/externalservices/changelly.d.ts.map +1 -1
- package/ts_build/src/externalservices/changelly.js +54 -6
- package/ts_build/src/externalservices/changelly.js.map +1 -1
- package/ts_build/src/externalservices/moonpay.d.ts.map +1 -1
- package/ts_build/src/externalservices/moonpay.js +85 -19
- package/ts_build/src/externalservices/moonpay.js.map +1 -1
- package/ts_build/src/externalservices/oneInch.d.ts.map +1 -1
- package/ts_build/src/externalservices/oneInch.js +13 -5
- package/ts_build/src/externalservices/oneInch.js.map +1 -1
- package/ts_build/src/externalservices/ramp.d.ts.map +1 -1
- package/ts_build/src/externalservices/ramp.js +42 -8
- package/ts_build/src/externalservices/ramp.js.map +1 -1
- package/ts_build/src/externalservices/sardine.d.ts.map +1 -1
- package/ts_build/src/externalservices/sardine.js +51 -10
- package/ts_build/src/externalservices/sardine.js.map +1 -1
- package/ts_build/src/externalservices/simplex.d.ts.map +1 -1
- package/ts_build/src/externalservices/simplex.js +55 -11
- package/ts_build/src/externalservices/simplex.js.map +1 -1
- package/ts_build/src/externalservices/thorswap.d.ts.map +1 -1
- package/ts_build/src/externalservices/thorswap.js +45 -11
- package/ts_build/src/externalservices/thorswap.js.map +1 -1
- package/ts_build/src/externalservices/transak.d.ts.map +1 -1
- package/ts_build/src/externalservices/transak.js +53 -11
- package/ts_build/src/externalservices/transak.js.map +1 -1
- package/ts_build/src/lib/chain/btc/index.d.ts.map +1 -1
- package/ts_build/src/lib/chain/btc/index.js +3 -2
- package/ts_build/src/lib/chain/btc/index.js.map +1 -1
- package/ts_build/src/lib/chain/eth/index.d.ts +8 -1
- package/ts_build/src/lib/chain/eth/index.d.ts.map +1 -1
- package/ts_build/src/lib/chain/eth/index.js +114 -102
- package/ts_build/src/lib/chain/eth/index.js.map +1 -1
- package/ts_build/src/lib/chain/sol/index.js +1 -1
- package/ts_build/src/lib/chain/sol/index.js.map +1 -1
- package/ts_build/src/lib/chain/xrp/index.js +1 -1
- package/ts_build/src/lib/chain/xrp/index.js.map +1 -1
- package/ts_build/src/lib/routes/services.d.ts.map +1 -1
- package/ts_build/src/lib/routes/services.js +0 -10
- package/ts_build/src/lib/routes/services.js.map +1 -1
- package/ts_build/src/lib/routes/wallets.d.ts.map +1 -1
- package/ts_build/src/lib/routes/wallets.js +2 -1
- package/ts_build/src/lib/routes/wallets.js.map +1 -1
- package/ts_build/src/lib/server.d.ts +1 -3
- package/ts_build/src/lib/server.d.ts.map +1 -1
- package/ts_build/src/lib/server.js +128 -96
- package/ts_build/src/lib/server.js.map +1 -1
- package/ts_build/src/lib/storage.d.ts +17 -10
- package/ts_build/src/lib/storage.d.ts.map +1 -1
- package/ts_build/src/lib/storage.js +73 -20
- package/ts_build/src/lib/storage.js.map +1 -1
- package/ts_build/test/chain/eth.test.js +59 -0
- package/ts_build/test/chain/eth.test.js.map +1 -1
- package/ts_build/test/chain/sol.test.js +1 -1
- package/ts_build/test/chain/sol.test.js.map +1 -1
- package/ts_build/test/chain/xrp.test.js +1 -1
- package/ts_build/test/chain/xrp.test.js.map +1 -1
- package/ts_build/test/expressapp.test.js +132 -0
- package/ts_build/test/expressapp.test.js.map +1 -1
- package/ts_build/test/integration/addressReads.test.d.ts +2 -0
- package/ts_build/test/integration/addressReads.test.d.ts.map +1 -0
- package/ts_build/test/integration/addressReads.test.js +465 -0
- package/ts_build/test/integration/addressReads.test.js.map +1 -0
- package/ts_build/test/integration/bcmonitor.test.js +40 -0
- package/ts_build/test/integration/bcmonitor.test.js.map +1 -1
- package/ts_build/test/integration/externalservices/banxa.test.d.ts.map +1 -1
- package/ts_build/test/integration/externalservices/banxa.test.js +93 -5
- package/ts_build/test/integration/externalservices/banxa.test.js.map +1 -1
- package/ts_build/test/integration/externalservices/changelly.test.d.ts.map +1 -1
- package/ts_build/test/integration/externalservices/changelly.test.js +111 -6
- package/ts_build/test/integration/externalservices/changelly.test.js.map +1 -1
- package/ts_build/test/integration/externalservices/coinGecko.test.js +4 -0
- package/ts_build/test/integration/externalservices/coinGecko.test.js.map +1 -1
- package/ts_build/test/integration/externalservices/moonpay.test.d.ts.map +1 -1
- package/ts_build/test/integration/externalservices/moonpay.test.js +169 -9
- package/ts_build/test/integration/externalservices/moonpay.test.js.map +1 -1
- package/ts_build/test/integration/externalservices/oneInch.test.d.ts.map +1 -1
- package/ts_build/test/integration/externalservices/oneInch.test.js +26 -1
- package/ts_build/test/integration/externalservices/oneInch.test.js.map +1 -1
- package/ts_build/test/integration/externalservices/ramp.test.d.ts.map +1 -1
- package/ts_build/test/integration/externalservices/ramp.test.js +115 -4
- package/ts_build/test/integration/externalservices/ramp.test.js.map +1 -1
- package/ts_build/test/integration/externalservices/sardine.test.d.ts.map +1 -1
- package/ts_build/test/integration/externalservices/sardine.test.js +94 -5
- package/ts_build/test/integration/externalservices/sardine.test.js.map +1 -1
- package/ts_build/test/integration/externalservices/simplex.test.d.ts.map +1 -1
- package/ts_build/test/integration/externalservices/simplex.test.js +89 -5
- package/ts_build/test/integration/externalservices/simplex.test.js.map +1 -1
- package/ts_build/test/integration/externalservices/thorswap.test.d.ts.map +1 -1
- package/ts_build/test/integration/externalservices/thorswap.test.js +76 -5
- package/ts_build/test/integration/externalservices/thorswap.test.js.map +1 -1
- package/ts_build/test/integration/externalservices/transak.test.d.ts.map +1 -1
- package/ts_build/test/integration/externalservices/transak.test.js +93 -6
- package/ts_build/test/integration/externalservices/transak.test.js.map +1 -1
- package/ts_build/test/integration/helpers.js +1 -1
- package/ts_build/test/integration/helpers.js.map +1 -1
- package/ts_build/test/integration/server.test.js +225 -124
- package/ts_build/test/integration/server.test.js.map +1 -1
- package/ts_build/test/integration/walletReads.test.d.ts +2 -0
- package/ts_build/test/integration/walletReads.test.d.ts.map +1 -0
- package/ts_build/test/integration/walletReads.test.js +436 -0
- package/ts_build/test/integration/walletReads.test.js.map +1 -0
- package/src/externalservices/wyre.ts +0 -131
- package/test/integration/externalservices/wyre.test.ts +0 -270
- package/ts_build/src/externalservices/wyre.d.ts +0 -7
- package/ts_build/src/externalservices/wyre.d.ts.map +0 -1
- package/ts_build/src/externalservices/wyre.js +0 -144
- package/ts_build/src/externalservices/wyre.js.map +0 -1
- package/ts_build/test/integration/externalservices/wyre.test.d.ts +0 -2
- package/ts_build/test/integration/externalservices/wyre.test.d.ts.map +0 -1
- package/ts_build/test/integration/externalservices/wyre.test.js +0 -281
- package/ts_build/test/integration/externalservices/wyre.test.js.map +0 -1
package/src/lib/server.ts
CHANGED
|
@@ -27,7 +27,6 @@ import { SardineService } from '../externalservices/sardine';
|
|
|
27
27
|
import { SimplexService } from '../externalservices/simplex';
|
|
28
28
|
import { ThorswapService } from '../externalservices/thorswap';
|
|
29
29
|
import { TransakService } from '../externalservices/transak';
|
|
30
|
-
import { WyreService } from '../externalservices/wyre';
|
|
31
30
|
import { serverMessages } from '../serverMessages';
|
|
32
31
|
import { BCHAddressTranslator } from './bchaddresstranslator';
|
|
33
32
|
import { BlockChainExplorer } from './blockchainexplorer';
|
|
@@ -160,7 +159,6 @@ export class WalletService implements IWalletService {
|
|
|
160
159
|
simplex: SimplexService;
|
|
161
160
|
thorswap: ThorswapService;
|
|
162
161
|
transak: TransakService;
|
|
163
|
-
wyre: WyreService;
|
|
164
162
|
coinGecko: CoinGeckoService;
|
|
165
163
|
};
|
|
166
164
|
|
|
@@ -190,7 +188,6 @@ export class WalletService implements IWalletService {
|
|
|
190
188
|
simplex: new SimplexService(),
|
|
191
189
|
thorswap: new ThorswapService(),
|
|
192
190
|
transak: new TransakService(),
|
|
193
|
-
wyre: new WyreService(),
|
|
194
191
|
coinGecko: new CoinGeckoService(this.storage),
|
|
195
192
|
};
|
|
196
193
|
}
|
|
@@ -876,26 +873,29 @@ export class WalletService implements IWalletService {
|
|
|
876
873
|
pendingTxps?: ITxProposal[];
|
|
877
874
|
preferences?: boolean;
|
|
878
875
|
} = {};
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
next => {
|
|
882
|
-
this.getWallet({}, (err, wallet: Partial<Wallet>) => {
|
|
883
|
-
if (err) return next(err);
|
|
884
|
-
if (this._upgradeNeeded(UPGRADES.SOL_bwc_$lt_10_10_12, wallet)) {
|
|
885
|
-
return next(Errors.UPGRADE_NEEDED);
|
|
886
|
-
}
|
|
876
|
+
this.getWallet({}, (err, fetchedWallet: Wallet) => {
|
|
877
|
+
if (err) return cb(err);
|
|
887
878
|
|
|
879
|
+
// Read the wallet once, and give each step its own copy, the same as reading it again: the steps change their
|
|
880
|
+
// wallet in place (this one strips copayer data, the balance code sets tokenAddress on it), and the balance step
|
|
881
|
+
// can store its copy whole.
|
|
882
|
+
const copyOfWallet = () => Wallet.fromObj(fetchedWallet.toObject());
|
|
883
|
+
const omit = (obj, keys: string[]): any => Object.fromEntries(Object.entries(obj).filter(([key]) => !keys.includes(key)));
|
|
884
|
+
async.parallel(
|
|
885
|
+
[
|
|
886
|
+
next => {
|
|
887
|
+
let wallet: Partial<Wallet> = copyOfWallet();
|
|
888
888
|
const walletExtendedKeys = ['publicKeyRing', 'pubKey', 'addressManager'];
|
|
889
889
|
const copayerExtendedKeys = ['xPubKey', 'requestPubKey', 'signature', 'addressManager', 'customData'];
|
|
890
890
|
|
|
891
891
|
wallet.copayers = (wallet.copayers || []).map(copayer => {
|
|
892
892
|
if (copayer.id == this.copayerId) return copayer;
|
|
893
|
-
return
|
|
893
|
+
return omit(copayer, ['customData']);
|
|
894
894
|
});
|
|
895
895
|
if (!opts.includeExtendedInfo) {
|
|
896
|
-
wallet =
|
|
896
|
+
wallet = omit(wallet, walletExtendedKeys);
|
|
897
897
|
wallet.copayers = (wallet.copayers || []).map(copayer => {
|
|
898
|
-
return
|
|
898
|
+
return omit(copayer, copayerExtendedKeys);
|
|
899
899
|
}) as Copayer[];
|
|
900
900
|
}
|
|
901
901
|
status.wallet = wallet;
|
|
@@ -906,46 +906,45 @@ export class WalletService implements IWalletService {
|
|
|
906
906
|
status.serverMessage = deprecatedServerMessage(wallet, this.appName, this.appVersion);
|
|
907
907
|
}
|
|
908
908
|
next();
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
909
|
+
},
|
|
910
|
+
next => {
|
|
911
|
+
this.getBalance({ ...opts, wallet: copyOfWallet() }, (err, balance) => {
|
|
912
|
+
// ignore WALLET_NEED_SCAN err is includeExtendedInfo is given
|
|
913
|
+
// (to allow `importWallet` to import a wallet, while scan has
|
|
914
|
+
// failed)
|
|
915
|
+
if (opts.includeExtendedInfo) {
|
|
916
|
+
if (err && err.code != 'WALLET_NEED_SCAN') {
|
|
917
|
+
return next(err);
|
|
918
|
+
}
|
|
919
|
+
} else if (err) {
|
|
919
920
|
return next(err);
|
|
920
921
|
}
|
|
921
|
-
} else if (err) {
|
|
922
|
-
return next(err);
|
|
923
|
-
}
|
|
924
922
|
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
923
|
+
status.balance = balance;
|
|
924
|
+
next();
|
|
925
|
+
});
|
|
926
|
+
},
|
|
927
|
+
next => {
|
|
928
|
+
this.getPendingTxs({ ...opts, wallet: copyOfWallet() }, (err, pendingTxps) => {
|
|
929
|
+
if (err) return next(err);
|
|
930
|
+
status.pendingTxps = pendingTxps;
|
|
931
|
+
next();
|
|
932
|
+
});
|
|
933
|
+
},
|
|
934
|
+
next => {
|
|
935
|
+
this.getPreferences({}, (err, preferences) => {
|
|
936
|
+
if (err) return next(err);
|
|
937
|
+
status.preferences = preferences;
|
|
938
|
+
next();
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
],
|
|
942
|
+
err => {
|
|
943
|
+
if (err) return cb(err);
|
|
944
|
+
return cb(null, status);
|
|
942
945
|
}
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
if (err) return cb(err);
|
|
946
|
-
return cb(null, status);
|
|
947
|
-
}
|
|
948
|
-
);
|
|
946
|
+
);
|
|
947
|
+
});
|
|
949
948
|
}
|
|
950
949
|
|
|
951
950
|
/**
|
|
@@ -1538,9 +1537,10 @@ export class WalletService implements IWalletService {
|
|
|
1538
1537
|
_canCreateAddress(ignoreMaxGap, cb) {
|
|
1539
1538
|
if (ignoreMaxGap) return cb(null, true);
|
|
1540
1539
|
|
|
1541
|
-
|
|
1540
|
+
const newest = { noChange: true, reverse: true, limit: Defaults.MAX_MAIN_ADDRESS_GAP };
|
|
1541
|
+
this.storage.fetchAddressesPage(this.walletId, newest, (err, addresses: IAddress[]) => {
|
|
1542
1542
|
if (err) return cb(err);
|
|
1543
|
-
const latestAddresses = addresses.
|
|
1543
|
+
const latestAddresses = addresses.reverse();
|
|
1544
1544
|
if (
|
|
1545
1545
|
latestAddresses.length < Defaults.MAX_MAIN_ADDRESS_GAP ||
|
|
1546
1546
|
latestAddresses.some(a => a.hasActivity)
|
|
@@ -1641,7 +1641,7 @@ export class WalletService implements IWalletService {
|
|
|
1641
1641
|
};
|
|
1642
1642
|
|
|
1643
1643
|
const getFirstAddress = (wallet, cb) => {
|
|
1644
|
-
this.storage.
|
|
1644
|
+
this.storage.fetchAddressesPage(this.walletId, { limit: 1 }, (err, addresses) => {
|
|
1645
1645
|
if (err) return cb(err);
|
|
1646
1646
|
if (addresses?.length) {
|
|
1647
1647
|
const x = addresses[0];
|
|
@@ -1694,17 +1694,9 @@ export class WalletService implements IWalletService {
|
|
|
1694
1694
|
|
|
1695
1695
|
getAddresses(opts: GetAddressesOpts, cb) {
|
|
1696
1696
|
opts = opts || {};
|
|
1697
|
-
|
|
1697
|
+
const { noChange, addresses: only, reverse, skip, limit } = opts;
|
|
1698
|
+
this.storage.fetchAddressesPage(this.walletId, { noChange, addresses: only, reverse, skip, limit }, (err, addresses) => {
|
|
1698
1699
|
if (err) return cb(err);
|
|
1699
|
-
if (opts.noChange) {
|
|
1700
|
-
addresses = addresses.filter(a => !a.isChange);
|
|
1701
|
-
}
|
|
1702
|
-
if (opts.addresses) {
|
|
1703
|
-
addresses = addresses.filter(a => opts.addresses.includes(a.address));
|
|
1704
|
-
}
|
|
1705
|
-
if (opts.reverse) addresses.reverse();
|
|
1706
|
-
if (opts.skip > 0) addresses = addresses.slice(opts.skip);
|
|
1707
|
-
if (opts.limit > 0) addresses = addresses.slice(0, opts.limit);
|
|
1708
1700
|
|
|
1709
1701
|
this.getWallet({}, (err, wallet) => {
|
|
1710
1702
|
if (err) return cb(err);
|
|
@@ -1786,7 +1778,7 @@ export class WalletService implements IWalletService {
|
|
|
1786
1778
|
async.series(
|
|
1787
1779
|
[
|
|
1788
1780
|
next => {
|
|
1789
|
-
|
|
1781
|
+
const useWallet = (err, w) => {
|
|
1790
1782
|
if (err) return next(err);
|
|
1791
1783
|
|
|
1792
1784
|
wallet = w;
|
|
@@ -1796,7 +1788,10 @@ export class WalletService implements IWalletService {
|
|
|
1796
1788
|
bc = this._getBlockchainExplorer(wallet.chain, wallet.network);
|
|
1797
1789
|
if (!bc) return cb(new Error('Could not get blockchain explorer instance'));
|
|
1798
1790
|
return next();
|
|
1799
|
-
}
|
|
1791
|
+
};
|
|
1792
|
+
// the balance code passes the wallet it already has
|
|
1793
|
+
if (opts.wallet) return useWallet(null, opts.wallet);
|
|
1794
|
+
this.getWallet({}, useWallet);
|
|
1800
1795
|
},
|
|
1801
1796
|
next => {
|
|
1802
1797
|
if (Array.isArray(opts.addresses)) {
|
|
@@ -1892,7 +1887,7 @@ export class WalletService implements IWalletService {
|
|
|
1892
1887
|
next => {
|
|
1893
1888
|
utxoIndex = _.keyBy(allUtxos, utxoKey);
|
|
1894
1889
|
|
|
1895
|
-
this.getPendingTxs({ numberFormat: opts.numberFormat }, (err, txps) => {
|
|
1890
|
+
this.getPendingTxs({ numberFormat: opts.numberFormat, wallet }, (err, txps) => {
|
|
1896
1891
|
if (err) return next(err);
|
|
1897
1892
|
|
|
1898
1893
|
const lockedInputs = txps.flatMap(t => t.inputs).map(utxoKey);
|
|
@@ -1936,7 +1931,8 @@ export class WalletService implements IWalletService {
|
|
|
1936
1931
|
});
|
|
1937
1932
|
logger.debug(`Got ${allUtxos.length} usable UTXOs`);
|
|
1938
1933
|
return next();
|
|
1939
|
-
}
|
|
1934
|
+
},
|
|
1935
|
+
wallet
|
|
1940
1936
|
);
|
|
1941
1937
|
},
|
|
1942
1938
|
next => {
|
|
@@ -1949,6 +1945,7 @@ export class WalletService implements IWalletService {
|
|
|
1949
1945
|
}
|
|
1950
1946
|
utxo.path = addressToPath[utxo.address].path;
|
|
1951
1947
|
utxo.publicKeys = addressToPath[utxo.address].publicKeys;
|
|
1948
|
+
utxo.isChange = addressToPath[utxo.address].isChange;
|
|
1952
1949
|
}
|
|
1953
1950
|
return next();
|
|
1954
1951
|
}
|
|
@@ -2402,6 +2399,37 @@ export class WalletService implements IWalletService {
|
|
|
2402
2399
|
}
|
|
2403
2400
|
|
|
2404
2401
|
_validateAndSanitizeTxOpts(wallet, opts, cb) {
|
|
2402
|
+
const gasFeeFields = ['gasPrice', 'maxGasFee', 'priorityGasFee'];
|
|
2403
|
+
const hasGasFeeOverride = gasFeeFields.some(key => opts[key] != null);
|
|
2404
|
+
if (hasGasFeeOverride) {
|
|
2405
|
+
if (!Constants.EVM_CHAINS[wallet.chain.toUpperCase()]) {
|
|
2406
|
+
return cb(new ClientError('Gas fee overrides are only supported for EVM chains'));
|
|
2407
|
+
}
|
|
2408
|
+
for (const key of gasFeeFields) {
|
|
2409
|
+
if (opts[key] != null && (!Number.isSafeInteger(opts[key]) || opts[key] <= 0)) {
|
|
2410
|
+
return cb(new ClientError(`${key} must be a positive safe integer in wei`));
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
if (opts.gasPrice != null && Number(opts.txType || 0) !== 0) {
|
|
2414
|
+
return cb(new ClientError('gasPrice requires a type 0 transaction'));
|
|
2415
|
+
}
|
|
2416
|
+
if ((opts.maxGasFee != null || opts.priorityGasFee != null) && Number(opts.txType) !== 2) {
|
|
2417
|
+
return cb(new ClientError('maxGasFee and priorityGasFee require a type 2 transaction'));
|
|
2418
|
+
}
|
|
2419
|
+
if ((opts.maxGasFee != null) !== (opts.priorityGasFee != null)) {
|
|
2420
|
+
return cb(new ClientError('maxGasFee and priorityGasFee must be supplied together'));
|
|
2421
|
+
}
|
|
2422
|
+
if (opts.priorityGasFee > opts.maxGasFee) {
|
|
2423
|
+
return cb(new ClientError('priorityGasFee must not exceed maxGasFee'));
|
|
2424
|
+
}
|
|
2425
|
+
if (opts.feePerKb != null || opts.fee != null) {
|
|
2426
|
+
return cb(new ClientError('Gas fee overrides cannot be combined with feePerKb or fee'));
|
|
2427
|
+
}
|
|
2428
|
+
// sendMax calculates its amount before getFee, using a separate fee estimate.
|
|
2429
|
+
if (opts.sendMax) {
|
|
2430
|
+
return cb(new ClientError('Gas fee overrides are not supported with sendMax'));
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2405
2433
|
async.series(
|
|
2406
2434
|
[
|
|
2407
2435
|
next => {
|
|
@@ -2409,7 +2437,7 @@ export class WalletService implements IWalletService {
|
|
|
2409
2437
|
boolToNum(!!opts.feeLevel) + boolToNum(Utils.isNumber(opts.feePerKb)) + boolToNum(Utils.isNumber(opts.fee));
|
|
2410
2438
|
if (feeArgs > 1) return next(new ClientError('Only one of feeLevel/feePerKb/fee can be specified'));
|
|
2411
2439
|
|
|
2412
|
-
if (feeArgs == 0) {
|
|
2440
|
+
if (feeArgs == 0 && !hasGasFeeOverride) {
|
|
2413
2441
|
opts.feeLevel = 'normal';
|
|
2414
2442
|
}
|
|
2415
2443
|
|
|
@@ -2421,6 +2449,11 @@ export class WalletService implements IWalletService {
|
|
|
2421
2449
|
);
|
|
2422
2450
|
}
|
|
2423
2451
|
|
|
2452
|
+
// An explicit gas fee determines the price, not the named fee level.
|
|
2453
|
+
if (hasGasFeeOverride) {
|
|
2454
|
+
delete opts.feeLevel;
|
|
2455
|
+
}
|
|
2456
|
+
|
|
2424
2457
|
const error = ChainService.checkUtxos(wallet.chain, opts);
|
|
2425
2458
|
if (error) {
|
|
2426
2459
|
return next(new ClientError('fee can only be set when inputs are specified'));
|
|
@@ -2782,6 +2815,9 @@ export class WalletService implements IWalletService {
|
|
|
2782
2815
|
* @param {Boolean} opts.enableRBF - Optional. enable BTC Replace By Fee
|
|
2783
2816
|
* @param {Boolean} opts.replaceTxByFee - Optional. Ignore locked utxos check ( used for replacing a transaction designated as RBF)
|
|
2784
2817
|
* @param {number} opts.txType - Optional. Type of EVM transaction
|
|
2818
|
+
* @param {number} opts.gasPrice - Optional. Type 0 gas price in wei. Overrides feeLevel; incompatible with fee/feePerKb/sendMax.
|
|
2819
|
+
* @param {number} opts.maxGasFee - Optional. Type 2 maximum fee per gas in wei; requires priorityGasFee.
|
|
2820
|
+
* @param {number} opts.priorityGasFee - Optional. Positive type 2 priority fee per gas in wei; requires maxGasFee and must not exceed it. Gas fee overrides cannot be used with fee/feePerKb/sendMax.
|
|
2785
2821
|
* @param {number} opts.gasLimitBuffer - Optional. Percentage of buffer to add to the gasLimit
|
|
2786
2822
|
* @param {number} opts.priorityFeePercentile - Optional. Percentile of targeted priority fee rate
|
|
2787
2823
|
* @param {Boolean} opts.multiTx - Optional. Proposal will create multiple transactions
|
|
@@ -3172,19 +3208,23 @@ export class WalletService implements IWalletService {
|
|
|
3172
3208
|
* @returns {Object} txProposal
|
|
3173
3209
|
*/
|
|
3174
3210
|
getTx(opts, cb: (err: any, txp?: TxProposal) => void) {
|
|
3175
|
-
|
|
3211
|
+
// the tx and its note are both completed from the wallet, so read it once
|
|
3212
|
+
this.storage.fetchWallet(this.walletId, (err, wallet) => {
|
|
3176
3213
|
if (err) return cb(err);
|
|
3177
|
-
|
|
3214
|
+
this.storage.fetchTx(this.walletId, opts.txProposalId, (err, txp) => {
|
|
3215
|
+
if (err) return cb(err);
|
|
3216
|
+
if (!txp) return cb(Errors.TX_NOT_FOUND);
|
|
3178
3217
|
|
|
3179
|
-
|
|
3218
|
+
if (!txp.txid) return cb(null, txp);
|
|
3180
3219
|
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3220
|
+
this.storage.fetchTxNote(this.walletId, txp.txid, (err, note) => {
|
|
3221
|
+
if (err) {
|
|
3222
|
+
this.logw(`Error fetching tx note for ${txp.txid}:`, err);
|
|
3223
|
+
}
|
|
3224
|
+
txp.note = note;
|
|
3225
|
+
return cb(null, txp);
|
|
3226
|
+
}, wallet);
|
|
3227
|
+
}, wallet);
|
|
3188
3228
|
});
|
|
3189
3229
|
}
|
|
3190
3230
|
|
|
@@ -3202,19 +3242,22 @@ export class WalletService implements IWalletService {
|
|
|
3202
3242
|
// foreign wallet's proposal data to any authenticated copayer. The
|
|
3203
3243
|
// internal global consumers (BlockchainMonitor, getWalletFromIdentifier)
|
|
3204
3244
|
// are unaffected; they keep calling storage.fetchTxByHash directly.
|
|
3205
|
-
this.storage.
|
|
3245
|
+
this.storage.fetchWallet(this.walletId, (err, wallet) => {
|
|
3206
3246
|
if (err) return cb(err);
|
|
3207
|
-
|
|
3247
|
+
this.storage.fetchTxByHashForWallet(this.walletId, opts.txid, (err, txp) => {
|
|
3248
|
+
if (err) return cb(err);
|
|
3249
|
+
if (!txp) return cb(Errors.TX_NOT_FOUND);
|
|
3208
3250
|
|
|
3209
|
-
|
|
3251
|
+
if (!txp.txid) return cb(null, txp);
|
|
3210
3252
|
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3253
|
+
this.storage.fetchTxNote(this.walletId, txp.txid, (err, note) => {
|
|
3254
|
+
if (err) {
|
|
3255
|
+
this.logw(`Error fetching tx note for ${txp.txid}:`, err);
|
|
3256
|
+
}
|
|
3257
|
+
txp.note = note;
|
|
3258
|
+
return cb(null, txp);
|
|
3259
|
+
}, wallet);
|
|
3260
|
+
}, wallet);
|
|
3218
3261
|
});
|
|
3219
3262
|
}
|
|
3220
3263
|
|
|
@@ -3844,7 +3887,7 @@ export class WalletService implements IWalletService {
|
|
|
3844
3887
|
return cb(err, txps);
|
|
3845
3888
|
}
|
|
3846
3889
|
);
|
|
3847
|
-
});
|
|
3890
|
+
}, opts.wallet);
|
|
3848
3891
|
}
|
|
3849
3892
|
}
|
|
3850
3893
|
|
|
@@ -4870,7 +4913,8 @@ export class WalletService implements IWalletService {
|
|
|
4870
4913
|
minTs,
|
|
4871
4914
|
maxTs
|
|
4872
4915
|
},
|
|
4873
|
-
done
|
|
4916
|
+
done,
|
|
4917
|
+
wallet
|
|
4874
4918
|
);
|
|
4875
4919
|
},
|
|
4876
4920
|
done => {
|
|
@@ -4879,7 +4923,8 @@ export class WalletService implements IWalletService {
|
|
|
4879
4923
|
{
|
|
4880
4924
|
minTs
|
|
4881
4925
|
},
|
|
4882
|
-
done
|
|
4926
|
+
done,
|
|
4927
|
+
wallet
|
|
4883
4928
|
);
|
|
4884
4929
|
}
|
|
4885
4930
|
],
|
package/src/lib/storage.ts
CHANGED
|
@@ -304,8 +304,9 @@ export class Storage {
|
|
|
304
304
|
}
|
|
305
305
|
|
|
306
306
|
// TODO: should be done client-side
|
|
307
|
-
|
|
308
|
-
|
|
307
|
+
// Callers that already have the wallet pass it, to save reading it again
|
|
308
|
+
_completeTxData(walletId: string, txs: TxProposal | TxProposal[], cb: (err?: any, txs?: TxProposal | TxProposal[]) => void, wallet?: Wallet) {
|
|
309
|
+
const complete = (err, wallet: Wallet) => {
|
|
309
310
|
if (err) return cb(err);
|
|
310
311
|
for (const tx of [].concat(txs) as TxProposal[]) {
|
|
311
312
|
tx.derivationStrategy = wallet.derivationStrategy || 'BIP45';
|
|
@@ -318,11 +319,13 @@ export class Storage {
|
|
|
318
319
|
tx.raw = tx.getRawTx();
|
|
319
320
|
}
|
|
320
321
|
return cb(null, txs);
|
|
321
|
-
}
|
|
322
|
+
};
|
|
323
|
+
if (wallet) return complete(null, wallet);
|
|
324
|
+
this.fetchWallet(walletId, complete);
|
|
322
325
|
}
|
|
323
326
|
|
|
324
327
|
// TODO: remove walletId from signature
|
|
325
|
-
fetchTx(walletId: string, txProposalId: string, cb: (err?: any, tx?: TxProposal) => void) {
|
|
328
|
+
fetchTx(walletId: string, txProposalId: string, cb: (err?: any, tx?: TxProposal) => void, wallet?: Wallet) {
|
|
326
329
|
if (!this.db) return cb();
|
|
327
330
|
|
|
328
331
|
this.db.collection(collections.TXS).findOne(
|
|
@@ -333,7 +336,7 @@ export class Storage {
|
|
|
333
336
|
(err, result) => {
|
|
334
337
|
if (err) return cb(err);
|
|
335
338
|
if (!result) return cb();
|
|
336
|
-
return this._completeTxData(walletId, TxProposal.fromObj(result), cb);
|
|
339
|
+
return this._completeTxData(walletId, TxProposal.fromObj(result), cb, wallet);
|
|
337
340
|
}
|
|
338
341
|
);
|
|
339
342
|
}
|
|
@@ -356,7 +359,7 @@ export class Storage {
|
|
|
356
359
|
|
|
357
360
|
// Wallet-scoped counterpart to fetchTxByHash, used by the authenticated
|
|
358
361
|
// by-hash API read.
|
|
359
|
-
fetchTxByHashForWallet(walletId: string, hash, cb: (err?: any, tx?: TxProposal) => void) {
|
|
362
|
+
fetchTxByHashForWallet(walletId: string, hash, cb: (err?: any, tx?: TxProposal) => void, wallet?: Wallet) {
|
|
360
363
|
if (!this.db) return cb();
|
|
361
364
|
|
|
362
365
|
this.db.collection(collections.TXS).findOne(
|
|
@@ -368,7 +371,7 @@ export class Storage {
|
|
|
368
371
|
if (err) return cb(err);
|
|
369
372
|
if (!result) return cb();
|
|
370
373
|
|
|
371
|
-
return this._completeTxData(walletId, TxProposal.fromObj(result), cb);
|
|
374
|
+
return this._completeTxData(walletId, TxProposal.fromObj(result), cb, wallet);
|
|
372
375
|
}
|
|
373
376
|
);
|
|
374
377
|
}
|
|
@@ -444,7 +447,7 @@ export class Storage {
|
|
|
444
447
|
});
|
|
445
448
|
}
|
|
446
449
|
|
|
447
|
-
fetchPendingTxs(walletId, cb) {
|
|
450
|
+
fetchPendingTxs(walletId, cb, wallet?: Wallet) {
|
|
448
451
|
this.db
|
|
449
452
|
.collection(collections.TXS)
|
|
450
453
|
.find({
|
|
@@ -460,7 +463,7 @@ export class Storage {
|
|
|
460
463
|
const txs = _.map(result, tx => {
|
|
461
464
|
return TxProposal.fromObj(tx);
|
|
462
465
|
});
|
|
463
|
-
return this._completeTxData(walletId, txs, cb);
|
|
466
|
+
return this._completeTxData(walletId, txs, cb, wallet);
|
|
464
467
|
});
|
|
465
468
|
}
|
|
466
469
|
|
|
@@ -472,7 +475,7 @@ export class Storage {
|
|
|
472
475
|
* @param opts.maxTs
|
|
473
476
|
* @param opts.limit
|
|
474
477
|
*/
|
|
475
|
-
fetchTxs(walletId, opts, cb) {
|
|
478
|
+
fetchTxs(walletId, opts, cb, wallet?: Wallet) {
|
|
476
479
|
opts = opts || {};
|
|
477
480
|
|
|
478
481
|
const tsFilter: { $gte?: number; $lte?: number } = {};
|
|
@@ -499,7 +502,7 @@ export class Storage {
|
|
|
499
502
|
const txs = _.map(result, tx => {
|
|
500
503
|
return TxProposal.fromObj(tx);
|
|
501
504
|
});
|
|
502
|
-
return this._completeTxData(walletId, txs, cb);
|
|
505
|
+
return this._completeTxData(walletId, txs, cb, wallet);
|
|
503
506
|
});
|
|
504
507
|
}
|
|
505
508
|
|
|
@@ -511,7 +514,7 @@ export class Storage {
|
|
|
511
514
|
* @param opts.maxTs
|
|
512
515
|
* @param opts.limit
|
|
513
516
|
*/
|
|
514
|
-
fetchBroadcastedTxs(walletId, opts, cb) {
|
|
517
|
+
fetchBroadcastedTxs(walletId, opts, cb, wallet?: Wallet) {
|
|
515
518
|
opts = opts || {};
|
|
516
519
|
|
|
517
520
|
const tsFilter: { $gte?: number; $lte?: number } = {};
|
|
@@ -543,7 +546,7 @@ export class Storage {
|
|
|
543
546
|
const txs = _.map(result, tx => {
|
|
544
547
|
return TxProposal.fromObj(tx);
|
|
545
548
|
});
|
|
546
|
-
return this._completeTxData(walletId, txs, cb);
|
|
549
|
+
return this._completeTxData(walletId, txs, cb, wallet);
|
|
547
550
|
});
|
|
548
551
|
}
|
|
549
552
|
|
|
@@ -678,6 +681,70 @@ export class Storage {
|
|
|
678
681
|
});
|
|
679
682
|
}
|
|
680
683
|
|
|
684
|
+
/**
|
|
685
|
+
* A page of the wallet's addresses, in fetchAddresses order (createdOn, then path), reading only the addresses the
|
|
686
|
+
* page needs. noChange and addresses filter the list, then reverse, skip and limit apply in that order.
|
|
687
|
+
*
|
|
688
|
+
* @param walletId
|
|
689
|
+
* @param opts.noChange - leave out change addresses
|
|
690
|
+
* @param opts.addresses - only these addresses
|
|
691
|
+
* @param opts.reverse - newest first
|
|
692
|
+
* @param opts.skip
|
|
693
|
+
* @param opts.limit
|
|
694
|
+
*/
|
|
695
|
+
fetchAddressesPage(
|
|
696
|
+
walletId: string,
|
|
697
|
+
opts: { noChange?: boolean; addresses?: string[]; reverse?: boolean; skip?: number; limit?: number },
|
|
698
|
+
cb: (err?: any, addresses?: Address[]) => void
|
|
699
|
+
) {
|
|
700
|
+
const query: { walletId: string; isChange?: { $in: Array<false | null> }; address?: { $in: string[] } } = { walletId };
|
|
701
|
+
// false, null or missing, the same addresses as !isChange
|
|
702
|
+
if (opts.noChange) query.isChange = { $in: [false, null] };
|
|
703
|
+
if (opts.addresses) query.address = { $in: opts.addresses };
|
|
704
|
+
// skip and limit apply only when positive, and as whole numbers, as slice does
|
|
705
|
+
const skip = opts.skip > 0 ? Math.floor(opts.skip) : 0;
|
|
706
|
+
const limit = opts.limit > 0 ? Math.floor(opts.limit) : null;
|
|
707
|
+
const collection = this.db.collection(collections.ADDRESSES);
|
|
708
|
+
|
|
709
|
+
const page = docs => {
|
|
710
|
+
let addresses = Utils.sortAsc(docs.map(Address.fromObj), 'createdOn', 'path');
|
|
711
|
+
if (opts.reverse) addresses.reverse();
|
|
712
|
+
if (skip) addresses = addresses.slice(skip);
|
|
713
|
+
if (limit !== null) addresses = addresses.slice(0, limit);
|
|
714
|
+
return addresses;
|
|
715
|
+
};
|
|
716
|
+
const readAll = () => collection.find(query).sort({ createdOn: 1 }).toArray((err, docs) => {
|
|
717
|
+
if (err) return cb(err);
|
|
718
|
+
return cb(null, page(docs));
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
// filtering by address, or reading without a limit or with one too large for a query limit, reads every matching
|
|
722
|
+
// address
|
|
723
|
+
if (opts.addresses || !(limit > 0) || !Number.isSafeInteger(skip + limit + 1)) return readAll();
|
|
724
|
+
|
|
725
|
+
// fetchAddresses sorts an address with a missing createdOn last and one with a null createdOn first, but Mongo sorts
|
|
726
|
+
// both first, so a wallet that has either is read whole
|
|
727
|
+
collection.findOne({ walletId, createdOn: null }, { projection: { _id: 1 } }, (err, unordered) => {
|
|
728
|
+
if (err) return cb(err);
|
|
729
|
+
if (unordered) return readAll();
|
|
730
|
+
|
|
731
|
+
// The page is within the first skip + limit addresses by createdOn plus any others sharing the last one's
|
|
732
|
+
// createdOn, whatever order addresses created in the same second are in. One more address shows whether there are any.
|
|
733
|
+
const count = skip + limit;
|
|
734
|
+
collection.find(query).sort({ createdOn: opts.reverse ? -1 : 1 }).limit(count + 1).toArray((err, docs) => {
|
|
735
|
+
if (err) return cb(err);
|
|
736
|
+
if (docs.length <= count || docs[count].createdOn !== docs[count - 1].createdOn) {
|
|
737
|
+
return cb(null, page(docs.slice(0, count)));
|
|
738
|
+
}
|
|
739
|
+
collection.find({ ...query, createdOn: docs[count - 1].createdOn }).toArray((err, ties) => {
|
|
740
|
+
if (err) return cb(err);
|
|
741
|
+
const read = new Set(docs.slice(0, count).map(doc => String(doc._id)));
|
|
742
|
+
return cb(null, page([...docs.slice(0, count), ...ties.filter(doc => !read.has(String(doc._id)))]));
|
|
743
|
+
});
|
|
744
|
+
});
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
|
|
681
748
|
migrateToCashAddr(walletId, cb) {
|
|
682
749
|
const cursor = this.db.collection(collections.ADDRESSES).find({
|
|
683
750
|
walletId
|
|
@@ -1300,7 +1367,7 @@ export class Storage {
|
|
|
1300
1367
|
});
|
|
1301
1368
|
}
|
|
1302
1369
|
|
|
1303
|
-
fetchTxNote(walletId, txid, cb: (err?: any, note?: TxNote) => void) {
|
|
1370
|
+
fetchTxNote(walletId, txid, cb: (err?: any, note?: TxNote) => void, wallet?: Wallet) {
|
|
1304
1371
|
this.db.collection(collections.TX_NOTES).findOne(
|
|
1305
1372
|
{
|
|
1306
1373
|
walletId,
|
|
@@ -1309,20 +1376,23 @@ export class Storage {
|
|
|
1309
1376
|
(err, result) => {
|
|
1310
1377
|
if (err) return cb(err);
|
|
1311
1378
|
if (!result) return cb();
|
|
1312
|
-
return this._completeTxNotesData(walletId, TxNote.fromObj(result), cb);
|
|
1379
|
+
return this._completeTxNotesData(walletId, TxNote.fromObj(result), cb, wallet);
|
|
1313
1380
|
}
|
|
1314
1381
|
);
|
|
1315
1382
|
}
|
|
1316
1383
|
|
|
1317
1384
|
// TODO: should be done client-side
|
|
1318
|
-
|
|
1319
|
-
|
|
1385
|
+
// Callers that already have the wallet pass it, to save reading it again
|
|
1386
|
+
_completeTxNotesData(walletId, notes: TxNote | TxNote[], cb: (err?: any, notes?: TxNote | TxNote[]) => void, wallet?: Wallet) {
|
|
1387
|
+
const complete = (err, wallet: Wallet) => {
|
|
1320
1388
|
if (err) return cb(err);
|
|
1321
1389
|
for (const note of [].concat(notes) as TxNote[]) {
|
|
1322
1390
|
note.editedByName = wallet.getCopayer(note.editedBy).name;
|
|
1323
1391
|
}
|
|
1324
1392
|
return cb(null, notes);
|
|
1325
|
-
}
|
|
1393
|
+
};
|
|
1394
|
+
if (wallet) return complete(null, wallet);
|
|
1395
|
+
this.fetchWallet(walletId, complete);
|
|
1326
1396
|
}
|
|
1327
1397
|
|
|
1328
1398
|
/**
|
|
@@ -1331,7 +1401,7 @@ export class Storage {
|
|
|
1331
1401
|
* @param walletId
|
|
1332
1402
|
* @param opts.minTs
|
|
1333
1403
|
*/
|
|
1334
|
-
fetchTxNotes(walletId, opts, cb) {
|
|
1404
|
+
fetchTxNotes(walletId, opts, cb, wallet?: Wallet) {
|
|
1335
1405
|
const filter: { walletId: string; editedOn?: { $gte: number } } = {
|
|
1336
1406
|
walletId
|
|
1337
1407
|
};
|
|
@@ -1349,7 +1419,7 @@ export class Storage {
|
|
|
1349
1419
|
return TxNote.fromObj(note);
|
|
1350
1420
|
})
|
|
1351
1421
|
);
|
|
1352
|
-
return this._completeTxNotesData(walletId, notes, cb);
|
|
1422
|
+
return this._completeTxNotesData(walletId, notes, cb, wallet);
|
|
1353
1423
|
});
|
|
1354
1424
|
}
|
|
1355
1425
|
|