@dioxide-js/silas
Version:
RPC utility for Silas
228 lines (225 loc) • 8.77 kB
JavaScript
import { __awaiter } from '../node_modules/tslib/tslib.es6.mjs';
import { encode as encode$1 } from '../node_modules/base64-arraybuffer/dist/base64-arraybuffer.es5.mjs';
import { s as sha256Exports } from '../_virtual/sha256.mjs';
import base32Encode from '../_virtual/index2.mjs';
import { dataview } from '../node_modules/@dioxide-js/misc/dist/misc.es5.mjs';
import TransactionService from '../api/transactions.mjs';
import { sleep } from '../utils/index.mjs';
import PowDifficulty from '../utils/powDifficulty.mjs';
import OverviewService from '../api/overview.mjs';
import { toUint8Array } from '../utils/buffer.mjs';
import { DIOAddress } from '../utils/address/index.mjs';
import { isValidAddress } from '../utils/validator.mjs';
function encode(data) {
return encode$1(data);
}
class Transaction {
constructor(opts) {
this.alg = 'sm2';
this.n = 0;
this.duration = {
compose: 0,
sign: 0,
verify: 0,
computedNonce: 0,
all: 0,
};
this.getTx = (hash) => __awaiter(this, void 0, void 0, function* () {
return this.txnServices.getTransactionByHash(hash);
});
const { alg = 'sm2', showTxFlow = false, apiKey, n = 0, customSign } = opts;
this.txnServices = new TransactionService({ apiKey });
this.overViewServices = new OverviewService({ apiKey });
this.alg = alg;
this.customSign = customSign;
this.showTxFlow = showTxFlow;
if (typeof n !== 'undefined' && typeof n !== 'number') {
throw 'n muse be number';
}
this.n = n;
}
compose(originalTxn) {
return __awaiter(this, void 0, void 0, function* () {
const ret = yield this.txnServices.compose(originalTxn);
return ret.TxData;
});
}
sign(originalTxn, secretKey, option) {
return __awaiter(this, void 0, void 0, function* () {
const t0 = Date.now();
if (typeof secretKey === 'string') {
secretKey = toUint8Array(secretKey);
}
const dioAddress = new DIOAddress(this.alg, secretKey);
const txdata = yield this.compose(originalTxn);
this.duration.compose = Date.now() - t0;
const t1 = Date.now();
let pk = null;
let longPK = null;
if (dioAddress.alg === 'sm2') {
pk = yield dioAddress.getPubicKeyFromPrivateKey(secretKey);
longPK = dataview.concat(new Uint8Array([4]), pk);
}
else {
longPK = pk = dioAddress.addressToPublicKey(originalTxn.sender);
}
if (!pk) {
throw new Error('pk error');
}
const dataWithPK = dioAddress.insertPKIntoTxData(txdata, [
{ encryptedMethodOrderNumber: dioAddress.methodNum, publicKey: pk },
]);
const raw = encode(dataWithPK);
const signedInfo = this.customSign
? yield this.customSign(dataWithPK, secretKey)
: yield dioAddress.sign(dataWithPK, secretKey, option);
this.duration.sign = Date.now() - t1;
const t2 = Date.now();
this.duration.verify = Date.now() - t2;
const t3 = Date.now();
const finalInfo = dataview.concat(dataWithPK, signedInfo);
const powDiff = new PowDifficulty({
originTxn: finalInfo.buffer,
ttl: originalTxn.ttl,
n: this.n,
debug: this.showTxFlow,
});
const finalInfowithNonce = powDiff.getHashMixinNonnce();
this.duration.computedNonce = Date.now() - t3;
const hash = base32Encode(sha256Exports.sha256.arrayBuffer(finalInfowithNonce), 'Crockford');
this.duration.all = Date.now() - t0;
if (this.showTxFlow) {
console.log('Tx Flow =>', this.duration);
}
return {
composedTxDataWithPK: raw,
signature: encode(signedInfo),
longPK: encode(longPK),
rawTxData: encode(finalInfowithNonce),
hash: hash.toLowerCase(),
pk: encode(pk),
txFlow: this.duration,
nonceTime: powDiff.t,
};
});
}
send(secretKey, originTxn) {
return __awaiter(this, void 0, void 0, function* () {
const { rawTxData } = yield this.sign(originTxn, secretKey);
const ret = yield this.txnServices.sendTransaction({
txdata: rawTxData,
});
return ret.Hash;
});
}
sendRawTx(rawTxData) {
return __awaiter(this, void 0, void 0, function* () {
const ret = yield this.txnServices.sendTransaction({
txdata: rawTxData,
});
return ret.Hash;
});
}
getEstimatedFee(originTxn) {
return __awaiter(this, void 0, void 0, function* () {
const { function: func, args, delegatee, scale = 3, tokens } = originTxn;
const overview = yield this.overViewServices.chainStatus();
const avgGasPrice = (overview === null || overview === void 0 ? void 0 : overview.AvgGasPrice) || 0;
const to = args.to || args.To;
const ret = yield this.txnServices.compose({
sender: to,
gasprice: avgGasPrice,
delegatee: delegatee,
function: func,
args,
tokens,
});
const gasLimit = ret.GasOffered.toString();
const gasFee = this.calculateGasFee({
average: avgGasPrice,
scale: scale,
gasLimit: Number(gasLimit),
});
return gasFee;
});
}
calculateGasFee(options) {
const { average, scale = 3, gasLimit } = options;
const gasPrice = parseInt(((scale - 1) * 0.25 + 0.5) * average + '', 10);
const gasFee = gasPrice * gasLimit;
return gasFee;
}
transfer(params) {
return __awaiter(this, void 0, void 0, function* () {
const { to, amount, secretKey, ttl, sender } = params;
if (!sender) {
throw 'sender unfilled';
}
if (!isValidAddress(sender)) {
throw 'invalid sender';
}
return this.send(secretKey, {
sender,
gasprice: 100,
function: 'core.coin.transfer',
args: {
To: to,
Amount: amount,
},
ttl,
});
});
}
sk2base32Address(sk, alg) {
return __awaiter(this, void 0, void 0, function* () {
if (typeof sk === 'string') {
sk = toUint8Array(sk);
}
const dioAddress = new DIOAddress(alg, sk);
const { address } = yield dioAddress.generate();
return address.toLowerCase();
});
}
sureFinalized(hash, options) {
return __awaiter(this, void 0, void 0, function* () {
const holdOn = ['Not found'];
const { max = 10, verbose = false, interval = 1 } = options || {};
const trace = (message) => {
const indicator = `[surefinalized] => ${hash} `;
if (verbose) {
console.log(indicator + message);
}
};
let loop = 0;
while (true) {
loop++;
if (loop > max) {
trace(`stop because execeed max limit(${loop})`);
return false;
}
trace(`continue(${loop})`);
yield sleep(interval);
try {
const detail = yield this.getTx(hash);
if (!(detail === null || detail === void 0 ? void 0 : detail.Content)) {
continue;
}
const { Content } = detail;
const { Invocation } = Content;
if (Invocation.Return[0] === 0) {
return Content;
}
return false;
}
catch (ex) {
if (typeof ex === 'string' && holdOn.some((cause) => cause === ex)) {
continue;
}
return false;
}
}
});
}
}
export { Transaction };
//# sourceMappingURL=transaction.mjs.map