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 all 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
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
Loading