hive-stream 3.0.4 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/DOCUMENTATION.md +775 -2
  3. package/README.md +34 -1
  4. package/dist/adapters/mongodb.adapter.js +21 -30
  5. package/dist/adapters/mongodb.adapter.js.map +1 -1
  6. package/dist/adapters/postgresql.adapter.js +16 -16
  7. package/dist/adapters/postgresql.adapter.js.map +1 -1
  8. package/dist/adapters/sqlite.adapter.js +17 -17
  9. package/dist/adapters/sqlite.adapter.js.map +1 -1
  10. package/dist/api.js +2 -0
  11. package/dist/api.js.map +1 -1
  12. package/dist/builders.d.ts +269 -1
  13. package/dist/builders.js +883 -1
  14. package/dist/builders.js.map +1 -1
  15. package/dist/cli/index.d.ts +2 -0
  16. package/dist/cli/index.js +209 -0
  17. package/dist/cli/index.js.map +1 -0
  18. package/dist/cli/prompts.d.ts +17 -0
  19. package/dist/cli/prompts.js +109 -0
  20. package/dist/cli/prompts.js.map +1 -0
  21. package/dist/cli/scaffold.d.ts +21 -0
  22. package/dist/cli/scaffold.js +281 -0
  23. package/dist/cli/scaffold.js.map +1 -0
  24. package/dist/cli/templates.d.ts +28 -0
  25. package/dist/cli/templates.js +329 -0
  26. package/dist/cli/templates.js.map +1 -0
  27. package/dist/config.js +2 -2
  28. package/dist/config.js.map +1 -1
  29. package/dist/contracts/checkpoint.contract.d.ts +6 -0
  30. package/dist/contracts/checkpoint.contract.js +196 -0
  31. package/dist/contracts/checkpoint.contract.js.map +1 -0
  32. package/dist/contracts/nft.contract.d.ts +2 -0
  33. package/dist/contracts/nft.contract.js +174 -0
  34. package/dist/contracts/nft.contract.js.map +1 -1
  35. package/dist/contracts/roulette.contract.d.ts +9 -0
  36. package/dist/contracts/roulette.contract.js +365 -0
  37. package/dist/contracts/roulette.contract.js.map +1 -0
  38. package/dist/contracts/ticketing.contract.js +158 -1
  39. package/dist/contracts/ticketing.contract.js.map +1 -1
  40. package/dist/index.d.ts +2 -0
  41. package/dist/index.js +5 -1
  42. package/dist/index.js.map +1 -1
  43. package/dist/metadata.js +1 -0
  44. package/dist/metadata.js.map +1 -1
  45. package/dist/streamer.d.ts +105 -10
  46. package/dist/streamer.js +539 -61
  47. package/dist/streamer.js.map +1 -1
  48. package/dist/types/hive-stream.d.ts +306 -0
  49. package/dist/utils.d.ts +442 -0
  50. package/dist/utils.js +1423 -0
  51. package/dist/utils.js.map +1 -1
  52. package/package.json +7 -5
package/DOCUMENTATION.md CHANGED
@@ -13,6 +13,30 @@ This document focuses on the contract system and how to build robust contracts u
13
13
 
14
14
  ---
15
15
 
16
+ ## CLI
17
+
18
+ Hive Stream ships a scaffolding CLI. It generates a complete TypeScript app — package.json, tsconfig, `.env.example`, a wired-up `src/index.ts`, a starter custom contract, and a README with payload examples for every registered contract action.
19
+
20
+ ```shell
21
+ npx hive-stream new <name> [options] # scaffold a new app
22
+ npx hive-stream list # show templates and adapters
23
+ ```
24
+
25
+ Run `new` without options for interactive prompts, or pass flags for non-interactive use:
26
+
27
+ | Flag | Description |
28
+ | --- | --- |
29
+ | `-t, --template <id>` | `minimal`, `game`, `exchange`, `social`, `blog`, `token`, or `assets` |
30
+ | `-a, --adapter <id>` | `sqlite` (default), `mongodb`, or `postgresql` |
31
+ | `-u, --username <name>` | Hive account the app runs as |
32
+ | `--api` | Enable the built-in HTTP API server |
33
+ | `--install` | Run `npm install` after scaffolding |
34
+ | `-y, --yes` | Skip prompts and accept defaults |
35
+
36
+ Generated apps read `HIVE_USERNAME`, `HIVE_POSTING_KEY`, and `HIVE_ACTIVE_KEY` from `.env` via `new Streamer({ env: true })`.
37
+
38
+ ---
39
+
16
40
  ## Concepts
17
41
 
18
42
  ### Contracts
@@ -299,6 +323,7 @@ export const TipJar = defineContract({
299
323
  - `createTokenContract` - SQL-backed fungible tokens
300
324
  - `createNFTContract` - SQL-backed NFTs
301
325
  - `createRpsContract` - Rock/Paper/Scissors
326
+ - `createRouletteContract` - Roulette with straight, color, parity, dozen, column, and low/high bets
302
327
  - `createPollContract` - Polls and votes
303
328
  - `createTipJarContract` - Tip jar + message log
304
329
  - `createExchangeContract` - Deposits/withdrawals/orders/matching (SQL)
@@ -329,14 +354,16 @@ export const TipJar = defineContract({
329
354
  - `createPayrollContract` - Recurring payroll budgets with scheduled runs and recipient balances
330
355
  - `createProposalTimelockContract` - Timelock approval queues for delayed governance-style actions
331
356
  - `createBundleMarketplaceContract` - Bundle storefronts with inventory, purchases, and fulfillment tracking
332
- - `createTicketingContract` - Event ticket sales with check-ins, refunds, and capacity limits
357
+ - `createTicketingContract` - Event ticket sales with check-ins, refunds, capacity limits, transfers, and peer-to-peer resale
333
358
  - `createFanClubContract` - Paid clubs with member renewals, points, and perk redemptions
359
+ - `createCheckpointContract` - Periodic on-chain state hashes so independent operators can verify they derive identical state
334
360
 
335
361
  ### Example Snippets
336
362
 
337
363
  Quick-start snippets live in `examples/contracts/`:
338
364
 
339
365
  - `examples/contracts/rps.ts`
366
+ - `examples/contracts/roulette.ts`
340
367
  - `examples/contracts/poll.ts`
341
368
  - `examples/contracts/tipjar.ts`
342
369
  - `examples/contracts/exchange.ts`
@@ -435,8 +462,754 @@ When the built-in API server is running, the following endpoints are available:
435
462
 
436
463
  ---
437
464
 
465
+ ## Non-Custodial Assets Guide
466
+
467
+ The NFT and ticketing contracts support fully non-custodial trading: buyers pay sellers **directly**, and the contract only verifies the on-chain payment and flips ownership. Funds never pass through the app account.
468
+
469
+ ### Direct NFT sales
470
+
471
+ 1. The owner lists with `listNFT` (custom_json): `{"collectionSymbol":"ART","tokenId":"piece-1","price":"10.000","currency":"HIVE"}`.
472
+ 2. The buyer sends the exact price **to the seller's account** with the memo `{"hive_stream":{"contract":"nft","action":"buyNFTDirect","payload":{"collectionSymbol":"ART","tokenId":"piece-1"}}}`.
473
+ 3. The contract verifies the transfer recipient is the seller and the amount/currency match the listing, then transfers ownership.
474
+
475
+ The examples above assume the contract was registered as `createNFTContract({ name: 'nft' })` (what scaffolded apps do); the default registered name is `hivenft`.
476
+
477
+ Royalties cannot be enforced on a peer-to-peer payment; `buyNFTDirect` records the royalty owed (`royaltyDue`, `royaltyRecipient`) in the emitted event so marketplaces and sellers can honour them. The custodial `buyNFT` flow (payment to the app account, which pays out the seller and royalties) remains available.
478
+
479
+ ### Batch minting
480
+
481
+ `mintBatchNFT` (custom_json, collection creator only) mints many tokens in one operation, either as numbered editions or an explicit list:
482
+
483
+ ```json
484
+ {"collectionSymbol":"TICKETS","to":"some-account","editions":{"baseTokenId":"GA","count":100}}
485
+ {"collectionSymbol":"TICKETS","to":"some-account","tokens":[{"tokenId":"VIP-1","metadata":"{\"seat\":\"A1\"}"}]}
486
+ ```
487
+
488
+ Editions mint `GA-1` … `GA-100` (up to 1,000 per operation); explicit lists allow up to 50 tokens with per-token metadata. Both respect the collection's maximum supply atomically.
489
+
490
+ ### Ticket transfers and resale
491
+
492
+ Tickets from the ticketing contract are bearer assets:
493
+
494
+ - `transferTicket` (custom_json, owner only): `{"ticketId":"ticket-1","to":"friend-account"}`
495
+ - `listTicket` / `unlistTicket` (custom_json, owner only): `{"ticketId":"ticket-1","price":"3.000"}` — asset defaults to the event's asset
496
+ - `buyTicketDirect` (transfer memo): buyer pays the listed price **directly to the seller** with `{"hive_stream":{"contract":"ticketing","action":"buyTicketDirect","payload":{"ticketId":"ticket-1"}}}`
497
+
498
+ Transferring a ticket deactivates any active resale listing, and stale listings (where the seller no longer owns the ticket) are rejected at purchase time.
499
+
500
+ ### State checkpoints
501
+
502
+ Because contract state is derived off-chain, anyone can verify an app by replaying the chain. `createCheckpointContract` makes that verification cheap: it periodically publishes a deterministic sha256 hash of your contract tables as a custom_json operation, and records other operators' published hashes with a match/mismatch verdict.
503
+
504
+ ```ts
505
+ import { createCheckpointContract, TimeAction } from 'hive-stream';
506
+
507
+ await streamer.registerContract(createCheckpointContract({
508
+ account: 'my-app-account',
509
+ tables: ['nft_collections', 'nft_tokens', 'nft_listings']
510
+ }));
511
+
512
+ await streamer.registerAction(new TimeAction('1h', 'state-checkpoint', 'checkpoint', 'publishCheckpoint', {}));
513
+ ```
514
+
515
+ The hash is order-independent (rows are hashed individually and sorted), so any adapter backend that holds the same logical state produces the same digest. Publishing requires a posting key for the configured account. Results land in the `state_checkpoints` table; a `matched = 0` row means your node's state diverges from the publisher's (or the hash was computed after state moved on — recompute at the same cadence to confirm).
516
+
517
+ ---
518
+
519
+ ## Social Operations
520
+
521
+ Follow, unfollow, mute, and reblog users directly from the streamer:
522
+
523
+ ```typescript
524
+ // Direct methods
525
+ await streamer.follow('myaccount', 'targetuser');
526
+ await streamer.unfollow('myaccount', 'targetuser');
527
+ await streamer.mute('myaccount', 'spammer');
528
+ await streamer.reblog('myaccount', 'author', 'great-post');
529
+
530
+ // Or use builders
531
+ await streamer.ops.follow().follower('myaccount').following('targetuser').send();
532
+ await streamer.ops.unfollow().follower('myaccount').following('targetuser').send();
533
+ await streamer.ops.mute().follower('myaccount').following('spammer').send();
534
+ await streamer.ops.reblog().account('myaccount').author('author').permlink('great-post').send();
535
+ ```
536
+
537
+ ---
538
+
539
+ ## Staking Operations
540
+
541
+ Power up, power down, delegate, and undelegate Hive Power:
542
+
543
+ ```typescript
544
+ // Power up HIVE to HP
545
+ await streamer.powerUp('myaccount', 'myaccount', '100.000');
546
+ await streamer.ops.powerUp().from('myaccount').to('myaccount').amount(100).send();
547
+
548
+ // Power down (withdraw vesting)
549
+ await streamer.powerDown('myaccount', '50000.000000 VESTS');
550
+ await streamer.ops.powerDown().account('myaccount').vestingShares('50000.000000 VESTS').send();
551
+
552
+ // Cancel active power down
553
+ await streamer.cancelPowerDown('myaccount');
554
+ await streamer.ops.cancelPowerDown().account('myaccount').send();
555
+
556
+ // Delegate HP to another account
557
+ await streamer.delegateVestingShares('myaccount', 'recipient', '10000.000000 VESTS');
558
+ await streamer.ops.delegate().delegator('myaccount').delegatee('recipient').vestingShares('10000.000000 VESTS').send();
559
+
560
+ // Remove delegation
561
+ await streamer.undelegateVestingShares('myaccount', 'recipient');
562
+ await streamer.ops.undelegate().delegator('myaccount').delegatee('recipient').send();
563
+ ```
564
+
565
+ ---
566
+
567
+ ## Account Operations
568
+
569
+ ### Claim Rewards
570
+ ```typescript
571
+ await streamer.claimRewards('myaccount', '1.000 HIVE', '0.500 HBD', '100.000000 VESTS');
572
+ await streamer.ops.claimRewards()
573
+ .account('myaccount')
574
+ .rewardHive('1.000 HIVE')
575
+ .rewardHbd('0.500 HBD')
576
+ .rewardVests('100.000000 VESTS')
577
+ .send();
578
+ ```
579
+
580
+ ### Witness Voting
581
+ ```typescript
582
+ await streamer.witnessVote('myaccount', 'goodwitness', true);
583
+ await streamer.ops.witnessVote().account('myaccount').witness('goodwitness').approve().send();
584
+
585
+ // Remove witness vote
586
+ await streamer.witnessVote('myaccount', 'badwitness', false);
587
+ await streamer.ops.witnessVote().account('myaccount').witness('badwitness').unapprove().send();
588
+ ```
589
+
590
+ ### Governance Proxy
591
+ ```typescript
592
+ await streamer.setProxy('myaccount', 'trustedvoter');
593
+ await streamer.ops.setProxy().account('myaccount').proxy('trustedvoter').send();
594
+
595
+ // Remove proxy
596
+ await streamer.clearProxy('myaccount');
597
+ await streamer.ops.clearProxy().account('myaccount').send();
598
+ ```
599
+
600
+ ### Update Profile
601
+ ```typescript
602
+ await streamer.updateProfile('myaccount', {
603
+ name: 'My Display Name',
604
+ about: 'Hive developer',
605
+ location: 'Decentralized',
606
+ website: 'https://example.com',
607
+ profile_image: 'https://example.com/avatar.png',
608
+ cover_image: 'https://example.com/cover.png'
609
+ });
610
+
611
+ // Or use the builder
612
+ await streamer.ops.updateProfile()
613
+ .account('myaccount')
614
+ .name('My Display Name')
615
+ .about('Hive developer')
616
+ .website('https://example.com')
617
+ .profileImage('https://example.com/avatar.png')
618
+ .set('custom_field', 'custom_value')
619
+ .send();
620
+ ```
621
+
622
+ ### Account Lookup
623
+ ```typescript
624
+ const account = await streamer.getAccount('alice');
625
+ const accounts = await streamer.getAccounts(['alice', 'bob', 'charlie']);
626
+ ```
627
+
628
+ ---
629
+
630
+ ## Event Subscriptions
631
+
632
+ In addition to the existing `onTransfer`, `onCustomJson`, `onComment`, `onPost`, and escrow subscriptions, you can now subscribe to:
633
+
634
+ ```typescript
635
+ // Watch all votes on the blockchain
636
+ streamer.onVote((data, blockNumber, blockId, prevBlockId, trxId, blockTime) => {
637
+ console.log(`${data.voter} voted on @${data.author}/${data.permlink} with weight ${data.weight}`);
638
+ });
639
+
640
+ // Watch delegations
641
+ streamer.onDelegate((data, blockNumber, blockId, prevBlockId, trxId, blockTime) => {
642
+ console.log(`${data.delegator} delegated ${data.vesting_shares} to ${data.delegatee}`);
643
+ });
644
+
645
+ // Watch power ups
646
+ streamer.onPowerUp((data, blockNumber, blockId, prevBlockId, trxId, blockTime) => {
647
+ console.log(`${data.from} powered up ${data.amount} to ${data.to}`);
648
+ });
649
+
650
+ // Watch power downs
651
+ streamer.onPowerDown((data, blockNumber, blockId, prevBlockId, trxId, blockTime) => {
652
+ console.log(`${data.account} started power down of ${data.vesting_shares}`);
653
+ });
654
+
655
+ // Watch reward claims
656
+ streamer.onClaimRewards((data, blockNumber, blockId, prevBlockId, trxId, blockTime) => {
657
+ console.log(`${data.account} claimed rewards`);
658
+ });
659
+
660
+ // Watch witness votes
661
+ streamer.onAccountWitnessVote((data, blockNumber, blockId, prevBlockId, trxId, blockTime) => {
662
+ console.log(`${data.account} ${data.approve ? 'voted for' : 'unvoted'} witness ${data.witness}`);
663
+ });
664
+ ```
665
+
666
+ ---
667
+
668
+ ## Blockchain Helpers
669
+
670
+ ### Reputation Score
671
+ ```typescript
672
+ import { Utils } from 'hive-stream';
673
+
674
+ // Convert raw blockchain reputation to human-readable score (25-75 range)
675
+ const score = Utils.calculateReputation('253948692668213'); // e.g. 73.64
676
+ ```
677
+
678
+ ### VESTS / HP Conversion
679
+ ```typescript
680
+ // Convert VESTS to Hive Power
681
+ const hp = Utils.vestToHP('1000000', totalVestingFundHive, totalVestingShares); // '500.000'
682
+
683
+ // Convert HP to VESTS
684
+ const vests = Utils.hpToVest('500', totalVestingFundHive, totalVestingShares); // '1000000.000000'
685
+
686
+ // Get formatted VESTS string for delegation/power down
687
+ const vestsStr = Utils.hpToVestString('500', totalVestingFundHive, totalVestingShares); // '1000000.000000 VESTS'
688
+ ```
689
+
690
+ ### Parse Profile Metadata
691
+ ```typescript
692
+ const profile = Utils.parseProfileMetadata(account.posting_json_metadata);
693
+ // { name: 'Alice', about: '...', location: '...', website: '...', profile_image: '...', cover_image: '...' }
694
+ ```
695
+
696
+ ---
697
+
698
+ ## Query Namespace
699
+
700
+ The `streamer.query` namespace provides read-only access to the entire Hive blockchain. No keys required.
701
+
702
+ ### Chain State
703
+ ```typescript
704
+ const props = await streamer.query.getDynamicGlobalProperties();
705
+ const chainProps = await streamer.query.getChainProperties();
706
+ const config = await streamer.query.getConfig();
707
+ const price = await streamer.query.getCurrentMedianHistoryPrice();
708
+ const rewardFund = await streamer.query.getRewardFund('post');
709
+ ```
710
+
711
+ ### Content & Discussions
712
+ ```typescript
713
+ const post = await streamer.query.getContent('author', 'permlink');
714
+ const replies = await streamer.query.getContentReplies('author', 'permlink');
715
+ const votes = await streamer.query.getActiveVotes('author', 'permlink');
716
+
717
+ const trending = await streamer.query.getTrending({ tag: 'hive', limit: 10 });
718
+ const hot = await streamer.query.getHot({ limit: 20 });
719
+ const newPosts = await streamer.query.getCreated({ tag: 'dev', limit: 10 });
720
+ const blog = await streamer.query.getBlog('alice');
721
+ const feed = await streamer.query.getFeed('alice');
722
+ ```
723
+
724
+ ### Social Graph
725
+ ```typescript
726
+ const followers = await streamer.query.getFollowers('alice', '', 'blog', 100);
727
+ const following = await streamer.query.getFollowing('alice', '', 'blog', 100);
728
+ const counts = await streamer.query.getFollowCount('alice');
729
+ ```
730
+
731
+ ### Delegations & Vesting
732
+ ```typescript
733
+ const delegations = await streamer.query.getVestingDelegations('alice');
734
+ ```
735
+
736
+ ### Account History
737
+ ```typescript
738
+ const history = await streamer.query.getAccountHistory('alice', -1, 100);
739
+ ```
740
+
741
+ ### Market & Orders
742
+ ```typescript
743
+ const orderBook = await streamer.query.getOrderBook(50);
744
+ const openOrders = await streamer.query.getOpenOrders('alice');
745
+ ```
746
+
747
+ ### Resource Credits & Voting Power
748
+ ```typescript
749
+ const rc = await streamer.query.getRCMana('alice');
750
+ const vp = await streamer.query.getVPMana('alice');
751
+ const rcAccounts = await streamer.query.findRCAccounts(['alice', 'bob']);
752
+ ```
753
+
754
+ ### Communities & Notifications
755
+ ```typescript
756
+ const community = await streamer.query.getCommunity('hive-12345');
757
+ const communities = await streamer.query.listCommunities({ limit: 50 });
758
+ const notifications = await streamer.query.getAccountNotifications('alice');
759
+ const subs = await streamer.query.listAllSubscriptions('alice');
760
+ ```
761
+
762
+ ### Witnesses
763
+ ```typescript
764
+ const witness = await streamer.query.getWitnessByAccount('someguy');
765
+ const topWitnesses = await streamer.query.getWitnessesByVote('', 100);
766
+ ```
767
+
768
+ ### Blocks & Transactions
769
+ ```typescript
770
+ const block = await streamer.query.getBlock(12345678);
771
+ const header = await streamer.query.getBlockHeader(12345678);
772
+ const ops = await streamer.query.getOperations(12345678);
773
+ const txStatus = await streamer.query.findTransaction('trx-id-here');
774
+ ```
775
+
776
+ ### Conversions & Savings
777
+ ```typescript
778
+ const conversions = await streamer.query.getConversionRequests('alice');
779
+ const collateralized = await streamer.query.getCollateralizedConversionRequests('alice');
780
+ const savingsWithdrawals = await streamer.query.getSavingsWithdrawFrom('alice');
781
+ ```
782
+
783
+ ### Proposals & Account Lookup
784
+ ```typescript
785
+ const proposals = await streamer.query.getProposals({ status: 'votable', limit: 50 });
786
+ const accounts = await streamer.query.lookupAccounts('ali', 10);
787
+ ```
788
+
789
+ ---
790
+
791
+ ## Savings Operations
792
+
793
+ ```typescript
794
+ // Transfer to savings
795
+ await streamer.transferToSavings('myaccount', 'myaccount', '100', 'HIVE');
796
+ await streamer.ops.transferToSavings().from('myaccount').hive(100).send();
797
+
798
+ // Transfer from savings (3-day delay)
799
+ await streamer.transferFromSavings('myaccount', 'myaccount', '50', 'HBD', 1);
800
+ await streamer.ops.transferFromSavings().from('myaccount').hbd(50).requestId(1).send();
801
+
802
+ // Cancel pending savings withdrawal
803
+ await streamer.cancelTransferFromSavings('myaccount', 1);
804
+ ```
805
+
806
+ ---
807
+
808
+ ## Convert Operations
809
+
810
+ ```typescript
811
+ // Convert HBD to HIVE (3.5-day delay)
812
+ await streamer.convert('myaccount', '10.000 HBD');
813
+ await streamer.ops.convert().from('myaccount').hbd(10).send();
814
+
815
+ // Collateralized convert HIVE to HBD (instant, with collateral)
816
+ await streamer.collateralizedConvert('myaccount', '10.000 HIVE');
817
+ await streamer.ops.collateralizedConvert().from('myaccount').hive(10).send();
818
+ ```
819
+
820
+ ---
821
+
822
+ ## Market Operations
823
+
824
+ ```typescript
825
+ // Create a limit order (sell HIVE for HBD)
826
+ await streamer.ops.limitOrder()
827
+ .owner('myaccount')
828
+ .amountToSell('10.000 HIVE')
829
+ .minToReceive('4.000 HBD')
830
+ .expiration(new Date(Date.now() + 7 * 24 * 60 * 60 * 1000))
831
+ .send();
832
+
833
+ // Cancel an order
834
+ await streamer.ops.cancelOrder().owner('myaccount').orderId(12345).send();
835
+ ```
836
+
837
+ ---
838
+
839
+ ## Content Operations
840
+
841
+ ```typescript
842
+ // Delete a post or comment
843
+ await streamer.deleteComment('myaccount', 'my-permlink');
844
+ await streamer.ops.deleteComment().author('myaccount').permlink('my-permlink').send();
845
+
846
+ // Set post options with beneficiaries
847
+ await streamer.ops.commentOptions()
848
+ .author('myaccount')
849
+ .permlink('my-post')
850
+ .maxAcceptedPayout('1000.000 HBD')
851
+ .percentHbd(5000)
852
+ .beneficiary('devfund', 500)
853
+ .beneficiary('curator', 1000)
854
+ .send();
855
+ ```
856
+
857
+ ---
858
+
859
+ ## Vesting Withdrawal Routes
860
+
861
+ ```typescript
862
+ // Route 50% of power-down to another account
863
+ await streamer.ops.withdrawRoute()
864
+ .from('myaccount')
865
+ .to('savings-account')
866
+ .percent(5000)
867
+ .autoVest(true) // Auto power-up at destination
868
+ .send();
869
+ ```
870
+
871
+ ---
872
+
873
+ ## Witness Operations
874
+
875
+ ```typescript
876
+ // Publish price feed (witnesses only)
877
+ await streamer.feedPublish('mywitness', '0.400 HBD', '1.000 HIVE');
878
+ ```
879
+
880
+ ---
881
+
882
+ ## Additional Event Subscriptions
883
+
884
+ ```typescript
885
+ // Watch follows/unfollows/mutes
886
+ streamer.onFollow((data, blockNumber, blockId, prevBlockId, trxId, blockTime) => {
887
+ console.log(`${data.follower} ${data.what.length ? 'followed' : 'unfollowed'} ${data.following}`);
888
+ });
889
+
890
+ // Watch reblogs
891
+ streamer.onReblog((data, blockNumber, blockId, prevBlockId, trxId, blockTime) => {
892
+ console.log(`${data.account} reblogged @${data.author}/${data.permlink}`);
893
+ });
894
+
895
+ // Watch account updates
896
+ streamer.onAccountUpdate((data, blockNumber, blockId, prevBlockId, trxId, blockTime) => {
897
+ console.log(`${data.account} updated their account`);
898
+ });
899
+
900
+ // Watch comment deletions
901
+ streamer.onDeleteComment((data, blockNumber, blockId, prevBlockId, trxId, blockTime) => {
902
+ console.log(`${data.author} deleted ${data.permlink}`);
903
+ });
904
+
905
+ // Watch market activity
906
+ streamer.onLimitOrder((data, blockNumber, blockId, prevBlockId, trxId, blockTime) => {
907
+ console.log('Market order activity:', data);
908
+ });
909
+
910
+ // Watch savings transfers
911
+ streamer.onSavingsTransfer((data, blockNumber, blockId, prevBlockId, trxId, blockTime) => {
912
+ console.log('Savings transfer:', data);
913
+ });
914
+
915
+ // Watch HBD/HIVE conversions
916
+ streamer.onConvert((data, blockNumber, blockId, prevBlockId, trxId, blockTime) => {
917
+ console.log('Conversion:', data);
918
+ });
919
+ ```
920
+
921
+ ---
922
+
923
+ ## Content Helpers
924
+
925
+ ```typescript
926
+ import { Utils } from 'hive-stream';
927
+
928
+ // Generate a permlink from a title
929
+ const permlink = Utils.generatePermlink('My Awesome Post!'); // 'my-awesome-post'
930
+
931
+ // Generate a reply permlink
932
+ const replyPermlink = Utils.generateReplyPermlink('parent-post'); // 're-parent-post-20260318...'
933
+
934
+ // Create post metadata JSON
935
+ const metadata = Utils.createPostMetadata({
936
+ tags: ['hive', 'development'],
937
+ image: ['https://example.com/hero.png'],
938
+ description: 'A great post about Hive development',
939
+ app: 'my-app/1.0'
940
+ });
941
+ ```
942
+
943
+ ---
944
+
945
+ ## Account Validation
946
+
947
+ ```typescript
948
+ // Validate account name format
949
+ const error = Utils.validateAccountName('alice'); // null (valid)
950
+ const error2 = Utils.validateAccountName('AB'); // 'Account name must be at least 3 characters'
951
+
952
+ // Quick boolean check
953
+ const valid = Utils.isValidAccountName('alice'); // true
954
+
955
+ // Check if account exists on chain
956
+ const exists = await Utils.accountExists(client, 'alice'); // true/false
957
+ ```
958
+
959
+ ---
960
+
961
+ ## URL & Link Parsing
962
+
963
+ ```typescript
964
+ // Parse Hive URLs
965
+ Utils.parseHiveUrl('@alice/my-post');
966
+ // { author: 'alice', permlink: 'my-post' }
967
+
968
+ Utils.parseHiveUrl('https://hive.blog/hive-12345/@alice/my-post');
969
+ // { author: 'alice', permlink: 'my-post', category: 'hive-12345' }
970
+ ```
971
+
972
+ ---
973
+
974
+ ## Voting Power & Vote Value
975
+
976
+ ```typescript
977
+ // Calculate current voting mana %
978
+ const mana = Utils.calculateVotingMana(account); // 98.5
979
+
980
+ // Get effective vesting shares (own + received - delegated)
981
+ const effectiveVests = Utils.getEffectiveVestingShares(account);
982
+
983
+ // Estimate vote value in USD
984
+ const voteValue = Utils.estimateVoteValue(
985
+ mana, // current voting mana %
986
+ 100, // vote weight %
987
+ effectiveVests, // effective vesting shares
988
+ rewardFund, // from query.getRewardFund()
989
+ medianPrice // from query.getCurrentMedianHistoryPrice()
990
+ );
991
+ ```
992
+
993
+ ---
994
+
995
+ ## Memo Encryption
996
+
997
+ ```typescript
998
+ // Encode a private memo
999
+ const encoded = Utils.encodeMemo(privateMemoKey, receiverPublicMemoKey, 'secret message');
1000
+
1001
+ // Decode a private memo
1002
+ const decoded = Utils.decodeMemo(privateMemoKey, encodedMemo);
1003
+ ```
1004
+
1005
+ ---
1006
+
1007
+ ## Post Builder
1008
+
1009
+ The post builder is the easiest way to create posts and replies on Hive. It atomically combines `comment` + `comment_options` in a single transaction, handling beneficiaries, metadata, communities, and more.
1010
+
1011
+ ```typescript
1012
+ // Create a post with beneficiaries
1013
+ await streamer.ops.post()
1014
+ .author('alice')
1015
+ .title('My First dApp Post')
1016
+ .body('Hello from hive-stream!')
1017
+ .tags('hive', 'dev', 'tutorial')
1018
+ .community('hive-169321')
1019
+ .beneficiary('devfund', 500) // 5%
1020
+ .beneficiary('alice-savings', 1000) // 10%
1021
+ .maxAcceptedPayout(100, 'HBD')
1022
+ .percentHbd(5000) // 50% HBD
1023
+ .app('my-app/1.0')
1024
+ .image('https://example.com/hero.png')
1025
+ .description('A tutorial on building dApps')
1026
+ .metadata('canonical_url', 'https://myapp.com/posts/1')
1027
+ .send();
1028
+
1029
+ // Reply to a post
1030
+ await streamer.ops.post()
1031
+ .author('bob')
1032
+ .parentAuthor('alice')
1033
+ .parentPermlink('my-first-dapp-post')
1034
+ .body('Great post! Very helpful.')
1035
+ .send();
1036
+
1037
+ // Post with zero payout (decline rewards)
1038
+ await streamer.ops.post()
1039
+ .author('alice')
1040
+ .title('PSA: Important Announcement')
1041
+ .body('This is a community announcement.')
1042
+ .tags('psa')
1043
+ .maxAcceptedPayout(0, 'HBD')
1044
+ .send();
1045
+ ```
1046
+
1047
+ ---
1048
+
1049
+ ## Batch Operations
1050
+
1051
+ Batch multiple operations into a single atomic transaction:
1052
+
1053
+ ```typescript
1054
+ // Vote + comment in one transaction
1055
+ await streamer.batch()
1056
+ .vote('alice', 'bob', 'great-post', 10000)
1057
+ .comment('alice', 're-great-post-reply', 'bob', 'great-post', '', 'Love this!', '{}')
1058
+ .send();
1059
+
1060
+ // Multiple transfers in one transaction
1061
+ await streamer.batch()
1062
+ .transfer('alice', 'bob', '1.000 HIVE', 'payment 1')
1063
+ .transfer('alice', 'carol', '2.000 HIVE', 'payment 2')
1064
+ .transfer('alice', 'dave', '3.000 HIVE', 'payment 3')
1065
+ .send();
1066
+
1067
+ // Mix different operation types
1068
+ await streamer.batch()
1069
+ .add(['transfer', { from: 'alice', to: 'bob', amount: '1.000 HIVE', memo: '' }])
1070
+ .customJson('myapp', { action: 'register' }, 'alice')
1071
+ .send();
1072
+ ```
1073
+
1074
+ ---
1075
+
1076
+ ## Authority Management
1077
+
1078
+ Manage posting authority for Hive apps:
1079
+
1080
+ ```typescript
1081
+ // Check if an app has posting authority
1082
+ const hasAuth = await streamer.hasPostingAuth('alice', 'myapp');
1083
+
1084
+ // Grant posting authority to an app
1085
+ await streamer.grantPostingAuth('alice', 'myapp');
1086
+
1087
+ // Revoke posting authority
1088
+ await streamer.revokePostingAuth('alice', 'myapp');
1089
+ ```
1090
+
1091
+ ---
1092
+
1093
+ ## Power Down Schedule
1094
+
1095
+ Calculate when power down payments arrive:
1096
+
1097
+ ```typescript
1098
+ const schedule = Utils.calculatePowerDownSchedule(account);
1099
+ // [
1100
+ // { week: 1, date: 2026-03-25T..., amount: '1000.000000', vestingShares: '1000.000000 VESTS' },
1101
+ // { week: 2, date: 2026-04-01T..., amount: '1000.000000', vestingShares: '1000.000000 VESTS' },
1102
+ // ...up to 13 weeks
1103
+ // ]
1104
+ ```
1105
+
1106
+ ---
1107
+
1108
+ ## HBD Savings Interest
1109
+
1110
+ ```typescript
1111
+ const interest = Utils.calculateHbdInterest(
1112
+ account.savings_hbd_balance, // '1000.000 HBD'
1113
+ account.savings_hbd_last_interest_payment,
1114
+ 15 // current APR %
1115
+ );
1116
+ // '12.328' (pending interest in HBD)
1117
+ ```
1118
+
1119
+ ---
1120
+
1121
+ ## Payout Helpers
1122
+
1123
+ ```typescript
1124
+ // Is the post still earning?
1125
+ Utils.isInPayoutWindow(post); // true/false
1126
+
1127
+ // How long until payout?
1128
+ Utils.timeUntilPayout(post); // milliseconds (0 if already paid)
1129
+
1130
+ // What's the payout value?
1131
+ Utils.getPendingPayout(post); // '10.500' (HBD)
1132
+ ```
1133
+
1134
+ ---
1135
+
1136
+ ## Account Value Calculator
1137
+
1138
+ ```typescript
1139
+ const value = Utils.calculateAccountValue(
1140
+ account,
1141
+ hivePrice, // e.g. 0.40 USD
1142
+ hbdPrice, // e.g. 1.00 USD
1143
+ totalVestingFundHive,
1144
+ totalVestingShares
1145
+ );
1146
+ // {
1147
+ // hive: 100, // liquid HIVE
1148
+ // hbd: 50, // liquid HBD
1149
+ // savings_hive: 200, // savings HIVE
1150
+ // savings_hbd: 100, // savings HBD
1151
+ // hp: 500, // Hive Power (HP)
1152
+ // total_hive: 800, // total in HIVE terms
1153
+ // total_usd: 470.0 // total in USD
1154
+ // }
1155
+ ```
1156
+
1157
+ ---
1158
+
1159
+ ## Content Extraction
1160
+
1161
+ ```typescript
1162
+ // Extract all images from a post body (for thumbnails, galleries)
1163
+ const images = Utils.extractImagesFromBody(post.body);
1164
+ // ['https://example.com/img1.png', 'https://example.com/img2.jpg']
1165
+
1166
+ // Extract all hyperlinks (excluding images)
1167
+ const links = Utils.extractLinksFromBody(post.body);
1168
+
1169
+ // Generate a plain-text summary (for previews, SEO)
1170
+ const summary = Utils.generatePostSummary(post.body, 200);
1171
+ // 'Hello world. This is a great post about...'
1172
+ ```
1173
+
1174
+ ---
1175
+
1176
+ ## Hivesigner URLs
1177
+
1178
+ ```typescript
1179
+ // Generate signing URLs for any operation
1180
+ const transferUrl = Utils.getTransferUrl('bob', 'thanks', '1.000 HIVE', 'https://myapp.com');
1181
+ const voteUrl = Utils.getVoteUrl('alice', 'bob', 'great-post', 10000);
1182
+ const delegateUrl = Utils.getDelegateUrl('alice', 'bob', '1000.000000 VESTS');
1183
+ const followUrl = Utils.getFollowUrl('alice', 'bob');
1184
+
1185
+ // Generic signing URL for any operation type
1186
+ const url = Utils.getHivesignerSignUrl('transfer', {
1187
+ from: 'alice', to: 'bob', amount: '1.000 HIVE'
1188
+ }, 'https://myapp.com/callback');
1189
+ ```
1190
+
1191
+ ---
1192
+
1193
+ ## Transfer Memo Helpers
1194
+
1195
+ ```typescript
1196
+ // Check if a memo is encrypted
1197
+ Utils.isEncryptedMemo('#encrypted-content'); // true
1198
+ Utils.isEncryptedMemo('regular memo'); // false
1199
+
1200
+ // Create structured JSON memos (for exchanges, apps)
1201
+ const memo = Utils.createJsonMemo({ action: 'deposit', orderId: 12345 });
1202
+
1203
+ // Parse JSON memos from transfers
1204
+ const data = Utils.parseJsonMemo('{"action":"deposit","orderId":12345}');
1205
+ // { action: 'deposit', orderId: 12345 }
1206
+ Utils.parseJsonMemo('regular memo'); // null
1207
+ ```
1208
+
1209
+ ---
1210
+
438
1211
  ## Utilities
439
- The library includes helpers for JSON parsing, randomness, and transfer verification. See `src/utils.ts` for details.
1212
+ The library includes additional helpers for JSON parsing, randomness, seeded RNG, and transfer verification. See `src/utils.ts` for the complete API.
440
1213
 
441
1214
  ---
442
1215