From 2e72db26c30faa52b6e25df6d554fe3f40cb3893 Mon Sep 17 00:00:00 2001 From: andreyvEze Date: Fri, 22 Apr 2022 14:45:45 +0300 Subject: [PATCH 1/4] Add serialize, sign steps to Transactions class --- lib/transactions/Transactions.js | 187 ++++++++++-- lib/transactions/signed/SignedTransaction.js | 17 +- src/transactions/Transactions.ts | 296 +++++++++++++++++-- src/transactions/signed/SignedTransaction.ts | 26 +- 4 files changed, 444 insertions(+), 82 deletions(-) diff --git a/lib/transactions/Transactions.js b/lib/transactions/Transactions.js index dabbde99..d183eb3e 100644 --- a/lib/transactions/Transactions.js +++ b/lib/transactions/Transactions.js @@ -9,13 +9,26 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.Transactions = void 0; +exports.Transactions = 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 Autorization_1 = require("../entities/Autorization"); +const RawAction_1 = require("../entities/RawAction"); +const RawTransaction_1 = require("../entities/RawTransaction"); const ValidationError_1 = require("../entities/ValidationError"); const validation_1 = require("../utils/validation"); -const textEncoder = new text_encoding_1.TextEncoder(); -const textDecoder = new text_encoding_1.TextDecoder(); +const defaultTextEncoder = new text_encoding_1.TextEncoder(); +const defaultTextDecoder = new text_encoding_1.TextDecoder(); +exports.signAllAuthorityProvider = { + getRequiredKeys(authorityProviderArgs) { + return __awaiter(this, void 0, void 0, function* () { + const { availableKeys } = authorityProviderArgs; + return availableKeys; + }); + }, +}; class Transactions { constructor() { this.publicKey = ''; @@ -25,17 +38,16 @@ class Transactions { this.validationRules = null; } getActor(publicKey = '') { - const actor = Transactions.FioProvider.accountHash((publicKey == '') ? this.publicKey : publicKey); - return actor; + return Transactions.FioProvider.accountHash((publicKey === '' || !publicKey) ? this.publicKey : publicKey); } getChainInfo() { return __awaiter(this, void 0, void 0, function* () { const options = { - method: 'GET', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, + method: 'GET', }; const res = yield Transactions.fetchJson(Transactions.baseUrl + 'chain/get_info', options); return yield res.json(); @@ -43,34 +55,34 @@ class Transactions { } getBlock(chain) { return __awaiter(this, void 0, void 0, function* () { - if (chain == undefined) { + if (chain === undefined || !chain) { throw new Error('chain undefined'); } - if (chain.last_irreversible_block_num == undefined) { + if (chain.last_irreversible_block_num === undefined) { throw new Error('chain.last_irreversible_block_num undefined'); } const res = yield Transactions.fetchJson(Transactions.baseUrl + 'chain/get_block', { - method: 'POST', + body: JSON.stringify({ + block_num_or_id: chain.last_irreversible_block_num, + }), headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, - body: JSON.stringify({ - block_num_or_id: chain.last_irreversible_block_num, - }), + method: 'POST', }); return yield res.json(); }); } - pushToServer(transaction, endpoint, dryRun) { + getChainDataForTx() { return __awaiter(this, void 0, void 0, function* () { - const privky = new Array(); - privky.push(this.privateKey); - let chain, block; + let chain; + let block; try { chain = yield this.getChainInfo(); } catch (error) { + // tslint:disable-next-line:no-console console.error('chain:: ' + error); const e = new Error(`Error while fetching chain info`); e.errorCode = 800; @@ -80,27 +92,136 @@ class Transactions { 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 = 801; throw e; } - transaction.ref_block_num = block.block_num & 0xFFFF; - transaction.ref_block_prefix = block.ref_block_prefix; const expiration = new Date(chain.head_block_time + 'Z'); expiration.setSeconds(expiration.getSeconds() + 180); const expirationStr = expiration.toISOString(); - transaction.expiration = expirationStr.substr(0, expirationStr.length - 1); + return { + chain_id: chain.chain_id, + expiration: expirationStr.substr(0, expirationStr.length - 1), + // tslint:disable-next-line:no-bitwise + ref_block_num: block.block_num & 0xFFFF, + ref_block_prefix: block.ref_block_prefix, + }; + }); + } + setRawTransactionExp(rawTransaction, chainData) { + rawTransaction.ref_block_num = chainData.ref_block_num; + rawTransaction.ref_block_prefix = chainData.ref_block_prefix; + rawTransaction.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 = defaultTextDecoder, textEncoder = 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({ account, action, data, publicKey, chainData }) { + return __awaiter(this, void 0, void 0, function* () { + const rawTransaction = new RawTransaction_1.RawTransaction(); + const rawaction = new RawAction_1.RawAction(); + rawaction.account = account; + const actor = yield this.getActor(publicKey); + if (!data.actor) { + data.actor = actor; + } + rawaction.authorization.push(new Autorization_1.Autorization(actor)); + rawaction.account = account; + rawaction.name = action; + rawaction.data = data; + rawTransaction.actions.push(rawaction); + if (chainData && chainData.ref_block_num) { + this.setRawTransactionExp(rawTransaction, chainData); + } + return rawTransaction; + }); + } + serialize({ chainId, abiMap = Transactions.abiMap, transaction, textDecoder = defaultTextDecoder, textEncoder = defaultTextEncoder, }) { + return __awaiter(this, void 0, void 0, function* () { + const api = this.initFioJsApi({ + abiMap, + chainId, + privateKeys: [], + textDecoder, + textEncoder, + }); + return yield api.transact(transaction, { sign: false }); + }); + } + sign({ abiMap = Transactions.abiMap, chainId, privateKeys, transaction, serializedTransaction, serializedContextFreeData, }) { + return __awaiter(this, void 0, void 0, function* () { + 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 privky = new Array(); + privky.push(this.privateKey); + const chainData = yield this.getChainDataForTx(); + this.setRawTransactionExp(transaction, chainData); if (dryRun) { return Transactions.FioProvider.prepareTransaction({ - transaction, chainId: chain.chain_id, privateKeys: privky, abiMap: Transactions.abiMap, - textDecoder: new text_encoding_1.TextDecoder(), textEncoder: new text_encoding_1.TextEncoder(), + abiMap: Transactions.abiMap, + chainId: chainData.chain_id, + privateKeys: privky, + textDecoder: new text_encoding_1.TextDecoder(), + textEncoder: new text_encoding_1.TextEncoder(), + transaction, }); } else { const signedTransaction = yield Transactions.FioProvider.prepareTransaction({ - transaction, chainId: chain.chain_id, privateKeys: privky, abiMap: Transactions.abiMap, - textDecoder: new text_encoding_1.TextDecoder(), textEncoder: new text_encoding_1.TextEncoder(), + abiMap: Transactions.abiMap, + chainId: chainData.chain_id, + privateKeys: privky, + textDecoder: new text_encoding_1.TextDecoder(), + textEncoder: new text_encoding_1.TextEncoder(), + transaction, }); return this.executeCall(endpoint, JSON.stringify(signedTransaction)); } @@ -118,12 +239,12 @@ class Transactions { } else { options = { - method: 'POST', + body, headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, - body, + method: 'POST', }; } try { @@ -134,6 +255,7 @@ class Transactions { error.json = yield res.json(); } catch (e) { + // tslint:disable-next-line:no-console console.log(e); error.json = {}; } @@ -143,17 +265,28 @@ class Transactions { return res.json(); } catch (e) { + // @ts-ignore e.requestParams = { endPoint, body, fetchOptions }; throw e; } }); } getCipherContent(contentType, content, privateKey, publicKey) { - const cipher = fiojs_1.Fio.createSharedCipher({ privateKey, publicKey, textEncoder, textDecoder }); + const cipher = fiojs_1.Fio.createSharedCipher({ + privateKey, + publicKey, + textDecoder: defaultTextDecoder, + textEncoder: defaultTextEncoder, + }); return cipher.encrypt(contentType, content); } getUnCipherContent(contentType, content, privateKey, publicKey) { - const cipher = fiojs_1.Fio.createSharedCipher({ privateKey, publicKey, textEncoder, textDecoder }); + const cipher = fiojs_1.Fio.createSharedCipher({ + privateKey, + publicKey, + textDecoder: defaultTextDecoder, + textEncoder: defaultTextEncoder, + }); return cipher.decrypt(contentType, content); } validate() { diff --git a/lib/transactions/signed/SignedTransaction.js b/lib/transactions/signed/SignedTransaction.js index 717ff903..9b13bf4e 100644 --- a/lib/transactions/signed/SignedTransaction.js +++ b/lib/transactions/signed/SignedTransaction.js @@ -10,9 +10,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SignedTransaction = void 0; -const Autorization_1 = require("../../entities/Autorization"); -const RawAction_1 = require("../../entities/RawAction"); -const RawTransaction_1 = require("../../entities/RawTransaction"); const Transactions_1 = require("../Transactions"); class SignedTransaction extends Transactions_1.Transactions { static prepareResponse(result, includeTrxId = false) { @@ -35,15 +32,11 @@ class SignedTransaction extends Transactions_1.Transactions { return __awaiter(this, void 0, void 0, function* () { this.privateKey = privateKey; this.publicKey = publicKey; - const rawTransaction = new RawTransaction_1.RawTransaction(); - const rawaction = new RawAction_1.RawAction(); - rawaction.account = this.getAccount(); - const actor = yield this.getActor(); - rawaction.authorization.push(new Autorization_1.Autorization(actor)); - rawaction.account = this.getAccount(); - rawaction.name = this.getAction(); - rawaction.data = this.getData(); - rawTransaction.actions.push(rawaction); + const rawTransaction = yield this.createRawTransaction({ + account: this.getAccount(), + action: this.getAction(), + data: this.getData(), + }); const result = yield this.pushToServer(rawTransaction, this.getEndPoint(), dryRun); return this.prepareResponse(result); }); diff --git a/src/transactions/Transactions.ts b/src/transactions/Transactions.ts index c9da1a4b..c6ca9fde 100644 --- a/src/transactions/Transactions.ts +++ b/src/transactions/Transactions.ts @@ -1,20 +1,49 @@ -import { Fio } from '@fioprotocol/fiojs' +import { Api as FioJsApi, Fio } from '@fioprotocol/fiojs' +import { + AbiProvider, + AuthorityProvider, + AuthorityProviderArgs, + BinaryAbi, +} from '@fioprotocol/fiojs/dist/chain-api-interfaces' +import { PushTransactionArgs } from '@fioprotocol/fiojs/dist/chain-rpc-interfaces' + +import { JsSignatureProvider } from '@fioprotocol/fiojs/dist/chain-jssig' +import { arrayToHex, base64ToBinary } from '@fioprotocol/fiojs/dist/chain-numeric' + import { TextDecoder, TextEncoder } from 'text-encoding' + import { AbiResponse } from '../entities/AbiResponse' +import { Autorization } from '../entities/Autorization' +import { RawAction } from '../entities/RawAction' import { RawTransaction } from '../entities/RawTransaction' import { ValidationError } from '../entities/ValidationError' + import { validate } from '../utils/validation' -type FetchJson = (uri: string, opts?: Object) => any -const textEncoder: TextEncoder = new TextEncoder() -const textDecoder: TextDecoder = new TextDecoder() +type FetchJson = (uri: string, opts?: object) => any +interface SignedTxArgs { + compression: number, + packed_context_free_data: string, + packed_trx: string, + signatures: string[], +} + +const defaultTextEncoder: TextEncoder = new TextEncoder() +const defaultTextDecoder: TextDecoder = new TextDecoder() + +export const signAllAuthorityProvider: AuthorityProvider = { + async getRequiredKeys(authorityProviderArgs: AuthorityProviderArgs) { + const { availableKeys } = authorityProviderArgs + return availableKeys + }, +} export class Transactions { public static baseUrl: string public static abiMap: Map = new Map() public static FioProvider: { prepareTransaction(param: any): Promise; - accountHash(pubkey: string): string + accountHash(pubKey: string): string } public static fetchJson: FetchJson @@ -26,49 +55,53 @@ export class Transactions { public validationRules: any | null = null public getActor(publicKey: string = ''): string { - const actor = Transactions.FioProvider.accountHash((publicKey == '') ? this.publicKey : publicKey) - return actor + return Transactions.FioProvider.accountHash((publicKey === '' || !publicKey) ? this.publicKey : publicKey) } public async getChainInfo(): Promise { const options = { - method: 'GET', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, + method: 'GET', } const res = await Transactions.fetchJson(Transactions.baseUrl + 'chain/get_info', options) return await res.json() } public async getBlock(chain: any): Promise { - if (chain == undefined) { + if (chain === undefined || !chain) { throw new Error('chain undefined') } - if (chain.last_irreversible_block_num == undefined) { + if (chain.last_irreversible_block_num === undefined) { throw new Error('chain.last_irreversible_block_num undefined') } const res = await Transactions.fetchJson(Transactions.baseUrl + 'chain/get_block', { - method: 'POST', + body: JSON.stringify({ + block_num_or_id: chain.last_irreversible_block_num, + }), headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, - body: JSON.stringify({ - block_num_or_id: chain.last_irreversible_block_num, - }), + method: 'POST', }) return await res.json() } - public async pushToServer(transaction: RawTransaction, endpoint: string, dryRun: boolean): Promise { - const privky: string[] = new Array() - privky.push(this.privateKey) - let chain, block + public async getChainDataForTx(): Promise<{ + chain_id: number, + ref_block_num: number, + ref_block_prefix: number, + expiration: string, + }> { + let chain + let block try { chain = await this.getChainInfo() } catch (error) { + // tslint:disable-next-line:no-console console.error('chain:: ' + error) const e: Error & { errorCode?: number } = new Error(`Error while fetching chain info`) e.errorCode = 800 @@ -77,26 +110,209 @@ export class Transactions { try { block = await this.getBlock(chain) } catch (error) { + // tslint:disable-next-line:no-console console.error('block: ' + error) const e: Error & { errorCode?: number } = new Error(`Error while fetching block`) e.errorCode = 801 throw e } - transaction.ref_block_num = block.block_num & 0xFFFF - transaction.ref_block_prefix = block.ref_block_prefix const expiration = new Date(chain.head_block_time + 'Z') expiration.setSeconds(expiration.getSeconds() + 180) const expirationStr = expiration.toISOString() - transaction.expiration = expirationStr.substr(0, expirationStr.length - 1) + return { + chain_id: chain.chain_id, + expiration: expirationStr.substr(0, expirationStr.length - 1), + // tslint:disable-next-line:no-bitwise + ref_block_num: block.block_num & 0xFFFF, + ref_block_prefix: block.ref_block_prefix, + } + } + + public setRawTransactionExp( + rawTransaction: RawTransaction, + chainData: { + ref_block_num: number, + ref_block_prefix: number, + expiration: string, + }, + ): void { + rawTransaction.ref_block_num = chainData.ref_block_num + rawTransaction.ref_block_prefix = chainData.ref_block_prefix + rawTransaction.expiration = chainData.expiration + } + + public generateApiProvider( + abiMap: Map, + ): AbiProvider { + return { + async getRawAbi(accountName: string) { + const rawAbi = abiMap.get(accountName) + if (!rawAbi) { + throw new Error(`Missing ABI for account ${accountName}`) + } + const abi = base64ToBinary(rawAbi.abi) + const binaryAbi: BinaryAbi = { accountName: rawAbi.account_name, abi } + return binaryAbi + }, + } + } + + public initFioJsApi( + { + chainId, + abiMap, + textDecoder = defaultTextDecoder, + textEncoder = defaultTextEncoder, + privateKeys, + }: { + chainId: string, + abiMap: Map, + privateKeys: string[], + textDecoder?: TextDecoder, + textEncoder?: TextEncoder, + }, + ): FioJsApi { + return new FioJsApi({ + abiProvider: this.generateApiProvider(abiMap), + authorityProvider: signAllAuthorityProvider, + chainId, + signatureProvider: new JsSignatureProvider(privateKeys), + textDecoder, + textEncoder, + }) + } + + public async createRawTransaction( + { account, action, data, publicKey, chainData }: { + account: string; + action: string; + data: any, + publicKey?: string, + chainData?: { + ref_block_num: number, + ref_block_prefix: number, + expiration: string, + } + }, + ): Promise { + const rawTransaction = new RawTransaction() + const rawaction = new RawAction() + rawaction.account = account + const actor = await this.getActor(publicKey) + + if (!data.actor) { + data.actor = actor + } + + rawaction.authorization.push(new Autorization(actor)) + rawaction.account = account + rawaction.name = action + rawaction.data = data + rawTransaction.actions.push(rawaction) + + if (chainData && chainData.ref_block_num) { + this.setRawTransactionExp(rawTransaction, chainData) + } + + return rawTransaction + } + + public async serialize( + { + chainId, + abiMap = Transactions.abiMap, + transaction, + textDecoder = defaultTextDecoder, + textEncoder = defaultTextEncoder, + }: { + transaction: RawTransaction, + chainId: string, + abiMap?: Map, + textDecoder?: TextDecoder, + textEncoder?: TextEncoder, + }, + ): Promise { + + const api = this.initFioJsApi({ + abiMap, + chainId, + privateKeys: [], + textDecoder, + textEncoder, + }) + + return await api.transact(transaction, { sign: false }) + } + + public async sign( + { + abiMap = Transactions.abiMap, + chainId, + privateKeys, + transaction, + serializedTransaction, + serializedContextFreeData, + }: { + abiMap?: Map, + chainId: string, + privateKeys: string[], + transaction: RawTransaction, + serializedTransaction: any, + serializedContextFreeData: any, + }, + ): Promise { + const signatureProvider = new JsSignatureProvider(privateKeys) + const availableKeys = await signatureProvider.getAvailableKeys() + const requiredKeys = await signAllAuthorityProvider.getRequiredKeys({ transaction, availableKeys }) + + const api = this.initFioJsApi({ + abiMap, + chainId, + privateKeys, + }) + const abis: BinaryAbi[] = await api.getTransactionAbis(transaction) + + const signedTx = await signatureProvider.sign({ + abis, + chainId, + requiredKeys, + serializedContextFreeData, + serializedTransaction, + }) + + return { + compression: 0, + packed_context_free_data: arrayToHex(signedTx.serializedContextFreeData || new Uint8Array(0)), + packed_trx: arrayToHex(signedTx.serializedTransaction), + signatures: signedTx.signatures, + } + } + + public async pushToServer(transaction: RawTransaction, endpoint: string, dryRun: boolean): Promise { + const privky: string[] = new Array() + privky.push(this.privateKey) + + const chainData = await this.getChainDataForTx() + + this.setRawTransactionExp(transaction, chainData) + if (dryRun) { return Transactions.FioProvider.prepareTransaction({ - transaction, chainId: chain.chain_id, privateKeys: privky, abiMap: Transactions.abiMap, - textDecoder: new TextDecoder(), textEncoder: new TextEncoder(), + abiMap: Transactions.abiMap, + chainId: chainData.chain_id, + privateKeys: privky, + textDecoder: new TextDecoder(), + textEncoder: new TextEncoder(), + transaction, }) } else { const signedTransaction = await Transactions.FioProvider.prepareTransaction({ - transaction, chainId: chain.chain_id, privateKeys: privky, abiMap: Transactions.abiMap, - textDecoder: new TextDecoder(), textEncoder: new TextEncoder(), + abiMap: Transactions.abiMap, + chainId: chainData.chain_id, + privateKeys: privky, + textDecoder: new TextDecoder(), + textEncoder: new TextEncoder(), + transaction, }) return this.executeCall(endpoint, JSON.stringify(signedTransaction)) } @@ -113,21 +329,30 @@ export class Transactions { } } else { options = { - method: 'POST', + body, headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, - body, + method: 'POST', } } try { const res = await Transactions.fetchJson(Transactions.baseUrl + endPoint, options) if (!res.ok) { - const error: Error & { json?: Object, errorCode?: string, requestParams?: { endPoint: string, body: string, fetchOptions?: any } } = new Error(`Error ${res.status} while fetching ${Transactions.baseUrl + endPoint}`) + const error: Error & { + json?: object, + errorCode?: string, + requestParams?: { + endPoint: string, + body: string, + fetchOptions?: any, + }, + } = new Error(`Error ${res.status} while fetching ${Transactions.baseUrl + endPoint}`) try { error.json = await res.json() } catch (e) { + // tslint:disable-next-line:no-console console.log(e) error.json = {} } @@ -136,18 +361,29 @@ export class Transactions { } return res.json() } catch (e) { + // @ts-ignore e.requestParams = { endPoint, body, fetchOptions } throw e } } public getCipherContent(contentType: string, content: any, privateKey: string, publicKey: string) { - const cipher = Fio.createSharedCipher({ privateKey, publicKey, textEncoder, textDecoder }) + const cipher = Fio.createSharedCipher({ + privateKey, + publicKey, + textDecoder: defaultTextDecoder, + textEncoder: defaultTextEncoder, + }) return cipher.encrypt(contentType, content) } public getUnCipherContent(contentType: string, content: any, privateKey: string, publicKey: string) { - const cipher = Fio.createSharedCipher({ privateKey, publicKey, textEncoder, textDecoder }) + const cipher = Fio.createSharedCipher({ + privateKey, + publicKey, + textDecoder: defaultTextDecoder, + textEncoder: defaultTextEncoder, + }) return cipher.decrypt(contentType, content) } diff --git a/src/transactions/signed/SignedTransaction.ts b/src/transactions/signed/SignedTransaction.ts index 1e94bc7e..9d0caf8e 100644 --- a/src/transactions/signed/SignedTransaction.ts +++ b/src/transactions/signed/SignedTransaction.ts @@ -1,12 +1,15 @@ -import { Autorization } from '../../entities/Autorization' -import { RawAction } from '../../entities/RawAction' -import { RawTransaction } from '../../entities/RawTransaction' import { Transactions } from '../Transactions' export abstract class SignedTransaction extends Transactions { public static prepareResponse( - result: { transaction_id: string, processed: { block_num: number, action_traces: Array<{ receipt: { response: string }}>} } | any, + result: { + transaction_id: string, + processed: { + block_num: number, + action_traces: Array<{ receipt: { response: string }}>, + }, + } | any, includeTrxId: boolean = false, ): any { if (result.processed) { @@ -25,6 +28,7 @@ export abstract class SignedTransaction extends Transactions { try { return JSON.parse(processed.action_traces[0].receipt.response) } catch (e) { + // tslint:disable-next-line:no-console console.error(e) } @@ -41,16 +45,12 @@ export abstract class SignedTransaction extends Transactions { this.privateKey = privateKey this.publicKey = publicKey - const rawTransaction = new RawTransaction() - const rawaction = new RawAction() - rawaction.account = this.getAccount() - const actor = await this.getActor() + const rawTransaction = await this.createRawTransaction({ + account: this.getAccount(), + action: this.getAction(), + data: this.getData(), + }) - rawaction.authorization.push(new Autorization(actor)) - rawaction.account = this.getAccount() - rawaction.name = this.getAction() - rawaction.data = this.getData() - rawTransaction.actions.push(rawaction) const result = await this.pushToServer(rawTransaction, this.getEndPoint(), dryRun) return this.prepareResponse(result) } From 99c76dd493ef400310e77e0e5a13a53462f8acca Mon Sep 17 00:00:00 2001 From: andreyvEze Date: Fri, 22 Apr 2022 14:53:29 +0300 Subject: [PATCH 2/4] Add execute tx using serialize and sign steps example --- .../SendTokensSerializeSignSteps/index.js | 63 +++++++++++++++++++ .../SendTokensSerializeSignSteps/package.json | 14 +++++ 2 files changed, 77 insertions(+) create mode 100644 examples/SendTokensSerializeSignSteps/index.js create mode 100644 examples/SendTokensSerializeSignSteps/package.json diff --git a/examples/SendTokensSerializeSignSteps/index.js b/examples/SendTokensSerializeSignSteps/index.js new file mode 100644 index 00000000..6aff133b --- /dev/null +++ b/examples/SendTokensSerializeSignSteps/index.js @@ -0,0 +1,63 @@ +fetch = require('node-fetch') +const { FIOSDK } = require('../../lib/FIOSDK'); + +const privateKey = '' +const publicKey = '' +const payeePublicKey = '' + +const baseUrl = '' // https://testnet.fioprotocol.io:443/v1/ +const defaultFee = 800 * FIOSDK.SUFUnit + +const fetchJson = async (uri, opts = {}) => { + return fetch(uri, opts) +} + +const timeout = async (ms) => { + await new Promise(resolve => { + setTimeout(resolve, ms) + }) +} + +async function main () { + const fundsAmount = 2 * FIOSDK.SUFUnit + const publicFioSdk = new FIOSDK( + '', + '', + baseUrl, + fetchJson + ); + await timeout(4000) + + const chainData = await publicFioSdk.transactions.getChainDataForTx(); + const transaction = await publicFioSdk.transactions.createRawTransaction({ + action: 'trnsfiopubky', + account: 'fio.token', + data: { + payee_public_key: payeePublicKey, + amount: fundsAmount, + max_fee: defaultFee, + tpid: '', + // actor + }, + publicKey, + chainData, + }); + + const { serializedContextFreeData, serializedTransaction } = await publicFioSdk.transactions.serialize({ + chainId: chainData.chain_id, + transaction, + }); + + const signedTransaction = await publicFioSdk.transactions.sign({ + chainId: chainData.chain_id, + privateKeys: [privateKey], + transaction, + serializedTransaction, + serializedContextFreeData, + }); + + const result = await publicFioSdk.executePreparedTrx('transfer_tokens_pub_key', signedTransaction); + console.log(result); +} + +main() diff --git a/examples/SendTokensSerializeSignSteps/package.json b/examples/SendTokensSerializeSignSteps/package.json new file mode 100644 index 00000000..c8407075 --- /dev/null +++ b/examples/SendTokensSerializeSignSteps/package.json @@ -0,0 +1,14 @@ +{ + "name": "SendTokensSerializeSignSteps", + "version": "1.0.0", + "description": "Example of how to execute transaction using serialize/sign/execute steps", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Dapix, Inc. Copyright(c) 2020", + "license": "MIT", + "dependencies": { + "@fioprotocol/fiojs": "1.0.1" + } +} From fe56a6aa45dfb9e25a403d68ba0b1e8dd210d6ae Mon Sep 17 00:00:00 2001 From: andreyvEze Date: Tue, 26 Apr 2022 13:14:32 +0300 Subject: [PATCH 3/4] Add deserialize method to Transactions --- lib/transactions/Transactions.js | 12 +++++++++ lib/transactions/signed/SignedTransaction.js | 1 + src/transactions/Transactions.ts | 27 ++++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/lib/transactions/Transactions.js b/lib/transactions/Transactions.js index d183eb3e..5679bbbe 100644 --- a/lib/transactions/Transactions.js +++ b/lib/transactions/Transactions.js @@ -172,6 +172,18 @@ class Transactions { return yield api.transact(transaction, { sign: false }); }); } + deserialize({ chainId, abiMap = Transactions.abiMap, serializedTransaction, textDecoder = defaultTextDecoder, textEncoder = defaultTextEncoder, }) { + return __awaiter(this, void 0, void 0, function* () { + const api = this.initFioJsApi({ + abiMap, + chainId, + privateKeys: [], + textDecoder, + textEncoder, + }); + return yield api.deserializeTransactionWithActions(serializedTransaction); + }); + } sign({ abiMap = Transactions.abiMap, chainId, privateKeys, transaction, serializedTransaction, serializedContextFreeData, }) { return __awaiter(this, void 0, void 0, function* () { const signatureProvider = new chain_jssig_1.JsSignatureProvider(privateKeys); diff --git a/lib/transactions/signed/SignedTransaction.js b/lib/transactions/signed/SignedTransaction.js index 9b13bf4e..0aa98b40 100644 --- a/lib/transactions/signed/SignedTransaction.js +++ b/lib/transactions/signed/SignedTransaction.js @@ -24,6 +24,7 @@ class SignedTransaction extends Transactions_1.Transactions { return JSON.parse(processed.action_traces[0].receipt.response); } catch (e) { + // tslint:disable-next-line:no-console console.error(e); } return {}; diff --git a/src/transactions/Transactions.ts b/src/transactions/Transactions.ts index c6ca9fde..4c42b291 100644 --- a/src/transactions/Transactions.ts +++ b/src/transactions/Transactions.ts @@ -244,6 +244,33 @@ export class Transactions { return await api.transact(transaction, { sign: false }) } + public async deserialize( + { + chainId, + abiMap = Transactions.abiMap, + serializedTransaction, + textDecoder = defaultTextDecoder, + textEncoder = defaultTextEncoder, + }: { + serializedTransaction: Uint8Array, + chainId: string, + abiMap?: Map, + textDecoder?: TextDecoder, + textEncoder?: TextEncoder, + }, + ): Promise { + + const api = this.initFioJsApi({ + abiMap, + chainId, + privateKeys: [], + textDecoder, + textEncoder, + }) + + return await api.deserializeTransactionWithActions(serializedTransaction) + } + public async sign( { abiMap = Transactions.abiMap, From 8a1b4fdf17194ceceb85f540640df9dcebacaf91 Mon Sep 17 00:00:00 2001 From: Oleksii Trukhanov Date: Fri, 27 May 2022 19:10:42 +0300 Subject: [PATCH 4/4] Fix getAbi calls every time we make transaction --- lib/FIOSDK.js | 16 +++++++++------- src/FIOSDK.ts | 16 +++++++++------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/lib/FIOSDK.js b/lib/FIOSDK.js index d48ed06c..aeea9064 100644 --- a/lib/FIOSDK.js +++ b/lib/FIOSDK.js @@ -57,13 +57,15 @@ class FIOSDK { this.technologyProviderId = technologyProviderId; this.returnPreparedTrx = returnPreparedTrx; for (const accountName of constants_1.Constants.rawAbiAccountName) { - this.getAbi(accountName) - .then((response) => { - Transactions_1.Transactions.abiMap.set(response.account_name, response); - }) - .catch((error) => { - throw error; - }); + if (!Transactions_1.Transactions.abiMap.get(accountName)) { + this.getAbi(accountName) + .then((response) => { + Transactions_1.Transactions.abiMap.set(response.account_name, response); + }) + .catch((error) => { + throw error; + }); + } } } /** diff --git a/src/FIOSDK.ts b/src/FIOSDK.ts index 7d228e7b..4308393e 100644 --- a/src/FIOSDK.ts +++ b/src/FIOSDK.ts @@ -317,13 +317,15 @@ export class FIOSDK { this.returnPreparedTrx = returnPreparedTrx for (const accountName of Constants.rawAbiAccountName) { - this.getAbi(accountName) - .then((response) => { - Transactions.abiMap.set(response.account_name, response) - }) - .catch((error) => { - throw error - }) + if (!Transactions.abiMap.get(accountName)) { + this.getAbi(accountName) + .then((response) => { + Transactions.abiMap.set(response.account_name, response) + }) + .catch((error) => { + throw error + }) + } } }