@fioprotocol/fiosdk
Version:
The Foundation for Interwallet Operability (FIO) is a consortium of leading blockchain wallets, exchanges and payments providers that seeks to accelerate blockchain adoption by reducing the risk, complexity, and inconvenience of sending and receiving cryp
355 lines • 15.9 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());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Transactions = exports.FIO_BLOCK_NUMBER_ERROR_CODE = exports.FIO_CHAIN_INFO_ERROR_CODE = exports.fioApiErrorCodes = exports.signAllAuthorityProvider = void 0;
const fiojs_1 = require("@fioprotocol/fiojs");
const chain_jssig_1 = require("@fioprotocol/fiojs/dist/chain-jssig");
const chain_numeric_1 = require("@fioprotocol/fiojs/dist/chain-numeric");
const text_encoding_1 = require("text-encoding");
const entities_1 = require("../entities");
const constants_1 = require("../utils/constants");
const utils_1 = require("../utils/utils");
const validation_1 = require("../utils/validation");
exports.signAllAuthorityProvider = {
getRequiredKeys(authorityProviderArgs) {
return __awaiter(this, void 0, void 0, function* () {
const { availableKeys } = authorityProviderArgs;
return availableKeys;
});
},
};
exports.fioApiErrorCodes = [constants_1.API_ERROR_CODES.BAD_REQUEST, constants_1.API_ERROR_CODES.FORBIDDEN, constants_1.API_ERROR_CODES.NOT_FOUND, constants_1.API_ERROR_CODES.CONFLICT];
exports.FIO_CHAIN_INFO_ERROR_CODE = 800;
exports.FIO_BLOCK_NUMBER_ERROR_CODE = 801;
class Transactions {
constructor(config) {
this.config = config;
this.publicKey = '';
this.privateKey = '';
this.validationData = {};
this.validationRules = null;
this.expirationOffset = constants_1.defaultExpirationOffset;
}
getActor(publicKey = '') {
return this.config.fioProvider.accountHash((publicKey === '' || !publicKey) ? this.publicKey : publicKey);
}
getChainInfo() {
return __awaiter(this, void 0, void 0, function* () {
const options = {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
method: 'GET',
};
return yield this.multicastServers({ endpoint: `chain/${entities_1.EndPoint.getInfo}`, fetchOptions: options });
});
}
getBlock(chain) {
return __awaiter(this, void 0, void 0, function* () {
if (chain === undefined || !chain) {
throw new Error('chain undefined');
}
if (chain.last_irreversible_block_num === undefined) {
throw new Error('chain.last_irreversible_block_num undefined');
}
return yield this.multicastServers({
endpoint: `chain/${entities_1.EndPoint.getBlock}`, fetchOptions: {
body: JSON.stringify({
block_num_or_id: chain.last_irreversible_block_num,
}),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
method: 'POST',
},
});
});
}
getChainDataForTx() {
return __awaiter(this, void 0, void 0, function* () {
let chain;
let block;
try {
chain = yield this.getChainInfo();
}
catch (error) {
if (error.name === 'ValidationError') {
throw error;
}
// tslint:disable-next-line:no-console
console.error('chain:: ' + error);
const e = new Error(`Error while fetching chain info`);
e.errorCode = exports.FIO_CHAIN_INFO_ERROR_CODE;
throw e;
}
try {
block = yield this.getBlock(chain);
}
catch (error) {
// tslint:disable-next-line:no-console
console.error('block: ' + error);
const e = new Error(`Error while fetching block`);
e.errorCode = exports.FIO_BLOCK_NUMBER_ERROR_CODE;
throw e;
}
const expiration = new Date(chain.head_block_time + 'Z');
expiration.setSeconds(expiration.getSeconds() + this.expirationOffset);
const expirationStr = expiration.toISOString();
return {
chain_id: chain.chain_id,
expiration: expirationStr.substring(0, expirationStr.length - 1),
// tslint:disable-next-line:no-bitwise
ref_block_num: block.block_num & 0xFFFF,
ref_block_prefix: block.ref_block_prefix,
};
});
}
setRawRequestExp(rawRequest, chainData) {
rawRequest.ref_block_num = chainData.ref_block_num;
rawRequest.ref_block_prefix = chainData.ref_block_prefix;
rawRequest.expiration = chainData.expiration;
}
generateApiProvider(abiMap) {
return {
getRawAbi(accountName) {
return __awaiter(this, void 0, void 0, function* () {
const rawAbi = abiMap.get(accountName);
if (!rawAbi) {
throw new Error(`Missing ABI for account ${accountName}`);
}
const abi = (0, chain_numeric_1.base64ToBinary)(rawAbi.abi);
const binaryAbi = { accountName: rawAbi.account_name, abi };
return binaryAbi;
});
},
};
}
initFioJsApi({ chainId, abiMap, textDecoder = utils_1.defaultTextDecoder, textEncoder = utils_1.defaultTextEncoder, privateKeys, }) {
return new fiojs_1.Api({
abiProvider: this.generateApiProvider(abiMap),
authorityProvider: exports.signAllAuthorityProvider,
chainId,
signatureProvider: new chain_jssig_1.JsSignatureProvider(privateKeys),
textDecoder,
textEncoder,
});
}
createRawTransaction(_a) {
return __awaiter(this, arguments, void 0, function* ({ account, action, authPermission, data, publicKey, chainData, signingAccount }) {
const actor = this.getActor(publicKey);
if (!data.actor) {
data.actor = actor;
}
const rawTransaction = (0, utils_1.createRawRequest)({
actions: [
(0, utils_1.createRawAction)({
account,
actor: signingAccount,
authorization: [(0, utils_1.createAuthorization)(data.actor, authPermission)],
data,
name: action,
}),
],
});
if (chainData && chainData.ref_block_num) {
this.setRawRequestExp(rawTransaction, chainData);
}
return rawTransaction;
});
}
serialize(_a) {
return __awaiter(this, arguments, void 0, function* ({ chainId, abiMap = Transactions.abiMap, transaction, textDecoder = utils_1.defaultTextDecoder, textEncoder = utils_1.defaultTextEncoder, }) {
const api = this.initFioJsApi({
abiMap,
chainId,
privateKeys: [],
textDecoder,
textEncoder,
});
return yield api.transact(transaction, { sign: false });
});
}
deserialize(_a) {
return __awaiter(this, arguments, void 0, function* ({ chainId, abiMap = Transactions.abiMap, serializedTransaction, textDecoder = utils_1.defaultTextDecoder, textEncoder = utils_1.defaultTextEncoder, }) {
const api = this.initFioJsApi({
abiMap,
chainId,
privateKeys: [],
textDecoder,
textEncoder,
});
return yield api.deserializeTransactionWithActions(serializedTransaction);
});
}
sign(_a) {
return __awaiter(this, arguments, void 0, function* ({ abiMap = Transactions.abiMap, chainId, privateKeys, transaction, serializedTransaction, serializedContextFreeData, }) {
const signatureProvider = new chain_jssig_1.JsSignatureProvider(privateKeys);
const availableKeys = yield signatureProvider.getAvailableKeys();
const requiredKeys = yield exports.signAllAuthorityProvider.getRequiredKeys({ transaction, availableKeys });
const api = this.initFioJsApi({
abiMap,
chainId,
privateKeys,
});
const abis = yield api.getTransactionAbis(transaction);
const signedTx = yield signatureProvider.sign({
abis,
chainId,
requiredKeys,
serializedContextFreeData,
serializedTransaction,
});
return {
compression: 0,
packed_context_free_data: (0, chain_numeric_1.arrayToHex)(signedTx.serializedContextFreeData || new Uint8Array(0)),
packed_trx: (0, chain_numeric_1.arrayToHex)(signedTx.serializedTransaction),
signatures: signedTx.signatures,
};
});
}
pushToServer(transaction, endpoint, dryRun) {
return __awaiter(this, void 0, void 0, function* () {
const privateKeys = [];
privateKeys.push(this.privateKey);
const chainData = yield this.getChainDataForTx();
this.setRawRequestExp(transaction, chainData);
const signedTransaction = yield this.config.fioProvider.prepareTransaction({
abiMap: Transactions.abiMap,
chainId: chainData.chain_id,
privateKeys,
textDecoder: new text_encoding_1.TextDecoder(),
textEncoder: new text_encoding_1.TextEncoder(),
transaction,
});
if (dryRun) {
return signedTransaction;
}
return this.multicastServers({ endpoint, body: JSON.stringify(signedTransaction) });
});
}
executeCall(_a) {
return __awaiter(this, arguments, void 0, function* ({ baseUrl, endPoint, body, fetchOptions, signal, }) {
var _b, _c;
let options;
this.validate();
if (fetchOptions != null) {
options = fetchOptions;
if (body != null) {
options.body = body;
}
}
else {
options = {
body,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
method: 'POST',
};
}
options.signal = signal;
try {
const res = yield this.config.fetchJson(baseUrl + endPoint, options);
if (res === undefined) {
const error = new Error(`Error: Can't reach the site ${baseUrl}${endPoint}. Possible wrong url.`);
return {
data: {
code: 500,
message: error.message,
},
isError: true,
};
}
if (!res.ok) {
const error = new entities_1.ExecuteCallError(`Error ${res.status} while fetching ${baseUrl + endPoint}`, res.status);
try {
error.json = yield res.json();
if (exports.fioApiErrorCodes.indexOf(res.status) > -1) {
if (error.json &&
error.json.fields &&
error.json.fields[0] &&
error.json.fields[0].error) {
error.message = error.json.fields[0].error;
}
return {
data: {
code: error.errorCode || res.status,
json: error.json,
message: error.message,
},
isError: true,
};
}
}
catch (e) {
error.json = {};
(_c = (_b = this.config).logger) === null || _c === void 0 ? void 0 : _c.call(_b, {
context: {
endpoint: endPoint,
error,
},
type: 'execute',
});
}
throw error;
}
return res.json();
}
catch (e) {
// @ts-ignore
e.requestParams = { baseUrl, endPoint, body, fetchOptions };
throw e;
}
});
}
multicastServers(req) {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b, _c, _d;
const { endpoint, body, fetchOptions, requestTimeout } = req;
const res = yield (0, utils_1.asyncWaterfall)({
asyncFunctions: this.config.baseUrls.map((apiUrl) => (signal) => this.executeCall({ baseUrl: apiUrl, endPoint: endpoint, body, fetchOptions, signal })),
requestTimeout,
baseUrls: this.config.baseUrls,
});
// TODO asyncWaterfall can throw errors and error interface can be different
if (res === null || res === void 0 ? void 0 : res.isError) {
const error = new entities_1.FioError(res.errorMessage || res.data.message);
error.json = res.data.json;
error.list = res.data.list;
error.errorCode = res.data.code;
(_b = (_a = this.config).logger) === null || _b === void 0 ? void 0 : _b.call(_a, { type: 'request', context: Object.assign(Object.assign({}, req), { error }) });
throw error;
}
(_d = (_c = this.config).logger) === null || _d === void 0 ? void 0 : _d.call(_c, { type: 'request', context: Object.assign(Object.assign({}, req), { res }) });
return res;
});
}
getCipherContent(contentType, content, privateKey, publicKey) {
return (0, utils_1.getCipherContent)(contentType, content, privateKey, publicKey);
}
getUnCipherContent(contentType, content, privateKey, publicKey) {
return (0, utils_1.getUnCipherContent)(contentType, content, privateKey, publicKey);
}
validate() {
if (this.validationRules) {
const validation = (0, validation_1.validate)(this.validationData, this.validationRules);
if (!validation.isValid) {
throw new entities_1.ValidationError(validation.errors, `Validation error`);
}
}
}
}
exports.Transactions = Transactions;
Transactions.abiMap = new Map();
//# sourceMappingURL=Transactions.js.map
;