Skip to content

Commit

Permalink
feat(inital providers): scaffold initial providers
Browse files Browse the repository at this point in the history
  • Loading branch information
atticusofsparta committed Feb 13, 2024
1 parent 6969d3a commit 4949514
Show file tree
Hide file tree
Showing 19 changed files with 915 additions and 2,133 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ lib
**/*.cjs
**/*.mjs
examples
jest.config.ts
2 changes: 1 addition & 1 deletion bundle.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const bundle = () => {
platform: 'browser',
target: ['esnext'],
format: 'esm',
globalName: 'turbo',
globalName: 'ar-io',
plugins: [
polyfillNode({
polyfills: {
Expand Down
2 changes: 1 addition & 1 deletion examples/node/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ import { DefaultClient } from '../../lib/index.js';

(async () => {
const client = new DefaultClient();
console.log(turbo);
console.log(client);
})();
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,10 @@
"ts-node": "^10.9.1",
"typescript": "^5.1.6"
},
"repository": "https://github.com/ardriveapp/node-sdk-template.git"
"repository": "https://github.com/ardriveapp/node-sdk-template.git",
"dependencies": {
"arweave": "^1.14.4",
"fetch-retry": "^5.0.6",
"warp-contracts": "^1.4.34"
}
}
97 changes: 97 additions & 0 deletions src/common/ArIo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/**
* 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 <http://www.gnu.org/licenses/>.
*/
import Arweave from 'arweave';
import fetchBuilder from 'fetch-retry';

import { IContractStateProvider } from '../types.js';

export class ArIoError extends Error {
constructor(message: string) {
super(message);
this.name = 'ArIoError';
}
}

export class ArIo implements IContractStateProvider {
_arweave: Arweave;
_contractStateProviders: IContractStateProvider[];
http: typeof fetch;
log: (message: string) => void;

constructor({
arweave,
contractStateProviders,
logger,
}: {
arweave?: Arweave;
contractStateProviders: IContractStateProvider[];
logger?: (message: string) => void;
}) {
this._arweave = arweave ?? Arweave.init({}); // use default arweave instance if not provided
this._contractStateProviders = contractStateProviders;
this.http = fetchBuilder(fetch, {
retries: 3,
retryDelay: 2000,
retryOn: [429, 500, 502, 503, 504],
});
this.log =
logger ??
((message: string) => {
console.debug(`[ArIo Client]: ${message}`);
});
}

/**
* Fetches the state of a contract from the Arweave network.
* @param contractId - The contract ID to fetch the state for.
* @param strategy - The strategy to use when fetching the state - 'race', 'compare', or 'fallback'.
* - 'race' will call each provider and return the first result.
* - 'compare' will call each provider and return the result that has the highest blockheight evaluated.
* - 'fallback' will call first remote providers, then gql providers if remote fetch failed.
* @returns The state of the contract.
*
* @example
* const state = await ario.getContractState('contractId', 'fallback');
*/
async getContractState<ContractState>(
contractId: string,
strategy: 'race' | 'compare' | 'fallback' = 'race',
): Promise<ContractState> {
this.log(
`Fetching contract state for contract [${contractId}] using a ${strategy} strategy `,
);
switch (strategy) {
case 'race':
return Promise.race(
this._contractStateProviders.map((provider) =>
provider.getContractState<ContractState>(contractId),
),
);
case 'compare':
// TODO: implement compare strategy
throw new Error('Not implemented');
case 'fallback':
// TODO: implement fallback strategy
throw new Error('Not implemented');
default: {
const message = `Invalid strategy provided for contract [${contractId}]: ${strategy}`;
this.log(message);
throw new ArIoError(message);
}
}
}
}
108 changes: 108 additions & 0 deletions src/common/ContractStateProviders/ArNSRemoteCache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/**
* 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 <http://www.gnu.org/licenses/>.
*/
import fetchBuilder from 'fetch-retry';

import { IContractStateProvider } from '../../types.js';
import { validateArweaveId } from '../../utils/index.js';

export class ArNSRemoteCacheError extends Error {
constructor(message: string) {
super(message);
this.name = 'ArNSRemoteCacheError';
}
}

/**
* ArNSRemoteCache class implements the IContractStateProvider interface.
* It provides methods to interact with a remote ArNS SmartWeave State Evaluator.
*
* @property {string} remoteCacheUrl - The URL of the remote cache. Defaults to 'api.arns.app'.
* @property {string} apiVersion - The API version to use for the remote cache. Defaults to 'v1'.
* @property {(message: string) => void} log - A logging function. If not provided, it defaults to a function that logs debug messages to the console.
* @property {typeof fetch} http - A fetch function with retry capabilities.
* @property {Object} httpOptions - Options to pass to the fetch function.
*
* @example
* const cache = new ArNSRemoteCache({}) || new ArNSRemoteCache({
* url: 'https://example.com/cache',
* logger: message => console.log(`Custom logger: ${message}`),
* version: 'v1',
* httpOptions: {
* retries: 3,
* retryDelay: 2000,
* retryOn: [404, 429, 503],
* },
* });
*/
export class ArNSRemoteCache implements IContractStateProvider {
remoteCacheUrl: string;
apiVersion: string;
log: (message: string) => void;
http: typeof fetch;
constructor({
url = 'api.arns.app',
logger,
version = 'v1',
httpOptions = {
retries: 3,
retryDelay: 2000,
retryOn: [404, 429, 503],
},
}: {
url?: string;
logger?: (message: string) => void;
version?: string;
httpOptions?: Parameters<typeof fetchBuilder>[1];
}) {
this.remoteCacheUrl = url;
this.apiVersion = version;
this.log =
logger ??
((message: string) => {
console.debug(`[ArNS Remote Cache]: ${message}`);
});
this.http = fetchBuilder(fetch, httpOptions);
}

/**
* Fetches the state of a contract from the remote cache.
* @param {string} contractId - The Arweave transaction id of the contract.
*/
async getContractState<ContractState>(
contractId: string,
): Promise<ContractState> {
validateArweaveId(contractId);

this.log(`Fetching contract state for [${contractId}]`);

const response = await this.http(
`${this.remoteCacheUrl}/${this.apiVersion}/contract/${contractId}`,
).catch((error) => {
const message = `Failed to fetch contract state for [${contractId}]: ${error}`;

this.log(message);

throw new ArNSRemoteCacheError(message);
});

this.log(
`Fetched contract state for [${contractId}]. State size: ${response.headers.get('content-length')} bytes.`,
);

return response.json();
}
}
4 changes: 1 addition & 3 deletions src/index.ts → src/common/ContractStateProviders/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,4 @@
* 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/>.
*/

// eslint-disable-next-line
export class DefaultClient {}
export * from './ArNSRemoteCache.js';
18 changes: 18 additions & 0 deletions src/common/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* 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 <http://www.gnu.org/licenses/>.
*/
export * from './ArIo.js';
export * from './ContractStateProviders/index.js';
18 changes: 18 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* 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 <http://www.gnu.org/licenses/>.
*/

export const ARWEAVE_TX_REGEX = new RegExp('^[a-zA-Z0-9_-]{43}$');
19 changes: 19 additions & 0 deletions src/node/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* 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 <http://www.gnu.org/licenses/>.
*/
export * from '../types.js';
export * from '../common/index.js';
export * from '../constants.js';
23 changes: 23 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* 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 <http://www.gnu.org/licenses/>.
*/

export interface IContractStateProvider {
/**
* The ContractStateProvider interface is used to define a contract state provider.
*/
getContractState<T>(contractId: string): Promise<T>;
}
25 changes: 25 additions & 0 deletions src/utils/arweave.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* 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 <http://www.gnu.org/licenses/>.
*/
import { ARWEAVE_TX_REGEX } from '../constants.js';

export const validateArweaveId = (id: string): void => {
if (!ARWEAVE_TX_REGEX.test(id)) {
throw new Error(
`Invalid contract id: [${id}]. Must be a valid Arweave transaction id.`,
);
}
};
17 changes: 17 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* 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 <http://www.gnu.org/licenses/>.
*/
export * from './arweave.js';
19 changes: 19 additions & 0 deletions src/web/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* 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 <http://www.gnu.org/licenses/>.
*/
export * from '../types.js';
export * from '../common/index.js';
export * from '../constants.js';

0 comments on commit 4949514

Please sign in to comment.