@vechain/connex-driver
Version:
Connex framework driver implementation
179 lines • 16 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { DriverNoVendor } from './driver-no-vendor';
import { Transaction, Certificate, blake2b256 } from 'thor-devkit';
import { randomBytes } from 'crypto';
import BigNumber from 'bignumber.js';
/** class fully implements DriverInterface */
export class Driver extends DriverNoVendor {
/**
* create driver instance
* it will fetch config(genesis, head) via net as construction params
* @param net
* @param wallet
*/
static connect(net, wallet) {
return __awaiter(this, void 0, void 0, function* () {
const genesis = yield net.http('GET', 'blocks/0');
const best = yield net.http('GET', 'blocks/best', {
validateResponseHeader: headers => {
const xgid = headers['x-genesis-id'];
if (xgid && xgid !== genesis.id) {
throw new Error(`responded 'x-genesis-id' not matched`);
}
}
});
const head = {
id: best.id,
number: best.number,
timestamp: best.timestamp,
parentID: best.parentID,
txsFeatures: best.txsFeatures,
gasLimit: best.gasLimit,
};
if (best.baseFeePerGas) {
head.baseFeePerGas = best.baseFeePerGas;
}
return new Driver(net, genesis, head, wallet);
});
}
constructor(net, genesis, initialHead, wallet) {
super(net, genesis, initialHead);
this.wallet = wallet;
/** params for tx construction */
this.txParams = {
expiration: 18,
gasPriceCoef: 0,
maxPriorityFeePerGas: 0,
txType: TransactionType.DynamicFee
};
}
signTx(msg, options) {
return __awaiter(this, void 0, void 0, function* () {
options.onAccepted && options.onAccepted();
const key = this.findKey(options.signer);
const clauses = msg.map(c => ({
to: c.to ? c.to.toLowerCase() : null,
value: c.value.toString().toLowerCase(),
data: (c.data || '0x').toLowerCase(),
}));
const gas = options.gas || (yield this.estimateGas(clauses, key.address));
// Base transaction body
const baseTxBody = {
chainTag: Number.parseInt(this.genesis.id.slice(-2), 16),
blockRef: this.head.id.slice(0, 18),
expiration: this.txParams.expiration,
clauses,
gas,
dependsOn: options.dependsOn || null,
nonce: '0x' + randomBytes(8).toString('hex')
};
// Determine transaction type and create appropriate body
let txType = this.txParams.txType;
if (txType === TransactionType.DynamicFee && !this.head.baseFeePerGas) {
// If baseFeePerGas is not available, means dynamic fee is not enabled
// in the current block, fallback to legacy transaction
txType = TransactionType.Legacy;
}
let txBody;
if (txType === TransactionType.DynamicFee) {
// Dynamic fee transaction
txBody = Object.assign(Object.assign({}, baseTxBody), { type: TransactionType.DynamicFee, maxPriorityFeePerGas: this.txParams.maxPriorityFeePerGas.toString(), maxFeePerGas: new BigNumber(this.txParams.maxPriorityFeePerGas).plus(this.head.baseFeePerGas).toString() });
}
else {
// Legacy transaction
txBody = Object.assign(Object.assign({}, baseTxBody), { type: TransactionType.Legacy, gasPriceCoef: this.txParams.gasPriceCoef });
}
let tx;
if (options.delegator) {
const delegatedTx = new Transaction(Object.assign(Object.assign({}, txBody), { reserved: { features: 1 /* vip191 */ } }));
const originSig = yield key.sign(delegatedTx.signingHash());
const unsigned = {
raw: '0x' + delegatedTx.encode().toString('hex'),
origin: key.address
};
try {
const result = yield this.net.http('POST', options.delegator.url, { body: unsigned });
delegatedTx.signature = Buffer.concat([originSig, Buffer.from(result.signature.slice(2), 'hex')]);
tx = delegatedTx;
}
catch (err) {
// tslint:disable-next-line: no-console
console.warn('tx delegation error: ', err);
// fallback to non-vip191 tx
}
}
if (!tx) {
tx = new Transaction(txBody);
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
tx.signature = yield key.sign(tx.signingHash());
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
const raw = `0x${tx.encode().toString('hex')}`;
if (this.onTxCommit) {
this.onTxCommit({
id: tx.id,
raw,
resend: () => __awaiter(this, void 0, void 0, function* () {
yield this.sendTx(raw);
})
});
}
yield this.sendTx(raw);
return {
txid: tx.id,
signer: key.address
};
});
}
signCert(msg, options) {
return __awaiter(this, void 0, void 0, function* () {
options.onAccepted && options.onAccepted();
const key = this.findKey(options.signer);
const annex = {
domain: 'localhost',
timestamp: this.head.timestamp,
signer: key.address
};
const unsigned = Certificate.encode(Object.assign(Object.assign({}, msg), annex));
const signature = yield key.sign(blake2b256(unsigned));
return {
annex,
signature: '0x' + signature.toString('hex')
};
});
}
findKey(addr) {
if (this.wallet) {
const keys = this.wallet.list;
const key = addr ? keys.find(k => k.address === addr) : keys[0];
if (key) {
return key;
}
}
throw new Error('empty wallet');
}
sendTx(raw) {
return this.httpPost('transactions', { raw });
}
estimateGas(clauses, caller) {
return __awaiter(this, void 0, void 0, function* () {
const outputs = yield this.explain({
clauses,
caller,
}, this.head.id);
const execGas = outputs.reduce((sum, out) => sum + out.gasUsed, 0);
const intrinsicGas = Transaction.intrinsicGas(clauses);
return intrinsicGas + (execGas ? (execGas + 15000) : 0);
});
}
}
export const TransactionType = Transaction.Type;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHJpdmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2RyaXZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7QUFDQSxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sb0JBQW9CLENBQUE7QUFFbkQsT0FBTyxFQUFFLFdBQVcsRUFBRSxXQUFXLEVBQUUsVUFBVSxFQUFFLE1BQU0sYUFBYSxDQUFBO0FBQ2xFLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxRQUFRLENBQUE7QUFDcEMsT0FBTyxTQUFTLE1BQU0sY0FBYyxDQUFBO0FBRXBDLDZDQUE2QztBQUM3QyxNQUFNLE9BQU8sTUFBTyxTQUFRLGNBQWM7SUFDdEM7Ozs7O09BS0c7SUFDSSxNQUFNLENBQU8sT0FBTyxDQUFDLEdBQVEsRUFBRSxNQUFlOztZQUNqRCxNQUFNLE9BQU8sR0FBc0IsTUFBTSxHQUFHLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxVQUFVLENBQUMsQ0FBQTtZQUNwRSxNQUFNLElBQUksR0FBc0IsTUFBTSxHQUFHLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxhQUFhLEVBQUU7Z0JBQ2pFLHNCQUFzQixFQUFFLE9BQU8sQ0FBQyxFQUFFO29CQUM5QixNQUFNLElBQUksR0FBRyxPQUFPLENBQUMsY0FBYyxDQUFDLENBQUE7b0JBQ3BDLElBQUksSUFBSSxJQUFJLElBQUksS0FBSyxPQUFPLENBQUMsRUFBRSxFQUFFLENBQUM7d0JBQzlCLE1BQU0sSUFBSSxLQUFLLENBQUMsc0NBQXNDLENBQUMsQ0FBQTtvQkFDM0QsQ0FBQztnQkFDTCxDQUFDO2FBQ0osQ0FBQyxDQUFBO1lBRUYsTUFBTSxJQUFJLEdBQStCO2dCQUNyQyxFQUFFLEVBQUUsSUFBSSxDQUFDLEVBQUU7Z0JBQ1gsTUFBTSxFQUFFLElBQUksQ0FBQyxNQUFNO2dCQUNuQixTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVM7Z0JBQ3pCLFFBQVEsRUFBRSxJQUFJLENBQUMsUUFBUTtnQkFDdkIs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