Skip to content

Commit

Permalink
fix(init): cleanup init overload methods and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
dtfiedler committed Apr 25, 2024
1 parent eb51dd0 commit fa328d2
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 96 deletions.
6 changes: 3 additions & 3 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,12 @@ export type WithSigner<T = NonNullable<unknown>> = {
export type OptionalSigner<T = NonNullable<unknown>> = {
signer?: ContractSigner;
} & T;
export type ContractConfiguration =
export type ContractConfiguration<T = NonNullable<unknown>> =
| {
contract: WarpContract<unknown> | RemoteContract<unknown>;
contract?: WarpContract<T> | RemoteContract<T>;
}
| {
contractTxId: string;
contractTxId?: string;
};

export type EvaluationOptions = {
Expand Down
64 changes: 43 additions & 21 deletions src/common/ant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,18 @@ export class ANT {
* ANT.createContract({ contractTxId: 'myContractTxId' });
* ```
*/
static createContract(config: ContractConfiguration): WarpContract<ANTState> {
static createWriteableContract(
config: Required<ContractConfiguration<ANTState>>,
): WarpContract<ANTState> {
if (isContractConfiguration<ANTState>(config)) {
if (config.contract instanceof WarpContract) {
return config.contract;
}
return config.contract instanceof WarpContract
? config.contract
: new WarpContract<ANTState>(config.contract.configuration());
} else if (isContractTxIdConfiguration(config)) {
return new WarpContract<ANTState>({ contractTxId: config.contractTxId });
} else {
throw new InvalidContractConfigurationError();
}
throw new InvalidContractConfigurationError();
}

/**
Expand All @@ -83,16 +86,20 @@ export class ANT {
* const readable = ANT.init({ contract: myContract });
* ```
*/
static init(
config: WithSigner<
{ contract: WarpContract<ANTState> } | { contractTxId: string }
>,
): ANTWritable;
static init(config?: ContractConfiguration): ANTReadable;
static init(config: OptionalSigner<ContractConfiguration>) {
if (config.signer) {
const signer = config.signer;
const contract = this.createContract(config);
static init(config: Required<ContractConfiguration<ANTState>>): ANTReadable;
static init({
signer,
...config
}: WithSigner<
// must be a WarpContract to get a ArIOWriteable
{ contract: WarpContract<ANTState> } | { contractTxId: string }
>): ANTWritable;
static init({
signer,
...config
}: OptionalSigner<Required<ContractConfiguration<ANTState>>>) {
if (signer) {
const contract = this.createWriteableContract(config);
return new ANTWritable({ signer, contract });
} else {
return new ANTReadable(config);
Expand All @@ -103,13 +110,15 @@ export class ANT {
export class ANTReadable implements ANTReadContract {
protected contract: RemoteContract<ANTState> | WarpContract<ANTState>;

constructor(config: ContractConfiguration) {
constructor(config: Required<ContractConfiguration<ANTState>>) {
if (isContractConfiguration<ANTState>(config)) {
this.contract = config.contract;
} else if (isContractTxIdConfiguration(config)) {
this.contract = new RemoteContract<ANTState>({
contractTxId: config.contractTxId,
});
} else {
throw new InvalidContractConfigurationError();
}
}

Expand Down Expand Up @@ -319,7 +328,7 @@ export class ANTReadable implements ANTReadContract {
evaluationOptions,
}: EvaluationParameters<{ address: string }>): Promise<number> {
const balances = await this.getBalances({ evaluationOptions });
return balances[address];
return balances[address] || 0;
}
}

Expand All @@ -328,11 +337,24 @@ export class ANTWritable extends ANTReadable {
private signer: ContractSigner;

constructor({
contract,
signer,
}: { contract: WarpContract<ANTState> } & WithSigner) {
super({ contract });
this.signer = signer;
...config
}: WithSigner<
{ contract: WarpContract<ANTState> } | { contractTxId: string }
>) {
if (isContractConfiguration<ANTState>(config)) {
super({ contract: config.contract });
this.signer = signer;
} else if (isContractTxIdConfiguration(config)) {
super({
contract: new WarpContract<ANTState>({
contractTxId: config.contractTxId,
}),
});
this.signer = signer;
} else {
throw new InvalidContractConfigurationError();
}
}

/**
Expand Down
124 changes: 85 additions & 39 deletions src/common/ar-io.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,53 +61,80 @@ export class ArIO {
* ArIO.createContract({ contractTxId: 'myContractTxId' });
* ```
*/
static createContract(
config: ContractConfiguration,
static createWriteableContract(
config?: ContractConfiguration<ArIOState>,
): WarpContract<ArIOState> {
if (isContractConfiguration<ArIOState>(config)) {
if (config.contract instanceof WarpContract) {
return config.contract;
}
if (!config || Object.keys(config).length === 0) {
return new WarpContract<ArIOState>({
contractTxId: ARNS_TESTNET_REGISTRY_TX,
});
} else if (isContractConfiguration<ArIOState>(config)) {
return config.contract instanceof WarpContract
? config.contract
: new WarpContract<ArIOState>(config.contract.configuration());
} else if (isContractTxIdConfiguration(config)) {
return new WarpContract<ArIOState>({ contractTxId: config.contractTxId });
} else {
throw new InvalidContractConfigurationError();
}
throw new InvalidContractConfigurationError();
}

/**
* Initializes an ArIO instance.
*
* There are two overloads for this function:
* 1. When a signer is provided in the configuration, it returns an instance of ArIOWritable.
* 2. When a signer is NOT provided in the configuration, it returns an instance of ArIOReadable.
* There are multiple overloads for this function:
* 1. When nothing is provided, it returns an instance of ArIOReadable.
* 1. When a signer is provided, it returns an instance of ArIOWritable.
* 2. When a signer is NOT provided, it returns an instance of ArIOReadable.
*
*
* @param {ContractConfiguration & WithSigner} config - The configuration object.
* @param {WithSigner<ContractConfiguration<ArIOState>>} config - The configuration object.
* If a signer is provided, it should be an object that implements the ContractSigner interface.
*
* @returns {ArIOWritable | ArIOReadable} - An instance of ArIOWritable if a signer is provided, otherwise an instance of ArIOReadable.
* @throws {Error} - Throws an error if the configuration is invalid.
*
* @example
* Overload 1: When signer is provided
* Overload 1: When nothing is provide, ArIOReadable is returned.
* ```ts
* const writable = ArIO.init({ signer: mySigner, contract: myContract });
*```
* Overload 2: When signer is not provided
* const readable = ArIO.init();
* ```
* Overload 2: When signer is not provided with contract, ArIOReadable is returned.
* ```ts
* const readable = ArIO.init({ contract: myContract });
* ```
* Overload 3: When signer is not provided with a contractTxId, ArIOReadable is returned.
* ```ts
* const readable = ArIO.init({ contractTxId: 'myContractTxId' });
* ```
* Overload 4: When signer is provided without any contract configuration, ArIOWritable is returned.
* ```ts
* const writable = ArIO.init({ signer: mySigner });
*```
* Overload 5: When signer is provided with a contract configuration, ArIOWritable is returned.
* ```ts
* const writable = ArIO.init({ signer: mySigner, contract: myContract });
* ```
* Overload 6: When signer is provided with a contractTxId, ArIOWritable is returned.
* ```ts
* const writable = ArIO.init({ signer: mySigner, contractTxId: 'myContractTxId' });
* ```
*/
static init(): ArIOReadable;
static init({ signer }: WithSigner): ArIOWritable;
static init(
config: WithSigner<
{ contract: WarpContract<ArIOState> } | { contractTxId: string }
>,
): ArIOWritable;
static init(config?: ContractConfiguration): ArIOReadable;
static init(config?: OptionalSigner<ContractConfiguration>) {
if (config?.signer) {
const signer = config.signer;
const contract = this.createContract(config);
config?: Required<ContractConfiguration<ArIOState>>,
): ArIOReadable;
static init({
signer,
...config
}: WithSigner<
// must be a WarpContract to get a ArIOWriteable
{ contract: WarpContract<ArIOState> } | { contractTxId: string }
>): ArIOWritable;
static init(config?: OptionalSigner<ContractConfiguration<ArIOState>>) {
if (config && config.signer) {
const { signer, ...rest } = config;
const contract = this.createWriteableContract(rest);
return new ArIOWritable({ signer, contract });
} else {
return new ArIOReadable(config);
Expand All @@ -118,21 +145,19 @@ export class ArIO {
export class ArIOReadable implements ArIOReadContract {
protected contract: RemoteContract<ArIOState> | WarpContract<ArIOState>;

constructor(
config: ContractConfiguration = {
contract: new RemoteContract<ArIOState>({
constructor(config?: ContractConfiguration<ArIOState>) {
if (!config) {
this.contract = new RemoteContract<ArIOState>({
contractTxId: ARNS_TESTNET_REGISTRY_TX,
}),
},
) {
if (isContractConfiguration<ArIOState>(config)) {
});
} else if (isContractConfiguration<ArIOState>(config)) {
this.contract = config.contract;
} else if (isContractTxIdConfiguration(config)) {
this.contract = new RemoteContract<ArIOState>({
contractTxId: config.contractTxId,
});
} else {
throw new Error('Invalid configuration.');
throw new InvalidContractConfigurationError();
}
}

Expand Down Expand Up @@ -506,13 +531,34 @@ export class ArIOWritable extends ArIOReadable implements ArIOWriteContract {
protected declare contract: WarpContract<ArIOState>;
private signer: ContractSigner;
constructor({
contract,
signer,
}: {
contract: WarpContract<ArIOState>;
} & WithSigner) {
super({ contract });
this.signer = signer;
...config
}: WithSigner<
| {
contract?: WarpContract<ArIOState>;
}
| { contractTxId?: string }
>) {
if (!config) {

Check failure on line 542 in src/common/ar-io.ts

View workflow job for this annotation

GitHub Actions / build (18.x, lint)

Unexpected object value in conditional. The condition is always true

Check failure on line 542 in src/common/ar-io.ts

View workflow job for this annotation

GitHub Actions / build (20.x, lint)

Unexpected object value in conditional. The condition is always true
super({
contract: new WarpContract<ArIOState>({
contractTxId: ARNS_TESTNET_REGISTRY_TX,
}),
});
this.signer = signer;
} else if (isContractConfiguration<ArIOState>(config)) {
super({ contract: config.contract });
this.signer = signer;
} else if (isContractTxIdConfiguration(config)) {
super({
contract: new WarpContract<ArIOState>({
contractTxId: config.contractTxId,
}),
});
this.signer = signer;
} else {
throw new InvalidContractConfigurationError();
}
}

/**
Expand Down
16 changes: 9 additions & 7 deletions src/utils/smartweave.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { EvaluationManifest } from 'warp-contracts';

import { RemoteContract, WarpContract } from '../common/index.js';
import { SORT_KEY_REGEX } from '../constants.js';
import { SortKey } from '../types.js';
import { ContractConfiguration, SortKey } from '../types.js';
import { tagsToObject, validateArweaveId } from './arweave.js';

export function isSortKey(sortKey: string): sortKey is SortKey {
Expand Down Expand Up @@ -65,26 +65,28 @@ export async function getContractManifest({
arweave: Arweave;
contractTxId: string;
}): Promise<EvaluationManifest> {
const { tags: encodedTags } = await arweave.transactions.get(contractTxId);
const { tags: encodedTags } = await arweave.transactions
.get(contractTxId)
.catch(() => ({ tags: [] }));
const decodedTags = tagsToObject(encodedTags);
const contractManifestString = decodedTags['Contract-Manifest'] ?? '{}';
// TODO throw if manifest is missing
const contractManifest = JSON.parse(contractManifestString);
return contractManifest;
}

export function isContractConfiguration<T>(config: unknown): config is {
export function isContractConfiguration<T>(
config: ContractConfiguration<T>,
): config is {
contract: WarpContract<T> | RemoteContract<T>;
} {
return typeof config === 'object' && config !== null && 'contract' in config;
return 'contract' in config;
}

export function isContractTxIdConfiguration(
config: unknown,
config: ContractConfiguration,
): config is { contractTxId: string } {
return (
typeof config === 'object' &&
config !== null &&
'contractTxId' in config &&
typeof config.contractTxId === 'string' &&
validateArweaveId(config.contractTxId) === true
Expand Down

0 comments on commit fa328d2

Please sign in to comment.