Skip to content

Commit 5154bd1

Browse files
authored
Merge pull request #969 from IntersectMBO/prepare-for-npm
Prepare `cardano-wasm` for NPM
2 parents e72912b + 20c310e commit 5154bd1

File tree

9 files changed

+414
-16
lines changed

9 files changed

+414
-16
lines changed

cardano-wasm/js-test/basic-test.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ test('test output matches', async ({ page }) => {
88
// Wait for the test to finish running (we signal this by creating a tag with id "finish-tag" and text "Finished test!")
99
await expect(page.locator('#finish-tag')).toHaveText("Finished test!");
1010
// 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.
11-
await expect(page.locator('#test-output')).toHaveText("> \"Api object:\"> [object] { objectType: cardano-api newTx: async function(...args) newExperimentalEraTx: async function(...args) newConwayTx: async function(...args) newGrpcConnection: async function(...args) generatePaymentWallet: async function(...args) restorePaymentWalletFromSigningKeyBech32: async function(...args) generateTestnetPaymentWallet: async function(...args) restoreTestnetPaymentWalletFromSigningKeyBech32: async function(...args) }> \"Bech32 of address:\"> \"addr_test1vp93p9em3regvgylxuvet6fgr3e9sn259pcejgrk4ykystcs7v8j6\"> \"UnsignedTx object:\"> [object] { objectType: UnsignedTx addTxInput: function(txId,txIx) addSimpleTxOut: function(destAddr,lovelaceAmount) 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\"");
11+
await expect(page.locator('#test-output')).toHaveText("> \"Api object:\"> [object] { objectType: cardano-api newTx: async function(...args) newExperimentalEraTx: async function(...args) newConwayTx: async function(...args) newGrpcConnection: async function(...args) generatePaymentWallet: async function(...args) restorePaymentWalletFromSigningKeyBech32: async function(...args) generateTestnetPaymentWallet: async function(...args) restoreTestnetPaymentWalletFromSigningKeyBech32: async function(...args) }> \"Bech32 of address:\"> \"addr_test1vp93p9em3regvgylxuvet6fgr3e9sn259pcejgrk4ykystcs7v8j6\"> \"UnsignedTx object:\"> [object] { objectType: UnsignedTx addTxInput: function (txId,txIx) addSimpleTxOut: function (destAddr,lovelaceAmount) 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\"");
1212
});

cardano-wasm/lib-wrapper/cardano-api.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import { createInitializer } from './main.js';
1010
const wasmUrl = './cardano-wasm.wasm';
1111

1212
const getWasi = async () => {
13-
return (await import("https://unpkg.com/@bjorn3/[email protected]/dist/index.js")).WASI;
13+
const { WASI } = await import("https://unpkg.com/@bjorn3/[email protected]/dist/index.js");
14+
return new WASI([], [], []);
1415
};
1516

1617
const loadWasmModule = async (importObject) => {

cardano-wasm/lib-wrapper/main.js

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
* for the NPM package (e.g: "browser.js" and "node.js").
1313
**/
1414

15-
import ghc_wasm_jsffi from "./cardano-wasm.js";
1615
const __exports = {};
1716

1817
export function createInitializer(getWasi, loadWasmModule) {
@@ -24,19 +23,24 @@ export function createInitializer(getWasi, loadWasmModule) {
2423
* Convert Base64 to Base16 encoding
2524
*/
2625
base64ToHex: function(base64) {
27-
const binary = atob(base64);
28-
return [...binary].reduce((hex, char) => {
29-
const byteHex = char.charCodeAt(0).toString(16).padStart(2, '0');
30-
return hex + byteHex;
31-
}, '');
26+
if (typeof atob === 'function') {
27+
const binary = atob(base64);
28+
return [...binary].reduce((hex, char) => {
29+
const byteHex = char.charCodeAt(0).toString(16).padStart(2, '0');
30+
return hex + byteHex;
31+
}, '');
32+
} else {
33+
return Buffer.from(base64, 'base64').toString('hex');
34+
}
3235
}
3336
}
3437

3538
return async function initialise() {
3639

37-
const WASI = await getWasi();
40+
const ghc_wasm_jsffi = (await eval(`import('./cardano-wasm.js')`)).default;
41+
42+
const wasi = await getWasi();
3843

39-
const wasi = new WASI([], [], []);
4044
const importObject = {
4145
ghc_wasm_jsffi: ghc_wasm_jsffi(__exports),
4246
wasi_snapshot_preview1: wasi.wasiImport,
@@ -55,10 +59,12 @@ export function createInitializer(getWasi, loadWasmModule) {
5559
// 'this' and 'arguments' are passed through from the wrapper to 'func'.
5660
// Using eval allows the returned function to have named parameters for inspectability.
5761
const wrapper = eval(`
58-
(function(${paramString}) {
59-
return func.apply(this, arguments);
62+
(function (f) {
63+
return (function (${paramString}) {
64+
return f.apply(this, arguments);
65+
});
6066
})
61-
`);
67+
`)(func);
6268
return wrapper;
6369
}
6470

@@ -67,10 +73,12 @@ export function createInitializer(getWasi, loadWasmModule) {
6773
const paramString = params.map(p => p.name).join(',');
6874
// Dynamically create an async function.
6975
const wrapper = eval(`
70-
(async function(${paramString}) {
71-
return await func.apply(this, arguments);
76+
(function (f) {
77+
return (async function (${paramString}) {
78+
return await f.apply(this, arguments);
79+
});
7280
})
73-
`);
81+
`)(func);
7482
return wrapper;
7583
}
7684

@@ -132,5 +140,6 @@ export function createInitializer(getWasi, loadWasmModule) {
132140

133141
return cardanoApi;
134142
}
143+
135144
};
136145

cardano-wasm/npm-wrapper/README.md

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
2+
# cardano-wasm
3+
4+
JavaScript bindings to the WASM build of [cardano-api](https://github.com/intersectmbo/cardano-api). You can find more information about the WASM build in the [cardano-wasm](https://github.com/IntersectMBO/cardano-api/tree/master/cardano-wasm) subfolder.
5+
6+
This package provides functionalities to inspect, create, and manipulate transactions and other information related to Cardano blockchains, as well as interacting directly with them through the `cardano-node`.
7+
8+
-----
9+
10+
## 📦 Installation
11+
12+
To install the package, run the following command in your project's directory:
13+
14+
```bash
15+
npm install cardano-wasm
16+
```
17+
18+
-----
19+
20+
## 🚀 Usage
21+
22+
Because this library is a wrapper around a WebAssembly (WASM) module, you must first initialize it asynchronously. The dynamic `import()` function returns a promise that resolves to the module, and its `default` export is an `async` function that loads and prepares the API.
23+
24+
Here is a clean, modern example using an `async` IIFE (Immediately Invoked Function Expression) to get you started.
25+
26+
Create an `index.js` file:
27+
28+
```javascript
29+
// A self-executing async function to initialize and use the Cardano API
30+
(async () => {
31+
try {
32+
// 1. Dynamically import the package.
33+
const cardanoModule = await import('cardano-wasm');
34+
35+
// 2. The module's default export is an async function that initializes the WASM instance.
36+
// Await this to get the usable API object.
37+
const api = await cardanoModule.default();
38+
console.log("Cardano API loaded successfully!");
39+
40+
// 3. Now you can use the API to perform Cardano operations.
41+
// For example, let's create a new transaction body.
42+
const tx = await api.newTx();
43+
console.log("New Transaction Body Created:", tx);
44+
45+
// You can now build upon the 'tx' object to add inputs, outputs, etc.
46+
47+
} catch (error) {
48+
console.error("Failed to load or use the Cardano API:", error);
49+
}
50+
})();
51+
```
52+
53+
### Node.js
54+
55+
To run this file, you can simply use Node.js:
56+
57+
```bash
58+
node index.js
59+
```
60+
61+
### Webpack
62+
63+
Alternatively you can use `cardano-wasm` as part of a `webpack` project, but you'll need to install `html-webpack-plugin` and `copy-webpack-plugin`:
64+
65+
```bash
66+
npm install html-webpack-plugin copy-webpack-plugin
67+
```
68+
69+
And do a couple of adjustments to the `webpack.config.js`:
70+
71+
```js
72+
const HtmlWebpackPlugin = require('html-webpack-plugin');
73+
const CopyWebpackPlugin = require('copy-webpack-plugin');
74+
const path = require('path');
75+
76+
module.exports = {
77+
entry: './src/index.js',
78+
79+
target: ['web', 'es2020'],
80+
81+
output: {
82+
filename: 'main.js',
83+
path: path.resolve(__dirname, 'dist'),
84+
publicPath: '',
85+
},
86+
87+
experiments: {
88+
asyncWebAssembly: true,
89+
},
90+
91+
devtool: 'source-map',
92+
93+
plugins: [
94+
new HtmlWebpackPlugin({ template: './index.html' }),
95+
new CopyWebpackPlugin({
96+
patterns: [
97+
{
98+
from: 'node_modules/cardano-wasm/dist/cardano-wasm.js',
99+
to: 'cardano-wasm.js',
100+
},
101+
],
102+
}),
103+
],
104+
105+
module: {
106+
rules: [
107+
{
108+
test: /\.wasm$/,
109+
type: 'asset/resource',
110+
},
111+
],
112+
},
113+
114+
mode: 'development',
115+
};
116+
```
117+
118+
Your `webpack.config.js` configuration may vary, but it could be necessary to adjust the `target`, `experiments`, `devtool`, `plugins`, and `module` keys as shown in the example above.
119+
120+
-----
121+
122+
## 📖 API Reference
123+
124+
For a detailed understanding of all available functionalities, please refer to the official [**cardano-wasm documentation**](https://cardano-api.cardano.intersectmbo.org/cardano-wasm/typedoc/).
125+
126+
-----
127+
128+
## 🚗 Road map
129+
130+
So far, `cardano-wasm` supports:
131+
- Basic wallet management.
132+
- Basic transaction building.
133+
- Transaction signing and submission through web-grpc.
134+
- Basic node communication through web-grpc.
135+
136+
In the future, we aim to add support for:
137+
- Core wallet and staking features:
138+
- Extended wallet management (with mnemonics and stake addresses)
139+
- Staking and Delegation (certificates)
140+
- Multi-asset and metadata support
141+
- Native scripts and Plutus support
142+
- Governance:
143+
- DRep management
144+
- Voting
145+
- Constitution and committee management
146+
- Advanced transaction features
147+
- Validity interval
148+
- Reward withdrawals
149+
150+
In parallel, we will be working on expanding the support for querying the node through gRPC and web-grpc.
151+
152+
-----
153+
154+
## 📄 License
155+
156+
This project is licensed under **Apache-2.0 license**.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* @jest-environment node
3+
*/
4+
import initialise from "./dist/node.cjs";
5+
6+
// Main test suite for the Cardano API
7+
describe('Cardano API', () => {
8+
9+
let api;
10+
11+
beforeAll(async () => {
12+
api = await initialise();
13+
});
14+
15+
// Test case adapted from your browser script
16+
it('should build, sign, and serialize a simple transaction', async () => {
17+
// Test constants
18+
const PREVIEW_MAGIC_NUMBER = 2;
19+
const secretKey = "addr_sk1648253w4tf6fv5fk28dc7crsjsaw7d9ymhztd4favg3cwkhz7x8sl5u3ms";
20+
21+
const expectedAddress = "addr_test1vp93p9em3regvgylxuvet6fgr3e9sn259pcejgrk4ykystcs7v8j6";
22+
const txInputHash = "be6efd42a3d7b9a00d09d77a5d41e55ceaf0bd093a8aa8a893ce70d9caafd978";
23+
const outputAddress = "addr_test1vzpfxhjyjdlgk5c0xt8xw26avqxs52rtf69993j4tajehpcue4v2v";
24+
25+
// Restore wallet and verify the address
26+
const wallet = await api.restoreTestnetPaymentWalletFromSigningKeyBech32(PREVIEW_MAGIC_NUMBER, secretKey);
27+
const bech32Address = await wallet.getAddressBech32();
28+
expect(bech32Address).toBe(expectedAddress);
29+
30+
// Create a new transaction
31+
const emptyTx = await api.newTx();
32+
expect(emptyTx).toBeDefined();
33+
34+
// Add inputs and outputs
35+
const tx = await emptyTx
36+
.addTxInput(txInputHash, 0)
37+
.addSimpleTxOut(outputAddress, 10_000_000n);
38+
39+
// Set the fee and sign the transaction
40+
const signedTx = await tx.setFee(10_000n).signWithPaymentKey(secretKey);
41+
expect(signedTx).toBeDefined();
42+
43+
// Serialize the transaction to CBOR format
44+
const txCbor = await signedTx.txToCbor();
45+
expect(txCbor).toBe("84a300d9010281825820be6efd42a3d7b9a00d09d77a5d41e55ceaf0bd093a8aa8a893ce70d9caafd97800018182581d6082935e44937e8b530f32ce672b5d600d0a286b4e8a52c6555f659b871a0098968002192710a100d9010281825820adfc1c30385916da87db1ba3328f0690a57ebb2a6ac9f6f86b2d97f943adae0058400b19a00593e659ad0f10951f0f7d1e8a8b93112c60f67277529f91340581639e92ed4d0042ff92a0076cd69deb7e708acfdb73bb4ae79cf4bc06fd6d15efa208f5f6");
46+
});
47+
48+
});
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
{
2+
"name": "cardano-wasm",
3+
"version": "10.0.0",
4+
"description": "JavaScript bindings to the WASM build of cardano-api (https://github.com/intersectmbo/cardano-api), which provides functionalities to inspect, create, and manipulate, transactions and other information related to Cardano blockchains, as well as interacting directly with them through the cardano-node.",
5+
"main": "./dist/node.cjs",
6+
"scripts": {
7+
"build": "rollup -c",
8+
"prepublishOnly": "npm run build",
9+
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
10+
},
11+
"type": "module",
12+
"repository": {
13+
"type": "git",
14+
"url": "git+https://github.com/IntersectMBO/cardano-api.git"
15+
},
16+
"keywords": [
17+
"cardano",
18+
"cardano-api",
19+
"wasm",
20+
"cardano-node",
21+
"cryptocurrency",
22+
"blockchain",
23+
"transaction",
24+
"ada",
25+
"lovelace",
26+
"testnet",
27+
"mainnet"
28+
],
29+
"author": "Input Output Global <[email protected]>",
30+
"files": [
31+
"dist"
32+
],
33+
"license": "Apache-2.0",
34+
"types": "./dist/cardano-api.d.ts",
35+
"bugs": {
36+
"url": "https://github.com/IntersectMBO/cardano-api/issues"
37+
},
38+
"homepage": "https://github.com/IntersectMBO/cardano-api/tree/master/cardano-wasm#readme",
39+
"module": "./dist/node.mjs",
40+
"browser": "./dist/browser.mjs",
41+
"exports": {
42+
".": {
43+
"types": "./dist/cardano-api.d.ts",
44+
"browser": "./dist/browser.mjs",
45+
"node": {
46+
"import": "./dist/node.mjs",
47+
"require": "./dist/node.cjs"
48+
},
49+
"import": "./dist/node.mjs",
50+
"require": "./dist/node.cjs"
51+
}
52+
},
53+
"devDependencies": {
54+
"@rollup/plugin-commonjs": "^28.0.6",
55+
"@rollup/plugin-node-resolve": "^16.0.1",
56+
"rollup": "^4.52.3",
57+
"rollup-plugin-copy": "^3.5.0",
58+
"rollup-plugin-string": "^3.0.0",
59+
"jest": "^29.7.0"
60+
},
61+
"dependencies": {
62+
"@bjorn3/browser_wasi_shim": "^0.4.2"
63+
},
64+
"jest": {
65+
"testEnvironment": "node"
66+
}
67+
}

0 commit comments

Comments
 (0)