bitcore-wallet-service
Version:
A service for Mutisig HD Bitcoin Wallets
1,239 lines • 86.9 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExpressApp = void 0;
const async = __importStar(require("async"));
const cors_1 = __importDefault(require("cors"));
const express_1 = __importDefault(require("express"));
const lodash_1 = __importDefault(require("lodash"));
const path_1 = __importDefault(require("path"));
require("source-map-support/register");
const config_1 = __importDefault(require("../config"));
const common_1 = require("./common");
const clienterror_1 = require("./errors/clienterror");
const errordefinitions_1 = require("./errors/errordefinitions");
const logger_1 = require("./logger");
const middleware_1 = require("./middleware");
const server_1 = require("./server");
const stats_1 = require("./stats");
const compression = require('compression');
const RateLimit = require('express-rate-limit');
const rp = require('request-promise-native');
const Defaults = common_1.Common.Defaults;
class ExpressApp {
constructor() {
this.app = (0, express_1.default)();
}
start(opts, cb) {
opts = opts || {};
this.app.use(compression());
this.app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'x-signature,x-identity,x-identities,x-session,x-client-version,x-wallet-id,X-Requested-With,Content-Type,Authorization');
res.setHeader('x-service-version', server_1.WalletService.getServiceVersion());
next();
});
const allowCORS = (req, res, next) => {
if ('OPTIONS' == req.method) {
res.sendStatus(200);
res.end();
return;
}
next();
};
this.app.use(allowCORS);
this.app.enable('trust proxy');
this.app.use((req, res, next) => {
req.on('abort', () => {
logger_1.logger.warn('Request aborted by the client');
});
next();
});
const POST_LIMIT = 1024 * 100;
const POST_LIMIT_LARGE = 2 * 1024 * 1024;
this.app.use((req, res, next) => {
if (req.path.includes('/txproposals')) {
return express_1.default.json({ limit: POST_LIMIT_LARGE })(req, res, next);
}
else {
return express_1.default.json({ limit: POST_LIMIT })(req, res, next);
}
});
this.app.use((req, res, next) => {
if (config_1.default.maintenanceOpts.maintenanceMode === true) {
let errorCode = 503;
let errorMessage = 'BWS down for maintenance';
res.status(503).send({ code: errorCode, message: errorMessage });
}
else {
next();
}
});
if (opts.disableLogs) {
logger_1.transport.level = 'error';
}
else {
this.app.use((0, middleware_1.LogMiddleware)());
}
const router = express_1.default.Router();
const returnError = (err, res, req) => {
if (res.headersSent) {
return;
}
if (err instanceof clienterror_1.ClientError) {
const status = err.code == 'NOT_AUTHORIZED' ? 401 : 400;
if (!opts.disableLogs)
logger_1.logger.info('Client Err: ' + status + ' ' + req.url + ' ' + JSON.stringify(err));
const clientError = {
code: err.code,
message: err.message
};
if (err.messageData)
clientError.messageData = err.messageData;
res
.status(status)
.json(clientError)
.end();
}
else {
let code = 500, message;
if (err && ((err.code && lodash_1.default.isNumber(err.code)) || (err.statusCode && lodash_1.default.isNumber(err.statusCode)))) {
code = err.code || err.statusCode;
message = err.message || err.body;
}
const m = message || err.toString();
if (!opts.disableLogs)
logger_1.logger.error(req.url + ' :' + code + ':' + m);
res
.status(code || 500)
.json({
error: m
})
.end();
}
};
const logDeprecated = req => {
logger_1.logger.warn('DEPRECATED', req.method, req.url, '(' + req.header('x-client-version') + ')');
};
const getCredentials = req => {
const identity = req.header('x-identity');
if (!identity)
return;
return {
copayerId: identity,
signature: req.header('x-signature'),
session: req.header('x-session')
};
};
const getServer = (req, res) => {
const opts = {
clientVersion: req.header('x-client-version'),
userAgent: req.header('user-agent')
};
return server_1.WalletService.getInstance(opts);
};
const getServerWithAuth = (req, res, opts, cb) => {
if (lodash_1.default.isFunction(opts)) {
cb = opts;
opts = {};
}
opts = opts || {};
const credentials = getCredentials(req);
if (!credentials)
return returnError(new clienterror_1.ClientError({
code: 'NOT_AUTHORIZED'
}), res, req);
const auth = {
copayerId: credentials.copayerId,
message: req.method.toLowerCase() + '|' + req.url + '|' + JSON.stringify(req.body),
signature: credentials.signature,
clientVersion: req.header('x-client-version'),
userAgent: req.header('user-agent'),
walletId: req.header('x-wallet-id'),
session: undefined
};
if (opts.allowSession) {
auth.session = credentials.session;
}
server_1.WalletService.getInstanceWithAuth(auth, (err, server) => {
opts = opts;
if (err) {
if (opts.silentFailure) {
return cb(null, err);
}
else {
return returnError(err, res, req);
}
}
if (opts.onlySupportStaff && !server.copayerIsSupportStaff) {
return returnError(new clienterror_1.ClientError({
code: 'NOT_AUTHORIZED'
}), res, req);
}
if (server.copayerIsSupportStaff) {
req.isSupportStaff = true;
}
if (opts.onlyMarketingStaff && !server.copayerIsMarketingStaff) {
return returnError(new clienterror_1.ClientError({
code: 'NOT_AUTHORIZED'
}), res, req);
}
req.walletId = server.walletId;
req.copayerId = server.copayerId;
return cb(server);
});
};
const getServerWithMultiAuth = (req, res, opts = {}) => {
const identities = req.headers['x-identities'] ? req.headers['x-identities'].split(',') : false;
const signature = req.headers['x-signature'];
if (!identities || !signature) {
throw new clienterror_1.ClientError({ code: 'NOT_AUTHORIZED' });
}
if (!Array.isArray(identities)) {
throw new clienterror_1.ClientError({ code: 'NOT_AUTHORIZED' });
}
return identities.map(id => new Promise((resolve, reject) => getServerWithAuth(Object.assign(req, {
headers: {
...req.headers,
'x-identity': id
}
}), res, opts, (server, err) => (err ? reject(err) : resolve(server)))));
};
let createWalletLimiter;
if (Defaults.RateLimit.createWallet && !opts.ignoreRateLimiter) {
logger_1.logger.info('Limiting wallet creation per IP: %o req/h', ((Defaults.RateLimit.createWallet.max / Defaults.RateLimit.createWallet.windowMs) * 60 * 60 * 1000).toFixed(2));
createWalletLimiter = new RateLimit(Defaults.RateLimit.createWallet);
}
else {
createWalletLimiter = (req, res, next) => {
next();
};
}
const ONE_MINUTE = 60;
function SetPublicCache(res, seconds) {
res.setHeader('Cache-Control', `public, max-age=${seconds}, stale-if-error=${10 * seconds}`);
}
router.get('/latest-version', async (req, res) => {
SetPublicCache(res, 10 * ONE_MINUTE);
try {
res.setHeader('User-Agent', 'copay');
var options = {
uri: 'https://api.github.com/repos/bitpay/wallet/releases/latest',
headers: {
'User-Agent': 'Copay'
},
json: true
};
let server;
try {
server = getServer(req, res);
}
catch (ex) {
return returnError(ex, res, req);
}
server.storage.checkAndUseGlobalCache('latest-copay-version', Defaults.COPAY_VERSION_CACHE_DURATION, async (err, version) => {
if (err) {
res.send(err);
}
if (version) {
res.json({ version });
}
else {
try {
const htmlString = await rp(options);
if (htmlString['tag_name']) {
server.storage.storeGlobalCache('latest-copay-version', htmlString['tag_name'], err => {
res.json({ version: htmlString['tag_name'] });
});
}
}
catch (err) {
res.send(err);
}
}
});
}
catch (err) {
res.send(err);
}
});
router.post('/v1/wallets/', createWalletLimiter, (req, res) => {
logDeprecated(req);
return returnError(new clienterror_1.ClientError('BIP45 wallet creation no longer supported'), res, req);
});
router.post('/v2/wallets/', createWalletLimiter, (req, res) => {
let server;
try {
server = getServer(req, res);
}
catch (ex) {
return returnError(ex, res, req);
}
server.createWallet(req.body, (err, walletId) => {
if (err)
return returnError(err, res, req);
res.json({
walletId
});
});
});
router.put('/v1/copayers/:id/', (req, res) => {
req.body.copayerId = req.params['id'];
let server;
try {
server = getServer(req, res);
}
catch (ex) {
return returnError(ex, res, req);
}
server.addAccess(req.body, (err, result) => {
if (err)
return returnError(err, res, req);
res.json(result);
});
});
router.post('/v1/wallets/:id/copayers/', (req, res) => {
logDeprecated(req);
return returnError(new clienterror_1.ClientError('BIP45 wallet creation no longer supported'), res, req);
});
router.post('/v2/wallets/:id/copayers/', (req, res) => {
req.body.walletId = req.params['id'];
let server;
try {
server = getServer(req, res);
}
catch (ex) {
return returnError(ex, res, req);
}
server.joinWallet(req.body, (err, result) => {
if (err)
return returnError(err, res, req);
res.json(result);
});
});
router.get('/v1/wallets/', (req, res) => {
logDeprecated(req);
getServerWithAuth(req, res, server => {
server.getStatus({
includeExtendedInfo: true
}, (err, status) => {
if (err)
return returnError(err, res, req);
res.json(status);
});
});
});
router.get('/v2/wallets/', (req, res) => {
getServerWithAuth(req, res, server => {
const opts = { includeExtendedInfo: false, twoStep: false };
if (req.query.includeExtendedInfo == '1')
opts.includeExtendedInfo = true;
if (req.query.twoStep == '1')
opts.twoStep = true;
server.getStatus(opts, (err, status) => {
if (err)
return returnError(err, res, req);
res.json(status);
});
});
});
router.get('/v3/wallets/', (req, res) => {
getServerWithAuth(req, res, server => {
const opts = {
includeExtendedInfo: false,
twoStep: false,
includeServerMessages: false,
tokenAddress: req.query.tokenAddress,
multisigContractAddress: req.query.multisigContractAddress,
network: req.query.network
};
if (req.query.includeExtendedInfo == '1')
opts.includeExtendedInfo = true;
if (req.query.twoStep == '1')
opts.twoStep = true;
if (req.query.serverMessageArray == '1')
opts.includeServerMessages = true;
server.getStatus(opts, (err, status) => {
if (err)
return returnError(err, res, req);
res.json(status);
});
});
});
router.get('/v1/wallets/all/', async (req, res) => {
let responses;
const buildOpts = (req, copayerId) => {
const getParam = (param, returnArray = false) => {
const value = req.query[`${copayerId}:${param}`] || req.query[copayerId]?.[param];
if (returnArray) {
return Array.isArray(value) ? value : value ? [value] : null;
}
return value ? value : null;
};
const opts = {
includeExtendedInfo: req.query.includeExtendedInfo == '1',
twoStep: req.query.twoStep == '1',
silentFailure: req.query.silentFailure == '1',
includeServerMessages: req.query.serverMessageArray == '1',
tokenAddresses: getParam('tokenAddress', true),
multisigContractAddress: getParam('multisigContractAddress'),
network: getParam('network')
};
return opts;
};
try {
responses = await Promise.all(getServerWithMultiAuth(req, res, { silentFailure: req.query.silentFailure == '1' }).map(promise => promise.then((server) => new Promise(resolve => {
let options = buildOpts(req, server.copayerId);
if (options.tokenAddresses) {
options.tokenAddresses.unshift(null);
return async.concat(options.tokenAddresses, (tokenAddress, cb) => {
let optsClone = JSON.parse(JSON.stringify(options));
optsClone.tokenAddresses = null;
optsClone.tokenAddress = tokenAddress;
return server.getStatus(optsClone, (err, status) => {
let result = {
walletId: server.walletId,
tokenAddress: optsClone.tokenAddress,
success: true,
...(err ? { success: false, message: err.message } : {}),
status
};
if (err && err.message)
logger_1.logger.error(`An error occurred retrieving wallet status - id: ${server.walletId} - token address: ${optsClone.tokenAddress} - err: ${err.message}`);
cb(null, result);
});
}, (err, result) => {
return resolve(result);
});
}
else {
return server.getStatus(options, (err, status) => {
return resolve([
{
walletId: server.walletId,
tokenAddress: null,
success: true,
...(err ? { success: false, message: err.message } : {}),
status
}
]);
});
}
}), ({ message }) => Promise.resolve({ success: false, error: message }))));
}
catch (err) {
return returnError(err, res, req);
}
return res.json(lodash_1.default.flatten(responses));
});
router.get('/v1/wallets/:identifier/', (req, res) => {
getServerWithAuth(req, res, {
onlySupportStaff: true
}, server => {
const opts = {
identifier: req.params['identifier'],
walletCheck: ['1', 'true'].includes(req.query['walletCheck'])
};
server.getWalletFromIdentifier(opts, (err, wallet) => {
if (err)
return returnError(err, res, req);
if (!wallet)
return res.end();
server.walletId = wallet.id;
const opts = { includeExtendedInfo: false };
if (req.query.includeExtendedInfo == '1')
opts.includeExtendedInfo = true;
server.getStatus(opts, (err, status) => {
if (err)
return returnError(err, res, req);
res.json(status);
});
});
});
});
router.get('/v1/preferences/', (req, res) => {
getServerWithAuth(req, res, server => {
server.getPreferences({}, (err, preferences) => {
if (err)
return returnError(err, res, req);
res.json(preferences);
});
});
});
router.put('/v1/preferences', (req, res) => {
getServerWithAuth(req, res, server => {
server.savePreferences(req.body, (err, result) => {
if (err)
return returnError(err, res, req);
res.json(result);
});
});
});
router.get('/v1/txproposals/', (req, res) => {
getServerWithAuth(req, res, server => {
server.getPendingTxs({ noCashAddr: true }, (err, pendings) => {
if (err)
return returnError(err, res, req);
res.json(pendings);
});
});
});
router.get('/v2/txproposals/', (req, res) => {
getServerWithAuth(req, res, server => {
server.getPendingTxs({}, (err, pendings) => {
if (err)
return returnError(err, res, req);
res.json(pendings);
});
});
});
router.post('/v1/txproposals/', (req, res) => {
const err = errordefinitions_1.Errors.UPGRADE_NEEDED;
return returnError(err, res, req);
});
router.post('/v2/txproposals/', (req, res) => {
getServerWithAuth(req, res, server => {
req.body.noCashAddr = true;
req.body.txpVersion = 3;
server.createTx(req.body, (err, txp) => {
if (err)
return returnError(err, res, req);
res.json(txp);
});
});
});
router.post('/v3/txproposals/', (req, res) => {
getServerWithAuth(req, res, server => {
req.body.txpVersion = 3;
server.createTx(req.body, (err, txp) => {
if (err)
return returnError(err, res, req);
res.json(txp);
});
});
});
router.post('/v1/advertisements/', (req, res) => {
getServerWithAuth(req, res, {
onlyMarketingStaff: true
}, server => {
server.createAdvert(req.body, (err, advert) => {
if (err) {
return returnError(err, res, req);
}
if (advert)
res.json(advert);
});
});
});
router.get('/v1/advertisements/', (req, res) => {
let server;
let testing = req.query.testing;
try {
server = getServer(req, res);
}
catch (ex) {
return returnError(ex, res, req);
}
if (testing) {
server.getTestingAdverts(req.body, (err, ads) => {
if (err)
returnError(err, res, req);
res.json(ads);
});
}
else {
SetPublicCache(res, 5 * ONE_MINUTE);
server.getAdverts(req.body, (err, ads) => {
if (err)
returnError(err, res, req);
res.json(ads);
});
}
});
router.get('/v1/advertisements/:adId/', (req, res) => {
let server;
try {
server = getServer(req, res);
}
catch (ex) {
return returnError(ex, res, req);
}
let opts = { adId: req.params['adId'] };
if (req.params['adId']) {
server.getAdvert(opts, (err, ad) => {
if (err)
returnError(err, res, req);
res.json(ad);
});
}
});
router.get('/v1/advertisements/country/:country', (req, res) => {
let server;
let country = req.params['country'];
let opts = { country };
try {
server = getServer(req, res);
}
catch (ex) {
return returnError(ex, res, req);
}
server.getAdvertsByCountry(opts, (err, ads) => {
if (err)
returnError(err, res, req);
res.json(ads);
});
});
router.post('/v1/advertisements/:adId/activate', (req, res) => {
let opts = { adId: req.params['adId'] };
getServerWithAuth(req, res, {
onlyMarketingStaff: true
}, server => {
if (req.params['adId']) {
server.activateAdvert(opts, (err, ad) => {
if (err)
returnError(err, res, req);
res.json({ advertisementId: opts.adId, message: 'advert activated' });
});
}
});
});
router.post('/v1/advertisements/:adId/deactivate', (req, res) => {
let opts = { adId: req.params['adId'] };
getServerWithAuth(req, res, {
onlyMarketingStaff: true
}, server => {
if (req.params['adId']) {
server.deactivateAdvert(opts, (err, ad) => {
if (err)
returnError(err, res, req);
res.json({ advertisementId: opts.adId, message: 'advert deactivated' });
});
}
});
});
router.delete('/v1/advertisements/:adId/', (req, res) => {
getServerWithAuth(req, res, {
onlyMarketingStaff: true
}, server => {
req.body.adId = req.params['adId'];
server.removeAdvert(req.body, (err, removedAd) => {
if (err)
returnError(err, res, req);
if (removedAd) {
res.json(removedAd);
}
});
});
});
router.post('/v1/addresses/', (req, res) => {
logDeprecated(req);
getServerWithAuth(req, res, server => {
server.createAddress({
ignoreMaxGap: true
}, (err, address) => {
if (err)
return returnError(err, res, req);
res.json(address);
});
});
});
router.post('/v2/addresses/', (req, res) => {
logDeprecated(req);
getServerWithAuth(req, res, server => {
server.createAddress({
ignoreMaxGap: true
}, (err, address) => {
if (err)
return returnError(err, res, req);
res.json(address);
});
});
});
router.post('/v3/addresses/', (req, res) => {
getServerWithAuth(req, res, server => {
let opts = req.body;
opts = opts || {};
opts.noCashAddr = true;
server.createAddress(opts, (err, address) => {
if (err)
return returnError(err, res, req);
res.json(address);
});
});
});
router.post('/v4/addresses/', (req, res) => {
getServerWithAuth(req, res, server => {
server.createAddress(req.body, (err, address) => {
if (err)
return returnError(err, res, req);
res.json(address);
});
});
});
router.get('/v1/addresses/', (req, res) => {
getServerWithAuth(req, res, server => {
const opts = {};
if (req.query.limit)
opts.limit = +req.query.limit;
if (req.query.skip)
opts.skip = +req.query.skip;
opts.reverse = req.query.reverse == '1';
server.getMainAddresses(opts, (err, addresses) => {
if (err)
return returnError(err, res, req);
res.json(addresses);
});
});
});
router.get('/v1/balance/', (req, res) => {
getServerWithAuth(req, res, server => {
const opts = {};
if (req.query.coin)
opts.coin = req.query.coin;
if (req.query.twoStep == '1')
opts.twoStep = true;
if (req.query.tokenAddress)
opts.tokenAddress = req.query.tokenAddress;
if (req.query.multisigContractAddress)
opts.multisigContractAddress = req.query.multisigContractAddress;
server.getBalance(opts, (err, balance) => {
if (err)
return returnError(err, res, req);
res.json(balance);
});
});
});
let estimateFeeLimiter;
if (Defaults.RateLimit.estimateFee && !opts.ignoreRateLimiter) {
logger_1.logger.info('Limiting estimate fee per IP: %o req/h', ((Defaults.RateLimit.estimateFee.max / Defaults.RateLimit.estimateFee.windowMs) * 60 * 60 * 1000).toFixed(2));
estimateFeeLimiter = new RateLimit(Defaults.RateLimit.estimateFee);
}
else {
estimateFeeLimiter = (req, res, next) => {
next();
};
}
router.get('/v1/feelevels/', estimateFeeLimiter, (req, res) => {
SetPublicCache(res, 1 * ONE_MINUTE);
logDeprecated(req);
const opts = {};
if (req.query.network)
opts.network = req.query.network;
let server;
try {
server = getServer(req, res);
}
catch (ex) {
return returnError(ex, res, req);
}
server.getFeeLevels(opts, (err, feeLevels) => {
if (err)
return returnError(err, res, req);
lodash_1.default.each(feeLevels, feeLevel => {
feeLevel.feePerKB = feeLevel.feePerKb;
delete feeLevel.feePerKb;
});
res.json(feeLevels);
});
});
router.get('/v2/feelevels/', (req, res) => {
const opts = {};
SetPublicCache(res, 1 * ONE_MINUTE);
if (req.query.coin)
opts.coin = req.query.coin;
if (req.query.chain || req.query.coin)
opts.chain = (req.query.chain || req.query.coin);
if (req.query.network)
opts.network = req.query.network;
let server;
try {
server = getServer(req, res);
}
catch (ex) {
return returnError(ex, res, req);
}
server.getFeeLevels(opts, (err, feeLevels) => {
if (err)
return returnError(err, res, req);
res.json(feeLevels);
});
});
router.post('/v3/estimateGas/', (req, res) => {
getServerWithAuth(req, res, async (server) => {
try {
const gasLimit = await server.estimateGas(req.body);
res.json(gasLimit);
}
catch (err) {
returnError(err, res, req);
}
});
});
router.post('/v1/ethmultisig/', (req, res) => {
getServerWithAuth(req, res, async (server) => {
try {
const multisigContractInstantiationInfo = await server.getMultisigContractInstantiationInfo(req.body);
res.json(multisigContractInstantiationInfo);
}
catch (err) {
returnError(err, res, req);
}
});
});
router.post('/v1/ethmultisig/info', (req, res) => {
getServerWithAuth(req, res, async (server) => {
try {
const multisigContractInfo = await server.getMultisigContractInfo(req.body);
res.json(multisigContractInfo);
}
catch (err) {
returnError(err, res, req);
}
});
});
router.post('/v1/multisig/', (req, res) => {
getServerWithAuth(req, res, async (server) => {
try {
const multisigContractInstantiationInfo = await server.getMultisigContractInstantiationInfo(req.body);
res.json(multisigContractInstantiationInfo);
}
catch (err) {
returnError(err, res, req);
}
});
});
router.post('/v1/multisig/info', (req, res) => {
getServerWithAuth(req, res, async (server) => {
try {
const multisigContractInfo = await server.getMultisigContractInfo(req.body);
res.json(multisigContractInfo);
}
catch (err) {
returnError(err, res, req);
}
});
});
router.post('/v1/token/info', (req, res) => {
getServerWithAuth(req, res, async (server) => {
try {
const tokenContractInfo = await server.getTokenContractInfo(req.body);
res.json(tokenContractInfo);
}
catch (err) {
returnError(err, res, req);
}
});
});
router.get('/v1/sendmaxinfo/', (req, res) => {
getServerWithAuth(req, res, server => {
const q = req.query;
const opts = {};
if (q.feePerKb)
opts.feePerKb = +q.feePerKb;
if (q.feeLevel)
opts.feeLevel = Number(q.feeLevel);
if (q.excludeUnconfirmedUtxos == '1')
opts.excludeUnconfirmedUtxos = true;
if (q.returnInputs == '1')
opts.returnInputs = true;
server.getSendMaxInfo(opts, (err, info) => {
if (err)
return returnError(err, res, req);
res.json(info);
});
});
});
router.get('/v1/utxos/', (req, res) => {
const opts = {};
const addresses = req.query.addresses;
if (addresses && lodash_1.default.isString(addresses))
opts.addresses = req.query.addresses.split(',');
getServerWithAuth(req, res, server => {
server.getUtxos(opts, (err, utxos) => {
if (err)
return returnError(err, res, req);
res.json(utxos);
});
});
});
router.get('/v1/txcoins/', (req, res) => {
const txId = req.query.txId;
getServerWithAuth(req, res, server => {
server.getCoinsForTx({ txId }, (err, coins) => {
if (err)
return returnError(err, res, req);
res.json(coins);
});
});
});
router.post('/v1/broadcast_raw/', (req, res) => {
getServerWithAuth(req, res, server => {
server.broadcastRawTx(req.body, (err, txid) => {
if (err)
return returnError(err, res, req);
res.json(txid);
res.end();
});
});
});
router.post('/v1/txproposals/:id/signatures/', (req, res) => {
getServerWithAuth(req, res, server => {
req.body.maxTxpVersion = 3;
req.body.txProposalId = req.params['id'];
server.signTx(req.body, (err, txp) => {
if (err)
return returnError(err, res, req);
res.json(txp);
res.end();
});
});
});
router.post('/v2/txproposals/:id/signatures/', (req, res) => {
getServerWithAuth(req, res, server => {
req.body.txProposalId = req.params['id'];
req.body.maxTxpVersion = 3;
req.body.supportBchSchnorr = true;
server.signTx(req.body, (err, txp) => {
if (err)
return returnError(err, res, req);
res.json(txp);
res.end();
});
});
});
router.post('/v1/txproposals/:id/publish/', (req, res) => {
getServerWithAuth(req, res, server => {
req.body.txProposalId = req.params['id'];
req.body.noCashAddr = true;
server.publishTx(req.body, (err, txp) => {
if (err)
return returnError(err, res, req);
res.json(txp);
res.end();
});
});
});
router.post('/v2/txproposals/:id/publish/', (req, res) => {
getServerWithAuth(req, res, server => {
req.body.txProposalId = req.params['id'];
server.publishTx(req.body, (err, txp) => {
if (err)
return returnError(err, res, req);
res.json(txp);
res.end();
});
});
});
router.post('/v1/txproposals/:id/broadcast/', (req, res) => {
getServerWithAuth(req, res, server => {
req.body.txProposalId = req.params['id'];
server.broadcastTx(req.body, (err, txp) => {
if (err)
return returnError(err, res, req);
res.json(txp);
res.end();
});
});
});
router.post('/v1/txproposals/:id/rejections', (req, res) => {
getServerWithAuth(req, res, server => {
req.body.txProposalId = req.params['id'];
server.rejectTx(req.body, (err, txp) => {
if (err)
return returnError(err, res, req);
res.json(txp);
res.end();
});
});
});
router.delete('/v1/txproposals/:id/', (req, res) => {
getServerWithAuth(req, res, server => {
req.body.txProposalId = req.params['id'];
server.removePendingTx(req.body, err => {
if (err)
return returnError(err, res, req);
res.json({
success: true
});
res.end();
});
});
});
router.get('/v1/txproposals/:id/', (req, res) => {
getServerWithAuth(req, res, server => {
req.body.txProposalId = req.params['id'];
server.getTx(req.body, (err, tx) => {
if (err)
return returnError(err, res, req);
res.json(tx);
res.end();
});
});
});
router.get('/v1/txproposalsbyhash/:id/', (req, res) => {
getServerWithAuth(req, res, server => {
req.body.txid = req.params['id'];
server.getTxByHash(req.body, (err, tx) => {
if (err)
return returnError(err, res, req);
res.json(tx);
res.end();
});
});
});
router.get('/v1/txhistory/', (req, res) => {
getServerWithAuth(req, res, server => {
const opts = {};
if (req.query.skip)
opts.skip = +req.query.skip;
if (req.query.limit)
opts.limit = +req.query.limit;
if (req.query.tokenAddress)
opts.tokenAddress = req.query.tokenAddress;
if (req.query.multisigContractAddress)
opts.multisigContractAddress = req.query.multisigContractAddress;
if (req.query.includeExtendedInfo == '1')
opts.includeExtendedInfo = true;
server.getTxHistory(opts, (err, txs) => {
if (err)
return returnError(err, res, req);
res.json(txs);
res.end();
});
});
});
router.post('/v1/addresses/scan/', (req, res) => {
getServerWithAuth(req, res, server => {
req.body = req.body || {};
req.body.startIdx = server.copayerIsSupportStaff ? Number(req.body.startIdx) : null;
server.startScan(req.body, (err, started) => {
if (err)
return returnError(err, res, req);
res.json(started);
res.end();
});
});
});
router.get('/v1/stats/', (req, res) => {
SetPublicCache(res, 1 * ONE_MINUTE);
const opts = {};
if (req.query.network)
opts.network = req.query.network;
if (req.query.coin)
opts.coin = req.query.coin;
if (req.query.from)
opts.from = req.query.from;
if (req.query.to)
opts.to = req.query.to;
const stats = new stats_1.Stats(opts);
stats.run((err, data) => {
if (err)
return returnError(err, res, req);
res.json(data);
res.end();
});
});
router.get('/v1/version/', (req, res) => {
SetPublicCache(res, 1 * ONE_MINUTE);
res.json({
serviceVersion: server_1.WalletService.getServiceVersion()
});
res.end();
});
router.post('/v1/login/', (req, res) => {
getServerWithAuth(req, res, server => {
server.login({}, (err, session) => {
if (err)
return returnError(err, res, req);
res.json(session);
});
});
});
router.post('/v1/logout/', (req, res) => {
getServerWithAuth(req, res, server => {
server.logout({}, err => {
if (err)
return returnError(err, res, req);
res.end();
});
});
});
router.get('/v1/notifications/', (req, res) => {
getServerWithAuth(req, res, {
allowSession: true
}, server => {
const timeSpan = req.query.timeSpan
? Math.min(+req.query.timeSpan || 0, Defaults.MAX_NOTIFICATIONS_TIMESPAN)
: Defaults.NOTIFICATIONS_TIMESPAN;
const opts = {
minTs: +Date.now() - timeSpan * 1000,
notificationId: req.query.notificationId
};
server.getNotifications(opts, (err, notifications) => {
if (err)
return returnError(err, res, req);
res.json(notifications);
});
});
});
router.get('/v1/txnotes/:txid', (req, res) => {
getServerWithAuth(req, res, server => {
const opts = {
txid: req.params['txid']
};
server.getTxNote(opts, (err, note) => {
if (err)
return returnError(err, res, req);
res.json(note);
});
});
});
router.put('/v1/txnotes/:txid/', (req, res) => {
req.body.txid = req.params['txid'];
getServerWithAuth(req, res, server => {
server.editTxNote(req.body, (err, note) => {
if (err)
return returnError(err, res, req);
res.json(note);
});
});
});
router.get('/v1/txnotes/', (req, res) => {
getServerWithAuth(req, res, server => {
const opts = {};
if (req.query.minTs && lodash_1.default.isNumber(+req.query.minTs)) {
opts.minTs = +req.query.minTs;
}
server.getTxNotes(opts, (err, notes) => {
if (err)
return returnError(err, res, req);
res.json(notes);
});
});
});
router.get('/v1/nonce/:address', (req, res) => {
getServerWithAuth(req, res, async (server) => {
const opts = {
coin: req.query.coin || 'eth',
chain: req.query.chain,
network: req.query.network || 'livenet',
address: req.params['address']
};
try {
const nonce = await server.getNonce(opts);
res.json(nonce);
}
catch (err) {
returnError(err, res, req);
}
});
});
router.post('/v1/clearcache/', (req, res) => {
getServerWithAuth(req, res, server => {
const opts = req.query;
server.clearWalletCache(opts).then(val => {
if (val) {
res.sendStatus(200);
}
else {
res.sendStatus(500);
}
});
});
});
router.get('/v1/fiatrates/:code/', (req, res) => {
SetPublicCache(res, 5 * ONE_MINUTE);
let server;
const opts = {
code: req.params['code'],
coin: req.query.coin || 'btc',
ts: req.query.ts ? +req.query.ts : null
};
try {
server = getServer(req, res);
}
catch (ex) {
return returnError(ex, res, req);
}
server.getFiatRate(opts, (err, rates) => {
if (err)
return returnError(err, res, req);
res.json(rates);
});
});
router.get('/v2/fiatrates/:code/', (req, res) => {
SetPublicCache(res, 5 * ONE_MINUTE);
let server;
const opts = {
code: req.params['code'],
ts: req.query.ts ? +req.query.ts : null
};
try {
server = getServer(req, res);
}
catch (ex) {
return returnError(ex, res, req);
}
server.getHistoricalRates(opts, (err, rates) => {
if (err)
return returnError(err, res, req);
res.json(rates);
});
});
router.get('/v3/fiatrates/', (req, res) => {
SetPublicCache(res, 5 * ONE_MINUTE);
let server;
const opts = {
code: req.query.code || null,
ts: req.query.ts ? +req.query.ts : null
};
try {
server = getServer(req, res);
}
catch (ex) {
return returnError(ex, res, req);
}
server.getFiatRates(opts, (err, rates) => {
if (err)
return returnError(err, res, req);
res.json(rates);
});
});
router.get('/v3/fiatrates/:coin/', (req, res) => {
SetPublicCache(res, 5 * ONE_MINUTE);