diff --git a/packages/builder/src/vector/agent-network.ts b/packages/builder/src/vector/agent-network.ts index ede7b89..1001f6e 100644 --- a/packages/builder/src/vector/agent-network.ts +++ b/packages/builder/src/vector/agent-network.ts @@ -306,21 +306,21 @@ ${capList} if (!vkeyHash) throw new Error('Cannot derive payment key hash from wallet address'); const utxos = await lucid.utxosAt(await lucid.wallet().address()); - const seedUtxo = utxos.find(u => { + const oneShotUtxo = utxos.find(u => { const keys = Object.keys(u.assets); return keys.length === 1 && keys[0] === 'lovelace' && u.assets['lovelace'] >= MIN_AP3X_DEPOSIT + 2_000_000n; }) || utxos[0]; - if (!seedUtxo) throw new Error('No UTxOs in wallet. Please fund the wallet first.'); + if (!oneShotUtxo) throw new Error('No UTxOs in wallet. Please fund the wallet first.'); - const nftAssetName = deriveNftAssetName(seedUtxo.txHash, seedUtxo.outputIndex); + const nftAssetName = deriveNftAssetName(oneShotUtxo.txHash, oneShotUtxo.outputIndex); const nftUnit = `${REGISTRY_POLICY_ID}${nftAssetName}`; const datum = buildAgentDatum(vkeyHash, name, description, capabilities, framework, endpoint); const registryScript = { type: 'PlutusV3' as const, script: REGISTRY_SCRIPT_CBOR }; const registryAddress = validatorToAddress('Mainnet', registryScript); - const registerRedeemer = Data.to(new Constr(0, [new Constr(0, [seedUtxo.txHash, BigInt(seedUtxo.outputIndex)])])); + const registerRedeemer = Data.to(new Constr(0, [new Constr(0, [oneShotUtxo.txHash, BigInt(oneShotUtxo.outputIndex)])])); const tx = await lucid.newTx() - .collectFrom([seedUtxo]) + .collectFrom([oneShotUtxo]) .mintAssets({ [nftUnit]: 1n }, registerRedeemer) .attach.MintingPolicy(registryScript) .pay.ToAddressWithData(registryAddress, { kind: "inline", value: datum }, { lovelace: MIN_AP3X_DEPOSIT, [nftUnit]: 1n }) diff --git a/packages/builder/test/integration/keyless-build.test.ts b/packages/builder/test/integration/keyless-build.test.ts index 76c1951..cc3c63a 100644 --- a/packages/builder/test/integration/keyless-build.test.ts +++ b/packages/builder/test/integration/keyless-build.test.ts @@ -45,11 +45,20 @@ describe('keyless build against live testnet', () => { const tx = CML.Transaction.from_cbor_hex(cbor); const outs = tx.body().outputs(); let paid = 0n; + // A self-send means the payment output and the change output land at the + // same address, so summing every output can't tell a correct 3 AP3X + // payment apart from a dropped/wrong one (change alone dwarfs the floor). + // Count outputs at exactly the requested lovelace amount instead - the + // same exact-count pattern the build_transaction test below uses. + let paymentOutputs = 0; for (let i = 0; i < outs.len(); i++) { const o = outs.get(i); assert.equal(o.address().to_bech32(), OWN_ADDRESS, 'self-send must only pay the own address'); - paid += BigInt(o.amount().coin().toString()); + const lovelace = BigInt(o.amount().coin().toString()); + paid += lovelace; + if (lovelace === 3_000_000n) paymentOutputs++; } + assert.ok(paymentOutputs >= 1, `expected a 3 AP3X payment output, found ${paymentOutputs} (total paid: ${paid})`); const fee = BigInt(tx.body().fee().toString()); assert.ok(fee >= 156_253n && fee <= 500_000n, `fee out of sane range: ${fee}`); const vkeys = tx.witness_set().vkeywitnesses(); @@ -62,6 +71,7 @@ describe('keyless build against live testnet', () => { const buildText = await callTool(ctx.client, 'vector_build_send_apex', { changeAddress: OWN_ADDRESS, recipientAddress: OWN_ADDRESS, amount: 3, }); + assertBuildNotSubmitted(buildText); const cbor = extractCbor(buildText); const text = await callTool(ctx.client, 'vector_dry_run', { txCbor: cbor }); assert.match(text, /Valid: Yes/); diff --git a/packages/builder/test/integration/run.test.ts b/packages/builder/test/integration/run.test.ts index 1009417..89bb122 100644 --- a/packages/builder/test/integration/run.test.ts +++ b/packages/builder/test/integration/run.test.ts @@ -10,7 +10,6 @@ let ctx: ServerContext; let mnemonic: string; let walletAddress: string; let walletHasAda = false; -let walletBalanceAda = 0; let agentDid: string | null = null; before(async () => { @@ -66,7 +65,6 @@ describe('Wallet Tools', () => { const balanceMatch = text.match(/AP3X Balance:\s*([\d.]+)/); if (balanceMatch && parseFloat(balanceMatch[1]) > 0) { walletHasAda = true; - walletBalanceAda = parseFloat(balanceMatch[1]); console.log(`Wallet funded: ${balanceMatch[1]} AP3X`); } else { console.log('Wallet has 0 AP3X - transaction tests will verify error handling');