feat: save source through bundlr (#283)
This commit is contained in:
@@ -120,7 +120,8 @@ describe('Testing the Profit Sharing Token', () => {
|
||||
|
||||
const newSource = fs.readFileSync(path.join(__dirname, '../data/token-evolve.js'), 'utf8');
|
||||
|
||||
const newSrcTxId = await pst.save({ src: newSource }, warp.environment);
|
||||
const srcTx = await warp.createSourceTx({ src: newSource }, wallet);
|
||||
const newSrcTxId = await warp.saveSourceTx(srcTx);
|
||||
await mineBlock(warp);
|
||||
|
||||
await pst.evolve(newSrcTxId);
|
||||
|
||||
@@ -159,13 +159,15 @@ describe('Testing the Warp client for AssemblyScript WASM contract', () => {
|
||||
|
||||
const newContractSrc = fs.readFileSync(path.join(__dirname, '../data/wasm/as/assemblyscript-counter-evolve.wasm'));
|
||||
|
||||
const newSrcTxId = await contract.save(
|
||||
const srcTx = await warp.createSourceTx(
|
||||
{
|
||||
src: newContractSrc,
|
||||
wasmSrcCodeDir: path.join(__dirname, '../data/wasm/as/assembly-evolve')
|
||||
},
|
||||
warp.environment
|
||||
wallet
|
||||
);
|
||||
const newSrcTxId = await warp.saveSourceTx(srcTx);
|
||||
|
||||
await mineBlock(warp);
|
||||
|
||||
await contract.evolve(newSrcTxId);
|
||||
|
||||
@@ -197,13 +197,14 @@ describe('Testing the Go WASM Profit Sharing Token', () => {
|
||||
|
||||
const newContractSrc = fs.readFileSync(path.join(__dirname, '../data/wasm/go/go-pst-evolve.wasm'));
|
||||
|
||||
const newSrcTxId = await pst.save(
|
||||
const srcTx = await warp.createSourceTx(
|
||||
{
|
||||
src: newContractSrc,
|
||||
wasmSrcCodeDir: path.join(__dirname, '../data/wasm/go/src-evolve')
|
||||
},
|
||||
warp.environment
|
||||
wallet
|
||||
);
|
||||
const newSrcTxId = await warp.saveSourceTx(srcTx);
|
||||
|
||||
await mineBlock(warp);
|
||||
|
||||
|
||||
@@ -223,14 +223,15 @@ describe('Testing the Rust WASM Profit Sharing Token', () => {
|
||||
|
||||
const newContractSrc = fs.readFileSync(path.join(__dirname, '../data/wasm/rust/rust-pst-evolve_bg.wasm'));
|
||||
|
||||
const newSrcTxId = await pst.save(
|
||||
const srcTx = await warp.createSourceTx(
|
||||
{
|
||||
src: newContractSrc,
|
||||
wasmSrcCodeDir: path.join(__dirname, '../data/wasm/rust/src-evolve'),
|
||||
wasmGlueCode: path.join(__dirname, '../data/wasm/rust/rust-pst-evolve.js')
|
||||
},
|
||||
warp.environment
|
||||
wallet
|
||||
);
|
||||
const newSrcTxId = await warp.saveSourceTx(srcTx);
|
||||
|
||||
await mineBlock(warp);
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import { InteractionResult } from '../core/modules/impl/HandlerExecutorFactory';
|
||||
import { EvaluationOptions, EvalStateResult } from '../core/modules/StateEvaluator';
|
||||
import { GQLNodeInterface } from '../legacy/gqlResult';
|
||||
import { ArTransfer, Tags, ArWallet } from './deploy/CreateContract';
|
||||
import { Source } from './deploy/Source';
|
||||
import { SignatureType } from './Signature';
|
||||
|
||||
export type CurrentTx = { interactionTxId: string; contractTxId: string };
|
||||
@@ -69,7 +68,7 @@ export type InnerCallData = { callingInteraction: GQLNodeInterface; callType: In
|
||||
* A base interface to be implemented by SmartWeave Contracts clients
|
||||
* - contains "low-level" methods that allow to interact with any contract
|
||||
*/
|
||||
export interface Contract<State = unknown> extends Source {
|
||||
export interface Contract<State = unknown> {
|
||||
/**
|
||||
* Returns the Arweave transaction id of this contract.
|
||||
*/
|
||||
|
||||
@@ -754,17 +754,6 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
||||
return await this.writeInteraction<any>({ function: 'evolve', value: newSrcTxId }, options);
|
||||
}
|
||||
|
||||
async save(sourceData: SourceData): Promise<any> {
|
||||
if (!this.signature) {
|
||||
throw new Error("Wallet not connected. Use 'connect' method first.");
|
||||
}
|
||||
const source = new SourceImpl(this.warp);
|
||||
|
||||
const srcTx = await source.save(sourceData, this.warp.environment, this.signature);
|
||||
|
||||
return srcTx.id;
|
||||
}
|
||||
|
||||
get rootSortKey(): string {
|
||||
return this._rootSortKey;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { JWKInterface } from 'arweave/node/lib/wallet';
|
||||
import { SignatureType } from '../../contract/Signature';
|
||||
import { Source } from './Source';
|
||||
|
||||
export type Tags = { name: string; value: string }[];
|
||||
|
||||
@@ -43,7 +44,7 @@ export interface ContractDeploy {
|
||||
srcTxId?: string;
|
||||
}
|
||||
|
||||
export interface CreateContract {
|
||||
export interface CreateContract extends Source {
|
||||
deploy(contractData: ContractData, disableBundling?: boolean): Promise<ContractDeploy>;
|
||||
|
||||
deployFromSourceTx(contractData: FromSrcTxContractData, disableBundling?: boolean): Promise<ContractDeploy>;
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
import { ArWallet } from './CreateContract';
|
||||
import { SourceData } from './impl/SourceImpl';
|
||||
import { WarpEnvironment } from '../../core/Warp';
|
||||
import { SignatureType } from '../../contract/Signature';
|
||||
|
||||
import Transaction from 'arweave/node/lib/transaction';
|
||||
export interface Source {
|
||||
/**
|
||||
* allows to post contract source on Arweave
|
||||
* @param contractSource - contract source...
|
||||
* allows to create contract source
|
||||
* @param sourceData - contract source data
|
||||
* @param wallet - either Arweave wallet or custom signature type
|
||||
*/
|
||||
save(
|
||||
contractSource: SourceData,
|
||||
env: WarpEnvironment,
|
||||
signer?: ArWallet | SignatureType,
|
||||
useBundler?: boolean
|
||||
): Promise<string | null>;
|
||||
createSourceTx(sourceData: SourceData, wallet: ArWallet | SignatureType): Promise<Transaction>;
|
||||
|
||||
/**
|
||||
* allows to save contract source
|
||||
* @param sourceTx - contract source transaction
|
||||
* @param disableBundling = whether source should be deployed through bundlr using Warp Gateway
|
||||
*/
|
||||
saveSourceTx(sourceTx: Transaction, disableBundling?: boolean): Promise<string>;
|
||||
}
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
/* eslint-disable */
|
||||
import Arweave from 'arweave';
|
||||
import Transaction from 'arweave/node/lib/transaction';
|
||||
import { Signature } from '../../../contract/Signature';
|
||||
import { Signature, SignatureType } from '../../../contract/Signature';
|
||||
import { SmartWeaveTags } from '../../../core/SmartWeaveTags';
|
||||
import { Warp } from '../../../core/Warp';
|
||||
import { WARP_GW_URL } from '../../../core/WarpFactory';
|
||||
import { LoggerFactory } from '../../../logging/LoggerFactory';
|
||||
import { CreateContract, ContractData, ContractDeploy, FromSrcTxContractData } from '../CreateContract';
|
||||
import { SourceImpl } from './SourceImpl';
|
||||
import { CreateContract, ContractData, ContractDeploy, FromSrcTxContractData, ArWallet } from '../CreateContract';
|
||||
import { SourceData, SourceImpl } from './SourceImpl';
|
||||
import { Buffer } from 'redstone-isomorphic';
|
||||
|
||||
export class DefaultCreateContract implements CreateContract {
|
||||
private readonly logger = LoggerFactory.INST.create('DefaultCreateContract');
|
||||
private readonly source: SourceImpl;
|
||||
|
||||
private signature: Signature;
|
||||
|
||||
constructor(private readonly arweave: Arweave, private warp: Warp) {
|
||||
this.deployFromSourceTx = this.deployFromSourceTx.bind(this);
|
||||
this.source = new SourceImpl(this.warp);
|
||||
}
|
||||
|
||||
async deploy(contractData: ContractData, disableBundling?: boolean): Promise<ContractDeploy> {
|
||||
@@ -24,9 +27,11 @@ export class DefaultCreateContract implements CreateContract {
|
||||
const effectiveUseBundler =
|
||||
disableBundling == undefined ? this.warp.definitionLoader.type() == 'warp' : !disableBundling;
|
||||
|
||||
const source = new SourceImpl(this.warp);
|
||||
const srcTx = await this.source.createSourceTx(contractData, wallet);
|
||||
if (!effectiveUseBundler) {
|
||||
await this.source.saveSourceTx(srcTx, true);
|
||||
}
|
||||
|
||||
const srcTx = await source.save(contractData, this.warp.environment, wallet, effectiveUseBundler);
|
||||
this.logger.debug('Creating new contract');
|
||||
|
||||
return await this.deployFromSourceTx(
|
||||
@@ -94,7 +99,7 @@ export class DefaultCreateContract implements CreateContract {
|
||||
let responseOk: boolean;
|
||||
let response: { status: number; statusText: string; data: any };
|
||||
if (effectiveUseBundler) {
|
||||
const result = await this.post(contractTX, srcTx);
|
||||
const result = await this.postContract(contractTX, srcTx);
|
||||
this.logger.debug(result);
|
||||
responseOk = true;
|
||||
} else {
|
||||
@@ -136,7 +141,15 @@ export class DefaultCreateContract implements CreateContract {
|
||||
}
|
||||
}
|
||||
|
||||
private async post(contractTx: Transaction, srcTx: Transaction = null): Promise<any> {
|
||||
async createSourceTx(sourceData: SourceData, wallet: ArWallet | SignatureType): Promise<Transaction> {
|
||||
return this.source.createSourceTx(sourceData, wallet);
|
||||
}
|
||||
|
||||
async saveSourceTx(srcTx: Transaction, disableBundling?: boolean): Promise<string> {
|
||||
return this.source.saveSourceTx(srcTx, disableBundling);
|
||||
}
|
||||
|
||||
private async postContract(contractTx: Transaction, srcTx: Transaction = null): Promise<any> {
|
||||
let body: any = {
|
||||
contractTx
|
||||
};
|
||||
|
||||
@@ -8,8 +8,11 @@ import { SmartWeaveTags } from '../../../core/SmartWeaveTags';
|
||||
import { LoggerFactory } from '../../../logging/LoggerFactory';
|
||||
import { Source } from '../Source';
|
||||
import { Buffer } from 'redstone-isomorphic';
|
||||
import { Warp, WarpEnvironment } from '../../../core/Warp';
|
||||
import { Warp } from '../../../core/Warp';
|
||||
import { Signature, SignatureType } from '../../../contract/Signature';
|
||||
import Transaction from 'arweave/node/lib/transaction';
|
||||
import { WARP_GW_URL } from '../../../core/WarpFactory';
|
||||
import { TagsParser } from '../../../core/modules/impl/TagsParser';
|
||||
|
||||
const wasmTypeMapping: Map<number, string> = new Map([
|
||||
[1, 'assemblyscript'],
|
||||
@@ -31,21 +34,14 @@ export class SourceImpl implements Source {
|
||||
|
||||
constructor(private readonly warp: Warp) {}
|
||||
|
||||
async save(
|
||||
contractData: SourceData,
|
||||
env: WarpEnvironment,
|
||||
signature: ArWallet | SignatureType,
|
||||
useBundler = false
|
||||
): Promise<any> {
|
||||
async createSourceTx(sourceData: SourceData, wallet: ArWallet | SignatureType): Promise<Transaction> {
|
||||
this.logger.debug('Creating new contract source');
|
||||
|
||||
const { src, wasmSrcCodeDir, wasmGlueCode } = contractData;
|
||||
const { src, wasmSrcCodeDir, wasmGlueCode } = sourceData;
|
||||
|
||||
this.signature = new Signature(this.warp, signature);
|
||||
this.signature = new Signature(this.warp, wallet);
|
||||
const signer = this.signature.signer;
|
||||
|
||||
this.signature.checkNonArweaveSigningAvailability(useBundler);
|
||||
|
||||
const contractType: ContractType = src instanceof Buffer ? 'wasm' : 'js';
|
||||
let srcTx;
|
||||
let wasmLang = null;
|
||||
@@ -122,7 +118,7 @@ export class SourceImpl implements Source {
|
||||
srcTx.addTag(SmartWeaveTags.WASM_META, JSON.stringify(metadata));
|
||||
}
|
||||
|
||||
if (env === 'testnet') {
|
||||
if (this.warp.environment === 'testnet') {
|
||||
srcTx.addTag(SmartWeaveTags.WARP_TESTNET, '1.0.0');
|
||||
}
|
||||
|
||||
@@ -130,17 +126,40 @@ export class SourceImpl implements Source {
|
||||
|
||||
this.logger.debug('Posting transaction with source');
|
||||
|
||||
// note: in case of useBundler = true, we're posting both
|
||||
// src tx and contract tx in one request.
|
||||
let responseOk = true;
|
||||
return srcTx;
|
||||
}
|
||||
|
||||
async saveSourceTx(srcTx: Transaction, disableBundling: boolean = false): Promise<string> {
|
||||
this.logger.debug('Saving contract source', srcTx.id);
|
||||
|
||||
if (this.warp.environment == 'local') {
|
||||
disableBundling = true;
|
||||
}
|
||||
|
||||
const effectiveUseBundler =
|
||||
disableBundling == undefined ? this.warp.definitionLoader.type() == 'warp' : !disableBundling;
|
||||
|
||||
const tagsParser = new TagsParser();
|
||||
const signatureTag = tagsParser.getTag(srcTx, SmartWeaveTags.SIGNATURE_TYPE);
|
||||
|
||||
if (signatureTag && signatureTag != 'arweave' && !effectiveUseBundler) {
|
||||
throw new Error(`Unable to save source with signature type: ${signatureTag} when bundling is disabled.`);
|
||||
}
|
||||
|
||||
let responseOk: boolean;
|
||||
let response: { status: number; statusText: string; data: any };
|
||||
if (!useBundler) {
|
||||
|
||||
if (!disableBundling) {
|
||||
const result = await this.postSource(srcTx);
|
||||
this.logger.debug(result);
|
||||
responseOk = true;
|
||||
} else {
|
||||
response = await this.warp.arweave.transactions.post(srcTx);
|
||||
responseOk = response.status === 200 || response.status === 208;
|
||||
}
|
||||
|
||||
if (responseOk) {
|
||||
return srcTx;
|
||||
return srcTx.id;
|
||||
} else {
|
||||
throw new Error(
|
||||
`Unable to write Contract Source. Arweave responded with status ${response.status}: ${response.statusText}`
|
||||
@@ -187,6 +206,26 @@ export class SourceImpl implements Source {
|
||||
|
||||
return outputStreamBuffer.getContents();
|
||||
}
|
||||
|
||||
private async postSource(srcTx: Transaction = null): Promise<any> {
|
||||
const response = await fetch(`${WARP_GW_URL}/gateway/sources/deploy`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ srcTx }),
|
||||
headers: {
|
||||
'Accept-Encoding': 'gzip, deflate, br',
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
} else {
|
||||
throw new Error(
|
||||
`Error while posting contract source. Sequencer responded with status ${response.status} ${response.statusText}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function dummyImports(moduleImports: WebAssembly.ModuleImportDescriptor[]) {
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import Arweave from 'arweave';
|
||||
import { Contract, InnerCallData } from '../contract/Contract';
|
||||
import { CreateContract } from '../contract/deploy/CreateContract';
|
||||
import {
|
||||
ArWallet,
|
||||
ContractData,
|
||||
ContractDeploy,
|
||||
CreateContract,
|
||||
FromSrcTxContractData
|
||||
} from '../contract/deploy/CreateContract';
|
||||
import { DefaultCreateContract } from '../contract/deploy/impl/DefaultCreateContract';
|
||||
import { HandlerBasedContract } from '../contract/HandlerBasedContract';
|
||||
import { PstContract } from '../contract/PstContract';
|
||||
@@ -15,6 +21,9 @@ import { WarpBuilder } from './WarpBuilder';
|
||||
import { WarpPluginType, WarpPlugin, knownWarpPlugins } from './WarpPlugin';
|
||||
import { SortKeyCache } from '../cache/SortKeyCache';
|
||||
import { ContractDefinition } from './ContractDefinition';
|
||||
import { SignatureType } from '../contract/Signature';
|
||||
import { SourceData } from '../contract/deploy/impl/SourceImpl';
|
||||
import Transaction from 'arweave/node/lib/transaction';
|
||||
|
||||
export type WarpEnvironment = 'local' | 'testnet' | 'mainnet' | 'custom';
|
||||
|
||||
@@ -27,6 +36,9 @@ export type WarpEnvironment = 'local' | 'testnet' | 'mainnet' | 'custom';
|
||||
* contract and perform operations on them (see {@link Contract})
|
||||
*/
|
||||
export class Warp {
|
||||
/**
|
||||
* @deprecated createContract will be a private field, please use its methods directly e.g. await warp.deploy(...)
|
||||
*/
|
||||
readonly createContract: CreateContract;
|
||||
readonly testing: Testing;
|
||||
|
||||
@@ -61,6 +73,26 @@ export class Warp {
|
||||
return new HandlerBasedContract<State>(contractTxId, this, callingContract, innerCallData);
|
||||
}
|
||||
|
||||
async deploy(contractData: ContractData, disableBundling?: boolean): Promise<ContractDeploy> {
|
||||
return await this.createContract.deploy(contractData, disableBundling);
|
||||
}
|
||||
|
||||
async deployFromSourceTx(contractData: FromSrcTxContractData, disableBundling?: boolean): Promise<ContractDeploy> {
|
||||
return await this.createContract.deployFromSourceTx(contractData, disableBundling);
|
||||
}
|
||||
|
||||
async deployBundled(rawDataItem: Buffer): Promise<ContractDeploy> {
|
||||
return await this.createContract.deployBundled(rawDataItem);
|
||||
}
|
||||
|
||||
async createSourceTx(sourceData: SourceData, wallet: ArWallet | SignatureType): Promise<Transaction> {
|
||||
return await this.createContract.createSourceTx(sourceData, wallet);
|
||||
}
|
||||
|
||||
async saveSourceTx(srcTx: Transaction, disableBundling?: boolean): Promise<string> {
|
||||
return await this.createContract.saveSourceTx(srcTx, disableBundling);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to connect to a contract that conforms to the Profit Sharing Token standard
|
||||
* @param contractTxId
|
||||
|
||||
66
tools/data/js/token-evolve.js
Normal file
66
tools/data/js/token-evolve.js
Normal file
@@ -0,0 +1,66 @@
|
||||
export function handle(state, action) {
|
||||
const balances = state.balances;
|
||||
const canEvolve = state.canEvolve;
|
||||
const input = action.input;
|
||||
const caller = action.caller;
|
||||
|
||||
if (input.function === 'transfer') {
|
||||
const target = input.target;
|
||||
const qty = input.qty;
|
||||
|
||||
if (!Number.isInteger(qty)) {
|
||||
throw new ContractError('Invalid value for "qty". Must be an integer');
|
||||
}
|
||||
|
||||
if (!target) {
|
||||
throw new ContractError('No target specified');
|
||||
}
|
||||
|
||||
if (qty <= 0 || caller === target) {
|
||||
throw new ContractError('Invalid token transfer');
|
||||
}
|
||||
|
||||
if (balances[caller] < qty) {
|
||||
throw new ContractError(`Caller balance not high enough to send ${qty} token(s)!`);
|
||||
}
|
||||
|
||||
// Lower the token balance of the caller
|
||||
balances[caller] -= qty;
|
||||
if (target in balances) {
|
||||
// Wallet already exists in state, add new tokens
|
||||
balances[target] += qty;
|
||||
} else {
|
||||
// Wallet is new, set starting balance
|
||||
balances[target] = qty;
|
||||
}
|
||||
|
||||
return { state };
|
||||
}
|
||||
|
||||
if (input.function === 'balance') {
|
||||
const target = input.target;
|
||||
const ticker = state.ticker;
|
||||
|
||||
if (typeof target !== 'string') {
|
||||
throw new ContractError('Must specify target to get balance for');
|
||||
}
|
||||
|
||||
if (typeof balances[target] !== 'number') {
|
||||
throw new ContractError('Cannot get balance, target does not exist');
|
||||
}
|
||||
|
||||
return { result: { target, ticker, balance: balances[target] + 555 } };
|
||||
}
|
||||
|
||||
if (input.function === 'evolve' && canEvolve) {
|
||||
if (state.owner !== caller) {
|
||||
throw new ContractError('Only the owner can evolve a contract.');
|
||||
}
|
||||
|
||||
state.evolve = input.value;
|
||||
|
||||
return { state };
|
||||
}
|
||||
|
||||
throw new ContractError(`No function supplied or function not recognised: "${input.function}"`);
|
||||
}
|
||||
69
tools/evolve.ts
Normal file
69
tools/evolve.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
/* eslint-disable */
|
||||
import { defaultCacheOptions, LoggerFactory, WarpFactory } from '../src';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { JWKInterface } from 'arweave/node/lib/wallet';
|
||||
|
||||
async function main() {
|
||||
let wallet: JWKInterface = readJSON('./.secrets/jwk.json');
|
||||
LoggerFactory.INST.logLevel('info');
|
||||
const logger = LoggerFactory.INST.create('evolve');
|
||||
|
||||
try {
|
||||
const warp = WarpFactory.forMainnet({ ...defaultCacheOptions, inMemory: true });
|
||||
|
||||
const jsContractSrc = fs.readFileSync(path.join(__dirname, 'data/js/token-pst.js'), 'utf8');
|
||||
const newJsContractSrc = fs.readFileSync(path.join(__dirname, 'data/js/token-evolve.js'), 'utf8');
|
||||
const owner = await warp.arweave.wallets.jwkToAddress(wallet);
|
||||
const initialState = {
|
||||
ticker: 'EXAMPLE_PST_TOKEN',
|
||||
owner,
|
||||
canEvolve: true,
|
||||
balances: {
|
||||
'uhE-QeYS8i4pmUtnxQyHD7dzXFNaJ9oMK-IM-QPNY6M': 10000000,
|
||||
'33F0QHcb22W7LwWR1iRC8Az1ntZG09XQ03YWuw2ABqA': 23111222
|
||||
},
|
||||
wallets: {}
|
||||
};
|
||||
|
||||
const { contractTxId } = await warp.deploy({
|
||||
wallet,
|
||||
initState: JSON.stringify(initialState),
|
||||
src: jsContractSrc
|
||||
});
|
||||
|
||||
const contract = warp.contract<any>(contractTxId).connect(wallet);
|
||||
|
||||
const { result } = await contract.viewState<any>({
|
||||
function: 'balance',
|
||||
target: 'uhE-QeYS8i4pmUtnxQyHD7dzXFNaJ9oMK-IM-QPNY6M'
|
||||
});
|
||||
|
||||
console.log('Original result', result);
|
||||
|
||||
const srcTx = await warp.createSourceTx({ src: newJsContractSrc }, wallet);
|
||||
const newSrcTxId = await warp.saveSourceTx(srcTx);
|
||||
console.log('Save result', newSrcTxId);
|
||||
await contract.evolve(newSrcTxId);
|
||||
|
||||
const { result: evolvedResult } = await contract.viewState<any>({
|
||||
function: 'balance',
|
||||
target: 'uhE-QeYS8i4pmUtnxQyHD7dzXFNaJ9oMK-IM-QPNY6M'
|
||||
});
|
||||
|
||||
console.log('Evolved result', evolvedResult);
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
export function readJSON(path: string): JWKInterface {
|
||||
const content = fs.readFileSync(path, 'utf-8');
|
||||
try {
|
||||
return JSON.parse(content);
|
||||
} catch (e) {
|
||||
throw new Error(`File "${path}" does not contain a valid JSON`);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((e) => console.error(e));
|
||||
Reference in New Issue
Block a user