PHP WebShell
Текущая директория: /opt/BitGoJS/modules/sdk-api/src/v1
Просмотр файла: pendingapproval.ts
/**
* @hidden
*/
/**
*/
// Pending Approval Object
// Handles approving, rejecting and getting information on pending approvals
//
// Copyright 2015, BitGo, Inc. All Rights Reserved.
//
import { common } from '@bitgo/sdk-core';
import * as utxolib from '@bitgo/utxo-lib';
import _ from 'lodash';
import { tryPromise } from '../util';
//
// Constructor
//
const PendingApproval = function (bitgo, pendingApproval, wallet) {
// @ts-expect-error - no implicit this
this.bitgo = bitgo;
// @ts-expect-error - no implicit this
this.pendingApproval = pendingApproval;
// @ts-expect-error - no implicit this
this.wallet = wallet;
};
//
// id
// Get the id of this pending approval.
//
PendingApproval.prototype.id = function () {
return this.pendingApproval.id;
};
//
// ownerType
// Get the owner type (wallet or enterprise)
// Pending approvals can be approved or modified by different scopes (depending on how they were created)
// If a pending approval is owned by a wallet, then it can be approved by administrators of the wallet
// If a pending approval is owned by an enterprise, then it can be approved by administrators of the enterprise
//
PendingApproval.prototype.ownerType = function (params, callback) {
params = params || {};
common.validateParams(params, [], [], callback);
if (this.pendingApproval.walletId) {
return 'wallet';
} else if (this.pendingApproval.enterprise) {
return 'enterprise';
} else {
throw new Error('unexpected pending approval owner: neither walletId nor enterprise was present');
}
};
//
// walletId
// Get the wallet ID that owns / is associated with the pending approval
//
PendingApproval.prototype.walletId = function () {
return this.pendingApproval.walletId;
};
//
// enterpriseId
// Get the enterprise ID that owns / is associated with the pending approval
//
PendingApproval.prototype.enterpriseId = function () {
return this.pendingApproval.enterprise;
};
//
// state
// Get the state of the pending approval
//
PendingApproval.prototype.state = function () {
return this.pendingApproval.state;
};
//
// creator
// Get the id of the user that performed the action resulting in this pending approval
//
PendingApproval.prototype.creator = function () {
return this.pendingApproval.creator;
};
//
// type
// Get the type of the pending approval (what it approves)
// Example: transactionRequest, tagUpdateRequest, policyRuleRequest
//
PendingApproval.prototype.type = function () {
if (!this.pendingApproval.info) {
throw new Error('pending approval info is not available');
}
return this.pendingApproval.info.type;
};
//
// type
// Get information about the pending approval
//
PendingApproval.prototype.info = function () {
return this.pendingApproval.info;
};
//
// approvalsRequired
// get the number of approvals that are required for this pending approval to be approved.
// Defaults to 1 if approvalsRequired doesn't exist on the object
//
PendingApproval.prototype.approvalsRequired = function () {
return this.pendingApproval.approvalsRequired || 1;
};
//
// url
// Gets the url for this pending approval
//
PendingApproval.prototype.url = function (extra) {
extra = extra || '';
return this.bitgo.url('/pendingapprovals/' + this.id() + extra);
};
//
// get
// Refetches this pending approval and returns it
//
PendingApproval.prototype.get = function (params, callback) {
params = params || {};
common.validateParams(params, [], [], callback);
const self = this;
return Promise.resolve(this.bitgo.get(this.url()).result())
.then(function (res) {
self.pendingApproval = res;
return self;
})
.then(callback)
.catch(callback);
};
//
// Helper function to ensure that self.wallet is set
//
PendingApproval.prototype.populateWallet = function () {
const self = this;
if (!self.wallet) {
return self.bitgo
.wallets()
.get({ id: self.info().transactionRequest.sourceWallet })
.then(function (wallet) {
if (!wallet) {
throw new Error('unexpected - unable to get wallet using sourcewallet');
}
self.wallet = wallet;
});
}
if (self.wallet.id() !== self.info().transactionRequest.sourceWallet) {
throw new Error('unexpected source wallet for pending approval');
}
return Promise.resolve(); // otherwise returns undefined
};
//
// helper function to recreate and sign a transaction on a wallet
// we should hopefully be able to move this logic server side soon
//
PendingApproval.prototype.recreateAndSignTransaction = function (params, callback) {
params = _.extend({}, params);
common.validateParams(params, ['txHex'], [], callback);
const transaction = utxolib.bitgo.createTransactionFromHex(params.txHex, utxolib.networks.bitcoin);
if (!transaction.outs) {
throw new Error('transaction had no outputs or failed to parse successfully');
}
const network = utxolib.networks[common.Environments[this.bitgo.getEnv()].network];
params.recipients = {};
const self = this;
return tryPromise(function () {
if (self.info().transactionRequest.recipients) {
// recipients object found on the pending approvals - use it
params.recipients = self.info().transactionRequest.recipients;
return;
}
if (transaction.outs.length <= 2) {
transaction.outs.forEach(function (out) {
const outAddress = utxolib.address.fromOutputScript(out.script, network);
if (self.info().transactionRequest.destinationAddress === outAddress) {
// If this is the destination, then spend to it
params.recipients[outAddress] = out.value;
}
});
return;
}
// This looks like a sendmany
// Attempt to figure out the outputs by choosing all outputs that were not going back to the wallet as change addresses
return self.wallet.addresses({ chain: 1, sort: -1, limit: 500 }).then(function (result) {
const changeAddresses = _.keyBy(result.addresses, 'address');
transaction.outs.forEach(function (out) {
const outAddress = utxolib.address.fromOutputScript(out.script, network);
if (!changeAddresses[outAddress]) {
// If this is not a change address, then spend to it
params.recipients[outAddress] = out.value;
}
});
});
}).then(function () {
return self.wallet.createAndSignTransaction(params);
});
};
//
// constructApprovalTx
// constructs/signs a transaction for this pending approval, returning the txHex (but not sending it)
//
PendingApproval.prototype.constructApprovalTx = function (params, callback) {
params = params || {};
common.validateParams(params, [], ['walletPassphrase'], callback);
if (this.type() === 'transactionRequest' && !(params.walletPassphrase || params.xprv)) {
throw new Error('wallet passphrase or xprv required to approve a transactionRequest');
}
if (params.useOriginalFee) {
if (!_.isBoolean(params.useOriginalFee)) {
throw new Error('invalid type for useOriginalFeeRate');
}
if (params.fee || params.feeRate || params.feeTxConfirmTarget) {
throw new Error('cannot specify a fee/feerate/feeTxConfirmTarget as well as useOriginalFee');
}
}
const self = this;
return tryPromise(function () {
if (self.type() === 'transactionRequest') {
const extendParams: any = { txHex: self.info().transactionRequest.transaction };
if (params.useOriginalFee) {
extendParams.fee = self.info().transactionRequest.fee;
}
return self.populateWallet().then(function () {
return self.recreateAndSignTransaction(_.extend(params, extendParams));
});
}
});
};
//
// approve
// sets the pending approval to an approved state
//
PendingApproval.prototype.approve = function (params, callback) {
params = params || {};
common.validateParams(params, [], ['walletPassphrase', 'otp'], callback);
let canRecreateTransaction = true;
if (this.type() === 'transactionRequest') {
if (!params.walletPassphrase && !params.xprv) {
canRecreateTransaction = false;
}
// check the wallet balance and compare it with the transaction amount and fee
if (_.isUndefined(params.forceRecreate) && _.isObject(_.get(this, 'wallet.wallet'))) {
const requestedAmount = this.pendingApproval.info.transactionRequest.requestedAmount || 0;
const walletBalance = this.wallet.wallet.spendableBalance;
const delta = Math.abs(requestedAmount - walletBalance);
if (delta <= 10000) {
// it's a sweep because we're within 10k satoshis of the wallet balance
canRecreateTransaction = false;
}
}
}
const self = this;
return tryPromise(function () {
if (self.type() === 'transactionRequest') {
if (params.tx) {
// the approval tx was reconstructed and explicitly specified - pass it through
return {
tx: params.tx,
};
}
// this user may not have spending privileges or a passphrase may not have been passed in
if (!canRecreateTransaction) {
return {
tx: self.info().transactionRequest.transaction,
};
}
return self.populateWallet().then(function () {
const recreationParams = _.extend(
{},
params,
{ txHex: self.info().transactionRequest.transaction },
self.info().transactionRequest.buildParams
);
// delete the old build params because we want 'recreateAndSign' to recreate the transaction
delete recreationParams.fee;
delete recreationParams.unspents;
delete recreationParams.txInfo;
delete recreationParams.estimatedSize;
delete recreationParams.changeAddresses;
return self.recreateAndSignTransaction(recreationParams);
});
}
})
.then(function (transaction) {
const approvalParams: any = { state: 'approved', otp: params.otp };
if (transaction) {
approvalParams.tx = transaction.tx;
}
return Promise.resolve(self.bitgo.put(self.url()).send(approvalParams).result()).then(callback).catch(callback);
})
.catch(function (error) {
if (
!canRecreateTransaction &&
(error.message.indexOf('could not find unspent output for input') !== -1 ||
error.message.indexOf('transaction conflicts with an existing transaction in the send queue') !== -1)
) {
throw new Error('unspents expired, wallet passphrase or xprv required to recreate transaction');
}
if (
_.isUndefined(params.forceRecreate) &&
error.message.indexOf('could not find unspent output for input') !== -1
) {
// if the unspents can't be found, we must retry with a newly constructed transaction, so we delete the tx and try again
// deleting params.tx will force the code to reach the 'recreateAndSignTransaction' function
delete params.tx;
params.forceRecreate = true;
self.approve(params, callback);
} else {
throw error;
}
});
};
//
// rejected
// sets the pending approval to a rejected state
//
PendingApproval.prototype.reject = function (params, callback) {
params = params || {};
common.validateParams(params, [], [], callback);
return Promise.resolve(this.bitgo.put(this.url()).send({ state: 'rejected' }).result())
.then(callback)
.catch(callback);
};
//
// cancel
// rejects the pending approval
//
PendingApproval.prototype.cancel = function (params, callback) {
return this.reject(params, callback);
};
export = PendingApproval;
Выполнить команду
Для локальной разработки. Не используйте в интернете!