Skip to content
Merged
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
67 changes: 26 additions & 41 deletions .github/workflows/haskell-wasm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ on:
- master
# GH caches are removed when not accessed within 7 days - this schedule runs the job every 6 days making
# sure that we always have some caches on master
# schedule:
# - cron: '0 0 */6 * *'
schedule:
- cron: '0 0 */6 * *'

jobs:
build:
Expand All @@ -30,7 +30,7 @@ jobs:

env:
# Modify this value to "invalidate" the cabal cache.
CABAL_CACHE_VERSION: "2025-05-29"
CABAL_CACHE_VERSION: "2026-05-29"

concurrency:
group: >
Expand Down Expand Up @@ -90,59 +90,44 @@ jobs:
STORE=$(wasm32-wasi-cabal path --store | tail -n 1)
EOF

# Cache is disabled because GHA default builders are not able to build all dependencies
# because they lack RAM, so having the cache expire would break the CI check.
# For this reason, we are providing a build of the dependencies instead in
# the "Restore cached deps" step, and we make the check not required.
# When we are able to make this CI check self-sufficient, we should reenable the
# caching and remove the manual restoring of cached deps.

# From the dependency list we restore the cached dependencies.
# We use the hash of `dependencies.txt` as part of the cache key because that will be stable
# until the `index-state` values in the `cabal.project` file changes.
# - name: Restore cached dependencies
# uses: actions/cache/restore@v4
# id: cache
# with:
# path: |
# ${{ env.STORE }}
# dist-newstyle
# key:
# wasm-cache-${{ env.CABAL_CACHE_VERSION }}-${{ runner.os }}-${{ env.GHC }}-${{ hashFiles('cardano-wasm/dependencies.txt') }}
# restore-keys: |
# wasm-cache-${{ env.CABAL_CACHE_VERSION }}-${{ runner.os }}-${{ env.GHC }}-

- name: Restore cached deps
run: |
wget "https://agrius.feralhosting.com/palas/wasm-cache/d1a007cb07f43d2418e4d177c7e7dffc25b30cda49c50e669a1672ffadf42fe1.tar.xz"
tar -xf d1a007cb07f43d2418e4d177c7e7dffc25b30cda49c50e669a1672ffadf42fe1.tar.xz
rm d1a007cb07f43d2418e4d177c7e7dffc25b30cda49c50e669a1672ffadf42fe1.tar.xz
rm -fr ~/.ghc-wasm/.cabal/store/
mv store ~/.ghc-wasm/.cabal/
- name: Restore cached dependencies
uses: actions/cache/restore@v4
id: cache
with:
path: |
${{ env.STORE }}
dist-newstyle
key:
wasm-cache-${{ env.CABAL_CACHE_VERSION }}-${{ runner.os }}-${{ env.GHC }}-${{ hashFiles('cardano-wasm/dependencies.txt') }}
restore-keys: |
wasm-cache-${{ env.CABAL_CACHE_VERSION }}-${{ runner.os }}-${{ env.GHC }}-

# Now we install the dependencies. If the cache was found and restored in the previous step,
# this should be a no-op, but if the cache key was not found we need to build stuff so we can
# cache it for the next step.
- name: Install dependencies
run: |
wasm32-wasi-cabal build cardano-wasm --only-dependencies --no-semaphore -j1 --ghc-options="-j1"
wasm32-wasi-cabal build cardano-wasm --only-dependencies

# Always store the cabal cache.
# - name: Cache Cabal store
# uses: actions/cache/save@v4
# if: always()
# with:
# path: |
# ${{ env.STORE }}
# dist-newstyle
# key:
# ${{ steps.cache.outputs.cache-primary-key }}
- name: Cache Cabal store
uses: actions/cache/save@v4
if: always()
with:
path: |
${{ env.STORE }}
dist-newstyle
key:
${{ steps.cache.outputs.cache-primary-key }}

# Now we build.
- name: Build all
run: |
wasm32-wasi-cabal build cardano-wasm --no-semaphore -j1 --ghc-options="-j1"
wasm32-wasi-cabal build cardano-wasi --no-semaphore -j1 --ghc-options="-j1"
wasm32-wasi-cabal build cardano-wasm
wasm32-wasi-cabal build cardano-wasi
wasm-opt -O4 $(env -u CABAL_CONFIG wasm32-wasi-cabal list-bin exe:cardano-wasi | tail -n1) -o cardano-wasm/cardano-wasi.wasm

- name: Prepare example
Expand Down
6 changes: 5 additions & 1 deletion cardano-wasm/cardano-wasm.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,18 @@ library cardano-wasi-lib
src-lib

exposed-modules:
Cardano.Wasm.Api.Certificate.StakeCertificate
Cardano.Wasm.Api.GRPC
Cardano.Wasm.Api.Info
Cardano.Wasm.Api.InfoToTypeScript
Cardano.Wasm.Api.Tx
Cardano.Wasm.Api.TypeScriptDefs
Cardano.Wasm.Api.Wallet
Cardano.Wasm.ExceptionHandling
Cardano.Wasm.NumberConversion

other-modules:
Cardano.Wasm.Internal.Api.Era
Cardano.Wasm.Internal.Api.Random

build-depends:
Expand Down Expand Up @@ -77,8 +80,9 @@ executable cardano-wasi
if arch(wasm32)
ghc-options:
-no-hs-main
"-optl-Wl,--strip-all,--export=hs_init,--export=newTx,--export=newExperimentalEraTx,--export=newConwayTx,--export=addTxInput,--export=addSimpleTxOut,--export=appendCertificateToTx,--export=setFee,--export=estimateMinFee,--export=signWithPaymentKey,--export=alsoSignWithPaymentKey,--export=toCbor,--export=generatePaymentWallet,--export=generateStakeWallet,--export=restorePaymentWalletFromSigningKeyBech32,--export=restoreStakeWalletFromSigningKeyBech32,--export=generateTestnetPaymentWallet,--export=generateTestnetStakeWallet,--export=restoreTestnetPaymentWalletFromSigningKeyBech32,--export=restoreTestnetStakeWalletFromSigningKeyBech32,--export=getAddressBech32,--export=getBech32ForPaymentVerificationKey,--export=getBech32ForPaymentSigningKey,--export=getBech32ForStakeVerificationKey,--export=getBech32ForStakeSigningKey,--export=getBase16ForPaymentVerificationKeyHash,--export=getBase16ForStakeVerificationKeyHash,--export=mallocNBytes,--export=getStrLen,--export=freeMemory"
"-optl-Wl,--strip-all,--export=hs_init,--export=newTx,--export=newUpcomingEraTx,--export=addTxInput,--export=addSimpleTxOut,--export=appendCertificateToTx,--export=setFee,--export=estimateMinFee,--export=signWithPaymentKey,--export=alsoSignWithPaymentKey,--export=toCbor,--export=makeStakeAddressStakeDelegationCertificate,--export=makeStakeAddressStakeDelegationCertificateUpcomingEra,--export=makeStakeAddressRegistrationCertificate,--export=makeStakeAddressRegistrationCertificateUpcomingEra,--export=makeStakeAddressUnregistrationCertificate,--export=makeStakeAddressUnregistrationCertificateUpcomingEra,--export=generatePaymentWallet,--export=generateStakeWallet,--export=restorePaymentWalletFromSigningKeyBech32,--export=restoreStakeWalletFromSigningKeyBech32,--export=generateTestnetPaymentWallet,--export=generateTestnetStakeWallet,--export=restoreTestnetPaymentWalletFromSigningKeyBech32,--export=restoreTestnetStakeWalletFromSigningKeyBech32,--export=getAddressBech32,--export=getBech32ForPaymentVerificationKey,--export=getBech32ForPaymentSigningKey,--export=getBech32ForStakeVerificationKey,--export=getBech32ForStakeSigningKey,--export=getBase16ForPaymentVerificationKeyHash,--export=getBase16ForStakeVerificationKeyHash,--export=mallocNBytes,--export=getStrLen,--export=freeMemory"
other-modules:
Cardano.Wasi.Internal.Api.Certificate.StakeCertificate
Cardano.Wasi.Internal.Api.GRPC
Cardano.Wasi.Internal.Api.Memory
Cardano.Wasi.Internal.Api.Tx
Expand Down
12 changes: 6 additions & 6 deletions cardano-wasm/examples/basic/example.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,26 @@ async function get_protocol_params() {
let protocolParams = await get_protocol_params();

const output = document.createElement("code");
output.innerText = "";
output.textContent = "";
output.id = "test-output";
document.body.appendChild(output);

function log(out) {
console.log(out);
if (typeof (out) == "object") {
output.innerText += "> [object] {\n";
output.textContent += "> [object] {\n";
for (let [key, val] of Object.entries(out)) {
let text = val.toString();
if (typeof (val) == "function") {
text = text.split("{")[0];
}
output.innerText += "    " + key + ": " + text + "\n";
output.textContent += "    " + key + ": " + text + "\n";
}
output.innerText += "  }\n";
output.textContent += "  }\n";
} else if (typeof (out) == "bigint") {
output.innerText += "> " + out.toString() + "n\n";
output.textContent += "> " + out.toString() + "n\n";
} else {
output.innerText += "> " + JSON.stringify(out) + "\n";
output.textContent += "> " + JSON.stringify(out) + "\n";
}
}

Expand Down
30 changes: 30 additions & 0 deletions cardano-wasm/js-test/basic-test.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
> "Api object:"
> [object] {
    objectType: cardano-api
    tx: [object Object]
    newGrpcConnection: async function (...args)
    certificate: [object Object]
    wallet: [object Object]
  }
> "Bech32 of address:"
> "addr_test1vp93p9em3regvgylxuvet6fgr3e9sn259pcejgrk4ykystcs7v8j6"
> "UnsignedTx object:"
> [object] {
    objectType: UnsignedTx
    addTxInput: function (txId,txIx)
    addSimpleTxOut: function (destAddr,lovelaceAmount)
    appendCertificateToTx: function (certCbor)
    setFee: function (lovelaceAmount)
    estimateMinFee: function (protocolParams,numKeyWitnesses,numByronKeyWitnesses,totalRefScriptSize)
    signWithPaymentKey: function (signingKey)
  }
> "Estimated fee:"
> 164005n
> "SignedTx object:"
> [object] {
    objectType: SignedTx
    alsoSignWithPaymentKey: function (signingKey)
    txToCbor: function ()
  }
> "Tx CBOR:"
> "84a300d9010281825820be6efd42a3d7b9a00d09d77a5d41e55ceaf0bd093a8aa8a893ce70d9caafd97800018182581d6082935e44937e8b530f32ce672b5d600d0a286b4e8a52c6555f659b871a00989680021a000280a5a100d9010281825820adfc1c30385916da87db1ba3328f0690a57ebb2a6ac9f6f86b2d97f943adae005840a49259b5977aea523b46f01261fbff93e0899e8700319e11f5ab96b67eb628fca1a233ce2d50ee3227b591b84f27237d920d63974d65728362382f751c4d9400f5f6"
17 changes: 15 additions & 2 deletions cardano-wasm/js-test/basic-test.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { test, expect } from '@playwright/test';
import { readFileSync, writeFileSync } from 'fs';
import { resolve } from 'path';

const goldenPath = resolve(__dirname, 'basic-test.golden');
const recreateGoldenFiles = !!process.env.RECREATE_GOLDEN_FILES;

test('test output matches', async ({ page }) => {
// Navigate to the test page
Expand All @@ -7,6 +12,14 @@ test('test output matches', async ({ page }) => {
await expect(page).toHaveTitle(/cardano-wasm test/);
// Wait for the test to finish running (we signal this by creating a tag with id "finish-tag" and text "Finished test!")
await expect(page.locator('#finish-tag')).toHaveText("Finished test!");
// Check the output of the test (from the example folder), which is displayed in the code element with id "test-output". The output contains information about the various objects and results of trying some of the functions.
await expect(page.locator('#test-output')).toHaveText("> \"Api object:\"> [object] { objectType: cardano-api tx: [object Object] newGrpcConnection: async function (...args) wallet: [object Object] }> \"Bech32 of address:\"> \"addr_test1vp93p9em3regvgylxuvet6fgr3e9sn259pcejgrk4ykystcs7v8j6\"> \"UnsignedTx object:\"> [object] { objectType: UnsignedTx addTxInput: function (txId,txIx) addSimpleTxOut: function (destAddr,lovelaceAmount) appendCertificateToTx: function (certCbor) setFee: function (lovelaceAmount) estimateMinFee: function (protocolParams,numKeyWitnesses,numByronKeyWitnesses,totalRefScriptSize) signWithPaymentKey: function (signingKey) }> \"Estimated fee:\"> 164005n> \"SignedTx object:\"> [object] { objectType: SignedTx alsoSignWithPaymentKey: function (signingKey) txToCbor: function () }> \"Tx CBOR:\"> \"84a300d9010281825820be6efd42a3d7b9a00d09d77a5d41e55ceaf0bd093a8aa8a893ce70d9caafd97800018182581d6082935e44937e8b530f32ce672b5d600d0a286b4e8a52c6555f659b871a00989680021a000280a5a100d9010281825820adfc1c30385916da87db1ba3328f0690a57ebb2a6ac9f6f86b2d97f943adae005840a49259b5977aea523b46f01261fbff93e0899e8700319e11f5ab96b67eb628fca1a233ce2d50ee3227b591b84f27237d920d63974d65728362382f751c4d9400f5f6\"");
// Check the output of the test against the golden file
const actual = await page.locator('#test-output').textContent();

if (recreateGoldenFiles) {
writeFileSync(goldenPath, actual ?? '', 'utf-8');
console.log(`Golden file updated: ${goldenPath}`);
} else {
const goldenText = readFileSync(goldenPath, 'utf-8');
expect(actual).toBe(goldenText);
}
});
69 changes: 66 additions & 3 deletions cardano-wasm/lib-wrapper/cardano-api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@ declare interface CardanoApi {
*/
tx: {
/**
* Create a new unsigned transaction in the current era (currently Conway).
* Create a new unsigned transaction in the current mainnet era (currently Conway).
* @returns A promise that resolves to a new `UnsignedTx` object.
*/
newTx(): Promise<UnsignedTx>;

/**
* Create a new unsigned transaction in the current experimental era (currently unavailable).
* Create a new unsigned transaction in the upcoming mainnet era (currently Dijkstra).
* @returns A promise that resolves to a new `UnsignedTx` object.
*/
newExperimentalEraTx(): Promise<UnsignedTx>;
newUpcomingEraTx(): Promise<UnsignedTx>;

/**
* Create a new unsigned transaction in the Conway era.
Expand All @@ -45,6 +45,69 @@ declare interface CardanoApi {
*/
newGrpcConnection(webGrpcUrl: string): Promise<GrpcConnection>;

/**
* Methods for creating certificates.
*/
certificate: {
/**
* Methods for creating certificates in the current mainnet era (currently Conway).
*/
mainnetEra: {
/**
* Make a certificate that delegates a stake address to a stake pool in the current mainnet era (currently Conway).
* @param stakeKeyHash The stake key hash in base16 format.
* @param poolId The pool ID in base16 format.
* @returns A promise that resolves to the CBOR-encoded certificate as a hex string.
*/
makeStakeAddressStakeDelegationCertificate(stakeKeyHash: string, poolId: string): Promise<string>;

/**
* Make a stake address registration certificate in the current mainnet era (currently Conway).
* @param stakeKeyHash The stake key hash in base16 format.
* @param deposit The deposit amount in lovelaces.
* @returns A promise that resolves to the CBOR-encoded certificate as a hex string.
*/
makeStakeAddressRegistrationCertificate(stakeKeyHash: string, deposit: bigint): Promise<string>;

/**
* Make a stake address unregistration certificate in the current mainnet era (currently Conway).
* @param stakeKeyHash The stake key hash in base16 format.
* @param deposit The deposit amount in lovelaces.
* @returns A promise that resolves to the CBOR-encoded certificate as a hex string.
*/
makeStakeAddressUnregistrationCertificate(stakeKeyHash: string, deposit: bigint): Promise<string>;
}

/**
* Methods for creating certificates in the current upcoming era (currently Dijkstra).
*/
upcomingEra: {
/**
* Make a certificate that delegates a stake address to a stake pool in the current upcoming era (currently Dijkstra).
* @param stakeKeyHash The stake key hash in base16 format.
* @param poolId The pool ID in base16 format.
* @returns A promise that resolves to the CBOR-encoded certificate as a hex string.
*/
makeStakeAddressStakeDelegationCertificateUpcomingEra(stakeKeyHash: string, poolId: string): Promise<string>;

/**
* Make a stake address registration certificate in the current upcoming era (currently Dijkstra).
* @param stakeKeyHash The stake key hash in base16 format.
* @param deposit The deposit amount in lovelaces.
* @returns A promise that resolves to the CBOR-encoded certificate as a hex string.
*/
makeStakeAddressRegistrationCertificateUpcomingEra(stakeKeyHash: string, deposit: bigint): Promise<string>;

/**
* Make a stake address unregistration certificate in the current upcoming era (currently Dijkstra).
* @param stakeKeyHash The stake key hash in base16 format.
* @param deposit The deposit amount in lovelaces.
* @returns A promise that resolves to the CBOR-encoded certificate as a hex string.
*/
makeStakeAddressUnregistrationCertificateUpcomingEra(stakeKeyHash: string, deposit: bigint): Promise<string>;
}
}

/**
* Methods for generating and restoring wallets.
*/
Expand Down
2 changes: 1 addition & 1 deletion cardano-wasm/lib-wrapper/grpc-connection.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ declare interface GrpcConnection {

/**
* Get the era from the Cardano Node using a GRPC-web client.
* @returns A promise that resolves to the current era number.
* @returns A promise that resolves to the current mainnet era number.
*/
getEra(): Promise<number>;

Expand Down
Loading
Loading