diff --git a/README.md b/README.md index b5f18c3..1083f16 100644 --- a/README.md +++ b/README.md @@ -335,7 +335,7 @@ npm run test ```bash # Contracts cd contracts -clarinet test --coverage +npm test # Agent cd agent diff --git a/contracts/package.json b/contracts/package.json index 7de9d9a..3ec3167 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -5,8 +5,8 @@ "description": "V-Mind smart contracts for Stacks blockchain", "scripts": { "dev": "clarinet integrate", - "test": "vitest run", - "test:legacy": "clarinet test --coverage", + "test": "npm run type-check && vitest run", + "type-check": "tsc --project tsconfig.json --noEmit", "check": "clarinet check", "deploy:testnet": "clarinet deployments apply -p deployments/testnet.yaml", "deploy:mainnet": "clarinet deployments apply -p deployments/mainnet.yaml", diff --git a/contracts/tests/alex-liquidity-adapter_test.ts b/contracts/tests/alex-liquidity-adapter_test.ts index c7a3863..0965d2a 100644 --- a/contracts/tests/alex-liquidity-adapter_test.ts +++ b/contracts/tests/alex-liquidity-adapter_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; function mock(account: Account) { return types.principal(`${account.address}.mock-alex-amm`); diff --git a/contracts/tests/helpers/legacy-clarinet.ts b/contracts/tests/helpers/legacy-clarinet.ts new file mode 100644 index 0000000..05018a4 --- /dev/null +++ b/contracts/tests/helpers/legacy-clarinet.ts @@ -0,0 +1,369 @@ +import { tx as sdkTx, type ParsedTransactionResult, type Simnet } from '@hirosystems/clarinet-sdk'; +import { Cl, type ClarityValue } from '@stacks/transactions'; +import { beforeEach, describe, expect, it } from 'vitest'; +import { c32address } from 'c32check'; +import { ADDR, bootSimnet } from './simnet'; + +export interface Account { + address: string; +} + +type LegacySdkSimnet = Simnet & { + mineBlock(txs: Array>): unknown[]; + callReadOnlyFn(contract: string, method: string, args: ClarityValue[], sender: string): unknown; + getAccounts?(): unknown; +}; + +type LegacyTransactionResult = Omit & { + result: LegacyValueAssertions; +}; + +type LegacyBlock = { + receipts: LegacyTransactionResult[]; +}; + +export type Chain = { + mineBlock(txs: Array>): LegacyBlock; + callReadOnlyFn(contract: string, method: string, args: ClarityValue[], sender: string): LegacyTransactionResult; +}; + +export const Tx = { + contractCall(contract: string, method: string, args: ClarityValue[], sender: string) { + return sdkTx.callPublicFn(contract, method, args, sender); + }, +}; + +export const types = { + principal(value: string) { + if (value.includes('.')) { + const [address, contractName] = value.split('.', 2); + if (address && contractName) { + return Cl.contractPrincipal(address, contractName); + } + } + + return Cl.standardPrincipal(value); + }, + ascii(value: string) { + return Cl.stringAscii(value); + }, + utf8(value: string) { + return Cl.stringUtf8(value); + }, + uint(value: number | bigint) { + return Cl.uint(value); + }, + bool(value: boolean) { + return Cl.bool(value); + }, + some(value: ClarityValue) { + return Cl.some(value); + }, + none() { + return Cl.none(); + }, + list(values: ClarityValue[]) { + return Cl.list(values); + }, + tuple(values: Record) { + return Cl.tuple(values); + }, +}; + +type LegacyValueAssertion = { + expectOk(): LegacyValueAssertions; + expectErr(): LegacyValueAssertions; + expectSome(): LegacyValueAssertions; + expectNone(): LegacyValueAssertions; + expectUint(expected?: number | bigint): LegacyValueAssertion; + expectBool(expected?: boolean): LegacyValueAssertion; + expectPrincipal(expected?: string): LegacyValueAssertion; + expectAscii(expected?: string): LegacyValueAssertion; + expectUtf8(expected?: string): LegacyValueAssertion; + expectList(): LegacyListAssertions; +}; + +class LegacyValueAssertions implements LegacyValueAssertion { + constructor(public readonly value: any) {} + + expectOk() { + this.assertType('ok'); + return new LegacyValueAssertions((this.value as { value: ClarityValue }).value); + } + + expectErr() { + this.assertType('err'); + return new LegacyValueAssertions((this.value as { value: ClarityValue }).value); + } + + expectSome() { + this.assertType('some'); + return new LegacyValueAssertions((this.value as { value: ClarityValue }).value); + } + + expectNone() { + this.assertType('none'); + return this; + } + + expectUint(expected?: number | bigint) { + this.assertType('uint'); + + if (expected !== undefined) { + expect(BigInt((this.value as { value: bigint | number }).value)).toBe(BigInt(expected)); + } + + return this; + } + + expectBool(expected?: boolean) { + if (this.value.type !== 'true' && this.value.type !== 'false') { + throw new Error(`Expected bool, got ${this.value.type}`); + } + + if (expected !== undefined) { + expect(this.value.type === 'true').toBe(expected); + } + + return this; + } + + expectPrincipal(expected?: string) { + if (this.value.type !== 'address' && this.value.type !== 'contract') { + throw new Error(`Expected principal, got ${this.value.type}`); + } + + if (expected !== undefined) { + expect(this.value.value).toBe(expected); + } + + return this; + } + + expectAscii(expected?: string) { + this.assertType('ascii'); + + if (expected !== undefined) { + expect((this.value as { value: string }).value).toBe(expected); + } + + return this; + } + + expectUtf8(expected?: string) { + this.assertType('utf8'); + + if (expected !== undefined) { + expect((this.value as { value: string }).value).toBe(expected); + } + + return this; + } + + expectList() { + this.assertType('list'); + return new LegacyListAssertions((this.value as { value: ClarityValue[] }).value); + } + + private assertType(expectedType: string) { + expect(this.value.type).toBe(expectedType); + } +} + +class LegacyListAssertions { + private index = 0; + + constructor(private readonly values: ClarityValue[]) {} + + private consume() { + if (this.index >= this.values.length) { + throw new Error('Expected more list items'); + } + + const value = this.values[this.index]; + if (value === undefined) { + throw new Error('Expected more list items'); + } + + this.index += 1; + return new LegacyValueAssertions(value); + } + + expectUint(expected?: number | bigint) { + this.consume().expectUint(expected); + return this; + } + + expectBool(expected?: boolean) { + this.consume().expectBool(expected); + return this; + } + + expectPrincipal(expected?: string) { + this.consume().expectPrincipal(expected); + return this; + } + + expectAscii(expected?: string) { + this.consume().expectAscii(expected); + return this; + } + + expectUtf8(expected?: string) { + this.consume().expectUtf8(expected); + return this; + } + + expectOk() { + return this.consume().expectOk(); + } + + expectErr() { + return this.consume().expectErr(); + } + + expectSome() { + return this.consume().expectSome(); + } + + expectNone() { + this.consume().expectNone(); + return this; + } + + expectList() { + return this.consume().expectList(); + } +} + +function hasResultProperty(value: unknown): value is { result: ClarityValue } { + return typeof value === 'object' && value !== null && 'result' in value; +} + +function wrapLegacyTransactionResult(rawResult: unknown): LegacyTransactionResult { + const payload = rawResult && typeof rawResult === 'object' ? { ...rawResult } : {}; + const resultValue = hasResultProperty(rawResult) ? rawResult.result : rawResult; + + return { + ...payload, + result: new LegacyValueAssertions(resultValue as ClarityValue), + } as LegacyTransactionResult; +} + +function normalizeAccountName(name: string) { + const walletMatch = name.match(/^wallet[-_]?([0-9]+)$/); + if (walletMatch) { + return `wallet_${walletMatch[1]}`; + } + + return name; +} + +function extractAddress(candidate: unknown): string | undefined { + if (typeof candidate === 'string') { + return candidate; + } + + if (candidate && typeof candidate === 'object') { + const record = candidate as Record; + + if (typeof record.address === 'string') { + return record.address; + } + + if (typeof record.principal === 'string') { + return record.principal; + } + + if (typeof record.stxAddress === 'string') { + return record.stxAddress; + } + } + + return undefined; +} + +function fallbackWalletAddress(walletIndex: number) { + return c32address(26, walletIndex.toString(16).padStart(40, '0')); +} + +function createLegacyAccounts(simnet: LegacySdkSimnet): Map { + const accounts = new Map(); + const rawAccounts = simnet.getAccounts?.(); + + const registerAccount = (name: string, candidate: unknown) => { + const address = extractAddress(candidate); + if (address) { + accounts.set(normalizeAccountName(name), { address }); + } + }; + + if (rawAccounts instanceof Map) { + for (const [name, candidate] of rawAccounts.entries()) { + registerAccount(name, candidate); + } + } else if (rawAccounts && typeof rawAccounts === 'object') { + for (const [name, candidate] of Object.entries(rawAccounts as Record)) { + registerAccount(name, candidate); + } + } + + if (!accounts.has('deployer')) { + accounts.set('deployer', { address: ADDR.deployer }); + } + + const fallbackWallets = [ + ADDR.wallet1, + ADDR.wallet2, + ADDR.wallet3, + ADDR.wallet4, + ADDR.wallet5, + ...Array.from({ length: 27 }, (_, index) => fallbackWalletAddress(index + 6)), + ]; + + for (let index = 1; index <= 32; index += 1) { + const accountName = `wallet_${index}`; + if (!accounts.has(accountName)) { + accounts.set(accountName, { address: fallbackWallets[index - 1] }); + } + } + + return accounts; +} + +function createLegacyChain(simnet: LegacySdkSimnet): Chain { + return { + mineBlock(txs) { + return { + receipts: simnet.mineBlock(txs).map((receipt) => wrapLegacyTransactionResult(receipt)), + }; + }, + callReadOnlyFn(contract, method, args, sender) { + return wrapLegacyTransactionResult(simnet.callReadOnlyFn(contract, method, args, sender)); + }, + }; +} + +type LegacyClarinetTestCase = { + name: string; + fn(chain: Chain, accounts: Map): Promise | void; +}; + +export const Clarinet = { + test(testCase: LegacyClarinetTestCase) { + describe(testCase.name, () => { + let chain: Chain; + let accounts: Map; + + beforeEach(async () => { + const simnet = (await bootSimnet()) as LegacySdkSimnet; + chain = createLegacyChain(simnet); + accounts = createLegacyAccounts(simnet); + }); + + it('runs the legacy test body', async () => { + await testCase.fn(chain, accounts); + }); + }); + }, +}; diff --git a/contracts/tests/helpers/simnet.ts b/contracts/tests/helpers/simnet.ts index 98f9f7e..afe42a8 100644 --- a/contracts/tests/helpers/simnet.ts +++ b/contracts/tests/helpers/simnet.ts @@ -4,6 +4,15 @@ import { expect } from 'vitest'; import fs from 'node:fs'; import path from 'node:path'; +export type TransactionResultLike = Omit & { + result: any; +}; + +export type SimnetLike = Omit & { + mineBlock(calls: Array>): TransactionResultLike[]; + callReadOnlyFn(contract: string, method: string, args: any[], sender: string): TransactionResultLike; +}; + export const ADDR = { deployer: 'ST33898GA3Q16CGXTNXACHEBF7TP5ABREYTTFC138', wallet1: 'ST15022M49CD9GZM1DYX6YTQA726DN9FC8BGTZTES', @@ -62,9 +71,9 @@ function ensureSdkCompatibleSimnetPlan() { fs.writeFileSync(planPath, output.join('\n')); } -export async function bootSimnet(): Promise { +export async function bootSimnet(): Promise { ensureSdkCompatibleSimnetPlan(); - return initSimnet('./Clarinet.toml'); + return initSimnet('./Clarinet.toml') as Promise; } export function p(address: string) { @@ -108,7 +117,7 @@ export function expectOkUint(result: any, value: number) { expect(Number(result.value.value)).toBe(value); } -export function mine(simnet: Simnet, calls: Array>): ParsedTransactionResult[] { +export function mine(simnet: SimnetLike, calls: Array>): TransactionResultLike[] { return simnet.mineBlock(calls); } @@ -121,7 +130,7 @@ export function adapterTraits() { ]; } -export function initializeVaultToken(simnet: Simnet) { +export function initializeVaultToken(simnet: SimnetLike) { return mine(simnet, [ tx.callPublicFn( 'vault-receipt-token', @@ -138,7 +147,7 @@ export function initializeVaultToken(simnet: Simnet) { ]); } -export function registerDefaultAssetAndStrategy(simnet: Simnet) { +export function registerDefaultAssetAndStrategy(simnet: SimnetLike) { return mine(simnet, [ tx.callPublicFn( 'protocol-config', diff --git a/contracts/tests/hermetica-adapter_test.ts b/contracts/tests/hermetica-adapter_test.ts index 1254dbf..4cf6f48 100644 --- a/contracts/tests/hermetica-adapter_test.ts +++ b/contracts/tests/hermetica-adapter_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; function mock(account: Account) { return types.principal(`${account.address}.mock-hermetica-staking`); diff --git a/contracts/tests/protocol-config.deposit-limits_test.ts b/contracts/tests/protocol-config.deposit-limits_test.ts index 8e24684..46ad0d5 100644 --- a/contracts/tests/protocol-config.deposit-limits_test.ts +++ b/contracts/tests/protocol-config.deposit-limits_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'protocol-config: owner can set minimum deposit', diff --git a/contracts/tests/protocol-config.error-codes_test.ts b/contracts/tests/protocol-config.error-codes_test.ts index 3160ed4..35ec26d 100644 --- a/contracts/tests/protocol-config.error-codes_test.ts +++ b/contracts/tests/protocol-config.error-codes_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'protocol-config: set-supported-asset-active on missing asset returns err-asset-not-supported', diff --git a/contracts/tests/protocol-config.fee-overrides_test.ts b/contracts/tests/protocol-config.fee-overrides_test.ts index 5486749..2c18011 100644 --- a/contracts/tests/protocol-config.fee-overrides_test.ts +++ b/contracts/tests/protocol-config.fee-overrides_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'protocol-config: owner can set and remove fee override', diff --git a/contracts/tests/protocol-config.performance-fee_test.ts b/contracts/tests/protocol-config.performance-fee_test.ts index 0733f41..1537982 100644 --- a/contracts/tests/protocol-config.performance-fee_test.ts +++ b/contracts/tests/protocol-config.performance-fee_test.ts @@ -1,6 +1,5 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; -import { assertEquals } from 'https://deno.land/std@0.90.0/testing/asserts.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; +import { expect } from 'vitest'; Clarinet.test({ name: 'protocol-config: owner can set performance fee within bounds', @@ -61,6 +60,6 @@ Clarinet.test({ const versionAfter = chain.callReadOnlyFn('protocol-config', 'get-config-version', [], deployer.address); versionAfter.result.expectUint(2); - assertEquals(block.receipts.length, 1); + expect(block.receipts.length).toBe(1); }, }); diff --git a/contracts/tests/protocol-config.rebalance-frequency_test.ts b/contracts/tests/protocol-config.rebalance-frequency_test.ts index 8531e9d..4ca5068 100644 --- a/contracts/tests/protocol-config.rebalance-frequency_test.ts +++ b/contracts/tests/protocol-config.rebalance-frequency_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'protocol-config: owner can set max rebalance frequency in blocks', diff --git a/contracts/tests/protocol-config.strategy-types_test.ts b/contracts/tests/protocol-config.strategy-types_test.ts index c9fc867..3971841 100644 --- a/contracts/tests/protocol-config.strategy-types_test.ts +++ b/contracts/tests/protocol-config.strategy-types_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'protocol-config: owner can whitelist and remove strategy type', diff --git a/contracts/tests/protocol-config.supported-assets_test.ts b/contracts/tests/protocol-config.supported-assets_test.ts index 57d5f0f..3ca47cc 100644 --- a/contracts/tests/protocol-config.supported-assets_test.ts +++ b/contracts/tests/protocol-config.supported-assets_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'protocol-config: owner can add and remove supported asset', diff --git a/contracts/tests/protocol-config.treasury_test.ts b/contracts/tests/protocol-config.treasury_test.ts index 2f04be1..0d45e89 100644 --- a/contracts/tests/protocol-config.treasury_test.ts +++ b/contracts/tests/protocol-config.treasury_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'protocol-config: owner can update treasury principal', diff --git a/contracts/tests/protocol-config.vault-limits_test.ts b/contracts/tests/protocol-config.vault-limits_test.ts index f71265e..0b55ca3 100644 --- a/contracts/tests/protocol-config.vault-limits_test.ts +++ b/contracts/tests/protocol-config.vault-limits_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'protocol-config: owner can set max active vaults per user', diff --git a/contracts/tests/security.access-control-role-lifecycle_test.ts b/contracts/tests/security.access-control-role-lifecycle_test.ts index 4520f1c..917cc6e 100644 --- a/contracts/tests/security.access-control-role-lifecycle_test.ts +++ b/contracts/tests/security.access-control-role-lifecycle_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'security: access-control role lifecycle enforces owner grants and self-renounce', diff --git a/contracts/tests/security.adapter-caller-guard_test.ts b/contracts/tests/security.adapter-caller-guard_test.ts index 558acb2..dd4ee07 100644 --- a/contracts/tests/security.adapter-caller-guard_test.ts +++ b/contracts/tests/security.adapter-caller-guard_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'security: adapters reject direct calls from unauthorized principals', diff --git a/contracts/tests/security.adapter-principal-readiness_test.ts b/contracts/tests/security.adapter-principal-readiness_test.ts index 19670a9..f538a20 100644 --- a/contracts/tests/security.adapter-principal-readiness_test.ts +++ b/contracts/tests/security.adapter-principal-readiness_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'security: adapters reject production entry points before principal initialization', diff --git a/contracts/tests/security.adapter-treasury-guard_test.ts b/contracts/tests/security.adapter-treasury-guard_test.ts index 9147400..f115b98 100644 --- a/contracts/tests/security.adapter-treasury-guard_test.ts +++ b/contracts/tests/security.adapter-treasury-guard_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'security: adapter fee collection rejects non-protocol treasury destination', diff --git a/contracts/tests/security.aum-circuit-breaker_test.ts b/contracts/tests/security.aum-circuit-breaker_test.ts index 4628029..5c4ea9e 100644 --- a/contracts/tests/security.aum-circuit-breaker_test.ts +++ b/contracts/tests/security.aum-circuit-breaker_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'security: AUM circuit breaker rejects excessive single-tx asset drop', diff --git a/contracts/tests/security.cooldown-bypass_test.ts b/contracts/tests/security.cooldown-bypass_test.ts index 5911c82..1ea3d69 100644 --- a/contracts/tests/security.cooldown-bypass_test.ts +++ b/contracts/tests/security.cooldown-bypass_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; const PROTOCOL_ZEST = 1; diff --git a/contracts/tests/security.cross-vault-drain_test.ts b/contracts/tests/security.cross-vault-drain_test.ts index 73fcfb7..f007e5b 100644 --- a/contracts/tests/security.cross-vault-drain_test.ts +++ b/contracts/tests/security.cross-vault-drain_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'security: attacker cannot drain another user vault', diff --git a/contracts/tests/security.execute-deactivated-strategy_test.ts b/contracts/tests/security.execute-deactivated-strategy_test.ts index d49210a..9742c09 100644 --- a/contracts/tests/security.execute-deactivated-strategy_test.ts +++ b/contracts/tests/security.execute-deactivated-strategy_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; const PROTOCOL_ZEST = 1; diff --git a/contracts/tests/security.execution-lock-integrity_test.ts b/contracts/tests/security.execution-lock-integrity_test.ts index f32cec8..6e9d0af 100644 --- a/contracts/tests/security.execution-lock-integrity_test.ts +++ b/contracts/tests/security.execution-lock-integrity_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; const PROTOCOL_ZEST = 1; diff --git a/contracts/tests/security.unauthorized-privileged_test.ts b/contracts/tests/security.unauthorized-privileged_test.ts index 4c83369..b94be58 100644 --- a/contracts/tests/security.unauthorized-privileged_test.ts +++ b/contracts/tests/security.unauthorized-privileged_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'security: unauthorized principals cannot call privileged mutating functions', diff --git a/contracts/tests/security.vault-asset-sync_test.ts b/contracts/tests/security.vault-asset-sync_test.ts index df82063..e130181 100644 --- a/contracts/tests/security.vault-asset-sync_test.ts +++ b/contracts/tests/security.vault-asset-sync_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'security: vault-core and receipt-token asset accounting stay synchronized', diff --git a/contracts/tests/security.withdraw-over-deposit_test.ts b/contracts/tests/security.withdraw-over-deposit_test.ts index 5bc932c..994939b 100644 --- a/contracts/tests/security.withdraw-over-deposit_test.ts +++ b/contracts/tests/security.withdraw-over-deposit_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'security: withdrawing more than deposited fails and preserves vault assets', diff --git a/contracts/tests/stackingdao-adapter_test.ts b/contracts/tests/stackingdao-adapter_test.ts index e9d3dae..42b216d 100644 --- a/contracts/tests/stackingdao-adapter_test.ts +++ b/contracts/tests/stackingdao-adapter_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; function mock(account: Account) { return types.principal(`${account.address}.mock-stackingdao-core`); diff --git a/contracts/tests/strategy-execution.emergency-exit_test.ts b/contracts/tests/strategy-execution.emergency-exit_test.ts index 6d48831..836670f 100644 --- a/contracts/tests/strategy-execution.emergency-exit_test.ts +++ b/contracts/tests/strategy-execution.emergency-exit_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; const PROTOCOL_ZEST = 1; diff --git a/contracts/tests/strategy-execution.execute-flow_test.ts b/contracts/tests/strategy-execution.execute-flow_test.ts index 5b097e7..930be23 100644 --- a/contracts/tests/strategy-execution.execute-flow_test.ts +++ b/contracts/tests/strategy-execution.execute-flow_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; const PROTOCOL_ZEST = 1; diff --git a/contracts/tests/strategy-execution.rebalance_test.ts b/contracts/tests/strategy-execution.rebalance_test.ts index 18d729d..85a23fa 100644 --- a/contracts/tests/strategy-execution.rebalance_test.ts +++ b/contracts/tests/strategy-execution.rebalance_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; const PROTOCOL_ZEST = 1; const PROTOCOL_ALEX = 2; diff --git a/contracts/tests/strategy-registry.activation_test.ts b/contracts/tests/strategy-registry.activation_test.ts index 92596ec..4fc440b 100644 --- a/contracts/tests/strategy-registry.activation_test.ts +++ b/contracts/tests/strategy-registry.activation_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'strategy-registry: registrar can deactivate and reactivate strategy', diff --git a/contracts/tests/strategy-registry.queries_test.ts b/contracts/tests/strategy-registry.queries_test.ts index a59145b..e028dd8 100644 --- a/contracts/tests/strategy-registry.queries_test.ts +++ b/contracts/tests/strategy-registry.queries_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'strategy-registry: list-strategies-by-type returns ids grouped by type', diff --git a/contracts/tests/strategy-registry.registration_test.ts b/contracts/tests/strategy-registry.registration_test.ts index 9e1512b..8d2d798 100644 --- a/contracts/tests/strategy-registry.registration_test.ts +++ b/contracts/tests/strategy-registry.registration_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'strategy-registry: owner can register strategy with valid metadata', diff --git a/contracts/tests/strategy-registry.validation_test.ts b/contracts/tests/strategy-registry.validation_test.ts index cf69e1b..709028c 100644 --- a/contracts/tests/strategy-registry.validation_test.ts +++ b/contracts/tests/strategy-registry.validation_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'strategy-registry: validate-strategy-execution succeeds for active strategy and authorized executor', diff --git a/contracts/tests/strategy-vault.creation_test.ts b/contracts/tests/strategy-vault.creation_test.ts index b8dbc70..9bd4409 100644 --- a/contracts/tests/strategy-vault.creation_test.ts +++ b/contracts/tests/strategy-vault.creation_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'vault-core: create-vault succeeds with supported asset and active strategy', diff --git a/contracts/tests/strategy-vault.deposit_test.ts b/contracts/tests/strategy-vault.deposit_test.ts index fbe1446..dffc7a3 100644 --- a/contracts/tests/strategy-vault.deposit_test.ts +++ b/contracts/tests/strategy-vault.deposit_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'vault-core: owner can deposit into active vault with matching asset', diff --git a/contracts/tests/strategy-vault.emergency_test.ts b/contracts/tests/strategy-vault.emergency_test.ts index b295542..dc16a9a 100644 --- a/contracts/tests/strategy-vault.emergency_test.ts +++ b/contracts/tests/strategy-vault.emergency_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'vault-core: protocol owner can emergency-withdraw regardless of vault lock/state', diff --git a/contracts/tests/strategy-vault.invariants_test.ts b/contracts/tests/strategy-vault.invariants_test.ts index 51d6c95..4db6d0c 100644 --- a/contracts/tests/strategy-vault.invariants_test.ts +++ b/contracts/tests/strategy-vault.invariants_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'vault-core invariant: balance never becomes negative on over-withdraw', diff --git a/contracts/tests/strategy-vault.pause-close_test.ts b/contracts/tests/strategy-vault.pause-close_test.ts index 6591183..679016f 100644 --- a/contracts/tests/strategy-vault.pause-close_test.ts +++ b/contracts/tests/strategy-vault.pause-close_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'vault-core: owner can pause and unpause vault, non-owner cannot pause', diff --git a/contracts/tests/strategy-vault.withdrawal_test.ts b/contracts/tests/strategy-vault.withdrawal_test.ts index 11b90a0..0f18c36 100644 --- a/contracts/tests/strategy-vault.withdrawal_test.ts +++ b/contracts/tests/strategy-vault.withdrawal_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'vault-core: supports partial and full withdrawal for owner', diff --git a/contracts/tests/vault-receipt-token.access-control_test.ts b/contracts/tests/vault-receipt-token.access-control_test.ts index 374a188..770a20e 100644 --- a/contracts/tests/vault-receipt-token.access-control_test.ts +++ b/contracts/tests/vault-receipt-token.access-control_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'vault-receipt-token: mint burn and sync are restricted to vault core contract caller', diff --git a/contracts/tests/vault-receipt-token.asset-sync_test.ts b/contracts/tests/vault-receipt-token.asset-sync_test.ts index b7fcf9c..dafe9e2 100644 --- a/contracts/tests/vault-receipt-token.asset-sync_test.ts +++ b/contracts/tests/vault-receipt-token.asset-sync_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'vault-receipt-token: cached vault assets sync across yield fee and emergency flows', diff --git a/contracts/tests/vault-receipt-token.initial-mint_test.ts b/contracts/tests/vault-receipt-token.initial-mint_test.ts index 31f0df2..a944f51 100644 --- a/contracts/tests/vault-receipt-token.initial-mint_test.ts +++ b/contracts/tests/vault-receipt-token.initial-mint_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'vault-receipt-token: first deposit mints initial shares at 1.0 price-per-share', diff --git a/contracts/tests/vault-receipt-token.metadata-init_test.ts b/contracts/tests/vault-receipt-token.metadata-init_test.ts index fb6ddea..002a263 100644 --- a/contracts/tests/vault-receipt-token.metadata-init_test.ts +++ b/contracts/tests/vault-receipt-token.metadata-init_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'vault-receipt-token: metadata is configurable once via initializer', diff --git a/contracts/tests/vault-receipt-token.price-per-share_test.ts b/contracts/tests/vault-receipt-token.price-per-share_test.ts index 0107822..e519fdd 100644 --- a/contracts/tests/vault-receipt-token.price-per-share_test.ts +++ b/contracts/tests/vault-receipt-token.price-per-share_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'vault-receipt-token: price-per-share updates after yield accrual', diff --git a/contracts/tests/vault-receipt-token.proportional-withdrawal_test.ts b/contracts/tests/vault-receipt-token.proportional-withdrawal_test.ts index 9e3b10e..7d1df5e 100644 --- a/contracts/tests/vault-receipt-token.proportional-withdrawal_test.ts +++ b/contracts/tests/vault-receipt-token.proportional-withdrawal_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'vault-receipt-token: withdrawal returns proportional assets for shares burned', diff --git a/contracts/tests/vault-receipt-token.registry_test.ts b/contracts/tests/vault-receipt-token.registry_test.ts index 5f95062..470a154 100644 --- a/contracts/tests/vault-receipt-token.registry_test.ts +++ b/contracts/tests/vault-receipt-token.registry_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'vault-receipt-token: vault share registry tracks balances and supply per vault id', diff --git a/contracts/tests/vault-receipt-token.sip010-compliance_test.ts b/contracts/tests/vault-receipt-token.sip010-compliance_test.ts index a8e5ede..65715c5 100644 --- a/contracts/tests/vault-receipt-token.sip010-compliance_test.ts +++ b/contracts/tests/vault-receipt-token.sip010-compliance_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'vault-receipt-token: implements required SIP-010 functions', diff --git a/contracts/tests/vault-receipt-token.transfer-context_test.ts b/contracts/tests/vault-receipt-token.transfer-context_test.ts index 7fdbb3b..465a201 100644 --- a/contracts/tests/vault-receipt-token.transfer-context_test.ts +++ b/contracts/tests/vault-receipt-token.transfer-context_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; Clarinet.test({ name: 'vault-receipt-token: transfer requires single-vault context for sender', diff --git a/contracts/tests/zest-protocol-adapter_test.ts b/contracts/tests/zest-protocol-adapter_test.ts index d647d4a..f119acc 100644 --- a/contracts/tests/zest-protocol-adapter_test.ts +++ b/contracts/tests/zest-protocol-adapter_test.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet/index.ts'; +import { Clarinet, Tx, Chain, Account, types } from './helpers/legacy-clarinet'; function mock(account: Account) { return types.principal(`${account.address}.mock-zest-protocol`); diff --git a/contracts/tsconfig.json b/contracts/tsconfig.json new file mode 100644 index 0000000..114d5a6 --- /dev/null +++ b/contracts/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2022"], + "module": "ESNext", + "moduleResolution": "Bundler", + "noEmit": true, + + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + + "strict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "noImplicitOverride": true, + "allowUnusedLabels": false, + "allowUnreachableCode": false, + + "skipLibCheck": true, + "resolveJsonModule": true, + + "types": ["node", "vitest/globals"] + }, + "include": ["tests/**/*.ts", "vitest.config.ts"], + "exclude": ["node_modules", "dist", ".cache"] +} \ No newline at end of file diff --git a/contracts/vitest.config.ts b/contracts/vitest.config.ts index 6dee4c0..ad9268e 100644 --- a/contracts/vitest.config.ts +++ b/contracts/vitest.config.ts @@ -4,6 +4,7 @@ export default defineConfig({ test: { include: [ 'tests/unit/**/*.test.ts', + 'tests/**/*_test.ts', 'tests/integration/**/*.test.ts', 'tests/adversarial/**/*.test.ts', ],