diff --git a/docker-compose.yaml b/docker-compose.yaml
index f856b1db..322eae9b 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -9,10 +9,16 @@ services:
environment:
- LOG_LEVEL=debug
- PREFETCH_CONTRACTS=true
- - PREFETCH_CONTRACT_IDS=_NctcA2sRy1-J4OmIQZbYFPM17piNcbdBPH2ncX2RL8
+ - PREFETCH_CONTRACT_IDS=['_NctcA2sRy1-J4OmIQZbYFPM17piNcbdBPH2ncX2RL8', 'UC2zwawQoTnh0TNd9mYLQS4wObBBeaOU5LPQTNETqA4']
- BOOTSTRAP_CONTRACTS=false
healthcheck:
- test: ['CMD', 'curl', '-f', 'http://localhost:3000/healthcheck']
+ test:
+ [
+ 'CMD',
+ 'curl',
+ '-f',
+ 'http://localhost:3000/healthcheck'
+ ]
interval: 10s
timeout: 5s
retries: 5
diff --git a/src/common.ts b/src/common.ts
index 405246fd..d511655f 100644
--- a/src/common.ts
+++ b/src/common.ts
@@ -15,6 +15,8 @@
* along with this program. If not, see .
*/
import {
+ ANTRecord,
+ ANTState,
ArIOState,
ArNSNameData,
EpochDistributionData,
@@ -27,6 +29,27 @@ export type BlockHeight = number;
export type SortKey = string;
export type WalletAddress = string;
+// TODO: append this with other configuration options (e.g. local vs. remote evaluation)
+export type ContractConfiguration =
+ | {
+ contract?: SmartWeaveContract;
+ }
+ | {
+ contractTxId: string;
+ };
+
+export function isContractConfiguration(
+ config: ContractConfiguration,
+): config is { contract: SmartWeaveContract } {
+ return 'contract' in config;
+}
+
+export function isContractTxIdConfiguration(
+ config: ContractConfiguration,
+): config is { contractTxId: string } {
+ return 'contractTxId' in config;
+}
+
export type EvaluationOptions = {
evalTo?: { sortKey: SortKey } | { blockHeight: BlockHeight };
// TODO: any other evaluation constraints
@@ -102,6 +125,18 @@ export interface ArIOContract {
}: EvaluationParameters): Promise;
}
+export interface ANTContract {
+ getState({ evaluationOptions }: EvaluationParameters): Promise;
+ getRecord({ domain, evaluationOptions }: EvaluationParameters<{ domain: string }>): Promise;
+ getRecords({ evaluationOptions }: EvaluationParameters): Promise>;
+ getOwner({ evaluationOptions }: EvaluationParameters): Promise;
+ getControllers({ evaluationOptions }: EvaluationParameters): Promise;
+ getTicker({ evaluationOptions }: EvaluationParameters): Promise;
+ getName({ evaluationOptions }: EvaluationParameters): Promise;
+ getBalance({ address, evaluationOptions }: EvaluationParameters<{ address: string }>): Promise;
+ getBalances({ evaluationOptions }: EvaluationParameters): Promise>;
+}
+
/* eslint-disable @typescript-eslint/no-explicit-any */
export interface Logger {
setLogLevel: (level: string) => void;
diff --git a/src/common/ant.ts b/src/common/ant.ts
new file mode 100644
index 00000000..8ea7fe91
--- /dev/null
+++ b/src/common/ant.ts
@@ -0,0 +1,93 @@
+/**
+ * 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 {
+ ANTContract,
+ ANTRecord,
+ ANTState,
+ ContractConfiguration,
+ EvaluationOptions,
+ EvaluationParameters,
+ SmartWeaveContract,
+ isContractConfiguration,
+ isContractTxIdConfiguration,
+} from '../types.js';
+import { RemoteContract } from './contracts/remote-contract.js';
+
+export class ANT implements ANTContract {
+ private contract: SmartWeaveContract;
+
+ constructor(config: ContractConfiguration) {
+
+ if (isContractConfiguration(config)) {
+ this.contract = config.contract;
+ } else if (isContractTxIdConfiguration(config)) {
+ this.contract = new RemoteContract({
+ contractTxId: config.contractTxId,
+ });
+ }
+ }
+
+ /**
+ * Returns the current state of the contract.
+ */
+ async getState(params: EvaluationParameters): Promise {
+ const state = await this.contract.getContractState(params);
+ return state;
+ }
+
+ async getRecord({ domain, evaluationOptions }: EvaluationParameters<{ domain: string; }>): Promise {
+ const records = await this.getRecords({ evaluationOptions });
+
+ return records[domain]
+ }
+ async getRecords({ evaluationOptions }: { evaluationOptions?: EvaluationOptions | Record | undefined; }): Promise> {
+ const state = await this.contract.getContractState({ evaluationOptions });
+ return state.records;
+ }
+
+ async getOwner({ evaluationOptions }: { evaluationOptions?: EvaluationOptions | Record | undefined; }): Promise {
+ const state = await this.contract.getContractState({ evaluationOptions });
+ return state.owner;
+ }
+
+ async getControllers({ evaluationOptions }: { evaluationOptions?: EvaluationOptions | Record | undefined; }): Promise {
+ const state = await this.contract.getContractState({ evaluationOptions });
+ return state.controllers;
+ }
+
+ async getName({ evaluationOptions }: { evaluationOptions?: EvaluationOptions | Record | undefined; }): Promise {
+ const state = await this.contract.getContractState({ evaluationOptions });
+ return state.name;
+ }
+
+ async getTicker({ evaluationOptions }: { evaluationOptions?: EvaluationOptions | Record | undefined; }): Promise {
+ const state = await this.contract.getContractState({ evaluationOptions });
+ return state.ticker;
+ }
+
+ async getBalances({ evaluationOptions }: { evaluationOptions?: EvaluationOptions | Record | undefined; }): Promise> {
+ const state = await this.contract.getContractState({ evaluationOptions });
+ return state.balances;
+ }
+
+ async getBalance({ address, evaluationOptions }: EvaluationParameters<{ address: string; }>): Promise {
+ const balances = await this.getBalances({ evaluationOptions });
+ return balances[address];
+ }
+
+
+}
diff --git a/src/common/ar-io.ts b/src/common/ar-io.ts
index a7dbb9ac..1bd4f575 100644
--- a/src/common/ar-io.ts
+++ b/src/common/ar-io.ts
@@ -19,35 +19,18 @@ import {
ArIOContract,
ArIOState,
ArNSNameData,
+ ContractConfiguration,
EpochDistributionData,
EvaluationParameters,
Gateway,
Observations,
SmartWeaveContract,
WeightedObserver,
+ isContractConfiguration,
+ isContractTxIdConfiguration,
} from '../types.js';
import { RemoteContract } from './contracts/remote-contract.js';
-// TODO: append this with other configuration options (e.g. local vs. remote evaluation)
-export type ContractConfiguration =
- | {
- contract?: SmartWeaveContract;
- }
- | {
- contractTxId: string;
- };
-
-function isContractConfiguration(
- config: ContractConfiguration,
-): config is { contract: SmartWeaveContract } {
- return 'contract' in config;
-}
-
-function isContractTxIdConfiguration(
- config: ContractConfiguration,
-): config is { contractTxId: string } {
- return 'contractTxId' in config;
-}
export class ArIO implements ArIOContract {
private contract: SmartWeaveContract;
diff --git a/src/contract-state.ts b/src/contract-state.ts
index 6dbece4b..8b15b55c 100644
--- a/src/contract-state.ts
+++ b/src/contract-state.ts
@@ -181,3 +181,20 @@ export interface ArIOState {
vaults: RegistryVaults;
prescribedObservers: PrescribedObservers;
}
+
+
+// ANT
+
+export type ANTRecord = {
+ transactionId: string;
+ ttlSeconds: number;
+};
+
+export type ANTState = {
+ owner: WalletAddress;
+ controllers: WalletAddress[];
+ name: string;
+ ticker: string;
+ records: Record;
+ balances: Balances;
+};
\ No newline at end of file
diff --git a/tests/ant.test.ts b/tests/ant.test.ts
new file mode 100644
index 00000000..c4e2d2a9
--- /dev/null
+++ b/tests/ant.test.ts
@@ -0,0 +1,64 @@
+import { ANT } from '../src/common/ant';
+import { RemoteContract } from '../src/common/contracts/remote-contract';
+import { ANTState } from '../src/contract-state';
+
+
+describe('ANT contract apis', () => {
+
+ const ant = new ANT({
+ contract: new RemoteContract({
+ url: process.env.REMOTE_CACHE_URL || 'http://localhost:3000',
+ contractTxId: "UC2zwawQoTnh0TNd9mYLQS4wObBBeaOU5LPQTNETqA4",
+ }),
+ });
+
+ const sortKey = "000001383961,0000000000000,13987aba2d71b6229989690c15d2838a4deef0a90c3fc9e4d7227ed17e35d0bd";
+ const blockHeight = 1383961;
+
+ it.each([[{ sortKey }], [{ blockHeight }]])(`should get contract state with evaluation options: ${JSON.stringify('%s')}`, async (evalTo) => {
+ const state = await ant.getState({ evaluationOptions: { evalTo } });
+ expect(state).toBeDefined();
+ });
+
+ it.each([[{ sortKey }], [{ blockHeight }]])(`should get record: ${JSON.stringify('%s')}`, async (evalTo) => {
+ const record = await ant.getRecord({ domain: "@", evaluationOptions: { evalTo } });
+ expect(record).toBeDefined();
+ });
+
+ it.each([[{ sortKey }], [{ blockHeight }]])(`should get records with evaluation options: ${JSON.stringify('%s')}`, async (evalTo) => {
+ const records = await ant.getRecords({ evaluationOptions: { evalTo } });
+ console.dir({ records: records['@'] }, { depth: 4 })
+ expect(records).toBeDefined();
+ });
+
+ it.each([[{ sortKey }], [{ blockHeight }]])(`should get owner with evaluation options: ${JSON.stringify('%s')}`, async (evalTo) => {
+ const owner = await ant.getOwner({ evaluationOptions: { evalTo } });
+ expect(owner).toBeDefined();
+ });
+
+ it.each([[{ sortKey }], [{ blockHeight }]])(`should get controllers with evaluation options: ${JSON.stringify('%s')}`, async (evalTo) => {
+ const controllers = await ant.getControllers({ evaluationOptions: { evalTo } });
+ expect(controllers).toBeDefined();
+ });
+
+ it.each([[{ sortKey }], [{ blockHeight }]])(`should get name with evaluation options: ${JSON.stringify('%s')}`, async (evalTo) => {
+ const state = await ant.getName({ evaluationOptions: { evalTo } });
+ expect(state).toBeDefined();
+ });
+
+ it.each([[{ sortKey }], [{ blockHeight }]])(`should get ticker with evaluation options: ${JSON.stringify('%s')}`, async (evalTo) => {
+ const state = await ant.getTicker({ evaluationOptions: { evalTo } });
+ expect(state).toBeDefined();
+ });
+
+ it.each([[{ sortKey }], [{ blockHeight }]])(`should get balances with evaluation options: ${JSON.stringify('%s')}`, async (evalTo) => {
+ const state = await ant.getBalances({ evaluationOptions: { evalTo } });
+ expect(state).toBeDefined();
+ });
+
+ it.each([[{ sortKey }], [{ blockHeight }]])(`should get balance with evaluation options: ${JSON.stringify('%s')}`, async (evalTo) => {
+ const state = await ant.getBalance({ address: "TRVCopHzzO1VSwRUUS8umkiO2MpAL53XtVGlLaJuI94", evaluationOptions: { evalTo } });
+ expect(state).toBeDefined();
+ });
+
+})
\ No newline at end of file