@pbgtoken/rwa-contract 1.0.2 → 1.0.3

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.
package/dist/index.js CHANGED
@@ -171,7 +171,7 @@ import { makeCast, makeUserFunc } from "@helios-lang/contract-utils";
171
171
  var tokenized_account = {
172
172
  $name: "tokenized_account",
173
173
  $purpose: "mixed",
174
- $currentScriptIndex: 0,
174
+ $currentScriptIndex: 1,
175
175
  $sourceCode: `mixed tokenized_account
176
176
 
177
177
  import { get_current_input, tx } from ScriptContext
@@ -421,7 +421,230 @@ func main(args: MixedArgs) -> () {
421
421
  "main": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "main", "arguments": [{ "name": "$datum", "isOptional": true, "type": { "kind": "internal", "name": "Data" } }, { "name": "args", "isOptional": false, "type": { "kind": "enum", "id": "__helios__mixedargs", "name": "MixedArgs", "variantTypes": [{ "kind": "variant", "name": "Other", "id": "__helios__mixedargs__other", "tag": 0, "fieldTypes": [{ "name": "redeemer", "type": { "kind": "internal", "name": "Data" } }] }, { "kind": "variant", "name": "Spending", "id": "__helios__mixedargs__spending", "tag": 1, "fieldTypes": [{ "name": "redeemer", "type": { "kind": "internal", "name": "Data" } }] }] } }], "requiresCurrentScript": false, "requiresScriptContext": true }, castConfig: config, validatorIndices: __validatorIndices })
422
422
  }
423
423
  };
424
- var __validatorIndices = { "tokenized_account": 0 };
424
+ var one_to_one_asset = {
425
+ $name: "one_to_one_asset",
426
+ $purpose: "mixed",
427
+ $currentScriptIndex: 0,
428
+ $sourceCode: `mixed one_to_one_asset
429
+
430
+ import { get_current_input, tx } from ScriptContext
431
+
432
+ struct State {
433
+ supply: Int "supply"
434
+
435
+ type: String "type" // eg. Bitcoin or ERC20
436
+ account: ByteArray "account" // if type==Private -> this is a hash of the actual account type + address
437
+
438
+ name: String "name"
439
+ description: String "description"
440
+ decimals: Int "decimals" // can't change
441
+ ticker: String "ticker" // can't change
442
+ url: String "url"
443
+ logo: String "logo"
444
+ }
445
+
446
+ enum Cip68Extra {
447
+ Unused
448
+ }
449
+
450
+ enum Metadata {
451
+ Cip68 {
452
+ state: State
453
+ version: Int
454
+ extra: Cip68Extra
455
+ }
456
+
457
+ func state(self) -> State {
458
+ self.switch{
459
+ x: Cip68 => x.state
460
+ }
461
+ }
462
+ }
463
+
464
+ struct Redeemer {
465
+ reserves: Int
466
+ }
467
+
468
+ const SEED_ID = TxOutputId::new(TxId::new(#), 0)
469
+ const ORACLE_KEYS = []PubKeyHash{}
470
+ const INITIAL_PRICE: Real = 0.001 // 1000 USD per token -> 0.001 USD per microtoken
471
+ const TYPE = "Bitcoin"
472
+ const ACCOUNT = #
473
+ const TICKER = "BTC"
474
+ const NAME = "wrapped " + TICKER
475
+ const DESCRIPTION = "wrapped" + TICKER + " operated by PBG"
476
+ const DECIMALS = 6
477
+ const URL = "https://www.pbg.io"
478
+ const LOGO = "https://assets.pbg.io/usdt_bridge.png"
479
+
480
+ const ticker_bytes = TICKER.encode_utf8()
481
+ const user_token_name = Cip67::fungible_token_label + ticker_bytes
482
+ const ref_token_name = Cip67::reference_token_label + ticker_bytes
483
+
484
+ const own_hash = Scripts::tokenized_account
485
+ const own_mph = MintingPolicyHash::from_script_hash(own_hash)
486
+ const own_address = Address::new(
487
+ SpendingCredential::new_validator(
488
+ ValidatorHash::from_script_hash(own_hash)
489
+ ),
490
+ Option[StakingCredential]::None
491
+ )
492
+
493
+ const ref_token_asset_class = AssetClass::new(own_mph, ref_token_name)
494
+ const user_token_asset_class = AssetClass::new(own_mph, user_token_name)
495
+
496
+ func validate_initialization() -> () {
497
+ assert(tx.inputs.any((input: TxInput) -> {
498
+ input.output_id == SEED_ID
499
+ }), "seed UTXO not spent")
500
+
501
+ ref_utxo = tx.outputs.find((output: TxOutput) -> {
502
+ output.address == own_address
503
+ && output.value.get_safe(ref_token_asset_class) == 1
504
+ })
505
+
506
+ metadata = ref_utxo.datum.inline.as_strictly[Metadata]
507
+
508
+ assert(metadata == Metadata::Cip68{
509
+ State{
510
+ supply: 0,
511
+ type: TYPE,
512
+ account: ACCOUNT,
513
+
514
+ name: NAME,
515
+ description: DESCRIPTION,
516
+ decimals: DECIMALS,
517
+ ticker: TICKER,
518
+ url: URL,
519
+ logo: LOGO
520
+ },
521
+ 1,
522
+ Cip68Extra::Unused
523
+ }, "metadata not initialized correctly")
524
+ }
525
+
526
+ func signed_by_quorum() -> Bool {
527
+ n_signers = ORACLE_KEYS.fold((n_signers: Int, key: PubKeyHash) -> {
528
+ n_signers + tx.is_signed_by(key).to_int()
529
+ }, 0)
530
+
531
+ n_signers > (ORACLE_KEYS.length/2)
532
+ }
533
+
534
+ func validate_state_change(redeemer: Redeemer, input: TxInput) -> () {
535
+ state0 = input.datum.inline.as[Metadata].state()
536
+
537
+ output = tx.outputs.find((output: TxOutput) -> {
538
+ output.address == own_address
539
+ && output.value.get_safe(ref_token_asset_class) > 0
540
+ })
541
+
542
+ state1 = output.datum.inline.as_strictly[Metadata].state()
543
+
544
+ // ensure constant metadata fields don't change
545
+ assert(state1.type == state0.type, "type not constant")
546
+ assert(state1.account == state0.account, "account not constant")
547
+ assert(state1.ticker == state0.ticker, "metadata ticker not constant")
548
+ assert(state1.decimals == state0.decimals, "metadata decimals not constant")
549
+
550
+ n = tx.minted.get_safe(user_token_asset_class)
551
+ N0 = state0.supply
552
+ N1 = state1.supply
553
+
554
+ assert((N1 - N0) == n, "current token supply not updated correctly")
555
+
556
+ if (n > 0) {
557
+ Redeemer{R} = redeemer
558
+
559
+ assert(N1 <= R, "too many tokens minted")
560
+ assert(signed_by_quorum(), "not signed by simple quorum of oracles")
561
+ }
562
+ }
563
+
564
+ func main(args: MixedArgs) -> () {
565
+ args.switch{
566
+ s: Spending => {
567
+ redeemer = s.redeemer.as[Redeemer]
568
+
569
+ utxo = get_current_input()
570
+
571
+ tokens = utxo.value.get_policy(own_mph)
572
+
573
+ if (tokens.is_empty()) {
574
+ // UTXOs that don't contain any tokens from current policy can always be spent.
575
+ // This can be used to remove garbage.
576
+ ()
577
+ } else {
578
+ (name, _) = tokens.head
579
+
580
+ if (name == ref_token_name) {
581
+ validate_state_change(redeemer, utxo)
582
+ } else {
583
+ error("unexpected token name")
584
+ }
585
+ }
586
+ },
587
+ Other => {
588
+ tokens = tx.minted.get_policy(own_mph)
589
+
590
+ (name, qty) = tokens.head
591
+ tail = tokens.tail
592
+
593
+ assert(tail.is_empty(), "only one token kind can be minted or burned")
594
+
595
+ if (name == user_token_name && qty != 0) {
596
+ // metadata token must be spent, which triggers Spending witness
597
+ assert(tx.inputs.any((input: TxInput) -> {
598
+ input.address == own_address
599
+ && input.value.get_safe(ref_token_asset_class) > 0
600
+ }), "ref token not spent")
601
+ } else if (qty == 1 && (name == ref_token_name)) {
602
+ validate_initialization()
603
+ } else {
604
+ error("invalid minted tokens")
605
+ }
606
+ }
607
+ }
608
+ }`,
609
+ $dependencies: [],
610
+ $hashDependencies: [tokenized_account],
611
+ $dependsOnOwnHash: false,
612
+ $Redeemer: (config) => makeCast({ "kind": "enum", "id": "__helios__mixedargs", "name": "MixedArgs", "variantTypes": [{ "kind": "variant", "name": "Other", "id": "__helios__mixedargs__other", "tag": 0, "fieldTypes": [{ "name": "redeemer", "type": { "kind": "internal", "name": "Data" } }] }, { "kind": "variant", "name": "Spending", "id": "__helios__mixedargs__spending", "tag": 1, "fieldTypes": [{ "name": "redeemer", "type": { "kind": "internal", "name": "Data" } }] }] }, config),
613
+ $Datum: (config) => makeCast({ "kind": "internal", "name": "Data" }, config),
614
+ $types: {
615
+ State: (config) => makeCast({ "kind": "struct", "format": "map", "id": "__module__one_to_one_asset__State[]", "name": "State", "fieldTypes": [{ "name": "supply", "type": { "kind": "internal", "name": "Int" }, "key": "supply" }, { "name": "type", "type": { "kind": "internal", "name": "String" }, "key": "type" }, { "name": "account", "type": { "kind": "internal", "name": "ByteArray" }, "key": "account" }, { "name": "name", "type": { "kind": "internal", "name": "String" }, "key": "name" }, { "name": "description", "type": { "kind": "internal", "name": "String" }, "key": "description" }, { "name": "decimals", "type": { "kind": "internal", "name": "Int" }, "key": "decimals" }, { "name": "ticker", "type": { "kind": "internal", "name": "String" }, "key": "ticker" }, { "name": "url", "type": { "kind": "internal", "name": "String" }, "key": "url" }, { "name": "logo", "type": { "kind": "internal", "name": "String" }, "key": "logo" }] }, config),
616
+ Cip68Extra: (config) => makeCast({ "kind": "enum", "name": "Cip68Extra", "id": "__module__one_to_one_asset__Cip68Extra[]", "variantTypes": [{ "kind": "variant", "tag": 0, "id": "__module__one_to_one_asset__Cip68Extra[]__Unused", "name": "Unused", "fieldTypes": [] }] }, config),
617
+ Metadata: (config) => makeCast({ "kind": "enum", "name": "Metadata", "id": "__module__one_to_one_asset__Metadata[]", "variantTypes": [{ "kind": "variant", "tag": 0, "id": "__module__one_to_one_asset__Metadata[]__Cip68", "name": "Cip68", "fieldTypes": [{ "name": "state", "type": { "kind": "struct", "format": "map", "id": "__module__one_to_one_asset__State[]", "name": "State", "fieldTypes": [{ "name": "supply", "type": { "kind": "internal", "name": "Int" }, "key": "supply" }, { "name": "type", "type": { "kind": "internal", "name": "String" }, "key": "type" }, { "name": "account", "type": { "kind": "internal", "name": "ByteArray" }, "key": "account" }, { "name": "name", "type": { "kind": "internal", "name": "String" }, "key": "name" }, { "name": "description", "type": { "kind": "internal", "name": "String" }, "key": "description" }, { "name": "decimals", "type": { "kind": "internal", "name": "Int" }, "key": "decimals" }, { "name": "ticker", "type": { "kind": "internal", "name": "String" }, "key": "ticker" }, { "name": "url", "type": { "kind": "internal", "name": "String" }, "key": "url" }, { "name": "logo", "type": { "kind": "internal", "name": "String" }, "key": "logo" }] } }, { "name": "version", "type": { "kind": "internal", "name": "Int" } }, { "name": "extra", "type": { "kind": "enum", "name": "Cip68Extra", "id": "__module__one_to_one_asset__Cip68Extra[]", "variantTypes": [{ "kind": "variant", "tag": 0, "id": "__module__one_to_one_asset__Cip68Extra[]__Unused", "name": "Unused", "fieldTypes": [] }] } }] }] }, config),
618
+ Redeemer: (config) => makeCast({ "kind": "struct", "format": "singleton", "id": "__module__one_to_one_asset__Redeemer[]", "name": "Redeemer", "fieldTypes": [{ "name": "reserves", "type": { "kind": "internal", "name": "Int" } }] }, config)
619
+ },
620
+ $functions: {
621
+ "Metadata::state": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "Metadata::state", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [{ "name": "self", "isOptional": false, "type": { "kind": "enum", "name": "Metadata", "id": "__module__one_to_one_asset__Metadata[]", "variantTypes": [{ "kind": "variant", "tag": 0, "id": "__module__one_to_one_asset__Metadata[]__Cip68", "name": "Cip68", "fieldTypes": [{ "name": "state", "type": { "kind": "struct", "format": "map", "id": "__module__one_to_one_asset__State[]", "name": "State", "fieldTypes": [{ "name": "supply", "type": { "kind": "internal", "name": "Int" }, "key": "supply" }, { "name": "type", "type": { "kind": "internal", "name": "String" }, "key": "type" }, { "name": "account", "type": { "kind": "internal", "name": "ByteArray" }, "key": "account" }, { "name": "name", "type": { "kind": "internal", "name": "String" }, "key": "name" }, { "name": "description", "type": { "kind": "internal", "name": "String" }, "key": "description" }, { "name": "decimals", "type": { "kind": "internal", "name": "Int" }, "key": "decimals" }, { "name": "ticker", "type": { "kind": "internal", "name": "String" }, "key": "ticker" }, { "name": "url", "type": { "kind": "internal", "name": "String" }, "key": "url" }, { "name": "logo", "type": { "kind": "internal", "name": "String" }, "key": "logo" }] } }, { "name": "version", "type": { "kind": "internal", "name": "Int" } }, { "name": "extra", "type": { "kind": "enum", "name": "Cip68Extra", "id": "__module__one_to_one_asset__Cip68Extra[]", "variantTypes": [{ "kind": "variant", "tag": 0, "id": "__module__one_to_one_asset__Cip68Extra[]__Unused", "name": "Unused", "fieldTypes": [] }] } }] }] } }], "returns": { "kind": "struct", "format": "map", "id": "__module__one_to_one_asset__State[]", "name": "State", "fieldTypes": [{ "name": "supply", "type": { "kind": "internal", "name": "Int" }, "key": "supply" }, { "name": "type", "type": { "kind": "internal", "name": "String" }, "key": "type" }, { "name": "account", "type": { "kind": "internal", "name": "ByteArray" }, "key": "account" }, { "name": "name", "type": { "kind": "internal", "name": "String" }, "key": "name" }, { "name": "description", "type": { "kind": "internal", "name": "String" }, "key": "description" }, { "name": "decimals", "type": { "kind": "internal", "name": "Int" }, "key": "decimals" }, { "name": "ticker", "type": { "kind": "internal", "name": "String" }, "key": "ticker" }, { "name": "url", "type": { "kind": "internal", "name": "String" }, "key": "url" }, { "name": "logo", "type": { "kind": "internal", "name": "String" }, "key": "logo" }] } }, castConfig: config, validatorIndices: __validatorIndices }),
622
+ "SEED_ID": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "SEED_ID", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "TxOutputId" } }, castConfig: config, validatorIndices: __validatorIndices }),
623
+ "ORACLE_KEYS": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "ORACLE_KEYS", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "list", "itemType": { "kind": "internal", "name": "PubKeyHash" } } }, castConfig: config, validatorIndices: __validatorIndices }),
624
+ "INITIAL_PRICE": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "INITIAL_PRICE", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "Real" } }, castConfig: config, validatorIndices: __validatorIndices }),
625
+ "TYPE": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "TYPE", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "String" } }, castConfig: config, validatorIndices: __validatorIndices }),
626
+ "ACCOUNT": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "ACCOUNT", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "ByteArray" } }, castConfig: config, validatorIndices: __validatorIndices }),
627
+ "TICKER": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "TICKER", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "String" } }, castConfig: config, validatorIndices: __validatorIndices }),
628
+ "NAME": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "NAME", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "String" } }, castConfig: config, validatorIndices: __validatorIndices }),
629
+ "DESCRIPTION": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "DESCRIPTION", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "String" } }, castConfig: config, validatorIndices: __validatorIndices }),
630
+ "DECIMALS": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "DECIMALS", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "Int" } }, castConfig: config, validatorIndices: __validatorIndices }),
631
+ "URL": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "URL", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "String" } }, castConfig: config, validatorIndices: __validatorIndices }),
632
+ "LOGO": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "LOGO", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "String" } }, castConfig: config, validatorIndices: __validatorIndices }),
633
+ "ticker_bytes": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "ticker_bytes", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "ByteArray" } }, castConfig: config, validatorIndices: __validatorIndices }),
634
+ "user_token_name": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "user_token_name", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "ByteArray" } }, castConfig: config, validatorIndices: __validatorIndices }),
635
+ "ref_token_name": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "ref_token_name", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "ByteArray" } }, castConfig: config, validatorIndices: __validatorIndices }),
636
+ "own_hash": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "own_hash", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "ScriptHash" } }, castConfig: config, validatorIndices: __validatorIndices }),
637
+ "own_mph": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "own_mph", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "MintingPolicyHash" } }, castConfig: config, validatorIndices: __validatorIndices }),
638
+ "own_address": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "own_address", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "Address" } }, castConfig: config, validatorIndices: __validatorIndices }),
639
+ "ref_token_asset_class": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "ref_token_asset_class", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "AssetClass" } }, castConfig: config, validatorIndices: __validatorIndices }),
640
+ "user_token_asset_class": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "user_token_asset_class", "requiresCurrentScript": false, "requiresScriptContext": false, "arguments": [], "returns": { "kind": "internal", "name": "AssetClass" } }, castConfig: config, validatorIndices: __validatorIndices }),
641
+ "validate_initialization": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "validate_initialization", "requiresCurrentScript": false, "requiresScriptContext": true, "arguments": [] }, castConfig: config, validatorIndices: __validatorIndices }),
642
+ "signed_by_quorum": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "signed_by_quorum", "requiresCurrentScript": false, "requiresScriptContext": true, "arguments": [], "returns": { "kind": "internal", "name": "Bool" } }, castConfig: config, validatorIndices: __validatorIndices }),
643
+ "validate_state_change": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "validate_state_change", "requiresCurrentScript": false, "requiresScriptContext": true, "arguments": [{ "name": "redeemer", "isOptional": false, "type": { "kind": "struct", "format": "singleton", "id": "__module__one_to_one_asset__Redeemer[]", "name": "Redeemer", "fieldTypes": [{ "name": "reserves", "type": { "kind": "internal", "name": "Int" } }] } }, { "name": "input", "isOptional": false, "type": { "kind": "internal", "name": "TxInput" } }] }, castConfig: config, validatorIndices: __validatorIndices }),
644
+ "main": (uplc, config) => makeUserFunc(uplc, { ...{ "name": "main", "arguments": [{ "name": "$datum", "isOptional": true, "type": { "kind": "internal", "name": "Data" } }, { "name": "args", "isOptional": false, "type": { "kind": "enum", "id": "__helios__mixedargs", "name": "MixedArgs", "variantTypes": [{ "kind": "variant", "name": "Other", "id": "__helios__mixedargs__other", "tag": 0, "fieldTypes": [{ "name": "redeemer", "type": { "kind": "internal", "name": "Data" } }] }, { "kind": "variant", "name": "Spending", "id": "__helios__mixedargs__spending", "tag": 1, "fieldTypes": [{ "name": "redeemer", "type": { "kind": "internal", "name": "Data" } }] }] } }], "requiresCurrentScript": false, "requiresScriptContext": true }, castConfig: config, validatorIndices: __validatorIndices })
645
+ }
646
+ };
647
+ var __validatorIndices = { "one_to_one_asset": 0, "tokenized_account": 1 };
425
648
 
426
649
  // src/RWADatumProvider.ts
427
650
  var castMetadata = tokenized_account.$types.Metadata;
@@ -466,9 +689,648 @@ function filterTransfersAfter(transfers, after) {
466
689
  return transfers;
467
690
  }
468
691
  }
692
+
693
+ // src/CoinGeckoProvider.ts
694
+ var CoinGeckoProvider = class {
695
+ /**
696
+ * Get spot price for any token by CoinGecko asset ID.
697
+ *
698
+ * @param asset CoinGecko asset ID (e.g., "ethereum").
699
+ * @param currency Fiat or crypto currency symbol for price (default: "usd").
700
+ * @returns Spot price as a number, or 0 if not found.
701
+ */
702
+ async getSpotPrice(asset, currency = "usd") {
703
+ const url = "https://api.coingecko.com/api/v3/simple/price";
704
+ const params = new URLSearchParams({
705
+ ids: asset,
706
+ vs_currencies: currency.toLowerCase()
707
+ });
708
+ const response = await fetch(`${url}?${params.toString()}`);
709
+ const data = await response.json();
710
+ return data[asset]?.[currency.toLowerCase()] ?? 0;
711
+ }
712
+ /**
713
+ * Get spot prices for multiple tokens by their CoinGecko asset symbols.
714
+ *
715
+ * @param symbols Array of CoinGecko asset symbols (e.g., ["btc", "eth"]).
716
+ * @param currency Fiat or crypto currency symbol for price (default: "usd").
717
+ * @returns An object mapping each asset ID to its price data, or empty object if not found.
718
+ */
719
+ async getSpotPriceBySymbols(symbols, currency = "usd") {
720
+ const url = "https://api.coingecko.com/api/v3/simple/price";
721
+ const params = new URLSearchParams({
722
+ symbols: symbols.join(",").toLowerCase(),
723
+ vs_currencies: currency.toLowerCase()
724
+ });
725
+ const response = await fetch(`${url}?${params.toString()}`);
726
+ return await response.json();
727
+ }
728
+ /**
729
+ * Get spot price for a token by contract address and chain.
730
+ *
731
+ * @param contract The token contract address.
732
+ * @param chain The blockchain network ("ethereum", "binance-smart-chain", etc).
733
+ * @param currency Fiat or crypto currency symbol for price (default: "usd").
734
+ * @returns Spot price as a number, or 0 if not found.
735
+ */
736
+ async getSpotPriceByTokenAddress(contract, chain = "ethereum", currency = "usd") {
737
+ const url = `https://api.coingecko.com/api/v3/simple/token_price/${chain}`;
738
+ const params = new URLSearchParams({
739
+ contract_addresses: contract,
740
+ vs_currencies: currency.toLowerCase()
741
+ });
742
+ const response = await fetch(`${url}?${params.toString()}`);
743
+ const data = await response.json();
744
+ return data[contract]?.[currency.toLowerCase()] ?? 0;
745
+ }
746
+ };
747
+ function makeCoinGeckoProvider() {
748
+ return new CoinGeckoProvider();
749
+ }
750
+
751
+ // src/BitcoinWalletProvider.ts
752
+ var BitcoinWalletProviderImpl = class {
753
+ /**
754
+ * @param walletAddress The Bitcoin wallet address to query.
755
+ * @param priceProvider CoinGecko Price Provider
756
+ */
757
+ constructor(walletAddress, priceProvider) {
758
+ this.walletAddress = walletAddress;
759
+ this.priceProvider = priceProvider;
760
+ }
761
+ /**
762
+ * Calculates the net USD value of Bitcoin transactions for the given transfer IDs.
763
+ *
764
+ * - Adds value from outputs (vout) sent **to** the wallet.
765
+ * - Subtracts value from inputs (vin.prevout) **from** the wallet.
766
+ *
767
+ * Converts net BTC to USD using the current spot price.
768
+ *
769
+ * @param transfers Array of Bitcoin transaction hashes (TransferID[])
770
+ * @returns Net deposit amount in USD.
771
+ */
772
+ async deposits(transfers) {
773
+ const url = `https://blockstream.info/api/address/${this.walletAddress}/txs`;
774
+ const res = await fetch(url);
775
+ const txs = await res.json();
776
+ const depositTxIDs = new Set(transfers);
777
+ let totalDepositedBTC = 0;
778
+ for (const tx of txs) {
779
+ if (!depositTxIDs.has(tx.txid)) continue;
780
+ for (const vout of tx.vout) {
781
+ if (vout.scriptpubkey_address === this.walletAddress && typeof vout.value === "number") {
782
+ totalDepositedBTC += vout.value / 1e8;
783
+ }
784
+ }
785
+ for (const vin of tx.vin) {
786
+ if (vin.prevout?.scriptpubkey_address === this.walletAddress && typeof vin.prevout.value === "number") {
787
+ totalDepositedBTC -= vin.prevout.value / 1e8;
788
+ }
789
+ }
790
+ }
791
+ const price = await this.priceProvider.getSpotPrice("bitcoin", "usd");
792
+ return totalDepositedBTC * price;
793
+ }
794
+ /**
795
+ * Returns the balance of the wallet in BTC.
796
+ */
797
+ get balance() {
798
+ return this.getBalance();
799
+ }
800
+ /**
801
+ * Returns the USD value of the wallet's balance for native BTC.
802
+ */
803
+ get usdBalance() {
804
+ return this.getUSDValue();
805
+ }
806
+ /**
807
+ * Returns the transfer history for the wallet.
808
+ */
809
+ get transferHistory() {
810
+ return this.getTransferHistory();
811
+ }
812
+ /**
813
+ * Fetches the BTC balance for the wallet using Blockstream public API.
814
+ */
815
+ async getBalance() {
816
+ return await this.getSats() / 1e8;
817
+ }
818
+ async getSats() {
819
+ const url = `https://blockstream.info/api/address/${this.walletAddress}`;
820
+ const res = await fetch(url);
821
+ const data = await res.json();
822
+ const sats = data.chain_stats.funded_txo_sum - data.chain_stats.spent_txo_sum;
823
+ return sats;
824
+ }
825
+ /**
826
+ * Fetches the USD value of the wallet's BTC balance using CoinGecko.
827
+ */
828
+ async getUSDValue() {
829
+ const balance = await this.getBalance();
830
+ const price = await this.priceProvider.getSpotPrice("bitcoin", "usd");
831
+ return balance * price;
832
+ }
833
+ /**
834
+ * Fetches the transfer history for the wallet using Blockstream public API.
835
+ * Returns an array of transaction IDs.
836
+ */
837
+ async getTransferHistory() {
838
+ const url = `https://blockstream.info/api/address/${this.walletAddress}/txs`;
839
+ const res = await fetch(url);
840
+ if (!res.ok) return [];
841
+ const txs = await res.json();
842
+ return txs.map((tx) => tx.txid).reverse();
843
+ }
844
+ };
845
+ function makeBitcoinWalletProvider(walletAddress, priceProvider) {
846
+ return new BitcoinWalletProviderImpl(walletAddress, priceProvider);
847
+ }
848
+
849
+ // src/EthereumERC20AccountProvider.ts
850
+ import { ethers } from "ethers";
851
+ var EthereumERC20AccountProvider = class {
852
+ /**
853
+ * @param walletAddress The Ethereum wallet address to query.
854
+ * @param priceProvider CoinGecko Price Provider
855
+ * @param alchemyApiKey The Alchemy API key.
856
+ * @param tokenContractAddress The ERC-20 token contract address, or null for native ETH.
857
+ * @param rpcUrl The Ethereum RPC endpoint to use.
858
+ */
859
+ constructor(walletAddress, priceProvider, alchemyApiKey, tokenContractAddress = null, rpcUrl = "https://eth.rpc.blxrbdn.com") {
860
+ this.walletAddress = walletAddress;
861
+ this.priceProvider = priceProvider;
862
+ this.alchemyApiKey = alchemyApiKey;
863
+ this.tokenContractAddress = tokenContractAddress;
864
+ this.rpcUrl = rpcUrl;
865
+ }
866
+ /**
867
+ * Returns the balance of the wallet for the specified token or native ETH.
868
+ */
869
+ get balance() {
870
+ return this.getBalance();
871
+ }
872
+ /**
873
+ * Returns the USD value of the wallet's balance for the specified token or native ETH.
874
+ */
875
+ get usdBalance() {
876
+ return this.getUSDValue();
877
+ }
878
+ /**
879
+ * Returns the transfer history for the wallet.
880
+ */
881
+ get transferHistory() {
882
+ return this.getTransferHistory();
883
+ }
884
+ /**
885
+ * Calculates the net USD value of the specified Ethereum transactions.
886
+ *
887
+ * - For native ETH: uses transaction value directly.
888
+ * - For ERC-20 tokens: extracts transfer amounts from logs (tx.value is always 0).
889
+ * - Adds amounts received by the wallet, subtracts amounts sent.
890
+ *
891
+ * Converts the net token or ETH amount to USD using the current spot price.
892
+ *
893
+ * @param transfers Array of Ethereum transaction hashes (TransferID[])
894
+ * @returns Net deposit amount in USD.
895
+ */
896
+ async deposits(_transfers) {
897
+ const provider = new ethers.JsonRpcProvider(this.rpcUrl);
898
+ const lowerWalletAddress = this.walletAddress.toLowerCase();
899
+ let totalAmount = 0;
900
+ const price = this.tokenContractAddress ? await this.priceProvider.getSpotPriceByTokenAddress(
901
+ this.tokenContractAddress,
902
+ "ethereum",
903
+ "usd"
904
+ ) : await this.priceProvider.getSpotPrice("ethereum", "usd");
905
+ if (this.tokenContractAddress) {
906
+ const erc20Abi = [
907
+ "function decimals() view returns (uint8)",
908
+ "event Transfer(address indexed from, address indexed to, uint amount)"
909
+ ];
910
+ const contract = new ethers.Contract(this.tokenContractAddress, erc20Abi, provider);
911
+ const decimals = await contract.decimals();
912
+ for (const txHash of _transfers) {
913
+ const receipt = await provider.getTransactionReceipt(txHash);
914
+ if (!receipt) continue;
915
+ for (const log of receipt.logs) {
916
+ try {
917
+ const parsed = contract.interface.parseLog(log);
918
+ if (!parsed || parsed.name !== "Transfer") continue;
919
+ const [from, to, amountWei] = parsed.args;
920
+ const amount = Number(ethers.formatUnits(amountWei, decimals));
921
+ if (to === lowerWalletAddress) {
922
+ totalAmount += amount;
923
+ }
924
+ if (from === lowerWalletAddress) {
925
+ totalAmount -= amount;
926
+ }
927
+ } catch {
928
+ }
929
+ }
930
+ }
931
+ } else {
932
+ for (const txHash of _transfers) {
933
+ const tx = await provider.getTransaction(txHash);
934
+ if (!tx) continue;
935
+ const from = tx.from.toLowerCase();
936
+ const to = tx.to?.toLowerCase();
937
+ const amount = Number(ethers.formatEther(tx.value));
938
+ if (to === lowerWalletAddress) {
939
+ totalAmount += amount;
940
+ }
941
+ if (from === lowerWalletAddress) {
942
+ totalAmount -= amount;
943
+ }
944
+ }
945
+ }
946
+ return totalAmount * price;
947
+ }
948
+ /**
949
+ * Fetches the balance for the wallet.
950
+ * If tokenContractAddress is null, returns native ETH balance.
951
+ * Otherwise, returns ERC-20 token balance.
952
+ */
953
+ async getBalance() {
954
+ const provider = new ethers.JsonRpcProvider(this.rpcUrl);
955
+ if (!this.tokenContractAddress) {
956
+ const balance2 = await provider.getBalance(this.walletAddress);
957
+ return Number(ethers.formatEther(balance2));
958
+ }
959
+ const erc20Abi = [
960
+ "function balanceOf(address) view returns (uint256)",
961
+ "function decimals() view returns (uint8)"
962
+ ];
963
+ const contract = new ethers.Contract(this.tokenContractAddress, erc20Abi, provider);
964
+ const balance = await contract.balanceOf(this.walletAddress);
965
+ const decimals = await contract.decimals();
966
+ return Number(ethers.formatUnits(balance, decimals));
967
+ }
968
+ /**
969
+ * Fetches the USD value of the wallet's balance using CoinGecko.
970
+ * Uses the contract address for ERC-20 tokens, or native ETH if contract address is null.
971
+ */
972
+ async getUSDValue() {
973
+ const balance = await this.getBalance();
974
+ const price = this.tokenContractAddress ? await this.priceProvider.getSpotPriceByTokenAddress(this.tokenContractAddress, "ethereum", "usd") : await this.priceProvider.getSpotPrice("ethereum", "usd");
975
+ return balance * price;
976
+ }
977
+ /**
978
+ * Fetches the transfer history for the wallet using the Alchemy API.
979
+ * For ERC-20 tokens, filters by contract address and returns only ERC-20 transfers.
980
+ * For native ETH, returns both external and internal transfers.
981
+ * Returns an array of transaction hashes (TransferID[]).
982
+ */
983
+ async getTransferHistory() {
984
+ const url = `https://eth-mainnet.g.alchemy.com/v2/${this.alchemyApiKey}`;
985
+ const body = {
986
+ jsonrpc: "2.0",
987
+ method: "alchemy_getAssetTransfers",
988
+ params: [
989
+ {
990
+ fromAddress: this.walletAddress,
991
+ contractAddresses: this.tokenContractAddress ? [this.tokenContractAddress] : void 0,
992
+ category: this.tokenContractAddress ? ["erc20"] : ["external", "internal"]
993
+ }
994
+ ],
995
+ id: 1
996
+ };
997
+ const options = {
998
+ method: "POST",
999
+ headers: { "Content-Type": "application/json" },
1000
+ body: JSON.stringify(body)
1001
+ };
1002
+ try {
1003
+ const response = await fetch(url, options);
1004
+ const data = await response.json();
1005
+ const txHashs = data.result.transfers.map((transfer) => transfer.hash);
1006
+ return txHashs;
1007
+ } catch (error) {
1008
+ console.error(error);
1009
+ }
1010
+ return [];
1011
+ }
1012
+ };
1013
+ function makeEthereumERC20AccountProvider(walletAddress, priceProvider, alchemyApiKey, tokenContractAddress = null, rpcUrl = "https://eth.rpc.blxrbdn.com") {
1014
+ return new EthereumERC20AccountProvider(walletAddress, priceProvider, alchemyApiKey, tokenContractAddress, rpcUrl);
1015
+ }
1016
+
1017
+ // src/BinanceAccountProvider.ts
1018
+ import crypto from "crypto";
1019
+ var BinanceAccountProvider = class {
1020
+ /**
1021
+ * @param apiKey Binance API key.
1022
+ * @param priceProvider CoinGecko Price Provider
1023
+ */
1024
+ constructor(apiKey, priceProvider) {
1025
+ this.apiKey = apiKey;
1026
+ this.priceProvider = priceProvider;
1027
+ this.apiSecret = crypto.randomBytes(32).toString("hex");
1028
+ }
1029
+ baseURL = "https://api.binance.com";
1030
+ apiSecret;
1031
+ /**
1032
+ * Returns the total balance (not implemented, returns 0).
1033
+ */
1034
+ get balance() {
1035
+ return Promise.resolve(0);
1036
+ }
1037
+ /**
1038
+ * Returns the total USD value of all free balances in the account.
1039
+ */
1040
+ get usdBalance() {
1041
+ return this.getUSDValue();
1042
+ }
1043
+ /**
1044
+ * Returns the transfer history for the account.
1045
+ */
1046
+ get transferHistory() {
1047
+ return this.getTransferHistory();
1048
+ }
1049
+ /**
1050
+ * Returns the total USD value of the given transfer IDs.
1051
+ * Deposits are positive, withdrawals are negative.
1052
+ * @param transfers Array of TransferID.
1053
+ */
1054
+ async deposits(transfers) {
1055
+ const deposits = await this.fetchTransfers("/sapi/v1/capital/deposit/hisrec");
1056
+ const withdrawals = await this.fetchTransfers("/sapi/v1/capital/withdraw/history");
1057
+ const all = [
1058
+ ...deposits.map((d) => ({ ...d, type: "deposit" })),
1059
+ ...withdrawals.map((w) => ({ ...w, type: "withdrawal" }))
1060
+ ].filter((t) => transfers.includes(t.txId));
1061
+ const uniqueAssets = new Set(all.map((t) => t.asset.toLowerCase()));
1062
+ const prices = await this.priceProvider.getSpotPriceBySymbols(
1063
+ Array.from(uniqueAssets),
1064
+ "usd"
1065
+ );
1066
+ return all.reduce((sum, t) => {
1067
+ const price = prices[t.asset.toLowerCase()]?.usd ?? 0;
1068
+ const amount = parseFloat(t.amount) * price;
1069
+ return sum + (t.type === "deposit" ? amount : -amount);
1070
+ }, 0);
1071
+ }
1072
+ /**
1073
+ * Signs a query string using HMAC SHA256 with the API secret.
1074
+ * @param queryString The query string to sign.
1075
+ * @returns The signature as a hex string.
1076
+ */
1077
+ sign(queryString) {
1078
+ return crypto.createHmac("sha256", this.apiSecret).update(queryString).digest("hex");
1079
+ }
1080
+ /**
1081
+ * Makes a signed request to a Binance API endpoint.
1082
+ * @param endpoint The API endpoint.
1083
+ * @param params Query parameters as an object.
1084
+ * @returns The parsed JSON response.
1085
+ */
1086
+ async privateRequest(endpoint, params = {}) {
1087
+ const timestamp = Date.now();
1088
+ const query = new URLSearchParams({
1089
+ ...params,
1090
+ timestamp: timestamp.toString()
1091
+ }).toString();
1092
+ const signature = this.sign(query);
1093
+ const url = `${this.baseURL}${endpoint}?${query}&signature=${signature}`;
1094
+ const res = await fetch(url, {
1095
+ method: "GET",
1096
+ headers: {
1097
+ "X-MBX-APIKEY": this.apiKey
1098
+ }
1099
+ });
1100
+ if (!res.ok) {
1101
+ const errorText = await res.text();
1102
+ throw new Error(`Binance API error: ${res.status} ${errorText}`);
1103
+ }
1104
+ return await res.json();
1105
+ }
1106
+ /**
1107
+ * Fetches all asset balances for the account using Binance API.
1108
+ * @returns Array of Balance objects.
1109
+ */
1110
+ async getBalance() {
1111
+ const data = await this.privateRequest("/api/v3/account");
1112
+ return data.balances;
1113
+ }
1114
+ /**
1115
+ * Fetches the total USD value of all free balances using CoinGecko prices.
1116
+ * @returns Total USD value as a number.
1117
+ */
1118
+ async getUSDValue() {
1119
+ const balances = await this.getBalance();
1120
+ const prices = await this.priceProvider.getSpotPriceBySymbols(
1121
+ balances.map((balance) => balance.asset),
1122
+ "usd"
1123
+ );
1124
+ let totalUSD = 0;
1125
+ for (const balance of balances) {
1126
+ const symbol = balance.asset.toLowerCase();
1127
+ const price = prices[symbol]?.usd ?? 0;
1128
+ const freeAmount = parseFloat(balance.free);
1129
+ totalUSD += freeAmount * price;
1130
+ }
1131
+ return totalUSD;
1132
+ }
1133
+ /**
1134
+ * Fetches the transfer history for deposits and withdrawals.
1135
+ * @returns Array of transfer IDs.
1136
+ */
1137
+ async getTransferHistory() {
1138
+ const deposits = await this.fetchTransfers("/sapi/v1/capital/deposit/hisrec");
1139
+ const withdrawals = await this.fetchTransfers("/sapi/v1/capital/withdraw/history");
1140
+ const allTx = [...deposits, ...withdrawals].sort((a, b) => a.insertTime - b.insertTime);
1141
+ return allTx.map((t) => t.txId);
1142
+ }
1143
+ /**
1144
+ * Helper method to fetch either deposit or withdrawal history.
1145
+ * @param endpoint Binance API endpoint.
1146
+ * @returns Array of BinanceTransfer.
1147
+ */
1148
+ async fetchTransfers(endpoint) {
1149
+ const data = await this.privateRequest(endpoint);
1150
+ return Array.isArray(data) ? data.filter((t) => t.status === 1) : [];
1151
+ }
1152
+ };
1153
+ function makeBinanceAccountProvider(apiKey, priceProvider) {
1154
+ return new BinanceAccountProvider(apiKey, priceProvider);
1155
+ }
1156
+
1157
+ // src/BSCAccountProvider.ts
1158
+ import { ethers as ethers2 } from "ethers";
1159
+ var BSCAccountProvider = class {
1160
+ /**
1161
+ * @param walletAddress The BSC wallet address to query.
1162
+ * @param priceProvider CoinGecko Price Provider
1163
+ * @param alchemyApiKey The Alchemy API key.
1164
+ * @param tokenContractAddress The BEP-20 token contract address, or null for native BNB.
1165
+ * @param rpcUrl The BSC RPC endpoint to use.
1166
+ */
1167
+ constructor(walletAddress, priceProvider, alchemyApiKey, tokenContractAddress = null, rpcUrl = "https://bsc-dataseed.binance.org/") {
1168
+ this.walletAddress = walletAddress;
1169
+ this.priceProvider = priceProvider;
1170
+ this.alchemyApiKey = alchemyApiKey;
1171
+ this.tokenContractAddress = tokenContractAddress;
1172
+ this.rpcUrl = rpcUrl;
1173
+ }
1174
+ /**
1175
+ * Returns the balance of the wallet for the specified token or native BNB.
1176
+ */
1177
+ get balance() {
1178
+ return this.getBalance();
1179
+ }
1180
+ /**
1181
+ * Returns the USD value of the wallet's balance for the specified token or native BNB.
1182
+ */
1183
+ get usdBalance() {
1184
+ return this.getUSDValue();
1185
+ }
1186
+ /**
1187
+ * Returns the transfer history for the wallet.
1188
+ */
1189
+ get transferHistory() {
1190
+ return this.getTransferHistory();
1191
+ }
1192
+ /**
1193
+ * Calculates the net USD value of the specified Binance Smart Chain transactions.
1194
+ *
1195
+ * - For native BNB: uses transaction value directly.
1196
+ * - For BEP-20 tokens: extracts transfer amounts from logs (tx.value is always 0).
1197
+ * - Adds amounts received by the wallet, subtracts amounts sent.
1198
+ *
1199
+ * Converts the net token or BNB amount to USD using the current spot price.
1200
+ *
1201
+ * @param transfers Array of Binance Smart Chain transaction hashes (TransferID[])
1202
+ * @returns Net deposit amount in USD.
1203
+ */
1204
+ async deposits(_transfers) {
1205
+ const provider = new ethers2.JsonRpcProvider(this.rpcUrl);
1206
+ const lowerWalletAddress = this.walletAddress.toLowerCase();
1207
+ let usdTotal = 0;
1208
+ if (this.tokenContractAddress) {
1209
+ const bep20Abi = [
1210
+ "function decimals() view returns (uint8)",
1211
+ "event Transfer(address indexed from, address indexed to, uint amount)"
1212
+ ];
1213
+ const contract = new ethers2.Contract(this.tokenContractAddress, bep20Abi, provider);
1214
+ const decimals = await contract.decimals();
1215
+ for (const txHash of _transfers) {
1216
+ const receipt = await provider.getTransactionReceipt(txHash);
1217
+ if (!receipt) continue;
1218
+ for (const log of receipt.logs) {
1219
+ try {
1220
+ const parsed = contract.interface.parseLog(log);
1221
+ if (!parsed || parsed.name !== "Transfer") continue;
1222
+ const [from, to, amountWei] = parsed.args;
1223
+ const amount = Number(ethers2.formatUnits(amountWei, decimals));
1224
+ const price = await this.priceProvider.getSpotPriceByTokenAddress(
1225
+ this.tokenContractAddress,
1226
+ "binance-smart-chain",
1227
+ "usd"
1228
+ );
1229
+ if (to === lowerWalletAddress) {
1230
+ usdTotal += amount * price;
1231
+ }
1232
+ if (from === lowerWalletAddress) {
1233
+ usdTotal -= amount * price;
1234
+ }
1235
+ } catch {
1236
+ }
1237
+ }
1238
+ }
1239
+ } else {
1240
+ for (const txHash of _transfers) {
1241
+ const tx = await provider.getTransaction(txHash);
1242
+ if (!tx) continue;
1243
+ const from = tx.from.toLowerCase();
1244
+ const to = tx.to?.toLowerCase();
1245
+ const amount = Number(ethers2.formatEther(tx.value));
1246
+ const price = await this.priceProvider.getSpotPrice("binancecoin", "usd");
1247
+ if (to === lowerWalletAddress) {
1248
+ usdTotal += amount * price;
1249
+ }
1250
+ if (from === lowerWalletAddress) {
1251
+ usdTotal -= amount * price;
1252
+ }
1253
+ }
1254
+ }
1255
+ return usdTotal;
1256
+ }
1257
+ /**
1258
+ * Fetches the balance for the wallet.
1259
+ * If tokenContractAddress is null, returns native BNB balance.
1260
+ * Otherwise, returns BEP-20 token balance.
1261
+ */
1262
+ async getBalance() {
1263
+ const provider = new ethers2.JsonRpcProvider(this.rpcUrl);
1264
+ if (!this.tokenContractAddress) {
1265
+ const balance2 = await provider.getBalance(this.walletAddress);
1266
+ return Number(ethers2.formatEther(balance2));
1267
+ }
1268
+ const bep20Abi = [
1269
+ "function balanceOf(address) view returns (uint256)",
1270
+ "function decimals() view returns (uint8)"
1271
+ ];
1272
+ const contract = new ethers2.Contract(this.tokenContractAddress, bep20Abi, provider);
1273
+ const balance = await contract.balanceOf(this.walletAddress);
1274
+ const decimals = await contract.decimals();
1275
+ return Number(ethers2.formatUnits(balance, decimals));
1276
+ }
1277
+ /**
1278
+ * Fetches the USD value of the wallet's balance using CoinGecko.
1279
+ * Uses the contract address for BEP-20 tokens, or native BNB if contract address is null.
1280
+ */
1281
+ async getUSDValue() {
1282
+ const balance = await this.getBalance();
1283
+ const price = this.tokenContractAddress ? await this.priceProvider.getSpotPriceByTokenAddress(this.tokenContractAddress, "ethereum", "usd") : await this.priceProvider.getSpotPrice("binancecoin", "usd");
1284
+ return balance * price;
1285
+ }
1286
+ /**
1287
+ * Fetches the transfer history for the wallet using the Alchemy API.
1288
+ * For BEP-20 tokens, filters by contract address and returns only BEP-20 transfers.
1289
+ * For native BNB, returns both external and internal transfers.
1290
+ * Returns an array of transaction hashes (TransferID[]).
1291
+ */
1292
+ async getTransferHistory() {
1293
+ const url = `https://bnb-mainnet.g.alchemy.com/v2/${this.alchemyApiKey}}`;
1294
+ const body = {
1295
+ jsonrpc: "2.0",
1296
+ method: "alchemy_getAssetTransfers",
1297
+ params: [
1298
+ {
1299
+ fromAddress: this.walletAddress,
1300
+ contractAddresses: this.tokenContractAddress ? [this.tokenContractAddress] : void 0,
1301
+ category: this.tokenContractAddress ? ["bep20"] : ["external", "internal"]
1302
+ }
1303
+ ],
1304
+ id: 1
1305
+ };
1306
+ const options = {
1307
+ method: "POST",
1308
+ headers: { "Content-Type": "application/json" },
1309
+ body: JSON.stringify(body)
1310
+ };
1311
+ try {
1312
+ const response = await fetch(url, options);
1313
+ const data = await response.json();
1314
+ const txHashs = data.result.transfers.map((transfer) => transfer.hash);
1315
+ return txHashs;
1316
+ } catch (error) {
1317
+ console.error(error);
1318
+ }
1319
+ return [];
1320
+ }
1321
+ };
1322
+ function makeBSCAccountProvider(walletAddress, priceProvider, alchemyApiKey, tokenContractAddress = null, rpcUrl = "https://bsc-dataseed.binance.org/") {
1323
+ return new BSCAccountProvider(walletAddress, priceProvider, alchemyApiKey, tokenContractAddress, rpcUrl);
1324
+ }
469
1325
  export {
470
1326
  filterTransfersAfter,
1327
+ makeBSCAccountProvider,
1328
+ makeBinanceAccountProvider,
1329
+ makeBitcoinWalletProvider,
471
1330
  makeCardanoWalletProvider,
1331
+ makeCoinGeckoProvider,
1332
+ makeEthereumERC20AccountProvider,
472
1333
  makeRWADatumProvider,
1334
+ one_to_one_asset,
473
1335
  tokenized_account
474
1336
  };