Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@
"typescript": "^5.9.2",
"vitest": "^3.2.4"
},
"pnpm": {
"overrides": {
"scalus": "link:./vendor/scalus"
}
},
"engines": {
"node": ">=18.0.0",
"pnpm": ">=8.0.0"
Expand Down
1 change: 1 addition & 0 deletions packages/evolution-devnet/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"peerDependencies": {
"@evolution-sdk/aiken-uplc": "workspace:*",
"@evolution-sdk/evolution": "workspace:*",
"@evolution-sdk/scalus-emulator": "workspace:*",
"@evolution-sdk/scalus-uplc": "workspace:*",
"@effect/platform": "^0.90.10",
"@effect/platform-node": "^0.96.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { describe, expect, it } from "@effect/vitest"
import { Cardano } from "@evolution-sdk/evolution"
import * as KeyHash from "@evolution-sdk/evolution/KeyHash"
import * as TransactionHash from "@evolution-sdk/evolution/TransactionHash"

import { createNodeEmulatorSetup } from "./utils/nodeEmulator.js"

describe("TxBuilder addSigner (node-emulator)", () => {
it("should include requiredSigners in transaction body and submit successfully", async () => {
const { client, genesisUtxos } = await createNodeEmulatorSetup()
const myAddress = await client.address()

const paymentCredential = myAddress.paymentCredential
if (paymentCredential._tag !== "KeyHash") throw new Error("Expected KeyHash credential")

const signBuilder = await client
.newTx()
.addSigner({ keyHash: paymentCredential })
.payToAddress({
address: myAddress,
assets: Cardano.Assets.fromLovelace(5_000_000n)
})
.build({ availableUtxos: [...genesisUtxos] })

const tx = await signBuilder.toTransaction()

expect(tx.body.requiredSigners).toBeDefined()
expect(tx.body.requiredSigners?.length).toBe(1)
expect(tx.body.requiredSigners?.[0]._tag).toBe("KeyHash")
expect(KeyHash.toHex(tx.body.requiredSigners![0])).toBe(KeyHash.toHex(paymentCredential))

const submitBuilder = await signBuilder.sign()
const txHash = await submitBuilder.submit()
expect(TransactionHash.toHex(txHash).length).toBe(64)

const confirmed = await client.awaitTx(txHash, 1000)
expect(confirmed).toBe(true)
})

it("should support multi-sig with partial signing and assembly", async () => {
const { client: client1, genesisUtxos } = await createNodeEmulatorSetup({ accountIndex: 0 })
const setup2 = await createNodeEmulatorSetup({ accountIndex: 1 })
const client2 = setup2.client

const address1 = await client1.address()
const address2 = await client2.address()

const credential1 = address1.paymentCredential
const credential2 = address2.paymentCredential
if (credential1._tag !== "KeyHash" || credential2._tag !== "KeyHash") {
throw new Error("Expected KeyHash credentials")
}

const signBuilder = await client1
.newTx()
.addSigner({ keyHash: credential1 })
.addSigner({ keyHash: credential2 })
.payToAddress({
address: address1,
assets: Cardano.Assets.fromLovelace(5_000_000n)
})
.build({ availableUtxos: [...genesisUtxos] })

const tx = await signBuilder.toTransaction()

expect(tx.body.requiredSigners).toBeDefined()
expect(tx.body.requiredSigners?.length).toBe(2)

const requiredHashes = tx.body.requiredSigners!.map((k) => KeyHash.toHex(k))
expect(requiredHashes).toContain(KeyHash.toHex(credential1))
expect(requiredHashes).toContain(KeyHash.toHex(credential2))

const witness1 = await signBuilder.partialSign()
expect(witness1.vkeyWitnesses?.length).toBe(1)

const witness2 = await client2.signTx(tx)
expect(witness2.vkeyWitnesses?.length).toBe(1)

const submitBuilder = await signBuilder.assemble([witness1, witness2])

const txHash = await submitBuilder.submit()
expect(TransactionHash.toHex(txHash).length).toBe(64)

const confirmed = await client1.awaitTx(txHash, 1000)
expect(confirmed).toBe(true)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import "@evolution-sdk/scalus-emulator"

import { describe, expect, it } from "@effect/vitest"
import { Cardano } from "@evolution-sdk/evolution"
import * as Assets from "@evolution-sdk/evolution/Assets"
import type { SignBuilder } from "@evolution-sdk/evolution/sdk/builders/SignBuilder"
import { createClient } from "@evolution-sdk/evolution/sdk/client/ClientImpl"
import * as SlotConfig from "@evolution-sdk/evolution/Time/SlotConfig"
import * as TransactionHash from "@evolution-sdk/evolution/TransactionHash"
import * as UTxO from "@evolution-sdk/evolution/UTxO"

const TEST_MNEMONIC =
"test test test test test test test test test test test test test test test test test test test test test test test sauce"

/**
* Same chain-submit test as TxBuilder.Chain.test.ts, but backed by the in-process
* Scalus node-emulator — no docker cluster, no kupo, no ogmios.
*/
describe("TxBuilder.chainResult (node-emulator)", () => {
it("should chain multiple transactions and submit them all", async () => {
// Build the wallet's address first (via a no-provider client).
const tempClient = createClient({
network: 0,
wallet: { type: "seed", mnemonic: TEST_MNEMONIC, accountIndex: 0, addressType: "Base" }
})
const address = await tempClient.address()

// Seed the emulator with one large UTxO for the wallet.
const genesisTxId = TransactionHash.fromHex("00".repeat(32))
const genesisUtxos: ReadonlyArray<Cardano.UTxO.UTxO> = [
new UTxO.UTxO({
transactionId: genesisTxId,
index: 0n,
address,
assets: Assets.fromLovelace(500_000_000_000n)
})
]

const slotConfig = SlotConfig.SLOT_CONFIG_NETWORK.Preprod

const client = createClient({
network: 0,
slotConfig,
provider: {
type: "node-emulator",
slotConfig,
initialUtxos: genesisUtxos
},
wallet: {
type: "seed",
mnemonic: TEST_MNEMONIC,
accountIndex: 0,
addressType: "Base"
}
})

const TX_COUNT = 5

let available = [...genesisUtxos]
const txs: Array<SignBuilder> = []

for (let i = 0; i < TX_COUNT; i++) {
const tx = await client
.newTx()
.payToAddress({ address, assets: Cardano.Assets.fromLovelace(10_000_000n) })
.build({ availableUtxos: available })
txs.push(tx)
available = [...tx.chainResult().available]
}

const txHashes = txs.map((tx) => tx.chainResult().txHash)
expect(new Set(txHashes).size).toBe(TX_COUNT)

const submittedHashes: Array<TransactionHash.TransactionHash> = []
for (const tx of txs) {
const hash = await tx.signAndSubmit()
submittedHashes.push(hash)
}

for (let i = 0; i < TX_COUNT; i++) {
expect(TransactionHash.toHex(submittedHashes[i])).toBe(txs[i].chainResult().txHash)
}

for (const hash of submittedHashes) {
expect(await client.awaitTx(hash, 1000)).toBe(true)
}
})
})
Loading