UNPKG

open-libra-sdk

Version:

A minimalist Typescript library for interacting with the Open Libra blockchain.

332 lines (243 loc) 10.3 kB
# open-libra-sdk A minimalist Typescript library for interacting with the Open Libra blockchain. ```bash npm install open-libra-sdk ``` [https://www.npmjs.com/package/open-libra-sdk](https://www.npmjs.com/package/open-libra-sdk) ### Quick Start ```typescript // Uses LibraWallet for common account operations import { LibraWallet, Network, addressFromString } from 'open-libra-sdk' const TESTNET_URL = "https://testnet.openlibra.io/v1"; const MNEM = "your mnemonic..." // For mainnet, just initialize with your mnemonic const wallet_mainnet = LibraWallet.fromMnemonic(MNEM); // optionally, connect to a local testnet, by adding vars const wallet = LibraWallet.fromMnemonic(MNEM, Network.TESTNET, TESTNET_URL); // check your connection to the fullnode const ledgerInfo = await wallet.client?.getLedgerInfo(); console.log("block height:", ledgerInfo?.block_height); // get the account's state from chain await wallet.syncOnchain(); // parse an address which you'd like to send a tx to const addressObj = addressFromString( "0xDECAFC0FFEE", ); // use the transfer helper function const tx = await wallet.buildTransferTx(addressObj, 100); // wait for the result const res = await wallet.signSubmitWait(tx); if (res.success == false) { throw "Tx fails" } ``` ## Common Operations #### Create a client You may not need to instantiate a wallet to check the chain status. Below you can check you can connect to a fullnode, and get the API index with latest block info ```typescript import { LibraClient, Network } from 'open-libra-sdk' const TESTNET_URL = "https://testnet.openlibra.io/v1"; // for mainnet const client_mainnet = new LibraClient(); // local testnet const client_testnet = new LibraClient(Network.TESTNET, TESTNET_URL); const ledgerInfo = await client_testnet.getLedgerInfo(); console.log("block height:", ledgerInfo.block_height); expect(Number(ledgerInfo.block_height)).toBeGreaterThan(0); // Advanced: // You can reuse this client instance to create a LibraWallet instance for a user. // First get the Ed25519Account type, in this case generated: const edAccount = Ed25519Account.generate() // then init a wallet const wallet = LibraWallet.fromPrivateKey(edAccount.accountAddress, edAccount.privateKey, client_testnet); // now you can use the wallet to interact with the chain const id = await wallet.client?.general.getChainId(); console.log("chain id:", id); ``` #### Querying Latest Blocks and Transaction Versions You can easily query the latest blocks and transaction versions using the SDK helpers: ```typescript import { LibraClient, Network, getLatestBlocks, getLatestTxVersions } from "open-libra-sdk"; // Create a client for MAINNET (or TESTNET, as needed) const client = new LibraClient(Network.MAINNET); // Query the latest 5 blocks const latestBlocks = await getLatestBlocks(client, 5); console.log("Latest 5 blocks:\n", JSON.stringify(latestBlocks, null, 2)); // Query the latest 5 transaction versions (all types) const latestVersions = await getLatestTxVersions(client, 5, false); console.log("Latest 5 transaction versions (all types):\n", JSON.stringify(latestVersions, null, 2)); // Query the latest 5 user transactions only const latestUserTxs = await getLatestTxVersions(client, 5, true); console.log("Latest 5 user transactions only:\n", JSON.stringify(latestUserTxs, null, 2)); ``` #### Initialize a wallet ```typescript // You can construct the wallet object for offline (cold wallet) // cases as well as online wallets to update state and submit transactions // COLD WALLETS // simple case: cold wallet, where no key rotation happened const coldWalletFromMnem = LibraWallet.fromMnemonic(MNEM); console.log("address:", coldWalletFromMnem.getAddress().toStringLong()); // set specific private key and address: in case of key rotation const addressObj = addressFromString("0xDECAFC0FFEE"); const pkey = new Ed25519PrivateKey("0x74f18da2b80b1820b58116197b1c41f8a36e1b37a15c7fb434bb42dd7bdaa66b"); const coldWalletWithKey = LibraWallet.fromPrivateKey( addressObj, pkey, ); console.log( "other address:", coldWalletWithKey.getAddress().toStringLong(), ); // ONLINE WALLETS const mainnetHotWallet = LibraWallet.fromMnemonic( MNEM, Network.MAINNET, MAINNET_URL, ); console.log("fullnode url:", mainnetHotWallet.client?.config.fullnode); // Online wallet, using testnet const testnetHotWallet = LibraWallet.fromMnemonic( MNEM, Network.TESTNET, TESTNET_URL, ); // Get the latest account state. // Checks if key is rotated and update the LibraWallet.onchainAddress // will also update to the most recent sequence number found on chain // plus if the authentication key was changed await testnetHotWallet.syncOnchain().then(() => { console.log( "sequence number:", testnetHotWallet.txOptions.accountSequenceNumber, ); }); ``` #### Build transactions for Entry Functions Using the same wallet function above you can build arbitrary "entry functions" which call onchain smart contracts. ```typescript // ... continued from above const tx = await testnetHotWallet.buildTransaction( "0x1::ol_account::transfer", [ // address string (here's a good practice to check address parsing) addressFromString("0x1234").toStringLong(), // number of coins 100, ], ); const res = await testnetHotWallet.signSubmitWait(tx); if (res.success == false) { throw "Tx failed"; } ``` Or use the `transfer` helper for simple account transfers. ```typescript // ... continued from above // remember to update the account sequence number like so: await testnetHotWallet.syncOnchain(); // send another transaction const tx2 = await testnetHotWallet.buildTransferTx( addressFromString("0xabcde"), 100, ); const res2 = await testnetHotWallet.signSubmitWait(tx2); if (res2.success == false) { throw "Tx failed"; } ``` #### Fetch Arbitrary Data You can define a Typescript type, and the Libra.getResource will coerce the type in typescript ```typescript import { LibraClient, Network } from 'open-libra-sdk' const TESTNET_URL = "https://testnet.openlibra.io/v1"; const libra = new LibraClient(Network.TESTNET, TESTNET_URL); interface Coin { coin: { value: number; }; } const res = await libra.getResource<Coin>( // alice "0x87515d94a244235a1433d7117bc0cb154c613c2f4b1e67ca8d98a542ee3f59f5", "0x1::coin::CoinStore<0x1::libra_coin::LibraCoin>", ); if (res.coin.value == 0) { throw "no coin found" } ``` ## Troubleshooting There's a known issue when executing using `bun`. Calling a fullnode with an `https` url API will fail. Since http/2 is not fully developed in `bun` as of 1.2.2. NodeJS (with npm, yarn, pnpm) does not appear to produce this error. Deno is untested. ## Flavors Look in the `./examples` folder for commonjs, Node, and typescript imports of the module. In a common JS file you can import the sdk to manage wallets and query the chain. See the minimal example: ```typescript // Example for how common js would import the sdk const libraSDK = require('open-libra-sdk'); const main = async () => { const mnem = libraSDK.generateMnemonic(); console.log("Generate a mnemonic:\n"); console.log(mnem, "\n"); let coldWallet = libraSDK.LibraWallet.fromMnemonic(mnem); console.log(coldWallet.getAddress().toStringLong()) let client = new libraSDK.LibraClient(libraSDK.Network.MAINNET); console.log(`Client created for: ${client.config.network}`); // call a view function with a helper object that contains the // payload for querying the current validators // let vals = await client.general.viewJson(libraSDK.currentValidatorsPayload); // console.log(vals); } main() ``` # Testnet in a bottle Start a containerized testnet with docker etc. This repo contains a `tests/support/container/compose.yml` which will create a three node testnet with production binaries. ```bash # with npm/yarn/bun: bun run testnet bun run testnet-down ## call docker directly with: cd tests/support/container docker compose up --detach --timeout 600 docker compose down ``` # Maintainers ### Bun `bun` is the default Node/JS/TS runtime for OL development. https://bun.sh/docs/installation ### To install dependencies: ```bash bun install ``` ### To run tests: ```bash bun test ``` End to end tests will start a local testnet before each test, and requires `docker` be installed. ### Documentation The examples references in README.md must all be tested at: `./tests/e2e_tests/docs.tests.ts` ## Updating the View Function Sugar File (For Core Maintainers) Before each release, you should regenerate the TypeScript view function sugar file to ensure it reflects the latest Move modules and view functions. ### Steps: 1. **Ensure your Move source files are up to date.** - The script expects a directory containing your `.move` files (e.g., `./my-move-modules`). 2. **Run the codegen script:** ```sh bun ./scripts/viewCodeGen.ts <PATH_TO_MOVE_DIR> ``` - Replace `<PATH_TO_MOVE_DIR>` with the path to your Move modules directory. - Example: ```sh bun ./scripts/viewCodeGen.ts ./my-move-modules ``` 3. **Check the output:** - The generated file will be at `./src/views/viewFunctionsSugar.ts`. - Review the file for correctness if needed. 4. **Commit the updated file:** - Add and commit `src/views/viewFunctionsSugar.ts` to your release branch. ### Notes - The script will overwrite the existing sugar file. - If you add, remove, or change any `#[view]` functions in your Move code, you must regenerate this file before release. - The generated file uses local imports for development and will work out of the box with the rest of the TypeScript SDK.