Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(PE-5742): add records api to arns remote cache #8

Merged
merged 12 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,17 @@ import { ArIO } from '@ar-io/sdk';

const arIO = new ArIO({});
const address = 'QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ';
// testnet
const testnetBalance = await arIO.testnet.getBalance({ address });
const testnetGateway = await arIO.testnet.getGateway({ address });
// mainnet
const domain = 'ardrive';
const contractTxIds = [
'I-cxQhfh0Zb9UqQNizC9PiLC41KpUeA9hjiVV02rQRw',
'DGWp8_6c1YywKgCgBFhncMglciQyCdfX1swil4qjNSc',
];

dtfiedler marked this conversation as resolved.
Show resolved Hide resolved
const balance = await arIO.mainnet.getBalance({ address });
const gateway = await arIO.mainnet.getGateway({ address });
const record = await arIO.mainnet.getRecord({ domain });
const records = await arIO.mainnet.getRecords({ contractTxIds });
const allRecords = await arIO.mainnet.getRecords({});
atticusofsparta marked this conversation as resolved.
Show resolved Hide resolved
```

## Usage
Expand Down Expand Up @@ -94,12 +99,14 @@ Types are exported from `./lib/types/[node/web]/index.d.ts` and should be automa

The contract that the following methods retrieve data from are determined by the `testnet` or `devnet` clients - see examples above for implementation details.

| Method Name | Description |
| ------------------------- | ----------------------------------------------- |
| `getBalance({ address })` | Retrieves the balance of the specified address. |
| `getBalances()` | Retrieves all balances on the ArIO contract. |
| `getGateway({ address })` | Retrieves the specified gateway by address. |
| `getGateways()` | Retrieves all gateways. |
| Method Name | Description |
| ------------------------- | ----------------------------------------------------- |
| `getBalance({ address })` | Retrieves the balance of the specified address. |
| `getBalances()` | Retrieves all balances on the ArIO contract. |
| `getGateway({ address })` | Retrieves the specified gateway by address. |
| `getGateways()` | Retrieves all gateways. |
| `getRecord({ domain })` | Retrieves a specified ArNS record by the domain name. |
| `getRecords()` | Retrieves all records |

dtfiedler marked this conversation as resolved.
Show resolved Hide resolved
## Developers

Expand Down
61 changes: 41 additions & 20 deletions src/common/caches/arns-remote-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import { ARNS_TESTNET_REGISTRY_TX } from '../../constants.js';
import {
ArIOContract,
ArNSNameData,
ArNSStateResponse,
ContractCache,
Gateway,
Expand All @@ -26,6 +27,14 @@ import { NotFound } from '../error.js';
import { AxiosHTTPService } from '../http.js';
import { DefaultLogger } from '../logger.js';

const validateContractTxId = (contractTxId: string) => {
if (!contractTxId) {
throw new Error(
'Contract TxId not set, set one before calling this function.',
);
}
};

export class ArNSRemoteCache implements ContractCache, ArIOContract {
private contractTxId: string;
private logger: DefaultLogger;
Expand Down Expand Up @@ -55,11 +64,8 @@ export class ArNSRemoteCache implements ContractCache, ArIOContract {
}

async getGateway({ address }: { address: string }) {
if (!this.contractTxId) {
throw new Error(
'Contract TxId not set, set one before calling this function.',
);
}
validateContractTxId(this.contractTxId);
dtfiedler marked this conversation as resolved.
Show resolved Hide resolved

this.logger.debug(`Fetching gateway ${address}`);
const gateway = await this.getGateways().then((gateways) => {
if (gateways[address] === undefined) {
Expand All @@ -71,11 +77,8 @@ export class ArNSRemoteCache implements ContractCache, ArIOContract {
}

async getGateways() {
if (!this.contractTxId) {
throw new Error(
'Contract TxId not set, set one before calling this function.',
);
}
validateContractTxId(this.contractTxId);

this.logger.debug(`Fetching gateways`);
const { result } = await this.http.get<
ArNSStateResponse<'result', Record<string, Gateway>>
Expand All @@ -86,11 +89,8 @@ export class ArNSRemoteCache implements ContractCache, ArIOContract {
}

async getBalance({ address }: { address: string }) {
if (!this.contractTxId) {
throw new Error(
'Contract TxId not set, set one before calling this function.',
);
}
validateContractTxId(this.contractTxId);

this.logger.debug(`Fetching balance for ${address}`);
const { result } = await this.http
.get<ArNSStateResponse<'result', number>>({
Expand All @@ -106,11 +106,8 @@ export class ArNSRemoteCache implements ContractCache, ArIOContract {
}

async getBalances() {
if (!this.contractTxId) {
throw new Error(
'Contract TxId not set, set one before calling this function.',
);
}
validateContractTxId(this.contractTxId);

this.logger.debug(`Fetching balances`);
const { result } = await this.http.get<
ArNSStateResponse<'result', Record<string, number>>
Expand All @@ -119,4 +116,28 @@ export class ArNSRemoteCache implements ContractCache, ArIOContract {
});
return result;
}

async getRecord({ domain }: { domain: string }): Promise<ArNSNameData> {
validateContractTxId(this.contractTxId);

this.logger.debug(`Fetching record for ${domain}`);
const { result } = await this.http.get<
ArNSStateResponse<'result', ArNSNameData>
>({
endpoint: `/contract/${this.contractTxId.toString()}/state/records/${domain}`,
});
return result;
}

async getRecords(): Promise<Record<string, ArNSNameData>> {
validateContractTxId(this.contractTxId);

this.logger.debug(`Fetching all records`);
const { result } = await this.http.get<
ArNSStateResponse<'result', Record<string, ArNSNameData>>
>({
endpoint: `/contract/${this.contractTxId.toString()}/state/records`,
});
return result;
}
}
4 changes: 3 additions & 1 deletion src/types/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Gateway } from './contract-state.js';
import { ArNSNameData, Gateway } from './contract-state.js';

export interface ContractCache {
/**
Expand All @@ -28,6 +28,8 @@ export interface ArIOContract {
getGateways(): Promise<Record<WalletAddress, Gateway>>;
getBalance({ address }: { address: WalletAddress }): Promise<number>;
getBalances(): Promise<Record<WalletAddress, number>>;
getRecord({ domain }: { domain: string }): Promise<ArNSNameData>;
getRecords(): Promise<Record<string, ArNSNameData>>;
}

/* eslint-disable @typescript-eslint/no-explicit-any */
Expand Down
25 changes: 25 additions & 0 deletions tests/arns-remote-cache.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { NotFound } from '../src/common/error.js';
describe('ArNSRemoteCache', () => {
const remoteCacheProvider = new ArNSRemoteCache({});

// gateway tests
it('should be able to fetch gateways', async () => {
const gateways = await remoteCacheProvider.getGateways();
expect(gateways).toBeDefined();
Expand All @@ -18,6 +19,7 @@ describe('ArNSRemoteCache', () => {
expect(error).toBeInstanceOf(NotFound);
});

// balance tests
it('should fetch a balance', async () => {
const balance = await remoteCacheProvider.getBalance({
address: 'some-address',
Expand All @@ -29,4 +31,27 @@ describe('ArNSRemoteCache', () => {
const balances = await remoteCacheProvider.getBalances();
expect(balances).toBeDefined();
});

// records tests
it('should fetch a record', async () => {
const record = await remoteCacheProvider.getRecord({
domain: 'ar-io',
});
expect(record).toBeDefined();
});

it('should throw NotFound error on non existent record', async () => {
const error = await remoteCacheProvider
.getRecord({
domain: 'some-domain',
})
.catch((e) => e);
expect(error).toBeInstanceOf(NotFound);
});

it('should fetch all records', async () => {
const records = await remoteCacheProvider.getRecords();

expect(records).toBeDefined();
});
});