diff --git a/packages/builder/test/fixtures/capture-protocol-params.mjs b/packages/builder/test/fixtures/capture-protocol-params.mjs new file mode 100644 index 0000000..b41aa48 --- /dev/null +++ b/packages/builder/test/fixtures/capture-protocol-params.mjs @@ -0,0 +1,22 @@ +// packages/builder/test/fixtures/capture-protocol-params.mjs +// +// One-time capture: writes Lucid-format protocol parameters from the live +// Vector testnet Ogmios endpoint into protocol-params.fixture.json, using +// OgmiosProvider's own mapping so the fixture can never drift from the +// conversion logic. Run manually after `npm run build`: +// +// node packages/builder/test/fixtures/capture-protocol-params.mjs +// +// Never runs in CI — unit tests read the checked-in JSON. +import { writeFileSync } from 'node:fs'; +import { resolve, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { OgmiosProvider } from '@apexfusion/vector-mcp-shared/provider'; + +const ogmiosUrl = process.env.VECTOR_OGMIOS_URL || 'https://ogmios.vector.testnet.apexfusion.org'; +const provider = new OgmiosProvider({ ogmiosUrl, submitUrl: 'http://unused.invalid', koiosUrl: '' }); +const params = await provider.getProtocolParameters(); +const json = JSON.stringify(params, (_k, v) => (typeof v === 'bigint' ? { __bigint: v.toString() } : v), 2); +const out = resolve(dirname(fileURLToPath(import.meta.url)), 'protocol-params.fixture.json'); +writeFileSync(out, json + '\n'); +console.log(`wrote ${out}`); diff --git a/packages/builder/test/fixtures/fixture-provider.test.ts b/packages/builder/test/fixtures/fixture-provider.test.ts new file mode 100644 index 0000000..db3994a --- /dev/null +++ b/packages/builder/test/fixtures/fixture-provider.test.ts @@ -0,0 +1,40 @@ +// packages/builder/test/fixtures/fixture-provider.test.ts +import { describe, test } from 'node:test'; +import assert from 'node:assert/strict'; +import { Lucid } from '@lucid-evolution/lucid'; +import { FixtureProvider, fixtureProtocolParameters, FIXTURE_UTXOS, OWN_ADDRESS, FOREIGN_ADDRESS } from './fixture-provider.ts'; + +describe('FixtureProvider', () => { + test('serves the captured protocol parameters with bigints revived', () => { + const p = fixtureProtocolParameters(); + assert.equal(p.minFeeA, 45); + assert.equal(p.minFeeB, 156253); + assert.equal(typeof p.coinsPerUtxoByte, 'bigint'); + assert.ok(p.costModels.PlutusV2, 'PlutusV2 cost model missing from fixture'); + }); + + test('getUtxos filters by address and returns nothing for a foreign address', async () => { + const provider = new FixtureProvider(FIXTURE_UTXOS); + assert.equal((await provider.getUtxos(OWN_ADDRESS)).length, 3); + assert.equal((await provider.getUtxos(FOREIGN_ADDRESS)).length, 0); + }); + + test('network-only methods throw loudly instead of hanging', async () => { + const provider = new FixtureProvider(FIXTURE_UTXOS); + await assert.rejects(() => provider.submitTx('84'), /offline/); + await assert.rejects(() => provider.getDelegation('stake1xyz'), /offline/); + }); + + // The offline replication of the PR-4 spike: a REAL unsigned transaction, + // built with no key and no network. If this test passes, every later build + // test has a working foundation. + test('Lucid + FixtureProvider builds a real unsigned tx via fromAddress', async () => { + const lucid = await Lucid(new FixtureProvider(FIXTURE_UTXOS), 'Mainnet'); + const utxos = await lucid.utxosAt(OWN_ADDRESS); + lucid.selectWallet.fromAddress(OWN_ADDRESS, utxos); + const tx = await lucid.newTx().pay.ToAddress(FOREIGN_ADDRESS, { lovelace: 3_000_000n }).complete(); + const cbor = tx.toCBOR(); + assert.ok(cbor.length > 200, `unsigned tx CBOR suspiciously short: ${cbor.length} hex chars`); + assert.match(cbor, /^84/, 'Conway-era tx CBOR must start with array header 0x84'); + }); +}); diff --git a/packages/builder/test/fixtures/fixture-provider.ts b/packages/builder/test/fixtures/fixture-provider.ts new file mode 100644 index 0000000..e01a89c --- /dev/null +++ b/packages/builder/test/fixtures/fixture-provider.ts @@ -0,0 +1,87 @@ +// packages/builder/test/fixtures/fixture-provider.ts +// +// Offline stand-in for OgmiosProvider in unit tests. Serves the checked-in +// protocol-params fixture (captured from the live testnet by +// capture-protocol-params.mjs) and a caller-supplied UTxO set. Every method +// that would need the chain throws loudly so a test that would touch the +// network fails fast instead of hanging or silently passing. +import { readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; +import type { + Provider, ProtocolParameters, UTxO, OutRef, Address, Credential, + Delegation, DatumHash, Datum, RewardAddress, Transaction, TxHash, Unit, +} from '@lucid-evolution/lucid'; + +export const OWN_ADDRESS = 'addr1qylasu4y34ccwt8hv55tkswa9fthtck9xtppvc4y03kwhaztd0jlxdzk72zj8mgy6x4269v9gytkgffh3k8d3l8987yqu5c35z'; +export const FOREIGN_ADDRESS = 'addr1wx434t2jc3m5uhdf7tq05xjdqu3q5z7a2lhrmn5mapsd43srh7ll8'; +export const FIXTURE_TXHASH = 'e17a2ebab8eae3850f959290041b3bef3f3597584f8cf4a728e14777dddb8c32'; +export const FIXTURE_TOKEN_UNIT = '1111111111111111111111111111111111111111111111111111111154657374546f6b656e'; + +export const FIXTURE_UTXOS: UTxO[] = [ + { txHash: FIXTURE_TXHASH, outputIndex: 0, address: OWN_ADDRESS, assets: { lovelace: 1_000_000_000n } }, + { txHash: FIXTURE_TXHASH, outputIndex: 1, address: OWN_ADDRESS, assets: { lovelace: 5_000_000n } }, + { txHash: FIXTURE_TXHASH, outputIndex: 2, address: OWN_ADDRESS, assets: { lovelace: 10_000_000n, [FIXTURE_TOKEN_UNIT]: 500n } }, +]; + +const PARAMS_PATH = resolve(import.meta.dirname!, 'protocol-params.fixture.json'); + +export function fixtureProtocolParameters(): ProtocolParameters { + return JSON.parse(readFileSync(PARAMS_PATH, 'utf-8'), (_k, v) => + v !== null && typeof v === 'object' && '__bigint' in v ? BigInt(v.__bigint) : v, + ) as ProtocolParameters; +} + +export class FixtureProvider implements Provider { + private utxos: UTxO[]; + + // Not a parameter-property constructor (`constructor(private utxos...)`): + // Node's native type-stripping (no ts-node/tsx loader in the plain `node + // --test` invocation this file is designed to run under) only erases + // types, it does not transform TS-only constructs like parameter + // properties, and rejects them with ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX. + // Same behavior as the parameter-property form, just spelled so `node` + // can strip-and-run it directly. Matches OgmiosProvider's own style in + // provider.ts, which never uses parameter properties either. + constructor(utxos: UTxO[]) { + this.utxos = utxos; + } + + async getProtocolParameters(): Promise { + return fixtureProtocolParameters(); + } + + async getUtxos(addressOrCredential: Address | Credential): Promise { + if (typeof addressOrCredential !== 'string') { + throw new Error('FixtureProvider: credential queries not supported offline'); + } + return this.utxos.filter((u) => u.address === addressOrCredential); + } + + async getUtxosByOutRef(outRefs: Array): Promise { + return this.utxos.filter((u) => + outRefs.some((r) => r.txHash === u.txHash && Number(r.outputIndex) === u.outputIndex), + ); + } + + async getUtxosWithUnit(_a: Address | Credential, _u: Unit): Promise { + throw new Error('FixtureProvider: offline — getUtxosWithUnit unavailable'); + } + async getUtxoByUnit(_u: Unit): Promise { + throw new Error('FixtureProvider: offline — getUtxoByUnit unavailable'); + } + async getDelegation(_r: RewardAddress): Promise { + throw new Error('FixtureProvider: offline — getDelegation unavailable'); + } + async getDatum(_d: DatumHash): Promise { + throw new Error('FixtureProvider: offline — getDatum unavailable'); + } + awaitTx(_tx: TxHash, _interval?: number): Promise { + throw new Error('FixtureProvider: offline — awaitTx unavailable'); + } + async submitTx(_tx: Transaction): Promise { + throw new Error('FixtureProvider: offline — submitTx unavailable'); + } + async evaluateTx(_tx: Transaction, _additional?: UTxO[]): Promise { + throw new Error('FixtureProvider: offline — evaluateTx unavailable'); + } +} diff --git a/packages/builder/test/fixtures/protocol-params.fixture.json b/packages/builder/test/fixtures/protocol-params.fixture.json new file mode 100644 index 0000000..f5c910e --- /dev/null +++ b/packages/builder/test/fixtures/protocol-params.fixture.json @@ -0,0 +1,678 @@ +{ + "minFeeA": 45, + "minFeeB": 156253, + "maxTxSize": 16384, + "maxValSize": 5000, + "keyDeposit": { + "__bigint": "500000000" + }, + "poolDeposit": { + "__bigint": "5000000000000" + }, + "priceMem": 0.0577, + "priceStep": 0.0000721, + "maxTxExMem": { + "__bigint": "16000000" + }, + "maxTxExSteps": { + "__bigint": "10000000000" + }, + "coinsPerUtxoByte": { + "__bigint": "4310" + }, + "collateralPercentage": 150, + "maxCollateralInputs": 3, + "costModels": { + "PlutusV1": [ + 197209, + 0, + 1, + 1, + 396231, + 621, + 0, + 1, + 150000, + 1000, + 0, + 1, + 150000, + 32, + 2477736, + 29175, + 4, + 29773, + 100, + 29773, + 100, + 29773, + 100, + 29773, + 100, + 29773, + 100, + 29773, + 100, + 100, + 100, + 29773, + 100, + 150000, + 32, + 150000, + 32, + 150000, + 32, + 150000, + 1000, + 0, + 1, + 150000, + 32, + 150000, + 1000, + 0, + 8, + 148000, + 425507, + 118, + 0, + 1, + 1, + 150000, + 1000, + 0, + 8, + 150000, + 112536, + 247, + 1, + 150000, + 10000, + 1, + 136542, + 1326, + 1, + 1000, + 150000, + 1000, + 1, + 150000, + 32, + 150000, + 32, + 150000, + 32, + 1, + 1, + 150000, + 1, + 150000, + 4, + 103599, + 248, + 1, + 103599, + 248, + 1, + 145276, + 1366, + 1, + 179690, + 497, + 1, + 150000, + 32, + 150000, + 32, + 150000, + 32, + 150000, + 32, + 150000, + 32, + 150000, + 32, + 148000, + 425507, + 118, + 0, + 1, + 1, + 61516, + 11218, + 0, + 1, + 150000, + 32, + 148000, + 425507, + 118, + 0, + 1, + 1, + 148000, + 425507, + 118, + 0, + 1, + 1, + 2477736, + 29175, + 4, + 0, + 82363, + 4, + 150000, + 5000, + 0, + 1, + 150000, + 32, + 197209, + 0, + 1, + 1, + 150000, + 32, + 150000, + 32, + 150000, + 32, + 150000, + 32, + 150000, + 32, + 150000, + 32, + 150000, + 32, + 3345831, + 1, + 1 + ], + "PlutusV2": [ + 205665, + 812, + 1, + 1, + 1000, + 571, + 0, + 1, + 1000, + 24177, + 4, + 1, + 1000, + 32, + 117366, + 10475, + 4, + 23000, + 100, + 23000, + 100, + 23000, + 100, + 23000, + 100, + 23000, + 100, + 23000, + 100, + 100, + 100, + 23000, + 100, + 19537, + 32, + 175354, + 32, + 46417, + 4, + 221973, + 511, + 0, + 1, + 89141, + 32, + 497525, + 14068, + 4, + 2, + 196500, + 453240, + 220, + 0, + 1, + 1, + 1000, + 28662, + 4, + 2, + 245000, + 216773, + 62, + 1, + 1060367, + 12586, + 1, + 208512, + 421, + 1, + 187000, + 1000, + 52998, + 1, + 80436, + 32, + 43249, + 32, + 1000, + 32, + 80556, + 1, + 57667, + 4, + 1000, + 10, + 197145, + 156, + 1, + 197145, + 156, + 1, + 204924, + 473, + 1, + 208896, + 511, + 1, + 52467, + 32, + 64832, + 32, + 65493, + 32, + 22558, + 32, + 16563, + 32, + 76511, + 32, + 196500, + 453240, + 220, + 0, + 1, + 1, + 69522, + 11687, + 0, + 1, + 60091, + 32, + 196500, + 453240, + 220, + 0, + 1, + 1, + 196500, + 453240, + 220, + 0, + 1, + 1, + 1159724, + 392670, + 0, + 2, + 806990, + 30482, + 4, + 1927926, + 82523, + 4, + 265318, + 0, + 4, + 0, + 85931, + 32, + 205665, + 812, + 1, + 1, + 41182, + 32, + 212342, + 32, + 31220, + 32, + 32696, + 32, + 43357, + 32, + 32247, + 32, + 38314, + 32, + 35892428, + 10, + 9462713, + 1021, + 10, + 38887044, + 32947, + 10 + ], + "PlutusV3": [ + 100788, + 420, + 1, + 1, + 1000, + 173, + 0, + 1, + 1000, + 59957, + 4, + 1, + 11183, + 32, + 201305, + 8356, + 4, + 16000, + 100, + 16000, + 100, + 16000, + 100, + 16000, + 100, + 16000, + 100, + 16000, + 100, + 100, + 100, + 16000, + 100, + 94375, + 32, + 132994, + 32, + 61462, + 4, + 72010, + 178, + 0, + 1, + 22151, + 32, + 91189, + 769, + 4, + 2, + 85848, + 123203, + 7305, + -900, + 1716, + 549, + 57, + 85848, + 0, + 1, + 1, + 1000, + 42921, + 4, + 2, + 24548, + 29498, + 38, + 1, + 898148, + 27279, + 1, + 51775, + 558, + 1, + 39184, + 1000, + 60594, + 1, + 141895, + 32, + 83150, + 32, + 15299, + 32, + 76049, + 1, + 13169, + 4, + 22100, + 10, + 28999, + 74, + 1, + 28999, + 74, + 1, + 43285, + 552, + 1, + 44749, + 541, + 1, + 33852, + 32, + 68246, + 32, + 72362, + 32, + 7243, + 32, + 7391, + 32, + 11546, + 32, + 85848, + 123203, + 7305, + -900, + 1716, + 549, + 57, + 85848, + 0, + 1, + 90434, + 519, + 0, + 1, + 74433, + 32, + 85848, + 123203, + 7305, + -900, + 1716, + 549, + 57, + 85848, + 0, + 1, + 1, + 85848, + 123203, + 7305, + -900, + 1716, + 549, + 57, + 85848, + 0, + 1, + 955506, + 213312, + 0, + 2, + 270652, + 22588, + 4, + 1457325, + 64566, + 4, + 20467, + 1, + 4, + 0, + 141992, + 32, + 100788, + 420, + 1, + 1, + 81663, + 32, + 59498, + 32, + 20142, + 32, + 24588, + 32, + 20744, + 32, + 25933, + 32, + 24623, + 32, + 43053543, + 10, + 53384111, + 14333, + 10, + 43574283, + 26308, + 10, + 16000, + 100, + 16000, + 100, + 962335, + 18, + 2780678, + 6, + 442008, + 1, + 52538055, + 3756, + 18, + 267929, + 18, + 76433006, + 8868, + 18, + 52948122, + 18, + 1995836, + 36, + 3227919, + 12, + 901022, + 1, + 166917843, + 4307, + 36, + 284546, + 36, + 158221314, + 26549, + 36, + 74698472, + 36, + 333849714, + 1, + 254006273, + 72, + 2174038, + 72, + 2261318, + 64571, + 4, + 207616, + 8310, + 4, + 1293828, + 28716, + 63, + 0, + 1, + 1006041, + 43623, + 251, + 0, + 1, + 100181, + 726, + 719, + 0, + 1, + 100181, + 726, + 719, + 0, + 1, + 100181, + 726, + 719, + 0, + 1, + 107878, + 680, + 0, + 1, + 95336, + 1, + 281145, + 18848, + 0, + 1, + 180194, + 159, + 1, + 1, + 158519, + 8942, + 0, + 1, + 159378, + 8813, + 0, + 1, + 107490, + 3298, + 1, + 106057, + 655, + 1, + 1964219, + 24520, + 3 + ] + }, + "minFeeRefScriptCostPerByte": 15, + "drepDeposit": { + "__bigint": "1000000000000" + }, + "govActionDeposit": { + "__bigint": "1000000000" + } +}