From 4208bd023c9f1026a49a5bea3a06beb51a3494b7 Mon Sep 17 00:00:00 2001 From: atticusofsparta Date: Fri, 1 Mar 2024 13:36:36 -0600 Subject: [PATCH 01/15] fix(tests): update ario test --- README.md | 6 +++++- tests/ar-io.test.ts | 2 -- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index af2ec2bc..fc393d75 100644 --- a/README.md +++ b/README.md @@ -182,10 +182,14 @@ const balance = arIO.getBalance({ Retrieves the balances of the ArIO contract. -```typescript + + +```typescript + const arIO = new ArIO(); const balances = arIO.getBalances(); // output diff --git a/tests/ar-io.test.ts b/tests/ar-io.test.ts index cce59a37..b17a45c7 100644 --- a/tests/ar-io.test.ts +++ b/tests/ar-io.test.ts @@ -5,7 +5,5 @@ describe('ArIO Client', () => { const arioClient = new ArIO(); expect(arioClient).toBeInstanceOf(ArIO); - expect(arioClient.testnet).toBeDefined(); - expect(arioClient.devnet).toBeDefined(); }); }); From a266731e14b480d04e3be1fa558de368f366682d Mon Sep 17 00:00:00 2001 From: atticusofsparta Date: Fri, 1 Mar 2024 14:13:15 -0600 Subject: [PATCH 02/15] fix(indentation): fix indentation in examples --- README.md | 133 +++++++++++++++++++++++++++--------------------------- 1 file changed, 67 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index fc393d75..27b244a5 100644 --- a/README.md +++ b/README.md @@ -49,40 +49,40 @@ const gateways = arIO.getGateways(); // output { "QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ": { - "end": 0, - "observerWallet": "IPdwa3Mb_9pDD8c2IaJx6aad51Ss-_TfStVwBuhtXMs", - "operatorStake": 250000, - "settings": { - "fqdn": "ar-io.dev", - "label": "AR.IO Test", - "note": "Test Gateway operated by PDS for the AR.IO ecosystem.", - "port": 443, - "properties": "raJgvbFU-YAnku-WsupIdbTsqqGLQiYpGzoqk9SCVgY", - "protocol": "https" - }, - "start": 1256694, - "stats": { - "failedConsecutiveEpochs": 0, - "passedEpochCount": 30, - "submittedEpochCount": 30, - "totalEpochParticipationCount": 31, - "totalEpochsPrescribedCount": 31 - }, - "status": "joined", - "vaults": {}, - "weights": { - "stakeWeight": 25, - "tenureWeight": 0.9031327160493827, - "gatewayRewardRatioWeight": 0.96875, - "observerRewardRatioWeight": 0.96875, - "compositeWeight": 21.189222170982834, - "normalizedCompositeWeight": 0.27485583057217183 - } -}, + "end": 0, + "observerWallet": "IPdwa3Mb_9pDD8c2IaJx6aad51Ss-_TfStVwBuhtXMs", + "operatorStake": 250000, + "settings": { + "fqdn": "ar-io.dev", + "label": "AR.IO Test", + "note": "Test Gateway operated by PDS for the AR.IO ecosystem.", + "port": 443, + "properties": "raJgvbFU-YAnku-WsupIdbTsqqGLQiYpGzoqk9SCVgY", + "protocol": "https" + }, + "start": 1256694, + "stats": { + "failedConsecutiveEpochs": 0, + "passedEpochCount": 30, + "submittedEpochCount": 30, + "totalEpochParticipationCount": 31, + "totalEpochsPrescribedCount": 31 + }, + "status": "joined", + "vaults": {}, + "weights": { + "stakeWeight": 25, + "tenureWeight": 0.9031327160493827, + "gatewayRewardRatioWeight": 0.96875, + "observerRewardRatioWeight": 0.96875, + "compositeWeight": 21.189222170982834, + "normalizedCompositeWeight": 0.27485583057217183 + } + }, "-RlCrWmyn9OaJ86tsr5qhmFRc0h5ovT5xjKQwySGZy0": { -"end": 0, -"observerWallet": "-RlCrWmyn9OaJ86tsr5qhmFRc0h5ovT5xjKQwySGZy0", -"operatorStake": 11300, + "end": 0, + "observerWallet": "-RlCrWmyn9OaJ86tsr5qhmFRc0h5ovT5xjKQwySGZy0", + "operatorStake": 11300, ... } ``` @@ -255,40 +255,40 @@ const gateways = arIO.getGateways(); // output { "QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ": { - "end": 0, - "observerWallet": "IPdwa3Mb_9pDD8c2IaJx6aad51Ss-_TfStVwBuhtXMs", - "operatorStake": 250000, - "settings": { - "fqdn": "ar-io.dev", - "label": "AR.IO Test", - "note": "Test Gateway operated by PDS for the AR.IO ecosystem.", - "port": 443, - "properties": "raJgvbFU-YAnku-WsupIdbTsqqGLQiYpGzoqk9SCVgY", - "protocol": "https" - }, - "start": 1256694, - "stats": { - "failedConsecutiveEpochs": 0, - "passedEpochCount": 30, - "submittedEpochCount": 30, - "totalEpochParticipationCount": 31, - "totalEpochsPrescribedCount": 31 - }, - "status": "joined", - "vaults": {}, - "weights": { - "stakeWeight": 25, - "tenureWeight": 0.9031327160493827, - "gatewayRewardRatioWeight": 0.96875, - "observerRewardRatioWeight": 0.96875, - "compositeWeight": 21.189222170982834, - "normalizedCompositeWeight": 0.27485583057217183 - } + "end": 0, + "observerWallet": "IPdwa3Mb_9pDD8c2IaJx6aad51Ss-_TfStVwBuhtXMs", + "operatorStake": 250000, + "settings": { + "fqdn": "ar-io.dev", + "label": "AR.IO Test", + "note": "Test Gateway operated by PDS for the AR.IO ecosystem.", + "port": 443, + "properties": "raJgvbFU-YAnku-WsupIdbTsqqGLQiYpGzoqk9SCVgY", + "protocol": "https" + }, + "start": 1256694, + "stats": { + "failedConsecutiveEpochs": 0, + "passedEpochCount": 30, + "submittedEpochCount": 30, + "totalEpochParticipationCount": 31, + "totalEpochsPrescribedCount": 31 + }, + "status": "joined", + "vaults": {}, + "weights": { + "stakeWeight": 25, + "tenureWeight": 0.9031327160493827, + "gatewayRewardRatioWeight": 0.96875, + "observerRewardRatioWeight": 0.96875, + "compositeWeight": 21.189222170982834, + "normalizedCompositeWeight": 0.27485583057217183 + } }, "-RlCrWmyn9OaJ86tsr5qhmFRc0h5ovT5xjKQwySGZy0": { -"end": 0, -"observerWallet": "-RlCrWmyn9OaJ86tsr5qhmFRc0h5ovT5xjKQwySGZy0", -"operatorStake": 11300, + "end": 0, + "observerWallet": "-RlCrWmyn9OaJ86tsr5qhmFRc0h5ovT5xjKQwySGZy0", + "operatorStake": 11300, ... } ``` @@ -299,7 +299,7 @@ Retrieves the domain info of the specified ArNS record. ```typescript const arIO = new ArIO(); -const record = arIO.getArNSRecord({ domain: 'INSERT_ARNS_NAME' }); +const record = arIO.getArNSRecord({ domain: 'ardrive' }); // output { "contractTxId": "bh9l1cy0aksiL_x9M359faGzM_yjralacHIUo8_nQXM", @@ -334,6 +334,7 @@ const records = arIO.getArNSRecords(); "type": "permabuy", "undernames": 10 } + ... } ``` From 77c68429af5e5a50f4ed2e8bd2bf7fdc67b8a2a6 Mon Sep 17 00:00:00 2001 From: atticusofsparta Date: Fri, 1 Mar 2024 14:19:43 -0600 Subject: [PATCH 03/15] fix(example web): update ario instatiation --- examples/web/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/web/index.html b/examples/web/index.html index 0c4375e9..8cea5943 100644 --- a/examples/web/index.html +++ b/examples/web/index.html @@ -87,7 +87,7 @@

Browse Records

import { ArIO } from './web.bundle.min.js'; // set up our client - const arIO = new ArIO().testnet; + const arIO = new ArIO(); // fetch data on page load async function init() { From ecc07f7f5c89fd8fbe66d2b79db44a62285057d4 Mon Sep 17 00:00:00 2001 From: Atticus Date: Tue, 5 Mar 2024 16:42:04 -0500 Subject: [PATCH 04/15] fix(readme): add grammar and example recs --- README.md | 357 ++++++++++++++++++++++++++---------------------------- 1 file changed, 174 insertions(+), 183 deletions(-) diff --git a/README.md b/README.md index 27b244a5..ef9cb6ad 100644 --- a/README.md +++ b/README.md @@ -46,45 +46,46 @@ import { ArIO } from '@ar-io/sdk'; const arIO = new ArIO(); const gateways = arIO.getGateways(); -// output -{ - "QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ": { - "end": 0, - "observerWallet": "IPdwa3Mb_9pDD8c2IaJx6aad51Ss-_TfStVwBuhtXMs", - "operatorStake": 250000, - "settings": { - "fqdn": "ar-io.dev", - "label": "AR.IO Test", - "note": "Test Gateway operated by PDS for the AR.IO ecosystem.", - "port": 443, - "properties": "raJgvbFU-YAnku-WsupIdbTsqqGLQiYpGzoqk9SCVgY", - "protocol": "https" - }, - "start": 1256694, - "stats": { - "failedConsecutiveEpochs": 0, - "passedEpochCount": 30, - "submittedEpochCount": 30, - "totalEpochParticipationCount": 31, - "totalEpochsPrescribedCount": 31 - }, - "status": "joined", - "vaults": {}, - "weights": { - "stakeWeight": 25, - "tenureWeight": 0.9031327160493827, - "gatewayRewardRatioWeight": 0.96875, - "observerRewardRatioWeight": 0.96875, - "compositeWeight": 21.189222170982834, - "normalizedCompositeWeight": 0.27485583057217183 - } - }, -"-RlCrWmyn9OaJ86tsr5qhmFRc0h5ovT5xjKQwySGZy0": { - "end": 0, - "observerWallet": "-RlCrWmyn9OaJ86tsr5qhmFRc0h5ovT5xjKQwySGZy0", - "operatorStake": 11300, -... -} +// outputs: + +// { +// "QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ": { +// "end": 0, +// "observerWallet": "IPdwa3Mb_9pDD8c2IaJx6aad51Ss-_TfStVwBuhtXMs", +// "operatorStake": 250000, +// "settings": { +// "fqdn": "ar-io.dev", +// "label": "AR.IO Test", +// "note": "Test Gateway operated by PDS for the AR.IO ecosystem.", +// "port": 443, +// "properties": "raJgvbFU-YAnku-WsupIdbTsqqGLQiYpGzoqk9SCVgY", +// "protocol": "https" +// }, +// "start": 1256694, +// "stats": { +// "failedConsecutiveEpochs": 0, +// "passedEpochCount": 30, +// "submittedEpochCount": 30, +// "totalEpochParticipationCount": 31, +// "totalEpochsPrescribedCount": 31 +// }, +// "status": "joined", +// "vaults": {}, +// "weights": { +// "stakeWeight": 25, +// "tenureWeight": 0.9031327160493827, +// "gatewayRewardRatioWeight": 0.96875, +// "observerRewardRatioWeight": 0.96875, +// "compositeWeight": 21.189222170982834, +// "normalizedCompositeWeight": 0.27485583057217183 +// } +// }, +// "-RlCrWmyn9OaJ86tsr5qhmFRc0h5ovT5xjKQwySGZy0": { +// "end": 0, +// "observerWallet": "-RlCrWmyn9OaJ86tsr5qhmFRc0h5ovT5xjKQwySGZy0", +// "operatorStake": 11300, +// ... +// } ``` # Usage @@ -130,52 +131,39 @@ The SDK provides TypeScript types. When you import the SDK in a TypeScript proje Types are exported from `./lib/types/[node/web]/index.d.ts` and should be automatically recognized by package managers, offering benefits such as type-checking and autocompletion. -## APIs +## Configuration -### Warp - -The SDK offers pass-throughs to warp evaluation options and will use the contracts configured evaluation options by default. - - - -### Remote Cache vs Local Cache (Warp and other GQL based evaluators) - -For reading contract state it is faster to use a remote cache that evaluates contract state. The tradeoff here is you are trusting the remote evaluator both accurately evaluates the state, and does not have a corrupted cached state. - -On remote caches you sacrifice trustlessness (depending on the cache) for fast response times, with local evaluation you sacrifice fast response times (taking potentially hours to evaluate the contract) for trustless evaluation. +### Contract Data Provider Configuration - - -### Caching Configurations +```typescript +const arIoContractId = 'INSERT_CUSTOM_REGISTRY_CONTRACT_ID'; -if running the a local contract cache you can point to both the local url of the cache and a locally deployed, or live deployed arweave mainnet contract +const contractDataProvider = new ArNSRemoteCache({ + arIoContractId, + remoteCacheUrl: 'http://localhost:3000', +}); -```typescript const arIOLocal = new ArIO({ - cacheConfig: { - remoteCacheUrl: 'http://localhost:3000', - contractTxId: 'INSERT_CUSTOM_REGISTRY_CONTRACT_ID', - }, + arIoContractId, + contractDataProvider, }); ``` +## APIs + #### `getBalance({ address })` -Retrieves the balance of the specified address. +Retrieves the balance of the specified wallet address. ```typescript const arIO = new ArIO(); const balance = arIO.getBalance({ address: 'INSERT_WALLET_ADDRESS', }); -// output -0; + +// outputs: 0 ``` #### `getBalances()` @@ -192,56 +180,60 @@ Retrieves the balances of the ArIO contract. const arIO = new ArIO(); const balances = arIO.getBalances(); -// output -{ - "-4xgjroXENKYhTWqrBo57HQwvDL51mMvSxJy6Y2Z_sA": 5000, - "-7vXsQZQDk8TMDlpiSLy3CnLi5PDPlAaN2DaynORpck": 5000, - "-9JU3W8g9nOAB1OrJQ8FxkaWCpv5slBET2HppTItbmk": 5000, - ... -} + +// outputs: + +// { +// "-4xgjroXENKYhTWqrBo57HQwvDL51mMvSxJy6Y2Z_sA": 5000, +// "-7vXsQZQDk8TMDlpiSLy3CnLi5PDPlAaN2DaynORpck": 5000, +// "-9JU3W8g9nOAB1OrJQ8FxkaWCpv5slBET2HppTItbmk": 5000, +// ... +// } ``` #### `getGateway({ address })` -Retrieves the gateway info of the specified address. +Retrieves a gateway's info by its staking wallet address. ```typescript const arIO = new ArIO(); const gateway = arIO.getGateway({ - address: 'INSERT_GATEWAY_ADDRESS', + stakingAddress: 'INSERT_GATEWAY_ADDRESS', }); -// output -{ - "end": 0, - "observerWallet": "IPdwa3Mb_9pDD8c2IaJx6aad51Ss-_TfStVwBuhtXMs", - "operatorStake": 250000, - "settings": { - "fqdn": "ar-io.dev", - "label": "AR.IO Test", - "note": "Test Gateway operated by PDS for the AR.IO ecosystem.", - "port": 443, - "properties": "raJgvbFU-YAnku-WsupIdbTsqqGLQiYpGzoqk9SCVgY", - "protocol": "https" - }, - "start": 1256694, - "stats": { - "failedConsecutiveEpochs": 0, - "passedEpochCount": 30, - "submittedEpochCount": 30, - "totalEpochParticipationCount": 31, - "totalEpochsPrescribedCount": 31 - }, - "status": "joined", - "vaults": {}, - "weights": { - "stakeWeight": 25, - "tenureWeight": 0.9031327160493827, - "gatewayRewardRatioWeight": 0.96875, - "observerRewardRatioWeight": 0.96875, - "compositeWeight": 21.189222170982834, - "normalizedCompositeWeight": 0.27485583057217183 - } -} + +// outputs: + +// { +// "end": 0, +// "observerWallet": "IPdwa3Mb_9pDD8c2IaJx6aad51Ss-_TfStVwBuhtXMs", +// "operatorStake": 250000, +// "settings": { +// "fqdn": "ar-io.dev", +// "label": "AR.IO Test", +// "note": "Test Gateway operated by PDS for the AR.IO ecosystem.", +// "port": 443, +// "properties": "raJgvbFU-YAnku-WsupIdbTsqqGLQiYpGzoqk9SCVgY", +// "protocol": "https" +// }, +// "start": 1256694, +// "stats": { +// "failedConsecutiveEpochs": 0, +// "passedEpochCount": 30, +// "submittedEpochCount": 30, +// "totalEpochParticipationCount": 31, +// "totalEpochsPrescribedCount": 31 +// }, +// "status": "joined", +// "vaults": {}, +// "weights": { +// "stakeWeight": 25, +// "tenureWeight": 0.9031327160493827, +// "gatewayRewardRatioWeight": 0.96875, +// "observerRewardRatioWeight": 0.96875, +// "compositeWeight": 21.189222170982834, +// "normalizedCompositeWeight": 0.27485583057217183 +// } +// } ``` @@ -252,100 +244,99 @@ Retrieves the registered gateways of the ArIO contract. ```typescript const arIO = new ArIO(); const gateways = arIO.getGateways(); -// output -{ - "QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ": { - "end": 0, - "observerWallet": "IPdwa3Mb_9pDD8c2IaJx6aad51Ss-_TfStVwBuhtXMs", - "operatorStake": 250000, - "settings": { - "fqdn": "ar-io.dev", - "label": "AR.IO Test", - "note": "Test Gateway operated by PDS for the AR.IO ecosystem.", - "port": 443, - "properties": "raJgvbFU-YAnku-WsupIdbTsqqGLQiYpGzoqk9SCVgY", - "protocol": "https" - }, - "start": 1256694, - "stats": { - "failedConsecutiveEpochs": 0, - "passedEpochCount": 30, - "submittedEpochCount": 30, - "totalEpochParticipationCount": 31, - "totalEpochsPrescribedCount": 31 - }, - "status": "joined", - "vaults": {}, - "weights": { - "stakeWeight": 25, - "tenureWeight": 0.9031327160493827, - "gatewayRewardRatioWeight": 0.96875, - "observerRewardRatioWeight": 0.96875, - "compositeWeight": 21.189222170982834, - "normalizedCompositeWeight": 0.27485583057217183 - } -}, -"-RlCrWmyn9OaJ86tsr5qhmFRc0h5ovT5xjKQwySGZy0": { - "end": 0, - "observerWallet": "-RlCrWmyn9OaJ86tsr5qhmFRc0h5ovT5xjKQwySGZy0", - "operatorStake": 11300, -... -} + +// outputs: + +// { +// "QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ": { +// "end": 0, +// "observerWallet": "IPdwa3Mb_9pDD8c2IaJx6aad51Ss-_TfStVwBuhtXMs", +// "operatorStake": 250000, +// "settings": { +// "fqdn": "ar-io.dev", +// "label": "AR.IO Test", +// "note": "Test Gateway operated by PDS for the AR.IO ecosystem.", +// "port": 443, +// "properties": "raJgvbFU-YAnku-WsupIdbTsqqGLQiYpGzoqk9SCVgY", +// "protocol": "https" +// }, +// "start": 1256694, +// "stats": { +// "failedConsecutiveEpochs": 0, +// "passedEpochCount": 30, +// "submittedEpochCount": 30, +// "totalEpochParticipationCount": 31, +// "totalEpochsPrescribedCount": 31 +// }, +// "status": "joined", +// "vaults": {}, +// "weights": { +// "stakeWeight": 25, +// "tenureWeight": 0.9031327160493827, +// "gatewayRewardRatioWeight": 0.96875, +// "observerRewardRatioWeight": 0.96875, +// "compositeWeight": 21.189222170982834, +// "normalizedCompositeWeight": 0.27485583057217183 +// } +// }, +// "-RlCrWmyn9OaJ86tsr5qhmFRc0h5ovT5xjKQwySGZy0": { +// "end": 0, +// "observerWallet": "-RlCrWmyn9OaJ86tsr5qhmFRc0h5ovT5xjKQwySGZy0", +// "operatorStake": 11300, +// ... +// } ``` #### `getArNSRecord({ domain })` -Retrieves the domain info of the specified ArNS record. +Retrieves the record info of the specified ArNS name. ```typescript const arIO = new ArIO(); -const record = arIO.getArNSRecord({ domain: 'ardrive' }); -// output -{ - "contractTxId": "bh9l1cy0aksiL_x9M359faGzM_yjralacHIUo8_nQXM", - "endTimestamp": 1711122739, - "startTimestamp": 1694101828, - "type": "lease", - "undernames": 100 -} +const record = arIO.getArNSRecord({ arnsName: 'ardrive' }); + +// outputs + +// { +// "contractTxId": "bh9l1cy0aksiL_x9M359faGzM_yjralacHIUo8_nQXM", +// "endTimestamp": 1711122739, +// "startTimestamp": 1694101828, +// "type": "lease", +// "undernames": 100 +// } ``` #### `getArNSRecords()` -Retrieves the registered ArNS domains of the ArIO contract. +Retrieves all registered ArNS records of the ArIO contract. ```typescript const arIO = new ArIO(); const records = arIO.getArNSRecords(); -// output - -{ - "ardrive": { - "contractTxId": "bh9l1cy0aksiL_x9M359faGzM_yjralacHIUo8_nQXM", - "endTimestamp": 1711122739, - "startTimestamp": 1694101828, - "type": "lease", - "undernames": 100 - }, - "ar-io": { - "contractTxId": "eNey-H9RB9uCdoJUvPULb35qhZVXZcEXv8xds4aHhkQ", - "purchasePrice": 17386.717520731843, - "startTimestamp": 1706747215, - "type": "permabuy", - "undernames": 10 - } - ... -} + +// outputs: + +// { +// "ardrive": { +// "contractTxId": "bh9l1cy0aksiL_x9M359faGzM_yjralacHIUo8_nQXM", +// "endTimestamp": 1711122739, +// "startTimestamp": 1694101828, +// "type": "lease", +// "undernames": 100 +// }, +// "ar-io": { +// "contractTxId": "eNey-H9RB9uCdoJUvPULb35qhZVXZcEXv8xds4aHhkQ", +// "purchasePrice": 17386.717520731843, +// "startTimestamp": 1706747215, +// "type": "permabuy", +// "undernames": 10 +// } +// ... +// } ``` ## Developers -### Requirements - -- `nvm` -- `node` (>= 18) -- `yarn` - ### Setup & Build - `yarn install` - installs dependencies From 855da2d1ce53ade61025e9a2513ac706e362e0b1 Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Tue, 5 Mar 2024 18:01:28 -0700 Subject: [PATCH 05/15] feat(contract): create new contract classes that impelement both warp and remote cache for ant contract and ar-io contracts --- src/common/ar-io.ts | 113 +++++++++++---- src/common/caches/arns-remote-cache.ts | 181 ------------------------- src/common/caches/remote-contract.ts | 88 ++++++++++++ src/common/caches/warp-contract.ts | 86 ++++++++++++ src/common/http.ts | 35 ----- src/types/common.ts | 63 +++++---- src/types/contract-state.ts | 16 ++- 7 files changed, 312 insertions(+), 270 deletions(-) delete mode 100644 src/common/caches/arns-remote-cache.ts create mode 100644 src/common/caches/remote-contract.ts create mode 100644 src/common/caches/warp-contract.ts diff --git a/src/common/ar-io.ts b/src/common/ar-io.ts index afcfc930..455bbfa8 100644 --- a/src/common/ar-io.ts +++ b/src/common/ar-io.ts @@ -14,57 +14,116 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ +import { ARNS_TESTNET_REGISTRY_TX } from '../constants.js'; import { + AntState, ArIOContract, + ArIOState, ArNSNameData, + EvaluationOptions, Gateway, - ReadInteractionFilters, + AntContract as IAntContract, + SmartWeaveContract, } from '../types/index.js'; -import { ArNSRemoteCache } from './index.js'; +import { ArIOServiceContract } from './index.js'; -export type CacheConfiguration = { - remoteCacheUrl?: string; - contractTxId?: string; -}; -export type ArIOConfiguration = { - cacheConfig?: CacheConfiguration; +export type ContractConfiguration = { + contract?: SmartWeaveContract; }; export class ArIO implements ArIOContract { - protected cache: ArIOContract; + private contract: SmartWeaveContract; - constructor({ cacheConfig }: ArIOConfiguration = {}) { - this.cache = new ArNSRemoteCache({ - contractTxId: cacheConfig?.contractTxId, - url: cacheConfig?.remoteCacheUrl, - }); + constructor({ + contract = new ArIOServiceContract({ + contractTxId: ARNS_TESTNET_REGISTRY_TX, + }), + }: ContractConfiguration) { + this.contract = contract; } - // implement ArIOContract interface + async getState(params: EvaluationOptions): Promise { + return this.contract.getContractState(params); + } async getArNSRecord( - params: { domain: string } & ReadInteractionFilters, + params: { domain: string } & EvaluationOptions, ): Promise { - return this.cache.getArNSRecord(params); + const records = await this.getArNSRecords(params); + return records[params.domain]; } async getArNSRecords( - params: ReadInteractionFilters, + params: EvaluationOptions, ): Promise> { - return this.cache.getArNSRecords(params); + const state = await this.contract.getContractState(params); + return state.records; } async getBalance( - params: { address: string } & ReadInteractionFilters, + params: { address: string } & EvaluationOptions, ): Promise { - return this.cache.getBalance(params); + const balances = await this.getBalances(params); + return balances[params.address] || 0; } - async getBalances(): Promise> { - return this.cache.getBalances(); + async getBalances( + params: EvaluationOptions, + ): Promise> { + const state = await this.contract.getContractState(params); + return state.balances; } - async getGateway(params: { address: string }): Promise { - return this.cache.getGateway(params); + async getGateway( + params: { address: string } & EvaluationOptions, + ): Promise { + return this.contract.readInteraction({ + functionName: 'gateway', + inputs: { + target: params.address, + }, + evaluationParameters: params.evaluationParameters, + }); } async getGateways( - params: ReadInteractionFilters, + params: EvaluationOptions, ): Promise> { - return this.cache.getGateways(params); + return this.contract.readInteraction({ + functionName: 'gateways', + evaluationParameters: params.evaluationParameters, + }); + } +} + +export class AntContract implements IAntContract { + private contract: SmartWeaveContract; + + constructor({ contract }: Required) { + this.contract = contract; + } + + async getState(params: EvaluationOptions): Promise { + return this.contract.getContractState(params); + } + + async getRecord( + params: { undername: string } & EvaluationOptions, + ): Promise<{ ttlSeconds: number; transactionId: string }> { + const state = await this.contract.getContractState(params); + return state.records[params.undername]; + } + + async getRecords( + params: EvaluationOptions, + ): Promise> { + const state = await this.contract.getContractState(params); + return state.records; + } + + async getOwner( + params: { domain: string } & EvaluationOptions, + ): Promise { + const state = await this.contract.getContractState(params); + return state.owner; + } + + async getControllers(params: EvaluationOptions): Promise { + const state = await this.contract.getContractState(params); + return state.controllers; } } diff --git a/src/common/caches/arns-remote-cache.ts b/src/common/caches/arns-remote-cache.ts deleted file mode 100644 index 8589aaa6..00000000 --- a/src/common/caches/arns-remote-cache.ts +++ /dev/null @@ -1,181 +0,0 @@ -/** - * 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 { ARNS_TESTNET_REGISTRY_TX, ARWEAVE_TX_REGEX } from '../../constants.js'; -import { - ArIOContract, - ArNSNameData, - ArNSStateResponse, - EvalToParams, - Gateway, - HTTPClient, - ReadInteractionFilters, -} from '../../types/index.js'; -import { isBlockHeight, isSortKey } from '../../utils/index.js'; -import { NotFound } from '../error.js'; -import { AxiosHTTPService } from '../http.js'; -import { DefaultLogger } from '../logger.js'; - -export class ArNSRemoteCache implements ArIOContract { - private contractTxId: string; - private logger: DefaultLogger; - private http: HTTPClient; - private apiVersion = 'v1' as const; // use v1 endpoints - constructor({ - url = 'https://api.arns.app', - logger = new DefaultLogger({ - level: 'debug', - logFormat: 'simple', - }), - contractTxId = ARNS_TESTNET_REGISTRY_TX, - }: { - url?: string; - logger?: DefaultLogger; - contractTxId?: string; - }) { - this.validateContractTxId(contractTxId); - this.contractTxId = contractTxId; - this.logger = logger; - this.http = new AxiosHTTPService({ - url: `${url}/${this.apiVersion}`, - logger, - }); - } - sortKeyOrBlockHeightParams(historicalIndex: any): EvalToParams { - if (isSortKey(historicalIndex?.sortKey)) { - return { sortKey: historicalIndex.sortKey }; - } - if (isBlockHeight(historicalIndex?.blockHeight)) { - return { blockHeight: historicalIndex.blockHeight }; - } - return {}; - } - private validateContractTxId(id: string) { - if (!ARWEAVE_TX_REGEX.test(id)) { - throw new Error(`Invalid contract tx id: ${id}`); - } - } - - async getGateway({ - address, - evaluationParameters, - }: { address: string } & ReadInteractionFilters) { - this.logger.debug(`Fetching gateway ${address}`); - - const gateway = await this.getGateways({ evaluationParameters }).then( - (gateways) => { - if (gateways[address] === undefined) { - throw new NotFound(`Gateway not found: ${address}`); - } - return gateways[address]; - }, - ); - return gateway; - } - - async getGateways({ evaluationParameters }: ReadInteractionFilters = {}) { - this.logger.debug(`Fetching gateways`); - - const params = this.sortKeyOrBlockHeightParams( - evaluationParameters?.evalTo, - ); - - const { result } = await this.http.get< - ArNSStateResponse<'result', Record> - >({ - endpoint: `/contract/${this.contractTxId.toString()}/read/gateways`, - params, - }); - return result; - } - - async getBalance({ - address, - evaluationParameters, - }: { address: string } & ReadInteractionFilters) { - this.logger.debug(`Fetching balance for ${address}`); - - const params = this.sortKeyOrBlockHeightParams( - evaluationParameters?.evalTo, - ); - - const { result } = await this.http - .get>({ - endpoint: `/contract/${this.contractTxId.toString()}/state/balances/${address}`, - params, - }) - .catch((e) => { - if (e instanceof NotFound) { - return { result: 0 }; - } - throw e; - }); - return result; - } - - async getBalances({ evaluationParameters }: ReadInteractionFilters = {}) { - this.logger.debug(`Fetching balances`); - - const params = this.sortKeyOrBlockHeightParams( - evaluationParameters?.evalTo, - ); - - const { result } = await this.http.get< - ArNSStateResponse<'result', Record> - >({ - endpoint: `/contract/${this.contractTxId.toString()}/state/balances`, - params, - }); - return result; - } - - async getArNSRecord({ - domain, - evaluationParameters, - }: { domain: string } & ReadInteractionFilters): Promise { - this.logger.debug(`Fetching record for ${domain}`); - - const params = this.sortKeyOrBlockHeightParams( - evaluationParameters?.evalTo, - ); - - const { result } = await this.http.get< - ArNSStateResponse<'result', ArNSNameData> - >({ - endpoint: `/contract/${this.contractTxId.toString()}/state/records/${domain}`, - params, - }); - return result; - } - - async getArNSRecords({ - evaluationParameters, - }: ReadInteractionFilters = {}): Promise> { - this.logger.debug(`Fetching all records`); - - const params = this.sortKeyOrBlockHeightParams( - evaluationParameters?.evalTo, - ); - - const { result } = await this.http.get< - ArNSStateResponse<'result', Record> - >({ - endpoint: `/contract/${this.contractTxId.toString()}/state/records`, - params, - }); - return result; - } -} diff --git a/src/common/caches/remote-contract.ts b/src/common/caches/remote-contract.ts new file mode 100644 index 00000000..93b3d35f --- /dev/null +++ b/src/common/caches/remote-contract.ts @@ -0,0 +1,88 @@ +/** + * 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 { + EvaluationParameters, + HTTPClient, + Logger, + SmartWeaveContract, +} from '../../types/index.js'; +import { AxiosHTTPService } from '../http.js'; +import { DefaultLogger } from '../logger.js'; + +export class ArIORemoteContract implements SmartWeaveContract { + private logger: Logger; + private http: HTTPClient; + private contractTxId: string; + + constructor({ + url = 'https://api.arns.app', + contractTxId, + logger = new DefaultLogger({ + level: 'debug', + logFormat: 'simple', + }), + }: { + contractTxId: string; + url?: string; + logger?: DefaultLogger; + }) { + this.logger = logger; + this.http = new AxiosHTTPService({ + url: `${url}/v1`, + logger, + }); + this.contractTxId = contractTxId; + } + + async getContractState({ + evaluationParameters, + }: { + evaluationParameters?: EvaluationParameters; + }): Promise { + this.logger.debug(`Fetching contract state`, { + contractTxId: this.contractTxId, + evaluationParameters, + }); + return this.http.get({ + endpoint: `/contract/${this.contractTxId}/state`, + params: evaluationParameters?.evalTo, + }); + } + + async readInteraction({ + functionName, + inputs, + evaluationParameters, + }: { + functionName: string; + inputs: object; + evaluationParameters: EvaluationParameters; + }): Promise { + this.logger.debug(`Evaluating read interaction on contract`, { + functionName, + inputs, + evaluationParameters, + }); + return this.http.get({ + endpoint: `/contract/${this.contractTxId}/read/${functionName}`, + params: { + ...evaluationParameters.evalTo, + ...inputs, + }, + }); + } +} diff --git a/src/common/caches/warp-contract.ts b/src/common/caches/warp-contract.ts new file mode 100644 index 00000000..6695b3a1 --- /dev/null +++ b/src/common/caches/warp-contract.ts @@ -0,0 +1,86 @@ +/** + * 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 { + Contract, + Warp, + WarpFactory, + defaultCacheOptions, +} from 'warp-contracts/web'; + +import { SmartWeaveContract } from '../../types/index.js'; + +export const defaultWarpClient = WarpFactory.forMainnet({ + ...defaultCacheOptions, + inMemory: true, // default to in memory for now, a custom warp implementation can be provided +}); + +export class WarpContract implements SmartWeaveContract { + private contract: Contract; + private contractTxId: string; + private cacheUrl: string | undefined; + + constructor({ + contractTxId, + cacheUrl, + warp = defaultWarpClient, + }: { + cacheUrl?: string; + warp: Warp; + contractTxId: string; + }) { + // sync state + this.contract = warp.contract(contractTxId); + this.cacheUrl = cacheUrl; + } + + private async syncState() { + if (this.cacheUrl !== undefined) { + await this.contract.syncState( + `${this.cacheUrl}/v1/contract/${this.contractTxId}`, + { + validity: true, + }, + ); + } + } + + async getContractState(): Promise { + await this.syncState(); + const evaluationResult = await this.contract.readState(); + if (!evaluationResult.cachedValue.state) { + throw new Error('Contract state is not available'); + } + return evaluationResult.cachedValue.state; + } + + async readInteraction({ + functionName, + inputs, + }: { + functionName: string; + inputs: object; + }): Promise { + const evaluationResult = await this.contract.viewState({ + functionName, + ...inputs, + }); + if (!evaluationResult.result) { + throw new Error('Contract state is not available'); + } + return evaluationResult.result; + } +} diff --git a/src/common/http.ts b/src/common/http.ts index 30ee90c0..1b22f463 100644 --- a/src/common/http.ts +++ b/src/common/http.ts @@ -69,39 +69,4 @@ export class AxiosHTTPService implements HTTPClient { return data; } - - // async post({ - // endpoint, - // signal, - // allowedStatuses = [200, 202], - // headers, - // data, - // }: { - // endpoint: string; - // signal?: AbortSignal; - // allowedStatuses?: number[]; - // headers?: Record; - // data: Readable | Buffer | ReadableStream; - // }): Promise { - // const { - // status, - // statusText, - // data: response, - // } = await this.axios.post(endpoint, data, { - // headers, - // signal, - // }); - - // if (!allowedStatuses.includes(status)) { - // switch (status) { - // case 404: - // throw new NotFound(statusText); - // case 400: - // throw new FailedRequestError(status, statusText); - // default: - // throw new UnknownError(statusText); - // } - // } - // return response; - // } } diff --git a/src/types/common.ts b/src/types/common.ts index 7529e45e..092877bc 100644 --- a/src/types/common.ts +++ b/src/types/common.ts @@ -14,46 +14,73 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import { ArNSNameData, Gateway } from './contract-state.js'; +import { + AntRecord, + AntState, + ArIOState, + ArNSNameData, + Gateway, +} from './contract-state.js'; export type BlockHeight = number; export type SortKey = string; +export type WalletAddress = string; export type EvalToParams = - | { sortKey?: SortKey } - | { blockHeight?: BlockHeight }; + | { sortKey: SortKey } + | { blockHeight: BlockHeight } + | undefined; export type EvaluationParameters = { evalTo?: EvalToParams; }; // TODO: extend type with other read filters (e.g max eval time) -export type ReadInteractionFilters = { +export type EvaluationOptions = { evaluationParameters?: EvaluationParameters; }; +export interface SmartWeaveContract { + getContractState(params: EvaluationOptions): Promise; + readInteraction( + params: { functionName: string; inputs?: unknown } & EvaluationOptions, + ): Promise; + // TODO: write interaction +} + // TODO: extend with additional methods export interface ArIOContract { + getState(params: EvaluationOptions): Promise; getGateway( - params: { address: WalletAddress } & ReadInteractionFilters, + params: { address: WalletAddress } & EvaluationOptions, ): Promise; getGateways( - params?: ReadInteractionFilters, + params?: EvaluationOptions, ): Promise>; getBalance( - params: { address: WalletAddress } & ReadInteractionFilters, + params: { address: WalletAddress } & EvaluationOptions, ): Promise; getBalances( - params?: ReadInteractionFilters, + params?: EvaluationOptions, ): Promise>; getArNSRecord( - params: { domain: string } & ReadInteractionFilters, + params: { domain: string } & EvaluationOptions, ): Promise; getArNSRecords( - params?: ReadInteractionFilters, + params?: EvaluationOptions, ): Promise>; } +export interface AntContract { + getState(params: EvaluationOptions): Promise; + getRecords(params: EvaluationOptions): Promise>; + getRecord( + params: { undername: string } & EvaluationOptions, + ): Promise; + getOwner(params: EvaluationOptions): Promise; + getControllers(params: EvaluationOptions): Promise; +} + /* eslint-disable @typescript-eslint/no-explicit-any */ export interface Logger { setLogLevel: (level: string) => void; @@ -65,8 +92,6 @@ export interface Logger { } /* eslint-enable @typescript-eslint/no-explicit-any */ -export type WalletAddress = string; - export interface HTTPClient { get({ endpoint, @@ -81,18 +106,4 @@ export interface HTTPClient { allowedStatuses?: number[]; params?: Record; }): Promise; - // TODO: add post method - // post({ - // endpoint, - // signal, - // headers, - // allowedStatuses, - // data, - // }: { - // endpoint: string; - // signal: AbortSignal; - // headers?: Record; - // allowedStatuses?: number[]; - // data: Readable | ReadableStream | Buffer; - // }): Promise; } diff --git a/src/types/contract-state.ts b/src/types/contract-state.ts index ce8c3ed2..722e71c8 100644 --- a/src/types/contract-state.ts +++ b/src/types/contract-state.ts @@ -165,7 +165,7 @@ export type RegistryVaults = Record; export type PrescribedObservers = Record; -export interface IOState { +export interface ArIOState { balances: Balances; name: string; // The friendly name of the token, shown in block explorers and marketplaces records: Record; // The list of all ArNS names and their associated data @@ -180,3 +180,17 @@ export interface IOState { vaults: RegistryVaults; prescribedObservers: PrescribedObservers; } + +export type AntRecord = { + ttlSeconds: number; + transactionId: string; +}; + +export interface AntState { + balances: Balances; + name: string; + ticket: string; + records: Record; + owner: string; + controllers: string[]; +} From 6eb7ef5ed7f0ec3e64e0c18e97a708d7c528ff21 Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Tue, 5 Mar 2024 18:39:53 -0700 Subject: [PATCH 06/15] feat(ant): create ant contract class for interacting with ant contracts --- src/common/ar-io.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/common/ar-io.ts b/src/common/ar-io.ts index 7489a273..f56287be 100644 --- a/src/common/ar-io.ts +++ b/src/common/ar-io.ts @@ -32,7 +32,7 @@ export type ContractConfiguration = { }; export class ArIO implements ArIOContract { - private contract: SmartWeaveContract; // TODO: this could just be SmartWeaveContract but with the generic ensures any custom setup requires the user to pass in the correct contract type + private contract: SmartWeaveContract; // TODO: this could just be scoped to WarpContract | ArIORemoteContract constructor({ contract = new ArIORemoteContract({ @@ -95,7 +95,9 @@ export class ArIO implements ArIOContract { export class AntContract implements ArNSAntContract { private contract: SmartWeaveContract; - constructor({ contract }: Required) { + constructor({ contractTxId, contract = new ArIORemoteContract({ + contractTxId, + }) }: { contractTxId: string } & ContractConfiguration) { this.contract = contract; } From a58e5b41777e0518abe670a499a4d1455892cae8 Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Tue, 5 Mar 2024 18:41:13 -0700 Subject: [PATCH 07/15] chore: make husky files executable --- .husky/commit-msg | 0 .husky/pre-commit | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 .husky/commit-msg mode change 100644 => 100755 .husky/pre-commit diff --git a/.husky/commit-msg b/.husky/commit-msg old mode 100644 new mode 100755 diff --git a/.husky/pre-commit b/.husky/pre-commit old mode 100644 new mode 100755 From 8119e5e2779de07ce7a4fe74abd4e4b8b10e7b32 Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Tue, 5 Mar 2024 18:44:10 -0700 Subject: [PATCH 08/15] chore: remove ant contract implementations for now We will come back to these. --- README.md | 4 +--- src/common/ar-io.ts | 46 ++----------------------------------- src/types/contract-state.ts | 14 ----------- 3 files changed, 3 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index a3899006..c03720c5 100644 --- a/README.md +++ b/README.md @@ -157,7 +157,7 @@ Alternatively - you can choose to evaluate contracts locally leveraging [Warp]. // setup a local contract and evaluate it using warp const localContract = new WarpContract({ contractTxId: 'TESTNET_CONTRACT_TX_ID', -}) +}); const localArIO = new ArIO({ contract: localContract, @@ -192,7 +192,6 @@ Retrieves the balances of the ArIO contract. --> ```typescript - const arIO = new ArIO(); const balances = arIO.getBalances(); @@ -249,7 +248,6 @@ const gateway = arIO.getGateway({ // "normalizedCompositeWeight": 0.27485583057217183 // } // } - ``` #### `getGateways()` diff --git a/src/common/ar-io.ts b/src/common/ar-io.ts index f56287be..5c8ff905 100644 --- a/src/common/ar-io.ts +++ b/src/common/ar-io.ts @@ -16,16 +16,14 @@ */ import { ARNS_TESTNET_REGISTRY_TX } from '../constants.js'; import { - AntState, ArIOContract, ArIOState, - ArNSAntContract, ArNSNameData, EvaluationOptions, Gateway, SmartWeaveContract, } from '../types/index.js'; -import { ArIORemoteContract, WarpContract } from './index.js'; +import { ArIORemoteContract } from './index.js'; export type ContractConfiguration = { contract?: SmartWeaveContract; @@ -39,7 +37,7 @@ export class ArIO implements ArIOContract { contractTxId: ARNS_TESTNET_REGISTRY_TX, }), }: { - contract?: SmartWeaveContract + contract?: SmartWeaveContract; }) { this.contract = contract; } @@ -91,43 +89,3 @@ export class ArIO implements ArIOContract { }); } } - -export class AntContract implements ArNSAntContract { - private contract: SmartWeaveContract; - - constructor({ contractTxId, contract = new ArIORemoteContract({ - contractTxId, - }) }: { contractTxId: string } & ContractConfiguration) { - this.contract = contract; - } - - async getState(params: EvaluationOptions): Promise { - return this.contract.getContractState(params); - } - - async getRecord( - params: { undername: string } & EvaluationOptions, - ): Promise<{ ttlSeconds: number; transactionId: string }> { - const state = await this.contract.getContractState(params); - return state.records[params.undername]; - } - - async getRecords( - params: EvaluationOptions, - ): Promise> { - const state = await this.contract.getContractState(params); - return state.records; - } - - async getOwner( - params: { domain: string } & EvaluationOptions, - ): Promise { - const state = await this.contract.getContractState(params); - return state.owner; - } - - async getControllers(params: EvaluationOptions): Promise { - const state = await this.contract.getContractState(params); - return state.controllers; - } -} diff --git a/src/types/contract-state.ts b/src/types/contract-state.ts index 722e71c8..6f5b7c54 100644 --- a/src/types/contract-state.ts +++ b/src/types/contract-state.ts @@ -180,17 +180,3 @@ export interface ArIOState { vaults: RegistryVaults; prescribedObservers: PrescribedObservers; } - -export type AntRecord = { - ttlSeconds: number; - transactionId: string; -}; - -export interface AntState { - balances: Balances; - name: string; - ticket: string; - records: Record; - owner: string; - controllers: string[]; -} From 885ce68c3b734ac95e1293078ce835333bc45d9f Mon Sep 17 00:00:00 2001 From: Atticus Date: Wed, 6 Mar 2024 06:49:06 -0500 Subject: [PATCH 09/15] fix(husky): add commit hooks --- .husky/commit-msg | 0 .husky/pre-commit | 0 README.md | 2 -- 3 files changed, 2 deletions(-) mode change 100644 => 100755 .husky/commit-msg mode change 100644 => 100755 .husky/pre-commit diff --git a/.husky/commit-msg b/.husky/commit-msg old mode 100644 new mode 100755 diff --git a/.husky/pre-commit b/.husky/pre-commit old mode 100644 new mode 100755 diff --git a/README.md b/README.md index ef9cb6ad..7417a31d 100644 --- a/README.md +++ b/README.md @@ -177,7 +177,6 @@ Retrieves the balances of the ArIO contract. --> ```typescript - const arIO = new ArIO(); const balances = arIO.getBalances(); @@ -234,7 +233,6 @@ const gateway = arIO.getGateway({ // "normalizedCompositeWeight": 0.27485583057217183 // } // } - ``` #### `getGateways()` From a961f6fa8da754e976313610e86dc3b2b7f307a6 Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Thu, 7 Mar 2024 09:50:51 -0600 Subject: [PATCH 10/15] chore: remove all ant state implementations --- src/types/common.ts | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/types/common.ts b/src/types/common.ts index a9e83a46..724080ba 100644 --- a/src/types/common.ts +++ b/src/types/common.ts @@ -14,13 +14,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import { - AntRecord, - AntState, - ArIOState, - ArNSNameData, - Gateway, -} from './contract-state.js'; +import { ArIOState, ArNSNameData, Gateway } from './contract-state.js'; export type BlockHeight = number; export type SortKey = string; @@ -71,16 +65,6 @@ export interface ArIOContract { ): Promise>; } -export interface ArNSAntContract { - getState(params: EvaluationOptions): Promise; - getRecords(params: EvaluationOptions): Promise>; - getRecord( - params: { undername: string } & EvaluationOptions, - ): Promise; - getOwner(params: EvaluationOptions): Promise; - getControllers(params: EvaluationOptions): Promise; -} - /* eslint-disable @typescript-eslint/no-explicit-any */ export interface Logger { setLogLevel: (level: string) => void; From 8164eef318cceefc1cba0aa2c5e5172cf9910a60 Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Fri, 8 Mar 2024 08:38:07 -0600 Subject: [PATCH 11/15] chore: update types, interfaces and add docker-compose for tests --- README.md | 34 ++-- docker-compose.yaml | 18 ++ package.json | 3 +- src/common/ar-io.ts | 157 ++++++++++++------ src/common/contracts/remote-contract.ts | 55 +++--- src/common/contracts/warp-contract.ts | 37 +++-- src/common/error.test.ts | 16 -- src/common/http.ts | 19 ++- src/types/common.ts | 81 +++++---- src/utils/http-client.ts | 1 + src/utils/index.ts | 2 +- .../evaluation.ts => smartweave.ts} | 4 +- src/utils/smartweave/evaluation.test.ts | 13 -- src/utils/smartweave/index.ts | 17 -- tests/ar-io.test.ts | 116 ++++++++++++- tests/arns-remote-cache/balances.test.ts | 82 --------- tests/arns-remote-cache/gateways.test.ts | 69 -------- tests/arns-remote-cache/records.test.ts | 99 ----------- {src => tests}/utils/arweave.test.ts | 4 +- tsconfig.json | 2 +- yarn.lock | 22 +-- 21 files changed, 380 insertions(+), 471 deletions(-) create mode 100644 docker-compose.yaml delete mode 100644 src/common/error.test.ts rename src/utils/{smartweave/evaluation.ts => smartweave.ts} (93%) delete mode 100644 src/utils/smartweave/evaluation.test.ts delete mode 100644 src/utils/smartweave/index.ts delete mode 100644 tests/arns-remote-cache/balances.test.ts delete mode 100644 tests/arns-remote-cache/gateways.test.ts delete mode 100644 tests/arns-remote-cache/records.test.ts rename {src => tests}/utils/arweave.test.ts (73%) diff --git a/README.md b/README.md index c03720c5..3a943a51 100644 --- a/README.md +++ b/README.md @@ -137,33 +137,27 @@ Types are exported from `./lib/types/[node/web]/index.d.ts` and should be automa ### Custom Contract Evaluation -```typescript -// use an experimental contract that satisfies the ArIOContract state and evaluate it using a remote contract service -const testnetContract = new ArIORemoteContract({ - contractTxId: 'TESTNET_CONTRACT_TX_ID', - remoteCacheUrl: 'http://localhost:3000', // my local cache service -}); - -const testnet = new ArIO({ - contract: remoteContract, -}); - -const gateways = testnet.getGateways(); // evaluates the ArIO contract using the remote cache service -``` - -Alternatively - you can choose to evaluate contracts locally leveraging [Warp]. +By default - the `ArIO` client uses the `mainnet` contract and exposes APIs relevant to the `ArIO` contract. You can provide custom `contract` or `contractTxId` to the `ArIO` constructor and expose those APIs, assuming the contract is compatible with the `ArIO` contract. ```typescript -// setup a local contract and evaluate it using warp -const localContract = new WarpContract({ +// provide a custom contractTxId to the client and default to remote evaluation +const remoteCustomArIO = new ArIO({ contractTxId: 'TESTNET_CONTRACT_TX_ID', }); -const localArIO = new ArIO({ - contract: localContract, +// provide a custom contract to the client, and specify local evaluation using warp +const localCustomArIO = new ArIO({ + contract: new WarpContract({ + contractTxId: 'TESTNET_CONTRACT_TX_ID', + }) }); -const gateways = localArIO.getGateways(); // evaluates the ArIO contract locally using warp +// provide a custom contract to the client, and specify local evaluation using remote cache +const remoteCacheCustomArIO = new ArIO({ + contract: new RemoteContract({ + contractTxId: 'TESTNET_CONTRACT_TX_ID', + }) +}); ``` ## APIs diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 00000000..8b0dc80c --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,18 @@ +version: '3' + +services: + arns-service: + image: ghcr.io/ar-io/arns-service:latest + build: . + ports: + - "3000:3000" + environment: + - LOG_LEVEL=debug + - PREFETCH_CONTRACTS=true + - PREFETCH_CONTRACT_IDS=_NctcA2sRy1-J4OmIQZbYFPM17piNcbdBPH2ncX2RL8 + - BOOTSTRAP_CONTRACTS=false + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000/healthcheck"] + interval: 10s + timeout: 5s + retries: 5 diff --git a/package.json b/package.json index f7a64cec..467e197f 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "format": "prettier --check .", "format:fix": "prettier --write .", "test": "yarn clean && c8 jest .", + "test:integration": "docker compose up -d && yarn test && docker compose down", "prepare": "husky install", "example:mjs": "yarn build:esm && node examples/node/index.mjs", "example:cjs": "yarn build:cjs && node examples/node/index.cjs", @@ -98,7 +99,7 @@ "dependencies": { "arweave": "^1.14.4", "axios": "1.4.0", - "warp-contracts": "1.4.29", + "warp-contracts": "^1.4.37", "winston": "^3.11.0" } } diff --git a/src/common/ar-io.ts b/src/common/ar-io.ts index 5c8ff905..8356dc67 100644 --- a/src/common/ar-io.ts +++ b/src/common/ar-io.ts @@ -19,73 +19,134 @@ import { ArIOContract, ArIOState, ArNSNameData, - EvaluationOptions, + EvaluationParameters, Gateway, SmartWeaveContract, } from '../types/index.js'; -import { ArIORemoteContract } from './index.js'; +import { RemoteContract } from './index.js'; -export type ContractConfiguration = { - contract?: SmartWeaveContract; -}; +// 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; // TODO: this could just be scoped to WarpContract | ArIORemoteContract + private contract: SmartWeaveContract; - constructor({ - contract = new ArIORemoteContract({ - contractTxId: ARNS_TESTNET_REGISTRY_TX, - }), - }: { - contract?: SmartWeaveContract; - }) { - this.contract = contract; + constructor( + config: ContractConfiguration = { + // default to a contract that uses the arns service to do the evaluation + contract: new RemoteContract({ + contractTxId: ARNS_TESTNET_REGISTRY_TX, + }), + }, + ) { + if (isContractConfiguration(config)) { + this.contract = config.contract; + } else if (isContractTxIdConfiguration(config)) { + this.contract = new RemoteContract({ + contractTxId: config.contractTxId, + }); + } } - async getState(params: EvaluationOptions): Promise { - return this.contract.getContractState(params); + /** + * Returns the current state of the contract. + */ + async getState(params: EvaluationParameters): Promise { + const state = await this.contract.getContractState(params); + return state; } - async getArNSRecord( - params: { domain: string } & EvaluationOptions, - ): Promise { - const records = await this.getArNSRecords(params); - return records[params.domain]; + + /** + * Returns the ARNS record for the given domain. + */ + async getArNSRecord({ + domain, + evaluationOptions, + }: EvaluationParameters<{ domain: string }>): Promise< + ArNSNameData | undefined + > { + const records = await this.getArNSRecords({ evaluationOptions }); + return records[domain]; } - async getArNSRecords( - params: EvaluationOptions, - ): Promise> { - const state = await this.contract.getContractState(params); + + /** + * Returns all ArNS records. + */ + async getArNSRecords({ + evaluationOptions, + }: EvaluationParameters = {}): Promise> { + const state = await this.contract.getContractState({ evaluationOptions }); return state.records; } - async getBalance( - params: { address: string } & EvaluationOptions, - ): Promise { - const balances = await this.getBalances(params); - return balances[params.address] || 0; + + /** + * Returns the balance of the given address. + */ + async getBalance({ + address, + evaluationOptions, + }: EvaluationParameters<{ address: string }>): Promise { + const balances = await this.getBalances({ evaluationOptions }); + return balances[address] || 0; } - async getBalances( - params: EvaluationOptions, - ): Promise> { - const state = await this.contract.getContractState(params); + + /** + * Returns the balances of all addresses. + */ + async getBalances({ evaluationOptions }: EvaluationParameters = {}): Promise< + Record + > { + const state = await this.contract.getContractState({ evaluationOptions }); return state.balances; } - async getGateway( - params: { address: string } & EvaluationOptions, - ): Promise { - return this.contract.readInteraction({ - functionName: 'gateway', - inputs: { - target: params.address, - }, - evaluationParameters: params.evaluationParameters, - }); + + /** + * Returns the gateway for the given address, including weights. + */ + async getGateway({ + address, + evaluationOptions, + }: EvaluationParameters<{ address: string }>): Promise { + return this.contract + .readInteraction<{ target: string }, Gateway>({ + functionName: 'gateway', + inputs: { + target: address, + }, + evaluationOptions, + }) + .catch(() => { + return undefined; + }); } - async getGateways( - params: EvaluationOptions, - ): Promise> { + + /** + * Returns all gateways, including weights. + */ + async getGateways({ evaluationOptions }: EvaluationParameters = {}): Promise< + Record | Record + > { return this.contract.readInteraction({ functionName: 'gateways', - evaluationParameters: params.evaluationParameters, + evaluationOptions, }); } } diff --git a/src/common/contracts/remote-contract.ts b/src/common/contracts/remote-contract.ts index 93b3d35f..c2c27549 100644 --- a/src/common/contracts/remote-contract.ts +++ b/src/common/contracts/remote-contract.ts @@ -23,7 +23,8 @@ import { import { AxiosHTTPService } from '../http.js'; import { DefaultLogger } from '../logger.js'; -export class ArIORemoteContract implements SmartWeaveContract { +// TODO: this assumes the API structure matches the current arns-service API - we will want to consider another interface that exposes relevant APIs with client implementations (arns-service, DRE nodes, etc.) +export class RemoteContract implements SmartWeaveContract { private logger: Logger; private http: HTTPClient; private contractTxId: string; @@ -31,58 +32,58 @@ export class ArIORemoteContract implements SmartWeaveContract { constructor({ url = 'https://api.arns.app', contractTxId, - logger = new DefaultLogger({ - level: 'debug', - logFormat: 'simple', - }), + logger = new DefaultLogger(), }: { contractTxId: string; url?: string; logger?: DefaultLogger; }) { + this.contractTxId = contractTxId; this.logger = logger; this.http = new AxiosHTTPService({ - url: `${url}/v1`, - logger, + url: `${url}/v1/contract/${contractTxId}`, }); - this.contractTxId = contractTxId; } async getContractState({ - evaluationParameters, - }: { - evaluationParameters?: EvaluationParameters; - }): Promise { + evaluationOptions, + }: EvaluationParameters = {}): Promise { this.logger.debug(`Fetching contract state`, { contractTxId: this.contractTxId, - evaluationParameters, + evaluationOptions, }); - return this.http.get({ - endpoint: `/contract/${this.contractTxId}/state`, - params: evaluationParameters?.evalTo, + const { state } = await this.http.get< + { sortKey: string } | { blockHeight: number } | Record, + { state: T } + >({ + endpoint: ``, + params: { + ...evaluationOptions?.evalTo, + }, }); + return state; } - async readInteraction({ + async readInteraction({ functionName, inputs, - evaluationParameters, - }: { - functionName: string; - inputs: object; - evaluationParameters: EvaluationParameters; - }): Promise { + evaluationOptions, + }: EvaluationParameters<{ functionName: string; inputs?: I }>): Promise { this.logger.debug(`Evaluating read interaction on contract`, { functionName, inputs, - evaluationParameters, + evaluationOptions, }); - return this.http.get({ - endpoint: `/contract/${this.contractTxId}/read/${functionName}`, + const { result } = await this.http.get< + I | Record, + { result: K } + >({ + endpoint: `/read/${functionName}`, params: { - ...evaluationParameters.evalTo, + ...evaluationOptions?.evalTo, ...inputs, }, }); + return result; } } diff --git a/src/common/contracts/warp-contract.ts b/src/common/contracts/warp-contract.ts index 4b1569fd..2fbce91c 100644 --- a/src/common/contracts/warp-contract.ts +++ b/src/common/contracts/warp-contract.ts @@ -21,14 +21,15 @@ import { defaultCacheOptions, } from 'warp-contracts'; -import { SmartWeaveContract } from '../../types/index.js'; +import { EvaluationParameters, SmartWeaveContract } from '../../types/index.js'; +import { FailedRequestError } from '../error.js'; export const defaultWarpClient = WarpFactory.forMainnet({ ...defaultCacheOptions, inMemory: true, // default to in memory for now, a custom warp implementation can be provided }); -export class WarpContract implements SmartWeaveContract { +export class WarpContract implements SmartWeaveContract { private contract: Contract; private contractTxId: string; private cacheUrl: string | undefined; @@ -58,28 +59,40 @@ export class WarpContract implements SmartWeaveContract { } } - async getContractState(): Promise { + async getContractState({ + evaluationOptions = {}, + }: EvaluationParameters): Promise { await this.syncState(); - const evaluationResult = await this.contract.readState(); + const evalTo = evaluationOptions?.evalTo; + let sortKeyOrBlockHeight: string | number | undefined; + if (evalTo && 'sortKey' in evalTo) { + sortKeyOrBlockHeight = evalTo.sortKey; + } else if (evalTo && 'blockHeight') { + sortKeyOrBlockHeight = evalTo.blockHeight; + } + + const evaluationResult = + await this.contract.readState(sortKeyOrBlockHeight); if (!evaluationResult.cachedValue.state) { - throw new Error('Contract state is not available'); + throw new FailedRequestError(502, 'Failed to evaluate contract state'); } - return evaluationResult.cachedValue.state; + return evaluationResult.cachedValue.state as T; } - async readInteraction({ + async readInteraction({ functionName, inputs, - }: { - functionName: string; - inputs: object; - }): Promise { + // TODO: view state only supports sort key so we won't be able to use block height + }: EvaluationParameters<{ functionName: string; inputs: I }>): Promise { const evaluationResult = await this.contract.viewState({ functionName, ...inputs, }); if (!evaluationResult.result) { - throw new Error('Contract state is not available'); + throw new FailedRequestError( + 502, + 'Failed to evaluate contract read interaction', + ); } return evaluationResult.result; } diff --git a/src/common/error.test.ts b/src/common/error.test.ts deleted file mode 100644 index 068838e7..00000000 --- a/src/common/error.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { BadRequest, NotFound } from './error.js'; - -describe('Error', () => { - it.each([ - ['BadRequest', BadRequest], - ['NotFound', NotFound], - ])( - 'Errors should inherit Base error and names should be applied appropriately', - (name, errorClass) => { - const message = 'This is a test error'; - const error = new errorClass(message); - expect(error.name).toEqual(name); - expect(error.message).toEqual(message); - }, - ); -}); diff --git a/src/common/http.ts b/src/common/http.ts index 1b22f463..7e566f0c 100644 --- a/src/common/http.ts +++ b/src/common/http.ts @@ -19,22 +19,28 @@ import { AxiosInstance } from 'axios'; import { HTTPClient, Logger } from '../types/index.js'; import { createAxiosInstance } from '../utils/index.js'; import { FailedRequestError, NotFound, UnknownError } from './error.js'; +import { DefaultLogger } from './logger.js'; export class AxiosHTTPService implements HTTPClient { private axios: AxiosInstance; private logger: Logger; // TODO: re-implement axios-retry. Currently that package is broken for nodenext. - constructor({ url, logger }: { url: string; logger: Logger }) { + constructor({ + url, + logger = new DefaultLogger(), + }: { + url: string; + logger?: Logger; + }) { this.logger = logger; this.axios = createAxiosInstance({ axiosConfig: { baseURL: url, - maxRedirects: 0, }, }); } - async get({ + async get({ endpoint, signal, allowedStatuses = [200, 202], @@ -45,17 +51,16 @@ export class AxiosHTTPService implements HTTPClient { signal?: AbortSignal; allowedStatuses?: number[]; headers?: Record; - params?: Record; - }): Promise { + params?: I; + }): Promise { this.logger.debug( `Get request to endpoint: ${endpoint} with params ${JSON.stringify(params, undefined, 2)}`, ); - const { status, statusText, data } = await this.axios.get(endpoint, { + const { status, statusText, data } = await this.axios.get(endpoint, { headers, signal, params, }); - if (!allowedStatuses.includes(status)) { switch (status) { case 404: diff --git a/src/types/common.ts b/src/types/common.ts index 724080ba..4bde8de5 100644 --- a/src/types/common.ts +++ b/src/types/common.ts @@ -20,49 +20,59 @@ export type BlockHeight = number; export type SortKey = string; export type WalletAddress = string; -export type EvalToParams = - | { sortKey: SortKey } - | { blockHeight: BlockHeight } - | undefined; - -export type EvaluationParameters = { - evalTo?: EvalToParams; -}; - -// TODO: extend type with other read filters (e.g max eval time) export type EvaluationOptions = { - evaluationParameters?: EvaluationParameters; + evalTo?: { sortKey: SortKey } | { blockHeight: BlockHeight }; + // TODO: any other evaluation constraints }; -export interface SmartWeaveContract { - getContractState(params: EvaluationOptions): Promise; - readInteraction( - params: { functionName: string; inputs?: unknown } & EvaluationOptions, - ): Promise; +// combine evaluation parameters with read interaction inputs +export type EvaluationParameters> = { + evaluationOptions?: EvaluationOptions | Record | undefined; +} & T; + +export interface SmartWeaveContract { + getContractState(params: EvaluationParameters): Promise; + readInteraction({ + functionName, + inputs, + evaluationOptions, + }: EvaluationParameters<{ functionName: string; inputs?: I }>): Promise; // TODO: write interaction } // TODO: extend with additional methods export interface ArIOContract { - getState(params: EvaluationOptions): Promise; - getGateway( - params: { address: WalletAddress } & EvaluationOptions, - ): Promise; - getGateways( - params?: EvaluationOptions, - ): Promise>; + getState({ evaluationOptions }: EvaluationParameters): Promise; + getGateway({ + address, + evaluationOptions, + }: EvaluationParameters<{ address: WalletAddress }>): Promise< + Gateway | undefined + >; + getGateways({ + evaluationOptions, + }: EvaluationParameters): Promise< + Record | Record + >; getBalance( params: { address: WalletAddress } & EvaluationOptions, ): Promise; - getBalances( - params?: EvaluationOptions, - ): Promise>; - getArNSRecord( - params: { domain: string } & EvaluationOptions, - ): Promise; - getArNSRecords( - params?: EvaluationOptions, - ): Promise>; + getBalances({ + evaluationOptions, + }: EvaluationParameters): Promise< + Record | Record + >; + getArNSRecord({ + domain, + evaluationOptions, + }: EvaluationParameters<{ domain: string }>): Promise< + ArNSNameData | undefined + >; + getArNSRecords({ + evaluationOptions, + }: EvaluationParameters): Promise< + Record | Record + >; } /* eslint-disable @typescript-eslint/no-explicit-any */ @@ -75,9 +85,8 @@ export interface Logger { debug: (message: string, ...args: any[]) => void; } /* eslint-enable @typescript-eslint/no-explicit-any */ - export interface HTTPClient { - get({ + get({ endpoint, signal, headers, @@ -88,6 +97,6 @@ export interface HTTPClient { signal?: AbortSignal; headers?: Record; allowedStatuses?: number[]; - params?: Record; - }): Promise; + params?: object | I; + }): Promise; } diff --git a/src/utils/http-client.ts b/src/utils/http-client.ts index 4513e352..0da741a8 100644 --- a/src/utils/http-client.ts +++ b/src/utils/http-client.ts @@ -28,6 +28,7 @@ export const createAxiosInstance = ({ }: AxiosInstanceParameters = {}): AxiosInstance => { const axiosInstance = axios.create({ ...axiosConfig, + maxRedirects: 0, headers: { ...axiosConfig.headers, 'x-source-version': `${version}`, diff --git a/src/utils/index.ts b/src/utils/index.ts index 521e4f01..0738d3ef 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -16,4 +16,4 @@ */ export * from './arweave.js'; export * from './http-client.js'; -export * from './smartweave/index.js'; +export * from './smartweave.js'; diff --git a/src/utils/smartweave/evaluation.ts b/src/utils/smartweave.ts similarity index 93% rename from src/utils/smartweave/evaluation.ts rename to src/utils/smartweave.ts index 4d1deddc..c13fb4ac 100644 --- a/src/utils/smartweave/evaluation.ts +++ b/src/utils/smartweave.ts @@ -14,8 +14,8 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import { SORT_KEY_REGEX } from '../../constants.js'; -import { SortKey } from '../../types/common.js'; +import { SORT_KEY_REGEX } from '../constants.js'; +import { SortKey } from '../types/common.js'; export function isSortKey(sortKey: string): sortKey is SortKey { return SmartWeaveSortKey.validate(sortKey); diff --git a/src/utils/smartweave/evaluation.test.ts b/src/utils/smartweave/evaluation.test.ts deleted file mode 100644 index dd8de782..00000000 --- a/src/utils/smartweave/evaluation.test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { SmartWeaveSortKey } from './evaluation.js'; - -describe(`Smartweave eval utils`, () => { - it(`should throw on a bad sort key`, async () => { - const sortKey = '123,456,abc'; - const error = await (async () => new SmartWeaveSortKey(sortKey))().catch( - (e) => e, - ); - - expect(error).toBeInstanceOf(Error); - expect(error.message).toContain(sortKey); - }); -}); diff --git a/src/utils/smartweave/index.ts b/src/utils/smartweave/index.ts deleted file mode 100644 index c0451be1..00000000 --- a/src/utils/smartweave/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * 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 . - */ -export * from './evaluation.js'; diff --git a/tests/ar-io.test.ts b/tests/ar-io.test.ts index b17a45c7..26bfce4d 100644 --- a/tests/ar-io.test.ts +++ b/tests/ar-io.test.ts @@ -1,9 +1,121 @@ import { ArIO } from '../src/common/ar-io.js'; +import { RemoteContract } from '../src/common/index.js'; +import { ARNS_DEVNET_REGISTRY_TX } from '../src/constants.js'; +import { ArIOState } from '../src/types/contract-state.js'; +import { SmartWeaveSortKey } from '../src/utils/smartweave.js'; +const gatewayAddress = '1H7WZIWhzwTH9FIcnuMqYkTsoyv1OTfGa_amvuYwrgo'; +const domain = 'ar-io'; +const evaluateToBlockHeight = 1377100; +const evaluateToSortKey = new SmartWeaveSortKey( + '000001376946,0000000000000,18d52956c8e13ae1f557b4e67f6f298b8ffd2a5cd96e42ec24ca649b7401510f', +); describe('ArIO Client', () => { + const arioClient = new ArIO({ + contract: new RemoteContract({ + url: process.env.REMOTE_CACHE_URL || 'http://localhost:3000', + contractTxId: ARNS_DEVNET_REGISTRY_TX + }), + }); it('should create a custom ArIO client', () => { - const arioClient = new ArIO(); - expect(arioClient).toBeInstanceOf(ArIO); }); + + it('should should return undefined for non existent gateway', async () => { + const nonExistent = await arioClient.getGateway({ address: 'some-address' }) + expect(nonExistent).toEqual(undefined) + }); + + it('should return gateway state at a given block height', async () => { + const gateway = await arioClient.getGateway({ + address: gatewayAddress, + evaluationOptions: { evalTo: { blockHeight: evaluateToBlockHeight } }, + }); + expect(gateway).toBeDefined(); + }); + + it('should return gateway state at a given sort key', async () => { + const gateway = await arioClient.getGateway({ + address: gatewayAddress, + evaluationOptions: { evalTo: { sortKey: evaluateToSortKey.toString() } }, + }); + expect(gateway).toBeDefined(); + }); + + it('should return gateways state at a given block height', async () => { + const gateways = await arioClient.getGateways({ + evaluationOptions: { evalTo: { blockHeight: evaluateToBlockHeight } }, + }); + expect(gateways[gatewayAddress]).toBeDefined(); + }); + + it('should return gateways state at a given sort key', async () => { + const gateways = await arioClient.getGateways({ + evaluationOptions: { evalTo: { sortKey: evaluateToSortKey.toString() } }, + }); + expect(gateways[gatewayAddress]).toBeDefined(); + }); + + it('should fetch a record', async () => { + const record = await arioClient.getArNSRecord({ domain: 'ar-io' }); + expect(record).toBeDefined(); + }); + + it('should throw NotFound error on non existent record', async () => { + const nonExistent = await arioClient.getArNSRecord({ domain: 'some-domain' }) + expect(nonExistent).toEqual(undefined) + }); + + it('should fetch all records', async () => { + const records = await arioClient.getArNSRecords(); + expect(records).toBeDefined(); + }); + + it('should return record at a given block height', async () => { + const currentRecord = await arioClient.getArNSRecord({ + domain, + evaluationOptions: { + evalTo: { blockHeight: evaluateToBlockHeight + 1 }, + }, + }); + expect(currentRecord).toBeDefined(); + + const nonExistent = await arioClient + .getArNSRecord({ + domain, + evaluationOptions: { + evalTo: { blockHeight: 0 }, + }, + }) + + expect(nonExistent).toEqual(undefined) + }); + + it('should return record at a given sort key', async () => { + const record = await arioClient.getArNSRecord({ + domain, + evaluationOptions: { + evalTo: { sortKey: evaluateToSortKey.toString() }, + }, + }); + expect(record).toBeDefined(); + }); + + it('should return records at a given block height', async () => { + const records = await arioClient.getArNSRecords({ + evaluationOptions: { + evalTo: { blockHeight: evaluateToBlockHeight }, + }, + }); + expect(records[domain]).toBeDefined(); + }); + + it('should return records at a given sort key', async () => { + const records = await arioClient.getArNSRecords({ + evaluationOptions: { + evalTo: { sortKey: evaluateToSortKey.toString() }, + }, + }); + expect(records[domain]).toBeDefined(); + }); }); diff --git a/tests/arns-remote-cache/balances.test.ts b/tests/arns-remote-cache/balances.test.ts deleted file mode 100644 index 253fafff..00000000 --- a/tests/arns-remote-cache/balances.test.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { ArNSRemoteCache } from '../../src/common/caches/arns-remote-cache.js'; -import { ARNS_DEVNET_REGISTRY_TX } from '../../src/constants.js'; -import { SmartWeaveSortKey } from '../../src/utils/index.js'; - -describe('ArNSRemoteCache ~ BALANCES', () => { - const remoteCacheProvider = new ArNSRemoteCache({ - contractTxId: ARNS_DEVNET_REGISTRY_TX, - }); - // balance tests - it('should fetch a balance', async () => { - const balance = await remoteCacheProvider.getBalance({ - address: 'some-address', - }); - expect(balance).toEqual(0); - }); - - it('should fetch all balances', async () => { - const balances = await remoteCacheProvider.getBalances(); - expect(balances).toBeDefined(); - }); - - it('should return balance at a given block height', async () => { - const address = 'ySqMsg7O0R-BcUw35R3nxJJKJyIdauLCQ4DUZqPCiYo'; - const transferBlockHeight = 1364752; - const currentBalance = await remoteCacheProvider.getBalance({ - address, - evaluationParameters: { - evalTo: { blockHeight: transferBlockHeight }, - }, - }); - const transferAmount = 20000; - - const balance = await remoteCacheProvider.getBalance({ - address, - evaluationParameters: { evalTo: { blockHeight: transferBlockHeight } }, - }); - expect(balance).toEqual(currentBalance); - - const previousBalance = await remoteCacheProvider.getBalance({ - address, - evaluationParameters: { - evalTo: { blockHeight: transferBlockHeight - 1 }, - }, - }); - expect(previousBalance).toEqual(currentBalance - transferAmount); - }); - - it('should return balance at a given sort key', async () => { - const address = 'ySqMsg7O0R-BcUw35R3nxJJKJyIdauLCQ4DUZqPCiYo'; - const balanceSortKey = new SmartWeaveSortKey( - '000001364752,0000000000000,7fee05ef004191b252b073628013f987033513c51116d283dc24c866b5c32d0a', - ); - const balance = await remoteCacheProvider.getBalance({ - address, - evaluationParameters: { evalTo: { sortKey: balanceSortKey.toString() } }, - }); - expect(balance).toEqual(20000); - }); - - it('should return balances at a given block height', async () => { - const address = 'ySqMsg7O0R-BcUw35R3nxJJKJyIdauLCQ4DUZqPCiYo'; - const transferBlockHeight = 1364752; - const currentBalance = await remoteCacheProvider.getBalance({ - address, - evaluationParameters: { - evalTo: { blockHeight: transferBlockHeight }, - }, - }); - const balances = await remoteCacheProvider.getBalances({ - evaluationParameters: { evalTo: { blockHeight: transferBlockHeight } }, - }); - - expect(balances[address]).toEqual(currentBalance); - - const previousBalances = await remoteCacheProvider.getBalances({ - evaluationParameters: { - evalTo: { blockHeight: transferBlockHeight - 1 }, - }, - }); - expect(previousBalances[address]).toEqual(undefined); - }); -}); diff --git a/tests/arns-remote-cache/gateways.test.ts b/tests/arns-remote-cache/gateways.test.ts deleted file mode 100644 index 9a0fb742..00000000 --- a/tests/arns-remote-cache/gateways.test.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { ArNSRemoteCache } from '../../src/common/caches/arns-remote-cache.js'; -import { NotFound } from '../../src/common/error.js'; -import { ARNS_DEVNET_REGISTRY_TX } from '../../src/constants.js'; -import { SmartWeaveSortKey } from '../../src/utils/index.js'; - -describe('ArNSRemoteCache ~ GATEWAYS', () => { - const remoteCacheProvider = new ArNSRemoteCache({ - contractTxId: ARNS_DEVNET_REGISTRY_TX, - }); - const address = '1H7WZIWhzwTH9FIcnuMqYkTsoyv1OTfGa_amvuYwrgo'; - const currentBlockHeight = 1377100; - const previousBlockHeight = currentBlockHeight + 100; - const currentSortKey = new SmartWeaveSortKey( - '000001376946,0000000000000,18d52956c8e13ae1f557b4e67f6f298b8ffd2a5cd96e42ec24ca649b7401510f', - ); - - // gateway tests - it('should be able to fetch gateways', async () => { - const gateways = await remoteCacheProvider.getGateways(); - expect(gateways).toBeDefined(); - }); - - it('should should throw NotFound error on non existent gateway', async () => { - const error = await remoteCacheProvider - .getGateway({ - address: 'some-address', - }) - .catch((e) => e); - expect(error).toBeInstanceOf(NotFound); - }); - - it('should return gateway state at a given block height', async () => { - const gateway = await remoteCacheProvider.getGateway({ - address, - evaluationParameters: { evalTo: { blockHeight: currentBlockHeight } }, - }); - - const previousGatewayState = await remoteCacheProvider.getGateway({ - address, - evaluationParameters: { evalTo: { blockHeight: previousBlockHeight } }, - }); - expect( - previousGatewayState.weights.tenureWeight === - gateway.weights.tenureWeight, - ).toBe(false); - }); - - it('should return gateway state at a given sort key', async () => { - const gateway = await remoteCacheProvider.getGateway({ - address, - evaluationParameters: { evalTo: { sortKey: currentSortKey.toString() } }, - }); - expect(gateway).toBeDefined(); - }); - - it('should return gateways state at a given block height', async () => { - const gateways = await remoteCacheProvider.getGateways({ - evaluationParameters: { evalTo: { blockHeight: currentBlockHeight } }, - }); - expect(gateways[address]).toBeDefined(); - }); - - it('should return gateways state at a given sort key', async () => { - const gateways = await remoteCacheProvider.getGateways({ - evaluationParameters: { evalTo: { sortKey: currentSortKey.toString() } }, - }); - expect(gateways[address]).toBeDefined(); - }); -}); diff --git a/tests/arns-remote-cache/records.test.ts b/tests/arns-remote-cache/records.test.ts deleted file mode 100644 index d093e7d5..00000000 --- a/tests/arns-remote-cache/records.test.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { ArNSRemoteCache } from '../../src/common/caches/arns-remote-cache.js'; -import { NotFound } from '../../src/common/error.js'; -import { ARNS_DEVNET_REGISTRY_TX } from '../../src/constants.js'; -import { SmartWeaveSortKey } from '../../src/utils/index.js'; - -describe('ArNSRemoteCache ~ RECORDS', () => { - const remoteCacheProvider = new ArNSRemoteCache({ - contractTxId: ARNS_DEVNET_REGISTRY_TX, - }); - // records tests - it('should fetch a record', async () => { - const record = await remoteCacheProvider.getArNSRecord({ - domain: 'ar-io', - }); - expect(record).toBeDefined(); - }); - - it('should throw NotFound error on non existent record', async () => { - const error = await remoteCacheProvider - .getArNSRecord({ - domain: 'some-domain', - }) - .catch((e) => e); - expect(error).toBeInstanceOf(NotFound); - }); - - it('should fetch all records', async () => { - const records = await remoteCacheProvider.getArNSRecords(); - - expect(records).toBeDefined(); - }); - - it('should return record at a given block height', async () => { - const domain = 'testing5'; - const registrationBlockHeight = 1363242; - const currentRecord = await remoteCacheProvider.getArNSRecord({ - domain, - evaluationParameters: { - evalTo: { blockHeight: registrationBlockHeight + 1 }, - }, - }); - expect(currentRecord).toBeDefined(); - - const error = await remoteCacheProvider - .getArNSRecord({ - domain, - evaluationParameters: { - evalTo: { blockHeight: registrationBlockHeight - 1 }, - }, - }) - .catch((e) => e); - expect(error).toBeInstanceOf(NotFound); - }); - - it('should return record at a given sort key', async () => { - const domain = 'testing5'; - const registrationSortKey = new SmartWeaveSortKey( - '000001363242,0000000000000,e7ac482567afa26cf205b158af46bf99f12b1dea0c1dd00caf9a573c8e648430', - ); - const record = await remoteCacheProvider.getArNSRecord({ - domain, - evaluationParameters: { - evalTo: { sortKey: registrationSortKey.toString() }, - }, - }); - expect(record).toBeDefined(); - }); - - it('should return records at a given block height', async () => { - const domain = 'testing5'; - const registrationBlockHeight = 1363242; - const currentRecords = await remoteCacheProvider.getArNSRecords({ - evaluationParameters: { - evalTo: { blockHeight: registrationBlockHeight }, - }, - }); - expect(currentRecords[domain]).toBeDefined(); - - const previousRecords = await remoteCacheProvider.getArNSRecords({ - evaluationParameters: { - evalTo: { blockHeight: registrationBlockHeight - 1 }, - }, - }); - expect(previousRecords[domain]).not.toBeDefined(); - }); - - it('should return records at a given sort key', async () => { - const domain = 'testing5'; - const registrationSortKey = new SmartWeaveSortKey( - '000001363242,0000000000000,e7ac482567afa26cf205b158af46bf99f12b1dea0c1dd00caf9a573c8e648430', - ); - const records = await remoteCacheProvider.getArNSRecords({ - evaluationParameters: { - evalTo: { sortKey: registrationSortKey.toString() }, - }, - }); - expect(records[domain]).toBeDefined(); - }); -}); diff --git a/src/utils/arweave.test.ts b/tests/utils/arweave.test.ts similarity index 73% rename from src/utils/arweave.test.ts rename to tests/utils/arweave.test.ts index 5710cfe6..88a9a372 100644 --- a/src/utils/arweave.test.ts +++ b/tests/utils/arweave.test.ts @@ -1,5 +1,5 @@ -import { ARNS_DEVNET_REGISTRY_TX } from '../constants.js'; -import { validateArweaveId } from './arweave.js'; +import { ARNS_DEVNET_REGISTRY_TX } from '../../src/constants.js'; +import { validateArweaveId } from '../../src/utils/arweave.js'; describe('Arweave ID Validation', () => { it('should validate a valid Arweave ID', () => { diff --git a/tsconfig.json b/tsconfig.json index 5f894ed0..c8ca7a46 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -24,6 +24,6 @@ "skipLibCheck": true, "strictNullChecks": true }, - "include": ["src"], + "include": ["src", "tests/utils/arweave.test.ts"], "exclude": ["lib", "node_modules", "bundles"] } diff --git a/yarn.lock b/yarn.lock index fb2d95c9..3826436e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2162,17 +2162,7 @@ arrify@^1.0.1: resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" integrity sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA== -arweave@1.13.7: - version "1.13.7" - resolved "https://registry.yarnpkg.com/arweave/-/arweave-1.13.7.tgz#cda8534c833baec372a7052c61f53b4e39a886d7" - integrity sha512-Hv+x2bSI6UyBHpuVbUDMMpMje1ETfpJWj52kKfz44O0IqDRi/LukOkkDUptup1p6OT6KP1/DdpnUnsNHoskFeA== - dependencies: - arconnect "^0.4.2" - asn1.js "^5.4.1" - base64-js "^1.5.1" - bignumber.js "^9.0.2" - -arweave@^1.10.13, arweave@^1.13.7, arweave@^1.14.4: +arweave@1.14.4, arweave@^1.10.13, arweave@^1.13.7, arweave@^1.14.4: version "1.14.4" resolved "https://registry.yarnpkg.com/arweave/-/arweave-1.14.4.tgz#5ba22136aa0e7fd9495258a3931fb770c9d6bf21" integrity sha512-tmqU9fug8XAmFETYwgUhLaD3WKav5DaM4p1vgJpEj/Px2ORPPMikwnSySlFymmL2qgRh2ZBcZsg11+RXPPGLsA== @@ -8074,13 +8064,13 @@ warp-arbundles@^1.0.4: buffer "^6.0.3" warp-isomorphic "^1.0.7" -warp-contracts@1.4.29: - version "1.4.29" - resolved "https://registry.yarnpkg.com/warp-contracts/-/warp-contracts-1.4.29.tgz#fbab7b64ad055f7245229d9bfab992161fda2f33" - integrity sha512-cwNgKmKl5vMd5ptLeyPqMug5IPCyQIJZMalQ7FBFZjLMC7atlMcNYDKgaBHi14vXjtTtw1g+V6BRETwQhqCfzA== +warp-contracts@^1.4.37: + version "1.4.37" + resolved "https://registry.yarnpkg.com/warp-contracts/-/warp-contracts-1.4.37.tgz#d22d34750bb50638188ca40d53015207688d1f58" + integrity sha512-jDQQF4FqRCy6OAnsb87d9LMzVp18eRTFyDQp9QirLD2C7IwBAH2DVrfpVcyPeZuxw4bNz3CSVemdMS+oFJIldQ== dependencies: archiver "^5.3.0" - arweave "1.13.7" + arweave "1.14.4" async-mutex "^0.4.0" bignumber.js "9.1.1" events "3.3.0" From a3d9801a81f0d5d393bbcc506c0e7b656bc0375a Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Fri, 8 Mar 2024 08:41:26 -0600 Subject: [PATCH 12/15] chore: update github action to run against local servcie, formatting --- .github/workflows/build.yml | 2 +- README.md | 4 ++-- docker-compose.yaml | 6 +++--- tests/ar-io.test.ts | 33 ++++++++++++++++++--------------- 4 files changed, 24 insertions(+), 21 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index aa7d3cc9..74fa8b39 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: node_version: [18.x, 20.x] - command: ['lint', 'format', 'test', 'build'] + command: ['lint', 'format', 'test:integration', 'build'] steps: - uses: actions/checkout@v4 diff --git a/README.md b/README.md index 3a943a51..c7e63e3d 100644 --- a/README.md +++ b/README.md @@ -149,14 +149,14 @@ const remoteCustomArIO = new ArIO({ const localCustomArIO = new ArIO({ contract: new WarpContract({ contractTxId: 'TESTNET_CONTRACT_TX_ID', - }) + }), }); // provide a custom contract to the client, and specify local evaluation using remote cache const remoteCacheCustomArIO = new ArIO({ contract: new RemoteContract({ contractTxId: 'TESTNET_CONTRACT_TX_ID', - }) + }), }); ``` diff --git a/docker-compose.yaml b/docker-compose.yaml index 8b0dc80c..f856b1db 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,18 +1,18 @@ version: '3' services: - arns-service: + arns-service: image: ghcr.io/ar-io/arns-service:latest build: . ports: - - "3000:3000" + - '3000:3000' environment: - LOG_LEVEL=debug - PREFETCH_CONTRACTS=true - PREFETCH_CONTRACT_IDS=_NctcA2sRy1-J4OmIQZbYFPM17piNcbdBPH2ncX2RL8 - 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/tests/ar-io.test.ts b/tests/ar-io.test.ts index 26bfce4d..79238dbb 100644 --- a/tests/ar-io.test.ts +++ b/tests/ar-io.test.ts @@ -14,7 +14,7 @@ describe('ArIO Client', () => { const arioClient = new ArIO({ contract: new RemoteContract({ url: process.env.REMOTE_CACHE_URL || 'http://localhost:3000', - contractTxId: ARNS_DEVNET_REGISTRY_TX + contractTxId: ARNS_DEVNET_REGISTRY_TX, }), }); it('should create a custom ArIO client', () => { @@ -22,8 +22,10 @@ describe('ArIO Client', () => { }); it('should should return undefined for non existent gateway', async () => { - const nonExistent = await arioClient.getGateway({ address: 'some-address' }) - expect(nonExistent).toEqual(undefined) + const nonExistent = await arioClient.getGateway({ + address: 'some-address', + }); + expect(nonExistent).toEqual(undefined); }); it('should return gateway state at a given block height', async () => { @@ -56,14 +58,16 @@ describe('ArIO Client', () => { expect(gateways[gatewayAddress]).toBeDefined(); }); - it('should fetch a record', async () => { + it('should fetch a record', async () => { const record = await arioClient.getArNSRecord({ domain: 'ar-io' }); expect(record).toBeDefined(); }); it('should throw NotFound error on non existent record', async () => { - const nonExistent = await arioClient.getArNSRecord({ domain: 'some-domain' }) - expect(nonExistent).toEqual(undefined) + const nonExistent = await arioClient.getArNSRecord({ + domain: 'some-domain', + }); + expect(nonExistent).toEqual(undefined); }); it('should fetch all records', async () => { @@ -80,15 +84,14 @@ describe('ArIO Client', () => { }); expect(currentRecord).toBeDefined(); - const nonExistent = await arioClient - .getArNSRecord({ - domain, - evaluationOptions: { - evalTo: { blockHeight: 0 }, - }, - }) - - expect(nonExistent).toEqual(undefined) + const nonExistent = await arioClient.getArNSRecord({ + domain, + evaluationOptions: { + evalTo: { blockHeight: 0 }, + }, + }); + + expect(nonExistent).toEqual(undefined); }); it('should return record at a given sort key', async () => { From 1c0897e0ba266dfd153582b41c5a24e7d941fd7a Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Fri, 8 Mar 2024 08:45:10 -0600 Subject: [PATCH 13/15] chore: pin warp to 1.4.29 to avoid build issues --- package.json | 2 +- yarn.lock | 22 ++++++++++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 467e197f..d6a0af53 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "dependencies": { "arweave": "^1.14.4", "axios": "1.4.0", - "warp-contracts": "^1.4.37", + "warp-contracts": "1.4.29", "winston": "^3.11.0" } } diff --git a/yarn.lock b/yarn.lock index 3826436e..fb2d95c9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2162,7 +2162,17 @@ arrify@^1.0.1: resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" integrity sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA== -arweave@1.14.4, arweave@^1.10.13, arweave@^1.13.7, arweave@^1.14.4: +arweave@1.13.7: + version "1.13.7" + resolved "https://registry.yarnpkg.com/arweave/-/arweave-1.13.7.tgz#cda8534c833baec372a7052c61f53b4e39a886d7" + integrity sha512-Hv+x2bSI6UyBHpuVbUDMMpMje1ETfpJWj52kKfz44O0IqDRi/LukOkkDUptup1p6OT6KP1/DdpnUnsNHoskFeA== + dependencies: + arconnect "^0.4.2" + asn1.js "^5.4.1" + base64-js "^1.5.1" + bignumber.js "^9.0.2" + +arweave@^1.10.13, arweave@^1.13.7, arweave@^1.14.4: version "1.14.4" resolved "https://registry.yarnpkg.com/arweave/-/arweave-1.14.4.tgz#5ba22136aa0e7fd9495258a3931fb770c9d6bf21" integrity sha512-tmqU9fug8XAmFETYwgUhLaD3WKav5DaM4p1vgJpEj/Px2ORPPMikwnSySlFymmL2qgRh2ZBcZsg11+RXPPGLsA== @@ -8064,13 +8074,13 @@ warp-arbundles@^1.0.4: buffer "^6.0.3" warp-isomorphic "^1.0.7" -warp-contracts@^1.4.37: - version "1.4.37" - resolved "https://registry.yarnpkg.com/warp-contracts/-/warp-contracts-1.4.37.tgz#d22d34750bb50638188ca40d53015207688d1f58" - integrity sha512-jDQQF4FqRCy6OAnsb87d9LMzVp18eRTFyDQp9QirLD2C7IwBAH2DVrfpVcyPeZuxw4bNz3CSVemdMS+oFJIldQ== +warp-contracts@1.4.29: + version "1.4.29" + resolved "https://registry.yarnpkg.com/warp-contracts/-/warp-contracts-1.4.29.tgz#fbab7b64ad055f7245229d9bfab992161fda2f33" + integrity sha512-cwNgKmKl5vMd5ptLeyPqMug5IPCyQIJZMalQ7FBFZjLMC7atlMcNYDKgaBHi14vXjtTtw1g+V6BRETwQhqCfzA== dependencies: archiver "^5.3.0" - arweave "1.14.4" + arweave "1.13.7" async-mutex "^0.4.0" bignumber.js "9.1.1" events "3.3.0" From d484544a843dc77b357815f8fa84e3994256933f Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Fri, 8 Mar 2024 09:02:06 -0600 Subject: [PATCH 14/15] chore: move type files up and cleanup exports --- src/{types => }/arns-service.ts | 0 src/{types => }/common.ts | 0 src/common/ar-io.ts | 4 ++-- src/common/contracts/index.ts | 18 ------------------ src/common/contracts/remote-contract.ts | 2 +- src/common/contracts/warp-contract.ts | 16 +++++++--------- src/common/http.ts | 2 +- src/common/index.ts | 5 ++++- src/common/logger.ts | 2 +- src/{types => }/contract-state.ts | 0 src/node/index.ts | 2 +- src/{types/index.ts => types.ts} | 1 - src/utils/arweave.ts | 2 +- src/utils/smartweave.ts | 2 +- src/web/index.ts | 2 +- tests/ar-io.test.ts | 4 ++-- tests/utils/arweave.test.ts | 14 -------------- tsconfig.json | 4 ++-- 18 files changed, 24 insertions(+), 56 deletions(-) rename src/{types => }/arns-service.ts (100%) rename src/{types => }/common.ts (100%) delete mode 100644 src/common/contracts/index.ts rename src/{types => }/contract-state.ts (100%) rename src/{types/index.ts => types.ts} (99%) delete mode 100644 tests/utils/arweave.test.ts diff --git a/src/types/arns-service.ts b/src/arns-service.ts similarity index 100% rename from src/types/arns-service.ts rename to src/arns-service.ts diff --git a/src/types/common.ts b/src/common.ts similarity index 100% rename from src/types/common.ts rename to src/common.ts diff --git a/src/common/ar-io.ts b/src/common/ar-io.ts index 8356dc67..b5679b16 100644 --- a/src/common/ar-io.ts +++ b/src/common/ar-io.ts @@ -22,8 +22,8 @@ import { EvaluationParameters, Gateway, SmartWeaveContract, -} from '../types/index.js'; -import { RemoteContract } from './index.js'; +} 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 = diff --git a/src/common/contracts/index.ts b/src/common/contracts/index.ts deleted file mode 100644 index 624451ad..00000000 --- a/src/common/contracts/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * 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 . - */ -export * from './remote-contract.js'; -export * from './warp-contract.js'; diff --git a/src/common/contracts/remote-contract.ts b/src/common/contracts/remote-contract.ts index c2c27549..8679f1c7 100644 --- a/src/common/contracts/remote-contract.ts +++ b/src/common/contracts/remote-contract.ts @@ -19,7 +19,7 @@ import { HTTPClient, Logger, SmartWeaveContract, -} from '../../types/index.js'; +} from '../../types.js'; import { AxiosHTTPService } from '../http.js'; import { DefaultLogger } from '../logger.js'; diff --git a/src/common/contracts/warp-contract.ts b/src/common/contracts/warp-contract.ts index 2fbce91c..bc664f5b 100644 --- a/src/common/contracts/warp-contract.ts +++ b/src/common/contracts/warp-contract.ts @@ -21,14 +21,9 @@ import { defaultCacheOptions, } from 'warp-contracts'; -import { EvaluationParameters, SmartWeaveContract } from '../../types/index.js'; +import { EvaluationParameters, SmartWeaveContract } from '../../types.js'; import { FailedRequestError } from '../error.js'; -export const defaultWarpClient = WarpFactory.forMainnet({ - ...defaultCacheOptions, - inMemory: true, // default to in memory for now, a custom warp implementation can be provided -}); - export class WarpContract implements SmartWeaveContract { private contract: Contract; private contractTxId: string; @@ -37,18 +32,21 @@ export class WarpContract implements SmartWeaveContract { constructor({ contractTxId, cacheUrl, - warp = defaultWarpClient, + warp = WarpFactory.forMainnet({ + ...defaultCacheOptions, + inMemory: true, // default to in memory for now, a custom warp implementation can be provided + }), }: { + contractTxId: string; cacheUrl?: string; warp: Warp; - contractTxId: string; }) { - // sync state this.contract = warp.contract(contractTxId); this.cacheUrl = cacheUrl; } private async syncState() { + // TODO: get contract manifest and set it before evaluating if (this.cacheUrl !== undefined) { await this.contract.syncState( `${this.cacheUrl}/v1/contract/${this.contractTxId}`, diff --git a/src/common/http.ts b/src/common/http.ts index 7e566f0c..1c5f5cb3 100644 --- a/src/common/http.ts +++ b/src/common/http.ts @@ -16,7 +16,7 @@ */ import { AxiosInstance } from 'axios'; -import { HTTPClient, Logger } from '../types/index.js'; +import { HTTPClient, Logger } from '../types.js'; import { createAxiosInstance } from '../utils/index.js'; import { FailedRequestError, NotFound, UnknownError } from './error.js'; import { DefaultLogger } from './logger.js'; diff --git a/src/common/index.ts b/src/common/index.ts index be44392a..e21bcabd 100644 --- a/src/common/index.ts +++ b/src/common/index.ts @@ -15,6 +15,9 @@ * along with this program. If not, see . */ export * from './ar-io.js'; -export * from './contracts/index.js'; export * from './error.js'; export * from './logger.js'; + +// contracts +export * from './contracts/remote-contract.js'; +export * from './contracts/warp-contract.js'; diff --git a/src/common/logger.ts b/src/common/logger.ts index 623c25b0..25aeb7f8 100644 --- a/src/common/logger.ts +++ b/src/common/logger.ts @@ -18,7 +18,7 @@ import 'setimmediate'; import winston, { createLogger, format, transports } from 'winston'; -import { Logger } from '../types/index.js'; +import { Logger } from '../types.js'; import { version } from '../version.js'; export class DefaultLogger implements Logger { diff --git a/src/types/contract-state.ts b/src/contract-state.ts similarity index 100% rename from src/types/contract-state.ts rename to src/contract-state.ts diff --git a/src/node/index.ts b/src/node/index.ts index d71e24b7..f72e7bc6 100644 --- a/src/node/index.ts +++ b/src/node/index.ts @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -export * from '../types/index.js'; +export * from '../types.js'; export * from '../common/index.js'; export * from '../constants.js'; export * from '../utils/index.js'; diff --git a/src/types/index.ts b/src/types.ts similarity index 99% rename from src/types/index.ts rename to src/types.ts index 476e1888..3b6b66f9 100644 --- a/src/types/index.ts +++ b/src/types.ts @@ -14,7 +14,6 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ - export * from './arns-service.js'; export * from './contract-state.js'; export * from './common.js'; diff --git a/src/utils/arweave.ts b/src/utils/arweave.ts index f5df514f..8b65fdef 100644 --- a/src/utils/arweave.ts +++ b/src/utils/arweave.ts @@ -14,8 +14,8 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ +import { BlockHeight } from '../common.js'; import { ARWEAVE_TX_REGEX } from '../constants.js'; -import { BlockHeight } from '../types/common.js'; export const validateArweaveId = (id: string): boolean => { return ARWEAVE_TX_REGEX.test(id); diff --git a/src/utils/smartweave.ts b/src/utils/smartweave.ts index c13fb4ac..762f4aee 100644 --- a/src/utils/smartweave.ts +++ b/src/utils/smartweave.ts @@ -14,8 +14,8 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ +import { SortKey } from '../common.js'; import { SORT_KEY_REGEX } from '../constants.js'; -import { SortKey } from '../types/common.js'; export function isSortKey(sortKey: string): sortKey is SortKey { return SmartWeaveSortKey.validate(sortKey); diff --git a/src/web/index.ts b/src/web/index.ts index d71e24b7..f72e7bc6 100644 --- a/src/web/index.ts +++ b/src/web/index.ts @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -export * from '../types/index.js'; +export * from '../types.js'; export * from '../common/index.js'; export * from '../constants.js'; export * from '../utils/index.js'; diff --git a/tests/ar-io.test.ts b/tests/ar-io.test.ts index 79238dbb..33457d4b 100644 --- a/tests/ar-io.test.ts +++ b/tests/ar-io.test.ts @@ -1,7 +1,7 @@ import { ArIO } from '../src/common/ar-io.js'; -import { RemoteContract } from '../src/common/index.js'; +import { RemoteContract } from '../src/common/contracts/remote-contract.js'; import { ARNS_DEVNET_REGISTRY_TX } from '../src/constants.js'; -import { ArIOState } from '../src/types/contract-state.js'; +import { ArIOState } from '../src/contract-state.js'; import { SmartWeaveSortKey } from '../src/utils/smartweave.js'; const gatewayAddress = '1H7WZIWhzwTH9FIcnuMqYkTsoyv1OTfGa_amvuYwrgo'; diff --git a/tests/utils/arweave.test.ts b/tests/utils/arweave.test.ts deleted file mode 100644 index 88a9a372..00000000 --- a/tests/utils/arweave.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ARNS_DEVNET_REGISTRY_TX } from '../../src/constants.js'; -import { validateArweaveId } from '../../src/utils/arweave.js'; - -describe('Arweave ID Validation', () => { - it('should validate a valid Arweave ID', () => { - const validId = ARNS_DEVNET_REGISTRY_TX; - expect(validateArweaveId(validId)).toBe(true); - }); - - it('should not validate an invalid Arweave ID', () => { - const invalidId = 'invalid-id'; - expect(validateArweaveId(invalidId)).toBe(false); - }); -}); diff --git a/tsconfig.json b/tsconfig.json index c8ca7a46..4dd6d5f0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -24,6 +24,6 @@ "skipLibCheck": true, "strictNullChecks": true }, - "include": ["src", "tests/utils/arweave.test.ts"], - "exclude": ["lib", "node_modules", "bundles"] + "include": ["src"], + "exclude": ["lib", "node_modules", "bundles", "tests"] } From 86b00a60393c139508c6547600dff1c378e7a3a9 Mon Sep 17 00:00:00 2001 From: dtfiedler Date: Fri, 8 Mar 2024 09:06:11 -0600 Subject: [PATCH 15/15] chore: update tests --- tests/ar-io.test.ts | 49 ++++++++++++++++++--------------------------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/tests/ar-io.test.ts b/tests/ar-io.test.ts index 33457d4b..c12b4336 100644 --- a/tests/ar-io.test.ts +++ b/tests/ar-io.test.ts @@ -11,91 +11,82 @@ const evaluateToSortKey = new SmartWeaveSortKey( '000001376946,0000000000000,18d52956c8e13ae1f557b4e67f6f298b8ffd2a5cd96e42ec24ca649b7401510f', ); describe('ArIO Client', () => { - const arioClient = new ArIO({ + const arIO = new ArIO({ contract: new RemoteContract({ url: process.env.REMOTE_CACHE_URL || 'http://localhost:3000', contractTxId: ARNS_DEVNET_REGISTRY_TX, }), }); it('should create a custom ArIO client', () => { - expect(arioClient).toBeInstanceOf(ArIO); + expect(arIO).toBeInstanceOf(ArIO); }); it('should should return undefined for non existent gateway', async () => { - const nonExistent = await arioClient.getGateway({ + const nonExistent = await arIO.getGateway({ address: 'some-address', }); expect(nonExistent).toEqual(undefined); }); - it('should return gateway state at a given block height', async () => { - const gateway = await arioClient.getGateway({ + it('should return gateways at a given block height', async () => { + const gateway = await arIO.getGateway({ address: gatewayAddress, evaluationOptions: { evalTo: { blockHeight: evaluateToBlockHeight } }, }); expect(gateway).toBeDefined(); }); - it('should return gateway state at a given sort key', async () => { - const gateway = await arioClient.getGateway({ + it('should return gateways at a given sort key', async () => { + const gateway = await arIO.getGateway({ address: gatewayAddress, evaluationOptions: { evalTo: { sortKey: evaluateToSortKey.toString() } }, }); expect(gateway).toBeDefined(); }); - it('should return gateways state at a given block height', async () => { - const gateways = await arioClient.getGateways({ + it('should return gateways at a given block height', async () => { + const gateways = await arIO.getGateways({ evaluationOptions: { evalTo: { blockHeight: evaluateToBlockHeight } }, }); expect(gateways[gatewayAddress]).toBeDefined(); }); - it('should return gateways state at a given sort key', async () => { - const gateways = await arioClient.getGateways({ + it('should return gateways at a given sort key', async () => { + const gateways = await arIO.getGateways({ evaluationOptions: { evalTo: { sortKey: evaluateToSortKey.toString() } }, }); expect(gateways[gatewayAddress]).toBeDefined(); }); - it('should fetch a record', async () => { - const record = await arioClient.getArNSRecord({ domain: 'ar-io' }); + it('should return the record for an existing domain', async () => { + const record = await arIO.getArNSRecord({ domain }); expect(record).toBeDefined(); }); - it('should throw NotFound error on non existent record', async () => { - const nonExistent = await arioClient.getArNSRecord({ + it('should throw return undefined for a non existent record', async () => { + const nonExistent = await arIO.getArNSRecord({ domain: 'some-domain', }); expect(nonExistent).toEqual(undefined); }); it('should fetch all records', async () => { - const records = await arioClient.getArNSRecords(); + const records = await arIO.getArNSRecords(); expect(records).toBeDefined(); }); it('should return record at a given block height', async () => { - const currentRecord = await arioClient.getArNSRecord({ + const currentRecord = await arIO.getArNSRecord({ domain, evaluationOptions: { evalTo: { blockHeight: evaluateToBlockHeight + 1 }, }, }); expect(currentRecord).toBeDefined(); - - const nonExistent = await arioClient.getArNSRecord({ - domain, - evaluationOptions: { - evalTo: { blockHeight: 0 }, - }, - }); - - expect(nonExistent).toEqual(undefined); }); it('should return record at a given sort key', async () => { - const record = await arioClient.getArNSRecord({ + const record = await arIO.getArNSRecord({ domain, evaluationOptions: { evalTo: { sortKey: evaluateToSortKey.toString() }, @@ -105,7 +96,7 @@ describe('ArIO Client', () => { }); it('should return records at a given block height', async () => { - const records = await arioClient.getArNSRecords({ + const records = await arIO.getArNSRecords({ evaluationOptions: { evalTo: { blockHeight: evaluateToBlockHeight }, }, @@ -114,7 +105,7 @@ describe('ArIO Client', () => { }); it('should return records at a given sort key', async () => { - const records = await arioClient.getArNSRecords({ + const records = await arIO.getArNSRecords({ evaluationOptions: { evalTo: { sortKey: evaluateToSortKey.toString() }, },