diff --git a/.changeset/little-sloths-cry.md b/.changeset/little-sloths-cry.md new file mode 100644 index 00000000..02d9c156 --- /dev/null +++ b/.changeset/little-sloths-cry.md @@ -0,0 +1,7 @@ +--- +"@mimicprotocol/lib-ts": patch +"@mimicprotocol/cli": patch +"@mimicprotocol/test-ts": patch +--- + +Add api query diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f5a63538..f12d3655 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,6 +51,10 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version-file: ".nvmrc" - name: Install shell: bash run: yarn @@ -66,6 +70,10 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version-file: .nvmrc - name: Install shell: bash run: yarn diff --git a/.nvmrc b/.nvmrc index 8fdd954d..7c897d99 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -22 \ No newline at end of file +22.15.1 \ No newline at end of file diff --git a/packages/cli/src/lib/InputsInterfaceGenerator.ts b/packages/cli/src/lib/InputsInterfaceGenerator.ts index 10b18b7d..1c963838 100644 --- a/packages/cli/src/lib/InputsInterfaceGenerator.ts +++ b/packages/cli/src/lib/InputsInterfaceGenerator.ts @@ -44,17 +44,10 @@ function generateImports(inputs: Record): string { } function generateInputsMapping(inputs: Record, originalInputs: ManifestInputs): string { + const variableTypes = new Set(['string', 'Address', 'Bytes', 'BigInt', 'BlockchainToken', 'TokenAmount']) return Object.entries(inputs) .map(([name, type]) => { - const declaration = - type === 'string' || - type === 'Address' || - type === 'Bytes' || - type === 'BigInt' || - type === 'BlockchainToken' || - type === 'TokenAmount' - ? `var ${name}: string | null` - : `const ${name}: ${type}` + const declaration = variableTypes.has(type) ? `var ${name}: string | null` : `const ${name}: ${type}` const originalInput = originalInputs[name] const hasDescription = typeof originalInput === 'object' && !!originalInput.description diff --git a/packages/cli/src/lib/ManifestHandler.ts b/packages/cli/src/lib/ManifestHandler.ts index 297d7308..85851ca3 100644 --- a/packages/cli/src/lib/ManifestHandler.ts +++ b/packages/cli/src/lib/ManifestHandler.ts @@ -4,7 +4,7 @@ import * as fs from 'fs' import { load } from 'js-yaml' import { ZodError } from 'zod' -import { DuplicateEntryError, EmptyManifestError, MoreThanOneEntryError } from '../errors' +import { DuplicateEntryError, EmptyManifestError, GENERIC_SUGGESTION, MoreThanOneEntryError } from '../errors' import { Manifest } from '../types' import { ManifestValidator } from '../validators' @@ -41,9 +41,9 @@ export default { }, } -function mergeIfUnique(list: Record[]) { +function mergeIfUnique(list: Record[] = []) { const merged: Record = {} - for (const obj of list || []) { + for (const obj of list) { const entries = Object.entries(obj) if (entries.length !== 1) throw new MoreThanOneEntryError(entries) const [key, val] = entries[0] @@ -72,9 +72,7 @@ function handleValidationError(command: Command, err: unknown): never { suggestions = err.errors.map((e) => `Fix Field "${e.path.join('.')}" -- ${e.message}`) } else { ;[message, code] = [`Unkown Error: ${err}`, 'UnknownError'] - suggestions = [ - 'Contact the Mimic team for further assistance at our website https://www.mimic.fi/ or discord https://discord.com/invite/cpcyV9EsEg', - ] + suggestions = GENERIC_SUGGESTION } command.error(message, { code, suggestions }) diff --git a/packages/lib-ts/index.ts b/packages/lib-ts/index.ts index 3f018da1..60b9f32e 100644 --- a/packages/lib-ts/index.ts +++ b/packages/lib-ts/index.ts @@ -4,6 +4,7 @@ export * from './src/evm' export * from './src/helpers' export * from './src/intents' export * from './src/log' +export * from './src/queries' export * from './src/storage' export * from './src/svm' export * from './src/tokens' diff --git a/packages/lib-ts/src/environment.ts b/packages/lib-ts/src/environment.ts index 12b93bd4..4ef61a5d 100644 --- a/packages/lib-ts/src/environment.ts +++ b/packages/lib-ts/src/environment.ts @@ -5,6 +5,8 @@ import { evm } from './evm' import { Consensus, ListType, MIMIC_HELPER_ADDRESS } from './helpers' import { Intent } from './intents' import { + ApiQuery, + ApiQueryResponse, EvmCallQuery, EvmCallQueryResponse, RelevantTokensQuery, @@ -38,6 +40,9 @@ export namespace environment { @external('environment', '_subgraphQuery') declare function _subgraphQuery(params: string): string + @external('environment', '_apiQuery') + declare function _apiQuery(params: string): string + @external('environment', '_svmAccountsInfoQuery') declare function _svmAccountsInfoQuery(params: string): string @@ -162,6 +167,20 @@ export namespace environment { return SvmAccountsInfoQueryResponse.fromJson(responseStr).toResult() } + /** + * Executes an HTTP API GET call and returns the raw (stringified) response body. + * @param url - The endpoint URL to call + * @param timestamp - Optional. Cache/snapshot timestamp used to fetch a previously cached response at the given point in time + * @returns A `Result` containing either the response body as a string or an error string + */ + export function apiQuery( + url: string, + timestamp: Date | null = null + ): Result { + const responseStr = _apiQuery(JSON.stringify(ApiQuery.from(url, timestamp))) + return ApiQueryResponse.fromJson(responseStr).toResult() + } + /** * Returns the current execution context containing environment information. * @returns The Context object containing: user, settler, timestamp, consensusThreshold and triggerPayload diff --git a/packages/lib-ts/src/queries/ApiQuery.ts b/packages/lib-ts/src/queries/ApiQuery.ts new file mode 100644 index 00000000..4971fbc8 --- /dev/null +++ b/packages/lib-ts/src/queries/ApiQuery.ts @@ -0,0 +1,36 @@ +import { Result } from '../types' + +import { QueryResponseBase } from './QueryResponse' + +@json +class ApiQueryBase { + constructor(public readonly url: string) {} +} + +@json +export class ApiQuery extends ApiQueryBase { + public readonly timestamp: i64 + + constructor(url: string, timestamp: i64) { + super(url) + this.timestamp = timestamp + } + + static from(url: string, timestamp: Date | null): ApiQueryBase { + return timestamp ? new ApiQuery(url, changetype(timestamp).getTime()) : new ApiQueryBase(url) + } +} + +@json +export class ApiQueryResponse extends QueryResponseBase { + public data: string + + constructor(success: string, data: string, error: string) { + super(success, error) + this.data = data + } + + toResult(): Result { + return this.buildResult(this.data, 'Unknown error getting API response') + } +} diff --git a/packages/lib-ts/src/queries/index.ts b/packages/lib-ts/src/queries/index.ts index c59a2729..f302bf0b 100644 --- a/packages/lib-ts/src/queries/index.ts +++ b/packages/lib-ts/src/queries/index.ts @@ -1,3 +1,4 @@ +export * from './ApiQuery' export * from './EvmCallQuery' export * from './QueryResponse' export * from './RelevantTokensQuery' diff --git a/packages/lib-ts/tests/queries/ApiQuery.spec.ts b/packages/lib-ts/tests/queries/ApiQuery.spec.ts new file mode 100644 index 00000000..495aee85 --- /dev/null +++ b/packages/lib-ts/tests/queries/ApiQuery.spec.ts @@ -0,0 +1,53 @@ +import { ApiQueryResponse } from '../../src/queries' + +describe('ApiQueryResponse', () => { + describe('toResult', () => { + describe('when response is successful', () => { + describe('when data is provided', () => { + it('should return result with data', () => { + const responseData = '{"test": true}' + const response = new ApiQueryResponse('true', responseData, '') + const result = response.toResult() + + expect(result.isOk).toBe(true) + const data = result.unwrap() + expect(data).toBe(responseData) + }) + }) + + describe('when data is empty', () => { + it('should return empty string', () => { + const response = new ApiQueryResponse('true', '', '') + const result = response.toResult() + + expect(result.isOk).toBe(true) + const data = result.unwrap() + expect(data).toBe('') + }) + }) + }) + + describe('when response is not successful', () => { + describe('when error message is provided', () => { + it('should return error with provided message', () => { + const errorMessage = 'Something went wrong' + const response = new ApiQueryResponse('false', '', errorMessage) + const result = response.toResult() + + expect(result.isError).toBe(true) + expect(result.error).toBe(errorMessage) + }) + }) + + describe('when error message is not provided', () => { + it('should return default error message', () => { + const response = new ApiQueryResponse('false', '', '') + const result = response.toResult() + + expect(result.isError).toBe(true) + expect(result.error).toBe('Unknown error getting API response') + }) + }) + }) + }) +}) diff --git a/packages/test-ts/package.json b/packages/test-ts/package.json index c95079bd..c4f3015d 100644 --- a/packages/test-ts/package.json +++ b/packages/test-ts/package.json @@ -14,8 +14,8 @@ "dist" ], "dependencies": { - "@mimicprotocol/runner-node": "~0.1.0", - "@mimicprotocol/sdk": "~0.1.0", + "@mimicprotocol/runner-node": "~0.1.1", + "@mimicprotocol/sdk": "~0.1.5", "ethers": "^6.15.0", "zod": "^3.24.1" }, diff --git a/yarn.lock b/yarn.lock index 43f89355..cde62eb2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -677,58 +677,58 @@ globby "^11.0.0" read-yaml-file "^1.1.0" -"@mimicprotocol/runner-node-darwin-arm64@0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@mimicprotocol/runner-node-darwin-arm64/-/runner-node-darwin-arm64-0.1.0.tgz#45548fa3fc51ec159734f2b420cfbdaf9d66a9a3" - integrity sha512-J3ykKFmW6wMI63h3M4Njrt+XjCvX7oXgrzoVHbDsj/Jx9Pjg5akZFL6fEZ9ljjY6jrmlvCE8DwM2b6eVyG3kEQ== +"@mimicprotocol/runner-node-darwin-arm64@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@mimicprotocol/runner-node-darwin-arm64/-/runner-node-darwin-arm64-0.1.1.tgz#9377fd020402f7461f7ceb7b10e3bc8810a291dd" + integrity sha512-DwInlHFvTi7M6FJsntV0TglJ0UovSw9f57+Mi59e/H9xrfzK/s2GEC/bAZTZJd4VkCzgTSK/H8vebc0oQUIBcw== -"@mimicprotocol/runner-node-darwin-x64@0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@mimicprotocol/runner-node-darwin-x64/-/runner-node-darwin-x64-0.1.0.tgz#60721cc66ad78f643b03e245d0e7c0eb68c72eeb" - integrity sha512-07739siHMJndIsPBHY8nK0Uv00q62j1eybZ34QBEynoh8HniTCapEbTtU8H9ac9QBu2bdx2VfssLNQxE9mbCBQ== +"@mimicprotocol/runner-node-darwin-x64@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@mimicprotocol/runner-node-darwin-x64/-/runner-node-darwin-x64-0.1.1.tgz#48e5c4d3c11138ee8305d8c1b9172463374a6ed8" + integrity sha512-mGnq35OIjlcQluPWCWqnwDUB8mF1/KeL8rt/bQhRPnn6tO7CmkNFvS4XSIq08hbfzql6otYMWoh7jZSDXSWgXQ== -"@mimicprotocol/runner-node-linux-arm64-gnu@0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@mimicprotocol/runner-node-linux-arm64-gnu/-/runner-node-linux-arm64-gnu-0.1.0.tgz#7ae21a7b46009483608e6328fb5814980eaf1c60" - integrity sha512-RUtegSQLZqvYVUtvHcXgGcqitPJMIAEpvPlB41WV6+z7HAU04i6OciIGzOpct1DWvaEKj5lRAGD2M0af17kIVA== +"@mimicprotocol/runner-node-linux-arm64-gnu@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@mimicprotocol/runner-node-linux-arm64-gnu/-/runner-node-linux-arm64-gnu-0.1.1.tgz#ffaf6027629035cb83e9454c143ab1c2868e820d" + integrity sha512-spYnR7wP5wZcX/rRyHUi7k1Gy7fZ3T6qn9I2EDm/u90ZtXW/HO1PobVGdhAo5NKQDYOCOPAQMoAs6X8C07UENA== -"@mimicprotocol/runner-node-linux-x64-gnu@0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@mimicprotocol/runner-node-linux-x64-gnu/-/runner-node-linux-x64-gnu-0.1.0.tgz#d0e0bd7b3d99071564f74f9a1cd9429fa7cb4c4e" - integrity sha512-HfuVdaWJW1t2GWNLYhAIGh+bBskUz21MivJlq4tmYyAHbic+4e0BxCrvpsuAQoks9aJR3y7Snftrc7eSjIompw== +"@mimicprotocol/runner-node-linux-x64-gnu@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@mimicprotocol/runner-node-linux-x64-gnu/-/runner-node-linux-x64-gnu-0.1.1.tgz#d854f05f3e95544b3f51a4f52dd251957020ad7e" + integrity sha512-Xzu9eVGTnSGvbfh8lssfNaTuFDlAnClA4y94IRZRCfTMsR2CYTcOJXp4t4/LOnS8nOPuzZM47FUtu4oDxGJvhw== -"@mimicprotocol/runner-node-linux-x64-musl@0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@mimicprotocol/runner-node-linux-x64-musl/-/runner-node-linux-x64-musl-0.1.0.tgz#d7de018fee7ac76094d6aee7ae6a28619eaf0dfe" - integrity sha512-QUOqXbtTaruJu+MffmtqwK4+pKhG11uAZQpoV3IoNWOSCugu2PL8glDgn24NN0mN4fzzFy66xB/5zJJlhMEWtQ== +"@mimicprotocol/runner-node-linux-x64-musl@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@mimicprotocol/runner-node-linux-x64-musl/-/runner-node-linux-x64-musl-0.1.1.tgz#af877e33b45dab19ee4ce2768a744f02bb802c7f" + integrity sha512-NTU6m9Wi9o6P0B1tXXaIuayHXiC5f5fw1gFPoSX9DxGGyIPSE4dr5ovtzhhl4qKI2WobjqPW4C9D9roYmOzGjQ== -"@mimicprotocol/runner-node-win32-arm64-msvc@0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@mimicprotocol/runner-node-win32-arm64-msvc/-/runner-node-win32-arm64-msvc-0.1.0.tgz#991b4b930d857e7311c4b67d77210726ebde89f4" - integrity sha512-BgzVfTPhuzeNLD3sUt1c3VTcgmWP3JLOsv+n/Op6SgZIfGsa84uw3peIPh4OZSBmqOpWTjvXLHxjI9D2scxnhg== +"@mimicprotocol/runner-node-win32-arm64-msvc@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@mimicprotocol/runner-node-win32-arm64-msvc/-/runner-node-win32-arm64-msvc-0.1.1.tgz#5fbac9d58719a5a9ca7a183ecadcd39d7a624eb1" + integrity sha512-O7mnWTcmNcPtnRI48Aq//yy04QohgbFmE/dsw68IiGMdM9SmR6Vy1UwMZRojvCbwO3ZWH0ByGA1qIO9UH+wg3Q== -"@mimicprotocol/runner-node-win32-x64-msvc@0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@mimicprotocol/runner-node-win32-x64-msvc/-/runner-node-win32-x64-msvc-0.1.0.tgz#640034312e7248ff178b33c01474ed0b44ed2585" - integrity sha512-kCefqdkMWj3s2gT3DROFhKPwO4viA/ilIL11baAlQiZaab1SnvKyE+sm7mf9knj5NKSSAuah7XyeRoI9tlgZpA== +"@mimicprotocol/runner-node-win32-x64-msvc@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@mimicprotocol/runner-node-win32-x64-msvc/-/runner-node-win32-x64-msvc-0.1.1.tgz#6e19c17dd45232552b01501978e6bbcdc039b9e2" + integrity sha512-ESNR8IDkXl0kLuGyRxe4+PV6tbIA5pDa//+FNyinUhJP6zb9Lf9K+1HXDe97PvFq2H4PwIM/UXeIHUPpXKypGw== -"@mimicprotocol/runner-node@~0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@mimicprotocol/runner-node/-/runner-node-0.1.0.tgz#33fa8e8088e80083ad4506bfbc5b86838e9e9082" - integrity sha512-3gy4ZQPIGaeZREwEO57GzQLcTSi8QdJ53AuFlHF5TDtbL3t5r9mksK3CjJ5dr5iPzy7IFgbb1pCP7QsRPlHAng== +"@mimicprotocol/runner-node@~0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@mimicprotocol/runner-node/-/runner-node-0.1.1.tgz#447e67634929a22dcddf053df69957009542923c" + integrity sha512-r9L/FF8q0T9qnmQ+2vkw4nauD1dT4sLtA77Hupjg1U0nEM8mM0KyIi0q8l+tzgvX7n4aNvbqxIfXIfCO2DmS6A== optionalDependencies: - "@mimicprotocol/runner-node-darwin-arm64" "0.1.0" - "@mimicprotocol/runner-node-darwin-x64" "0.1.0" - "@mimicprotocol/runner-node-linux-arm64-gnu" "0.1.0" - "@mimicprotocol/runner-node-linux-x64-gnu" "0.1.0" - "@mimicprotocol/runner-node-linux-x64-musl" "0.1.0" - "@mimicprotocol/runner-node-win32-arm64-msvc" "0.1.0" - "@mimicprotocol/runner-node-win32-x64-msvc" "0.1.0" - -"@mimicprotocol/sdk@~0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@mimicprotocol/sdk/-/sdk-0.1.0.tgz#a0bb3661cd0129bac253cba6c79dea7b6026b44e" - integrity sha512-wJqjsZC9qQOKi5j7rOutCSHg9aV1/Sg8+nMaLt0IX/z5P1Dk2BBFb8OJc1q1MFW9PZOAJSaFhtZlRxKkzhoopA== + "@mimicprotocol/runner-node-darwin-arm64" "0.1.1" + "@mimicprotocol/runner-node-darwin-x64" "0.1.1" + "@mimicprotocol/runner-node-linux-arm64-gnu" "0.1.1" + "@mimicprotocol/runner-node-linux-x64-gnu" "0.1.1" + "@mimicprotocol/runner-node-linux-x64-musl" "0.1.1" + "@mimicprotocol/runner-node-win32-arm64-msvc" "0.1.1" + "@mimicprotocol/runner-node-win32-x64-msvc" "0.1.1" + +"@mimicprotocol/sdk@~0.1.5": + version "0.1.5" + resolved "https://registry.yarnpkg.com/@mimicprotocol/sdk/-/sdk-0.1.5.tgz#2820867ac4acfa34bf3a0d7809b771cc0ba34173" + integrity sha512-am+smmgE8nl4196TiwVB5EbcYtYAegOc4Gt23wReNHxoB8iPgQIlCLX4n6Fh6tbrhIwbUM82vBqjUz8efQb1Ow== dependencies: "@coral-xyz/anchor" "0.32.1" "@solana/web3.js" "^1.98.4"