PHP WebShell
Текущая директория: /opt/BitGoJS/modules/sdk-coin-algo/src/lib
Просмотр файла: transaction.ts
import { BaseCoin as CoinConfig } from '@bitgo/statics';
import algosdk from 'algosdk';
import {
BaseTransaction,
TransactionType,
BaseKey,
InvalidTransactionError,
InvalidKey,
SigningError,
} from '@bitgo/sdk-core';
import utils from './utils';
import { KeyPair } from './keyPair';
import { TxData } from './ifaces';
export class Transaction extends BaseTransaction {
private _algoTransaction?: algosdk.Transaction;
private _signedTransaction?: Uint8Array;
private _numberOfRequiredSigners: number;
private _sender: string;
private _signers: string[];
private _signedBy: string[];
constructor(coinConfig: Readonly<CoinConfig>) {
super(coinConfig);
this._numberOfRequiredSigners = 0;
this._signers = [];
}
/** @inheritdoc */
canSign({ key }: BaseKey): boolean {
if (this._numberOfRequiredSigners === 0) {
return false;
}
if (this._numberOfRequiredSigners === 1) {
const kp = new KeyPair({ prv: key });
const addr = kp.getAddress();
if (addr === this._sender) {
return true;
} else {
return false;
}
} else {
return true;
}
}
sender(address: string): void {
this._sender = address;
}
/**
* Signs transaction.
*
* @param {KeyPair} keyPair Signer keys.
*/
sign(keyPair: KeyPair[]): void {
if (!this._algoTransaction) {
throw new InvalidTransactionError('Empty transaction');
}
if (this._numberOfRequiredSigners === 1) {
this.signSingle(keyPair[0]);
} else if (this._numberOfRequiredSigners > 1) {
this.signMultiSig(keyPair);
}
}
/**
* Signs transaction.
*
* @param {KeyPair} keyPair Signer keys.
*/
private signSingle(keyPair: KeyPair): void {
if (!this._algoTransaction) {
throw new InvalidTransactionError('Empty transaction');
}
const signKey = Buffer.from(keyPair.getSigningKey()).toString('hex');
if (signKey) {
this._signedTransaction = algosdk.signTransaction(this._algoTransaction, utils.toUint8Array(signKey)).blob;
} else {
throw new InvalidKey('Private key undefined');
}
}
/**
* Signs multisig transaction.
*
* @param {KeyPair} keyPair Signers keys.
*/
private signMultiSig(keyPair: KeyPair[]): void {
if (!this._algoTransaction) {
throw new InvalidTransactionError('Empty transaction');
}
if (this._signers.length === 0) {
throw new SigningError('Signers not specified for multisig');
}
if (keyPair.length === 0) {
throw new SigningError('Keypair not specified for multisig');
}
const multiSigOptions = {
version: 1,
threshold: this._numberOfRequiredSigners,
addrs: this._signers,
};
const msigAddress = algosdk.multisigAddress(multiSigOptions);
this._algoTransaction.from = algosdk.decodeAddress(msigAddress);
// Check if it is a signed or unsigned tx.
// If unsigned, sign it using first keypair and then append next signatures.
// If signed, appending next signatures.
let signedTx = this._signedTransaction
? this._signedTransaction
: algosdk.signMultisigTransaction(this._algoTransaction, multiSigOptions, keyPair.shift()!.getSigningKey()).blob;
keyPair.forEach((kp) => {
signedTx = algosdk.appendSignMultisigTransaction(signedTx, multiSigOptions, kp.getSigningKey()).blob;
});
this._signedTransaction = signedTx;
}
set signedTransaction(txn: Uint8Array) {
this._signedTransaction = txn;
}
get numberOfRequiredSigners(): number {
return this._numberOfRequiredSigners;
}
/**
* Sets the number of signers required for signing this transaction.
*
* @param {number} num Threshold number of signers.
*/
setNumberOfRequiredSigners(num: number): void {
this._numberOfRequiredSigners = num;
}
set signers(addrs: string[]) {
this._signers = addrs;
}
get signers(): string[] {
return this._signers;
}
set signedBy(signer: string[]) {
this._signedBy = signer;
}
get signedBy(): string[] {
return this._signedBy;
}
/**
* Sets algo transaction.
*
* @param {algosdk.Transaction} tx
*/
setAlgoTransaction(tx: algosdk.Transaction): void {
this._algoTransaction = tx;
}
/**
* Get underlaying algo transaction.
*
* @returns {algosdk.Transaction}
*/
getAlgoTransaction(): algosdk.Transaction | undefined {
return this._algoTransaction;
}
/**
* Set the transaction type.
*
* @param {TransactionType} transactionType The transaction type to be set.
*/
setTransactionType(transactionType: TransactionType): void {
this._type = transactionType;
}
estimateSize(): number {
if (!this._algoTransaction) {
throw new InvalidTransactionError('Empty transaction');
}
return this._algoTransaction.estimateSize();
}
/** @inheritdoc */
toBroadcastFormat(): Uint8Array {
if (!this._algoTransaction) {
throw new InvalidTransactionError('Empty transaction');
}
if (this._signedTransaction && this._signedTransaction.length > 0) {
return this._signedTransaction;
} else {
return algosdk.encodeUnsignedTransaction(this._algoTransaction);
}
}
/** @inheritdoc */
toJson(): TxData {
if (!this._algoTransaction) {
throw new InvalidTransactionError('Empty transaction');
}
const result: TxData = {
id: this._algoTransaction.txID(),
type: this._algoTransaction.type?.toString(),
from: algosdk.encodeAddress(this._algoTransaction.from.publicKey),
fee: this._algoTransaction.fee,
firstRound: this._algoTransaction.firstRound,
lastRound: this._algoTransaction.lastRound,
note: this._algoTransaction.note,
tokenId: this._algoTransaction?.assetIndex,
genesisID: this._algoTransaction.genesisID,
genesisHash: this._algoTransaction.genesisHash.toString('base64'),
};
if (this._algoTransaction.closeRemainderTo) {
result.closeRemainderTo = algosdk.encodeAddress(this._algoTransaction.closeRemainderTo.publicKey);
}
if (this.type === TransactionType.Send) {
result.to = algosdk.encodeAddress(this._algoTransaction.to.publicKey);
result.amount = this._algoTransaction.amount.toString();
}
if (this.type === TransactionType.WalletInitialization) {
if (!this._algoTransaction.nonParticipation) {
if (
this._algoTransaction.voteKey &&
this._algoTransaction.selectionKey &&
this._algoTransaction.voteFirst &&
this._algoTransaction.voteLast &&
this._algoTransaction.voteKeyDilution
) {
result.voteKey = this._algoTransaction.voteKey.toString('base64');
result.selectionKey = this._algoTransaction.selectionKey.toString('base64');
result.voteFirst = this._algoTransaction.voteFirst;
result.voteLast = this._algoTransaction.voteLast;
result.voteKeyDilution = this._algoTransaction.voteKeyDilution;
if (this._algoTransaction.stateProofKey) {
result.stateProofKey = this._algoTransaction.stateProofKey.toString('base64');
}
}
} else {
result.nonParticipation = this._algoTransaction.nonParticipation;
}
}
if (result.type === 'axfer' && result.to && result.amount) {
result.txType = utils.getTokenTxType(result.amount, result.from, result.to, result.closeRemainderTo);
result.tokenName = this._coinConfig.suffix;
}
return result;
}
/**
* Load the input and output data on this transaction.
*/
loadInputsAndOutputs(): void {
if (!this._algoTransaction) {
return;
}
if (this.type === TransactionType.Send) {
this._outputs = [
{
address: algosdk.encodeAddress(this._algoTransaction.to.publicKey),
value: this._algoTransaction.amount.toString(),
coin: this._coinConfig.name,
},
];
this._inputs = [
{
address: algosdk.encodeAddress(this._algoTransaction.from.publicKey),
value: this._algoTransaction.amount.toString(),
coin: this._coinConfig.name,
},
];
}
}
}
Выполнить команду
Для локальной разработки. Не используйте в интернете!