diff --git a/src/common/ant-registry.ts b/src/common/ant-registry.ts new file mode 100644 index 00000000..a1f348b3 --- /dev/null +++ b/src/common/ant-registry.ts @@ -0,0 +1,106 @@ +/** + * Copyright (C) 2022-2024 Permanent Data Solutions, Inc. All Rights Reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import { ANT_REGISTRY_ID } from '../constants.js'; +import { + AoANTRegistryRead, + AoANTRegistryWrite, + AoMessageResult, + AoSigner, + OptionalSigner, + ProcessConfiguration, + WithSigner, + isProcessConfiguration, + isProcessIdConfiguration, +} from '../types.js'; +import { createAoSigner } from '../utils/ao.js'; +import { AOProcess } from './index.js'; + +export class ANTRegistry { + static init(): AoANTRegistryRead; + static init( + config: Required & { signer?: undefined }, + ): AoANTRegistryRead; + static init({ + signer, + ...config + }: WithSigner>): AoANTRegistryRead; + static init( + config?: OptionalSigner, + ): AoANTRegistryRead | AoANTRegistryWrite { + if (config && config.signer) { + const { signer, ...rest } = config; + return new AoANTRegistryWriteable({ + ...rest, + signer, + }); + } + return new AoANTRegistryReadable(config); + } +} + +export class AoANTRegistryReadable implements AoANTRegistryRead { + protected process: AOProcess; + + constructor(config?: ProcessConfiguration) { + if ( + config && + (isProcessIdConfiguration(config) || isProcessConfiguration(config)) + ) { + this.process = AOProcess.fromConfiguration(config); + } else { + this.process = AOProcess.fromConfiguration({ + processId: ANT_REGISTRY_ID, + }); + } + } + + // Should we rename this to "getANTsByAddress"? seems more clear, though not same as handler name + async accessControlList({ address }: { address: string }): Promise { + return this.process.read({ + tags: [ + { name: 'Action', value: 'Access-Control-List' }, + { name: 'Address', value: address }, + ], + }); + } +} + +export class AoANTRegistryWriteable + extends AoANTRegistryReadable + implements AoANTRegistryWrite +{ + private signer: AoSigner; + + constructor({ signer, ...config }: WithSigner) { + super(config); + this.signer = createAoSigner(signer); + } + + async register({ + processId, + }: { + processId: string; + }): Promise { + return this.process.send({ + tags: [ + { name: 'Action', value: 'Register' }, + { name: 'Process-Id', value: processId }, + ], + signer: this.signer, + }); + } +} diff --git a/src/common/contracts/ao-process.ts b/src/common/contracts/ao-process.ts index 3ed4a61c..9e20e2e0 100644 --- a/src/common/contracts/ao-process.ts +++ b/src/common/contracts/ao-process.ts @@ -16,10 +16,20 @@ */ import { connect } from '@permaweb/aoconnect'; -import { AOContract, AoClient, AoSigner } from '../../types.js'; +import { + AOContract, + AoClient, + AoSigner, + ProcessConfiguration, + isProcessConfiguration, + isProcessIdConfiguration, +} from '../../types.js'; import { safeDecode } from '../../utils/json.js'; import { version } from '../../version.js'; -import { WriteInteractionError } from '../error.js'; +import { + InvalidContractConfigurationError, + WriteInteractionError, +} from '../error.js'; import { ILogger, Logger } from '../logger.js'; export class AOProcess implements AOContract { @@ -41,6 +51,18 @@ export class AOProcess implements AOContract { this.ao = ao; } + static fromConfiguration(config: Required) { + if (isProcessConfiguration(config)) { + return config.process; + } else if (isProcessIdConfiguration(config)) { + return new AOProcess({ + processId: config.processId, + }); + } else { + throw new InvalidContractConfigurationError(); + } + } + async read({ tags, retries = 3, diff --git a/src/common/index.ts b/src/common/index.ts index 63a144b8..959e5ea1 100644 --- a/src/common/index.ts +++ b/src/common/index.ts @@ -17,6 +17,7 @@ export * from './error.js'; export * from './logger.js'; export * from './ant.js'; +export * from './ant-registry.js'; // ao export * from './io.js'; diff --git a/src/constants.ts b/src/constants.ts index 7b72e504..f6308c9d 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -37,6 +37,10 @@ export const IO_DEVNET_PROCESS_ID = export const ioDevnetProcessId = IO_DEVNET_PROCESS_ID; export const IO_TESTNET_PROCESS_ID = 'agYcCFJtrMG6cqMuZfskIkFTGvUPddICmtQSBIoPdiA'; + +// TODO: set ant registry ID with deployed contract +export const ANT_REGISTRY_ID = + process.env.ANT_REGISTRY_ID ?? 'todo-set-ant-registry-id'; export const MIO_PER_IO = 1_000_000; export const AOS_MODULE_ID = 'cbn0KKrBZH7hdNkNokuXLtGryrWM--PjSTBqIzw9Kkk'; export const ANT_LUA_ID = 'Flwio4Lr08g6s6uim6lEJNnVGD9ylvz0_aafvpiL8FI'; diff --git a/src/io.ts b/src/io.ts index 27c36553..3f8d755f 100644 --- a/src/io.ts +++ b/src/io.ts @@ -325,6 +325,14 @@ export interface AoANTWrite extends AoANTRead { setName({ name }): Promise; } +export interface AoANTRegistryRead { + accessControlList(params: { address: string }): Promise; +} + +export interface AoANTRegistryWrite extends AoANTRegistryRead { + register(params: { processId: string }): Promise; +} + // AO Contract types export interface AoIOState { GatewayRegistry: Record;