From 9350f781dcadf481ebce7558cdecd6d27d43cc0c Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Wed, 24 Apr 2024 19:18:24 -0600 Subject: [PATCH 1/8] fix(types): use generic types and modify the requirements for init functions --- src/common.ts | 37 ++++++++++++--------------- src/common/ant.ts | 20 +++++---------- src/common/ar-io.ts | 18 +++++-------- src/common/arweave.ts | 23 +++++++++++++++++ src/common/contracts/warp-contract.ts | 15 +++-------- src/common/warp.ts | 28 ++++++++++++++++++++ src/constants.ts | 18 ------------- 7 files changed, 84 insertions(+), 75 deletions(-) create mode 100644 src/common/arweave.ts create mode 100644 src/common/warp.ts diff --git a/src/common.ts b/src/common.ts index 84068ed1..aa486be1 100644 --- a/src/common.ts +++ b/src/common.ts @@ -44,10 +44,15 @@ export type TransactionId = string; // TODO: append this with other configuration options (e.g. local vs. remote evaluation) export type ContractSigner = ArweaveSigner | ArconnectSigner; -export type WithSigner = { signer: ContractSigner }; // TODO: optionally allow JWK in place of signer +export type WithSigner> = { + signer: ContractSigner; +} & T; // TODO: optionally allow JWK in place of signer +export type OptionalSigner> = { + signer?: ContractSigner; +} & T; export type ContractConfiguration = | { - contract?: WarpContract | RemoteContract; + contract: WarpContract | RemoteContract; } | { contractTxId: string; @@ -63,13 +68,15 @@ export type EvaluationParameters> = { evaluationOptions?: EvaluationOptions | Record | undefined; } & T; -export type WriteParameters = { +export type ReadParameters = { functionName: string; - inputs: Input; - dryWrite?: boolean; - // TODO: add syncState and abortSignal options + inputs?: Input; }; +export type WriteParameters = WithSigner< + Required> +>; + export interface BaseContract { getState(params: EvaluationParameters): Promise; } @@ -79,10 +86,7 @@ export interface ReadContract { functionName, inputs, evaluationOptions, - }: EvaluationParameters<{ - functionName: string; - inputs?: Input; - }>): Promise; + }: EvaluationParameters>): Promise; } export interface WriteContract { @@ -95,15 +99,6 @@ export interface WriteContract { >): Promise; } -export interface SmartWeaveContract { - getContractState(params: EvaluationParameters): Promise; - readInteraction({ - functionName, - inputs, - evaluationOptions, - }: EvaluationParameters<{ functionName: string; inputs?: I }>): Promise; -} - // TODO: extend with additional methods export interface ArIOReadContract extends BaseContract { getGateway({ @@ -139,9 +134,9 @@ export interface ArIOReadContract extends BaseContract { getEpoch({ blockHeight, evaluationOptions, - }: { + }: EvaluationParameters<{ blockHeight: number; - } & EvaluationParameters): Promise; + }>): Promise; getCurrentEpoch({ evaluationOptions, }: EvaluationParameters): Promise; diff --git a/src/common/ant.ts b/src/common/ant.ts index 4b22dd1d..04ec1411 100644 --- a/src/common/ant.ts +++ b/src/common/ant.ts @@ -23,6 +23,7 @@ import { ContractSigner, EvaluationOptions, EvaluationParameters, + OptionalSigner, WithSigner, WriteInteractionResult, } from '../types.js'; @@ -83,20 +84,13 @@ export class ANT { * ``` */ static init( - config: ContractConfiguration & - WithSigner & - ({ contract: WarpContract } | { contractTxId: string }), + config: WithSigner< + { contract: WarpContract } | { contractTxId: string } + >, ): ANTWritable; - static init( - config?: ContractConfiguration & - ({ contract?: RemoteContract } | { contractTxId: string }), - ): ANTReadable; - static init( - config: ContractConfiguration & { - signer?: ContractSigner; - } = {}, - ) { - if (config?.signer) { + static init(config?: ContractConfiguration): ANTReadable; + static init(config: OptionalSigner) { + if (config.signer) { const signer = config.signer; const contract = this.createContract(config); return new ANTWritable({ signer, contract }); diff --git a/src/common/ar-io.ts b/src/common/ar-io.ts index e329f142..b112c179 100644 --- a/src/common/ar-io.ts +++ b/src/common/ar-io.ts @@ -31,6 +31,7 @@ import { Gateway, JoinNetworkParams, Observations, + OptionalSigner, RegistrationType, TransactionId, UpdateGatewaySettingsParams, @@ -98,19 +99,12 @@ export class ArIO { * ``` */ static init( - config: ContractConfiguration & - WithSigner & - ({ contract: WarpContract } | { contractTxId: string }), + config: WithSigner< + { contract: WarpContract } | { contractTxId: string } + >, ): ArIOWritable; - static init( - config?: ContractConfiguration & - ({ contract?: RemoteContract } | { contractTxId: string }), - ): ArIOReadable; - static init( - config: ContractConfiguration & { - signer?: ContractSigner; - } = {}, - ) { + static init(config?: ContractConfiguration): ArIOReadable; + static init(config?: OptionalSigner) { if (config?.signer) { const signer = config.signer; const contract = this.createContract(config); diff --git a/src/common/arweave.ts b/src/common/arweave.ts new file mode 100644 index 00000000..2d573c29 --- /dev/null +++ b/src/common/arweave.ts @@ -0,0 +1,23 @@ +/** + * Copyright (C) 2022-2024 Permanent Data Solutions, Inc. All Rights Reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import Arweave from 'arweave'; + +export const defaultArweave = Arweave.init({ + host: 'arweave.net', + port: 443, + protocol: 'https', +}); diff --git a/src/common/contracts/warp-contract.ts b/src/common/contracts/warp-contract.ts index 927b2cd1..1ee7d346 100644 --- a/src/common/contracts/warp-contract.ts +++ b/src/common/contracts/warp-contract.ts @@ -24,12 +24,12 @@ import { Warp, } from 'warp-contracts'; -import { defaultWarp } from '../../constants.js'; import { BaseContract, ContractSigner, EvaluationParameters, Logger, + OptionalSigner, ReadContract, WriteContract, WriteInteractionResult, @@ -39,6 +39,7 @@ import { sha256B64Url, toB64Url } from '../../utils/base64.js'; import { getContractManifest } from '../../utils/smartweave.js'; import { FailedRequestError, WriteInteractionError } from '../error.js'; import { DefaultLogger } from '../logger.js'; +import { defaultWarp } from '../warp.js'; LoggerFactory.INST.setOptions({ logLevel: 'fatal', @@ -99,8 +100,6 @@ export class WarpContract }, type: 'arweave', }); - //this.contract = this.contract.connect(warpSigner); - //this.signer = warpSigner; return warpSigner; } @@ -122,11 +121,7 @@ export class WarpContract return evaluationResult.cachedValue.state as T; } - async ensureContractInit({ - signer, - }: { - signer?: ContractSigner; - } = {}): Promise { + async ensureContractInit({ signer }: OptionalSigner = {}): Promise { this.logger.debug(`Checking contract initialized`, { contractTxId: this.contractTxId, }); @@ -190,9 +185,7 @@ export class WarpContract inputs, signer, // TODO: support dryWrite - }: EvaluationParameters> & { - signer: ContractSigner; - }): Promise { + }: WriteParameters): Promise { try { this.logger.debug(`Write interaction: ${functionName}`, { contractTxId: this.contractTxId, diff --git a/src/common/warp.ts b/src/common/warp.ts new file mode 100644 index 00000000..cb94e6f8 --- /dev/null +++ b/src/common/warp.ts @@ -0,0 +1,28 @@ +/** + * Copyright (C) 2022-2024 Permanent Data Solutions, Inc. All Rights Reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import { WarpFactory, defaultCacheOptions } from 'warp-contracts'; + +import { defaultArweave } from './arweave.js'; + +export const defaultWarp = WarpFactory.forMainnet( + { + ...defaultCacheOptions, + inMemory: true, + }, + true, + defaultArweave, +); diff --git a/src/constants.ts b/src/constants.ts index 8a2b45f1..d5651686 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -14,9 +14,6 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import Arweave from 'arweave'; -import { WarpFactory, defaultCacheOptions } from 'warp-contracts'; - export const ARWEAVE_TX_REGEX = new RegExp('^[a-zA-Z0-9_-]{43}$'); // sortkey: padded blockheight to 12, JS timestamp, hash of transactionID + block hash. Timestamp only applicable to L2 and normally is all zeros. export const SORT_KEY_REGEX = new RegExp( @@ -27,18 +24,3 @@ export const ARNS_TESTNET_REGISTRY_TX = export const ARNS_DEVNET_REGISTRY_TX = '_NctcA2sRy1-J4OmIQZbYFPM17piNcbdBPH2ncX2RL8'; - -export const defaultArweave = Arweave.init({ - host: 'ar-io.dev', - port: 443, - protocol: 'https', -}); - -export const defaultWarp = WarpFactory.forMainnet( - { - ...defaultCacheOptions, - inMemory: true, - }, - true, - defaultArweave, -); From 7f285bb73bebb384a0c20aafabc55e1fca76e34b Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Wed, 24 Apr 2024 20:02:29 -0600 Subject: [PATCH 2/8] fix(typeguards): make type guards accept unknowns --- src/utils/smartweave.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/utils/smartweave.ts b/src/utils/smartweave.ts index c1719cb9..4db7c31c 100644 --- a/src/utils/smartweave.ts +++ b/src/utils/smartweave.ts @@ -17,9 +17,9 @@ import Arweave from 'arweave'; import { EvaluationManifest } from 'warp-contracts'; -import { ContractConfiguration, SortKey } from '../common.js'; import { RemoteContract, WarpContract } from '../common/index.js'; import { SORT_KEY_REGEX } from '../constants.js'; +import { SortKey } from '../types.js'; import { tagsToObject, validateArweaveId } from './arweave.js'; export function isSortKey(sortKey: string): sortKey is SortKey { @@ -73,18 +73,20 @@ export async function getContractManifest({ return contractManifest; } -export function isContractConfiguration( - config: ContractConfiguration, -): config is { +export function isContractConfiguration(config: unknown): config is { contract: WarpContract | RemoteContract; } { - return 'contract' in config; + return typeof config === 'object' && config !== null && 'contract' in config; } export function isContractTxIdConfiguration( - config: ContractConfiguration, + config: unknown, ): config is { contractTxId: string } { return ( - 'contractTxId' in config && validateArweaveId(config.contractTxId) === true + typeof config === 'object' && + config !== null && + 'contractTxId' in config && + typeof config.contractTxId === 'string' && + validateArweaveId(config.contractTxId) === true ); } From fc8c26e3c33b27054ad69d6a406139d73283344f Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Wed, 24 Apr 2024 20:27:02 -0600 Subject: [PATCH 3/8] fix(arweave): default to the arweave node import to avoid issues with browser environments This is just a short-term hack. Suggest we make a PR to the arweave-js repo to export env specific arweave instances --- package.json | 2 +- src/common/arweave.ts | 2 +- src/utils/smartweave.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 52c371db..df9c0937 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "prepare": "husky install", "example:mjs": "yarn build:esm && node examples/node/index.mjs", "example:cjs": "yarn build:cjs && node examples/node/index.cjs", - "example:web": "yarn build:web && cp -r bundles/* examples/web && http-server --port 8080 --host -o examples/web" + "example:web": "yarn build:web && http-server --port 8080 --host -o examples/web" }, "devDependencies": { "@commitlint/cli": "^17.1.2", diff --git a/src/common/arweave.ts b/src/common/arweave.ts index 2d573c29..be6915ed 100644 --- a/src/common/arweave.ts +++ b/src/common/arweave.ts @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import Arweave from 'arweave'; +import { default as Arweave } from 'arweave/node'; export const defaultArweave = Arweave.init({ host: 'arweave.net', diff --git a/src/utils/smartweave.ts b/src/utils/smartweave.ts index 4db7c31c..20701826 100644 --- a/src/utils/smartweave.ts +++ b/src/utils/smartweave.ts @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import Arweave from 'arweave'; +import { default as Arweave } from 'arweave/node'; import { EvaluationManifest } from 'warp-contracts'; import { RemoteContract, WarpContract } from '../common/index.js'; From cd7b11860f03517bbf03fd294b80b53c4a13ba3d Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Wed, 24 Apr 2024 21:55:36 -0600 Subject: [PATCH 4/8] chore: move to tsconfig instead of complicating the import --- examples/web/index.html | 2 +- src/common/arweave.ts | 2 +- tsconfig.web.json | 5 ++++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/web/index.html b/examples/web/index.html index accefa71..0e08277c 100644 --- a/examples/web/index.html +++ b/examples/web/index.html @@ -23,7 +23,7 @@