diff --git a/examples/cjs/yarn.lock b/examples/cjs/yarn.lock index 478690c5..ca268865 100644 --- a/examples/cjs/yarn.lock +++ b/examples/cjs/yarn.lock @@ -555,9 +555,9 @@ axios@1.7.2: proxy-from-env "^1.1.0" axios@^1.4.0: - version "1.6.8" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66" - integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ== + version "1.7.3" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.3.tgz#a1125f2faf702bc8e8f2104ec3a76fab40257d85" + integrity sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw== dependencies: follow-redirects "^1.15.6" form-data "^4.0.0" diff --git a/examples/esm/yarn.lock b/examples/esm/yarn.lock index c6de4064..c4c85e13 100644 --- a/examples/esm/yarn.lock +++ b/examples/esm/yarn.lock @@ -540,9 +540,9 @@ axios@1.7.2: proxy-from-env "^1.1.0" axios@^1.4.0: - version "1.6.8" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66" - integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ== + version "1.7.3" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.3.tgz#a1125f2faf702bc8e8f2104ec3a76fab40257d85" + integrity sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw== dependencies: follow-redirects "^1.15.6" form-data "^4.0.0" diff --git a/examples/vite/yarn.lock b/examples/vite/yarn.lock index f3ec9124..985486f7 100644 --- a/examples/vite/yarn.lock +++ b/examples/vite/yarn.lock @@ -1461,11 +1461,11 @@ brace-expansion@^2.0.1: balanced-match "^1.0.0" braces@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: - fill-range "^7.0.1" + fill-range "^7.1.1" brorand@^1.0.1, brorand@^1.1.0: version "1.1.0" @@ -2298,10 +2298,10 @@ filelist@^1.0.4: dependencies: minimatch "^5.0.1" -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" @@ -4525,10 +4525,10 @@ tweetnacl@^1.0.3: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== -typescript@^4.9.5: - version "4.9.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" - integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +typescript@^5.3.3: + version "5.5.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba" + integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q== undici-types@~5.26.4: version "5.26.5" diff --git a/examples/webpack/yarn.lock b/examples/webpack/yarn.lock index 496f440b..6d192111 100644 --- a/examples/webpack/yarn.lock +++ b/examples/webpack/yarn.lock @@ -2345,9 +2345,9 @@ axios@1.7.2: proxy-from-env "^1.1.0" axios@^1.4.0: - version "1.6.8" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66" - integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ== + version "1.7.3" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.3.tgz#a1125f2faf702bc8e8f2104ec3a76fab40257d85" + integrity sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw== dependencies: follow-redirects "^1.15.6" form-data "^4.0.0" @@ -2515,11 +2515,11 @@ brace-expansion@^2.0.1: balanced-match "^1.0.0" braces@^3.0.2, braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: - fill-range "^7.0.1" + fill-range "^7.1.1" brorand@^1.0.1, brorand@^1.1.0: version "1.1.0" @@ -3688,10 +3688,10 @@ file-loader@^6.2.0: loader-utils "^2.0.0" schema-utils "^3.0.0" -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" diff --git a/package.json b/package.json index 9f2bfd7c..5c04ca67 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ar.io/sdk", - "version": "2.1.0", + "version": "2.2.0-alpha.4", "repository": { "type": "git", "url": "git+https://github.com/ar-io/ar-io-sdk.git" @@ -123,11 +123,12 @@ "@permaweb/aoconnect": "^0.0.57", "arbundles": "0.11.0", "arweave": "1.14.4", - "axios": "1.7.2", + "axios": "1.7.3", "axios-retry": "^4.3.0", "eventemitter3": "^5.0.1", "plimit-lit": "^3.0.1", - "winston": "^3.13.0" + "winston": "^3.13.0", + "zod": "^3.23.8" }, "lint-staged": { "**/*.{ts,js,mjs,cjs,md,json}": [ diff --git a/src/common.ts b/src/common.ts index cab11022..d7aedd7a 100644 --- a/src/common.ts +++ b/src/common.ts @@ -25,6 +25,8 @@ import { } from '@permaweb/aoconnect'; import { Signer } from 'arbundles'; +import { AoSigner } from './token.js'; + export type BlockHeight = number; export type SortKey = string; export type Timestamp = number; @@ -33,7 +35,7 @@ export type TransactionId = string; export type ProcessId = string; // TODO: append this with other configuration options (e.g. local vs. remote evaluation) -export type ContractSigner = Signer | Window['arweaveWallet']; +export type ContractSigner = Signer | Window['arweaveWallet'] | AoSigner; export type WithSigner> = { signer: ContractSigner; } & T; // TODO: optionally allow JWK in place of signer diff --git a/src/common/ant-registry.ts b/src/common/ant-registry.ts index ae2c2a95..69e6c2a1 100644 --- a/src/common/ant-registry.ts +++ b/src/common/ant-registry.ts @@ -37,7 +37,7 @@ export class ANTRegistry { static init({ signer, ...config - }: WithSigner>): AoANTRegistryRead; + }: WithSigner>): AoANTRegistryWrite; static init( config?: OptionalSigner, ): AoANTRegistryRead | AoANTRegistryWrite { diff --git a/src/constants.ts b/src/constants.ts index dd98981a..f4dfec89 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -41,6 +41,6 @@ export const IO_TESTNET_PROCESS_ID = export const ANT_REGISTRY_ID = 'i_le_yKKPVstLTDSmkHRqf-wYphMnwB9OhleiTgMkWc'; export const MIO_PER_IO = 1_000_000; export const AOS_MODULE_ID = 'cbn0KKrBZH7hdNkNokuXLtGryrWM--PjSTBqIzw9Kkk'; -export const ANT_LUA_ID = 'Flwio4Lr08g6s6uim6lEJNnVGD9ylvz0_aafvpiL8FI'; +export const ANT_LUA_ID = 'BVZZ0_ME8uq3zChN6bA6WsPmFhVpGDJ7Rb7l4qoGIDM'; export const DEFAULT_SCHEDULER_ID = '_GQ33BkPtZrqxA84vM8Zk-N2aO0toNNu_C-l-rawrBA'; diff --git a/src/io.ts b/src/io.ts index 47df5fca..d57f0126 100644 --- a/src/io.ts +++ b/src/io.ts @@ -96,11 +96,26 @@ export type AoVaultData = { endTimestamp: Timestamp; }; +export type AoEpochDistributionRewards = { + eligible: Record< + WalletAddress, + { + delegateRewards: Record; + operatorReward: number; + } + >; + // TODO: we could create a new type for this + distributed?: Record; +}; + export type AoEpochDistributionData = { - rewards: Record; - distributedTimestamp: Timestamp; - totalDistributedRewards: number; + rewards: AoEpochDistributionRewards; totalEligibleRewards: number; + totalEligibleObserverReward: number; + totalEligibleGatewayReward: number; + // TODO: we could create a new type for this + distributedTimestamp?: Timestamp; // only set if rewards have been distributed + totalDistributedRewards?: number; // only set if rewards have been distributed }; export type AoArNSReservedNameData = { diff --git a/src/utils/ao.ts b/src/utils/ao.ts index 9a810bf6..93befa5d 100644 --- a/src/utils/ao.ts +++ b/src/utils/ao.ts @@ -16,9 +16,10 @@ */ import { connect, createDataItemSigner } from '@permaweb/aoconnect'; import { createData } from 'arbundles'; +import { z } from 'zod'; import { defaultArweave } from '../common/arweave.js'; -import { AOProcess } from '../common/index.js'; +import { ANTRegistry, AOProcess, Logger } from '../common/index.js'; import { ANT_LUA_ID, ANT_REGISTRY_ID, @@ -27,6 +28,7 @@ import { } from '../constants.js'; import { AoANTRecord, + AoANTState, AoClient, AoSigner, ContractSigner, @@ -59,6 +61,13 @@ export async function spawnANT({ stateContractTxId?: string; antRegistryId?: string; }): Promise { + const registryClient = ANTRegistry.init({ + process: new AOProcess({ + processId: antRegistryId, + ao, + }), + signer: signer, + }); //TODO: cache locally and only fetch if not cached const luaString = (await defaultArweave.transactions.getData(luaCodeTxId, { decode: true, @@ -105,6 +114,8 @@ export async function spawnANT({ }); } + await registryClient.register({ processId }); + return processId; } @@ -143,7 +154,43 @@ export async function evolveANT({ return id; } +export function isAoSigner(value: unknown): value is AoSigner { + const TagSchema = z.object({ + name: z.string(), + value: z.union([z.string(), z.number()]), + }); + + const AoSignerSchema = z + .function() + .args( + z.object({ + data: z.union([z.string(), z.instanceof(Buffer)]), + tags: z.array(TagSchema).optional(), + target: z.string().optional(), + anchor: z.string().optional(), + }), + ) + .returns( + z.promise( + z.object({ + id: z.string(), + raw: z.instanceof(ArrayBuffer), + }), + ), + ); + try { + AoSignerSchema.parse(value); + return true; + } catch { + return false; + } +} + export function createAoSigner(signer: ContractSigner): AoSigner { + if (isAoSigner(signer)) { + return signer; + } + if (!('publicKey' in signer)) { return createDataItemSigner(signer) as AoSigner; } @@ -167,3 +214,42 @@ export function createAoSigner(signer: ContractSigner): AoSigner { return aoSigner; } + +// using passThrough to require the minimum fields and allow others (eg TotalSupply, Logo, etc) +export const AntStateSchema = z + .object({ + Name: z.string(), + Ticker: z.string(), + Owner: z.string(), + Controllers: z.array(z.string()), + Records: z.record( + z.string(), + z + .object({ + transactionId: z.string(), + ttlSeconds: z.number(), + }) + .passthrough(), + ), + Balances: z.record(z.string(), z.number()), + }) + .passthrough(); + +/** + * @param state + * @returns {boolean} + * @throws {z.ZodError} if the state object does not match the expected schema + */ +export function isAoANTState( + state: object, + logger: Logger = Logger.default, +): state is AoANTState { + try { + AntStateSchema.parse(state); + return true; + } catch (error) { + // this allows us to see the path of the error in the object as well as the expected schema on invalid fields + logger.error(error.issues); + return false; + } +} diff --git a/src/version.ts b/src/version.ts index 06c14d22..5c6b180e 100644 --- a/src/version.ts +++ b/src/version.ts @@ -17,4 +17,4 @@ // AUTOMATICALLY GENERATED FILE - DO NOT TOUCH -export const version = '2.1.0'; +export const version = '2.2.0-alpha.4'; diff --git a/tests/e2e/cjs/index.test.js b/tests/e2e/cjs/index.test.js index 05ab78a3..b740c656 100644 --- a/tests/e2e/cjs/index.test.js +++ b/tests/e2e/cjs/index.test.js @@ -1,10 +1,30 @@ -const { describe, it } = require('node:test'); +const { describe, it, before } = require('node:test'); const assert = require('node:assert/strict'); +const fs = require('node:fs'); /** * Ensure that npm link has been ran prior to running these tests * (simply running npm run test:integration will ensure npm link is ran) */ -const { IO, ioDevnetProcessId, ANTRegistry } = require('@ar.io/sdk'); +const { + IO, + ioDevnetProcessId, + ANTRegistry, + ANT, + createAoSigner, + ArweaveSigner, + IOWriteable, + AoANTWriteable, + AoANTRegistryWriteable, +} = require('@ar.io/sdk'); + +const testWalletJSON = fs.readFileSync('../test-wallet.json', { + encoding: 'utf-8', +}); +const testWallet = JSON.parse(testWalletJSON); +const signers = [ + new ArweaveSigner(testWallet), + createAoSigner(new ArweaveSigner(testWallet)), +]; const io = IO.init({ processId: ioDevnetProcessId, @@ -265,6 +285,14 @@ describe('IO', async () => { }); assert.ok(tokenCost); }); + + it('should be able to create IOWriteable with valid signers', async () => { + for (const signer of signers) { + const io = IO.init({ signer }); + + assert(io instanceof IOWriteable); + } + }); }); describe('ANTRegistry', async () => { @@ -276,4 +304,26 @@ describe('ANTRegistry', async () => { assert(Array.isArray(affiliatedAnts.Owned)); assert(Array.isArray(affiliatedAnts.Controlled)); }); + + it('should be able to create AoANTRegistryWriteable with valid signers', async () => { + for (const signer of signers) { + const registry = ANTRegistry.init({ + signer, + }); + assert(registry instanceof AoANTRegistryWriteable); + } + }); +}); + +describe('ANT', async () => { + it('should be able to create ANTWriteable with valid signers', async () => { + for (const signer of signers) { + const ant = ANT.init({ + processId: 'aWI_dq1JH7facsulLuas1X3l5dkKuWtixcZDYMw9mpg', + signer, + }); + + assert(ant instanceof AoANTWriteable); + } + }); }); diff --git a/tests/e2e/esm/index.test.js b/tests/e2e/esm/index.test.js index 98feda2e..704f3a52 100644 --- a/tests/e2e/esm/index.test.js +++ b/tests/e2e/esm/index.test.js @@ -1,7 +1,28 @@ -import { ANTRegistry, IO, ioDevnetProcessId } from '@ar.io/sdk'; +import { + ANT, + ANTRegistry, + AoANTRegistryWriteable, + AoANTWriteable, + ArweaveSigner, + IO, + IOWriteable, + createAoSigner, + ioDevnetProcessId, +} from '@ar.io/sdk'; import { strict as assert } from 'node:assert'; +import fs from 'node:fs'; import { describe, it } from 'node:test'; +const testWalletJSON = fs.readFileSync('../test-wallet.json', { + encoding: 'utf-8', +}); + +const testWallet = JSON.parse(testWalletJSON); +const signers = [ + new ArweaveSigner(testWallet), + createAoSigner(new ArweaveSigner(testWallet)), +]; + /** * Ensure that npm link has been ran prior to running these tests * (simply running npm run test:integration will ensure npm link is ran) @@ -10,6 +31,7 @@ import { describe, it } from 'node:test'; const io = IO.init({ processId: ioDevnetProcessId, }); + describe('IO', async () => { it('should be able to get the process information', async () => { const epoch = await io.getInfo(); @@ -266,6 +288,14 @@ describe('IO', async () => { }); assert.ok(tokenCost); }); + + it('should be able to create IOWriteable with valid signers', async () => { + for (const signer of signers) { + const io = IO.init({ signer }); + + assert(io instanceof IOWriteable); + } + }); }); describe('ANTRegistry', async () => { @@ -277,4 +307,26 @@ describe('ANTRegistry', async () => { assert(Array.isArray(affiliatedAnts.Owned)); assert(Array.isArray(affiliatedAnts.Controlled)); }); + + it('should be able to create AoANTRegistryWriteable with valid signers', async () => { + for (const signer of signers) { + const registry = ANTRegistry.init({ + signer, + }); + assert(registry instanceof AoANTRegistryWriteable); + } + }); +}); + +describe('ANT', async () => { + it('should be able to create ANTWriteable with valid signers', async () => { + for (const signer of signers) { + const ant = ANT.init({ + processId: 'aWI_dq1JH7facsulLuas1X3l5dkKuWtixcZDYMw9mpg', + signer, + }); + + assert(ant instanceof AoANTWriteable); + } + }); }); diff --git a/tests/e2e/test-wallet.json b/tests/e2e/test-wallet.json new file mode 100644 index 00000000..c1243469 --- /dev/null +++ b/tests/e2e/test-wallet.json @@ -0,0 +1,11 @@ +{ + "kty": "RSA", + "n": "s-Whr7NsoyY0x5nFZDAXIzLymEQvK-9ZW_-cH7TKfnxJn24Z9KndUl8fuE_7QRP2VUmlcTM-YjeS7maw68CZrk_2VKbxnN1Gcvj1SqfuvdnZ0aDgD-FQVEpnOWtAToAEZrV-mwiXBHGEIr8_ntmw4TZJwXmNjVUEhA4rVekKDGHD2k-mooc1wliNG-WMS5e-6Qbor_WNbI0G7HI1WJ46qgMZr4aK2OV1WkwL7Z-5xtSUKLJJJ9Uw1BJvjxaOPo3Eu6zJGxnTGMt3xKSD4ZnJV5H16W9609OTURLezVoIQ8VxmF5W_2sawueAp3g2x4dAwaBIhrfQVjn-Fq7bpTKFg1tlfuzIruh0KUXqftYfWpD3qJxHZLnUJl90oCLUw5oDs7Fvx23TToy92ouZdeMhHjIkNRDBGMAuOzrE5qz-f973LDR0Hz88wzBZ7mZDvSA_UEPKVoRZ99ON_w79BU-Pw1XlvYRAAEDrC1zWojVxMGCqRaWR4OMbQE5eWGTE-5tlpb2zuc970ihdwEmS54V6SLzx9mVAy6WfgWMLxYyqjz30538D8ZbUn8mGk4-WVoaXgncPU2F9bVs3bNaTuAWZZxAPZ7E139NTIrW-MbH_nTgch09vq7giI8XEWryWzRSGmZl-IoRN4Nnn2FyoX8nuk7QyZC2j-6y5j4jWXvkJABE", + "e": "AQAB", + "d": "PE0s9YhfUhDsgDu7Puof11yslP3GEiQZAA2ed8JSXjOrOhXd_XUzCvl32IB26EmYuN4G5vsWXjXiuqcRhvT4jsWe_KE5PCuwAboR_wRrspfju7EBalFMa_TExSp-U9H1p7gOyEkI1iR29m7FFKpD8DoSXxgvqsBk0x8sx49mHuBmljc81B4elxa3tjIr5Orow5PdS54z2b8sIvXli97-Kx7-7SdcQ3gm7i2vkeeIjm2TfFDG1ONRisTjQEN_StiaqY4xmzP83sLVUsUSr_ys0P3MQINt2LODHhoFNTCItK7qdPiqkNOGFO8k4P4a3qcnvb_Mj9vtqfmmglP6rFVTCzPHWSSZi2HOZlPGQc53Zx33ZS4moNNTo63hwsa4VKqWTNvZoZF9MafuBQYn_GovkaJkKB542VnhGoT-NsA3HISDhffT6U0q-vWqUOIZmgf8Ut3ghS9XiFUjnBn_EdDix_ryxnmrwpWwUk4q15gDCJ-Ych05uzUbtNPk0LdLRPofpcOJ0uPSv9-5r6ouYJoR92ODxXFhcWGq6GFVT9s9ETNMehw8A54J72n5XfnePF8D5qPe4YZw8DgcIAzmrr42jmNI5YSt3lBVfOoe8meYc_a77t0WmTsIia9nnBpgP4dl12s6EAentsFZfvLRU3cvCy1gsk2En3vPMdiRXJUzrkE", + "p": "9nbx4FZClWKyHFygc3djqRyKff44KD7KFMgOSEKbxvioop1lynSLjTsRMMzT31_htXq0COSLBrUmbzoI5iO8GUmU9F_ht1j4sbwcsYmyyLdNsURWZwYCb00LPDyqvqn87J5IuTifJ4CZB8e1EE3H8HrbnB-uKZJGiEhaIiCCs1-t0sGKY9QtuE2Ovly9RGkj6fi9yqIZyF6ySHopWtLQ90GtVh8El4eIL-PeGPBvahbffUNOwEpfc0QIjOBkrHT8wdlXuU3rWFSi7aKd2mVj6QFiq_pF-Z8ESc0NAQlLIY0kbKKv29blcpY9sgKBpTAYMYRj93AdQstNoddRZ5fdyw", + "q": "utth1reY8OFEDIHe48K_pyyPmoMsRCdFhtXEqBljRNaQKQk6ijajPco7ycYTucef1_TlIBbSfm6WropbRyi9e9ebaA0kWNqPYaef9vtYEwdq3f9YfrHXaVXSGmfWXw-fp81QGm6IDRN3YlRn-_6rWB7MynkvYdJSh7K_GRw39TUkcWIrKNbFmPi1InKi-i9VDTQJL1REkQmAduPScjRzsNGmM3Ngqt3PNfejhronSICSGrwbUJ9-C5sauGAIJo090KS2f8p15ljGQ_X0WT9JWnsZ59K8nL5cLOUVZpKc5U0AoS_HqR7O7BIFLJo8BEWiJd_Jmg-Ln6UCOzxWBZleEw", + "dp": "47PYm66WLXXVoCZjhsoSpTbdLLImJ-h6wuBhcZk4Wod5JWPNm1I5a-3aX_-c746h9Qy8MEsVtsi-DZzMg_MX4TT-DRhYbRAiE_L7f3r8Vjwj51Z_jQccUMAZVTmndieOqP1DqvwI7nH88BctzTZPNnoLUx5hxb6Cs35E56qplhcbfM-aj8iDxPbCnlUH96A7sfpBPmis8VWr2RIkCukibo2bGynlECoDRFt94gSgqp8fM5dvtm__53o_fAgEeuBKdL3cMjMu75iiPnIy7Icn2ymQg9rhs1GaoKR1EzQG2aSQtl1HpA_SRB9SOJfgN0FL2NO7l-tY3VD_FDrd1puUKQ", + "dq": "t3YSl9jnpwnl4Ena59EcjyznShOkcL4GO57DWTCkEMCCBmhzO6TtngtjrHZ4g52GSWi_VkRSI6S8-V4KxNExSdilUwIkP8FHqeAE5WBeV0CfIpxE7Q_7qgaDJT3ycp9KaFzjWzBPEFeejcLF3dtrrDeBZwKZDPiN44ISsrrMDktBHrn-GjjVBZ6badkYP4Adh7shkYCxWZ30rcZ9p3fsZx1Qi4-qx9jES_56Zht72mmyCeHLB9uwzABbuc8_8WoX2TT_onTMUX-0GqHwaXgDs3zOMJjuaw9UPRgnbPBib5itF5Vr-ZawH4SJ5AMDDka4L2uL62F7-yDuEe7pntG4VQ", + "qi": "PYzb643Ko2kHMwP7z44xAoldwzvKfKy-5rKjifzpJ-oX3Nsec99goV5JfDXDDmZhJmfqX93L9znEIFmA17dwzwJ6Gho2ReqDacze-SQNyKywuE77b5XXvCx-lVx1DWS0DTR7ykUkf8K6Pl6JKHNpc0FAeK8olk0PdqVf-TvXE8NjHc1vDsxwK2woiUkizyd-Xn054U1Abe_sy7_gwFFCNo7nUFJxk6AG4v4yinbxn8dAmAATh37sPnB3U4Ac4yNfFYSqf6HZzCMyt2UlPZsLKLZP2OtHoYaHWJWoZjfyChbaEleXTvaDIGzrgrnsOH5fsCvGie1zZAs1X7DhvCiTTQ" +} diff --git a/tests/e2e/web/yarn.lock b/tests/e2e/web/yarn.lock index cdf427ca..c7137dfb 100644 --- a/tests/e2e/web/yarn.lock +++ b/tests/e2e/web/yarn.lock @@ -1351,9 +1351,9 @@ axios@1.7.2: proxy-from-env "^1.1.0" axios@^1.4.0: - version "1.6.8" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66" - integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ== + version "1.7.3" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.3.tgz#a1125f2faf702bc8e8f2104ec3a76fab40257d85" + integrity sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw== dependencies: follow-redirects "^1.15.6" form-data "^4.0.0" diff --git a/tests/unit/ant.test.ts b/tests/unit/ant.test.ts new file mode 100644 index 00000000..429d8644 --- /dev/null +++ b/tests/unit/ant.test.ts @@ -0,0 +1,34 @@ +import assert from 'node:assert'; + +import { isAoANTState } from '../../src/utils/ao'; + +const testAoANTState = { + Name: 'TestToken', + Ticker: 'TST', + Denomination: 1, + Owner: ''.padEnd(43, '1'), + Controllers: [''.padEnd(43, '2')], + Records: { + record1: { + transactionId: ''.padEnd(43, '1'), + ttlSeconds: 3600, + }, + }, + Balances: { + [''.padEnd(43, '1')]: 1, + }, + Logo: ''.padEnd(43, '1'), + TotalSupply: 0, + Initialized: true, +}; +describe('ANT', () => { + it('should validate accurate ANT state', () => { + const res = isAoANTState(testAoANTState); + assert.strictEqual(res, true); + }); + + it('should invalidate inaccurate ANT state', () => { + const res = isAoANTState({ ...testAoANTState, Name: 1 }); + assert.strictEqual(res, false); + }); +}); diff --git a/tests/utils.ts b/tests/utils.ts deleted file mode 100644 index 6e8d25ab..00000000 --- a/tests/utils.ts +++ /dev/null @@ -1,186 +0,0 @@ -import Arweave from 'arweave'; -import { JWKInterface } from 'arweave/node/lib/wallet'; -import * as fs from 'fs'; -import path from 'path'; -import { ContractDeploy, SourceType, Warp } from 'warp-contracts'; - -import { WeightedObserver } from '../src/contract-state.js'; - -const oneYearSeconds = 60 * 60 * 24 * 365; - -export async function deployANTContract({ - jwk, - address, - warp, -}: { - jwk: JWKInterface; - address: string; - warp: Warp; -}): Promise { - const src = fs.readFileSync( - path.join(__dirname, '/integration/arlocal/ant-contract/index.js'), - 'utf8', - ); - const state = JSON.parse( - fs.readFileSync( - path.join( - __dirname, - '/integration/arlocal/ant-contract/initial-state.json', - ), - 'utf8', - ), - ); - return warp.deploy({ - wallet: jwk, - src: src, - initState: JSON.stringify({ - ...state, - owner: address, - controllers: [address], - balances: { [address]: 1000000 }, - }), - evaluationManifest: { - evaluationOptions: { - sourceType: SourceType.ARWEAVE, - }, - }, - }); -} - -export async function deployArIOContract({ - jwk, - address, - warp, - arweave, -}: { - jwk: JWKInterface; - address: string; - warp: Warp; - arweave: Arweave; -}): Promise { - const currentBlockTimestamp = (await arweave.blocks.getCurrent()).timestamp; - const src = fs.readFileSync( - path.join(__dirname, '/integration/arlocal/ar-io-contract/index.js'), - 'utf8', - ); - const state = JSON.parse( - fs.readFileSync( - path.join( - __dirname, - '/integration/arlocal/ar-io-contract/initial-state.json', - ), - 'utf8', - ), - ); - - // add the wallet owner as a prescribed observer and as a gateway - const prescribedObservers: WeightedObserver[] = - state.prescribedObservers['0']; - const lastObserver: WeightedObserver = - prescribedObservers.pop() as WeightedObserver; - const newPrescribedObserver: WeightedObserver = { - ...lastObserver, - gatewayAddress: address, - observerAddress: address, - }; - const updatedPrescribedObservers = [ - ...prescribedObservers, - newPrescribedObserver, - ]; - - return warp.deploy({ - wallet: jwk, - src: src, - initState: JSON.stringify({ - ...state, - records: { - ...state.records, - 'test-record': { - contractTxId: 'I-cxQhfh0Zb9UqQNizC9PiLC41KpUeA9hjiVV02rQRw', - endTimestamp: currentBlockTimestamp + oneYearSeconds, - purchasePrice: 0, - startTimestamp: currentBlockTimestamp, - type: 'lease', - undernames: 10, - }, - 'test-extend': { - contractTxId: 'I-cxQhfh0Zb9UqQNizC9PiLC41KpUeA9hjiVV02rQRw', - endTimestamp: currentBlockTimestamp + oneYearSeconds, - purchasePrice: 0, - startTimestamp: currentBlockTimestamp, - type: 'lease', - undernames: 10, - }, - 'test-undername': { - contractTxId: 'I-cxQhfh0Zb9UqQNizC9PiLC41KpUeA9hjiVV02rQRw', - endTimestamp: currentBlockTimestamp + oneYearSeconds, - purchasePrice: 0, - startTimestamp: currentBlockTimestamp, - type: 'lease', - undernames: 10, - }, - }, - owner: address, - balances: { [address]: 100_000_000_000_000 }, - prescribedObservers: { - 0: updatedPrescribedObservers, - }, - }), - evaluationManifest: { - evaluationOptions: { - sourceType: SourceType.ARWEAVE, - }, - }, - }); -} - -export async function createLocalWallet( - arweave: Arweave, - amount = 10_000_000_000_000, -): Promise<{ wallet: JWKInterface; address: string }> { - // ~~ Generate wallet and add funds ~~ - const wallet = await arweave.wallets.generate(); - const address = await arweave.wallets.jwkToAddress(wallet); - // mint some tokens - await arweave.api.get(`/mint/${address}/${amount}`); - - const walletDir = path.join(__dirname, './wallets'); - const walletPath = path.join(walletDir, `${address}.json`); - // save it to local directory - if (!fs.existsSync(walletPath)) { - fs.writeFileSync(walletPath, JSON.stringify(wallet)); - } - - return { - wallet, - address, - }; -} - -export function removeDirectories() { - ['./wallets', './contracts'].forEach((dir) => { - const dirPath = path.join(__dirname, dir); - if (fs.existsSync(dirPath)) { - fs.rmSync(dirPath, { recursive: true }); - } - }); -} - -export function createDirectories() { - ['./wallets', './contracts'].forEach((dir) => { - const dirPath = path.join(__dirname, dir); - if (!fs.existsSync(dirPath)) { - fs.mkdirSync(dirPath); - } - }); -} - -export function mineBlocks({ - arweave, - blocks = 1, -}: { - arweave: Arweave; - blocks?: number; -}) { - return arweave.api.get('/mine/' + blocks); -} diff --git a/yarn.lock b/yarn.lock index b524e48f..a9d1b6d2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2820,10 +2820,10 @@ axios-retry@^4.3.0: dependencies: is-retry-allowed "^2.2.0" -axios@1.7.2, axios@^1.4.0: - version "1.7.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.2.tgz#b625db8a7051fbea61c35a3cbb3a1daa7b9c7621" - integrity sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw== +axios@1.7.3, axios@^1.4.0: + version "1.7.3" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.3.tgz#a1125f2faf702bc8e8f2104ec3a76fab40257d85" + integrity sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw== dependencies: follow-redirects "^1.15.6" form-data "^4.0.0"