From 163c2f188a362d1ffb41e7cb3ba6deee9fe5147e Mon Sep 17 00:00:00 2001 From: atticusofsparta Date: Fri, 11 Oct 2024 11:42:41 -0600 Subject: [PATCH 01/97] fix(validations): add zod schema validations on ant returns --- src/common/ant-registry.ts | 4 +- src/common/ant.ts | 19 +++- src/common/contracts/ao-process.ts | 2 +- src/common/http.ts | 2 +- src/common/io.ts | 28 ++--- src/node/index.ts | 2 +- src/types/ant-registry.ts | 26 +++++ src/types/ant.ts | 165 +++++++++++++++++++++++++++++ src/{ => types}/common.ts | 19 ++++ src/{types.ts => types/index.ts} | 0 src/{ => types}/io.ts | 131 +---------------------- src/{ => types}/token.ts | 2 +- src/utils/ao.ts | 45 +------- src/utils/arweave.ts | 2 +- src/utils/processes.ts | 6 +- src/web/index.ts | 2 +- tests/unit/ant.test.ts | 2 +- tests/unit/token.test.ts | 2 +- 18 files changed, 262 insertions(+), 197 deletions(-) create mode 100644 src/types/ant-registry.ts create mode 100644 src/types/ant.ts rename src/{ => types}/common.ts (87%) rename src/{types.ts => types/index.ts} (100%) rename src/{ => types}/io.ts (78%) rename src/{ => types}/token.ts (98%) diff --git a/src/common/ant-registry.ts b/src/common/ant-registry.ts index adb3b51e..eeb85a54 100644 --- a/src/common/ant-registry.ts +++ b/src/common/ant-registry.ts @@ -17,6 +17,8 @@ import { ANT_REGISTRY_ID } from '../constants.js'; import { AoANTRegistryRead, AoANTRegistryWrite, +} from '../types/ant-registry.js'; +import { AoMessageResult, AoSigner, OptionalSigner, @@ -24,7 +26,7 @@ import { WithSigner, isProcessConfiguration, isProcessIdConfiguration, -} from '../types.js'; +} from '../types/index.js'; import { createAoSigner } from '../utils/ao.js'; import { AOProcess, InvalidContractConfigurationError } from './index.js'; diff --git a/src/common/ant.ts b/src/common/ant.ts index 74f97056..4ad09ab0 100644 --- a/src/common/ant.ts +++ b/src/common/ant.ts @@ -13,12 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { z } from 'zod'; + import { + AntBalancesSchema, + AntControllersSchema, + AntInfoSchema, + AntRecordSchema, + AntRecordsSchema, + AntStateSchema, AoANTInfo, AoANTRead, AoANTRecord, AoANTState, AoANTWrite, +} from '../types/ant.js'; +import { AoMessageResult, AoSigner, OptionalSigner, @@ -28,7 +38,7 @@ import { WriteOptions, isProcessConfiguration, isProcessIdConfiguration, -} from '../types.js'; +} from '../types/index.js'; import { createAoSigner } from '../utils/ao.js'; import { AOProcess, InvalidContractConfigurationError } from './index.js'; @@ -76,6 +86,7 @@ export class AoANTReadable implements AoANTRead { const res = await this.process.read({ tags, }); + AntStateSchema.parse(res); return res; } @@ -84,6 +95,7 @@ export class AoANTReadable implements AoANTRead { const info = await this.process.read({ tags, }); + AntInfoSchema.parse(info); return info; } @@ -105,6 +117,7 @@ export class AoANTReadable implements AoANTRead { const record = await this.process.read({ tags, }); + AntRecordSchema.parse(record); return record; } @@ -121,6 +134,7 @@ export class AoANTReadable implements AoANTRead { const records = await this.process.read>({ tags, }); + AntRecordsSchema.parse(records); return records; } @@ -150,6 +164,7 @@ export class AoANTReadable implements AoANTRead { const controllers = await this.process.read({ tags, }); + AntControllersSchema.parse(controllers); return controllers; } @@ -192,6 +207,7 @@ export class AoANTReadable implements AoANTRead { const balances = await this.process.read>({ tags, }); + AntBalancesSchema.parse(balances); return balances; } @@ -212,6 +228,7 @@ export class AoANTReadable implements AoANTRead { const balance = await this.process.read({ tags, }); + z.number().parse(balance); return balance; } } diff --git a/src/common/contracts/ao-process.ts b/src/common/contracts/ao-process.ts index 2de06a03..8aa69421 100644 --- a/src/common/contracts/ao-process.ts +++ b/src/common/contracts/ao-process.ts @@ -15,7 +15,7 @@ */ import { connect } from '@permaweb/aoconnect'; -import { AOContract, AoClient, AoSigner } from '../../types.js'; +import { AOContract, AoClient, AoSigner } from '../../types/index.js'; import { safeDecode } from '../../utils/json.js'; import { version } from '../../version.js'; import { WriteInteractionError } from '../error.js'; diff --git a/src/common/http.ts b/src/common/http.ts index 943de9c0..be11c489 100644 --- a/src/common/http.ts +++ b/src/common/http.ts @@ -15,7 +15,7 @@ */ import { AxiosInstance } from 'axios'; -import { HTTPClient } from '../types.js'; +import { HTTPClient } from '../types/index.js'; import { createAxiosInstance } from '../utils/index.js'; import { FailedRequestError, NotFound, UnknownError } from './error.js'; import { ILogger, Logger } from './logger.js'; diff --git a/src/common/io.ts b/src/common/io.ts index 4193f711..c63e9da3 100644 --- a/src/common/io.ts +++ b/src/common/io.ts @@ -16,19 +16,6 @@ import Arweave from 'arweave'; import { IO_TESTNET_PROCESS_ID } from '../constants.js'; -import { - AoArNSNameData, - AoEpochData, - AoEpochSettings, - AoGateway, - AoIORead, - AoIOWrite, - AoRegistrationFees, - EpochInput, - isProcessConfiguration, - isProcessIdConfiguration, -} from '../io.js'; -import { AoSigner, mIOToken } from '../token.js'; import { AoArNSNameDataWithName, AoArNSReservedNameData, @@ -49,7 +36,20 @@ import { WalletAddress, WithSigner, WriteOptions, -} from '../types.js'; +} from '../types/index.js'; +import { + AoArNSNameData, + AoEpochData, + AoEpochSettings, + AoGateway, + AoIORead, + AoIOWrite, + AoRegistrationFees, + EpochInput, + isProcessConfiguration, + isProcessIdConfiguration, +} from '../types/io.js'; +import { AoSigner, mIOToken } from '../types/token.js'; import { createAoSigner } from '../utils/ao.js'; import { defaultArweave } from './arweave.js'; import { AOProcess } from './contracts/ao-process.js'; diff --git a/src/node/index.ts b/src/node/index.ts index a40d0e6c..cd943831 100644 --- a/src/node/index.ts +++ b/src/node/index.ts @@ -15,7 +15,7 @@ */ export { ArweaveSigner, ArconnectSigner } from '@dha-team/arbundles'; -export * from '../types.js'; +export * from '../types/index.js'; export * from '../common/index.js'; export * from '../constants.js'; export * from '../utils/index.js'; diff --git a/src/types/ant-registry.ts b/src/types/ant-registry.ts new file mode 100644 index 00000000..781bd5b3 --- /dev/null +++ b/src/types/ant-registry.ts @@ -0,0 +1,26 @@ +/** + * Copyright (C) 2022-2024 Permanent Data Solutions, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { AoMessageResult } from './common.js'; + +export interface AoANTRegistryRead { + accessControlList(params: { + address: string; + }): Promise<{ Owned: string[]; Controlled: string[] }>; +} + +export interface AoANTRegistryWrite extends AoANTRegistryRead { + register(params: { processId: string }): Promise; +} diff --git a/src/types/ant.ts b/src/types/ant.ts new file mode 100644 index 00000000..aeda1e99 --- /dev/null +++ b/src/types/ant.ts @@ -0,0 +1,165 @@ +/** + * Copyright (C) 2022-2024 Permanent Data Solutions, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { z } from 'zod'; + +import { Logger } from '../common/logger.js'; +import { AoMessageResult, WalletAddress, WriteOptions } from './common.js'; + +export type AoANTState = { + Name: string; + Ticker: string; + Denomination: number; + Owner: WalletAddress; + Controllers: WalletAddress[]; + Records: Record; + Balances: Record; + Logo: string; + TotalSupply: number; + Initialized: boolean; + ['Source-Code-TX-ID']: string; +}; + +export type AoANTInfo = { + Name: string; + Owner: string; + Handlers: string[]; + ['Source-Code-TX-ID']: string; + // token related + Ticker: string; + ['Total-Supply']: string; + Logo: string; + Denomination: string; +}; + +export type AoANTRecord = { + transactionId: string; + ttlSeconds: number; +}; + +export interface AoANTRead { + getState(): Promise; + getInfo(): Promise; + getRecord({ undername }): Promise; + getRecords(): Promise>; + getOwner(): Promise; + getControllers(): Promise; + getTicker(): Promise; + getName(): Promise; + getBalance({ address }: { address: WalletAddress }): Promise; + getBalances(): Promise>; +} + +export interface AoANTWrite extends AoANTRead { + transfer( + { target }: { target: WalletAddress }, + options?: WriteOptions, + ): Promise; + addController( + { + controller, + }: { + controller: WalletAddress; + }, + options?: WriteOptions, + ): Promise; + removeController( + { + controller, + }: { + controller: WalletAddress; + }, + options?: WriteOptions, + ): Promise; + setRecord( + { + undername, + transactionId, + ttlSeconds, + }: { + undername: string; + transactionId: string; + ttlSeconds: number; + }, + options?: WriteOptions, + ): Promise; + removeRecord( + { undername }: { undername: string }, + options?: WriteOptions, + ): Promise; + setTicker( + { ticker }: { ticker: string }, + options?: WriteOptions, + ): Promise; + setName( + { name }: { name: string }, + options?: WriteOptions, + ): Promise; +} + +export const AntRecordSchema = z + .object({ + transactionId: z.string(), + ttlSeconds: z.number(), + }) + .passthrough(); +export const AntRecordsSchema = z.record(z.string(), AntRecordSchema); + +export const AntControllersSchema = z.array(z.string()); +export const AntBalancesSchema = z.record(z.string(), z.number()); + +// using passThrough to require the minimum fields and allow others (eg TotalSupply, Logo, etc) +export const AntStateSchema = z + .object({ + Name: z.string(), + Ticker: z.string(), + Owner: z.string(), + Controllers: AntControllersSchema, + Records: AntRecordsSchema, + Balances: AntBalancesSchema, + ['Source-Code-TX-ID']: z.string(), + }) + .passthrough(); + +export const AntInfoSchema = z + .object({ + Name: z.string(), + Owner: z.string(), + ['Source-Code-TX-ID']: z.string(), + Ticker: z.string(), + ['Total-Supply']: z.string(), + Logo: z.string(), + Denomination: z.string(), + }) + .passthrough(); + +/** + * @param state + * @returns {boolean} + * @throws {z.ZodError} if the state object does not match the expected schema + */ +export function isAoANTState( + state: object, + logger: Logger = Logger.default, +): state is AoANTState { + try { + AntStateSchema.parse(state); + return true; + } catch (error) { + // this allows us to see the path of the error in the object as well as the expected schema on invalid fields + logger.error(error.issues); + return false; + } +} diff --git a/src/common.ts b/src/types/common.ts similarity index 87% rename from src/common.ts rename to src/types/common.ts index 528012bf..f2fde650 100644 --- a/src/common.ts +++ b/src/types/common.ts @@ -88,3 +88,22 @@ export interface AoClient { unmonitor: typeof unmonitor; dryrun: typeof dryrun; } + +export interface AOContract { + read({ + tags, + retries, + }: { + tags?: { name: string; value: string }[]; + retries?: number; + }): Promise; + send({ + tags, + data, + signer, + }: { + tags: { name: string; value: string }[]; + data: string | undefined; + signer: AoSigner; + }): Promise<{ id: string; result?: K }>; +} diff --git a/src/types.ts b/src/types/index.ts similarity index 100% rename from src/types.ts rename to src/types/index.ts diff --git a/src/io.ts b/src/types/io.ts similarity index 78% rename from src/io.ts rename to src/types/io.ts index c01be761..5b6afab4 100644 --- a/src/io.ts +++ b/src/types/io.ts @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { AOProcess } from './common/index.js'; -import { mIOToken } from './token.js'; +import { AOProcess } from '../common/index.js'; +import { validateArweaveId } from '../utils/arweave.js'; import { AoMessageResult, - AoSigner, AtLeastOne, BlockHeight, ProcessId, @@ -25,8 +24,8 @@ import { TransactionId, WalletAddress, WriteOptions, -} from './types.js'; -import { validateArweaveId } from './utils/arweave.js'; +} from './index.js'; +import { mIOToken } from './token.js'; // Pagination @@ -233,39 +232,6 @@ export type AoBalanceWithAddress = { balance: number; }; -// ANT Contract - -export type AoANTState = { - Name: string; - Ticker: string; - Denomination: number; - Owner: WalletAddress; - Controllers: WalletAddress[]; - Records: Record; - Balances: Record; - Logo: string; - TotalSupply: number; - Initialized: boolean; - ['Source-Code-TX-ID']: string; -}; - -export type AoANTInfo = { - Name: string; - Owner: string; - Handlers: string[]; - ['Source-Code-TX-ID']: string; - // token related - Ticker: string; - ['Total-Supply']: string; - Logo: string; - Denomination: string; -}; - -export type AoANTRecord = { - transactionId: string; - ttlSeconds: number; -}; - // Input types // TODO: confirm what is required or if all can be optional and defaults will be provided @@ -279,25 +245,6 @@ export type AoUpdateGatewaySettingsParams = AtLeastOne; // Interfaces -export interface AOContract { - read({ - tags, - retries, - }: { - tags?: { name: string; value: string }[]; - retries?: number; - }): Promise; - send({ - tags, - data, - signer, - }: { - tags: { name: string; value: string }[]; - data: string | undefined; - signer: AoSigner; - }): Promise<{ id: string; result?: K }>; -} - export interface AoIORead { // read interactions getInfo(): Promise<{ @@ -471,76 +418,6 @@ export interface AoIOWrite extends AoIORead { ): Promise; } -export interface AoANTRead { - getState(): Promise; - getInfo(): Promise; - getRecord({ undername }): Promise; - getRecords(): Promise>; - getOwner(): Promise; - getControllers(): Promise; - getTicker(): Promise; - getName(): Promise; - getBalance({ address }: { address: WalletAddress }): Promise; - getBalances(): Promise>; -} - -export interface AoANTWrite extends AoANTRead { - transfer( - { target }: { target: WalletAddress }, - options?: WriteOptions, - ): Promise; - addController( - { - controller, - }: { - controller: WalletAddress; - }, - options?: WriteOptions, - ): Promise; - removeController( - { - controller, - }: { - controller: WalletAddress; - }, - options?: WriteOptions, - ): Promise; - setRecord( - { - undername, - transactionId, - ttlSeconds, - }: { - undername: string; - transactionId: string; - ttlSeconds: number; - }, - options?: WriteOptions, - ): Promise; - removeRecord( - { undername }: { undername: string }, - options?: WriteOptions, - ): Promise; - setTicker( - { ticker }: { ticker: string }, - options?: WriteOptions, - ): Promise; - setName( - { name }: { name: string }, - options?: WriteOptions, - ): Promise; -} - -export interface AoANTRegistryRead { - accessControlList(params: { - address: string; - }): Promise<{ Owned: string[]; Controlled: string[] }>; -} - -export interface AoANTRegistryWrite extends AoANTRegistryRead { - register(params: { processId: string }): Promise; -} - // Typeguard functions export function isProcessConfiguration( config: object, diff --git a/src/token.ts b/src/types/token.ts similarity index 98% rename from src/token.ts rename to src/types/token.ts index 2ff0ce96..5b932339 100644 --- a/src/token.ts +++ b/src/types/token.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { MIO_PER_IO } from './constants.js'; +import { MIO_PER_IO } from '../constants.js'; interface Equatable { equals(other: T): boolean; diff --git a/src/utils/ao.ts b/src/utils/ao.ts index b5b6cfc7..ae694186 100644 --- a/src/utils/ao.ts +++ b/src/utils/ao.ts @@ -26,14 +26,13 @@ import { AOS_MODULE_ID, DEFAULT_SCHEDULER_ID, } from '../constants.js'; +import { AoANTRecord } from '../types/ant.js'; import { - AoANTRecord, - AoANTState, AoClient, AoSigner, ContractSigner, WalletAddress, -} from '../types.js'; +} from '../types/index.js'; export async function spawnANT({ signer, @@ -254,43 +253,3 @@ export function createAoSigner(signer: ContractSigner): AoSigner { return aoSigner; } - -// using passThrough to require the minimum fields and allow others (eg TotalSupply, Logo, etc) -export const AntStateSchema = z - .object({ - Name: z.string(), - Ticker: z.string(), - Owner: z.string(), - Controllers: z.array(z.string()), - Records: z.record( - z.string(), - z - .object({ - transactionId: z.string(), - ttlSeconds: z.number(), - }) - .passthrough(), - ), - Balances: z.record(z.string(), z.number()), - ['Source-Code-TX-ID']: z.string(), - }) - .passthrough(); - -/** - * @param state - * @returns {boolean} - * @throws {z.ZodError} if the state object does not match the expected schema - */ -export function isAoANTState( - state: object, - logger: Logger = Logger.default, -): state is AoANTState { - try { - AntStateSchema.parse(state); - return true; - } catch (error) { - // this allows us to see the path of the error in the object as well as the expected schema on invalid fields - logger.error(error.issues); - return false; - } -} diff --git a/src/utils/arweave.ts b/src/utils/arweave.ts index 6d5e3631..518a514c 100644 --- a/src/utils/arweave.ts +++ b/src/utils/arweave.ts @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { BlockHeight } from '../common.js'; import { ARWEAVE_TX_REGEX } from '../constants.js'; +import { BlockHeight } from '../types/common.js'; export const validateArweaveId = (id: string): boolean => { return ARWEAVE_TX_REGEX.test(id); diff --git a/src/utils/processes.ts b/src/utils/processes.ts index 5a8d00f1..506210af 100644 --- a/src/utils/processes.ts +++ b/src/utils/processes.ts @@ -21,14 +21,14 @@ import { ANT } from '../common/ant.js'; import { IO } from '../common/io.js'; import { ILogger, Logger } from '../common/logger.js'; import { IO_TESTNET_PROCESS_ID } from '../constants.js'; +import { AoANTRegistryRead } from '../types/ant-registry.js'; +import { AoANTState } from '../types/ant.js'; import { - AoANTRegistryRead, - AoANTState, AoArNSNameData, AoIORead, ProcessId, WalletAddress, -} from '../types.js'; +} from '../types/index.js'; /** * @beta This API is in beta and may change in the future. diff --git a/src/web/index.ts b/src/web/index.ts index a40d0e6c..cd943831 100644 --- a/src/web/index.ts +++ b/src/web/index.ts @@ -15,7 +15,7 @@ */ export { ArweaveSigner, ArconnectSigner } from '@dha-team/arbundles'; -export * from '../types.js'; +export * from '../types/index.js'; export * from '../common/index.js'; export * from '../constants.js'; export * from '../utils/index.js'; diff --git a/tests/unit/ant.test.ts b/tests/unit/ant.test.ts index 97e6ba43..af10391c 100644 --- a/tests/unit/ant.test.ts +++ b/tests/unit/ant.test.ts @@ -1,7 +1,7 @@ import { strict as assert } from 'node:assert'; import { describe, it } from 'node:test'; -import { isAoANTState } from '../../src/utils/ao.js'; +import { isAoANTState } from '../../src/types/ant'; const testAoANTState = { Name: 'TestToken', diff --git a/tests/unit/token.test.ts b/tests/unit/token.test.ts index 8a4dd88a..36c9b401 100644 --- a/tests/unit/token.test.ts +++ b/tests/unit/token.test.ts @@ -1,7 +1,7 @@ import { strict as assert } from 'node:assert'; import { describe, it } from 'node:test'; -import { IOToken, mIOToken } from '../../src/token.js'; +import { IOToken, mIOToken } from '../../src/types/token'; describe('IOToken', () => { it('should throw an error on invalid input', () => { From db1520a0f2dc9b5045ce0db56f46fddf7456474d Mon Sep 17 00:00:00 2001 From: atticusofsparta Date: Fri, 11 Oct 2024 13:17:58 -0600 Subject: [PATCH 02/97] fix(tests): add js path on imports --- tests/unit/ant.test.ts | 2 +- tests/unit/token.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/ant.test.ts b/tests/unit/ant.test.ts index af10391c..af666aa5 100644 --- a/tests/unit/ant.test.ts +++ b/tests/unit/ant.test.ts @@ -1,7 +1,7 @@ import { strict as assert } from 'node:assert'; import { describe, it } from 'node:test'; -import { isAoANTState } from '../../src/types/ant'; +import { isAoANTState } from '../../src/types/ant.js'; const testAoANTState = { Name: 'TestToken', diff --git a/tests/unit/token.test.ts b/tests/unit/token.test.ts index 36c9b401..b8dc277a 100644 --- a/tests/unit/token.test.ts +++ b/tests/unit/token.test.ts @@ -1,7 +1,7 @@ import { strict as assert } from 'node:assert'; import { describe, it } from 'node:test'; -import { IOToken, mIOToken } from '../../src/types/token'; +import { IOToken, mIOToken } from '../../src/types/token.js'; describe('IOToken', () => { it('should throw an error on invalid input', () => { From feba5873e7efd47f314f5e22561d0d0e07c26908 Mon Sep 17 00:00:00 2001 From: atticusofsparta Date: Fri, 11 Oct 2024 15:01:02 -0600 Subject: [PATCH 03/97] fix(schemas): add zod schemas and tests --- src/common/ant.ts | 56 +++++++++++-- src/types/ant.ts | 178 ++++++++++++++++++++++------------------- tests/unit/ant.test.ts | 151 +++++++++++++++++++++++++++------- 3 files changed, 266 insertions(+), 119 deletions(-) diff --git a/src/common/ant.ts b/src/common/ant.ts index 4ad09ab0..63405911 100644 --- a/src/common/ant.ts +++ b/src/common/ant.ts @@ -86,7 +86,13 @@ export class AoANTReadable implements AoANTRead { const res = await this.process.read({ tags, }); - AntStateSchema.parse(res); + const schemaResult = AntStateSchema.safeParse(res); + if (!schemaResult.success) { + throw new Error( + 'Invalid ANT State\n' + + JSON.stringify(schemaResult.error.format(), null, 2), + ); + } return res; } @@ -95,7 +101,13 @@ export class AoANTReadable implements AoANTRead { const info = await this.process.read({ tags, }); - AntInfoSchema.parse(info); + const schemaResult = AntInfoSchema.safeParse(info); + if (!schemaResult.success) { + throw new Error( + 'Invalid ANT Info\n' + + JSON.stringify(schemaResult.error.format(), null, 2), + ); + } return info; } @@ -117,7 +129,13 @@ export class AoANTReadable implements AoANTRead { const record = await this.process.read({ tags, }); - AntRecordSchema.parse(record); + const schemaResult = AntRecordSchema.safeParse(record); + if (!schemaResult.success) { + throw new Error( + 'Invalid ANT Record\n' + + JSON.stringify(schemaResult.error.format(), null, 2), + ); + } return record; } @@ -134,7 +152,13 @@ export class AoANTReadable implements AoANTRead { const records = await this.process.read>({ tags, }); - AntRecordsSchema.parse(records); + const schemaResult = AntRecordsSchema.safeParse(records); + if (!schemaResult.success) { + throw new Error( + 'Invalid ANT Records\n' + + JSON.stringify(schemaResult.error.format(), null, 2), + ); + } return records; } @@ -164,7 +188,13 @@ export class AoANTReadable implements AoANTRead { const controllers = await this.process.read({ tags, }); - AntControllersSchema.parse(controllers); + const schemaResult = AntControllersSchema.safeParse(controllers); + if (!schemaResult.success) { + throw new Error( + 'Invalid ANT Controllers\n' + + JSON.stringify(schemaResult.error.format(), null, 2), + ); + } return controllers; } @@ -207,7 +237,13 @@ export class AoANTReadable implements AoANTRead { const balances = await this.process.read>({ tags, }); - AntBalancesSchema.parse(balances); + const schemaResult = AntBalancesSchema.safeParse(balances); + if (!schemaResult.success) { + throw new Error( + 'Invalid ANT Balances\n' + + JSON.stringify(schemaResult.error.format(), null, 2), + ); + } return balances; } @@ -228,7 +264,13 @@ export class AoANTReadable implements AoANTRead { const balance = await this.process.read({ tags, }); - z.number().parse(balance); + const schemaResult = z.number().safeParse(balance); + if (!schemaResult.success) { + throw new Error( + 'Invalid ANT Balance\n' + + JSON.stringify(schemaResult.error.format(), null, 2), + ); + } return balance; } } diff --git a/src/types/ant.ts b/src/types/ant.ts index aeda1e99..ae2f799a 100644 --- a/src/types/ant.ts +++ b/src/types/ant.ts @@ -16,38 +16,105 @@ import { z } from 'zod'; import { Logger } from '../common/logger.js'; +import { ARWEAVE_TX_REGEX } from '../constants.js'; import { AoMessageResult, WalletAddress, WriteOptions } from './common.js'; -export type AoANTState = { - Name: string; - Ticker: string; - Denomination: number; - Owner: WalletAddress; - Controllers: WalletAddress[]; - Records: Record; - Balances: Record; - Logo: string; - TotalSupply: number; - Initialized: boolean; - ['Source-Code-TX-ID']: string; -}; +/** + * example error: + * { + "code": "custom", + "message": "Must be an Arweave Transaction ID", + "path": [ + "Records", + "record1", + "transactionId" + ] + }, + */ +export const ArweaveTxIdSchema = z + .string({ + description: 'Arweave Transaction ID', + }) + .refine((val) => ARWEAVE_TX_REGEX.test(val), { + message: 'Must be an Arweave Transaction ID', + }); +export const AntRecordSchema = z.object({ + transactionId: ArweaveTxIdSchema.describe('The Target ID of the undername'), + ttlSeconds: z.number(), +}); +export type AoANTRecord = z.infer; + +export const AntRecordsSchema = z.record(z.string(), AntRecordSchema); +export const AntControllersSchema = z.array( + ArweaveTxIdSchema.describe('Controller address'), +); +export const AntBalancesSchema = z.record( + ArweaveTxIdSchema.describe('Holder address'), + z.number(), +); + +export const AntStateSchema = z.object({ + Name: z.string().describe('The name of the ANT.'), + Ticker: z.string().describe('The ticker symbol for the ANT.'), + Denomination: z.number(), + Owner: ArweaveTxIdSchema.describe('The Owners address.'), + Controllers: AntControllersSchema.describe( + 'Controllers of the ANT who have administrative privileges.', + ), + Records: AntRecordsSchema.describe('Records associated with the ANT.'), + Balances: AntBalancesSchema.describe( + 'Balance details for each address holding the ANT.', + ), + Logo: ArweaveTxIdSchema.describe('Transaction ID of the ANT logo.'), + TotalSupply: z + .number() + .describe('Total supply of the ANT in circulation.') + .min(0, { message: 'Total supply must be a non-negative number' }), + Initialized: z + .boolean() + .describe('Flag indicating whether the ANT has been initialized.'), + ['Source-Code-TX-ID']: ArweaveTxIdSchema.describe( + 'Transaction ID of the Source Code for the ANT.', + ), +}); + +export type AoANTState = z.infer; -export type AoANTInfo = { - Name: string; - Owner: string; - Handlers: string[]; - ['Source-Code-TX-ID']: string; - // token related - Ticker: string; - ['Total-Supply']: string; - Logo: string; - Denomination: string; -}; +export const AntInfoSchema = z.object({ + Name: z.string().describe('The name of the ANT.'), + Owner: ArweaveTxIdSchema.describe('The Owners address.'), + ['Source-Code-TX-ID']: ArweaveTxIdSchema.describe( + 'Transaction ID of the Source Code for the ANT.', + ), + Ticker: z.string().describe('The ticker symbol for the ANT.'), + ['Total-Supply']: z + .number() + .describe('Total supply of the ANT in circulation.') + .min(0, { message: 'Total supply must be a non-negative number' }), + Logo: ArweaveTxIdSchema.describe('Transaction ID of the ANT logo.'), + Denomination: z.number(), +}); -export type AoANTRecord = { - transactionId: string; - ttlSeconds: number; -}; +export type AoANTInfo = z.infer; + +/** + * @param state + * @returns {boolean} + * @throws {z.ZodError} if the state object does not match the expected schema + */ +export function isAoANTState( + state: object, + logger: Logger = Logger.default, +): state is AoANTState { + try { + AntStateSchema.parse(state); + return true; + } catch (error) { + // this allows us to see the path of the error in the object as well as the expected schema on invalid fields + logger.error(error.issues); + return false; + } +} export interface AoANTRead { getState(): Promise; @@ -108,58 +175,3 @@ export interface AoANTWrite extends AoANTRead { options?: WriteOptions, ): Promise; } - -export const AntRecordSchema = z - .object({ - transactionId: z.string(), - ttlSeconds: z.number(), - }) - .passthrough(); -export const AntRecordsSchema = z.record(z.string(), AntRecordSchema); - -export const AntControllersSchema = z.array(z.string()); -export const AntBalancesSchema = z.record(z.string(), z.number()); - -// using passThrough to require the minimum fields and allow others (eg TotalSupply, Logo, etc) -export const AntStateSchema = z - .object({ - Name: z.string(), - Ticker: z.string(), - Owner: z.string(), - Controllers: AntControllersSchema, - Records: AntRecordsSchema, - Balances: AntBalancesSchema, - ['Source-Code-TX-ID']: z.string(), - }) - .passthrough(); - -export const AntInfoSchema = z - .object({ - Name: z.string(), - Owner: z.string(), - ['Source-Code-TX-ID']: z.string(), - Ticker: z.string(), - ['Total-Supply']: z.string(), - Logo: z.string(), - Denomination: z.string(), - }) - .passthrough(); - -/** - * @param state - * @returns {boolean} - * @throws {z.ZodError} if the state object does not match the expected schema - */ -export function isAoANTState( - state: object, - logger: Logger = Logger.default, -): state is AoANTState { - try { - AntStateSchema.parse(state); - return true; - } catch (error) { - // this allows us to see the path of the error in the object as well as the expected schema on invalid fields - logger.error(error.issues); - return false; - } -} diff --git a/tests/unit/ant.test.ts b/tests/unit/ant.test.ts index af666aa5..1cb40741 100644 --- a/tests/unit/ant.test.ts +++ b/tests/unit/ant.test.ts @@ -1,36 +1,129 @@ import { strict as assert } from 'node:assert'; import { describe, it } from 'node:test'; +import { z } from 'zod'; -import { isAoANTState } from '../../src/types/ant.js'; - -const testAoANTState = { - Name: 'TestToken', - Ticker: 'TST', - Denomination: 1, - Owner: ''.padEnd(43, '1'), - Controllers: [''.padEnd(43, '2')], - Records: { - record1: { - transactionId: ''.padEnd(43, '1'), - ttlSeconds: 3600, - }, - }, - Balances: { - [''.padEnd(43, '1')]: 1, - }, - Logo: ''.padEnd(43, '1'), - TotalSupply: 0, - Initialized: true, - ['Source-Code-TX-ID']: ''.padEnd(43, '1'), -}; -describe('ANT', () => { - it('should validate accurate ANT state', () => { - const res = isAoANTState(testAoANTState); - assert.strictEqual(res, true); +import { + AntInfoSchema, + AntStateSchema, + isAoANTState, +} from '../../src/types/ant.js'; + +const stub_address = 'valid-address'.padEnd(43, '1'); + +describe('ANT Schemas', () => { + it('should validate AntStateSchema', () => { + const validState = { + Name: 'TestToken', + Ticker: 'TST', + Denomination: 0, + Owner: stub_address, + Controllers: [stub_address], + Records: { + record1: { + transactionId: stub_address, + ttlSeconds: 3600, + }, + }, + Balances: { + [stub_address]: 1, + }, + Logo: stub_address, + TotalSupply: 1, + Initialized: true, + ['Source-Code-TX-ID']: stub_address, + }; + const invalidState = { + Name: 'TestToken', + Ticker: 'TST', + Denomination: 0, + Owner: stub_address, + Controllers: [stub_address], + Records: { + record1: { + transactionId: 'invalid-id', + ttlSeconds: '3600', + }, + }, + Balances: { + [stub_address]: 1, + }, + Logo: stub_address, + TotalSupply: -1, + Initialized: true, + ['Source-Code-TX-ID']: stub_address, + }; + + assert.doesNotThrow(() => AntStateSchema.parse(validState)); + assert.throws(() => AntStateSchema.parse(invalidState), z.ZodError); }); - it('should invalidate inaccurate ANT state', () => { - const res = isAoANTState({ ...testAoANTState, Name: 1 }); - assert.strictEqual(res, false); + it('should validate AntInfoSchema', () => { + const validInfo = { + Name: 'TestToken', + Owner: stub_address, + ['Source-Code-TX-ID']: stub_address, + Ticker: 'TST', + ['Total-Supply']: 1, + Logo: stub_address, + Denomination: 0, + }; + const invalidInfo = { + Name: 'TestToken', + Owner: stub_address, + ['Source-Code-TX-ID']: stub_address, + Ticker: 'TST', + ['Total-Supply']: 1000, + Logo: stub_address, + Denomination: '1', + }; + + assert.doesNotThrow(() => AntInfoSchema.parse(validInfo)); + assert.throws(() => AntInfoSchema.parse(invalidInfo), z.ZodError); + }); + + it('should validate isAoANTState', () => { + const validState = { + Name: 'TestToken', + Ticker: 'TST', + Denomination: 0, + Owner: stub_address, + Controllers: [stub_address], + Records: { + record1: { + transactionId: stub_address, + ttlSeconds: 3600, + }, + }, + Balances: { + [stub_address]: 1, + }, + Logo: stub_address, + TotalSupply: 0, + Initialized: true, + ['Source-Code-TX-ID']: stub_address, + }; + const invalidState = { + Name: 'TestToken', + Ticker: 'TST', + Denomination: 0, + Owner: stub_address, + Controllers: [stub_address], + Records: { + record1: { + transactionId: 'invalid-id', + ttlSeconds: '3600', + }, + }, + Balances: { + [stub_address]: 1, + }, + Logo: stub_address, + TotalSupply: -1, + Initialized: true, + ['Source-Code-TX-ID']: stub_address, + }; + + assert.strictEqual(isAoANTState(validState), true); + assert.strictEqual(isAoANTState(invalidState), false); }); }); From 9cb2776da4a9886a1467abbf51b7c4f814bdb7a6 Mon Sep 17 00:00:00 2001 From: atticusofsparta Date: Fri, 11 Oct 2024 15:29:45 -0600 Subject: [PATCH 04/97] fix(schemas): add passthrough on schema checks for ants --- src/common/ant.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/common/ant.ts b/src/common/ant.ts index 63405911..88239e02 100644 --- a/src/common/ant.ts +++ b/src/common/ant.ts @@ -86,7 +86,13 @@ export class AoANTReadable implements AoANTRead { const res = await this.process.read({ tags, }); - const schemaResult = AntStateSchema.safeParse(res); + const schemaResult = AntStateSchema.passthrough() + .and( + z.object({ + Records: z.record(z.string(), AntRecordSchema.passthrough()), + }), + ) + .safeParse(res); if (!schemaResult.success) { throw new Error( 'Invalid ANT State\n' + @@ -101,7 +107,7 @@ export class AoANTReadable implements AoANTRead { const info = await this.process.read({ tags, }); - const schemaResult = AntInfoSchema.safeParse(info); + const schemaResult = AntInfoSchema.passthrough().safeParse(info); if (!schemaResult.success) { throw new Error( 'Invalid ANT Info\n' + @@ -129,7 +135,7 @@ export class AoANTReadable implements AoANTRead { const record = await this.process.read({ tags, }); - const schemaResult = AntRecordSchema.safeParse(record); + const schemaResult = AntRecordSchema.passthrough().safeParse(record); if (!schemaResult.success) { throw new Error( 'Invalid ANT Record\n' + From f3284ed038cebc8ddacae3ac0de3f740dcf86106 Mon Sep 17 00:00:00 2001 From: atticusofsparta Date: Mon, 14 Oct 2024 09:39:43 -0600 Subject: [PATCH 05/97] fix(schemas): update ant schema and tests --- src/types/ant.ts | 30 ++++++++++++++++++++++++------ tests/unit/ant.test.ts | 4 ++-- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/types/ant.ts b/src/types/ant.ts index ae2f799a..b47c4b3a 100644 --- a/src/types/ant.ts +++ b/src/types/ant.ts @@ -38,6 +38,18 @@ export const ArweaveTxIdSchema = z .refine((val) => ARWEAVE_TX_REGEX.test(val), { message: 'Must be an Arweave Transaction ID', }); + +export const IntegerStringSchema = z + .string({ + description: 'Integer String', + }) + .refine( + (val) => { + const num = parseInt(val); + return Number.isInteger(num) && num >= 0; + }, + { message: 'Must be a non negative integer string' }, + ); export const AntRecordSchema = z.object({ transactionId: ArweaveTxIdSchema.describe('The Target ID of the undername'), ttlSeconds: z.number(), @@ -56,7 +68,12 @@ export const AntBalancesSchema = z.record( export const AntStateSchema = z.object({ Name: z.string().describe('The name of the ANT.'), Ticker: z.string().describe('The ticker symbol for the ANT.'), - Denomination: z.number(), + Denomination: z + .number() + .describe( + 'The number of decimal places to use for the ANT. Defaults to 0 if not set representing whole numbers.', + ) + .min(0, { message: 'Denomination must be a non-negative number' }), Owner: ArweaveTxIdSchema.describe('The Owners address.'), Controllers: AntControllersSchema.describe( 'Controllers of the ANT who have administrative privileges.', @@ -87,12 +104,13 @@ export const AntInfoSchema = z.object({ 'Transaction ID of the Source Code for the ANT.', ), Ticker: z.string().describe('The ticker symbol for the ANT.'), - ['Total-Supply']: z - .number() - .describe('Total supply of the ANT in circulation.') - .min(0, { message: 'Total supply must be a non-negative number' }), + ['Total-Supply']: IntegerStringSchema.describe( + 'Total supply of the ANT in circulation.', + ), Logo: ArweaveTxIdSchema.describe('Transaction ID of the ANT logo.'), - Denomination: z.number(), + Denomination: IntegerStringSchema.describe( + 'The number of decimal places to use for the ANT. Defaults to 0 if not set representing whole numbers.', + ), }); export type AoANTInfo = z.infer; diff --git a/tests/unit/ant.test.ts b/tests/unit/ant.test.ts index 1cb40741..a79c2989 100644 --- a/tests/unit/ant.test.ts +++ b/tests/unit/ant.test.ts @@ -63,9 +63,9 @@ describe('ANT Schemas', () => { Owner: stub_address, ['Source-Code-TX-ID']: stub_address, Ticker: 'TST', - ['Total-Supply']: 1, + ['Total-Supply']: '1', Logo: stub_address, - Denomination: 0, + Denomination: '0', }; const invalidInfo = { Name: 'TestToken', From 367537a07b97180bfffee6cf0d6133cc03afdc4e Mon Sep 17 00:00:00 2001 From: atticusofsparta Date: Mon, 14 Oct 2024 15:50:54 -0600 Subject: [PATCH 06/97] fix(util): create schema parsing util to pretty format errors --- src/common/ant.ts | 65 +++++++++------------------------------------ src/types/ant.ts | 18 +++---------- src/types/common.ts | 1 + src/utils/index.ts | 1 + src/utils/schema.ts | 15 +++++++++++ 5 files changed, 34 insertions(+), 66 deletions(-) create mode 100644 src/utils/schema.ts diff --git a/src/common/ant.ts b/src/common/ant.ts index 88239e02..ed0c9b16 100644 --- a/src/common/ant.ts +++ b/src/common/ant.ts @@ -40,6 +40,7 @@ import { isProcessIdConfiguration, } from '../types/index.js'; import { createAoSigner } from '../utils/ao.js'; +import { parseSchemaResult } from '../utils/schema.js'; import { AOProcess, InvalidContractConfigurationError } from './index.js'; export class ANT { @@ -86,19 +87,15 @@ export class AoANTReadable implements AoANTRead { const res = await this.process.read({ tags, }); - const schemaResult = AntStateSchema.passthrough() - .and( + + parseSchemaResult( + AntStateSchema.passthrough().and( z.object({ Records: z.record(z.string(), AntRecordSchema.passthrough()), }), - ) - .safeParse(res); - if (!schemaResult.success) { - throw new Error( - 'Invalid ANT State\n' + - JSON.stringify(schemaResult.error.format(), null, 2), - ); - } + ), + res, + ); return res; } @@ -107,13 +104,7 @@ export class AoANTReadable implements AoANTRead { const info = await this.process.read({ tags, }); - const schemaResult = AntInfoSchema.passthrough().safeParse(info); - if (!schemaResult.success) { - throw new Error( - 'Invalid ANT Info\n' + - JSON.stringify(schemaResult.error.format(), null, 2), - ); - } + parseSchemaResult(AntInfoSchema.passthrough(), info); return info; } @@ -135,13 +126,7 @@ export class AoANTReadable implements AoANTRead { const record = await this.process.read({ tags, }); - const schemaResult = AntRecordSchema.passthrough().safeParse(record); - if (!schemaResult.success) { - throw new Error( - 'Invalid ANT Record\n' + - JSON.stringify(schemaResult.error.format(), null, 2), - ); - } + parseSchemaResult(AntRecordSchema.passthrough(), record); return record; } @@ -158,13 +143,7 @@ export class AoANTReadable implements AoANTRead { const records = await this.process.read>({ tags, }); - const schemaResult = AntRecordsSchema.safeParse(records); - if (!schemaResult.success) { - throw new Error( - 'Invalid ANT Records\n' + - JSON.stringify(schemaResult.error.format(), null, 2), - ); - } + parseSchemaResult(AntRecordsSchema, records); return records; } @@ -194,13 +173,7 @@ export class AoANTReadable implements AoANTRead { const controllers = await this.process.read({ tags, }); - const schemaResult = AntControllersSchema.safeParse(controllers); - if (!schemaResult.success) { - throw new Error( - 'Invalid ANT Controllers\n' + - JSON.stringify(schemaResult.error.format(), null, 2), - ); - } + parseSchemaResult(AntControllersSchema, controllers); return controllers; } @@ -243,13 +216,7 @@ export class AoANTReadable implements AoANTRead { const balances = await this.process.read>({ tags, }); - const schemaResult = AntBalancesSchema.safeParse(balances); - if (!schemaResult.success) { - throw new Error( - 'Invalid ANT Balances\n' + - JSON.stringify(schemaResult.error.format(), null, 2), - ); - } + parseSchemaResult(AntBalancesSchema, balances); return balances; } @@ -270,13 +237,7 @@ export class AoANTReadable implements AoANTRead { const balance = await this.process.read({ tags, }); - const schemaResult = z.number().safeParse(balance); - if (!schemaResult.success) { - throw new Error( - 'Invalid ANT Balance\n' + - JSON.stringify(schemaResult.error.format(), null, 2), - ); - } + parseSchemaResult(z.number(), balance); return balance; } } diff --git a/src/types/ant.ts b/src/types/ant.ts index b47c4b3a..7a8f8557 100644 --- a/src/types/ant.ts +++ b/src/types/ant.ts @@ -93,6 +93,7 @@ export const AntStateSchema = z.object({ ['Source-Code-TX-ID']: ArweaveTxIdSchema.describe( 'Transaction ID of the Source Code for the ANT.', ), + Handlers: z.array(z.string()), }); export type AoANTState = z.infer; @@ -116,22 +117,11 @@ export const AntInfoSchema = z.object({ export type AoANTInfo = z.infer; /** - * @param state + * @param state {object} * @returns {boolean} - * @throws {z.ZodError} if the state object does not match the expected schema */ -export function isAoANTState( - state: object, - logger: Logger = Logger.default, -): state is AoANTState { - try { - AntStateSchema.parse(state); - return true; - } catch (error) { - // this allows us to see the path of the error in the object as well as the expected schema on invalid fields - logger.error(error.issues); - return false; - } +export function isAoANTState(state: object): state is AoANTState { + return AntStateSchema.safeParse(state).success; } export interface AoANTRead { diff --git a/src/types/common.ts b/src/types/common.ts index f2fde650..764cd40f 100644 --- a/src/types/common.ts +++ b/src/types/common.ts @@ -23,6 +23,7 @@ import { spawn, unmonitor, } from '@permaweb/aoconnect'; +import { z } from 'zod'; import { AoSigner } from './token.js'; diff --git a/src/utils/index.ts b/src/utils/index.ts index d7e8dd72..c35511df 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -18,3 +18,4 @@ export * from './http-client.js'; export * from './ao.js'; export * from './json.js'; export * from './processes.js'; +export * from './schema.js'; diff --git a/src/utils/schema.ts b/src/utils/schema.ts new file mode 100644 index 00000000..6eb874bf --- /dev/null +++ b/src/utils/schema.ts @@ -0,0 +1,15 @@ +import { z } from 'zod'; + +/** + * + * @param schema - zod schema + * @param v - value to parse + * @throws {z.SafeParseError} - if the value fails to parse + */ +export function parseSchemaResult(schema: z.ZodTypeAny, v: unknown) { + const schemaResult = schema.safeParse(v); + if (!schemaResult.success) { + throw new Error(JSON.stringify(schemaResult.error.format(), null, 2)); + } + return schemaResult; +} From 44cc472139ddc9462f024b4f6877643fcedec034 Mon Sep 17 00:00:00 2001 From: atticusofsparta Date: Mon, 14 Oct 2024 16:15:23 -0600 Subject: [PATCH 07/97] fix(schema): specify HandlerNames instead of Handlers --- src/types/ant.ts | 5 +++-- src/types/common.ts | 1 - src/utils/schema.ts | 15 +++++++++++++++ tests/e2e/esm/index.test.js | 2 +- tests/unit/ant.test.ts | 2 ++ 5 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/types/ant.ts b/src/types/ant.ts index 7a8f8557..2d7ebee2 100644 --- a/src/types/ant.ts +++ b/src/types/ant.ts @@ -15,7 +15,6 @@ */ import { z } from 'zod'; -import { Logger } from '../common/logger.js'; import { ARWEAVE_TX_REGEX } from '../constants.js'; import { AoMessageResult, WalletAddress, WriteOptions } from './common.js'; @@ -93,7 +92,6 @@ export const AntStateSchema = z.object({ ['Source-Code-TX-ID']: ArweaveTxIdSchema.describe( 'Transaction ID of the Source Code for the ANT.', ), - Handlers: z.array(z.string()), }); export type AoANTState = z.infer; @@ -112,6 +110,9 @@ export const AntInfoSchema = z.object({ Denomination: IntegerStringSchema.describe( 'The number of decimal places to use for the ANT. Defaults to 0 if not set representing whole numbers.', ), + HandlerNames: z + .array(z.string({ description: 'Handler Name' })) + .describe('List of handlers for the ANT.'), }); export type AoANTInfo = z.infer; diff --git a/src/types/common.ts b/src/types/common.ts index 764cd40f..f2fde650 100644 --- a/src/types/common.ts +++ b/src/types/common.ts @@ -23,7 +23,6 @@ import { spawn, unmonitor, } from '@permaweb/aoconnect'; -import { z } from 'zod'; import { AoSigner } from './token.js'; diff --git a/src/utils/schema.ts b/src/utils/schema.ts index 6eb874bf..5e7bf511 100644 --- a/src/utils/schema.ts +++ b/src/utils/schema.ts @@ -1,3 +1,18 @@ +/** + * Copyright (C) 2022-2024 Permanent Data Solutions, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ import { z } from 'zod'; /** diff --git a/tests/e2e/esm/index.test.js b/tests/e2e/esm/index.test.js index ab23682b..3312a955 100644 --- a/tests/e2e/esm/index.test.js +++ b/tests/e2e/esm/index.test.js @@ -364,7 +364,7 @@ describe('ANTRegistry', async () => { }); describe('ANT', async () => { - const processId = 'aWI_dq1JH7facsulLuas1X3l5dkKuWtixcZDYMw9mpg'; + const processId = 'YcxE5IbqZYK72H64ELoysxiJ-0wb36deYPv55wgl8xo'; const ant = ANT.init({ processId, }); diff --git a/tests/unit/ant.test.ts b/tests/unit/ant.test.ts index a79c2989..0f778cd1 100644 --- a/tests/unit/ant.test.ts +++ b/tests/unit/ant.test.ts @@ -66,6 +66,7 @@ describe('ANT Schemas', () => { ['Total-Supply']: '1', Logo: stub_address, Denomination: '0', + HandlerNames: ['handler1', 'handler2'], }; const invalidInfo = { Name: 'TestToken', @@ -75,6 +76,7 @@ describe('ANT Schemas', () => { ['Total-Supply']: 1000, Logo: stub_address, Denomination: '1', + HandlerNames: ['handler1', 'handler2'], }; assert.doesNotThrow(() => AntInfoSchema.parse(validInfo)); From 6ec52e4b07b5731f9388239ada9fd0f64373b445 Mon Sep 17 00:00:00 2001 From: atticusofsparta Date: Tue, 15 Oct 2024 15:36:43 -0600 Subject: [PATCH 08/97] fix(schema): update handlers schema --- src/types/ant.ts | 41 ++++++++++++++++++++++++++++++++++++++--- tests/unit/ant.test.ts | 5 +++-- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/types/ant.ts b/src/types/ant.ts index 2d7ebee2..79b9c390 100644 --- a/src/types/ant.ts +++ b/src/types/ant.ts @@ -49,6 +49,7 @@ export const IntegerStringSchema = z }, { message: 'Must be a non negative integer string' }, ); + export const AntRecordSchema = z.object({ transactionId: ArweaveTxIdSchema.describe('The Target ID of the undername'), ttlSeconds: z.number(), @@ -95,6 +96,37 @@ export const AntStateSchema = z.object({ }); export type AoANTState = z.infer; +export const AntHandlerNames = [ + 'evolve', + '_eval', + '_default', + 'transfer', + 'balance', + 'balances', + 'totalSupply', + 'info', + 'addController', + 'removeController', + 'controllers', + 'setRecord', + 'removeRecord', + 'record', + 'records', + 'setName', + 'setTicker', + 'initializeState', + 'state', +]; +export const AntHandlersSchema = z + .array(z.string({ description: 'Handler Name' })) + .refine( + (antHandlers: string[]) => { + return AntHandlerNames.every((handler) => antHandlers.includes(handler)); + }, + { + message: 'ANT is missing required handlers', + }, + ); export const AntInfoSchema = z.object({ Name: z.string().describe('The name of the ANT.'), @@ -110,9 +142,12 @@ export const AntInfoSchema = z.object({ Denomination: IntegerStringSchema.describe( 'The number of decimal places to use for the ANT. Defaults to 0 if not set representing whole numbers.', ), - HandlerNames: z - .array(z.string({ description: 'Handler Name' })) - .describe('List of handlers for the ANT.'), + Handlers: AntHandlersSchema.optional().describe( + 'List of handlers for the ANT.', + ), + HandlerNames: AntHandlersSchema.optional().describe( + 'Deprecated: List of handlers for the ANT. Use "Handlers" instead.', + ), }); export type AoANTInfo = z.infer; diff --git a/tests/unit/ant.test.ts b/tests/unit/ant.test.ts index 0f778cd1..252d278d 100644 --- a/tests/unit/ant.test.ts +++ b/tests/unit/ant.test.ts @@ -3,6 +3,7 @@ import { describe, it } from 'node:test'; import { z } from 'zod'; import { + AntHandlerNames, AntInfoSchema, AntStateSchema, isAoANTState, @@ -66,7 +67,7 @@ describe('ANT Schemas', () => { ['Total-Supply']: '1', Logo: stub_address, Denomination: '0', - HandlerNames: ['handler1', 'handler2'], + Handlers: AntHandlerNames, }; const invalidInfo = { Name: 'TestToken', @@ -76,7 +77,7 @@ describe('ANT Schemas', () => { ['Total-Supply']: 1000, Logo: stub_address, Denomination: '1', - HandlerNames: ['handler1', 'handler2'], + Handlers: AntHandlerNames, }; assert.doesNotThrow(() => AntInfoSchema.parse(validInfo)); From 4864abf361ffb6fdef877f52833194beb228cfbf Mon Sep 17 00:00:00 2001 From: atticusofsparta Date: Wed, 16 Oct 2024 09:58:34 -0600 Subject: [PATCH 09/97] fix(schema): add strict mode to ANT with default to false --- examples/vite/src/App.tsx | 2 +- src/common/ant.ts | 57 +++++++++++++++++++++++-------------- src/utils/processes.ts | 1 + tests/e2e/cjs/index.test.js | 1 + tests/e2e/esm/index.test.js | 2 ++ 5 files changed, 41 insertions(+), 22 deletions(-) diff --git a/examples/vite/src/App.tsx b/examples/vite/src/App.tsx index 193e6ce2..0139d109 100644 --- a/examples/vite/src/App.tsx +++ b/examples/vite/src/App.tsx @@ -6,7 +6,7 @@ import remarkGfm from 'remark-gfm'; import './App.css'; const contractTxId = 'ilwT4ObFQ7cGPbW-8z-h7mvvWGt_yhWNlqxNjSUgiYY'; -const antContract = ANT.init({ contractTxId }); +const antContract = ANT.init({ contractTxId, strict: true }); function App() { const [contract, setContract] = useState('Loading...'); diff --git a/src/common/ant.ts b/src/common/ant.ts index ed0c9b16..60212971 100644 --- a/src/common/ant.ts +++ b/src/common/ant.ts @@ -45,22 +45,30 @@ import { AOProcess, InvalidContractConfigurationError } from './index.js'; export class ANT { static init( - config: Required & { signer?: undefined }, + config: Required & { + signer?: undefined; + strict?: boolean; + }, ): AoANTRead; static init({ signer, ...config - }: WithSigner>): AoANTWrite; + }: WithSigner> & { + strict?: boolean; + }): AoANTWrite; static init({ signer, + strict = false, ...config - }: OptionalSigner>): AoANTRead | AoANTWrite { + }: OptionalSigner> & { strict?: boolean }): + | AoANTRead + | AoANTWrite { // ao supported implementation if (isProcessConfiguration(config) || isProcessIdConfiguration(config)) { if (!signer) { - return new AoANTReadable(config); + return new AoANTReadable({ strict, ...config }); } - return new AoANTWriteable({ signer, ...config }); + return new AoANTWriteable({ signer, strict, ...config }); } throw new InvalidContractConfigurationError(); @@ -69,8 +77,10 @@ export class ANT { export class AoANTReadable implements AoANTRead { protected process: AOProcess; + private strict: boolean; - constructor(config: Required) { + constructor(config: Required & { strict?: boolean }) { + this.strict = config.strict || false; if (isProcessConfiguration(config)) { this.process = config.process; } else if (isProcessIdConfiguration(config)) { @@ -87,15 +97,17 @@ export class AoANTReadable implements AoANTRead { const res = await this.process.read({ tags, }); + if (this.strict) { + parseSchemaResult( + AntStateSchema.passthrough().and( + z.object({ + Records: z.record(z.string(), AntRecordSchema.passthrough()), + }), + ), + res, + ); + } - parseSchemaResult( - AntStateSchema.passthrough().and( - z.object({ - Records: z.record(z.string(), AntRecordSchema.passthrough()), - }), - ), - res, - ); return res; } @@ -104,7 +116,9 @@ export class AoANTReadable implements AoANTRead { const info = await this.process.read({ tags, }); - parseSchemaResult(AntInfoSchema.passthrough(), info); + if (this.strict) { + parseSchemaResult(AntInfoSchema.passthrough(), info); + } return info; } @@ -126,7 +140,8 @@ export class AoANTReadable implements AoANTRead { const record = await this.process.read({ tags, }); - parseSchemaResult(AntRecordSchema.passthrough(), record); + if (this.strict) parseSchemaResult(AntRecordSchema.passthrough(), record); + return record; } @@ -143,7 +158,7 @@ export class AoANTReadable implements AoANTRead { const records = await this.process.read>({ tags, }); - parseSchemaResult(AntRecordsSchema, records); + if (this.strict) parseSchemaResult(AntRecordsSchema, records); return records; } @@ -173,7 +188,7 @@ export class AoANTReadable implements AoANTRead { const controllers = await this.process.read({ tags, }); - parseSchemaResult(AntControllersSchema, controllers); + if (this.strict) parseSchemaResult(AntControllersSchema, controllers); return controllers; } @@ -216,7 +231,7 @@ export class AoANTReadable implements AoANTRead { const balances = await this.process.read>({ tags, }); - parseSchemaResult(AntBalancesSchema, balances); + if (this.strict) parseSchemaResult(AntBalancesSchema, balances); return balances; } @@ -237,7 +252,7 @@ export class AoANTReadable implements AoANTRead { const balance = await this.process.read({ tags, }); - parseSchemaResult(z.number(), balance); + if (this.strict) parseSchemaResult(z.number(), balance); return balance; } } @@ -248,7 +263,7 @@ export class AoANTWriteable extends AoANTReadable implements AoANTWrite { constructor({ signer, ...config - }: WithSigner>) { + }: WithSigner> & { strict?: boolean }) { super(config); this.signer = createAoSigner(signer); } diff --git a/src/utils/processes.ts b/src/utils/processes.ts index 506210af..f2f882e8 100644 --- a/src/utils/processes.ts +++ b/src/utils/processes.ts @@ -144,6 +144,7 @@ export class ArNSEventEmitter extends EventEmitter { } const ant = ANT.init({ processId, + strict: true, }); const state: AoANTState | undefined = (await timeout( this.timeoutMs, diff --git a/tests/e2e/cjs/index.test.js b/tests/e2e/cjs/index.test.js index 9375c1c6..14603fec 100644 --- a/tests/e2e/cjs/index.test.js +++ b/tests/e2e/cjs/index.test.js @@ -359,6 +359,7 @@ describe('ANT', async () => { const ant = ANT.init({ processId: 'aWI_dq1JH7facsulLuas1X3l5dkKuWtixcZDYMw9mpg', signer, + strict: true, }); assert(ant instanceof AoANTWriteable); diff --git a/tests/e2e/esm/index.test.js b/tests/e2e/esm/index.test.js index 3312a955..00b6c059 100644 --- a/tests/e2e/esm/index.test.js +++ b/tests/e2e/esm/index.test.js @@ -367,6 +367,7 @@ describe('ANT', async () => { const processId = 'YcxE5IbqZYK72H64ELoysxiJ-0wb36deYPv55wgl8xo'; const ant = ANT.init({ processId, + strict: true, }); it('should be able to create ANTWriteable with valid signers', async () => { @@ -374,6 +375,7 @@ describe('ANT', async () => { const writeable = ANT.init({ processId, signer, + strict: true, }); assert(writeable instanceof AoANTWriteable); From e39c2e1e56411ed788037ed1f1c29de8cd9fac57 Mon Sep 17 00:00:00 2001 From: atticusofsparta Date: Wed, 16 Oct 2024 10:01:51 -0600 Subject: [PATCH 10/97] chore(test): add tests for strict and non strict ant --- tests/e2e/cjs/index.test.js | 6 ++++++ tests/e2e/esm/index.test.js | 10 ++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/tests/e2e/cjs/index.test.js b/tests/e2e/cjs/index.test.js index 14603fec..dfbda0dd 100644 --- a/tests/e2e/cjs/index.test.js +++ b/tests/e2e/cjs/index.test.js @@ -361,8 +361,14 @@ describe('ANT', async () => { signer, strict: true, }); + const strictAnt = ANT.init({ + processId: 'aWI_dq1JH7facsulLuas1X3l5dkKuWtixcZDYMw9mpg', + signer, + strict: true, + }); assert(ant instanceof AoANTWriteable); + assert(strictAnt instanceof AoANTWriteable); } }); }); diff --git a/tests/e2e/esm/index.test.js b/tests/e2e/esm/index.test.js index 00b6c059..a1b62394 100644 --- a/tests/e2e/esm/index.test.js +++ b/tests/e2e/esm/index.test.js @@ -372,13 +372,19 @@ describe('ANT', async () => { it('should be able to create ANTWriteable with valid signers', async () => { for (const signer of signers) { - const writeable = ANT.init({ + const nonStrictAnt = ANT.init({ + processId, + signer, + strict: true, + }); + const strictAnt = ANT.init({ processId, signer, strict: true, }); - assert(writeable instanceof AoANTWriteable); + assert(nonStrictAnt instanceof AoANTWriteable); + assert(strictAnt instanceof AoANTWriteable); } }); From e14722032c79baefb9729fac2f5a328168797005 Mon Sep 17 00:00:00 2001 From: atticusofsparta Date: Wed, 16 Oct 2024 14:24:17 -0600 Subject: [PATCH 11/97] fix(strict): allow for passing in strict mode on apis --- src/common/ant.ts | 63 ++++++++++++++++++++++++++++++++--------------- src/types/ant.ts | 26 ++++++++++++------- 2 files changed, 60 insertions(+), 29 deletions(-) diff --git a/src/common/ant.ts b/src/common/ant.ts index 60212971..ad865272 100644 --- a/src/common/ant.ts +++ b/src/common/ant.ts @@ -19,6 +19,7 @@ import { AntBalancesSchema, AntControllersSchema, AntInfoSchema, + AntReadOptions, AntRecordSchema, AntRecordsSchema, AntStateSchema, @@ -92,12 +93,14 @@ export class AoANTReadable implements AoANTRead { } } - async getState(): Promise { + async getState( + { strict }: AntReadOptions = { strict: this.strict }, + ): Promise { const tags = [{ name: 'Action', value: 'State' }]; const res = await this.process.read({ tags, }); - if (this.strict) { + if (strict) { parseSchemaResult( AntStateSchema.passthrough().and( z.object({ @@ -111,12 +114,14 @@ export class AoANTReadable implements AoANTRead { return res; } - async getInfo(): Promise { + async getInfo( + { strict }: AntReadOptions = { strict: this.strict }, + ): Promise { const tags = [{ name: 'Action', value: 'Info' }]; const info = await this.process.read({ tags, }); - if (this.strict) { + if (strict) { parseSchemaResult(AntInfoSchema.passthrough(), info); } return info; @@ -131,7 +136,10 @@ export class AoANTReadable implements AoANTRead { * ant.getRecord({ undername: "john" }); * ``` */ - async getRecord({ undername }: { undername: string }): Promise { + async getRecord( + { undername }: { undername: string }, + { strict }: AntReadOptions = { strict: this.strict }, + ): Promise { const tags = [ { name: 'Sub-Domain', value: undername }, { name: 'Action', value: 'Record' }, @@ -140,7 +148,7 @@ export class AoANTReadable implements AoANTRead { const record = await this.process.read({ tags, }); - if (this.strict) parseSchemaResult(AntRecordSchema.passthrough(), record); + if (strict) parseSchemaResult(AntRecordSchema.passthrough(), record); return record; } @@ -153,12 +161,14 @@ export class AoANTReadable implements AoANTRead { * ant.getRecords(); * ```` */ - async getRecords(): Promise> { + async getRecords( + { strict }: AntReadOptions = { strict: this.strict }, + ): Promise> { const tags = [{ name: 'Action', value: 'Records' }]; const records = await this.process.read>({ tags, }); - if (this.strict) parseSchemaResult(AntRecordsSchema, records); + if (strict) parseSchemaResult(AntRecordsSchema, records); return records; } @@ -170,8 +180,10 @@ export class AoANTReadable implements AoANTRead { * ant.getOwner(); * ``` */ - async getOwner(): Promise { - const info = await this.getInfo(); + async getOwner( + { strict }: AntReadOptions = { strict: this.strict }, + ): Promise { + const info = await this.getInfo({ strict }); return info.Owner; } @@ -183,12 +195,14 @@ export class AoANTReadable implements AoANTRead { * ant.getControllers(); * ``` */ - async getControllers(): Promise { + async getControllers( + { strict }: AntReadOptions = { strict: this.strict }, + ): Promise { const tags = [{ name: 'Action', value: 'Controllers' }]; const controllers = await this.process.read({ tags, }); - if (this.strict) parseSchemaResult(AntControllersSchema, controllers); + if (strict) parseSchemaResult(AntControllersSchema, controllers); return controllers; } @@ -200,8 +214,10 @@ export class AoANTReadable implements AoANTRead { * ant.getName(); * ``` */ - async getName(): Promise { - const info = await this.getInfo(); + async getName( + { strict }: AntReadOptions = { strict: this.strict }, + ): Promise { + const info = await this.getInfo({ strict }); return info.Name; } @@ -213,8 +229,10 @@ export class AoANTReadable implements AoANTRead { * ant.getTicker(); * ``` */ - async getTicker(): Promise { - const info = await this.getInfo(); + async getTicker( + { strict }: AntReadOptions = { strict: this.strict }, + ): Promise { + const info = await this.getInfo({ strict }); return info.Ticker; } @@ -226,12 +244,14 @@ export class AoANTReadable implements AoANTRead { * ant.getBalances(); * ``` */ - async getBalances(): Promise> { + async getBalances( + { strict }: AntReadOptions = { strict: this.strict }, + ): Promise> { const tags = [{ name: 'Action', value: 'Balances' }]; const balances = await this.process.read>({ tags, }); - if (this.strict) parseSchemaResult(AntBalancesSchema, balances); + if (strict) parseSchemaResult(AntBalancesSchema, balances); return balances; } @@ -244,7 +264,10 @@ export class AoANTReadable implements AoANTRead { * ant.getBalance({ address }); * ``` */ - async getBalance({ address }: { address: string }): Promise { + async getBalance( + { address }: { address: string }, + { strict }: AntReadOptions = { strict: this.strict }, + ): Promise { const tags = [ { name: 'Action', value: 'Balance' }, { name: 'Recipient', value: address }, @@ -252,7 +275,7 @@ export class AoANTReadable implements AoANTRead { const balance = await this.process.read({ tags, }); - if (this.strict) parseSchemaResult(z.number(), balance); + if (strict) parseSchemaResult(z.number(), balance); return balance; } } diff --git a/src/types/ant.ts b/src/types/ant.ts index 79b9c390..27f82534 100644 --- a/src/types/ant.ts +++ b/src/types/ant.ts @@ -160,17 +160,25 @@ export function isAoANTState(state: object): state is AoANTState { return AntStateSchema.safeParse(state).success; } +export type AntReadOptions = { strict?: boolean }; + export interface AoANTRead { - getState(): Promise; - getInfo(): Promise; - getRecord({ undername }): Promise; - getRecords(): Promise>; - getOwner(): Promise; + getState(opts?: AntReadOptions): Promise; + getInfo(opts?: AntReadOptions): Promise; + getRecord( + { undername }: { undername: string }, + opts?: AntReadOptions, + ): Promise; + getRecords(opts?: AntReadOptions): Promise>; + getOwner(opts?: AntReadOptions): Promise; getControllers(): Promise; - getTicker(): Promise; - getName(): Promise; - getBalance({ address }: { address: WalletAddress }): Promise; - getBalances(): Promise>; + getTicker(opts?: AntReadOptions): Promise; + getName(opts?: AntReadOptions): Promise; + getBalance( + { address }: { address: WalletAddress }, + opts?: AntReadOptions, + ): Promise; + getBalances(opts?: AntReadOptions): Promise>; } export interface AoANTWrite extends AoANTRead { From 077ccbdb0db51d9fb48530106ed45908a1f4c7cd Mon Sep 17 00:00:00 2001 From: atticusofsparta Date: Wed, 16 Oct 2024 15:31:43 -0600 Subject: [PATCH 12/97] chore(test): add tests for strict mode --- tests/e2e/cjs/index.test.js | 64 ++++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/tests/e2e/cjs/index.test.js b/tests/e2e/cjs/index.test.js index da4fd85b..3edfb53a 100644 --- a/tests/e2e/cjs/index.test.js +++ b/tests/e2e/cjs/index.test.js @@ -30,12 +30,14 @@ const signers = [ createAoSigner(new ArweaveSigner(testWallet)), ]; +const aoClient = connect({ + CU_URL: 'http://localhost:6363', +}); + const io = IO.init({ process: new AOProcess({ processId: process.env.IO_PROCESS_ID || ioDevnetProcessId, - ao: connect({ - CU_URL: 'http://localhost:6363', - }), + ao: aoClient, }), }); @@ -359,21 +361,73 @@ describe('ANTRegistry', async () => { }); describe('ANT', async () => { + const processId = 'YcxE5IbqZYK72H64ELoysxiJ-0wb36deYPv55wgl8xo'; it('should be able to create ANTWriteable with valid signers', async () => { for (const signer of signers) { const ant = ANT.init({ - processId: 'aWI_dq1JH7facsulLuas1X3l5dkKuWtixcZDYMw9mpg', + processId, signer, strict: true, + ao: aoClient, }); const strictAnt = ANT.init({ - processId: 'aWI_dq1JH7facsulLuas1X3l5dkKuWtixcZDYMw9mpg', + processId, signer, strict: true, + ao: aoClient, }); assert(ant instanceof AoANTWriteable); assert(strictAnt instanceof AoANTWriteable); } }); + + it(`should be able to read methods when strict is true`, async () => { + const strict = true; + const ant = ANT.init({ + processId, + ao: aoClient, + }); + const state = await ant.getState({ strict }); + assert(state); + const info = await ant.getInfo({ strict }); + assert(info); + const balance = await ant.getBalance( + { address: '7waR8v4STuwPnTck1zFVkQqJh5K9q9Zik4Y5-5dV7nk' }, + { strict }, + ); + assert(balance); + const balances = await ant.getBalances({ strict }); + assert(balances); + const controllers = await ant.getControllers({ strict }); + assert(controllers); + const records = await ant.getRecords({ strict }); + assert(records); + const record = await ant.getRecord({ name: 'ardrive' }, { strict }); + assert(record); + }); + it(`should be able to read methods when strict is false`, async () => { + const strict = false; + const ant = ANT.init({ + processId, + ao: aoClient, + }); + const state = await ant.getState({ strict }); + assert(state); + const info = await ant.getInfo({ strict }); + assert(info); + const balance = await ant.getBalance( + { address: '7waR8v4STuwPnTck1zFVkQqJh5K9q9Zik4Y5-5dV7nk' }, + { strict }, + ); + assert(balance); + const balances = await ant.getBalances({ strict }); + assert(balances); + const controllers = await ant.getControllers({ strict }); + assert(controllers); + const records = await ant.getRecords({ strict }); + assert(records); + const record = await ant.getRecord({ name: 'ardrive' }, { strict }); + assert(record); + }); }); From f999c498192edc8da2419016ec5b3f7f27f6e728 Mon Sep 17 00:00:00 2001 From: atticusofsparta Date: Wed, 16 Oct 2024 15:42:45 -0600 Subject: [PATCH 13/97] fix(test): correct params for get record --- tests/e2e/cjs/index.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/cjs/index.test.js b/tests/e2e/cjs/index.test.js index 3edfb53a..dbb06d94 100644 --- a/tests/e2e/cjs/index.test.js +++ b/tests/e2e/cjs/index.test.js @@ -403,7 +403,7 @@ describe('ANT', async () => { assert(controllers); const records = await ant.getRecords({ strict }); assert(records); - const record = await ant.getRecord({ name: 'ardrive' }, { strict }); + const record = await ant.getRecord({ undername: 'ardrive' }, { strict }); assert(record); }); it(`should be able to read methods when strict is false`, async () => { @@ -427,7 +427,7 @@ describe('ANT', async () => { assert(controllers); const records = await ant.getRecords({ strict }); assert(records); - const record = await ant.getRecord({ name: 'ardrive' }, { strict }); + const record = await ant.getRecord({ undername: 'ardrive' }, { strict }); assert(record); }); }); From 95244ea820ac5970b5c4ee3abad92a765765df33 Mon Sep 17 00:00:00 2001 From: atticusofsparta Date: Wed, 16 Oct 2024 16:22:17 -0600 Subject: [PATCH 14/97] fix(tests): add esm tests and remove redundant cjs tests --- tests/e2e/cjs/index.test.js | 49 ------------------------------------- tests/e2e/esm/index.test.js | 22 +++++++++-------- 2 files changed, 12 insertions(+), 59 deletions(-) diff --git a/tests/e2e/cjs/index.test.js b/tests/e2e/cjs/index.test.js index dbb06d94..923cca7e 100644 --- a/tests/e2e/cjs/index.test.js +++ b/tests/e2e/cjs/index.test.js @@ -381,53 +381,4 @@ describe('ANT', async () => { assert(strictAnt instanceof AoANTWriteable); } }); - - it(`should be able to read methods when strict is true`, async () => { - const strict = true; - const ant = ANT.init({ - processId, - ao: aoClient, - }); - const state = await ant.getState({ strict }); - assert(state); - const info = await ant.getInfo({ strict }); - assert(info); - const balance = await ant.getBalance( - { address: '7waR8v4STuwPnTck1zFVkQqJh5K9q9Zik4Y5-5dV7nk' }, - { strict }, - ); - assert(balance); - const balances = await ant.getBalances({ strict }); - assert(balances); - const controllers = await ant.getControllers({ strict }); - assert(controllers); - const records = await ant.getRecords({ strict }); - assert(records); - const record = await ant.getRecord({ undername: 'ardrive' }, { strict }); - assert(record); - }); - it(`should be able to read methods when strict is false`, async () => { - const strict = false; - const ant = ANT.init({ - processId, - ao: aoClient, - }); - const state = await ant.getState({ strict }); - assert(state); - const info = await ant.getInfo({ strict }); - assert(info); - const balance = await ant.getBalance( - { address: '7waR8v4STuwPnTck1zFVkQqJh5K9q9Zik4Y5-5dV7nk' }, - { strict }, - ); - assert(balance); - const balances = await ant.getBalances({ strict }); - assert(balances); - const controllers = await ant.getControllers({ strict }); - assert(controllers); - const records = await ant.getRecords({ strict }); - assert(records); - const record = await ant.getRecord({ undername: 'ardrive' }, { strict }); - assert(record); - }); }); diff --git a/tests/e2e/esm/index.test.js b/tests/e2e/esm/index.test.js index eff99b2b..a57145ac 100644 --- a/tests/e2e/esm/index.test.js +++ b/tests/e2e/esm/index.test.js @@ -399,7 +399,6 @@ describe('ANT', async () => { const processId = 'YcxE5IbqZYK72H64ELoysxiJ-0wb36deYPv55wgl8xo'; const ant = ANT.init({ processId, - strict: true, }); it('should be able to create ANTWriteable with valid signers', async () => { @@ -407,7 +406,7 @@ describe('ANT', async () => { const nonStrictAnt = ANT.init({ processId, signer, - strict: true, + strict: false, }); const strictAnt = ANT.init({ processId, @@ -421,17 +420,17 @@ describe('ANT', async () => { }); it('should be able to get ANT info', async () => { - const info = await ant.getInfo({ processId }); + const info = await ant.getInfo({ strict: true }); assert.ok(info); }); it('should be able to get the ANT records', async () => { - const records = await ant.getRecords({ processId }); + const records = await ant.getRecords({ strict: true }, { strict: true }); assert.ok(records); }); it('should be able to get a @ record from the ANT', async () => { - const record = await ant.getRecord({ undername: '@' }); + const record = await ant.getRecord({ undername: '@' }, { strict: true }); assert.ok(record); }); @@ -456,19 +455,22 @@ describe('ANT', async () => { }); it('should be able to get the ANT state', async () => { - const state = await ant.getState(); + const state = await ant.getState({ strict: true }); assert.ok(state); }); it('should be able to get the ANT balance for an address', async () => { - const balance = await ant.getBalance({ - address: '"7waR8v4STuwPnTck1zFVkQqJh5K9q9Zik4Y5-5dV7nk', - }); + const balance = await ant.getBalance( + { + address: '7waR8v4STuwPnTck1zFVkQqJh5K9q9Zik4Y5-5dV7nk', + }, + { strict: true }, + ); assert.notEqual(balance, undefined); }); it('should be able to get the ANT balances', async () => { - const balances = await ant.getBalances(); + const balances = await ant.getBalances({ strict: true }); assert.ok(balances); }); }); From 62c914086b936b0c91cea757709941c1987f3ade Mon Sep 17 00:00:00 2001 From: atticusofsparta Date: Thu, 17 Oct 2024 08:19:04 -0600 Subject: [PATCH 15/97] fix(tests): simplify strict check on test --- tests/e2e/esm/index.test.js | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/tests/e2e/esm/index.test.js b/tests/e2e/esm/index.test.js index a57145ac..cdfa6d9c 100644 --- a/tests/e2e/esm/index.test.js +++ b/tests/e2e/esm/index.test.js @@ -399,6 +399,7 @@ describe('ANT', async () => { const processId = 'YcxE5IbqZYK72H64ELoysxiJ-0wb36deYPv55wgl8xo'; const ant = ANT.init({ processId, + strict: true, }); it('should be able to create ANTWriteable with valid signers', async () => { @@ -420,17 +421,17 @@ describe('ANT', async () => { }); it('should be able to get ANT info', async () => { - const info = await ant.getInfo({ strict: true }); + const info = await ant.getInfo(); assert.ok(info); }); it('should be able to get the ANT records', async () => { - const records = await ant.getRecords({ strict: true }, { strict: true }); + const records = await ant.getRecords(); assert.ok(records); }); it('should be able to get a @ record from the ANT', async () => { - const record = await ant.getRecord({ undername: '@' }, { strict: true }); + const record = await ant.getRecord({ undername: '@' }); assert.ok(record); }); @@ -455,22 +456,19 @@ describe('ANT', async () => { }); it('should be able to get the ANT state', async () => { - const state = await ant.getState({ strict: true }); + const state = await ant.getState(); assert.ok(state); }); it('should be able to get the ANT balance for an address', async () => { - const balance = await ant.getBalance( - { - address: '7waR8v4STuwPnTck1zFVkQqJh5K9q9Zik4Y5-5dV7nk', - }, - { strict: true }, - ); + const balance = await ant.getBalance({ + address: '7waR8v4STuwPnTck1zFVkQqJh5K9q9Zik4Y5-5dV7nk', + }); assert.notEqual(balance, undefined); }); it('should be able to get the ANT balances', async () => { - const balances = await ant.getBalances({ strict: true }); + const balances = await ant.getBalances(); assert.ok(balances); }); }); From 1e52dafb3b5c8e651401325c630faf3f50a4c060 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 17 Oct 2024 15:32:55 +0000 Subject: [PATCH 16/97] chore(release): 2.3.3-alpha.1 [skip ci] ## [2.3.3-alpha.1](https://github.com/ar-io/ar-io-sdk/compare/v2.3.2...v2.3.3-alpha.1) (2024-10-17) ### Bug Fixes * **schema:** add strict mode to ANT with default to false ([4864abf](https://github.com/ar-io/ar-io-sdk/commit/4864abf361ffb6fdef877f52833194beb228cfbf)) * **schemas:** add passthrough on schema checks for ants ([9cb2776](https://github.com/ar-io/ar-io-sdk/commit/9cb2776da4a9886a1467abbf51b7c4f814bdb7a6)) * **schemas:** add zod schemas and tests ([feba587](https://github.com/ar-io/ar-io-sdk/commit/feba5873e7efd47f314f5e22561d0d0e07c26908)) * **schema:** specify HandlerNames instead of Handlers ([44cc472](https://github.com/ar-io/ar-io-sdk/commit/44cc472139ddc9462f024b4f6877643fcedec034)) * **schemas:** update ant schema and tests ([f3284ed](https://github.com/ar-io/ar-io-sdk/commit/f3284ed038cebc8ddacae3ac0de3f740dcf86106)) * **schema:** update handlers schema ([6ec52e4](https://github.com/ar-io/ar-io-sdk/commit/6ec52e4b07b5731f9388239ada9fd0f64373b445)) * **strict:** allow for passing in strict mode on apis ([e147220](https://github.com/ar-io/ar-io-sdk/commit/e14722032c79baefb9729fac2f5a328168797005)) * **test:** correct params for get record ([f999c49](https://github.com/ar-io/ar-io-sdk/commit/f999c498192edc8da2419016ec5b3f7f27f6e728)) * **tests:** add esm tests and remove redundant cjs tests ([95244ea](https://github.com/ar-io/ar-io-sdk/commit/95244ea820ac5970b5c4ee3abad92a765765df33)) * **tests:** add js path on imports ([db1520a](https://github.com/ar-io/ar-io-sdk/commit/db1520a0f2dc9b5045ce0db56f46fddf7456474d)) * **tests:** simplify strict check on test ([62c9140](https://github.com/ar-io/ar-io-sdk/commit/62c914086b936b0c91cea757709941c1987f3ade)) * **util:** create schema parsing util to pretty format errors ([367537a](https://github.com/ar-io/ar-io-sdk/commit/367537a07b97180bfffee6cf0d6133cc03afdc4e)) * **validations:** add zod schema validations on ant returns ([163c2f1](https://github.com/ar-io/ar-io-sdk/commit/163c2f188a362d1ffb41e7cb3ba6deee9fe5147e)) --- CHANGELOG.md | 19 +++++++++++++++++++ package.json | 2 +- src/version.ts | 2 +- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14b0e879..d32cdbcf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,22 @@ +## [2.3.3-alpha.1](https://github.com/ar-io/ar-io-sdk/compare/v2.3.2...v2.3.3-alpha.1) (2024-10-17) + + +### Bug Fixes + +* **schema:** add strict mode to ANT with default to false ([4864abf](https://github.com/ar-io/ar-io-sdk/commit/4864abf361ffb6fdef877f52833194beb228cfbf)) +* **schemas:** add passthrough on schema checks for ants ([9cb2776](https://github.com/ar-io/ar-io-sdk/commit/9cb2776da4a9886a1467abbf51b7c4f814bdb7a6)) +* **schemas:** add zod schemas and tests ([feba587](https://github.com/ar-io/ar-io-sdk/commit/feba5873e7efd47f314f5e22561d0d0e07c26908)) +* **schema:** specify HandlerNames instead of Handlers ([44cc472](https://github.com/ar-io/ar-io-sdk/commit/44cc472139ddc9462f024b4f6877643fcedec034)) +* **schemas:** update ant schema and tests ([f3284ed](https://github.com/ar-io/ar-io-sdk/commit/f3284ed038cebc8ddacae3ac0de3f740dcf86106)) +* **schema:** update handlers schema ([6ec52e4](https://github.com/ar-io/ar-io-sdk/commit/6ec52e4b07b5731f9388239ada9fd0f64373b445)) +* **strict:** allow for passing in strict mode on apis ([e147220](https://github.com/ar-io/ar-io-sdk/commit/e14722032c79baefb9729fac2f5a328168797005)) +* **test:** correct params for get record ([f999c49](https://github.com/ar-io/ar-io-sdk/commit/f999c498192edc8da2419016ec5b3f7f27f6e728)) +* **tests:** add esm tests and remove redundant cjs tests ([95244ea](https://github.com/ar-io/ar-io-sdk/commit/95244ea820ac5970b5c4ee3abad92a765765df33)) +* **tests:** add js path on imports ([db1520a](https://github.com/ar-io/ar-io-sdk/commit/db1520a0f2dc9b5045ce0db56f46fddf7456474d)) +* **tests:** simplify strict check on test ([62c9140](https://github.com/ar-io/ar-io-sdk/commit/62c914086b936b0c91cea757709941c1987f3ade)) +* **util:** create schema parsing util to pretty format errors ([367537a](https://github.com/ar-io/ar-io-sdk/commit/367537a07b97180bfffee6cf0d6133cc03afdc4e)) +* **validations:** add zod schema validations on ant returns ([163c2f1](https://github.com/ar-io/ar-io-sdk/commit/163c2f188a362d1ffb41e7cb3ba6deee9fe5147e)) + ## [2.3.2](https://github.com/ar-io/ar-io-sdk/compare/v2.3.1...v2.3.2) (2024-10-16) diff --git a/package.json b/package.json index beb4ad34..e35b21d1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ar.io/sdk", - "version": "2.3.2", + "version": "2.3.3-alpha.1", "repository": { "type": "git", "url": "git+https://github.com/ar-io/ar-io-sdk.git" diff --git a/src/version.ts b/src/version.ts index 493353ea..04ca73c3 100644 --- a/src/version.ts +++ b/src/version.ts @@ -16,4 +16,4 @@ // AUTOMATICALLY GENERATED FILE - DO NOT TOUCH -export const version = '2.3.2'; +export const version = '2.3.3-alpha.1'; From 4b4cb8f6e6ee98c875541df21a0845f6069501f7 Mon Sep 17 00:00:00 2001 From: vilenarios Date: Wed, 23 Oct 2024 15:38:34 -0400 Subject: [PATCH 17/97] feat(delegates): add instant delegate withdrawal for a fee --- README.md | 40 +++++++++++++++++++++++++++++++++++++--- src/common/io.ts | 35 ++++++++++++++++++++++++++++++++++- src/types/io.ts | 8 ++++++++ 3 files changed, 79 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 824a4431..d4b389db 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,8 @@ This is the home of [ar.io] SDK. This SDK provides functionality for interacting - [`leaveNetwork()`](#leavenetwork) - [`updateGatewaySettings(gatewaySettings)`](#updategatewaysettingsgatewaysettings) - [`increaseDelegateStake({ target, qty })`](#increasedelegatestake-target-qty-) - - [`decreaseDelegateStake({ target, qty })`](#decreasedelegatestake-target-qty-) + - [`decreaseDelegateStake({ target, qty, instant })`](#decreasedelegatestake-target-qty-instant-) + - [`instantDelegateWithdrawal({ target, vaultId })`](#instantdelegatewithdrawal-target-vaultid-) - [`increaseOperatorStake({ qty })`](#increaseoperatorstake-qty-) - [`decreaseOperatorStake({ qty })`](#decreaseoperatorstake-qty-) - [`saveObservations({ reportTxId, failedGateways })`](#saveobservations-reporttxid-failedgateways-) @@ -941,9 +942,9 @@ const { id: txId } = await io.increaseDelegateStake( ); ``` -#### `decreaseDelegateStake({ target, qty })` +#### `decreaseDelegateStake({ target, qty, instant })` -Decreases the callers stake on the target gateway. +Decreases the callers stake on the target gateway. Can instantly decrease stake by setting instant to `true`. _Note: Requires `signer` to be provided on `IO.init` to sign the transaction._ @@ -960,6 +961,39 @@ const { id: txId } = await io.decreaseDelegateStake( ); ``` +Pay the early withdrawal fee and withdraw instantly. + +```typescript +const io = IO.init({ signer: new ArweaveSigner(jwk) }); +const { id: txId } = await io.decreaseDelegateStake({ + target: 't4Xr0_J4Iurt7caNST02cMotaz2FIbWQ4Kbj616RHl3', + qty: new IOToken(100).toMIO(), + instant: true, // Immediately withdraw this stake and pay the instant withdrawal fee +}); +``` + +#### `instantDelegateWithdrawal({ target, vaultId })` + +Instantly withdraws vaulted delegate tokens at the cost of the early withdrawal fee. + +_Note: Requires `signer` to be provided on `IO.init` to sign the transaction._ + +```typescript +const io = IO.init({ signer: new ArweaveSigner(jwk) }); +const { id: txId } = await io.instantDelegateWithdrawal( + { + // gateway address where delegate vault exists + target: 't4Xr0_J4Iurt7caNST02cMotaz2FIbWQ4Kbj616RHl3', + // delegated vault id to cancel + vaultId: 'fDrr0_J4Iurt7caNST02cMotaz2FIbWQ4Kcj616RHl3', + }, + // optional additional tags + { + tags: [{ name: 'App-Name', value: 'My-Awesome-App' }], + }, +); +``` + #### `increaseOperatorStake({ qty })` Increases the callers operator stake. Must be executed with a wallet registered as a gateway operator. diff --git a/src/common/io.ts b/src/common/io.ts index 31434f7b..01c04f3d 100644 --- a/src/common/io.ts +++ b/src/common/io.ts @@ -824,6 +824,39 @@ export class IOWriteable extends IOReadable implements AoIOWrite { params: { target: string; decreaseQty: number | mIOToken; + instant: boolean; + }, + options?: WriteOptions, + ): Promise { + const { tags = [] } = options || {}; + if (params.instant == true) { + return this.process.send({ + signer: this.signer, + tags: [ + ...tags, + { name: 'Action', value: 'Decrease-Delegate-Stake' }, + { name: 'Target', value: params.target }, + { name: 'Quantity', value: params.decreaseQty.valueOf().toString() }, + { name: 'Instant', value: 'true' }, + ], + }); + } else { + return this.process.send({ + signer: this.signer, + tags: [ + ...tags, + { name: 'Action', value: 'Decrease-Delegate-Stake' }, + { name: 'Target', value: params.target }, + { name: 'Quantity', value: params.decreaseQty.valueOf().toString() }, + ], + }); + } + } + + async instantDelegateWithdrawal( + params: { + target: string; + vaultId: string; }, options?: WriteOptions, ): Promise { @@ -834,7 +867,7 @@ export class IOWriteable extends IOReadable implements AoIOWrite { ...tags, { name: 'Action', value: 'Decrease-Delegate-Stake' }, { name: 'Target', value: params.target }, - { name: 'Quantity', value: params.decreaseQty.valueOf().toString() }, + { name: 'Vault-Id', value: params.vaultId }, ], }); } diff --git a/src/types/io.ts b/src/types/io.ts index 79552a4f..6c815a48 100644 --- a/src/types/io.ts +++ b/src/types/io.ts @@ -402,6 +402,14 @@ export interface AoIOWrite extends AoIORead { params: { target: WalletAddress; decreaseQty: number | mIOToken; + instant: true; + }, + options?: WriteOptions, + ): Promise; + instantDelegateWithdrawal( + params: { + target: WalletAddress; + vaultId: string; }, options?: WriteOptions, ): Promise; From ae7be5caaa79f470eb9755c05c85467e98643d55 Mon Sep 17 00:00:00 2001 From: vilenarios Date: Wed, 23 Oct 2024 17:21:44 -0400 Subject: [PATCH 18/97] fix(delegates): fixes type --- src/types/io.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/io.ts b/src/types/io.ts index 6c815a48..71f5e8be 100644 --- a/src/types/io.ts +++ b/src/types/io.ts @@ -402,7 +402,7 @@ export interface AoIOWrite extends AoIORead { params: { target: WalletAddress; decreaseQty: number | mIOToken; - instant: true; + instant?: boolean; }, options?: WriteOptions, ): Promise; From 5e82a6a02f2c19ef647b164b97140d0d7e04240e Mon Sep 17 00:00:00 2001 From: vilenarios Date: Wed, 23 Oct 2024 17:39:02 -0400 Subject: [PATCH 19/97] chore(delegates): removes conditional --- src/common/io.ts | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/src/common/io.ts b/src/common/io.ts index 01c04f3d..e9b8b08c 100644 --- a/src/common/io.ts +++ b/src/common/io.ts @@ -824,33 +824,22 @@ export class IOWriteable extends IOReadable implements AoIOWrite { params: { target: string; decreaseQty: number | mIOToken; - instant: boolean; + instant?: boolean; }, options?: WriteOptions, ): Promise { const { tags = [] } = options || {}; - if (params.instant == true) { - return this.process.send({ - signer: this.signer, - tags: [ - ...tags, - { name: 'Action', value: 'Decrease-Delegate-Stake' }, - { name: 'Target', value: params.target }, - { name: 'Quantity', value: params.decreaseQty.valueOf().toString() }, - { name: 'Instant', value: 'true' }, - ], - }); - } else { - return this.process.send({ - signer: this.signer, - tags: [ - ...tags, - { name: 'Action', value: 'Decrease-Delegate-Stake' }, - { name: 'Target', value: params.target }, - { name: 'Quantity', value: params.decreaseQty.valueOf().toString() }, - ], - }); - } + + return this.process.send({ + signer: this.signer, + tags: [ + ...tags, + { name: 'Action', value: 'Decrease-Delegate-Stake' }, + { name: 'Target', value: params.target }, + { name: 'Quantity', value: params.decreaseQty.valueOf().toString() }, + { name: 'Instant', value: String(params.instant ?? false) }, + ], + }); } async instantDelegateWithdrawal( From 663de6f04557549f010f12d435c303e051d00501 Mon Sep 17 00:00:00 2001 From: Dylan Fiedler Date: Wed, 23 Oct 2024 17:57:48 -0500 Subject: [PATCH 20/97] fix(tag): small tweak to instant tag --- src/common/io.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/io.ts b/src/common/io.ts index e9b8b08c..f1faa8fd 100644 --- a/src/common/io.ts +++ b/src/common/io.ts @@ -837,7 +837,7 @@ export class IOWriteable extends IOReadable implements AoIOWrite { { name: 'Action', value: 'Decrease-Delegate-Stake' }, { name: 'Target', value: params.target }, { name: 'Quantity', value: params.decreaseQty.valueOf().toString() }, - { name: 'Instant', value: String(params.instant ?? false) }, + { name: 'Instant', value: `${params.instant || false}`, ], }); } From 72446aa0c1bfa8c69faf7781022a098c0e57d960 Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Wed, 23 Oct 2024 17:59:56 -0500 Subject: [PATCH 21/97] fix(lint): add lint fix and missing bracket --- src/common/io.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/io.ts b/src/common/io.ts index f1faa8fd..398f552e 100644 --- a/src/common/io.ts +++ b/src/common/io.ts @@ -837,7 +837,7 @@ export class IOWriteable extends IOReadable implements AoIOWrite { { name: 'Action', value: 'Decrease-Delegate-Stake' }, { name: 'Target', value: params.target }, { name: 'Quantity', value: params.decreaseQty.valueOf().toString() }, - { name: 'Instant', value: `${params.instant || false}`, + { name: 'Instant', value: `${params.instant || false}` }, ], }); } From e4fc4873a290e27cddcdaff3f78d08814a815a40 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 23 Oct 2024 23:04:24 +0000 Subject: [PATCH 22/97] chore(release): 2.4.0-alpha.1 [skip ci] # [2.4.0-alpha.1](https://github.com/ar-io/ar-io-sdk/compare/v2.3.3-alpha.1...v2.4.0-alpha.1) (2024-10-23) ### Bug Fixes * **delegates:** fixes type ([ae7be5c](https://github.com/ar-io/ar-io-sdk/commit/ae7be5caaa79f470eb9755c05c85467e98643d55)) * **lint:** add lint fix and missing bracket ([72446aa](https://github.com/ar-io/ar-io-sdk/commit/72446aa0c1bfa8c69faf7781022a098c0e57d960)) * **tag:** small tweak to instant tag ([663de6f](https://github.com/ar-io/ar-io-sdk/commit/663de6f04557549f010f12d435c303e051d00501)) ### Features * **delegates:** add instant delegate withdrawal for a fee ([4b4cb8f](https://github.com/ar-io/ar-io-sdk/commit/4b4cb8f6e6ee98c875541df21a0845f6069501f7)) --- CHANGELOG.md | 14 ++++++++++++++ package.json | 2 +- src/version.ts | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d32cdbcf..893d2e72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +# [2.4.0-alpha.1](https://github.com/ar-io/ar-io-sdk/compare/v2.3.3-alpha.1...v2.4.0-alpha.1) (2024-10-23) + + +### Bug Fixes + +* **delegates:** fixes type ([ae7be5c](https://github.com/ar-io/ar-io-sdk/commit/ae7be5caaa79f470eb9755c05c85467e98643d55)) +* **lint:** add lint fix and missing bracket ([72446aa](https://github.com/ar-io/ar-io-sdk/commit/72446aa0c1bfa8c69faf7781022a098c0e57d960)) +* **tag:** small tweak to instant tag ([663de6f](https://github.com/ar-io/ar-io-sdk/commit/663de6f04557549f010f12d435c303e051d00501)) + + +### Features + +* **delegates:** add instant delegate withdrawal for a fee ([4b4cb8f](https://github.com/ar-io/ar-io-sdk/commit/4b4cb8f6e6ee98c875541df21a0845f6069501f7)) + ## [2.3.3-alpha.1](https://github.com/ar-io/ar-io-sdk/compare/v2.3.2...v2.3.3-alpha.1) (2024-10-17) diff --git a/package.json b/package.json index e35b21d1..6ab3e813 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ar.io/sdk", - "version": "2.3.3-alpha.1", + "version": "2.4.0-alpha.1", "repository": { "type": "git", "url": "git+https://github.com/ar-io/ar-io-sdk.git" diff --git a/src/version.ts b/src/version.ts index 04ca73c3..8ba82545 100644 --- a/src/version.ts +++ b/src/version.ts @@ -16,4 +16,4 @@ // AUTOMATICALLY GENERATED FILE - DO NOT TOUCH -export const version = '2.3.3-alpha.1'; +export const version = '2.4.0-alpha.1'; From 974897b3458906dac325089d34c4ed45d780f368 Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Mon, 21 Oct 2024 10:37:45 -0500 Subject: [PATCH 23/97] feat(auctions): add auctions api to IO classes --- src/common/io.ts | 60 ++++ src/types/io.ts | 30 ++ tests/e2e/cjs/index.test.js | 602 ++++++++++++++++++------------------ tests/e2e/esm/index.test.js | 66 ++-- 4 files changed, 413 insertions(+), 345 deletions(-) diff --git a/src/common/io.ts b/src/common/io.ts index 398f552e..50b642eb 100644 --- a/src/common/io.ts +++ b/src/common/io.ts @@ -19,6 +19,7 @@ import { IO_TESTNET_PROCESS_ID } from '../constants.js'; import { AoArNSNameDataWithName, AoArNSReservedNameData, + AoAuction, AoBalanceWithAddress, AoEpochDistributionData, AoEpochObservationData, @@ -590,6 +591,39 @@ export class IOReadable implements AoIORead { tags: [{ name: 'Action', value: 'Demand-Factor' }], }); } + + // Auctions + async getAuctions( + params?: PaginationParams, + ): Promise> { + const allTags = [ + { name: 'Action', value: 'Auctions' }, + { name: 'Cursor', value: params?.cursor?.toString() }, + { name: 'Limit', value: params?.limit?.toString() }, + { name: 'Sort-By', value: params?.sortBy }, + { name: 'Sort-Order', value: params?.sortOrder }, + ]; + + const prunedTags: { name: string; value: string }[] = allTags.filter( + (tag: { + name: string; + value: string | undefined; + }): tag is { name: string; value: string } => tag.value !== undefined, + ); + + return this.process.read>({ + tags: prunedTags, + }); + } + + async getAuction({ name }: { name: string }): Promise { + return this.process.read({ + tags: [ + { name: 'Action', value: 'Auction-Info' }, + { name: 'Name', value: name }, + ], + }); + } } export class IOWriteable extends IOReadable implements AoIOWrite { @@ -1005,4 +1039,30 @@ export class IOWriteable extends IOReadable implements AoIOWrite { ], }); } + + async submitAuctionBid( + params: { name: string; processId: string; quantity?: number }, + options?: WriteOptions, + ): Promise { + const { tags = [] } = options || {}; + const allTags = [ + ...tags, + { name: 'Action', value: 'Submit-Auction-Bid' }, + { name: 'Name', value: params.name }, + { name: 'Process-Id', value: params.processId }, + { name: 'Quantity', value: params.quantity?.toString() ?? undefined }, + ]; + + const prunedTags: { name: string; value: string }[] = allTags.filter( + (tag: { + name: string; + value: string | undefined; + }): tag is { name: string; value: string } => tag.value !== undefined, + ); + + return this.process.send({ + signer: this.signer, + tags: prunedTags, + }); + } } diff --git a/src/types/io.ts b/src/types/io.ts index 71f5e8be..fd824ed8 100644 --- a/src/types/io.ts +++ b/src/types/io.ts @@ -257,6 +257,26 @@ export type AoBalanceWithAddress = { balance: number; }; +// Auctions +export type AoAuctionSettings = { + durationMs: number; + decayRate: number; + scalingExponent: number; + baseFee: number; + demandFactor: number; +}; + +export type AoAuction = { + name: string; + type: 'permabuy' | 'lease'; + startTimestamp: Timestamp; + endTimestamp: Timestamp; + startPrice: number; + floorPrice: number; + currentPrice: number; + settings: AoAuctionSettings; +}; + // Input types // TODO: confirm what is required or if all can be optional and defaults will be provided @@ -331,6 +351,8 @@ export interface AoIORead { }): Promise; getRegistrationFees(): Promise; getDemandFactor(): Promise; + getAuctions(params?: PaginationParams): Promise>; + getAuction({ name }: { name: string }): Promise; } export interface AoIOWrite extends AoIORead { @@ -450,6 +472,14 @@ export interface AoIOWrite extends AoIORead { }, options?: WriteOptions, ): Promise; + submitAuctionBid( + params: { + name: string; + processId: string; + quantity?: number; + }, + options?: WriteOptions, + ): Promise; } // Typeguard functions diff --git a/tests/e2e/cjs/index.test.js b/tests/e2e/cjs/index.test.js index 923cca7e..cc272fa3 100644 --- a/tests/e2e/cjs/index.test.js +++ b/tests/e2e/cjs/index.test.js @@ -41,344 +41,346 @@ const io = IO.init({ }), }); -describe('IO', async () => { - let compose; - before(async () => { - compose = await new DockerComposeEnvironment( - projectRootPath, - '../docker-compose.test.yml', - ) - .withBuild() - .withWaitStrategy('ao-cu-1', Wait.forHttp('/', 6363)) - .up(['ao-cu']); - }); +describe('e2e cjs tests', async () => { + describe('IO', async () => { + let compose; + before(async () => { + compose = await new DockerComposeEnvironment( + projectRootPath, + '../docker-compose.test.yml', + ) + .withBuild() + .withWaitStrategy('ao-cu-1', Wait.forHttp('/', 6363)) + .up(['ao-cu']); + }); - after(async () => { - await compose.down(); - }); - it('should be able to get the process information', async () => { - const epoch = await io.getInfo(); - assert.ok(epoch); - }); + after(async () => { + await compose.down(); + }); + it('should be able to get the process information', async () => { + const epoch = await io.getInfo(); + assert.ok(epoch); + }); - it('should be able to get the total token supply', async () => { - const tokenSupply = await io.getTokenSupply(); - assert.ok(tokenSupply); - }); + it('should be able to get the total token supply', async () => { + const tokenSupply = await io.getTokenSupply(); + assert.ok(tokenSupply); + }); - it('should be able to get first set of arns records', async () => { - const records = await io.getArNSRecords(); - assert.ok(records); - assert(records.limit === 100); - assert(records.sortOrder === 'desc'); - assert(records.sortBy === 'startTimestamp'); - assert(typeof records.totalItems === 'number'); - assert(typeof records.sortBy === 'string'); - assert(typeof records.sortOrder === 'string'); - assert(typeof records.limit === 'number'); - assert(typeof records.hasMore === 'boolean'); - if (records.nextCursor) { - assert(typeof records.nextCursor === 'string'); - } - assert(Array.isArray(records.items)); - records.items.forEach((record) => { - assert(typeof record.processId === 'string'); - assert(typeof record.name === 'string'); - assert(typeof record.startTimestamp === 'number'); - assert(['lease', 'permabuy'].includes(record.type)); - assert(typeof record.undernameLimit === 'number'); + it('should be able to get first set of arns records', async () => { + const records = await io.getArNSRecords(); + assert.ok(records); + assert(records.limit === 100); + assert(records.sortOrder === 'desc'); + assert(records.sortBy === 'startTimestamp'); + assert(typeof records.totalItems === 'number'); + assert(typeof records.sortBy === 'string'); + assert(typeof records.sortOrder === 'string'); + assert(typeof records.limit === 'number'); + assert(typeof records.hasMore === 'boolean'); + if (records.nextCursor) { + assert(typeof records.nextCursor === 'string'); + } + assert(Array.isArray(records.items)); + records.items.forEach((record) => { + assert(typeof record.processId === 'string'); + assert(typeof record.name === 'string'); + assert(typeof record.startTimestamp === 'number'); + assert(['lease', 'permabuy'].includes(record.type)); + assert(typeof record.undernameLimit === 'number'); + }); }); - }); - it('should be able to return a specific page of arns records', async () => { - const records = await io.getArNSRecords({ - cursor: 'ardrive', - limit: 5, - sortOrder: 'desc', - sortBy: 'name', + it('should be able to return a specific page of arns records', async () => { + const records = await io.getArNSRecords({ + cursor: 'ardrive', + limit: 5, + sortOrder: 'desc', + sortBy: 'name', + }); + assert.ok(records); + assert(records.limit === 5); + assert(records.sortOrder === 'desc'); + assert(records.sortBy === 'name'); + assert(typeof records.totalItems === 'number'); + assert(typeof records.sortBy === 'string'); + assert(typeof records.sortOrder === 'string'); + assert(typeof records.limit === 'number'); + assert(typeof records.hasMore === 'boolean'); + if (records.nextCursor) { + assert(typeof records.nextCursor === 'string'); + } + assert(Array.isArray(records.items)); + records.items.forEach((record) => { + assert(typeof record.processId === 'string'); + assert(typeof record.name === 'string'); + assert(typeof record.startTimestamp === 'number'); + assert(['lease', 'permabuy'].includes(record.type)); + assert(typeof record.undernameLimit === 'number'); + }); }); - assert.ok(records); - assert(records.limit === 5); - assert(records.sortOrder === 'desc'); - assert(records.sortBy === 'name'); - assert(typeof records.totalItems === 'number'); - assert(typeof records.sortBy === 'string'); - assert(typeof records.sortOrder === 'string'); - assert(typeof records.limit === 'number'); - assert(typeof records.hasMore === 'boolean'); - if (records.nextCursor) { - assert(typeof records.nextCursor === 'string'); - } - assert(Array.isArray(records.items)); - records.items.forEach((record) => { - assert(typeof record.processId === 'string'); - assert(typeof record.name === 'string'); - assert(typeof record.startTimestamp === 'number'); - assert(['lease', 'permabuy'].includes(record.type)); - assert(typeof record.undernameLimit === 'number'); + it('should be able to get a single arns record', async () => { + const arns = await io.getArNSRecord({ name: 'ardrive' }); + assert.ok(arns); }); - }); - it('should be able to get a single arns record', async () => { - const arns = await io.getArNSRecord({ name: 'ardrive' }); - assert.ok(arns); - }); - - it('should be able to get the current epoch using getCurrentEpoch', async () => { - const epoch = await io.getCurrentEpoch(); - assert.ok(epoch); - }); - - it('should be able to get the current epoch using getEpoch', async () => { - const epoch = await io.getEpoch({ epochIndex: 0 }); - assert.ok(epoch); - }); - it('should be able to get epoch-settings', async () => { - const epochSettings = await io.getEpochSettings(); - assert.ok(epochSettings); - }); + it('should be able to get the current epoch using getCurrentEpoch', async () => { + const epoch = await io.getCurrentEpoch(); + assert.ok(epoch); + }); - it('should be able to get reserved names', async () => { - const reservedNames = await io.getArNSReservedNames(); - assert.ok(reservedNames); - }); + it('should be able to get the current epoch using getEpoch', async () => { + const epoch = await io.getEpoch({ epochIndex: 0 }); + assert.ok(epoch); + }); - it('should be able to get a single reserved name', async () => { - const reservedNames = await io.getArNSReservedNames({ name: 'www ' }); - assert.ok(reservedNames); - }); + it('should be able to get epoch-settings', async () => { + const epochSettings = await io.getEpochSettings(); + assert.ok(epochSettings); + }); - it('should be able to get first page of gateways', async () => { - const gateways = await io.getGateways(); - assert.ok(gateways); - assert(gateways.limit === 100); - assert(gateways.sortOrder === 'desc'); - assert(gateways.sortBy === 'startTimestamp'); - assert(typeof gateways.totalItems === 'number'); - assert(typeof gateways.sortBy === 'string'); - assert(typeof gateways.sortOrder === 'string'); - assert(typeof gateways.limit === 'number'); - assert(typeof gateways.hasMore === 'boolean'); - if (gateways.nextCursor) { - assert(typeof gateways.nextCursor === 'string'); - } - assert(Array.isArray(gateways.items)); - gateways.items.forEach((gateway) => { - assert(typeof gateway.gatewayAddress === 'string'); - assert(typeof gateway.observerAddress === 'string'); - assert(typeof gateway.startTimestamp === 'number'); - assert(typeof gateway.operatorStake === 'number'); - assert(typeof gateway.totalDelegatedStake === 'number'); - assert(typeof gateway.settings === 'object'); - assert(typeof gateway.weights === 'object'); - assert(typeof gateway.weights.normalizedCompositeWeight === 'number'); - assert(typeof gateway.weights.compositeWeight === 'number'); - assert(typeof gateway.weights.stakeWeight === 'number'); - assert(typeof gateway.weights.tenureWeight === 'number'); - assert(typeof gateway.weights.observerRewardRatioWeight === 'number'); - assert(typeof gateway.weights.gatewayRewardRatioWeight === 'number'); + it('should be able to get reserved names', async () => { + const reservedNames = await io.getArNSReservedNames(); + assert.ok(reservedNames); }); - }); - it('should be able to get a specific page of gateways', async () => { - const gateways = await io.getGateways({ - cursor: 1000000, - limit: 1, - sortBy: 'operatorStake', - sortOrder: 'desc', + it('should be able to get a single reserved name', async () => { + const reservedNames = await io.getArNSReservedNames({ name: 'www ' }); + assert.ok(reservedNames); }); - assert.ok(gateways); - assert(gateways.limit === 1); - assert(gateways.sortOrder === 'desc'); - assert(gateways.sortBy === 'operatorStake'); - assert(typeof gateways.totalItems === 'number'); - assert(typeof gateways.sortBy === 'string'); - assert(typeof gateways.sortOrder === 'string'); - assert(typeof gateways.limit === 'number'); - assert(typeof gateways.hasMore === 'boolean'); - if (gateways.nextCursor) { - assert(typeof gateways.nextCursor === 'string'); - } - assert(Array.isArray(gateways.items)); - gateways.items.forEach((gateway) => { - assert(typeof gateway.gatewayAddress === 'string'); - assert(typeof gateway.observerAddress === 'string'); - assert(typeof gateway.startTimestamp === 'number'); - assert(typeof gateway.operatorStake === 'number'); - assert(typeof gateway.totalDelegatedStake === 'number'); - assert(typeof gateway.settings === 'object'); - assert(typeof gateway.weights === 'object'); - assert(typeof gateway.weights.normalizedCompositeWeight === 'number'); - assert(typeof gateway.weights.compositeWeight === 'number'); - assert(typeof gateway.weights.stakeWeight === 'number'); - assert(typeof gateway.weights.tenureWeight === 'number'); - assert(typeof gateway.weights.observerRewardRatioWeight === 'number'); - assert(typeof gateway.weights.gatewayRewardRatioWeight === 'number'); + + it('should be able to get first page of gateways', async () => { + const gateways = await io.getGateways(); + assert.ok(gateways); + assert(gateways.limit === 100); + assert(gateways.sortOrder === 'desc'); + assert(gateways.sortBy === 'startTimestamp'); + assert(typeof gateways.totalItems === 'number'); + assert(typeof gateways.sortBy === 'string'); + assert(typeof gateways.sortOrder === 'string'); + assert(typeof gateways.limit === 'number'); + assert(typeof gateways.hasMore === 'boolean'); + if (gateways.nextCursor) { + assert(typeof gateways.nextCursor === 'string'); + } + assert(Array.isArray(gateways.items)); + gateways.items.forEach((gateway) => { + assert(typeof gateway.gatewayAddress === 'string'); + assert(typeof gateway.observerAddress === 'string'); + assert(typeof gateway.startTimestamp === 'number'); + assert(typeof gateway.operatorStake === 'number'); + assert(typeof gateway.totalDelegatedStake === 'number'); + assert(typeof gateway.settings === 'object'); + assert(typeof gateway.weights === 'object'); + assert(typeof gateway.weights.normalizedCompositeWeight === 'number'); + assert(typeof gateway.weights.compositeWeight === 'number'); + assert(typeof gateway.weights.stakeWeight === 'number'); + assert(typeof gateway.weights.tenureWeight === 'number'); + assert(typeof gateway.weights.observerRewardRatioWeight === 'number'); + assert(typeof gateway.weights.gatewayRewardRatioWeight === 'number'); + }); }); - }); - it('should be able to get a single gateway', async () => { - const gateways = await io.getGateway({ - address: 'QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ', + it('should be able to get a specific page of gateways', async () => { + const gateways = await io.getGateways({ + cursor: 1000000, + limit: 1, + sortBy: 'operatorStake', + sortOrder: 'desc', + }); + assert.ok(gateways); + assert(gateways.limit === 1); + assert(gateways.sortOrder === 'desc'); + assert(gateways.sortBy === 'operatorStake'); + assert(typeof gateways.totalItems === 'number'); + assert(typeof gateways.sortBy === 'string'); + assert(typeof gateways.sortOrder === 'string'); + assert(typeof gateways.limit === 'number'); + assert(typeof gateways.hasMore === 'boolean'); + if (gateways.nextCursor) { + assert(typeof gateways.nextCursor === 'string'); + } + assert(Array.isArray(gateways.items)); + gateways.items.forEach((gateway) => { + assert(typeof gateway.gatewayAddress === 'string'); + assert(typeof gateway.observerAddress === 'string'); + assert(typeof gateway.startTimestamp === 'number'); + assert(typeof gateway.operatorStake === 'number'); + assert(typeof gateway.totalDelegatedStake === 'number'); + assert(typeof gateway.settings === 'object'); + assert(typeof gateway.weights === 'object'); + assert(typeof gateway.weights.normalizedCompositeWeight === 'number'); + assert(typeof gateway.weights.compositeWeight === 'number'); + assert(typeof gateway.weights.stakeWeight === 'number'); + assert(typeof gateway.weights.tenureWeight === 'number'); + assert(typeof gateway.weights.observerRewardRatioWeight === 'number'); + assert(typeof gateway.weights.gatewayRewardRatioWeight === 'number'); + }); }); - assert.ok(gateways); - }); - it('should be able to get balances, defaulting to first page', async () => { - const balances = await io.getBalances(); - assert.ok(balances); - assert(balances.limit === 100); - assert(balances.sortOrder === 'desc'); - assert(balances.sortBy === 'balance'); - assert(typeof balances.totalItems === 'number'); - assert(typeof balances.sortBy === 'string'); - assert(typeof balances.sortOrder === 'string'); - assert(typeof balances.limit === 'number'); - assert(typeof balances.hasMore === 'boolean'); - if (balances.nextCursor) { - assert(typeof gateways.nextCursor === 'string'); - } - assert(Array.isArray(balances.items)); - balances.items.forEach((wallet) => { - assert(typeof wallet.address === 'string'); - assert(typeof wallet.balance === 'number'); + it('should be able to get a single gateway', async () => { + const gateways = await io.getGateway({ + address: 'QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ', + }); + assert.ok(gateways); }); - }); - it('should be able to get balances of a specific to first page', async () => { - const balances = await io.getBalances({ - cursor: 1000000, - limit: 1, - sortBy: 'address', - sortOrder: 'asc', + it('should be able to get balances, defaulting to first page', async () => { + const balances = await io.getBalances(); + assert.ok(balances); + assert(balances.limit === 100); + assert(balances.sortOrder === 'desc'); + assert(balances.sortBy === 'balance'); + assert(typeof balances.totalItems === 'number'); + assert(typeof balances.sortBy === 'string'); + assert(typeof balances.sortOrder === 'string'); + assert(typeof balances.limit === 'number'); + assert(typeof balances.hasMore === 'boolean'); + if (balances.nextCursor) { + assert(typeof gateways.nextCursor === 'string'); + } + assert(Array.isArray(balances.items)); + balances.items.forEach((wallet) => { + assert(typeof wallet.address === 'string'); + assert(typeof wallet.balance === 'number'); + }); }); - assert.ok(balances); - assert(balances.limit === 1); - assert(balances.sortOrder === 'asc'); - assert(balances.sortBy === 'address'); - assert(typeof balances.totalItems === 'number'); - assert(typeof balances.sortBy === 'string'); - assert(typeof balances.sortOrder === 'string'); - assert(typeof balances.limit === 'number'); - assert(typeof balances.hasMore === 'boolean'); - if (balances.nextCursor) { - assert(typeof balances.nextCursor === 'string'); - } - assert(Array.isArray(balances.items)); - balances.items.forEach((wallet) => { - assert(typeof wallet.address === 'string'); - assert(typeof wallet.balance === 'number'); + + it('should be able to get balances of a specific to first page', async () => { + const balances = await io.getBalances({ + cursor: 1000000, + limit: 1, + sortBy: 'address', + sortOrder: 'asc', + }); + assert.ok(balances); + assert(balances.limit === 1); + assert(balances.sortOrder === 'asc'); + assert(balances.sortBy === 'address'); + assert(typeof balances.totalItems === 'number'); + assert(typeof balances.sortBy === 'string'); + assert(typeof balances.sortOrder === 'string'); + assert(typeof balances.limit === 'number'); + assert(typeof balances.hasMore === 'boolean'); + if (balances.nextCursor) { + assert(typeof balances.nextCursor === 'string'); + } + assert(Array.isArray(balances.items)); + balances.items.forEach((wallet) => { + assert(typeof wallet.address === 'string'); + assert(typeof wallet.balance === 'number'); + }); }); - }); - it('should be able to get a single balance', async () => { - const balances = await io.getBalance({ - address: 'QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ', + it('should be able to get a single balance', async () => { + const balances = await io.getBalance({ + address: 'QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ', + }); + assert.ok(balances); }); - assert.ok(balances); - }); - it('should be able to get prescribed names', async () => { - const prescribedNames = await io.getPrescribedNames(); - assert.ok(prescribedNames); - }); + it('should be able to get prescribed names', async () => { + const prescribedNames = await io.getPrescribedNames(); + assert.ok(prescribedNames); + }); - it('should return the prescribed observers for a given epoch', async () => { - const observers = await io.getPrescribedObservers(); - assert.ok(observers); - for (const observer of observers) { - assert(typeof observer.gatewayAddress === 'string'); - assert(typeof observer.observerAddress === 'string'); - assert(typeof observer.stake === 'number'); - assert(typeof observer.startTimestamp === 'number'); - assert(typeof observer.stakeWeight === 'number'); - assert(typeof observer.tenureWeight === 'number'); - assert(typeof observer.gatewayRewardRatioWeight === 'number'); - assert(typeof observer.observerRewardRatioWeight === 'number'); - assert(typeof observer.compositeWeight === 'number'); - } - }); + it('should return the prescribed observers for a given epoch', async () => { + const observers = await io.getPrescribedObservers(); + assert.ok(observers); + for (const observer of observers) { + assert(typeof observer.gatewayAddress === 'string'); + assert(typeof observer.observerAddress === 'string'); + assert(typeof observer.stake === 'number'); + assert(typeof observer.startTimestamp === 'number'); + assert(typeof observer.stakeWeight === 'number'); + assert(typeof observer.tenureWeight === 'number'); + assert(typeof observer.gatewayRewardRatioWeight === 'number'); + assert(typeof observer.observerRewardRatioWeight === 'number'); + assert(typeof observer.compositeWeight === 'number'); + } + }); - it('should be able to get token cost for leasing a name', async () => { - const tokenCost = await io.getTokenCost({ - intent: 'Buy-Record', - name: 'new-name', - years: 1, + it('should be able to get token cost for leasing a name', async () => { + const tokenCost = await io.getTokenCost({ + intent: 'Buy-Record', + name: 'new-name', + years: 1, + }); + assert.ok(tokenCost); }); - assert.ok(tokenCost); - }); - it('should be able to get token cost for buying a name name', async () => { - const tokenCost = await io.getTokenCost({ - intent: 'Buy-Record', - name: 'new-name', - type: 'permabuy', + it('should be able to get token cost for buying a name name', async () => { + const tokenCost = await io.getTokenCost({ + intent: 'Buy-Record', + name: 'new-name', + type: 'permabuy', + }); + assert.ok(tokenCost); }); - assert.ok(tokenCost); - }); - it('should be able to get registration fees', async () => { - const registrationFees = await io.getRegistrationFees(); - assert(registrationFees); - assert.equal(Object.keys(registrationFees).length, 51); - for (const nameLength of Object.keys(registrationFees)) { - // assert lease is length of 5 - assert(registrationFees[nameLength]['lease']['1'] > 0); - assert(registrationFees[nameLength]['lease']['2'] > 0); - assert(registrationFees[nameLength]['lease']['3'] > 0); - assert(registrationFees[nameLength]['lease']['4'] > 0); - assert(registrationFees[nameLength]['lease']['5'] > 0); - assert(registrationFees[nameLength]['permabuy'] > 0); - } - }); - it('should be able to create IOWriteable with valid signers', async () => { - for (const signer of signers) { - const io = IO.init({ signer }); + it('should be able to get registration fees', async () => { + const registrationFees = await io.getRegistrationFees(); + assert(registrationFees); + assert.equal(Object.keys(registrationFees).length, 51); + for (const nameLength of Object.keys(registrationFees)) { + // assert lease is length of 5 + assert(registrationFees[nameLength]['lease']['1'] > 0); + assert(registrationFees[nameLength]['lease']['2'] > 0); + assert(registrationFees[nameLength]['lease']['3'] > 0); + assert(registrationFees[nameLength]['lease']['4'] > 0); + assert(registrationFees[nameLength]['lease']['5'] > 0); + assert(registrationFees[nameLength]['permabuy'] > 0); + } + }); + it('should be able to create IOWriteable with valid signers', async () => { + for (const signer of signers) { + const io = IO.init({ signer }); - assert(io instanceof IOWriteable); - } + assert(io instanceof IOWriteable); + } + }); }); -}); -describe('ANTRegistry', async () => { - const registry = ANTRegistry.init(); - const address = '7waR8v4STuwPnTck1zFVkQqJh5K9q9Zik4Y5-5dV7nk'; + describe('ANTRegistry', async () => { + const registry = ANTRegistry.init(); + const address = '7waR8v4STuwPnTck1zFVkQqJh5K9q9Zik4Y5-5dV7nk'; - it('should retrieve ids from registry', async () => { - const affiliatedAnts = await registry.accessControlList({ address }); - assert(Array.isArray(affiliatedAnts.Owned)); - assert(Array.isArray(affiliatedAnts.Controlled)); - }); + it('should retrieve ids from registry', async () => { + const affiliatedAnts = await registry.accessControlList({ address }); + assert(Array.isArray(affiliatedAnts.Owned)); + assert(Array.isArray(affiliatedAnts.Controlled)); + }); - it('should be able to create AoANTRegistryWriteable with valid signers', async () => { - for (const signer of signers) { - const registry = ANTRegistry.init({ - signer, - }); - assert(registry instanceof AoANTRegistryWriteable); - } + it('should be able to create AoANTRegistryWriteable with valid signers', async () => { + for (const signer of signers) { + const registry = ANTRegistry.init({ + signer, + }); + assert(registry instanceof AoANTRegistryWriteable); + } + }); }); -}); -describe('ANT', async () => { - const processId = 'YcxE5IbqZYK72H64ELoysxiJ-0wb36deYPv55wgl8xo'; - it('should be able to create ANTWriteable with valid signers', async () => { - for (const signer of signers) { - const ant = ANT.init({ - processId, - signer, - strict: true, - ao: aoClient, - }); - const strictAnt = ANT.init({ - processId, - signer, - strict: true, - ao: aoClient, - }); + describe('ANT', async () => { + const processId = 'YcxE5IbqZYK72H64ELoysxiJ-0wb36deYPv55wgl8xo'; + it('should be able to create ANTWriteable with valid signers', async () => { + for (const signer of signers) { + const ant = ANT.init({ + processId, + signer, + strict: true, + ao: aoClient, + }); + const strictAnt = ANT.init({ + processId, + signer, + strict: true, + ao: aoClient, + }); - assert(ant instanceof AoANTWriteable); - assert(strictAnt instanceof AoANTWriteable); - } + assert(ant instanceof AoANTWriteable); + assert(strictAnt instanceof AoANTWriteable); + } + }); }); }); diff --git a/tests/e2e/esm/index.test.js b/tests/e2e/esm/index.test.js index cdfa6d9c..1a7f752d 100644 --- a/tests/e2e/esm/index.test.js +++ b/tests/e2e/esm/index.test.js @@ -41,7 +41,7 @@ const io = IO.init({ }), }); -describe('IO', async () => { +describe('e2e esm tests', async () => { let compose; before(async () => { compose = await new DockerComposeEnvironment( @@ -57,50 +57,16 @@ describe('IO', async () => { await compose.down(); }); - it('should be able to get the process information', async () => { - const info = await io.getInfo(); - assert.ok(info); - assert(typeof info.Name === 'string'); - assert(typeof info.Ticker === 'string'); - assert(typeof info.Logo === 'string'); - assert(typeof info.Denomination === 'number'); - assert(Array.isArray(info.Handlers)); - assert(typeof info.LastTickedEpochIndex === 'number'); - }); - - it('should be able to get the total token supply', async () => { - const tokenSupply = await io.getTokenSupply(); - assert.ok(tokenSupply); - assert(typeof tokenSupply.total === 'number'); - assert(typeof tokenSupply.circulating === 'number'); - assert(typeof tokenSupply.locked === 'number'); - assert(typeof tokenSupply.withdrawn === 'number'); - assert(typeof tokenSupply.delegated === 'number'); - assert(typeof tokenSupply.staked === 'number'); - assert(typeof tokenSupply.protocolBalance === 'number'); - }); - - it('should be able to get first set of arns records', async () => { - const records = await io.getArNSRecords(); - assert.ok(records); - assert(records.limit === 100); - assert(records.sortOrder === 'desc'); - assert(records.sortBy === 'startTimestamp'); - assert(typeof records.totalItems === 'number'); - assert(typeof records.sortBy === 'string'); - assert(typeof records.sortOrder === 'string'); - assert(typeof records.limit === 'number'); - assert(typeof records.hasMore === 'boolean'); - if (records.nextCursor) { - assert(typeof records.nextCursor === 'string'); - } - assert(Array.isArray(records.items)); - records.items.forEach((record) => { - assert(typeof record.processId === 'string'); - assert(typeof record.name === 'string'); - assert(typeof record.startTimestamp === 'number'); - assert(['lease', 'permabuy'].includes(record.type)); - assert(typeof record.undernameLimit === 'number'); + describe('IO', async () => { + it('should be able to get the process information', async () => { + const info = await io.getInfo(); + assert.ok(info); + assert(typeof info.Name === 'string'); + assert(typeof info.Ticker === 'string'); + assert(typeof info.Logo === 'string'); + assert(typeof info.Denomination === 'number'); + assert(Array.isArray(info.Handlers)); + assert(typeof info.LastTickedEpochIndex === 'number'); }); }); @@ -366,6 +332,16 @@ describe('IO', async () => { assert.ok(demandFactor); }); + it('should be able to get current auctions', async () => { + const auctions = await io.getAuctions(); + assert.ok(auctions); + }); + + it('should be able to get a specific auction', async () => { + const auction = await io.getAuction({ name: 'ardrive' }); + assert.ok(auction); + }); + it('should be able to create IOWriteable with valid signers', async () => { for (const signer of signers) { const io = IO.init({ signer }); From cb7d2b49edf0e40734052078d9b5f5723e134876 Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Mon, 21 Oct 2024 16:49:08 -0500 Subject: [PATCH 24/97] fix(types): update types to match contract --- examples/esm/index.mjs | 2 +- src/common/io.ts | 47 ++++++++++++++++++++++++++++++++++-------- src/types/io.ts | 21 +++++++++++++------ 3 files changed, 54 insertions(+), 16 deletions(-) diff --git a/examples/esm/index.mjs b/examples/esm/index.mjs index 7d6eead8..347b3c6f 100644 --- a/examples/esm/index.mjs +++ b/examples/esm/index.mjs @@ -20,7 +20,7 @@ import { const distributions = await arIO.getDistributions({ epochIndex: 0 }); const buyRecordCost = await arIO.getTokenCost({ intent: 'Buy-Record', - purchaseType: 'lease', + type: 'lease', name: 'ar-io-dapp-record', years: 1, }); diff --git a/src/common/io.ts b/src/common/io.ts index 50b642eb..87a20ba6 100644 --- a/src/common/io.ts +++ b/src/common/io.ts @@ -504,7 +504,7 @@ export class IOReadable implements AoIORead { async getTokenCost(params: { intent: 'Buy-Record'; - purchaseType: 'permabuy' | 'lease'; + type: 'permabuy' | 'lease'; years: number; name: string; }): Promise; @@ -520,13 +520,13 @@ export class IOReadable implements AoIORead { }): Promise; async getTokenCost({ intent, - purchaseType, + type, years, name, quantity, }: { intent: 'Buy-Record' | 'Extend-Lease' | 'Increase-Undername-Limit'; - purchaseType?: 'permabuy' | 'lease'; + type?: 'permabuy' | 'lease'; years?: number; name?: string; quantity?: number; @@ -551,7 +551,7 @@ export class IOReadable implements AoIORead { }, { name: 'Purchase-Type', - value: purchaseType, + value: type, }, { name: 'Timestamp', @@ -616,12 +616,41 @@ export class IOReadable implements AoIORead { }); } - async getAuction({ name }: { name: string }): Promise { + async getAuction({ + name, + type, + timestamp, + years, + // TODO: include prices, which is a separate message and requires a different tag, and is fairly expensive on compute + }: { + name: string; + timestamp?: number; + type: 'permabuy' | 'lease'; + years?: number; + }): Promise { + const allTags = [ + { name: 'Action', value: 'Auction-Info' }, + { name: 'Name', value: name }, + { + name: 'Timestamp', + value: timestamp?.toString() ?? Date.now().toString(), + }, + { name: 'Purchase-Type', value: type ?? 'lease' }, + { + name: 'Years', + value: type === 'lease' ? years?.toString() ?? '1' : undefined, + }, + ]; + + const prunedTags: { name: string; value: string }[] = allTags.filter( + (tag: { + name: string; + value: string | undefined; + }): tag is { name: string; value: string } => tag.value !== undefined, + ); + return this.process.read({ - tags: [ - { name: 'Action', value: 'Auction-Info' }, - { name: 'Name', value: name }, - ], + tags: prunedTags, }); } } diff --git a/src/types/io.ts b/src/types/io.ts index fd824ed8..b253add4 100644 --- a/src/types/io.ts +++ b/src/types/io.ts @@ -264,15 +264,13 @@ export type AoAuctionSettings = { scalingExponent: number; baseFee: number; demandFactor: number; + startPriceMultiplier: number; }; export type AoAuction = { name: string; - type: 'permabuy' | 'lease'; startTimestamp: Timestamp; endTimestamp: Timestamp; - startPrice: number; - floorPrice: number; currentPrice: number; settings: AoAuctionSettings; }; @@ -338,13 +336,13 @@ export interface AoIORead { getDistributions(epoch?: EpochInput): Promise; getTokenCost({ intent, - purchaseType, + type, years, name, quantity, }: { intent: 'Buy-Record' | 'Extend-Lease' | 'Increase-Undername-Limit'; - purchaseType?: 'permabuy' | 'lease'; + type?: 'permabuy' | 'lease'; years?: number; name?: string; quantity?: number; @@ -352,7 +350,18 @@ export interface AoIORead { getRegistrationFees(): Promise; getDemandFactor(): Promise; getAuctions(params?: PaginationParams): Promise>; - getAuction({ name }: { name: string }): Promise; + getAuction({ + name, + type, + timestamp, + years, + }: { + name: string; + type: 'permabuy' | 'lease'; + timestamp?: number; + years?: number; + // TODO: include prices, which is a separate message + }): Promise; } export interface AoIOWrite extends AoIORead { From 32001c2cfcaab4aa0e03aeee99888ba9e2efd6ba Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Wed, 23 Oct 2024 13:43:32 -0500 Subject: [PATCH 25/97] fix(auctions): update read APIs to fetch auctions, use vite example display active auction --- examples/vite/package.json | 1 + examples/vite/src/App.css | 2 +- examples/vite/src/App.tsx | 90 ++++++++++--- examples/vite/src/index.css | 2 +- examples/vite/yarn.lock | 252 +++++++++++++++++++++++++++++++++++- src/common/io.ts | 45 ++++--- src/types/io.ts | 13 +- 7 files changed, 361 insertions(+), 44 deletions(-) diff --git a/examples/vite/package.json b/examples/vite/package.json index b1422ca0..0d8cb871 100644 --- a/examples/vite/package.json +++ b/examples/vite/package.json @@ -7,6 +7,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-markdown": "^9.0.1", + "recharts": "^2.13.0", "remark-gfm": "^4.0.0" }, "devDependencies": { diff --git a/examples/vite/src/App.css b/examples/vite/src/App.css index d99bf119..ed6a79d2 100644 --- a/examples/vite/src/App.css +++ b/examples/vite/src/App.css @@ -1,6 +1,6 @@ .App { text-align: center; - background-color: #282c34; + background-color: #f0f0f0; } .markdown { diff --git a/examples/vite/src/App.tsx b/examples/vite/src/App.tsx index 0139d109..e013d801 100644 --- a/examples/vite/src/App.tsx +++ b/examples/vite/src/App.tsx @@ -1,33 +1,87 @@ -import { ANT, ANTState } from '@ar.io/sdk/web'; +import { + AoAuction, + AoAuctionPriceData, + IO, + IO_DEVNET_PROCESS_ID, + PaginationResult, +} from '@ar.io/sdk/web'; import { useEffect, useState } from 'react'; -import Markdown from 'react-markdown'; -import remarkGfm from 'remark-gfm'; +import { + Label, + Line, + LineChart, + ResponsiveContainer, + Tooltip, + XAxis, + YAxis, +} from 'recharts'; import './App.css'; -const contractTxId = 'ilwT4ObFQ7cGPbW-8z-h7mvvWGt_yhWNlqxNjSUgiYY'; -const antContract = ANT.init({ contractTxId, strict: true }); +const io = IO.init({ processId: IO_DEVNET_PROCESS_ID }); + +type AuctionWithPrices = AoAuction & { + prices: { timestamp: string; price: number }[]; +}; function App() { - const [contract, setContract] = useState('Loading...'); + const [auctions, setAuctions] = useState([]); useEffect(() => { - antContract - .getState() - .then((state: ANTState) => { - setContract(`\`\`\`json\n${JSON.stringify(state, null, 2)}`); - }) - .catch((error: unknown) => { - console.error(error); - setContract('Error loading contract state'); + console.log('fetching auctions', io); + io.getAuctions().then((page: PaginationResult) => { + page.items.forEach((auction: AoAuction) => { + console.log('auction', auction); + io.getAuctionPrices({ name: auction.name, type: 'lease' }).then( + (price: AoAuctionPriceData) => { + const arrayOfPrices = Object.entries(price.prices) + .sort(([timestampA], [timestampB]) => +timestampA - +timestampB) + .map(([timestamp, price]) => ({ + timestamp: new Date(+timestamp).toLocaleString('en-US', { + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + hour12: false, + }), + price: price / 10 ** 6, + })); + const auctionWithPrices = { ...auction, prices: arrayOfPrices }; + setAuctions((prev) => [...prev, auctionWithPrices]); + }, + ); }); + }); }, []); return ( -
- - {contract} - +
+ {/* add a line chart using recharts */} + {auctions.length > 0 && ( + + + + + + + + + + + + )}
); } diff --git a/examples/vite/src/index.css b/examples/vite/src/index.css index e8c77d29..20d9a81b 100644 --- a/examples/vite/src/index.css +++ b/examples/vite/src/index.css @@ -5,7 +5,7 @@ body { sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; - background-color: #282c34; + background-color: #f0f0f0; } code { diff --git a/examples/vite/yarn.lock b/examples/vite/yarn.lock index 8b1eaf2a..9f213319 100644 --- a/examples/vite/yarn.lock +++ b/examples/vite/yarn.lock @@ -199,6 +199,13 @@ dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.25.9.tgz#65884fd6dc255a775402cc1d9811082918f4bf00" + integrity sha512-4zpTHZ9Cm6L9L+uIqghQX8ZXg8HKFcjYO3qHoO8zTmRm6HQUJ8SSJ+KRvbMBZn0EGVlT4DRYeQ/6hjlyXBh+Kg== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/template@^7.22.15", "@babel/template@^7.24.0": version "7.24.0" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.0.tgz#c6a524aa93a4a05d66aaf31654258fae69d87d50" @@ -952,6 +959,57 @@ dependencies: "@babel/types" "^7.20.7" +"@types/d3-array@^3.0.3": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-3.2.1.tgz#1f6658e3d2006c4fceac53fde464166859f8b8c5" + integrity sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg== + +"@types/d3-color@*": + version "3.1.3" + resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-3.1.3.tgz#368c961a18de721da8200e80bf3943fb53136af2" + integrity sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A== + +"@types/d3-ease@^3.0.0": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/d3-ease/-/d3-ease-3.0.2.tgz#e28db1bfbfa617076f7770dd1d9a48eaa3b6c51b" + integrity sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA== + +"@types/d3-interpolate@^3.0.1": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz#412b90e84870285f2ff8a846c6eb60344f12a41c" + integrity sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA== + dependencies: + "@types/d3-color" "*" + +"@types/d3-path@*": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-3.1.0.tgz#2b907adce762a78e98828f0b438eaca339ae410a" + integrity sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ== + +"@types/d3-scale@^4.0.2": + version "4.0.8" + resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-4.0.8.tgz#d409b5f9dcf63074464bf8ddfb8ee5a1f95945bb" + integrity sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ== + dependencies: + "@types/d3-time" "*" + +"@types/d3-shape@^3.1.0": + version "3.1.6" + resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-3.1.6.tgz#65d40d5a548f0a023821773e39012805e6e31a72" + integrity sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA== + dependencies: + "@types/d3-path" "*" + +"@types/d3-time@*", "@types/d3-time@^3.0.0": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-3.0.3.tgz#3c186bbd9d12b9d84253b6be6487ca56b54f88be" + integrity sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw== + +"@types/d3-timer@^3.0.0": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/d3-timer/-/d3-timer-3.0.2.tgz#70bbda77dc23aa727413e22e214afa3f0e852f70" + integrity sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw== + "@types/debug@^4.0.0": version "4.1.12" resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917" @@ -1738,6 +1796,11 @@ clean-css@^5.2.2: dependencies: source-map "~0.6.0" +clsx@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999" + integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -1933,6 +1996,77 @@ csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== +"d3-array@2 - 3", "d3-array@2.10.0 - 3", d3-array@^3.1.6: + version "3.2.4" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.2.4.tgz#15fec33b237f97ac5d7c986dc77da273a8ed0bb5" + integrity sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg== + dependencies: + internmap "1 - 2" + +"d3-color@1 - 3": + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2" + integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA== + +d3-ease@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4" + integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w== + +"d3-format@1 - 3": + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.1.0.tgz#9260e23a28ea5cb109e93b21a06e24e2ebd55641" + integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA== + +"d3-interpolate@1.2.0 - 3", d3-interpolate@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d" + integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g== + dependencies: + d3-color "1 - 3" + +d3-path@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-3.1.0.tgz#22df939032fb5a71ae8b1800d61ddb7851c42526" + integrity sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ== + +d3-scale@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-4.0.2.tgz#82b38e8e8ff7080764f8dcec77bd4be393689396" + integrity sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ== + dependencies: + d3-array "2.10.0 - 3" + d3-format "1 - 3" + d3-interpolate "1.2.0 - 3" + d3-time "2.1.1 - 3" + d3-time-format "2 - 4" + +d3-shape@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-3.2.0.tgz#a1a839cbd9ba45f28674c69d7f855bcf91dfc6a5" + integrity sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA== + dependencies: + d3-path "^3.1.0" + +"d3-time-format@2 - 4": + version "4.1.0" + resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-4.1.0.tgz#7ab5257a5041d11ecb4fe70a5c7d16a195bb408a" + integrity sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg== + dependencies: + d3-time "1 - 3" + +"d3-time@1 - 3", "d3-time@2.1.1 - 3", d3-time@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-3.1.0.tgz#9310db56e992e3c0175e1ef385e545e48a9bb5c7" + integrity sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q== + dependencies: + d3-array "2 - 3" + +d3-timer@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0" + integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA== + debug@^4.0.0, debug@^4.1.0, debug@^4.3.1: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" @@ -1940,6 +2074,11 @@ debug@^4.0.0, debug@^4.1.0, debug@^4.3.1: dependencies: ms "2.1.2" +decimal.js-light@^2.4.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934" + integrity sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg== + decode-named-character-reference@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz#daabac9690874c394c81e4162a0304b35d824f0e" @@ -2038,6 +2177,14 @@ dom-accessibility-api@^0.5.6, dom-accessibility-api@^0.5.9: resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== +dom-helpers@^5.0.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" + integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== + dependencies: + "@babel/runtime" "^7.8.7" + csstype "^3.0.2" + dom-serializer@^1.0.1: version "1.4.1" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" @@ -2234,6 +2381,11 @@ estree-walker@^2.0.1, estree-walker@^2.0.2: resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== +eventemitter3@^4.0.1: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + events@3.3.0, events@^3.0.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" @@ -2273,6 +2425,11 @@ fast-copy@^3.0.0: resolved "https://registry.yarnpkg.com/fast-copy/-/fast-copy-3.0.2.tgz#59c68f59ccbcac82050ba992e0d5c389097c9d35" integrity sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ== +fast-equals@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/fast-equals/-/fast-equals-5.0.1.tgz#a4eefe3c5d1c0d021aeed0bc10ba5e0c12ee405d" + integrity sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ== + fast-glob@^3.2.11: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" @@ -2614,6 +2771,11 @@ internal-slot@^1.0.4: hasown "^2.0.0" side-channel "^1.0.4" +"internmap@1 - 2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/internmap/-/internmap-2.0.3.tgz#6685f23755e43c524e251d29cbc97248e3061009" + integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg== + is-alphabetical@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-2.0.1.tgz#01072053ea7c1036df3c7d19a6daaec7f19e789b" @@ -3021,7 +3183,7 @@ lodash.union@^4.6.0: resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" integrity sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw== -lodash@^4.17.15: +lodash@^4.17.15, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -3031,7 +3193,7 @@ longest-streak@^3.0.0: resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4" integrity sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g== -loose-envify@^1.1.0: +loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -3754,6 +3916,11 @@ nth-check@^2.0.1: dependencies: boolbase "^1.0.0" +object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + object-inspect@^1.13.1: version "1.13.1" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" @@ -3950,6 +4117,15 @@ process@^0.11.10: resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== +prop-types@^15.6.2, prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + property-information@^6.0.0: version "6.5.0" resolved "https://registry.yarnpkg.com/property-information/-/property-information-6.5.0.tgz#6212fbb52ba757e92ef4fb9d657563b933b7ffec" @@ -4017,12 +4193,17 @@ react-dom@^18.3.1: loose-envify "^1.1.0" scheduler "^0.23.2" +react-is@^16.13.1: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + react-is@^17.0.1: version "17.0.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-is@^18.0.0: +react-is@^18.0.0, react-is@^18.3.1: version "18.3.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== @@ -4048,6 +4229,25 @@ react-refresh@^0.14.0: resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.2.tgz#3833da01ce32da470f1f936b9d477da5c7028bf9" integrity sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA== +react-smooth@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/react-smooth/-/react-smooth-4.0.1.tgz#6200d8699bfe051ae40ba187988323b1449eab1a" + integrity sha512-OE4hm7XqR0jNOq3Qmk9mFLyd6p2+j6bvbPJ7qlB7+oo0eNcL2l7WQzG6MBnT3EXY6xzkLMUBec3AfewJdA0J8w== + dependencies: + fast-equals "^5.0.1" + prop-types "^15.8.1" + react-transition-group "^4.4.5" + +react-transition-group@^4.4.5: + version "4.4.5" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" + integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g== + dependencies: + "@babel/runtime" "^7.5.5" + dom-helpers "^5.0.1" + loose-envify "^1.4.0" + prop-types "^15.6.2" + react@^18.3.1: version "18.3.1" resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" @@ -4084,6 +4284,27 @@ readdir-glob@^1.1.2: dependencies: minimatch "^5.1.0" +recharts-scale@^0.4.4: + version "0.4.5" + resolved "https://registry.yarnpkg.com/recharts-scale/-/recharts-scale-0.4.5.tgz#0969271f14e732e642fcc5bd4ab270d6e87dd1d9" + integrity sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w== + dependencies: + decimal.js-light "^2.4.1" + +recharts@^2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/recharts/-/recharts-2.13.0.tgz#a293322ea357491393cc7ad6fcbb1e5f8e99bc93" + integrity sha512-sbfxjWQ+oLWSZEWmvbq/DFVdeRLqqA6d0CDjKx2PkxVVdoXo16jvENCE+u/x7HxOO+/fwx//nYRwb8p8X6s/lQ== + dependencies: + clsx "^2.0.0" + eventemitter3 "^4.0.1" + lodash "^4.17.21" + react-is "^18.3.1" + react-smooth "^4.0.0" + recharts-scale "^0.4.4" + tiny-invariant "^1.3.1" + victory-vendor "^36.6.8" + redent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" @@ -4471,6 +4692,11 @@ timers-browserify@^2.0.4: dependencies: setimmediate "^1.0.4" +tiny-invariant@^1.3.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz#46680b7a873a0d5d10005995eb90a70d74d60127" + integrity sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg== + tmp-promise@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/tmp-promise/-/tmp-promise-3.0.3.tgz#60a1a1cc98c988674fcbfd23b6e3367bdeac4ce7" @@ -4667,6 +4893,26 @@ vfile@^6.0.0: unist-util-stringify-position "^4.0.0" vfile-message "^4.0.0" +victory-vendor@^36.6.8: + version "36.9.2" + resolved "https://registry.yarnpkg.com/victory-vendor/-/victory-vendor-36.9.2.tgz#668b02a448fa4ea0f788dbf4228b7e64669ff801" + integrity sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ== + dependencies: + "@types/d3-array" "^3.0.3" + "@types/d3-ease" "^3.0.0" + "@types/d3-interpolate" "^3.0.1" + "@types/d3-scale" "^4.0.2" + "@types/d3-shape" "^3.1.0" + "@types/d3-time" "^3.0.0" + "@types/d3-timer" "^3.0.0" + d3-array "^3.1.6" + d3-ease "^3.0.1" + d3-interpolate "^3.0.1" + d3-scale "^4.0.2" + d3-shape "^3.1.0" + d3-time "^3.0.0" + d3-timer "^3.0.1" + vite-plugin-html@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/vite-plugin-html/-/vite-plugin-html-3.2.2.tgz#661834fa09015d3fda48ba694dbaa809396f5f7a" diff --git a/src/common/io.ts b/src/common/io.ts index 87a20ba6..5a25d78c 100644 --- a/src/common/io.ts +++ b/src/common/io.ts @@ -41,6 +41,7 @@ import { } from '../types/index.js'; import { AoArNSNameData, + AoAuctionPriceData, AoEpochData, AoEpochSettings, AoGateway, @@ -616,20 +617,30 @@ export class IOReadable implements AoIORead { }); } - async getAuction({ + async getAuction({ name }: { name: string }): Promise { + const allTags = [ + { name: 'Action', value: 'Auction-Info' }, + { name: 'Name', value: name }, + ]; + + return this.process.read({ + tags: allTags, + }); + } + + async getAuctionPrices({ name, type, - timestamp, years, - // TODO: include prices, which is a separate message and requires a different tag, and is fairly expensive on compute + timestamp, }: { name: string; - timestamp?: number; - type: 'permabuy' | 'lease'; + type?: 'permabuy' | 'lease'; years?: number; - }): Promise { - const allTags = [ - { name: 'Action', value: 'Auction-Info' }, + timestamp?: number; + }): Promise { + const prunedPriceTags: { name: string; value: string }[] = [ + { name: 'Action', value: 'Auction-Prices' }, { name: 'Name', value: name }, { name: 'Timestamp', @@ -638,19 +649,17 @@ export class IOReadable implements AoIORead { { name: 'Purchase-Type', value: type ?? 'lease' }, { name: 'Years', - value: type === 'lease' ? years?.toString() ?? '1' : undefined, + value: + type == undefined || type === 'lease' + ? years?.toString() ?? '1' + : undefined, }, - ]; - - const prunedTags: { name: string; value: string }[] = allTags.filter( - (tag: { - name: string; - value: string | undefined; - }): tag is { name: string; value: string } => tag.value !== undefined, + ].filter( + (tag): tag is { name: string; value: string } => tag.value !== undefined, ); - return this.process.read({ - tags: prunedTags, + return this.process.read({ + tags: prunedPriceTags, }); } } diff --git a/src/types/io.ts b/src/types/io.ts index b253add4..f6d6b64e 100644 --- a/src/types/io.ts +++ b/src/types/io.ts @@ -262,8 +262,6 @@ export type AoAuctionSettings = { durationMs: number; decayRate: number; scalingExponent: number; - baseFee: number; - demandFactor: number; startPriceMultiplier: number; }; @@ -271,10 +269,19 @@ export type AoAuction = { name: string; startTimestamp: Timestamp; endTimestamp: Timestamp; - currentPrice: number; + initiator: string; + baseFee: number; + demandFactor: number; settings: AoAuctionSettings; }; +export type AoAuctionPriceData = { + type: 'lease' | 'permabuy'; + years?: number; + prices: Record; + currentPrice: number; +}; + // Input types // TODO: confirm what is required or if all can be optional and defaults will be provided From 942ac7d2f683f2f5fd75d201f2b249f641adf41c Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Wed, 23 Oct 2024 13:53:27 -0500 Subject: [PATCH 26/97] chore(test): update tests and examples --- examples/vite/src/App.tsx | 2 - tests/e2e/esm/index.test.js | 669 ++++++++++++++++++------------------ 2 files changed, 344 insertions(+), 327 deletions(-) diff --git a/examples/vite/src/App.tsx b/examples/vite/src/App.tsx index e013d801..d834607e 100644 --- a/examples/vite/src/App.tsx +++ b/examples/vite/src/App.tsx @@ -28,10 +28,8 @@ function App() { const [auctions, setAuctions] = useState([]); useEffect(() => { - console.log('fetching auctions', io); io.getAuctions().then((page: PaginationResult) => { page.items.forEach((auction: AoAuction) => { - console.log('auction', auction); io.getAuctionPrices({ name: auction.name, type: 'lease' }).then( (price: AoAuctionPriceData) => { const arrayOfPrices = Object.entries(price.prices) diff --git a/tests/e2e/esm/index.test.js b/tests/e2e/esm/index.test.js index 1a7f752d..183ae55f 100644 --- a/tests/e2e/esm/index.test.js +++ b/tests/e2e/esm/index.test.js @@ -1,6 +1,7 @@ import { ANT, ANTRegistry, + ANT_REGISTRY_ID, AOProcess, AoANTRegistryWriteable, AoANTWriteable, @@ -68,383 +69,401 @@ describe('e2e esm tests', async () => { assert(Array.isArray(info.Handlers)); assert(typeof info.LastTickedEpochIndex === 'number'); }); - }); - it('should be able to return a specific page of arns records', async () => { - const records = await io.getArNSRecords({ - cursor: 'ardrive', - limit: 5, - sortOrder: 'desc', - sortBy: 'name', - }); - assert.ok(records); - assert(records.limit === 5); - assert(records.sortOrder === 'desc'); - assert(records.sortBy === 'name'); - assert(typeof records.totalItems === 'number'); - assert(typeof records.sortBy === 'string'); - assert(typeof records.sortOrder === 'string'); - assert(typeof records.limit === 'number'); - assert(typeof records.hasMore === 'boolean'); - if (records.nextCursor) { - assert(typeof records.nextCursor === 'string'); - } - assert(Array.isArray(records.items)); - records.items.forEach((record) => { - assert(typeof record.processId === 'string'); - assert(typeof record.name === 'string'); - assert(typeof record.startTimestamp === 'number'); - assert(['lease', 'permabuy'].includes(record.type)); - assert(typeof record.undernameLimit === 'number'); + it('should be able to return a specific page of arns records', async () => { + const records = await io.getArNSRecords({ + cursor: 'ardrive', + limit: 5, + sortOrder: 'desc', + sortBy: 'name', + }); + assert.ok(records); + assert(records.limit === 5); + assert(records.sortOrder === 'desc'); + assert(records.sortBy === 'name'); + assert(typeof records.totalItems === 'number'); + assert(typeof records.sortBy === 'string'); + assert(typeof records.sortOrder === 'string'); + assert(typeof records.limit === 'number'); + assert(typeof records.hasMore === 'boolean'); + if (records.nextCursor) { + assert(typeof records.nextCursor === 'string'); + } + assert(Array.isArray(records.items)); + records.items.forEach((record) => { + assert(typeof record.processId === 'string'); + assert(typeof record.name === 'string'); + assert(typeof record.startTimestamp === 'number'); + assert(['lease', 'permabuy'].includes(record.type)); + assert(typeof record.undernameLimit === 'number'); + }); + }); + it('should be able to get a single arns record', async () => { + const arns = await io.getArNSRecord({ name: 'ardrive' }); + assert.ok(arns); }); - }); - it('should be able to get a single arns record', async () => { - const arns = await io.getArNSRecord({ name: 'ardrive' }); - assert.ok(arns); - }); - it('should be able to get the current epoch', async () => { - const epoch = await io.getCurrentEpoch(); - assert.ok(epoch); - }); + it('should be able to get the current epoch', async () => { + const epoch = await io.getCurrentEpoch(); + assert.ok(epoch); + }); - it('should be able to get epoch-settings', async () => { - const epochSettings = await io.getEpochSettings(); - assert.ok(epochSettings); - }); + it('should be able to get epoch-settings', async () => { + const epochSettings = await io.getEpochSettings(); + assert.ok(epochSettings); + }); - it('should be able to get reserved names', async () => { - const reservedNames = await io.getArNSReservedNames(); - assert.ok(reservedNames); - }); + it('should be able to get reserved names', async () => { + const reservedNames = await io.getArNSReservedNames(); + assert.ok(reservedNames); + }); - it('should be able to get a single reserved name', async () => { - const reservedNames = await io.getArNSReservedNames({ name: 'www ' }); - assert.ok(reservedNames); - }); + it('should be able to get a single reserved name', async () => { + const reservedNames = await io.getArNSReservedNames({ name: 'www ' }); + assert.ok(reservedNames); + }); - it('should be able to get first page of gateways', async () => { - const gateways = await io.getGateways(); - assert.ok(gateways); - assert(gateways.limit === 100); - assert(gateways.sortOrder === 'desc'); - assert(gateways.sortBy === 'startTimestamp'); - assert(typeof gateways.totalItems === 'number'); - assert(typeof gateways.sortBy === 'string'); - assert(typeof gateways.sortOrder === 'string'); - assert(typeof gateways.limit === 'number'); - assert(typeof gateways.hasMore === 'boolean'); - if (gateways.nextCursor) { - assert(typeof gateways.nextCursor === 'string'); - } - assert(Array.isArray(gateways.items)); - gateways.items.forEach((gateway) => { - assert(typeof gateway.gatewayAddress === 'string'); - assert(typeof gateway.observerAddress === 'string'); - assert(typeof gateway.startTimestamp === 'number'); - assert(typeof gateway.operatorStake === 'number'); - assert(typeof gateway.totalDelegatedStake === 'number'); - assert(typeof gateway.settings === 'object'); - assert(typeof gateway.weights === 'object'); - assert(typeof gateway.weights.normalizedCompositeWeight === 'number'); - assert(typeof gateway.weights.compositeWeight === 'number'); - assert(typeof gateway.weights.stakeWeight === 'number'); - assert(typeof gateway.weights.tenureWeight === 'number'); - assert(typeof gateway.weights.observerRewardRatioWeight === 'number'); - assert(typeof gateway.weights.gatewayRewardRatioWeight === 'number'); + it('should be able to get first page of gateways', async () => { + const gateways = await io.getGateways(); + assert.ok(gateways); + assert(gateways.limit === 100); + assert(gateways.sortOrder === 'desc'); + assert(gateways.sortBy === 'startTimestamp'); + assert(typeof gateways.totalItems === 'number'); + assert(typeof gateways.sortBy === 'string'); + assert(typeof gateways.sortOrder === 'string'); + assert(typeof gateways.limit === 'number'); + assert(typeof gateways.hasMore === 'boolean'); + if (gateways.nextCursor) { + assert(typeof gateways.nextCursor === 'string'); + } + assert(Array.isArray(gateways.items)); + gateways.items.forEach((gateway) => { + assert(typeof gateway.gatewayAddress === 'string'); + assert(typeof gateway.observerAddress === 'string'); + assert(typeof gateway.startTimestamp === 'number'); + assert(typeof gateway.operatorStake === 'number'); + assert(typeof gateway.totalDelegatedStake === 'number'); + assert(typeof gateway.settings === 'object'); + assert(typeof gateway.weights === 'object'); + assert(typeof gateway.weights.normalizedCompositeWeight === 'number'); + assert(typeof gateway.weights.compositeWeight === 'number'); + assert(typeof gateway.weights.stakeWeight === 'number'); + assert(typeof gateway.weights.tenureWeight === 'number'); + assert(typeof gateway.weights.observerRewardRatioWeight === 'number'); + assert(typeof gateway.weights.gatewayRewardRatioWeight === 'number'); + }); }); - }); - it('should be able to get a specific page of gateways', async () => { - const gateways = await io.getGateways({ - cursor: 1000000, - limit: 1, - sortBy: 'operatorStake', - sortOrder: 'desc', - }); - assert.ok(gateways); - assert(gateways.limit === 1); - assert(gateways.sortOrder === 'desc'); - assert(gateways.sortBy === 'operatorStake'); - assert(typeof gateways.totalItems === 'number'); - assert(typeof gateways.sortBy === 'string'); - assert(typeof gateways.sortOrder === 'string'); - assert(typeof gateways.limit === 'number'); - assert(typeof gateways.hasMore === 'boolean'); - if (gateways.nextCursor) { - assert(typeof gateways.nextCursor === 'string'); - } - assert(Array.isArray(gateways.items)); - gateways.items.forEach((gateway) => { - assert(typeof gateway.gatewayAddress === 'string'); - assert(typeof gateway.observerAddress === 'string'); - assert(typeof gateway.startTimestamp === 'number'); - assert(typeof gateway.operatorStake === 'number'); - assert(typeof gateway.totalDelegatedStake === 'number'); - assert(typeof gateway.settings === 'object'); - assert(typeof gateway.weights === 'object'); - assert(typeof gateway.weights.normalizedCompositeWeight === 'number'); - assert(typeof gateway.weights.compositeWeight === 'number'); - assert(typeof gateway.weights.stakeWeight === 'number'); - assert(typeof gateway.weights.tenureWeight === 'number'); - assert(typeof gateway.weights.observerRewardRatioWeight === 'number'); - assert(typeof gateway.weights.gatewayRewardRatioWeight === 'number'); + it('should be able to get a specific page of gateways', async () => { + const gateways = await io.getGateways({ + cursor: 1000000, + limit: 1, + sortBy: 'operatorStake', + sortOrder: 'desc', + }); + assert.ok(gateways); + assert(gateways.limit === 1); + assert(gateways.sortOrder === 'desc'); + assert(gateways.sortBy === 'operatorStake'); + assert(typeof gateways.totalItems === 'number'); + assert(typeof gateways.sortBy === 'string'); + assert(typeof gateways.sortOrder === 'string'); + assert(typeof gateways.limit === 'number'); + assert(typeof gateways.hasMore === 'boolean'); + if (gateways.nextCursor) { + assert(typeof gateways.nextCursor === 'string'); + } + assert(Array.isArray(gateways.items)); + gateways.items.forEach((gateway) => { + assert(typeof gateway.gatewayAddress === 'string'); + assert(typeof gateway.observerAddress === 'string'); + assert(typeof gateway.startTimestamp === 'number'); + assert(typeof gateway.operatorStake === 'number'); + assert(typeof gateway.totalDelegatedStake === 'number'); + assert(typeof gateway.settings === 'object'); + assert(typeof gateway.weights === 'object'); + assert(typeof gateway.weights.normalizedCompositeWeight === 'number'); + assert(typeof gateway.weights.compositeWeight === 'number'); + assert(typeof gateway.weights.stakeWeight === 'number'); + assert(typeof gateway.weights.tenureWeight === 'number'); + assert(typeof gateway.weights.observerRewardRatioWeight === 'number'); + assert(typeof gateway.weights.gatewayRewardRatioWeight === 'number'); + }); }); - }); - it('should be able to get a single gateway', async () => { - const gateways = await io.getGateway({ - address: 'QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ', + it('should be able to get a single gateway', async () => { + const gateways = await io.getGateway({ + address: 'QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ', + }); + assert.ok(gateways); }); - assert.ok(gateways); - }); - it('should be able to get balances, defaulting to first page', async () => { - const balances = await io.getBalances(); - assert.ok(balances); - assert(balances.limit === 100); - assert(balances.sortOrder === 'desc'); - assert(balances.sortBy === 'balance'); - assert(typeof balances.totalItems === 'number'); - assert(typeof balances.sortBy === 'string'); - assert(typeof balances.sortOrder === 'string'); - assert(typeof balances.limit === 'number'); - assert(typeof balances.hasMore === 'boolean'); - if (balances.nextCursor) { - assert(typeof gateways.nextCursor === 'string'); - } - assert(Array.isArray(balances.items)); - balances.items.forEach((wallet) => { - assert(typeof wallet.address === 'string'); - assert(typeof wallet.balance === 'number'); + it('should be able to get balances, defaulting to first page', async () => { + const balances = await io.getBalances(); + assert.ok(balances); + assert(balances.limit === 100); + assert(balances.sortOrder === 'desc'); + assert(balances.sortBy === 'balance'); + assert(typeof balances.totalItems === 'number'); + assert(typeof balances.sortBy === 'string'); + assert(typeof balances.sortOrder === 'string'); + assert(typeof balances.limit === 'number'); + assert(typeof balances.hasMore === 'boolean'); + if (balances.nextCursor) { + assert(typeof gateways.nextCursor === 'string'); + } + assert(Array.isArray(balances.items)); + balances.items.forEach((wallet) => { + assert(typeof wallet.address === 'string'); + assert(typeof wallet.balance === 'number'); + }); }); - }); - it('should be able to get balances of a specific to first page', async () => { - const balances = await io.getBalances({ - cursor: 1000000, - limit: 1, - sortBy: 'address', - sortOrder: 'asc', - }); - assert.ok(balances); - assert(balances.limit === 1); - assert(balances.sortOrder === 'asc'); - assert(balances.sortBy === 'address'); - assert(typeof balances.totalItems === 'number'); - assert(typeof balances.sortBy === 'string'); - assert(typeof balances.sortOrder === 'string'); - assert(typeof balances.limit === 'number'); - assert(typeof balances.hasMore === 'boolean'); - if (balances.nextCursor) { - assert(typeof balances.nextCursor === 'string'); - } - assert(Array.isArray(balances.items)); - balances.items.forEach((wallet) => { - assert(typeof wallet.address === 'string'); - assert(typeof wallet.balance === 'number'); + it('should be able to get balances of a specific to first page', async () => { + const balances = await io.getBalances({ + cursor: 1000000, + limit: 1, + sortBy: 'address', + sortOrder: 'asc', + }); + assert.ok(balances); + assert(balances.limit === 1); + assert(balances.sortOrder === 'asc'); + assert(balances.sortBy === 'address'); + assert(typeof balances.totalItems === 'number'); + assert(typeof balances.sortBy === 'string'); + assert(typeof balances.sortOrder === 'string'); + assert(typeof balances.limit === 'number'); + assert(typeof balances.hasMore === 'boolean'); + if (balances.nextCursor) { + assert(typeof balances.nextCursor === 'string'); + } + assert(Array.isArray(balances.items)); + balances.items.forEach((wallet) => { + assert(typeof wallet.address === 'string'); + assert(typeof wallet.balance === 'number'); + }); }); - }); - it('should be able to get a single balance', async () => { - const balances = await io.getBalance({ - address: 'QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ', + it('should be able to get a single balance', async () => { + const balances = await io.getBalance({ + address: 'QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ', + }); + assert.ok(balances); }); - assert.ok(balances); - }); - it('should be able to get prescribed names', async () => { - const prescribedNames = await io.getPrescribedNames(); - assert.ok(prescribedNames); - }); + it('should be able to get prescribed names', async () => { + const prescribedNames = await io.getPrescribedNames(); + assert.ok(prescribedNames); + }); - it('should return the prescribed observers for a given epoch', async () => { - const observers = await io.getPrescribedObservers(); - assert.ok(observers); - for (const observer of observers) { - assert(typeof observer.gatewayAddress === 'string'); - assert(typeof observer.observerAddress === 'string'); - assert(typeof observer.stake === 'number'); - assert(typeof observer.startTimestamp === 'number'); - assert(typeof observer.stakeWeight === 'number'); - assert(typeof observer.tenureWeight === 'number'); - assert(typeof observer.gatewayRewardRatioWeight === 'number'); - assert(typeof observer.observerRewardRatioWeight === 'number'); - assert(typeof observer.compositeWeight === 'number'); - } - }); + it('should return the prescribed observers for a given epoch', async () => { + const observers = await io.getPrescribedObservers(); + assert.ok(observers); + for (const observer of observers) { + assert(typeof observer.gatewayAddress === 'string'); + assert(typeof observer.observerAddress === 'string'); + assert(typeof observer.stake === 'number'); + assert(typeof observer.startTimestamp === 'number'); + assert(typeof observer.stakeWeight === 'number'); + assert(typeof observer.tenureWeight === 'number'); + assert(typeof observer.gatewayRewardRatioWeight === 'number'); + assert(typeof observer.observerRewardRatioWeight === 'number'); + assert(typeof observer.compositeWeight === 'number'); + } + }); - it('should be able to get token cost for leasing a name', async () => { - const tokenCost = await io.getTokenCost({ - intent: 'Buy-Record', - name: 'new-name', - years: 1, + it('should be able to get token cost for leasing a name', async () => { + const tokenCost = await io.getTokenCost({ + intent: 'Buy-Record', + name: 'new-name', + years: 1, + }); + assert.ok(tokenCost); }); - assert.ok(tokenCost); - }); - it('should be able to get token cost for buying a name name', async () => { - const tokenCost = await io.getTokenCost({ - intent: 'Buy-Record', - name: 'new-name', - type: 'permabuy', + it('should be able to get token cost for buying a name name', async () => { + const tokenCost = await io.getTokenCost({ + intent: 'Buy-Record', + name: 'new-name', + type: 'permabuy', + }); + assert.ok(tokenCost); }); - assert.ok(tokenCost); - }); - it('should be able to get registration fees', async () => { - const registrationFees = await io.getRegistrationFees(); - assert(registrationFees); - assert.equal(Object.keys(registrationFees).length, 51); - for (const nameLength of Object.keys(registrationFees)) { - // assert lease is length of 5 - assert(registrationFees[nameLength]['lease']['1'] > 0); - assert(registrationFees[nameLength]['lease']['2'] > 0); - assert(registrationFees[nameLength]['lease']['3'] > 0); - assert(registrationFees[nameLength]['lease']['4'] > 0); - assert(registrationFees[nameLength]['lease']['5'] > 0); - assert(registrationFees[nameLength]['permabuy'] > 0); - } - }); + it('should be able to get registration fees', async () => { + const registrationFees = await io.getRegistrationFees(); + assert(registrationFees); + assert.equal(Object.keys(registrationFees).length, 51); + for (const nameLength of Object.keys(registrationFees)) { + // assert lease is length of 5 + assert(registrationFees[nameLength]['lease']['1'] > 0); + assert(registrationFees[nameLength]['lease']['2'] > 0); + assert(registrationFees[nameLength]['lease']['3'] > 0); + assert(registrationFees[nameLength]['lease']['4'] > 0); + assert(registrationFees[nameLength]['lease']['5'] > 0); + assert(registrationFees[nameLength]['permabuy'] > 0); + } + }); - it('should be able to get current epoch distributions', async () => { - const distributions = await io.getDistributions(); - assert.ok(distributions); - }); + it('should be able to get current epoch distributions', async () => { + const distributions = await io.getDistributions(); + assert.ok(distributions); + }); - it('should be able to get epoch distributions at a specific epoch', async () => { - const distributions = await io.getDistributions({ epochIndex: 0 }); - assert.ok(distributions); - }); + it('should be able to get epoch distributions at a specific epoch', async () => { + const distributions = await io.getDistributions({ epochIndex: 0 }); + assert.ok(distributions); + }); - it('should be able to get current epoch observations', async () => { - const observations = await io.getObservations(); - assert.ok(observations); - }); + it('should be able to get current epoch observations', async () => { + const observations = await io.getObservations(); + assert.ok(observations); + }); - it('should be able to get epoch observations at a specific epoch', async () => { - const observations = await io.getObservations({ epochIndex: 0 }); - assert.ok(observations); - }); + it('should be able to get epoch observations at a specific epoch', async () => { + const observations = await io.getObservations({ epochIndex: 0 }); + assert.ok(observations); + }); - it('should be able to get current demand factor', async () => { - const demandFactor = await io.getDemandFactor(); - assert.ok(demandFactor); - }); + it('should be able to get current demand factor', async () => { + const demandFactor = await io.getDemandFactor(); + assert.ok(demandFactor); + }); - it('should be able to get current auctions', async () => { - const auctions = await io.getAuctions(); - assert.ok(auctions); - }); + it('should be able to get current auctions', async () => { + const auctions = await io.getAuctions(); + assert.ok(auctions); + }); - it('should be able to get a specific auction', async () => { - const auction = await io.getAuction({ name: 'ardrive' }); - assert.ok(auction); - }); + it('should be able to get a specific auction', async () => { + const auction = await io.getAuction({ name: 'ardrive' }); + assert.ok(auction); + }); - it('should be able to create IOWriteable with valid signers', async () => { - for (const signer of signers) { - const io = IO.init({ signer }); + it('should be able to create IOWriteable with valid signers', async () => { + for (const signer of signers) { + const io = IO.init({ signer }); - assert(io instanceof IOWriteable); - } + assert(io instanceof IOWriteable); + } + }); }); -}); - -describe('ANTRegistry', async () => { - const registry = ANTRegistry.init(); - const address = '7waR8v4STuwPnTck1zFVkQqJh5K9q9Zik4Y5-5dV7nk'; - it('should retrieve ids from registry', async () => { - const affiliatedAnts = await registry.accessControlList({ address }); - assert(Array.isArray(affiliatedAnts.Owned)); - assert(Array.isArray(affiliatedAnts.Controlled)); - }); + describe('ANTRegistry', async () => { + const registry = ANTRegistry.init({ + process: new AOProcess({ + processId: ANT_REGISTRY_ID, + ao: aoClient, + }), + }); + const address = '7waR8v4STuwPnTck1zFVkQqJh5K9q9Zik4Y5-5dV7nk'; - it('should be able to create AoANTRegistryWriteable with valid signers', async () => { - for (const signer of signers) { - const registry = ANTRegistry.init({ - signer, - }); - assert(registry instanceof AoANTRegistryWriteable); - } - }); -}); + it('should retrieve ids from registry', async () => { + const affiliatedAnts = await registry.accessControlList({ address }); + assert(Array.isArray(affiliatedAnts.Owned)); + assert(Array.isArray(affiliatedAnts.Controlled)); + }); -describe('ANT', async () => { - const processId = 'YcxE5IbqZYK72H64ELoysxiJ-0wb36deYPv55wgl8xo'; - const ant = ANT.init({ - processId, - strict: true, + it('should be able to create AoANTRegistryWriteable with valid signers', async () => { + for (const signer of signers) { + const registry = ANTRegistry.init({ + signer, + process: new AOProcess({ + processId: ANT_REGISTRY_ID, + ao: aoClient, + }), + }); + assert(registry instanceof AoANTRegistryWriteable); + } + }); }); - it('should be able to create ANTWriteable with valid signers', async () => { - for (const signer of signers) { - const nonStrictAnt = ANT.init({ - processId, - signer, - strict: false, - }); - const strictAnt = ANT.init({ + describe('ANT', async () => { + const processId = 'YcxE5IbqZYK72H64ELoysxiJ-0wb36deYPv55wgl8xo'; + const ant = ANT.init({ + process: new AOProcess({ processId, - signer, - strict: true, - }); + ao: aoClient, + }), + strict: true, + }); - assert(nonStrictAnt instanceof AoANTWriteable); - assert(strictAnt instanceof AoANTWriteable); - } - }); + it('should be able to create ANTWriteable with valid signers', async () => { + for (const signer of signers) { + const nonStrictAnt = ANT.init({ + process: new AOProcess({ + processId, + ao: aoClient, + }), + signer, + strict: false, + }); + const strictAnt = ANT.init({ + process: new AOProcess({ + processId, + ao: aoClient, + }), + signer, + strict: true, + }); + + assert(nonStrictAnt instanceof AoANTWriteable); + assert(strictAnt instanceof AoANTWriteable); + } + }); - it('should be able to get ANT info', async () => { - const info = await ant.getInfo(); - assert.ok(info); - }); + it('should be able to get ANT info', async () => { + const info = await ant.getInfo(); + assert.ok(info); + }); - it('should be able to get the ANT records', async () => { - const records = await ant.getRecords(); - assert.ok(records); - }); + it('should be able to get the ANT records', async () => { + const records = await ant.getRecords(); + assert.ok(records); + }); - it('should be able to get a @ record from the ANT', async () => { - const record = await ant.getRecord({ undername: '@' }); - assert.ok(record); - }); + it('should be able to get a @ record from the ANT', async () => { + const record = await ant.getRecord({ undername: '@' }); + assert.ok(record); + }); - it('should be able to get the ANT owner', async () => { - const owner = await ant.getOwner(); - assert.ok(owner); - }); + it('should be able to get the ANT owner', async () => { + const owner = await ant.getOwner(); + assert.ok(owner); + }); - it('should be able to get the ANT name', async () => { - const name = await ant.getName(); - assert.ok(name); - }); + it('should be able to get the ANT name', async () => { + const name = await ant.getName(); + assert.ok(name); + }); - it('should be able to get the ANT ticker', async () => { - const ticker = await ant.getTicker(); - assert.ok(ticker); - }); + it('should be able to get the ANT ticker', async () => { + const ticker = await ant.getTicker(); + assert.ok(ticker); + }); - it('should be able to get the ANT controllers', async () => { - const controllers = await ant.getControllers(); - assert.ok(controllers); - }); + it('should be able to get the ANT controllers', async () => { + const controllers = await ant.getControllers(); + assert.ok(controllers); + }); - it('should be able to get the ANT state', async () => { - const state = await ant.getState(); - assert.ok(state); - }); + it('should be able to get the ANT state', async () => { + const state = await ant.getState(); + assert.ok(state); + }); - it('should be able to get the ANT balance for an address', async () => { - const balance = await ant.getBalance({ - address: '7waR8v4STuwPnTck1zFVkQqJh5K9q9Zik4Y5-5dV7nk', + it('should be able to get the ANT balance for an address', async () => { + const balance = await ant.getBalance({ + address: '7waR8v4STuwPnTck1zFVkQqJh5K9q9Zik4Y5-5dV7nk', + }); + assert.notEqual(balance, undefined); }); - assert.notEqual(balance, undefined); - }); - it('should be able to get the ANT balances', async () => { - const balances = await ant.getBalances(); - assert.ok(balances); + it('should be able to get the ANT balances', async () => { + const balances = await ant.getBalances(); + assert.ok(balances); + }); }); }); From bc212007ef065b5eb5af8face148da446dfb73cd Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Wed, 23 Oct 2024 14:59:14 -0500 Subject: [PATCH 27/97] fix(auctions): update types and add `intervalMs` The `intervalMs` allows you to provided the space between price intervals. --- src/common/io.ts | 17 +++++++++++++++++ src/types/io.ts | 14 ++++++++------ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/common/io.ts b/src/common/io.ts index 5a25d78c..f24a4842 100644 --- a/src/common/io.ts +++ b/src/common/io.ts @@ -628,16 +628,29 @@ export class IOReadable implements AoIORead { }); } + /** + * Get auction prices for a given auction at the provided intervals + * + * @param {Object} params - The parameters for fetching auction prices + * @param {string} params.name - The name of the auction + * @param {('permabuy'|'lease')} [params.type='lease'] - The type of purchase + * @param {number} [params.years=1] - The number of years for lease (only applicable if type is 'lease') + * @param {number} [params.timestamp=Date.now()] - The timestamp to fetch prices for + * @param {number} [params.intervalMs=900000] - The interval in milliseconds between price points (default is 15 minutes) + * @returns {Promise} The auction price data + */ async getAuctionPrices({ name, type, years, timestamp, + intervalMs, }: { name: string; type?: 'permabuy' | 'lease'; years?: number; timestamp?: number; + intervalMs?: number; }): Promise { const prunedPriceTags: { name: string; value: string }[] = [ { name: 'Action', value: 'Auction-Prices' }, @@ -654,6 +667,10 @@ export class IOReadable implements AoIORead { ? years?.toString() ?? '1' : undefined, }, + { + name: 'Price-Interval-Ms', + value: intervalMs?.toString() ?? '900000', + }, ].filter( (tag): tag is { name: string; value: string } => tag.value !== undefined, ); diff --git a/src/types/io.ts b/src/types/io.ts index f6d6b64e..136f604b 100644 --- a/src/types/io.ts +++ b/src/types/io.ts @@ -357,18 +357,20 @@ export interface AoIORead { getRegistrationFees(): Promise; getDemandFactor(): Promise; getAuctions(params?: PaginationParams): Promise>; - getAuction({ + getAuction({ name }: { name: string }): Promise; + getAuctionPrices({ name, type, - timestamp, years, + timestamp, + intervalMs, }: { name: string; - type: 'permabuy' | 'lease'; - timestamp?: number; + type: 'lease' | 'permabuy'; years?: number; - // TODO: include prices, which is a separate message - }): Promise; + timestamp?: number; + intervalMs?: number; + }): Promise; } export interface AoIOWrite extends AoIORead { From 19fa6da6bc8b44a456cccebd0a5396a379ca5fbd Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Wed, 23 Oct 2024 16:06:15 -0500 Subject: [PATCH 28/97] chore(test): update tests --- examples/vite/src/App.tsx | 51 +++++++++++++++++++++++-------------- tests/e2e/esm/index.test.js | 16 ++++++++---- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/examples/vite/src/App.tsx b/examples/vite/src/App.tsx index d834607e..f78c834b 100644 --- a/examples/vite/src/App.tsx +++ b/examples/vite/src/App.tsx @@ -10,6 +10,7 @@ import { Label, Line, LineChart, + ReferenceLine, ResponsiveContainer, Tooltip, XAxis, @@ -22,6 +23,7 @@ const io = IO.init({ processId: IO_DEVNET_PROCESS_ID }); type AuctionWithPrices = AoAuction & { prices: { timestamp: string; price: number }[]; + currentPrice: number; }; function App() { @@ -30,31 +32,36 @@ function App() { useEffect(() => { io.getAuctions().then((page: PaginationResult) => { page.items.forEach((auction: AoAuction) => { - io.getAuctionPrices({ name: auction.name, type: 'lease' }).then( - (price: AoAuctionPriceData) => { - const arrayOfPrices = Object.entries(price.prices) - .sort(([timestampA], [timestampB]) => +timestampA - +timestampB) - .map(([timestamp, price]) => ({ - timestamp: new Date(+timestamp).toLocaleString('en-US', { - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - hour12: false, - }), - price: price / 10 ** 6, - })); - const auctionWithPrices = { ...auction, prices: arrayOfPrices }; - setAuctions((prev) => [...prev, auctionWithPrices]); - }, - ); + io.getAuctionPrices({ + name: auction.name, + type: 'lease', + intervalMs: 1000 * 60 * 60 * 24, // 1 day + }).then((price: AoAuctionPriceData) => { + const arrayOfPrices = Object.entries(price.prices) + .sort(([timestampA], [timestampB]) => +timestampA - +timestampB) + .map(([timestamp, price]) => ({ + timestamp: new Date(+timestamp).toLocaleString('en-US', { + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + hour12: false, + }), + price: price / 10 ** 6, + })); + const auctionWithPrices = { + ...auction, + prices: arrayOfPrices, + currentPrice: price.currentPrice / 10 ** 6, + }; + setAuctions((prev) => [...prev, auctionWithPrices]); + }); }); }); }, []); return (
- {/* add a line chart using recharts */} {auctions.length > 0 && ( )} diff --git a/tests/e2e/esm/index.test.js b/tests/e2e/esm/index.test.js index 183ae55f..c733125d 100644 --- a/tests/e2e/esm/index.test.js +++ b/tests/e2e/esm/index.test.js @@ -33,12 +33,14 @@ const signers = [ * (simply running npm run test:integration will ensure npm link is ran) */ +const aoClient = connect({ + CU_URL: 'http://localhost:6363', +}); + const io = IO.init({ process: new AOProcess({ processId: ioDevnetProcessId, - ao: connect({ - CU_URL: 'http://localhost:6363', - }), + ao: aoClient, }), }); @@ -333,12 +335,16 @@ describe('e2e esm tests', async () => { }); it('should be able to get current auctions', async () => { - const auctions = await io.getAuctions(); + const { items: auctions } = await io.getAuctions(); assert.ok(auctions); }); it('should be able to get a specific auction', async () => { - const auction = await io.getAuction({ name: 'ardrive' }); + const { items: auctions } = await io.getAuctions(); + if (auctions.length === 0) { + return; + } + const auction = await io.getAuction({ name: auctions[0].name }); assert.ok(auction); }); From 16363e88602baaa55ae767fd16b058817926c35a Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Thu, 24 Oct 2024 09:01:28 -0500 Subject: [PATCH 29/97] feat(ant): support releasing of name of ANTs Permabought names can be released by the ANT owner. They will go into auction and if a bid is received, it will be split between the owner at the time of the release and the protocol balance. Otherwise, the name will be returned and can be reregistered by anyone. --- src/common/ant.ts | 26 +++++++++++++++++++++++++- src/types/ant.ts | 5 +++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/common/ant.ts b/src/common/ant.ts index ad865272..673a5643 100644 --- a/src/common/ant.ts +++ b/src/common/ant.ts @@ -457,7 +457,7 @@ export class AoANTWriteable extends AoANTReadable implements AoANTWrite { * @returns {Promise} The result of the interaction. * @example * ```ts - * ant.setName({ name: "ships at sea" }); + * ant.setName({ name: "test" }); // results in the resolution of `test_.ar.io` * ``` */ async setName( @@ -473,4 +473,28 @@ export class AoANTWriteable extends AoANTReadable implements AoANTWrite { signer: this.signer, }); } + + /** + * @param name @type {string} The name you want to release. The name will be put up for auction on the IO contract. 50% of the winning bid will be distributed to the ANT owner at the time of release. If no bids, the name will be released and can be reregistered by anyone. + * @param ioProcessId @type {string} The processId of the IO contract. This is where the ANT will send the message to release the name. + * @returns {Promise} The result of the interaction. + * @example + * ```ts + * ant.releaseName({ name: "ardrive", ioProcessId: IO_TESTNET_PROCESS_ID }); + * ``` + */ + async releaseName( + { name, ioProcessId }: { name: string; ioProcessId: string }, + options?: WriteOptions, + ): Promise { + return this.process.send({ + tags: [ + ...(options?.tags ?? []), + { name: 'Action', value: 'Release-Name' }, + { name: 'Name', value: name }, + { name: 'IO-Process-Id', value: ioProcessId }, + ], + signer: this.signer, + }); + } } diff --git a/src/types/ant.ts b/src/types/ant.ts index 27f82534..86a001c4 100644 --- a/src/types/ant.ts +++ b/src/types/ant.ts @@ -116,6 +116,7 @@ export const AntHandlerNames = [ 'setTicker', 'initializeState', 'state', + 'releaseName', ]; export const AntHandlersSchema = z .array(z.string({ description: 'Handler Name' })) @@ -226,4 +227,8 @@ export interface AoANTWrite extends AoANTRead { { name }: { name: string }, options?: WriteOptions, ): Promise; + releaseName( + { name, ioProcessId }: { name: string; ioProcessId: string }, + options?: WriteOptions, + ): Promise; } From ef57d91513cd2e31ea8dedd7a63b8f3cb33eb2bb Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Thu, 24 Oct 2024 09:16:39 -0500 Subject: [PATCH 30/97] chore(docs): update README with auction APIs --- README.md | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/README.md b/README.md index d4b389db..d044444b 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,9 @@ This is the home of [ar.io] SDK. This SDK provides functionality for interacting - [`getGateways({ cursor, limit, sortBy, sortOrder })`](#getgateways-cursor-limit-sortby-sortorder-) - [`getArNSRecord({ name })`](#getarnsrecord-name-) - [`getArNSRecords({ cursor, limit, sortBy, sortOrder })`](#getarnsrecords-cursor-limit-sortby-sortorder-) + - [`getAuctions({ cursor, limit, sortBy, sortOrder })`](#getauctions-cursor-limit-sortby-sortorder-) + - [`getAuction({ name })`](#getauction-name-) + - [`getAuctionPrice({ name, type, years, intervalMs })`](#getauctionprice-name-type-years-intervalms-) - [`getDemandFactor()`](#getdemandfactor) - [`getObservations({ epochIndex })`](#getobservations-epochindex-) - [`getDistributions({ epochIndex })`](#getdistributions-epochindex-) @@ -69,6 +72,7 @@ This is the home of [ar.io] SDK. This SDK provides functionality for interacting - [`removeRecord({ undername })`](#removerecord-undername-) - [`setName({ name })`](#setname-name-) - [`setTicker({ ticker })`](#setticker-ticker-) + - [`releaseName({ name, ioProcessId })`](#releasename-name-ioprocessid-) - [Configuration](#configuration-1) - [Logging](#logging) - [Configuration](#configuration-2) @@ -596,6 +600,128 @@ Available `sortBy` options are any of the keys on the record object, e.g. `name` } ``` +#### `getAuctions({ cursor, limit, sortBy, sortOrder })` + +Retrieves all active auctions of the IO process, paginated and sorted by the specified criteria. The `cursor` used for pagination is the last auction name from the previous request. + +```typescript +const io = IO.init(); +const auctions = await io.getAuctions({ + limit: 100, + sortBy: 'endTimestamp', + sortOrder: 'asc', // return the auctions ending soonest first +}); +``` + +
+ Output + +```json +{ + "items": [ + { + "name": "permalink", + "endTimestamp": 1730985241349, + "startTimestamp": 1729775641349, + "baseFee": 250000000, + "demandFactor": 1.05256, + "initiator": "GaQrvEMKBpkjofgnBi_B3IgIDmY_XYelVLB6GcRGrHc", + "settings": { + "durationMs": 1209600000, + "decayRate": 0.000000000016847809193121693, + "scalingExponent": 190, + "startPriceMultiplier": 50 + } + } + ] +} +``` + +#### `getAuction({ name })` + +Retrieves the auction data for the specified auction name. + +```typescript +const io = IO.init(); +const auction = await io.getAuction({ name: 'permalink' }); +``` + +
+ Output + +```json +{ + "name": "permalink", + "endTimestamp": 1730985241349, + "startTimestamp": 1729775641349, + "baseFee": 250000000, + "demandFactor": 1.05256, + "initiator": "GaQrvEMKBpkjofgnBi_B3IgIDmY_XYelVLB6GcRGrHc", + "settings": { + "durationMs": 1209600000, + "decayRate": 0.000000000016847809193121693, + "scalingExponent": 190, + "startPriceMultiplier": 50 + } +} +``` + +
+ +#### `getAuctionPrice({ name, type, years, intervalMs })` + +Retrieves the auction price curve of the specified auction name for the specified type, duration, and interval. The `intervalMs` is the number of milliseconds between price points on the curve. The default interval is 15 minutes. + +```typescript +const io = IO.init(); +const priceCurve = await io.getAuctionPrice({ + name: 'permalink', + type: 'lease', + years: 1, + intervalMs: 3600000, // 1 hour price intervals (default is 15 minutes) +}); +``` + +
+ Output + +```json +{ + "name": "permalink", + "type": "lease", + "currentPrice": 12582015000, + "years": 1, + "prices": { + "1730412841349": 1618516789, + "1729908841349": 8210426826, + "1730722441349": 592768907, + "1730859241349": 379659914, + "1730866441349": 370850139, + "1730884441349": 349705277, + "1730150041349": 3780993370, + "1730031241349": 5541718397, + "1730603641349": 872066253, + "1730715241349": 606815377, + "1730942041349": 289775172, + "1730916841349": 314621977, + "1730484841349": 1281957300, + "1730585641349": 924535164, + "1730232841349": 2895237473, + "1730675641349": 690200977, + "1730420041349": 1581242331, + "1729786441349": 12154428186, + "1730308441349": 2268298483, + "1730564041349": 991657913, + "1730081641349": 4712427282, + "1730909641349": 322102563, + "1730945641349": 286388732, + "1730024041349": 5671483398, + "1729937641349": 7485620175 + // ... + } +} +``` +
#### `getDemandFactor()` @@ -1413,6 +1539,19 @@ const { id: txId } = await ant.setTicker( ); ``` +#### `releaseName({ name, ioProcessId })` + +Releases a name from the auction and makes it available for auction on the IO contract. The name must be permanently owned by the releasing wallet. 50% of the winning bid will be distributed to the ANT owner at the time of release. If no bids, the name will be released and can be reregistered by anyone. + +_Note: Requires `signer` to be provided on `ANT.init` to sign the transaction._ + +```typescript +const { id: txId } = await ant.releaseName({ + name: 'permalink', + ioProcessId: IO_TESTNET_PROCESS_ID, // releases the name owned by the ANT and sends it to auction on the IO contract +}); +``` + ### Configuration ANT clients can be configured to use custom AO process. Refer to [AO Connect] for more information on how to configure the AO process to use specific AO infrastructure. From 6fe30f9ff3c3a659d660442dd803f3490a595546 Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Thu, 24 Oct 2024 09:22:56 -0500 Subject: [PATCH 31/97] chore: update test and docs --- package.json | 1 + tests/e2e/esm/index.test.js | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 6ab3e813..6bc6302b 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "test:link": "yarn build && yarn link", "test:e2e": "yarn test:cjs && yarn test:esm && yarn test:web", "prepare": "husky install", + "docs:update": "markdown-toc-gen insert README.md", "example:esm": "cd examples/esm && yarn && node index.mjs", "example:cjs": "yarn test:link && cd examples/cjs && yarn && node index.cjs", "example:web": "yarn test:link && build:web && http-server --port 8080 --host -o examples/web" diff --git a/tests/e2e/esm/index.test.js b/tests/e2e/esm/index.test.js index c733125d..7fc114cb 100644 --- a/tests/e2e/esm/index.test.js +++ b/tests/e2e/esm/index.test.js @@ -393,7 +393,6 @@ describe('e2e esm tests', async () => { processId, ao: aoClient, }), - strict: true, }); it('should be able to create ANTWriteable with valid signers', async () => { @@ -404,7 +403,6 @@ describe('e2e esm tests', async () => { ao: aoClient, }), signer, - strict: false, }); const strictAnt = ANT.init({ process: new AOProcess({ From a9b8d1ecef936f03586cdbac2e99f3eedd6a15fa Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Thu, 24 Oct 2024 09:23:46 -0500 Subject: [PATCH 32/97] chore(docs): small type in README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d044444b..5e547f44 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ This is the home of [ar.io] SDK. This SDK provides functionality for interacting - [`getArNSRecords({ cursor, limit, sortBy, sortOrder })`](#getarnsrecords-cursor-limit-sortby-sortorder-) - [`getAuctions({ cursor, limit, sortBy, sortOrder })`](#getauctions-cursor-limit-sortby-sortorder-) - [`getAuction({ name })`](#getauction-name-) - - [`getAuctionPrice({ name, type, years, intervalMs })`](#getauctionprice-name-type-years-intervalms-) + - [`getAuctionPrices({ name, type, years, intervalMs })`](#getauctionprices-name-type-years-intervalms-) - [`getDemandFactor()`](#getdemandfactor) - [`getObservations({ epochIndex })`](#getobservations-epochindex-) - [`getDistributions({ epochIndex })`](#getdistributions-epochindex-) @@ -668,13 +668,13 @@ const auction = await io.getAuction({ name: 'permalink' });
-#### `getAuctionPrice({ name, type, years, intervalMs })` +#### `getAuctionPrices({ name, type, years, intervalMs })` Retrieves the auction price curve of the specified auction name for the specified type, duration, and interval. The `intervalMs` is the number of milliseconds between price points on the curve. The default interval is 15 minutes. ```typescript const io = IO.init(); -const priceCurve = await io.getAuctionPrice({ +const priceCurve = await io.getAuctionPrices({ name: 'permalink', type: 'lease', years: 1, From 512c034eeab0c6d21dff9ce5d20a612d2d18a046 Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Thu, 24 Oct 2024 09:24:53 -0500 Subject: [PATCH 33/97] chore(docs): fix readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 5e547f44..86611028 100644 --- a/README.md +++ b/README.md @@ -600,6 +600,8 @@ Available `sortBy` options are any of the keys on the record object, e.g. `name` } ``` + + #### `getAuctions({ cursor, limit, sortBy, sortOrder })` Retrieves all active auctions of the IO process, paginated and sorted by the specified criteria. The `cursor` used for pagination is the last auction name from the previous request. From 775e1bcd8a5696485dd5b8737aaf79274599cf47 Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Thu, 24 Oct 2024 09:25:30 -0500 Subject: [PATCH 34/97] chore(docs): op, anotha one --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 86611028..5a17752e 100644 --- a/README.md +++ b/README.md @@ -639,6 +639,7 @@ const auctions = await io.getAuctions({ } ``` + #### `getAuction({ name })` Retrieves the auction data for the specified auction name. From 213f7d290e9b49e101a99d2b75b1a5d1919df7e8 Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Thu, 24 Oct 2024 09:26:11 -0500 Subject: [PATCH 35/97] chore(docs): woof this is hard --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5a17752e..cee62443 100644 --- a/README.md +++ b/README.md @@ -640,6 +640,7 @@ const auctions = await io.getAuctions({ ``` + #### `getAuction({ name })` Retrieves the auction data for the specified auction name. From b0d46d47407cf6fd045944f948cfa84e6feab26c Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Thu, 24 Oct 2024 09:28:21 -0500 Subject: [PATCH 36/97] chore(docs): modify auctions output --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cee62443..fb1834ed 100644 --- a/README.md +++ b/README.md @@ -635,7 +635,11 @@ const auctions = await io.getAuctions({ "startPriceMultiplier": 50 } } - ] + ], + "hasMore": false, + "totalItems": 1, + "sortBy": "endTimestamp", + "sortOrder": "asc" } ``` From 367416b337bdebe2192785a95ce9cbe1a21b7624 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 24 Oct 2024 16:05:29 +0000 Subject: [PATCH 37/97] chore(release): 2.4.0-alpha.2 [skip ci] # [2.4.0-alpha.2](https://github.com/ar-io/ar-io-sdk/compare/v2.4.0-alpha.1...v2.4.0-alpha.2) (2024-10-24) ### Bug Fixes * **auctions:** update read APIs to fetch auctions, use vite example display active auction ([32001c2](https://github.com/ar-io/ar-io-sdk/commit/32001c2cfcaab4aa0e03aeee99888ba9e2efd6ba)) * **auctions:** update types and add `intervalMs` ([bc21200](https://github.com/ar-io/ar-io-sdk/commit/bc212007ef065b5eb5af8face148da446dfb73cd)) * **types:** update types to match contract ([cb7d2b4](https://github.com/ar-io/ar-io-sdk/commit/cb7d2b49edf0e40734052078d9b5f5723e134876)) ### Features * **ant:** support releasing of name of ANTs ([16363e8](https://github.com/ar-io/ar-io-sdk/commit/16363e88602baaa55ae767fd16b058817926c35a)) * **auctions:** add auctions api to IO classes ([974897b](https://github.com/ar-io/ar-io-sdk/commit/974897b3458906dac325089d34c4ed45d780f368)) --- CHANGELOG.md | 15 +++++++++++++++ package.json | 2 +- src/version.ts | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 893d2e72..70182864 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +# [2.4.0-alpha.2](https://github.com/ar-io/ar-io-sdk/compare/v2.4.0-alpha.1...v2.4.0-alpha.2) (2024-10-24) + + +### Bug Fixes + +* **auctions:** update read APIs to fetch auctions, use vite example display active auction ([32001c2](https://github.com/ar-io/ar-io-sdk/commit/32001c2cfcaab4aa0e03aeee99888ba9e2efd6ba)) +* **auctions:** update types and add `intervalMs` ([bc21200](https://github.com/ar-io/ar-io-sdk/commit/bc212007ef065b5eb5af8face148da446dfb73cd)) +* **types:** update types to match contract ([cb7d2b4](https://github.com/ar-io/ar-io-sdk/commit/cb7d2b49edf0e40734052078d9b5f5723e134876)) + + +### Features + +* **ant:** support releasing of name of ANTs ([16363e8](https://github.com/ar-io/ar-io-sdk/commit/16363e88602baaa55ae767fd16b058817926c35a)) +* **auctions:** add auctions api to IO classes ([974897b](https://github.com/ar-io/ar-io-sdk/commit/974897b3458906dac325089d34c4ed45d780f368)) + # [2.4.0-alpha.1](https://github.com/ar-io/ar-io-sdk/compare/v2.3.3-alpha.1...v2.4.0-alpha.1) (2024-10-23) diff --git a/package.json b/package.json index 6bc6302b..cd50ee9f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ar.io/sdk", - "version": "2.4.0-alpha.1", + "version": "2.4.0-alpha.2", "repository": { "type": "git", "url": "git+https://github.com/ar-io/ar-io-sdk.git" diff --git a/src/version.ts b/src/version.ts index 8ba82545..90ec2920 100644 --- a/src/version.ts +++ b/src/version.ts @@ -16,4 +16,4 @@ // AUTOMATICALLY GENERATED FILE - DO NOT TOUCH -export const version = '2.4.0-alpha.1'; +export const version = '2.4.0-alpha.2'; From 6780a80b06a0a3c3943839e0f89c7d7a7ccb083d Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Thu, 24 Oct 2024 12:39:02 -0500 Subject: [PATCH 38/97] fix(auctions): fix submitAuctionApi to accept type and years --- src/common/io.ts | 12 ++++++++++-- src/types/io.ts | 2 ++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/common/io.ts b/src/common/io.ts index f24a4842..f5d01e65 100644 --- a/src/common/io.ts +++ b/src/common/io.ts @@ -1096,16 +1096,24 @@ export class IOWriteable extends IOReadable implements AoIOWrite { } async submitAuctionBid( - params: { name: string; processId: string; quantity?: number }, + params: { + name: string; + processId: string; + quantity?: number; + type?: 'lease' | 'permabuy'; + years?: number; + }, options?: WriteOptions, ): Promise { const { tags = [] } = options || {}; const allTags = [ ...tags, - { name: 'Action', value: 'Submit-Auction-Bid' }, + { name: 'Action', value: 'Auction-Bid' }, { name: 'Name', value: params.name }, { name: 'Process-Id', value: params.processId }, { name: 'Quantity', value: params.quantity?.toString() ?? undefined }, + { name: 'Purchase-Type', value: params.type || 'lease' }, + { name: 'Years', value: params.years?.toString() ?? undefined }, ]; const prunedTags: { name: string; value: string }[] = allTags.filter( diff --git a/src/types/io.ts b/src/types/io.ts index 136f604b..232ca588 100644 --- a/src/types/io.ts +++ b/src/types/io.ts @@ -495,6 +495,8 @@ export interface AoIOWrite extends AoIORead { name: string; processId: string; quantity?: number; + type?: 'lease' | 'permabuy'; + years?: number; }, options?: WriteOptions, ): Promise; From 6e45701bf25280fb5582f0ffa615a451f892e0ed Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Thu, 24 Oct 2024 12:42:27 -0500 Subject: [PATCH 39/97] chore(docs): add readme for submitAuctionBid) --- README.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/README.md b/README.md index fb1834ed..91feecfa 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ This is the home of [ar.io] SDK. This SDK provides functionality for interacting - [`increaseUndernameLimit({ name, qty })`](#increaseundernamelimit-name-qty-) - [`extendLease({ name, years })`](#extendlease-name-years-) - [`cancelDelegateWithdrawal({ address, vaultId })`](#canceldelegatewithdrawal-address-vaultid-) + - [`submitAuctionBid({ name, type, years, processId })`](#submitauctionbid-name-type-years-processid-) - [Configuration](#configuration) - [Arweave Name Tokens (ANT's)](#arweave-name-tokens-ants) - [ANT APIs](#ant-apis) @@ -1255,6 +1256,32 @@ const { id: txId } = await io.cancelDelegateWithdrawal( ); ``` +#### `submitAuctionBid({ name, type, years, processId })` + +Submit a bid for the current auction. If the bid is accepted, the name will be leased for the specified duration and assigned the specified type and processId. + +_Note: Requires `signer` to be provided on `IO.init` to sign the transaction._ + +```typescript +const io = IO.init({ signer: new ArweaveSigner(jwk) }); + +const auction = await io.getAuction({ name: 'permalink' }); + +// check the current price is under some threshold +if (auction && auction.currentPrice <= new IOToken(20_000).toMIO().valueOf()) { + const { id: txId } = await io.submitAuctionBid( + { + name: 'permalink', + type: 'lease', + years: 1, + processId: 'bh9l1cy0aksiL_x9M359faGzM_yjralacHIUo8_nQXM', + }, + // optional additional tags + { tags: [{ name: 'App-Name', value: 'My-Awesome-App' }] }, + ); +} +``` + ### Configuration The IO client class exposes APIs relevant to the ar.io process. It can be configured to use any AO Process ID that adheres to the [IO Network Spec]. By default, it will use the current [IO Testnet Process]. Refer to [AO Connect] for more information on how to configure an IO process to use specific AO infrastructure. From 9102240cb711d3d673a80d4d1ee3b390676af712 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 24 Oct 2024 17:49:41 +0000 Subject: [PATCH 40/97] chore(release): 2.4.0-alpha.3 [skip ci] # [2.4.0-alpha.3](https://github.com/ar-io/ar-io-sdk/compare/v2.4.0-alpha.2...v2.4.0-alpha.3) (2024-10-24) ### Bug Fixes * **auctions:** fix submitAuctionApi to accept type and years ([6780a80](https://github.com/ar-io/ar-io-sdk/commit/6780a80b06a0a3c3943839e0f89c7d7a7ccb083d)) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- src/version.ts | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70182864..de9602a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [2.4.0-alpha.3](https://github.com/ar-io/ar-io-sdk/compare/v2.4.0-alpha.2...v2.4.0-alpha.3) (2024-10-24) + + +### Bug Fixes + +* **auctions:** fix submitAuctionApi to accept type and years ([6780a80](https://github.com/ar-io/ar-io-sdk/commit/6780a80b06a0a3c3943839e0f89c7d7a7ccb083d)) + # [2.4.0-alpha.2](https://github.com/ar-io/ar-io-sdk/compare/v2.4.0-alpha.1...v2.4.0-alpha.2) (2024-10-24) diff --git a/package.json b/package.json index cd50ee9f..43b1a8d0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ar.io/sdk", - "version": "2.4.0-alpha.2", + "version": "2.4.0-alpha.3", "repository": { "type": "git", "url": "git+https://github.com/ar-io/ar-io-sdk.git" diff --git a/src/version.ts b/src/version.ts index 90ec2920..b9d4c716 100644 --- a/src/version.ts +++ b/src/version.ts @@ -16,4 +16,4 @@ // AUTOMATICALLY GENERATED FILE - DO NOT TOUCH -export const version = '2.4.0-alpha.2'; +export const version = '2.4.0-alpha.3'; From 5cb680affca0029fd10641c5e9d0b1d76f8902c9 Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Wed, 30 Oct 2024 11:08:03 -0500 Subject: [PATCH 41/97] fix(withdrawls): update API for cancelling withdrawls to allow delegate and operator withdrawls --- src/common/io.ts | 35 +++++++++++++++++++++++++++-------- src/types/io.ts | 4 ++-- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/common/io.ts b/src/common/io.ts index f5d01e65..d718be66 100644 --- a/src/common/io.ts +++ b/src/common/io.ts @@ -1079,19 +1079,38 @@ export class IOWriteable extends IOReadable implements AoIOWrite { }); } - async cancelDelegateWithdrawal( - params: { address: string; vaultId: string }, + /** + * Cancel a withdrawal from a gateway. + * + * @param {Object} params - The parameters for cancelling a withdrawal + * @param {string} [params.address] - The address of the withdrawal (optional). If not provided, the signer's address will be used. + * @param {string} params.vaultId - The vault ID of the withdrawal. + * @param {Object} [options] - The options for the cancellation + * @returns {Promise} The result of the cancellation + */ + async cancelWithdrawal( + params: { address?: WalletAddress; vaultId: string }, options?: WriteOptions | undefined, ): Promise { const { tags = [] } = options || {}; + + const allTags = [ + ...tags, + { name: 'Action', value: 'Cancel-Withdrawal' }, + { name: 'Address', value: params.address }, + { name: 'Vault-Id', value: params.vaultId }, + ]; + + const prunedTags: { name: string; value: string }[] = allTags.filter( + (tag: { + name: string; + value: string | undefined; + }): tag is { name: string; value: string } => tag.value !== undefined, + ); + return this.process.send({ signer: this.signer, - tags: [ - ...tags, - { name: 'Action', value: 'Cancel-Delegate-Withdrawal' }, - { name: 'Address', value: params.address }, - { name: 'Vault-Id', value: params.vaultId }, - ], + tags: prunedTags, }); } diff --git a/src/types/io.ts b/src/types/io.ts index 232ca588..9743b6ae 100644 --- a/src/types/io.ts +++ b/src/types/io.ts @@ -483,9 +483,9 @@ export interface AoIOWrite extends AoIORead { }, options?: WriteOptions, ): Promise; - cancelDelegateWithdrawal( + cancelWithdrawal( params: { - address: string; + address?: WalletAddress; vaultId: string; }, options?: WriteOptions, From a04cddc61eb6b76bdbe662993143e25f0e74c1bd Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 30 Oct 2024 16:32:10 +0000 Subject: [PATCH 42/97] chore(release): 2.4.0-alpha.4 [skip ci] # [2.4.0-alpha.4](https://github.com/ar-io/ar-io-sdk/compare/v2.4.0-alpha.3...v2.4.0-alpha.4) (2024-10-30) ### Bug Fixes * **withdrawls:** update API for cancelling withdrawls to allow delegate and operator withdrawls ([5cb680a](https://github.com/ar-io/ar-io-sdk/commit/5cb680affca0029fd10641c5e9d0b1d76f8902c9)) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- src/version.ts | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de9602a6..9ed2c842 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [2.4.0-alpha.4](https://github.com/ar-io/ar-io-sdk/compare/v2.4.0-alpha.3...v2.4.0-alpha.4) (2024-10-30) + + +### Bug Fixes + +* **withdrawls:** update API for cancelling withdrawls to allow delegate and operator withdrawls ([5cb680a](https://github.com/ar-io/ar-io-sdk/commit/5cb680affca0029fd10641c5e9d0b1d76f8902c9)) + # [2.4.0-alpha.3](https://github.com/ar-io/ar-io-sdk/compare/v2.4.0-alpha.2...v2.4.0-alpha.3) (2024-10-24) diff --git a/package.json b/package.json index 43b1a8d0..d2044798 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ar.io/sdk", - "version": "2.4.0-alpha.3", + "version": "2.4.0-alpha.4", "repository": { "type": "git", "url": "git+https://github.com/ar-io/ar-io-sdk.git" diff --git a/src/version.ts b/src/version.ts index b9d4c716..29c3e49d 100644 --- a/src/version.ts +++ b/src/version.ts @@ -16,4 +16,4 @@ // AUTOMATICALLY GENERATED FILE - DO NOT TOUCH -export const version = '2.4.0-alpha.3'; +export const version = '2.4.0-alpha.4'; From 3b260a2188f909dff1b8c6c0f41228f2444c8652 Mon Sep 17 00:00:00 2001 From: Philip Mataras Date: Wed, 30 Oct 2024 00:23:52 -0400 Subject: [PATCH 43/97] feat(ant): adds set-keywords and set-description methods for ants) --- README.md | 36 +++++++++++++++++++++++++++++++++- src/common/ant.ts | 44 ++++++++++++++++++++++++++++++++++++++++++ src/types/ant.ts | 17 ++++++++++++++++ tests/unit/ant.test.ts | 10 ++++++++++ 4 files changed, 106 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 91feecfa..d8cd0dc6 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,8 @@ This is the home of [ar.io] SDK. This SDK provides functionality for interacting - [`removeRecord({ undername })`](#removerecord-undername-) - [`setName({ name })`](#setname-name-) - [`setTicker({ ticker })`](#setticker-ticker-) + - [`setDescription({ description })`](#setdescription-description-) + - [`setKeywords({ keywords })`](#setkeywords-keywords-) - [`releaseName({ name, ioProcessId })`](#releasename-name-ioprocessid-) - [Configuration](#configuration-1) - [Logging](#logging) @@ -1339,8 +1341,10 @@ const info = await ant.getInfo(); ```json { - "name": "Ardrive", + "name": "ArDrive", "ticker": "ANT-ARDRIVE", + "description": "This is the ANT for the ArDrive decentralized web app.", + "keywords": ["File-sharing", "Publishing", "dApp"], "owner": "QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ" } ``` @@ -1381,6 +1385,8 @@ const state = await ant.getState(); }, "Initialized": true, "Ticker": "ANT-AR-IO", + "Description": "A friendly description for this ANT.", + "Keywords": ["keyword1", "keyword2", "keyword3"], "Logo": "Sie_26dvgyok0PZD_-iQAFOhOd5YxDTkczOLoqTTL_A", "Denomination": 0, "Name": "AR.IO Foundation", @@ -1574,6 +1580,34 @@ const { id: txId } = await ant.setTicker( ); ``` +#### `setDescription({ description })` + +Sets the description of the ANT process. + +_Note: Requires `signer` to be provided on `ANT.init` to sign the transaction._ + +```typescript +const { id: txId } = await ant.setDescription( + { description: 'A friendly description of this ANT' }, + // optional tags + { tags: [{ name: 'App-Name', value: 'My-Awesome-App' }] }, +); +``` + +#### `setKeywords({ keywords })` + +Sets the keywords of the ANT process. + +_Note: Requires `signer` to be provided on `ANT.init` to sign the transaction._ + +```typescript +const { id: txId } = await ant.setDescription( + { keywords: ['Game', 'FPS', 'AO'] }, + // optional tags + { tags: [{ name: 'App-Name', value: 'My-Awesome-App' }] }, +); +``` + #### `releaseName({ name, ioProcessId })` Releases a name from the auction and makes it available for auction on the IO contract. The name must be permanently owned by the releasing wallet. 50% of the winning bid will be distributed to the ANT owner at the time of release. If no bids, the name will be released and can be reregistered by anyone. diff --git a/src/common/ant.ts b/src/common/ant.ts index 673a5643..7ba29da0 100644 --- a/src/common/ant.ts +++ b/src/common/ant.ts @@ -474,6 +474,50 @@ export class AoANTWriteable extends AoANTReadable implements AoANTWrite { }); } + /** + * @param description @type {string} Sets the ANT Description. + * @returns {Promise} The result of the interaction. + * @example + * ```ts + * ant.setDescription({ description: "This name is used for the ArDrive" }); + * ``` + */ + async setDescription( + { description }: { description: string }, + options?: WriteOptions, + ): Promise { + return this.process.send({ + tags: [ + ...(options?.tags ?? []), + { name: 'Action', value: 'Set-Description' }, + { name: 'Description', value: description }, + ], + signer: this.signer, + }); + } + + /** + * @param keywords @type {string[]} Sets the ANT Keywords. + * @returns {Promise} The result of the interaction. + * @example + * ```ts + * ant.setKeywords({ keywords: ['keyword1', 'keyword2', 'keyword3']}); + * ``` + */ + async setKeywords( + { keywords }: { keywords: string[] }, + options?: WriteOptions, + ): Promise { + return this.process.send({ + tags: [ + ...(options?.tags ?? []), + { name: 'Action', value: 'Set-Keywords' }, + { name: 'Description', value: JSON.stringify(keywords) }, + ], + signer: this.signer, + }); + } + /** * @param name @type {string} The name you want to release. The name will be put up for auction on the IO contract. 50% of the winning bid will be distributed to the ANT owner at the time of release. If no bids, the name will be released and can be reregistered by anyone. * @param ioProcessId @type {string} The processId of the IO contract. This is where the ANT will send the message to release the name. diff --git a/src/types/ant.ts b/src/types/ant.ts index 86a001c4..6cef0510 100644 --- a/src/types/ant.ts +++ b/src/types/ant.ts @@ -50,6 +50,8 @@ export const IntegerStringSchema = z { message: 'Must be a non negative integer string' }, ); +export const AntDescriptionSchema = z.string(); // TODO: add specific limits for description ie max length +export const AntKeywordsSchema = z.array(z.string()); // TODO: add specific limits for keywords ie max amount and max length export const AntRecordSchema = z.object({ transactionId: ArweaveTxIdSchema.describe('The Target ID of the undername'), ttlSeconds: z.number(), @@ -68,6 +70,8 @@ export const AntBalancesSchema = z.record( export const AntStateSchema = z.object({ Name: z.string().describe('The name of the ANT.'), Ticker: z.string().describe('The ticker symbol for the ANT.'), + Description: z.string().describe('The description for the ANT.'), + Keywords: AntKeywordsSchema.describe('The keywords for the ANT.'), Denomination: z .number() .describe( @@ -114,6 +118,8 @@ export const AntHandlerNames = [ 'records', 'setName', 'setTicker', + 'setDescription', + 'setKeywords', 'initializeState', 'state', 'releaseName', @@ -139,6 +145,9 @@ export const AntInfoSchema = z.object({ ['Total-Supply']: IntegerStringSchema.describe( 'Total supply of the ANT in circulation.', ), + Description: AntDescriptionSchema.describe('The description for the ANT.'), + Keywords: AntKeywordsSchema.describe('The keywords for the ANT.'), + Logo: ArweaveTxIdSchema.describe('Transaction ID of the ANT logo.'), Denomination: IntegerStringSchema.describe( 'The number of decimal places to use for the ANT. Defaults to 0 if not set representing whole numbers.', @@ -223,6 +232,14 @@ export interface AoANTWrite extends AoANTRead { { ticker }: { ticker: string }, options?: WriteOptions, ): Promise; + setDescription( + { description }: { description: string }, + options?: WriteOptions, + ): Promise; + setKeywords( + { keywords }: { keywords: string[] }, + options?: WriteOptions, + ): Promise; setName( { name }: { name: string }, options?: WriteOptions, diff --git a/tests/unit/ant.test.ts b/tests/unit/ant.test.ts index 252d278d..da4910cc 100644 --- a/tests/unit/ant.test.ts +++ b/tests/unit/ant.test.ts @@ -16,6 +16,8 @@ describe('ANT Schemas', () => { const validState = { Name: 'TestToken', Ticker: 'TST', + Description: 'Test description', + Keywords: ['keyword1', 'keyword2', 'keyword3'], Denomination: 0, Owner: stub_address, Controllers: [stub_address], @@ -36,6 +38,8 @@ describe('ANT Schemas', () => { const invalidState = { Name: 'TestToken', Ticker: 'TST', + Description: 'Test description', + Keywords: ['keyword1', 'keyword2', 'keyword3'], Denomination: 0, Owner: stub_address, Controllers: [stub_address], @@ -64,6 +68,8 @@ describe('ANT Schemas', () => { Owner: stub_address, ['Source-Code-TX-ID']: stub_address, Ticker: 'TST', + Description: 'Test description', + Keywords: ['keyword1', 'keyword2', 'keyword3'], ['Total-Supply']: '1', Logo: stub_address, Denomination: '0', @@ -88,6 +94,8 @@ describe('ANT Schemas', () => { const validState = { Name: 'TestToken', Ticker: 'TST', + Description: 'Test description', + Keywords: ['keyword1', 'keyword2', 'keyword3'], Denomination: 0, Owner: stub_address, Controllers: [stub_address], @@ -108,6 +116,8 @@ describe('ANT Schemas', () => { const invalidState = { Name: 'TestToken', Ticker: 'TST', + Description: 'Test description', + Keywords: ['keyword1', 'keyword2', 'keyword3'], Denomination: 0, Owner: stub_address, Controllers: [stub_address], From 0c52ae539ee6df0905ddb30bc498540f95b08356 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 31 Oct 2024 19:04:59 +0000 Subject: [PATCH 44/97] chore(release): 2.4.0-alpha.5 [skip ci] # [2.4.0-alpha.5](https://github.com/ar-io/ar-io-sdk/compare/v2.4.0-alpha.4...v2.4.0-alpha.5) (2024-10-31) ### Features * **ant:** adds set-keywords and set-description methods for ants) ([3b260a2](https://github.com/ar-io/ar-io-sdk/commit/3b260a2188f909dff1b8c6c0f41228f2444c8652)) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- src/version.ts | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ed2c842..d7d35833 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [2.4.0-alpha.5](https://github.com/ar-io/ar-io-sdk/compare/v2.4.0-alpha.4...v2.4.0-alpha.5) (2024-10-31) + + +### Features + +* **ant:** adds set-keywords and set-description methods for ants) ([3b260a2](https://github.com/ar-io/ar-io-sdk/commit/3b260a2188f909dff1b8c6c0f41228f2444c8652)) + # [2.4.0-alpha.4](https://github.com/ar-io/ar-io-sdk/compare/v2.4.0-alpha.3...v2.4.0-alpha.4) (2024-10-30) diff --git a/package.json b/package.json index d2044798..b4936461 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ar.io/sdk", - "version": "2.4.0-alpha.4", + "version": "2.4.0-alpha.5", "repository": { "type": "git", "url": "git+https://github.com/ar-io/ar-io-sdk.git" diff --git a/src/version.ts b/src/version.ts index 29c3e49d..b9cad0b9 100644 --- a/src/version.ts +++ b/src/version.ts @@ -16,4 +16,4 @@ // AUTOMATICALLY GENERATED FILE - DO NOT TOUCH -export const version = '2.4.0-alpha.4'; +export const version = '2.4.0-alpha.5'; From 9c1726dc6f6f5f87c3b8b3d027593a3c749932ee Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Wed, 30 Oct 2024 13:45:52 -0500 Subject: [PATCH 45/97] feat(arns): add upgradeRecord API We may change this to `upgradeName` or something else --- src/common/io.ts | 34 ++++++++++++++++++++++++++++++++++ src/types/io.ts | 6 ++++++ 2 files changed, 40 insertions(+) diff --git a/src/common/io.ts b/src/common/io.ts index d718be66..38f7a4c6 100644 --- a/src/common/io.ts +++ b/src/common/io.ts @@ -1041,6 +1041,40 @@ export class IOWriteable extends IOReadable implements AoIOWrite { }); } + /** + * Upgrades an existing leased record to a permabuy. + * + * @param {Object} params - The parameters for upgrading a record + * @param {string} params.name - The name of the record to upgrade + * @param {Object} [options] - The options for the upgrade + * @returns {Promise} The result of the upgrade + */ + async upgradeRecord( + params: { + name: string; + }, + options?: WriteOptions, + ): Promise { + const { tags = [] } = options || {}; + return this.process.send({ + signer: this.signer, + tags: [ + ...tags, + { name: 'Action', value: 'Upgrade-Name' }, // TODO: align on Update-Record vs. Upgrade-Name (contract currently uses Upgrade-Name) + { name: 'Name', value: params.name }, + ], + }); + } + + /** + * Extends the lease of an existing leased record. + * + * @param {Object} params - The parameters for extending a lease + * @param {string} params.name - The name of the record to extend + * @param {number} params.years - The number of years to extend the lease + * @param {Object} [options] - The options for the extension + * @returns {Promise} The result of the extension + */ async extendLease( params: { name: string; diff --git a/src/types/io.ts b/src/types/io.ts index 9743b6ae..fc8e3108 100644 --- a/src/types/io.ts +++ b/src/types/io.ts @@ -469,6 +469,12 @@ export interface AoIOWrite extends AoIORead { }, options?: WriteOptions, ): Promise; + upgradeRecord( + params: { + name: string; + }, + options?: WriteOptions, + ): Promise; extendLease( params: { name: string; From a6212c972bcce4fd697c60695cc2eb86d42ff8e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 17:32:47 +0000 Subject: [PATCH 46/97] chore(deps): bump the npm_and_yarn group across 8 directories with 1 update Bumps the npm_and_yarn group with 1 update in the / directory: [secp256k1](https://github.com/cryptocoinjs/secp256k1-node). Bumps the npm_and_yarn group with 1 update in the /examples/cjs directory: [secp256k1](https://github.com/cryptocoinjs/secp256k1-node). Bumps the npm_and_yarn group with 1 update in the /examples/esm directory: [secp256k1](https://github.com/cryptocoinjs/secp256k1-node). Bumps the npm_and_yarn group with 1 update in the /examples/vite directory: [secp256k1](https://github.com/cryptocoinjs/secp256k1-node). Bumps the npm_and_yarn group with 1 update in the /examples/webpack directory: [secp256k1](https://github.com/cryptocoinjs/secp256k1-node). Bumps the npm_and_yarn group with 1 update in the /tests/e2e/cjs directory: [secp256k1](https://github.com/cryptocoinjs/secp256k1-node). Bumps the npm_and_yarn group with 1 update in the /tests/e2e/esm directory: [secp256k1](https://github.com/cryptocoinjs/secp256k1-node). Bumps the npm_and_yarn group with 1 update in the /tests/e2e/web directory: [secp256k1](https://github.com/cryptocoinjs/secp256k1-node). Updates `secp256k1` from 5.0.0 to 5.0.1 - [Release notes](https://github.com/cryptocoinjs/secp256k1-node/releases) - [Commits](https://github.com/cryptocoinjs/secp256k1-node/compare/v5.0.0...v5.0.1) Updates `secp256k1` from 5.0.0 to 5.0.1 - [Release notes](https://github.com/cryptocoinjs/secp256k1-node/releases) - [Commits](https://github.com/cryptocoinjs/secp256k1-node/compare/v5.0.0...v5.0.1) Updates `secp256k1` from 5.0.0 to 5.0.1 - [Release notes](https://github.com/cryptocoinjs/secp256k1-node/releases) - [Commits](https://github.com/cryptocoinjs/secp256k1-node/compare/v5.0.0...v5.0.1) Updates `secp256k1` from 5.0.0 to 5.0.1 - [Release notes](https://github.com/cryptocoinjs/secp256k1-node/releases) - [Commits](https://github.com/cryptocoinjs/secp256k1-node/compare/v5.0.0...v5.0.1) Updates `secp256k1` from 5.0.0 to 5.0.1 - [Release notes](https://github.com/cryptocoinjs/secp256k1-node/releases) - [Commits](https://github.com/cryptocoinjs/secp256k1-node/compare/v5.0.0...v5.0.1) Updates `secp256k1` from 5.0.0 to 5.0.1 - [Release notes](https://github.com/cryptocoinjs/secp256k1-node/releases) - [Commits](https://github.com/cryptocoinjs/secp256k1-node/compare/v5.0.0...v5.0.1) Updates `secp256k1` from 5.0.0 to 5.0.1 - [Release notes](https://github.com/cryptocoinjs/secp256k1-node/releases) - [Commits](https://github.com/cryptocoinjs/secp256k1-node/compare/v5.0.0...v5.0.1) Updates `secp256k1` from 5.0.0 to 5.0.1 - [Release notes](https://github.com/cryptocoinjs/secp256k1-node/releases) - [Commits](https://github.com/cryptocoinjs/secp256k1-node/compare/v5.0.0...v5.0.1) --- updated-dependencies: - dependency-name: secp256k1 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: secp256k1 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: secp256k1 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: secp256k1 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: secp256k1 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: secp256k1 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: secp256k1 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: secp256k1 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] --- examples/cjs/yarn.lock | 16 ++++++++-------- examples/esm/yarn.lock | 16 ++++++++-------- examples/vite/yarn.lock | 23 ++++++++++++++++++----- examples/webpack/yarn.lock | 23 ++++++++++++++++++----- tests/e2e/cjs/yarn.lock | 16 ++++++++-------- tests/e2e/esm/yarn.lock | 16 ++++++++-------- tests/e2e/web/yarn.lock | 23 ++++++++++++++++++----- yarn.lock | 23 ++++++++++++++++++----- 8 files changed, 104 insertions(+), 52 deletions(-) diff --git a/examples/cjs/yarn.lock b/examples/cjs/yarn.lock index ca268865..3890cbda 100644 --- a/examples/cjs/yarn.lock +++ b/examples/cjs/yarn.lock @@ -796,10 +796,10 @@ elliptic@6.5.4: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" -elliptic@^6.5.4: - version "6.5.5" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.5.tgz#c715e09f78b6923977610d4c2346d6ce22e6dded" - integrity sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw== +elliptic@^6.5.7: + version "6.5.7" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.7.tgz#8ec4da2cb2939926a1b9a73619d768207e647c8b" + integrity sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q== dependencies: bn.js "^4.11.9" brorand "^1.1.0" @@ -1317,11 +1317,11 @@ scrypt-js@3.0.1: integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== secp256k1@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-5.0.0.tgz#be6f0c8c7722e2481e9773336d351de8cddd12f7" - integrity sha512-TKWX8xvoGHrxVdqbYeZM9w+izTF4b9z3NhSaDkdn81btvuh+ivbIMGT/zQvDtTFWhRlThpoz6LEYTr7n8A5GcA== + version "5.0.1" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-5.0.1.tgz#dc2c86187d48ff2da756f0f7e96417ee03c414b1" + integrity sha512-lDFs9AAIaWP9UCdtWrotXWWF9t8PWgQDcxqgAnpM9rMqxb3Oaq2J0thzPVSxBwdJgyQtkU/sYtFtbM1RSt/iYA== dependencies: - elliptic "^6.5.4" + elliptic "^6.5.7" node-addon-api "^5.0.0" node-gyp-build "^4.2.0" diff --git a/examples/esm/yarn.lock b/examples/esm/yarn.lock index c4c85e13..32b96509 100644 --- a/examples/esm/yarn.lock +++ b/examples/esm/yarn.lock @@ -774,10 +774,10 @@ elliptic@6.5.4: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" -elliptic@^6.5.4: - version "6.5.5" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.5.tgz#c715e09f78b6923977610d4c2346d6ce22e6dded" - integrity sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw== +elliptic@^6.5.7: + version "6.5.7" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.7.tgz#8ec4da2cb2939926a1b9a73619d768207e647c8b" + integrity sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q== dependencies: bn.js "^4.11.9" brorand "^1.1.0" @@ -1246,11 +1246,11 @@ scrypt-js@3.0.1: integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== secp256k1@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-5.0.0.tgz#be6f0c8c7722e2481e9773336d351de8cddd12f7" - integrity sha512-TKWX8xvoGHrxVdqbYeZM9w+izTF4b9z3NhSaDkdn81btvuh+ivbIMGT/zQvDtTFWhRlThpoz6LEYTr7n8A5GcA== + version "5.0.1" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-5.0.1.tgz#dc2c86187d48ff2da756f0f7e96417ee03c414b1" + integrity sha512-lDFs9AAIaWP9UCdtWrotXWWF9t8PWgQDcxqgAnpM9rMqxb3Oaq2J0thzPVSxBwdJgyQtkU/sYtFtbM1RSt/iYA== dependencies: - elliptic "^6.5.4" + elliptic "^6.5.7" node-addon-api "^5.0.0" node-gyp-build "^4.2.0" diff --git a/examples/vite/yarn.lock b/examples/vite/yarn.lock index 9f213319..2978e213 100644 --- a/examples/vite/yarn.lock +++ b/examples/vite/yarn.lock @@ -2270,7 +2270,7 @@ elliptic@6.5.4: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" -elliptic@^6.5.3, elliptic@^6.5.4, elliptic@^6.5.5: +elliptic@^6.5.3, elliptic@^6.5.5: version "6.5.5" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.5.tgz#c715e09f78b6923977610d4c2346d6ce22e6dded" integrity sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw== @@ -2283,6 +2283,19 @@ elliptic@^6.5.3, elliptic@^6.5.4, elliptic@^6.5.5: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" +elliptic@^6.5.7: + version "6.5.7" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.7.tgz#8ec4da2cb2939926a1b9a73619d768207e647c8b" + integrity sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" @@ -4486,11 +4499,11 @@ scrypt-js@3.0.1: integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== secp256k1@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-5.0.0.tgz#be6f0c8c7722e2481e9773336d351de8cddd12f7" - integrity sha512-TKWX8xvoGHrxVdqbYeZM9w+izTF4b9z3NhSaDkdn81btvuh+ivbIMGT/zQvDtTFWhRlThpoz6LEYTr7n8A5GcA== + version "5.0.1" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-5.0.1.tgz#dc2c86187d48ff2da756f0f7e96417ee03c414b1" + integrity sha512-lDFs9AAIaWP9UCdtWrotXWWF9t8PWgQDcxqgAnpM9rMqxb3Oaq2J0thzPVSxBwdJgyQtkU/sYtFtbM1RSt/iYA== dependencies: - elliptic "^6.5.4" + elliptic "^6.5.7" node-addon-api "^5.0.0" node-gyp-build "^4.2.0" diff --git a/examples/webpack/yarn.lock b/examples/webpack/yarn.lock index 4c56f6ac..e247bb2a 100644 --- a/examples/webpack/yarn.lock +++ b/examples/webpack/yarn.lock @@ -3374,7 +3374,7 @@ elliptic@6.5.4: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" -elliptic@^6.5.3, elliptic@^6.5.4, elliptic@^6.5.5: +elliptic@^6.5.3, elliptic@^6.5.5: version "6.5.5" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.5.tgz#c715e09f78b6923977610d4c2346d6ce22e6dded" integrity sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw== @@ -3387,6 +3387,19 @@ elliptic@^6.5.3, elliptic@^6.5.4, elliptic@^6.5.5: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" +elliptic@^6.5.7: + version "6.5.7" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.7.tgz#8ec4da2cb2939926a1b9a73619d768207e647c8b" + integrity sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -6538,11 +6551,11 @@ scrypt-js@3.0.1: integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== secp256k1@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-5.0.0.tgz#be6f0c8c7722e2481e9773336d351de8cddd12f7" - integrity sha512-TKWX8xvoGHrxVdqbYeZM9w+izTF4b9z3NhSaDkdn81btvuh+ivbIMGT/zQvDtTFWhRlThpoz6LEYTr7n8A5GcA== + version "5.0.1" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-5.0.1.tgz#dc2c86187d48ff2da756f0f7e96417ee03c414b1" + integrity sha512-lDFs9AAIaWP9UCdtWrotXWWF9t8PWgQDcxqgAnpM9rMqxb3Oaq2J0thzPVSxBwdJgyQtkU/sYtFtbM1RSt/iYA== dependencies: - elliptic "^6.5.4" + elliptic "^6.5.7" node-addon-api "^5.0.0" node-gyp-build "^4.2.0" diff --git a/tests/e2e/cjs/yarn.lock b/tests/e2e/cjs/yarn.lock index 46d05d90..1d96fcbf 100644 --- a/tests/e2e/cjs/yarn.lock +++ b/tests/e2e/cjs/yarn.lock @@ -785,10 +785,10 @@ elliptic@6.5.4: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" -elliptic@^6.5.4: - version "6.5.5" - resolved "https://registry.npmjs.org/elliptic/-/elliptic-6.5.5.tgz" - integrity sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw== +elliptic@^6.5.7: + version "6.5.7" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.7.tgz#8ec4da2cb2939926a1b9a73619d768207e647c8b" + integrity sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q== dependencies: bn.js "^4.11.9" brorand "^1.1.0" @@ -1289,11 +1289,11 @@ scrypt-js@3.0.1: integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== secp256k1@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/secp256k1/-/secp256k1-5.0.0.tgz" - integrity sha512-TKWX8xvoGHrxVdqbYeZM9w+izTF4b9z3NhSaDkdn81btvuh+ivbIMGT/zQvDtTFWhRlThpoz6LEYTr7n8A5GcA== + version "5.0.1" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-5.0.1.tgz#dc2c86187d48ff2da756f0f7e96417ee03c414b1" + integrity sha512-lDFs9AAIaWP9UCdtWrotXWWF9t8PWgQDcxqgAnpM9rMqxb3Oaq2J0thzPVSxBwdJgyQtkU/sYtFtbM1RSt/iYA== dependencies: - elliptic "^6.5.4" + elliptic "^6.5.7" node-addon-api "^5.0.0" node-gyp-build "^4.2.0" diff --git a/tests/e2e/esm/yarn.lock b/tests/e2e/esm/yarn.lock index 9a75a12c..f8477428 100644 --- a/tests/e2e/esm/yarn.lock +++ b/tests/e2e/esm/yarn.lock @@ -785,10 +785,10 @@ elliptic@6.5.4: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" -elliptic@^6.5.4: - version "6.5.5" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.5.tgz#c715e09f78b6923977610d4c2346d6ce22e6dded" - integrity sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw== +elliptic@^6.5.7: + version "6.5.7" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.7.tgz#8ec4da2cb2939926a1b9a73619d768207e647c8b" + integrity sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q== dependencies: bn.js "^4.11.9" brorand "^1.1.0" @@ -1289,11 +1289,11 @@ scrypt-js@3.0.1: integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== secp256k1@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-5.0.0.tgz#be6f0c8c7722e2481e9773336d351de8cddd12f7" - integrity sha512-TKWX8xvoGHrxVdqbYeZM9w+izTF4b9z3NhSaDkdn81btvuh+ivbIMGT/zQvDtTFWhRlThpoz6LEYTr7n8A5GcA== + version "5.0.1" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-5.0.1.tgz#dc2c86187d48ff2da756f0f7e96417ee03c414b1" + integrity sha512-lDFs9AAIaWP9UCdtWrotXWWF9t8PWgQDcxqgAnpM9rMqxb3Oaq2J0thzPVSxBwdJgyQtkU/sYtFtbM1RSt/iYA== dependencies: - elliptic "^6.5.4" + elliptic "^6.5.7" node-addon-api "^5.0.0" node-gyp-build "^4.2.0" diff --git a/tests/e2e/web/yarn.lock b/tests/e2e/web/yarn.lock index e3f08cb1..55246d4c 100644 --- a/tests/e2e/web/yarn.lock +++ b/tests/e2e/web/yarn.lock @@ -2327,7 +2327,7 @@ elliptic@6.5.4: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" -elliptic@^6.5.3, elliptic@^6.5.4, elliptic@^6.5.5: +elliptic@^6.5.3, elliptic@^6.5.5: version "6.5.5" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.5.tgz#c715e09f78b6923977610d4c2346d6ce22e6dded" integrity sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw== @@ -2340,6 +2340,19 @@ elliptic@^6.5.3, elliptic@^6.5.4, elliptic@^6.5.5: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" +elliptic@^6.5.7: + version "6.5.7" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.7.tgz#8ec4da2cb2939926a1b9a73619d768207e647c8b" + integrity sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -4632,11 +4645,11 @@ scrypt-js@3.0.1: integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== secp256k1@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-5.0.0.tgz#be6f0c8c7722e2481e9773336d351de8cddd12f7" - integrity sha512-TKWX8xvoGHrxVdqbYeZM9w+izTF4b9z3NhSaDkdn81btvuh+ivbIMGT/zQvDtTFWhRlThpoz6LEYTr7n8A5GcA== + version "5.0.1" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-5.0.1.tgz#dc2c86187d48ff2da756f0f7e96417ee03c414b1" + integrity sha512-lDFs9AAIaWP9UCdtWrotXWWF9t8PWgQDcxqgAnpM9rMqxb3Oaq2J0thzPVSxBwdJgyQtkU/sYtFtbM1RSt/iYA== dependencies: - elliptic "^6.5.4" + elliptic "^6.5.7" node-addon-api "^5.0.0" node-gyp-build "^4.2.0" diff --git a/yarn.lock b/yarn.lock index 3e2df580..9fc756c9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3474,7 +3474,7 @@ elliptic@6.5.4: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" -elliptic@^6.5.3, elliptic@^6.5.4, elliptic@^6.5.5: +elliptic@^6.5.3, elliptic@^6.5.5: version "6.5.5" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.5.tgz#c715e09f78b6923977610d4c2346d6ce22e6dded" integrity sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw== @@ -3487,6 +3487,19 @@ elliptic@^6.5.3, elliptic@^6.5.4, elliptic@^6.5.5: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" +elliptic@^6.5.7: + version "6.5.7" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.7.tgz#8ec4da2cb2939926a1b9a73619d768207e647c8b" + integrity sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + emoji-regex@^10.3.0: version "10.3.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.3.0.tgz#76998b9268409eb3dae3de989254d456e70cfe23" @@ -7753,11 +7766,11 @@ scrypt-js@3.0.1: integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== secp256k1@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-5.0.0.tgz#be6f0c8c7722e2481e9773336d351de8cddd12f7" - integrity sha512-TKWX8xvoGHrxVdqbYeZM9w+izTF4b9z3NhSaDkdn81btvuh+ivbIMGT/zQvDtTFWhRlThpoz6LEYTr7n8A5GcA== + version "5.0.1" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-5.0.1.tgz#dc2c86187d48ff2da756f0f7e96417ee03c414b1" + integrity sha512-lDFs9AAIaWP9UCdtWrotXWWF9t8PWgQDcxqgAnpM9rMqxb3Oaq2J0thzPVSxBwdJgyQtkU/sYtFtbM1RSt/iYA== dependencies: - elliptic "^6.5.4" + elliptic "^6.5.7" node-addon-api "^5.0.0" node-gyp-build "^4.2.0" From 99941976bcdcb3950c228ae97c8a78cb5bf593e9 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 4 Nov 2024 14:09:39 +0000 Subject: [PATCH 47/97] chore(release): 2.4.0-alpha.6 [skip ci] # [2.4.0-alpha.6](https://github.com/ar-io/ar-io-sdk/compare/v2.4.0-alpha.5...v2.4.0-alpha.6) (2024-11-04) ### Features * **arns:** add upgradeRecord API ([9c1726d](https://github.com/ar-io/ar-io-sdk/commit/9c1726dc6f6f5f87c3b8b3d027593a3c749932ee)) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- src/version.ts | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7d35833..62ef16de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [2.4.0-alpha.6](https://github.com/ar-io/ar-io-sdk/compare/v2.4.0-alpha.5...v2.4.0-alpha.6) (2024-11-04) + + +### Features + +* **arns:** add upgradeRecord API ([9c1726d](https://github.com/ar-io/ar-io-sdk/commit/9c1726dc6f6f5f87c3b8b3d027593a3c749932ee)) + # [2.4.0-alpha.5](https://github.com/ar-io/ar-io-sdk/compare/v2.4.0-alpha.4...v2.4.0-alpha.5) (2024-10-31) diff --git a/package.json b/package.json index b4936461..ae549ac2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ar.io/sdk", - "version": "2.4.0-alpha.5", + "version": "2.4.0-alpha.6", "repository": { "type": "git", "url": "git+https://github.com/ar-io/ar-io-sdk.git" diff --git a/src/version.ts b/src/version.ts index b9cad0b9..a48c40ae 100644 --- a/src/version.ts +++ b/src/version.ts @@ -16,4 +16,4 @@ // AUTOMATICALLY GENERATED FILE - DO NOT TOUCH -export const version = '2.4.0-alpha.5'; +export const version = '2.4.0-alpha.6'; From ea9f3eb90843f1181b83d9f876b75a059efb8811 Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Sat, 2 Nov 2024 07:57:00 -0600 Subject: [PATCH 48/97] fix(io): consolidate `instantGatewayWithdrawal` and `instantGatewayWithdrawal` to just `instantWithdrawal`, update `cancelWithdrawal --- src/common/io.ts | 38 ++++++++++++++++++++++++++++---------- src/types/io.ts | 6 +++--- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/common/io.ts b/src/common/io.ts index 38f7a4c6..70e8f2a9 100644 --- a/src/common/io.ts +++ b/src/common/io.ts @@ -931,22 +931,40 @@ export class IOWriteable extends IOReadable implements AoIOWrite { }); } - async instantDelegateWithdrawal( + /** + * Initiates an instant withdrawal from a gateway. + * + * @param {Object} params - The parameters for initiating an instant withdrawal + * @param {string} params.address - The gateway address of the withdrawal, if not provided, the signer's address will be used + * @param {string} params.vaultId - The vault ID of the withdrawal + * @returns {Promise} The result of the withdrawal + */ + async instantWithdrawal( params: { - target: string; + gatewayAddress?: string; vaultId: string; }, options?: WriteOptions, ): Promise { const { tags = [] } = options || {}; + + const allTags = [ + ...tags, + { name: 'Action', value: 'Instant-Withdrawal' }, + { name: 'Vault-Id', value: params.vaultId }, + { name: 'Address', value: params.gatewayAddress }, + ]; + + const prunedTags: { name: string; value: string }[] = allTags.filter( + (tag: { + name: string; + value: string | undefined; + }): tag is { name: string; value: string } => tag.value !== undefined, + ); + return this.process.send({ signer: this.signer, - tags: [ - ...tags, - { name: 'Action', value: 'Decrease-Delegate-Stake' }, - { name: 'Target', value: params.target }, - { name: 'Vault-Id', value: params.vaultId }, - ], + tags: prunedTags, }); } @@ -1123,7 +1141,7 @@ export class IOWriteable extends IOReadable implements AoIOWrite { * @returns {Promise} The result of the cancellation */ async cancelWithdrawal( - params: { address?: WalletAddress; vaultId: string }, + params: { gatewayAddress?: WalletAddress; vaultId: string }, options?: WriteOptions | undefined, ): Promise { const { tags = [] } = options || {}; @@ -1131,8 +1149,8 @@ export class IOWriteable extends IOReadable implements AoIOWrite { const allTags = [ ...tags, { name: 'Action', value: 'Cancel-Withdrawal' }, - { name: 'Address', value: params.address }, { name: 'Vault-Id', value: params.vaultId }, + { name: 'Address', value: params.gatewayAddress }, ]; const prunedTags: { name: string; value: string }[] = allTags.filter( diff --git a/src/types/io.ts b/src/types/io.ts index fc8e3108..793f4890 100644 --- a/src/types/io.ts +++ b/src/types/io.ts @@ -446,9 +446,9 @@ export interface AoIOWrite extends AoIORead { }, options?: WriteOptions, ): Promise; - instantDelegateWithdrawal( + instantWithdrawal( params: { - target: WalletAddress; + gatewayAddress?: WalletAddress; vaultId: string; }, options?: WriteOptions, @@ -491,7 +491,7 @@ export interface AoIOWrite extends AoIORead { ): Promise; cancelWithdrawal( params: { - address?: WalletAddress; + gatewayAddress?: WalletAddress; vaultId: string; }, options?: WriteOptions, From 6397436274c7fe74a64f25aaf690a15b66564a15 Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Sat, 2 Nov 2024 08:05:52 -0600 Subject: [PATCH 49/97] chore(docs): update README --- README.md | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index d8cd0dc6..81fa34b5 100644 --- a/README.md +++ b/README.md @@ -48,14 +48,14 @@ This is the home of [ar.io] SDK. This SDK provides functionality for interacting - [`updateGatewaySettings(gatewaySettings)`](#updategatewaysettingsgatewaysettings) - [`increaseDelegateStake({ target, qty })`](#increasedelegatestake-target-qty-) - [`decreaseDelegateStake({ target, qty, instant })`](#decreasedelegatestake-target-qty-instant-) - - [`instantDelegateWithdrawal({ target, vaultId })`](#instantdelegatewithdrawal-target-vaultid-) + - [`instantWithdrawal({ gatewayAddress, vaultId })`](#instantwithdrawal-gatewayaddress-vaultid-) - [`increaseOperatorStake({ qty })`](#increaseoperatorstake-qty-) - [`decreaseOperatorStake({ qty })`](#decreaseoperatorstake-qty-) - [`saveObservations({ reportTxId, failedGateways })`](#saveobservations-reporttxid-failedgateways-) - [`transfer({ target, qty })`](#transfer-target-qty-) - [`increaseUndernameLimit({ name, qty })`](#increaseundernamelimit-name-qty-) - [`extendLease({ name, years })`](#extendlease-name-years-) - - [`cancelDelegateWithdrawal({ address, vaultId })`](#canceldelegatewithdrawal-address-vaultid-) + - [`cancelWithdrawal({ gatewayAddress, vaultId })`](#cancelwithdrawal-gatewayaddress-vaultid-) - [`submitAuctionBid({ name, type, years, processId })`](#submitauctionbid-name-type-years-processid-) - [Configuration](#configuration) - [Arweave Name Tokens (ANT's)](#arweave-name-tokens-ants) @@ -1109,18 +1109,19 @@ const { id: txId } = await io.decreaseDelegateStake({ }); ``` -#### `instantDelegateWithdrawal({ target, vaultId })` +#### `instantWithdrawal({ gatewayAddress, vaultId })` -Instantly withdraws vaulted delegate tokens at the cost of the early withdrawal fee. +Instantly withdraws an existing vault on a gateway. If no `gatewayAddress` is provided, the signer's address will be used. _Note: Requires `signer` to be provided on `IO.init` to sign the transaction._ ```typescript const io = IO.init({ signer: new ArweaveSigner(jwk) }); -const { id: txId } = await io.instantDelegateWithdrawal( +// removes a delegated vault from a gateway +const { id: txId } = await io.instantWithdrawal( { // gateway address where delegate vault exists - target: 't4Xr0_J4Iurt7caNST02cMotaz2FIbWQ4Kbj616RHl3', + gatewayAddress: 't4Xr0_J4Iurt7caNST02cMotaz2FIbWQ4Kbj616RHl3', // delegated vault id to cancel vaultId: 'fDrr0_J4Iurt7caNST02cMotaz2FIbWQ4Kcj616RHl3', }, @@ -1129,6 +1130,12 @@ const { id: txId } = await io.instantDelegateWithdrawal( tags: [{ name: 'App-Name', value: 'My-Awesome-App' }], }, ); +// removes an operator vault from a gateway +const { id: txId } = await io.instantWithdrawal( + { + vaultId: 'fDrr0_J4Iurt7caNST02cMotaz2FIbWQ4Kcj616RHl3', + }, +); ``` #### `increaseOperatorStake({ qty })` @@ -1238,24 +1245,32 @@ const { id: txId } = await io.extendLease( ); ``` -#### `cancelDelegateWithdrawal({ address, vaultId })` +#### `cancelWithdrawal({ gatewayAddress, vaultId })` -Cancels a pending delegate withdrawal. +Cancels an existing vault on a gateway. The vaulted stake will be returned to the callers stake. If no `gatewayAddress` is provided, the signer's address will be used. _Note: Requires `signer` to be provided on `IO.init` to sign the transaction._ ```typescript const io = IO.init({ signer: new ArweaveSigner(jwk) }); -const { id: txId } = await io.cancelDelegateWithdrawal( +// cancels a delegated vault from a gateway +const { id: txId } = await io.cancelWithdrawal( { // gateway address where vault exists - address: 't4Xr0_J4Iurt7caNST02cMotaz2FIbWQ4Kbj616RHl3', + gatewayAddress: 't4Xr0_J4Iurt7caNST02cMotaz2FIbWQ4Kbj616RHl3', // vault id to cancel vaultId: 'fDrr0_J4Iurt7caNST02cMotaz2FIbWQ4Kcj616RHl3', }, // optional additional tags { tags: [{ name: 'App-Name', value: 'My-Awesome-App' }] }, ); +// cancels an operator vault from a gateway +const { id: txId } = await io.cancelWithdrawal( + { + // operator vault id to cancel + vaultId: 'fDrr0_J4Iurt7caNST02cMotaz2FIbWQ4Kcj616RHl3', + }, +); ``` #### `submitAuctionBid({ name, type, years, processId })` From 0d66c6ee97ce48588d8728ce67bad60b26f8a59e Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 4 Nov 2024 20:24:58 +0000 Subject: [PATCH 50/97] chore(release): 2.4.0-alpha.7 [skip ci] # [2.4.0-alpha.7](https://github.com/ar-io/ar-io-sdk/compare/v2.4.0-alpha.6...v2.4.0-alpha.7) (2024-11-04) ### Bug Fixes * **io:** consolidate `instantGatewayWithdrawal` and `instantGatewayWithdrawal` to just `instantWithdrawal`, update `cancelWithdrawal ([ea9f3eb](https://github.com/ar-io/ar-io-sdk/commit/ea9f3eb90843f1181b83d9f876b75a059efb8811)) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- src/version.ts | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62ef16de..151e9cee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [2.4.0-alpha.7](https://github.com/ar-io/ar-io-sdk/compare/v2.4.0-alpha.6...v2.4.0-alpha.7) (2024-11-04) + + +### Bug Fixes + +* **io:** consolidate `instantGatewayWithdrawal` and `instantGatewayWithdrawal` to just `instantWithdrawal`, update `cancelWithdrawal ([ea9f3eb](https://github.com/ar-io/ar-io-sdk/commit/ea9f3eb90843f1181b83d9f876b75a059efb8811)) + # [2.4.0-alpha.6](https://github.com/ar-io/ar-io-sdk/compare/v2.4.0-alpha.5...v2.4.0-alpha.6) (2024-11-04) diff --git a/package.json b/package.json index ae549ac2..64108a11 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ar.io/sdk", - "version": "2.4.0-alpha.6", + "version": "2.4.0-alpha.7", "repository": { "type": "git", "url": "git+https://github.com/ar-io/ar-io-sdk.git" diff --git a/src/version.ts b/src/version.ts index a48c40ae..be777bcd 100644 --- a/src/version.ts +++ b/src/version.ts @@ -16,4 +16,4 @@ // AUTOMATICALLY GENERATED FILE - DO NOT TOUCH -export const version = '2.4.0-alpha.6'; +export const version = '2.4.0-alpha.7'; From 9e705a995d454092b1ea415debba02a9e2efb487 Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Mon, 4 Nov 2024 08:04:51 -0600 Subject: [PATCH 51/97] fix(ant): add `reassignName` to ant implementation This allows the owner of an ANT process to reassign a specific name to a new process id --- README.md | 15 +++++++++++++++ src/common/ant.ts | 31 +++++++++++++++++++++++++++++++ src/types/ant.ts | 9 +++++++++ 3 files changed, 55 insertions(+) diff --git a/README.md b/README.md index 81fa34b5..1e5d0f68 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ This is the home of [ar.io] SDK. This SDK provides functionality for interacting - [`setDescription({ description })`](#setdescription-description-) - [`setKeywords({ keywords })`](#setkeywords-keywords-) - [`releaseName({ name, ioProcessId })`](#releasename-name-ioprocessid-) + - [`reassignName({ name, ioProcessId, antProcessId })`](#reassignname-name-ioprocessid-antprocessid-) - [Configuration](#configuration-1) - [Logging](#logging) - [Configuration](#configuration-2) @@ -1636,6 +1637,20 @@ const { id: txId } = await ant.releaseName({ }); ``` +#### `reassignName({ name, ioProcessId, antProcessId })` + +Reassigns a name to a new ANT. This can only be done by the current owner of the ANT. + +_Note: Requires `signer` to be provided on `ANT.init` to sign the transaction._ + +```typescript +const { id: txId } = await ant.reassignName({ + name: 'ardrive', + ioProcessId: IO_TESTNET_PROCESS_ID, + antProcessId: NEW_ANT_PROCESS_ID, // the new ANT process id that will take over ownership of the name +}); +``` + ### Configuration ANT clients can be configured to use custom AO process. Refer to [AO Connect] for more information on how to configure the AO process to use specific AO infrastructure. diff --git a/src/common/ant.ts b/src/common/ant.ts index 7ba29da0..f715e30f 100644 --- a/src/common/ant.ts +++ b/src/common/ant.ts @@ -541,4 +541,35 @@ export class AoANTWriteable extends AoANTReadable implements AoANTWrite { signer: this.signer, }); } + + /** + * Sends a message to the IO contract to reassign the name to a new ANT. This can only be done by the current owner of the ANT. + * @param name @type {string} The name you want to reassign. + * @param ioProcessId @type {string} The processId of the IO contract. + * @param antProcessId @type {string} The processId of the ANT contract. + * @returns {Promise} The result of the interaction. + * @example + * ```ts + * ant.reassignName({ name: "ardrive", ioProcessId: IO_TESTNET_PROCESS_ID, antProcessId: NEW_ANT_PROCESS_ID }); + * ``` + */ + async reassignName( + { + name, + ioProcessId, + antProcessId, + }: { name: string; ioProcessId: string; antProcessId: string }, + options?: WriteOptions, + ): Promise { + return this.process.send({ + tags: [ + ...(options?.tags ?? []), + { name: 'Action', value: 'Reassign-Name' }, + { name: 'Name', value: name }, + { name: 'IO-Process-Id', value: ioProcessId }, + { name: 'Process-Id', value: antProcessId }, + ], + signer: this.signer, + }); + } } diff --git a/src/types/ant.ts b/src/types/ant.ts index 6cef0510..a2e607ab 100644 --- a/src/types/ant.ts +++ b/src/types/ant.ts @@ -123,6 +123,7 @@ export const AntHandlerNames = [ 'initializeState', 'state', 'releaseName', + 'reassignName', ]; export const AntHandlersSchema = z .array(z.string({ description: 'Handler Name' })) @@ -248,4 +249,12 @@ export interface AoANTWrite extends AoANTRead { { name, ioProcessId }: { name: string; ioProcessId: string }, options?: WriteOptions, ): Promise; + reassignName( + { + name, + ioProcessId, + antProcessId, + }: { name: string; ioProcessId: string; antProcessId: string }, + options?: WriteOptions, + ): Promise; } From 617550dad99c82ea13eb47f078887522d1d61ecb Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 4 Nov 2024 20:31:39 +0000 Subject: [PATCH 52/97] chore(release): 2.4.0-alpha.8 [skip ci] # [2.4.0-alpha.8](https://github.com/ar-io/ar-io-sdk/compare/v2.4.0-alpha.7...v2.4.0-alpha.8) (2024-11-04) ### Bug Fixes * **ant:** add `reassignName` to ant implementation ([9e705a9](https://github.com/ar-io/ar-io-sdk/commit/9e705a995d454092b1ea415debba02a9e2efb487)) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- src/version.ts | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 151e9cee..81f2f68d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [2.4.0-alpha.8](https://github.com/ar-io/ar-io-sdk/compare/v2.4.0-alpha.7...v2.4.0-alpha.8) (2024-11-04) + + +### Bug Fixes + +* **ant:** add `reassignName` to ant implementation ([9e705a9](https://github.com/ar-io/ar-io-sdk/commit/9e705a995d454092b1ea415debba02a9e2efb487)) + # [2.4.0-alpha.7](https://github.com/ar-io/ar-io-sdk/compare/v2.4.0-alpha.6...v2.4.0-alpha.7) (2024-11-04) diff --git a/package.json b/package.json index 64108a11..98979c4c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ar.io/sdk", - "version": "2.4.0-alpha.7", + "version": "2.4.0-alpha.8", "repository": { "type": "git", "url": "git+https://github.com/ar-io/ar-io-sdk.git" diff --git a/src/version.ts b/src/version.ts index be777bcd..62b4aa6d 100644 --- a/src/version.ts +++ b/src/version.ts @@ -16,4 +16,4 @@ // AUTOMATICALLY GENERATED FILE - DO NOT TOUCH -export const version = '2.4.0-alpha.7'; +export const version = '2.4.0-alpha.8'; From 5fd2ccc5c5d781234eaddc10c005b928fbc0fb50 Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Tue, 5 Nov 2024 10:11:13 -0600 Subject: [PATCH 53/97] fix(auctions): update auction APIs and types --- README.md | 20 ++++++++++---------- examples/vite/src/App.tsx | 4 ++-- src/common/io.ts | 10 +++++++--- src/types/io.ts | 32 +++++++++++++++++--------------- tests/e2e/esm/index.test.js | 19 ++++++++++++++++--- 5 files changed, 52 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 1e5d0f68..1bac939b 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,9 @@ This is the home of [ar.io] SDK. This SDK provides functionality for interacting - [`getGateways({ cursor, limit, sortBy, sortOrder })`](#getgateways-cursor-limit-sortby-sortorder-) - [`getArNSRecord({ name })`](#getarnsrecord-name-) - [`getArNSRecords({ cursor, limit, sortBy, sortOrder })`](#getarnsrecords-cursor-limit-sortby-sortorder-) - - [`getAuctions({ cursor, limit, sortBy, sortOrder })`](#getauctions-cursor-limit-sortby-sortorder-) - - [`getAuction({ name })`](#getauction-name-) - - [`getAuctionPrices({ name, type, years, intervalMs })`](#getauctionprices-name-type-years-intervalms-) + - [`getArNSAuctions({ cursor, limit, sortBy, sortOrder })`](#getarnsauctions-cursor-limit-sortby-sortorder-) + - [`getArNSAuction({ name })`](#getarnsauction-name-) + - [`getArNSAuctionPrices({ name, type, years, intervalMs })`](#getarnsauctionprices-name-type-years-intervalms-) - [`getDemandFactor()`](#getdemandfactor) - [`getObservations({ epochIndex })`](#getobservations-epochindex-) - [`getDistributions({ epochIndex })`](#getdistributions-epochindex-) @@ -606,13 +606,13 @@ Available `sortBy` options are any of the keys on the record object, e.g. `name` -#### `getAuctions({ cursor, limit, sortBy, sortOrder })` +#### `getArNSAuctions({ cursor, limit, sortBy, sortOrder })` Retrieves all active auctions of the IO process, paginated and sorted by the specified criteria. The `cursor` used for pagination is the last auction name from the previous request. ```typescript const io = IO.init(); -const auctions = await io.getAuctions({ +const auctions = await io.getArNSAuctions({ limit: 100, sortBy: 'endTimestamp', sortOrder: 'asc', // return the auctions ending soonest first @@ -649,13 +649,13 @@ const auctions = await io.getAuctions({ -#### `getAuction({ name })` +#### `getArNSAuction({ name })` Retrieves the auction data for the specified auction name. ```typescript const io = IO.init(); -const auction = await io.getAuction({ name: 'permalink' }); +const auction = await io.getArNSAuction({ name: 'permalink' }); ```
@@ -680,13 +680,13 @@ const auction = await io.getAuction({ name: 'permalink' });
-#### `getAuctionPrices({ name, type, years, intervalMs })` +#### `getArNSAuctionPrices({ name, type, years, intervalMs })` Retrieves the auction price curve of the specified auction name for the specified type, duration, and interval. The `intervalMs` is the number of milliseconds between price points on the curve. The default interval is 15 minutes. ```typescript const io = IO.init(); -const priceCurve = await io.getAuctionPrices({ +const priceCurve = await io.getArNSAuctionPrices({ name: 'permalink', type: 'lease', years: 1, @@ -1283,7 +1283,7 @@ _Note: Requires `signer` to be provided on `IO.init` to sign the transaction._ ```typescript const io = IO.init({ signer: new ArweaveSigner(jwk) }); -const auction = await io.getAuction({ name: 'permalink' }); +const auction = await io.getArNSAuction({ name: 'permalink' }); // check the current price is under some threshold if (auction && auction.currentPrice <= new IOToken(20_000).toMIO().valueOf()) { diff --git a/examples/vite/src/App.tsx b/examples/vite/src/App.tsx index f78c834b..2757eb30 100644 --- a/examples/vite/src/App.tsx +++ b/examples/vite/src/App.tsx @@ -30,9 +30,9 @@ function App() { const [auctions, setAuctions] = useState([]); useEffect(() => { - io.getAuctions().then((page: PaginationResult) => { + io.getArNSAuctions().then((page: PaginationResult) => { page.items.forEach((auction: AoAuction) => { - io.getAuctionPrices({ + io.getArNSAuctionPrices({ name: auction.name, type: 'lease', intervalMs: 1000 * 60 * 60 * 24, // 1 day diff --git a/src/common/io.ts b/src/common/io.ts index 70e8f2a9..91f49a85 100644 --- a/src/common/io.ts +++ b/src/common/io.ts @@ -594,7 +594,7 @@ export class IOReadable implements AoIORead { } // Auctions - async getAuctions( + async getArNSAuctions( params?: PaginationParams, ): Promise> { const allTags = [ @@ -617,7 +617,11 @@ export class IOReadable implements AoIORead { }); } - async getAuction({ name }: { name: string }): Promise { + async getArNSAuction({ + name, + }: { + name: string; + }): Promise { const allTags = [ { name: 'Action', value: 'Auction-Info' }, { name: 'Name', value: name }, @@ -639,7 +643,7 @@ export class IOReadable implements AoIORead { * @param {number} [params.intervalMs=900000] - The interval in milliseconds between price points (default is 15 minutes) * @returns {Promise} The auction price data */ - async getAuctionPrices({ + async getArNSAuctionPrices({ name, type, years, diff --git a/src/types/io.ts b/src/types/io.ts index 793f4890..86140f15 100644 --- a/src/types/io.ts +++ b/src/types/io.ts @@ -335,6 +335,23 @@ export interface AoIORead { }: { name: string; }): Promise; + getArNSAuctions( + params?: PaginationParams, + ): Promise>; + getArNSAuction({ name }: { name: string }): Promise; + getArNSAuctionPrices({ + name, + type, + years, + timestamp, + intervalMs, + }: { + name: string; + type: 'lease' | 'permabuy'; + years?: number; + timestamp?: number; + intervalMs?: number; + }): Promise; getEpoch(epoch?: EpochInput): Promise; getCurrentEpoch(): Promise; getPrescribedObservers(epoch?: EpochInput): Promise; @@ -356,21 +373,6 @@ export interface AoIORead { }): Promise; getRegistrationFees(): Promise; getDemandFactor(): Promise; - getAuctions(params?: PaginationParams): Promise>; - getAuction({ name }: { name: string }): Promise; - getAuctionPrices({ - name, - type, - years, - timestamp, - intervalMs, - }: { - name: string; - type: 'lease' | 'permabuy'; - years?: number; - timestamp?: number; - intervalMs?: number; - }): Promise; } export interface AoIOWrite extends AoIORead { diff --git a/tests/e2e/esm/index.test.js b/tests/e2e/esm/index.test.js index 7fc114cb..28b7de87 100644 --- a/tests/e2e/esm/index.test.js +++ b/tests/e2e/esm/index.test.js @@ -335,19 +335,32 @@ describe('e2e esm tests', async () => { }); it('should be able to get current auctions', async () => { - const { items: auctions } = await io.getAuctions(); + const { items: auctions } = await io.getArNSAuctions(); assert.ok(auctions); }); it('should be able to get a specific auction', async () => { - const { items: auctions } = await io.getAuctions(); + const { items: auctions } = await io.getArNSAuctions(); if (auctions.length === 0) { return; } - const auction = await io.getAuction({ name: auctions[0].name }); + const auction = await io.getArNSAuction({ name: auctions[0].name }); assert.ok(auction); }); + it('should be able to get auction prices for an existing auction', async () => { + const { items: auctions } = await io.getArNSAuctions(); + if (auctions.length === 0) { + return; + } + const auctionPrices = await io.getArNSAuctionPrices({ + name: auctions[0].name, + type: 'lease', + years: 1, + }); + assert.ok(auctionPrices); + }); + it('should be able to create IOWriteable with valid signers', async () => { for (const signer of signers) { const io = IO.init({ signer }); From 5e68ac33ecaf5189fa31b45f323ea9f530d721cd Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Tue, 5 Nov 2024 10:31:02 -0600 Subject: [PATCH 54/97] chore(docs): update vite example --- examples/vite/src/App.tsx | 292 ++++++++++++++++++++++++++++++-------- package.json | 11 +- 2 files changed, 239 insertions(+), 64 deletions(-) diff --git a/examples/vite/src/App.tsx b/examples/vite/src/App.tsx index 2757eb30..b5b5dfde 100644 --- a/examples/vite/src/App.tsx +++ b/examples/vite/src/App.tsx @@ -1,6 +1,10 @@ import { + AoArNSNameData, + AoArNSNameDataWithName, AoAuction, AoAuctionPriceData, + AoGateway, + AoGatewayWithAddress, IO, IO_DEVNET_PROCESS_ID, PaginationResult, @@ -27,72 +31,242 @@ type AuctionWithPrices = AoAuction & { }; function App() { - const [auctions, setAuctions] = useState([]); + const [auctions, setAuctions] = useState([]); + const [selectedAuction, setSelectedAuction] = + useState(null); + const [names, setNames] = useState([]); + const [gateways, setGateways] = useState([]); + const [totalGateways, setTotalGateways] = useState(0); + const [totalNames, setTotalNames] = useState(0); + const [totalAuctions, setTotalAuctions] = useState(0); useEffect(() => { - io.getArNSAuctions().then((page: PaginationResult) => { - page.items.forEach((auction: AoAuction) => { - io.getArNSAuctionPrices({ - name: auction.name, - type: 'lease', - intervalMs: 1000 * 60 * 60 * 24, // 1 day - }).then((price: AoAuctionPriceData) => { - const arrayOfPrices = Object.entries(price.prices) - .sort(([timestampA], [timestampB]) => +timestampA - +timestampB) - .map(([timestamp, price]) => ({ - timestamp: new Date(+timestamp).toLocaleString('en-US', { - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - hour12: false, - }), - price: price / 10 ** 6, - })); - const auctionWithPrices = { - ...auction, - prices: arrayOfPrices, - currentPrice: price.currentPrice / 10 ** 6, - }; - setAuctions((prev) => [...prev, auctionWithPrices]); + // fetch first page of arns names + io.getArNSRecords({ limit: 10 }).then( + (page: PaginationResult) => { + setNames(page.items); + setTotalNames(page.totalItems); + }, + ); + + // fetch first page of gateways + io.getGateways({ limit: 10 }).then( + (page: PaginationResult) => { + setGateways(page.items); + setTotalGateways(page.totalItems); + }, + ); + + // get auction and prices for each auction + io.getArNSAuctions({ limit: 10 }).then( + (page: PaginationResult) => { + setAuctions(page.items); + setTotalAuctions(page.totalItems); + page.items.forEach((auction: AoAuction) => { + io.getArNSAuctionPrices({ + name: auction.name, + type: 'lease', + intervalMs: 1000 * 60 * 60 * 24, // 1 day + }).then((price: AoAuctionPriceData) => { + const arrayOfPrices = Object.entries(price.prices) + .sort(([timestampA], [timestampB]) => +timestampA - +timestampB) + .map(([timestamp, price]) => ({ + timestamp: new Date(+timestamp).toLocaleString('en-US', { + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + hour12: false, + }), + price: price / 10 ** 6, + })); + const auctionWithPrices = { + ...auction, + prices: arrayOfPrices, + currentPrice: price.currentPrice / 10 ** 6, + }; + setSelectedAuction(auctionWithPrices); + }); }); - }); - }); + }, + ); }, []); return ( -
- {auctions.length > 0 && ( - - +
+
+

ArNS Names

+
+ Total Names: {totalNames} +
+
+ + + + + + + + + + + {names.map((record) => ( + + + + + + + ))} + +
Name + Process + TypeExpiry
{record.name} + {record.processId.slice(0, 8)}... + {record.type} + {record.type === 'lease' && record.endTimestamp + ? new Date(record.endTimestamp).toLocaleDateString() + : 'N/A'} +
+
+
+ +
+

Active Gateways

+
+ Total Gateways: {totalGateways} +
+
+ + + + + + + + + + {gateways.map((gateway) => ( + + + + + + ))} + +
+ Address + Status + Stake (IO) +
+ {gateway.gatewayAddress.slice(0, 8)}... + {gateway.status} + {gateway.operatorStake / 10 ** 6} +
+
+
+
+

Active Auctions

+
+ Total Auctions: {totalAuctions} +
+
- - - - - - - - - - - )} + + + + + + + + + {auctions.map((auction) => ( + + + + + ))} + +
NameEnds
{auction.name} + {new Date(auction.endTimestamp).toLocaleDateString()} +
+
+ {selectedAuction && ( +
+ + + + + + + + + + + + +
+ )} +
+
); } diff --git a/package.json b/package.json index 98979c4c..9c9e850f 100644 --- a/package.json +++ b/package.json @@ -66,17 +66,18 @@ "format": "prettier --check .", "format:fix": "prettier --write .", "test": "yarn test:unit && yarn test:e2e", - "test:cjs": "yarn test:link && cd ./tests/e2e/cjs && yarn && yarn test", - "test:esm": "yarn test:link && cd ./tests/e2e/esm && yarn && yarn test", - "test:web": "yarn test:link && cd ./tests/e2e/web && yarn && yarn test", + "test:cjs": "yarn build:cjs && yarn link && cd ./tests/e2e/cjs && yarn && yarn test", + "test:esm": "yarn build:esm && yarn link && cd ./tests/e2e/esm && yarn && yarn test", + "test:web": "yarn build:web && yarn link && cd ./tests/e2e/web && yarn && yarn test", "test:unit": "NODE_OPTIONS=\"--import=./register.mjs\" node --test tests/unit/**.test.ts", "test:link": "yarn build && yarn link", "test:e2e": "yarn test:cjs && yarn test:esm && yarn test:web", "prepare": "husky install", "docs:update": "markdown-toc-gen insert README.md", "example:esm": "cd examples/esm && yarn && node index.mjs", - "example:cjs": "yarn test:link && cd examples/cjs && yarn && node index.cjs", - "example:web": "yarn test:link && build:web && http-server --port 8080 --host -o examples/web" + "example:cjs": "yarn build:cjs && yarn link && cd examples/cjs && yarn && node index.cjs", + "example:web": "yarn build:web && http-server --port 8080 --host -o examples/web", + "example:vite": "yarn build:esm && yarn link && cd examples/vite && yarn && yarn start" }, "devDependencies": { "@commitlint/cli": "^17.1.2", From 1acce25402bac5c69294aa36fa2c32d42b98bd46 Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Tue, 5 Nov 2024 10:34:24 -0600 Subject: [PATCH 55/97] chore(docs): update README --- README.md | 1 + examples/vite/README.md | 9 ++++++++- examples/vite/public/image.png | Bin 0 -> 213318 bytes examples/vite/public/screenshot.png | Bin 38072 -> 0 bytes 4 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 examples/vite/public/image.png delete mode 100644 examples/vite/public/screenshot.png diff --git a/README.md b/README.md index 1bac939b..fea5f136 100644 --- a/README.md +++ b/README.md @@ -1758,6 +1758,7 @@ For more information on how to use AO and AO Connect within this library, please - `yarn example:web` - opens up the example web page - `yarn example:cjs` - runs example CJS node script - `yarn example:esm` - runs example ESM node script +- `yarn example:vite` - runs example Vite web page ### Linting & Formatting diff --git a/examples/vite/README.md b/examples/vite/README.md index 5bd392c5..b05aeaaa 100644 --- a/examples/vite/README.md +++ b/examples/vite/README.md @@ -2,6 +2,13 @@ This example shows how to use the `@ar.io/sdk` within a Typescript/React project using [Vite]. + + +- [Getting Started](#getting-started) +- [Polyfills](#polyfills) + + + ## Getting Started 1. Install the dependencies: @@ -18,7 +25,7 @@ yarn start 3. Open your browser and navigate to `http://localhost:3000`. You should see: -![screenshot](./public/screenshot.png) +![screenshot](./public/image.png) ## Polyfills diff --git a/examples/vite/public/image.png b/examples/vite/public/image.png new file mode 100644 index 0000000000000000000000000000000000000000..23a210b99eaef6ff292d906151bebe662b93a063 GIT binary patch literal 213318 zcmeFZby$?&_BIZ~07IvA42`ICOAH|)3MitWbR$T2kF%P}AR9#h(0G9?A4GoPzSxNpO8X6Ra zhKA(=!vdeMbH}|zL&MFnl#^3emXm|4JJ_08TAQMwDTT%-Vn5PYCiT1Y9F&t)32~Ao zxD%y{cAe>Kzh?k6;HmeVN-zAIf{_Hpt*u7l5SnS61%WJ>aa zO)@d@x>;ZV6iwc2ZE9go67Bx@#P8V3i}TA%uDf0`>_ljs&>5%?I}iO#Y)I{FD4RA~ z`T>*AxvBQ7@&@7!mkf>kQ3ET-{oO5Aj(dDG_gdrwib%2`ejN6dF|24xPR(k4zuB1F zl{R3{ULDDjG#Uly4h{+&!5HO6sX}GEK6IRQ2h`0gpFHb@YO8XKD7OxJz9p^B=G91U z?Kzi@oby%jNOH;wBFXq*5^76j6dx_%8I?4{WBfo?eG4D&SYulw`R0B^vY{UPZJVHD z9W~#v@vrHA)T-njjwbHKq9g)0+xKwO43!n$N69>?(+~IyLAumC#*|K?(&>ob1<#z9VlESDpshH#3e}d3G=+eGp9u@ zu*~Z*?WlFIe@=6|;d?Zoxe+MTEAkS`5EC-Xkt-2=N8&RHw9^>L=0{PIqXo(osY}E` zL*duILY@*`lW9?>euOzBTZhO_$LrVpgzW*_myzqgy@VJ3QknPK@XLs4cV05Hh6_7r zHB9woVxzmAR5dksVD1cy9hnz;E#Xw4=gi0!CvN(@x?mi4CtZtt(SwFhqK}yGzI+Fu@R)m+cMBJ(l03zq!rOc1gloz8B)>VGL8!hTU)AZnJ6J zXb?7-D3l!aS>q%O)@XV=qhr?~%FT`U#BUyCnx-1-nj^``ae|;6vb;#Ob~bA!I`~&o8&WLToID$||B`GtJd1dQR3&u3S*4ky z`Ov|^!NcLT!+QtWN+E}j4sG-K?fV_x?AK%ODO-P+jfwmE)+y?Tf`^ia8hdO+`^LPe z1BJUlWrA1qS@@Z9Dzhz9U-V*EK>|;rZA=~8r2;bD;8XoWt-Ppg^$a`NnXJY_W|;w{ z=r8S>Iv=(2-R^GJ&VC}j({cB@)^%OW*zblPbMN|hikh;R@|e~-)b98!gms>ilix{e z%-Z5yy^hV9$cgdjX1vUTy2htFKQe!CuY|lRe)Z*U+}+oAD?7F0^=Mw1$yWwd|^5=9TZXa;0lAZ@ya*s?FxXU>{QhC;;sFGLuY&B!JQ273Y z?(I9C!3SdNO!Ti_=e|}q5AJnOdE#_UJS3Ar5a}s z0uQzQ(35-T^5^s?>nAK&;#f$m8~7{u4Ai9fZ-Y95zTw{{XbY!O7*L=MPl~{z&cP>S ziG0xJW^_;3ML_Sl&I_+Ls8yq&ve20E_ZPVD#^18POL+H7(7$J`C&0|J+vB&2 zcj&g2hE=L_H#K`-pqw(dhBbL(AQkqmxsc-I~6E*)7r1WD;rYx_BS@3)V}! zOa9A6^eprkzhQ)EKaZW~bS%6H{y^Q47xo)!OXNe-2-Bu<5GSXlA$`hl!raYeeMie& zl+8=|Xl5&tgn{H4GlL?H;$4=BO!5p0g&~EAJDv)ZiifS;VcOJ=l8+@HQ%#TthlRb$ z4W-qcE;U$ui41hX!=w21tXV6rWNF1{zr`}FkNbgEqpkxp0~sT|ujs`MkvGqJ=X|6y7_qR z@xZX>#AF#g*JG~U8yiy$6*ZRF-ikA?iEbyZRX;U)_OLRnLZvFVTrmBsX?erOmv40m zY>7W_9dg=!>dzb1;wuZTTO8Iqtl5u$8hEQzdR_oo^$M??o@cFKKlOk)8O&(rljEV9}UjmtKPHVMLIF=ON>9X)zcqBSaFJD zy;hE_Ibpj<^lUqiEKf5?{UmMaR-6(2U27C7v>+TI$RToA)?BuiymNc#*UIF$+xYBW zgFuyeXJ1b8PlwlT+?SKh$JM);FJ{YT@RTV}CKA6&*HSrdt14P3j<8*-JmL1#uO01J zk??I<=zN>2`b16FSkJ1~#@#eCqCtjQ@3?b|v+X2~HynX#*U}Os2E87izw59i zs5z)HKb1JOks$KDs7M>RJUQ&1sw14QgV%Aof8H&2>G#;`jyhC7%*8Cg(hJj%B%(Zf zzo*zjJ14?%>2$f z=>qEw^H!Rh$L~~9PqY;h0WpfA2{h55Ptmv0cG`F6mB?oZ4QOfK_IB55IG%hkh~`P~ zPL18OJIxT9X5^P_y8O85`;_KqQKHH;lhO>G=4Y@M72Vm81BICe_fj%a8UY^Yy!<%g_W z;QFJMkF=b$?%xwPwtc~EXku$*%I)^T4s{(g2{&=@_JygFA>8eSwT+{=ny9#jII+*kDiHV8v@bdHU^WOsBxaH_><7DV|%f^xA&qe;ej=ZU(v4f?ZlclW< z9CckoBU@)DNoHo$jsEfTXPl;PmjAqyjpJXB1s;$Gb%ckHo0sPw*9NCbpgt8>w{$bL z)|R(?0p<+cLyAvWNKoSHgnv8q&s+Y>sapSXDz5QgY&Qn(U4|5!CC+~gfhcm zN{=(-AIL}NamqN5Mx`?=cThrQh~*zRkh9|Gyfm4wvppV>5Gs|l-263Gd3)16_2;ts zPu+7DHMNl^i^)j_drJe1(EAY5C_gAG934jq@z+~qEjup~BBQVGtETsFM~QJbiQQQK z@!fx(MobQ?#`z!D`EyJizQypr?*NX#!DYz)!)4MD(~|!>n7?jNPX+l6-xB^07eXNG zJ^vp0KSu*cmxNVg|A*mF$<-5M*kb(iY5#E!82^7dq2K>MULVN^#pnC)fKV&=-x>O^ z4gIg3{NG*n50~`+wjbJKLS9}R))_Y9aI7^69LBNz<=ZXf5y*5B>O>XPJk`71pDrEI z!%NinabKrXh@iQ~o@9|x*v;P+aEDS?uv_}_tkikVWA?iW4}opblex>XX2meeZEoE&KwqiNuiZ)qdnBk5-CACda;sQK&U2lY~prtyx8>2w&q$^ z((@eN^S!~~V!!*<+ork!^pB^k_Mg~pT*45a&`q zbGDmyiSK*V#JKVbZed;A!S39Z9Q{|+G+2Uh664dW9I{?{0ZIAkwUR!Qt*bK12djj_tPTuQju@O(-vD@ttDgT-K>odX*TreO)P%)?L~!8 z2U{Zk6CL}ObO{_994Ot|q+=g}RY8qQ(_GdyTxe)O+8+di+#pehFELm}{#giJX1njo z9D&}4Kg*W~5_Db0M$v4ZT7*0nCWVcji_{(WY}IUf7c4}JlfWK7wfyL(q)x+qqH?oH zo?I>KolVu&RufUM=VpzFhen*~Tj{mJrpDK|H?&G~mW$0G=wgIhHgDX2zkRnMhfKfD z@Z|2F?cR!mj``C5 zk+((tMba$`cH+XH4qdk><*1S(GSU(K;f1Sz!JWAvKS)Ex%!=OCW;1vL1NK6OT)U_o zK(u{1SCl?^P%C=26)cT^d;P1%&*B_vo&71F)AcIN@DSb>a*bwFMz1ZIqb~gmQU=ep zB2n>rV5t;a>Po}+;OX6M(ihvKxl&+gS@*HL%5D&Q9e$f403SwTTSpr3A5J3DzET}nnUNKR04fe}xw};ba()aRwW;r{Jknwc1 zZ8n4;?YP_g=_}dIx=SA_Nsm3hs_0mzP_}T0%=eZyMqdf|PJM{%rq0OVtaYl5#n%Zc4ZyEy`!QPp{AlBFX22&@HNe$`4&;g5$Znj1E} zkOp9Qlr7PYySYLxO??Drc^h6UOv&si(|)2EY91Z;83W^}0B=YD9Y5x7p; zE7ran7o2Sd=1y~mM?U9qP(6YQr-PsNXPVn6ul&Nr=_ccEV7!mNIn`Wc2;qxhFSqXE zS?pg~R(dKzQ!Xj>7s^KqZ!F#KUC;lv@mwqh!GiQ-!Xm+4BI$j>3qhY4T$%*twW~Xn zmD`*9{Eb{A9!#J;;yvc_vW;sb@f!l^OyG)ZCASPliBJXOq>*o@LcOp?&cBe z2a&PWqqJhX3QzuUzuOX$<&`8)B0WVEGQU{!_J~Y*A+gK^a!Fwp7IRX1rt2{1)KRNQ zW1sKEF8VtSMhS+_Ul%TzkVf()(CS1}3~B4}gxu`fbza_I51Tih8k(u>KCSUP2nDn$#&#a>l+;YzEPpJsAY zr7(;pETJ)ZQ4FUhnqsh}9uC#y)X8GQM4cal2Pj`iyivcc=e6D9wTnIogNEHI)5;S$ z4br_^FOqH-j0fh4l$R9CPt^W&y?kWdey4*Y@;z3i#Xvp+;n1E#0>3+e3_7><*?q^E z8dt5ncJ#&8JNI$60t6j1LC2v3VbNwTeLD8We;S2@z$NsXez`#zb#|}!ohyD0XHsT9mr zOOTP?YaNK-35_<`^7xyquUQ0q1guzzt;>J9*t<34FwE}u-sTa`8uXdC2IKVY5gL&n zIWIPRd!V_mY%37)pf^5sxu{41(trYMvDp0GhGPglqfj?qp*40j7@dr_~z2bFhAPOhda;OizY5aH&uwB0KE{Ki%)4$SaC66oKiWGwdlEcWa z-sy}G&b?*JPTXH|-#<5;FAaqH+5X@PC*u?tG%g!?zgohbR9uK=HG~4dJGJb0tvfwX z2re%(^qxv4W!f*VJY6K5Vq=<-Szm0VcHVE1{)>XKLu`)ZcBmlFoiMuc~U=)$M4+9f!xl;uBSNA zxSy8UDMmFr>!NCnXjHEqbYM`{1v$a%!LiLkj5C}lqp7k_x!a_HQ2^O!n)Ep+Nee9S z+0Q-B7#D21?T|dFl;J*&#BRN_w?uaEfX104?qf7l@w7|~ZIhkP;_a!>uum+h#HKdr zk7vgkKGCI}PA<+KhDPHFUfJ*x4+Ix_?p!1J4D}~V3>wbT7Dao6n+J`-UUQ-X3(R$G za*GQfp{CtCrORAMKRqp@qKTr%5cE^gxo`oJQgZ_mwNhcQNpHQn>VRbF<`qf zur_JT#A&E1m&B*R3}EDL?Yz&?JBWq;Y|vv=q`D9~z(CZakd$cCq7~ zIvdxI^>iaF9$cC}K{pbD_PhE+c4mWeM3RCG-;6{EC`-Ya+aLkCMz$I~%2&?gvmM5# zDy6p9egbv?fupQ4vD;mq7_YjBEYAn<`e#TlYB|IC-xWu*VOHN7!_bIce~-<0oNi{$(6lj7{9$+ zrEPUJ*GIk(P3__eWbE{Nz}(Q23g8id2>5U zI)Zm6|FbzNT!^2+ya%$HVRKYyQhj4!|7rKJk?raAsXr2jLbmyU8G+t?=O~l>UM&Wv&`4x zT#wksa$nVL-a$@>a-_m4_7j8re%w?x)UF+Lm*B7XB{FFzC%T$fg2npK#J6Y1*~vqq z>reQwMP+7RRt^6^?{o|Zm_M_7sRymJw)mqt@t(qR4bhJ2g8UbQ>AhbjAn1I@te;rM z^aO5TzEqGK9q0#Gc|U2LD?hh0ua6VU&C#f2sBiyzri*HEdLmworB&$wmw(5=-nM12DzNRi$wT<(wP z7v!o)Y2ZTlE_y-ofj+}pj!CJ*OGvAfb*_LyoPoE>Mvdy92T<%los9fRV58F7XKIrB zKZ1g}Fd;F*F)Z%|1Jd4_r&j9F#Y)V6R%bU^1Gd|_Sw7|CoJmv|9)%OcmuyqD{D$5j zp#4Lur?^`F4P~Js<48$iiTxiM&J)pxM*alCpPnU{!KUEhEaicedZN4zA?$ymStKf& zealLXzKUi{^;<7YAvGvyC9zeHfud)uJKjIv^GBr-62K)Q`%Idci0!P4yEg#{|4f4+ zrGoT>Xc|Iprg^M%%9Am9c5=FWP9Dc-R4L+#!Su5@V57k2f5eFJL@yx9l+B^e) zMFpF8*2T*A?B4J=q5_*AQ~?X@;b?n$Nj%gZ_Eww*3=s}0?i!H$PukVB5`3e#ikpto zTSGyjvalIa*e66Lmh@^4^d(1{7k|IshlbI1s<~7{`ZSPxZ2j`w6yl=zo6ix}nX+gx zz=RwqZe9W-q$9?=djk_PTu?Ut9BCK*ohqM{P%uNguTr>J?;7kA&qzfyHZ}t-E zhmcI8Fp8hXBnTy$BZ#N=AX!r|`$*L+DI<#h3(D&Way%ocSNwGtrDbPIFrc^V)f9tbk)K>*3d-7#aqI4cUiP(xFsdT`1~3zz z*;Jz2PbbR)ipD{fM~QVLZ(FU>OyTUdJNk~^k=eqovoUb%X{h{&@Y|csSX3ZvT|uRU zTR|44ZmLl|jFjh-9@=mO8jD0a*RhEYq2{0)qYgm17z&c3u2bGkz-AtmDtDQ>edQ$$ z_{-b?%4le>&QFa;nH!0k-v`fyez(1sq~D}FSYy9>UuYWQh8y`kbAC=ol#Qql?H6UW zHyWF^WhE{oi13u<%<+iVp6Y(A>pUbnX}r<+E6=Oa#u_!>nRWMG171q~0Ho!S$7D$a zeIkIe2tXk`v)??lZ8bNE?R?z)*D4oNXF&WlE&IsV%_N4|PG9ey7<7nsNO+9fxfNJ! zoz%wSe;T^}pF&W$76gA$kw`@O*x`of#jbNimZj5raThnnV@8lOqiD78uHa|zbW%kI zcax(AA|Lf9eSi|e+?J}BOu@QMefdt?p`-1F@t4x!=a0WGRJ|vL5esi68^D@PBA}4r zDZoe}k34mj9LwFB8DjSMs+nFYYHqq0;6azqNMw5f_xHv%v(p$N#mh3%44iY4u+>q= zC(8Nf6$j&17{_!u_|f6KgizdSp1(#vh6p~AR9PD z9(m($y7!;D-MI`Pex>tE%&-5C6HxS|#a(ddpJdu!PlLMT3BckqzB=Z853lz2zZ_xh zj{}D^B=Y}#413tX16)+}rhdF4FHm>*dl>20!J#ZSoqr$05)leC+NVgWT!sF>Pe7fE z&;W<-Z&&<>F))G^IGGdoO!&tg{=OM93mi(Cwf_%ekOsW*UYqm5&3~Vy`yl%gY?-_J z?_=;K29Y#!bxr#(#_?aKB8gNTZt17ftPM%I1%u6`)oThTSYy`pIw3+K+=2`Q^rf5{G8w7*EtW-f2JnUi>^WtGxnGqPZM7p)^e5o+AndE4j3p7(8|s??pMl?Y;6ckWU#{St3>3I%hv zfB>gpKTVc)BtQMF_@~Q6Adu8bc1%!V4kfWX$+s%COVoA2VZ1zQyAdXNaWpqmHerJY zaE0rcCt&cKK*~%<@yxpDsO|8uiSfd2p^WLfQB4dE@Yccko6EOy2H^9#}p_mmLkn#nUraaI2oZ+@L*5~7bc)LIRj3=B% z=z0B)3x!>GAjMM)#K54Mw4@hE*$4rFx#8z*iQf0Jq*wgLJ)pH%M*mT&`dL6s@R*m? zB0%|fmJ|dJ9&|6D7F?}VrEn&QHVCMZrKpj%1|krWmPb=ggZB-A=xFuy1uANJ_^{0q z9$v568|9Eb?kHR3-PXa{&HW`e1%`+ zcX<_N475;6C!Pxt7uY-Lm*9^d`n&dQ#CGH!@oK>APB17OaiN$|+tvqo`bV-(bJ^o4 zP!12s?!Ik@V$A0vrKM7-nb8>}1_2kG-b-#c?U;6|Q*n85IwxhZr6#`oq11cE8>kq; z&Bvd!Q^kpEw*YlgTb7E`jC@i4Q+T?_f9A7)J>P&lS&RGSpB#9LOdU>jVP!yo8si=& z5^(O^fQ!3<(Hl>-!f#@LQV+r85Be3~geSNI5+q+oWHgYa!ynEk3X}neWOKwlAVt6Z zrGEi*O|iVx2@$|BkknTw2nO+$E6I5=X|uH(N6VO zh6LUg;s~YQqfz6?7V(n01>hFyomp;6vq+!KF#@R{3GeE=$%Qa)!D%cJnFTT6;gB3N-!s!ak|f z<{=<&n~N@``_o)=uDk+zPHzDsOhkH$UZJN?9K6Dih89YFI$9+`nq2%R<_fs=Lsiz( zn^mj%Ga$|UJZ@cIOlwoYWTxNuMB-#A(-7oigudVjcUwv=jS4LvMSuOmHE~)&0s90? zrK)gzVj8{)n4L3A6Xu*p)U#;Gk)pF9cU|p0Lbl<`LFsmF^sZO=h&7fjq!X*abV#b%q@dDe)-wZ6Madq%hRa>AqQ-7 zni9WMTTf?4 zusXF8M2cXvsYd*KlurvZ{c%7{Kq8jDkCVVGbT)yC_FlJlZ)Fhe*4$(+6he~%#5o7k zQ5=7ECO}#?u{-9AR|AhFp&OY-FZDbd}AZ`OWC+zMnx}(Ee8V_ zhV`9B==m3{t1Pj;%LkE*EhDuDrAvWG3yj2uy(P+EYw@s(o%jMTwuJY3A*Lx*Q$j)Y z^2F~z8lQQHxL#HyEuR7iZ($FoX@ZBE?DUkv%hi?TBm9zj!8G zrQO0Xd<8gX@S)WluOTQSi6p27^p_|FV;-WuVq4`_>7%1*etK{XxYStUp<98uyAV-uo7swpvhkfoAVl2n{-B z$qOh9`_5gpl%f8LAVkP5Zr)|RbVNZd(rZjzb*?i~MLNjMpHhY+DDnOd)wJhG`!jzm zX#(GH`G8@bj;U=X_L62-&gWy*hUx7=0Sgh3ZtZjwA}#Lbw7PA%6G9v)`yErm zgA8bdd2Q{AQWxmLn0|*?WjE{&rdtII?iK*n7f=*3q$`&YYX#{e+M%mHS;>9S4tHXv zbZ-CAcOHu`6QUk5X%ILfi~AFPh-swYQ#Wu!&qHt8@WCq_P43g)qJMy@J3`?UsPAb!xPo!Z4IhhQ!h>*a zetkAfOmO9Yg$|LU=}K9*j7#8K)YQX5?PrXlxvTl>TLYvYmG10d(b3r~WgS&7(1yk{ zfTB@zFi)p^rpPqezlV)B~$^P6;j+Yc+6M}I2Fn>^9cYe$6 ze(cO=NWmm==#KbosLX3L2TJ@>rgV5np>OyuVr#j1%cXu5{d9)Ncht@#+AA%DR0l60 znaf|Ai3cS(V+9yIig$hc$A>2ZAMWbz`G?`)ggG%2_m5`JD6oi9=kEYU^wdv5dbQ)V zs;!TWX8sglt&eMy?kCo?xQWawleB$AAZnDj6q9{z-1B^HAmLN_V@|0JQt?eS$yf%( zfZ*DKczkrsUD?m(drR~A^UYrjuxooyIWqZ@dqvc`6NeKs?mX=^my&1PZC)lTh#aue z!$l<168u*6m>Z*1CauK!LH5AUDsBg((C1TlKh6QFut#bm<^kbSLDR zRcs;7>%ohvob1Zy_=lie#v+O&AE9$Qf7R<@!DJ`?&fHUYeHZ%S&g6L-)-OOlGX~Go z<{=PRmy~*LOsO1;okG_=r>TYDrZvGb*NOWp%-__1r?N*D`cLLS>4(XFBhGw_g@(0Q z&H&^HFR43f;hBYzS<hMk#wd}oOa_n;iAU`Awa`dNc3SB?j^-uF7kGb@s*RRe09O|8`W%fPGFVL5Q-G-IA@rbQ=Q)lc^WEp}U&_m0#=y!cP6JIuhK&6KF2TG>rQjPTRGu-wl@iki%BsPL+}mRy+kZV zMRpndxQ zD!g8#p_ZL{a3RVyfVf}uGrvcLd5ZIJm1cz=s^G)93lyghy9{9+xsx$FdTkl2N;=bp__jocV*MzA32ScLKm9Jb;iFdyOlToe`1WtYPv75r*)Hxetb7oxi23U#+q