Skip to content

Commit

Permalink
feat(PE-5742): add records api to arns remote cache (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
atticusofsparta committed Feb 27, 2024
2 parents bf651bb + 5ba1175 commit c46cd39
Show file tree
Hide file tree
Showing 7 changed files with 227 additions and 47 deletions.
65 changes: 52 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,7 @@ yarn add @ar-io/sdk
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 balance = await arIO.mainnet.getBalance({ address });
const gateway = await arIO.mainnet.getGateway({ address });
const gateways = arIO.testnet.getGateways();
```

## Usage
Expand Down Expand Up @@ -94,12 +88,57 @@ 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. |
#### `getBalance({ address })`

Retrieves the balance of the specified address.

```typescript
const balance = new ArIO({}).testnet.getBalance({
address: 'INSERT_WALLET_ADDRESS',
});
```

#### `getBalances()`

Retrieves the balances of the ArIO contract.

```typescript
const balances = new ArIO({}).testnet.getBalances();
```

#### `getGateway({ address })`

Retrieves the gateway info of the specified address.

```typescript
const gateway = new ArIO({}).testnet.getGateway({
address: 'INSERT_GATEWAY_ADDRESS',
});
```

#### `getGateways()`

Retrieves the registered gateways of the ArIO contract.

```typescript
const gateways = new ArIO({}).testnet.getGateways();
```

#### `getRecord({ domain })`

Retrieves the domain info of the specified ArNS record.

```typescript
const record = new ArIO({}).testnet.getRecord({ domain: 'INSERT_ARNS_NAME' });
```

#### `getRecords()`

Retrieves the registered ArNS domains of the ArIO contract.

```typescript
const records = new ArIO({}).testnet.getRecords();
```

## Developers

Expand Down
25 changes: 21 additions & 4 deletions examples/node/index.cjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,28 @@
const { ArIO } = require('../../lib/cjs/node/index.js');
const {
ArIO,
ARNS_TESTNET_REGISTRY_TX,
} = require('../../lib/cjs/node/index.js');

(async () => {
const arIO = new ArIO({});
// testnet gateways
const testnetGateways = await arIO.testnet.getGateways();
// devnet gateways
const devnetGateways = await arIO.devnet.getGateways();
const protocolBalance = await arIO.testnet.getBalance({
address: ARNS_TESTNET_REGISTRY_TX,
});
const ardriveRecord = await arIO.testnet.getRecord({ domain: 'ardrive' });
const allRecords = await arIO.testnet.getRecords();

console.dir({ testnetGateways, devnetGateways }, { depth: 2 });
console.dir(
{
testnetGateways,
ardriveRecord,
protocolBalance,
arnsStats: {
'registered domains': Object.keys(allRecords).length,
ardrive: allRecords.ardrive,
},
},
{ depth: 2 },
);
})();
22 changes: 18 additions & 4 deletions examples/node/index.mjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
import { ArIO } from '../../lib/esm/node/index.js';
import { ARNS_TESTNET_REGISTRY_TX, ArIO } from '../../lib/esm/node/index.js';

(async () => {
const arIO = new ArIO({});
// testnet gateways
const testnetGateways = await arIO.testnet.getGateways();
// devnet gateways
const devnetGateways = await arIO.devnet.getGateways();
const protocolBalance = await arIO.testnet.getBalance({
address: ARNS_TESTNET_REGISTRY_TX,
});
const ardriveRecord = await arIO.testnet.getRecord({ domain: 'ardrive' });
const allRecords = await arIO.testnet.getRecords();

console.dir({ testnetGateways, devnetGateways }, { depth: 2 });
console.dir(
{
testnetGateways,
ardriveRecord,
protocolBalance,
arnsStats: {
'registered domains': Object.keys(allRecords).length,
ardrive: allRecords.ardrive,
},
},
{ depth: 2 },
);
})();
103 changes: 79 additions & 24 deletions examples/web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@
};
</script>
</head>
<body class="bg-background flex flex-col items-center p-10">
<body class="bg-background flex flex-col items-center p-10 gap-10">
<!-- gateways -->
<div
class="bg-surface flex flex-col gap-5 items-end justify-center p-5 rounded-md h-full"
style="width: 750px"
style="width: 750px; height: 500px"
>
<h1 class="text-textPrimary w-full font-bold">Browse Gateways</h1>
<div class="h-full w-full" style="overflow-y: scroll">
<table class="w-full bg-background text-textPrimary">
<thead>
Expand All @@ -41,48 +43,101 @@
</tbody>
</table>
</div>
<button
id="fetch-gateways-button"
class="animate-bounce bg-primary text-textPrimary p-2 rounded-md w-full text-surface hover:shadow-xl"
style="color: black"
>
Fetch Gateways
</button>
</div>
<!-- balance -->
<div
class="bg-surface flex flex-col gap-5 items-end justify-center p-5 rounded-md"
style="width: 750px"
>
<h1 class="w-full text-textPrimary font-bold">
Get Balance of address 7waR8v4STuwPnTck1zFVkQqJh5K9q9Zik4Y5-5dV7nk
</h1>
<div id="balance-result" class="text-primary mt-2">Loading...</div>
</div>
<!-- record -->
<div
class="bg-surface flex flex-col gap-5 items-end justify-center p-5 rounded-md"
style="width: 750px"
>
<h1 class="w-full text-textPrimary font-bold">Get Record "ardrive"</h1>
<div id="record-result" class="text-primary mt-2">Loading...</div>
</div>
<!-- get all records -->
<div
class="bg-surface flex flex-col gap-5 items-end justify-center p-5 rounded-md h-full"
style="width: 750px; height: 500px"
>
<h1 class="text-textPrimary w-full font-bold">Browse Records</h1>
<div class="h-full w-full" style="overflow-y: scroll">
<table class="w-full bg-background text-textPrimary">
<thead>
<tr>
<th class="px-4 py-2">Domain</th>
<th class="px-4 py-2">ANT</th>
<th class="px-4 py-2">Purchase type</th>
</tr>
</thead>
<tbody id="records-table-body">
<!-- Add more rows as needed -->
</tbody>
</table>
</div>
</div>
<script type="module">
import { ArIO } from './web.bundle.min.js';

// set up our client
const arIO = new ArIO({}).testnet;

async function setGateways() {
const tableBody = document.getElementById('table-body');
tableBody.innerHTML = `
<tr>
<td class="border border-surface px-4 py-2">Loading...</td>
</tr>
`;
// fetch data on page load
async function init() {
const gateways = await arIO.getGateways();
const balance = await arIO.getBalance({
address: '7waR8v4STuwPnTck1zFVkQqJh5K9q9Zik4Y5-5dV7nk',
});
const record = await arIO.getRecord({ domain: 'ardrive' });
const records = await arIO.getRecords();

// update the UI

tableBody.innerHTML = Object.entries(gateways)
document.getElementById('table-body').innerHTML = Object.entries(
gateways,
)
.map(([gatewayOwner, gateway]) => {
return `
<tr>
<td class="border border-surface px-4 py-2 text-primary"><a href="https://${gateway.settings.fqdn}" target="_blank">${gateway.settings.fqdn}</a></td>
<td class="border border-surface px-4 py-2 text-primary" style="width: fit-content"><a href="https://${gateway.settings.fqdn}" target="_blank">${gateway.settings.fqdn}</a></td>
<td class="border border-surface px-4 py-2 text-primary"><a href="https://arscan.io/address/${gatewayOwner}" target="_blank">${gatewayOwner}</a></td>
<td class="border border-surface px-4 py-2">${gateway.operatorStake} IO</td>
</tr>
`;
})
.join('');

document.getElementById('balance-result').textContent =
`Balance: ${balance} IO`;

document.getElementById('record-result').textContent = JSON.stringify(
record,
null,
2,
);

document.getElementById('records-table-body').innerHTML =
Object.entries(records)
.map(([domain, record]) => {
return `
<tr>
<td class="border border-surface px-4 py-2 text-primary"><a href="https://${domain}.arweave.dev" target="_blank">${domain}</a></td>
<td class="border border-surface px-4 py-2 text-primary"><a href="https://arscan.io/tx/${record.contractTxId}" target="_blank">${record.contractTxId}</a></td>
<td class="border border-surface px-4 py-2">${record.endTimestamp ? 'Lease' : 'Permanent'}</td>
</tr>
`;
})
.join('');
}

const fetchGatewaysButton = document.getElementById(
'fetch-gateways-button',
);
fetchGatewaysButton.addEventListener('click', () => {
setGateways();
});
window.addEventListener('load', init);
</script>
</body>
</html>
30 changes: 29 additions & 1 deletion src/common/caches/arns-remote-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
* 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 { ARNS_TESTNET_REGISTRY_TX } from '../../constants.js';
import { ARNS_TESTNET_REGISTRY_TX, ARWEAVE_TX_REGEX } from '../../constants.js';
import {
ArIOContract,
ArNSNameData,
ArNSStateResponse,
Gateway,
HTTPClient,
Expand All @@ -42,6 +43,7 @@ export class ArNSRemoteCache implements ArIOContract {
logger?: DefaultLogger;
contractTxId?: string;
}) {
this.validateContractTxId(contractTxId);
this.contractTxId = contractTxId;
this.logger = logger;
this.http = new AxiosHTTPService({
Expand All @@ -50,6 +52,12 @@ export class ArNSRemoteCache implements ArIOContract {
});
}

private validateContractTxId(id: string) {
if (!ARWEAVE_TX_REGEX.test(id)) {
throw new Error(`Invalid contract tx id: ${id}`);
}
}

async getGateway({ address }: { address: string }) {
this.logger.debug(`Fetching gateway ${address}`);
const gateway = await this.getGateways().then((gateways) => {
Expand Down Expand Up @@ -95,4 +103,24 @@ export class ArNSRemoteCache implements ArIOContract {
});
return result;
}

async getRecord({ domain }: { domain: string }): Promise<ArNSNameData> {
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>> {
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,14 +14,16 @@
* 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';

// TODO: extend with additional methods
export interface ArIOContract {
getGateway({ address }: { address: WalletAddress }): Promise<Gateway>;
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

0 comments on commit c46cd39

Please sign in to comment.