Files
warp/src/core/Warp.ts
ppedziwiatr e45c5f2f7b feat: events
2023-10-18 12:10:45 +02:00

235 lines
7.5 KiB
TypeScript

import Arweave from 'arweave';
import { Contract, InnerCallData } from '../contract/Contract';
import {
ArWallet,
BundlrNodeType,
ContractData,
ContractDeploy,
CreateContract,
FromSrcTxContractData
} from '../contract/deploy/CreateContract';
import { HandlerBasedContract } from '../contract/HandlerBasedContract';
import { PstContract } from '../contract/PstContract';
import { PstContractImpl } from '../contract/PstContractImpl';
import { Testing, Wallet } from '../contract/testing/Testing';
import { DefinitionLoader } from './modules/DefinitionLoader';
import { ExecutorFactory } from './modules/ExecutorFactory';
import { HandlerApi } from './modules/impl/HandlerExecutorFactory';
import { InteractionsLoader } from './modules/InteractionsLoader';
import { EvalStateResult, StateEvaluator } from './modules/StateEvaluator';
import { WarpBuilder } from './WarpBuilder';
import {
WarpPluginType,
WarpPlugin,
knownWarpPlugins,
knownWarpPluginsPartial,
WarpKnownPluginType
} from './WarpPlugin';
import { SortKeyCache } from '../cache/SortKeyCache';
import { ContractDefinition, SrcCache } from './ContractDefinition';
import { CustomSignature } from '../contract/Signature';
import { Transaction } from '../utils/types/arweave-types';
import { DEFAULT_LEVEL_DB_LOCATION, WARP_GW_URL } from './WarpFactory';
import { LevelDbCache } from '../cache/impl/LevelDbCache';
import { SourceData } from '../contract/deploy/Source';
import { Signer, DataItem } from 'warp-arbundles';
import { BasicSortKeyCache } from '../cache/BasicSortKeyCache';
export type WarpEnvironment = 'local' | 'testnet' | 'mainnet' | 'custom';
export type KVStorageFactory = (contractTxId: string) => SortKeyCache<unknown>;
/**
* The Warp "motherboard" ;-).
* This is the base class that supplies the implementation of the SmartWeave protocol
* Allows to plug-in different implementation of all the modules defined in the constructor.
*
* After being fully configured, it allows to "connect" to
* contract and perform operations on them (see {@link Contract})
*/
export class Warp {
private _createContract: CreateContract;
private _gwUrl = WARP_GW_URL;
private get createContract(): CreateContract {
if (!this._createContract) {
if (this.plugins.has('deploy')) {
const deployPlugin = this.loadPlugin<Warp, CreateContract>('deploy');
this._createContract = deployPlugin.process(this);
} else {
throw new Error(`In order to use CreateContract methods please attach DeployPlugin.`);
}
}
return this._createContract;
}
readonly testing: Testing;
kvStorageFactory: KVStorageFactory;
whoAmI: string;
eventTarget: EventTarget;
private readonly plugins: Map<WarpPluginType, WarpPlugin<unknown, unknown>> = new Map();
constructor(
readonly arweave: Arweave,
readonly definitionLoader: DefinitionLoader,
readonly interactionsLoader: InteractionsLoader,
readonly executorFactory: ExecutorFactory<HandlerApi<unknown>>,
readonly stateEvaluator: StateEvaluator,
readonly environment: WarpEnvironment = 'custom'
) {
this.testing = new Testing(arweave);
this.kvStorageFactory = (contractTxId: string) => {
return new LevelDbCache({
inMemory: false,
dbLocation: `${DEFAULT_LEVEL_DB_LOCATION}/kv/ldb/${contractTxId}`
});
};
this.eventTarget = new EventTarget();
}
static builder(
arweave: Arweave,
stateCache: BasicSortKeyCache<EvalStateResult<unknown>>,
environment: WarpEnvironment
): WarpBuilder {
return new WarpBuilder(arweave, stateCache, environment);
}
/**
* Allows to connect to any contract using its transaction id.
* @param contractTxId
* @param callingContract
*/
contract<State>(contractTxId: string, callingContract?: Contract, innerCallData?: InnerCallData): Contract<State> {
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 register(id: string, bundlrNode: BundlrNodeType): Promise<ContractDeploy> {
return await this.createContract.register(id, bundlrNode);
}
async createSource(
sourceData: SourceData,
wallet: ArWallet | CustomSignature | Signer,
disableBundling = false
): Promise<Transaction | DataItem> {
return await this.createContract.createSource(sourceData, wallet, disableBundling);
}
async saveSource(src: Transaction | DataItem, disableBundling?: boolean): Promise<string> {
return await this.createContract.saveSource(src, disableBundling);
}
/**
* Allows to connect to a contract that conforms to the Profit Sharing Token standard
* @param contractTxId
*/
pst(contractTxId: string): PstContract {
return new PstContractImpl(contractTxId, this);
}
useStateCache(stateCache: BasicSortKeyCache<EvalStateResult<unknown>>): Warp {
this.stateEvaluator.setCache(stateCache);
return this;
}
useContractCache(definition: BasicSortKeyCache<ContractDefinition<unknown>>, src: SortKeyCache<SrcCache>): Warp {
this.definitionLoader.setSrcCache(src);
this.definitionLoader.setCache(definition);
return this;
}
use(plugin: WarpPlugin<unknown, unknown>): Warp {
const pluginType = plugin.type();
if (!this.isPluginType(pluginType)) {
throw new Error(`Unknown plugin type ${pluginType}.`);
}
this.plugins.set(pluginType, plugin);
return this;
}
hasPlugin(type: WarpPluginType): boolean {
return this.plugins.has(type);
}
matchPlugins(type: string): WarpPluginType[] {
const pluginTypes = [...this.plugins.keys()];
return pluginTypes.filter((p) => p.match(type));
}
loadPlugin<P, Q>(type: WarpPluginType): WarpPlugin<P, Q> {
if (!this.hasPlugin(type)) {
throw new Error(`Plugin ${type} not registered.`);
}
return this.plugins.get(type) as WarpPlugin<P, Q>;
}
maybeLoadPlugin<P, Q>(type: WarpPluginType): WarpPlugin<P, Q> | null {
if (!this.hasPlugin(type)) {
return null;
}
return this.plugins.get(type) as WarpPlugin<P, Q>;
}
// Close cache connection
async close(): Promise<void> {
return Promise.all([
this.definitionLoader.getSrcCache().close(),
this.definitionLoader.getCache().close(),
this.stateEvaluator.getCache().close()
]).then();
}
async generateWallet(): Promise<Wallet> {
const wallet = await this.arweave.wallets.generate();
if (await this.testing.isArlocal()) {
await this.testing.addFunds(wallet);
}
return {
jwk: wallet,
address: await this.arweave.wallets.jwkToAddress(wallet)
};
}
private isPluginType(value: string): value is WarpPluginType {
return (
knownWarpPlugins.includes(value as WarpKnownPluginType) || knownWarpPluginsPartial.some((p) => value.match(p))
);
}
useKVStorageFactory(factory: KVStorageFactory): Warp {
this.kvStorageFactory = factory;
return this;
}
useGwUrl(url: string): Warp {
this._gwUrl = url;
return this;
}
gwUrl(): string {
return this._gwUrl;
}
}
export interface WarpAware {
set warp(warp: Warp);
}