@sovereign-sdk/modules 0.3.3 → 0.3.5

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
@@ -27,7 +27,6 @@ module.exports = __toCommonJS(src_exports);
27
27
 
28
28
  // src/bank.ts
29
29
  var import_sha2 = require("@noble/hashes/sha2");
30
- var import_bech32 = require("bech32");
31
30
  function getHolderAddress(holder) {
32
31
  if ("user" in holder) return holder.user;
33
32
  if ("module" in holder) return holder.module;
@@ -105,17 +104,14 @@ var Bank = class {
105
104
  async tokenMetadata(tokenId) {
106
105
  const token = await this.tokenIdOrElseGasTokenId(tokenId);
107
106
  const response = await this.rollup.http.get(
108
- `/modules/bank/state/tokens/items/${token}`
107
+ `/modules/bank/tokens/${token}/metadata`
109
108
  );
110
- const decoded = import_bech32.bech32m.decode(token);
111
- const bytes = import_bech32.bech32m.fromWords(decoded.words);
112
- const decimals = bytes[31];
113
109
  return {
114
- name: response.value.name,
115
- decimals,
116
- totalSupply: BigInt(response.value.total_supply),
117
- supplyCap: BigInt(response.value.supply_cap),
118
- admins: response.value.admins.map(getHolderAddress)
110
+ name: response.name,
111
+ decimals: response.decimals,
112
+ totalSupply: BigInt(response.total_supply),
113
+ supplyCap: BigInt(response.supply_cap),
114
+ admins: response.admins.map(getHolderAddress)
119
115
  };
120
116
  }
121
117
  /**
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/bank.ts"],"sourcesContent":["export * from \"./bank\";\n","import { sha256 } from \"@noble/hashes/sha2\";\nimport { type Rollup, SovereignClient } from \"@sovereign-sdk/web3\";\nimport { bech32m } from \"bech32\";\nimport type { ErrorResponse } from \"./types\";\n\n// TokenHolder is an enum: { user: string } | { module: string } | { derived: string }\ntype TokenHolderPayload =\n | { user: string }\n | { module: string }\n | { derived: string };\n\ntype TokenStatePayload = {\n key: string;\n value: {\n name: string;\n total_supply: string;\n supply_cap: string;\n admins: TokenHolderPayload[];\n };\n};\n\nexport type TokenMetadata = {\n name: string;\n decimals: number;\n totalSupply: bigint;\n supplyCap: bigint;\n admins: string[];\n};\n\nfunction getHolderAddress(holder: TokenHolderPayload): string {\n if (\"user\" in holder) return holder.user;\n if (\"module\" in holder) return holder.module;\n return holder.derived;\n}\n\ntype GasTokenIdPayload = {\n token_id: string;\n};\n\ntype BalancePayload = {\n amount: string;\n token_id: string;\n};\n\n/**\n * Derives a token ID from the originator, token name, and decimals.\n *\n * Matches the Rust `get_token_id` implementation: SHA-256 hash of\n * originator bytes + token name bytes + decimals byte, with the\n * last byte of the hash replaced by the decimals value.\n *\n * @param originator - The raw bytes of the token originator (e.g. decoded address bytes)\n * @param tokenName - The name of the token\n * @param decimals - The number of decimal places for the token\n * @returns The 32-byte token ID\n */\nexport function getTokenId(\n originator: Uint8Array,\n tokenName: string,\n decimals: number,\n): Uint8Array {\n const nameBytes = new TextEncoder().encode(tokenName);\n\n const buffer = new Uint8Array(originator.length + nameBytes.length + 1);\n buffer.set(originator, 0);\n buffer.set(nameBytes, originator.length);\n buffer[originator.length + nameBytes.length] = decimals;\n\n const hash = sha256(buffer);\n hash[31] = decimals;\n return hash;\n}\n\n/**\n * Bank class for interacting with the Sovereign SDK Bank module.\n */\nexport class Bank {\n // biome-ignore lint/suspicious/noExplicitAny: types arent used\n private readonly rollup: Rollup<any, any>;\n private _gasTokenId?: string;\n\n /**\n * Creates a new Bank instance.\n *\n * @param rollup - The rollup instance to use for HTTP requests\n */\n // biome-ignore lint/suspicious/noExplicitAny: types arent used\n constructor(rollup: Rollup<any, any>) {\n this.rollup = rollup;\n }\n\n /**\n * Gets the balance of a specific token for a given address.\n *\n * If no token ID is provided, the gas token balance will be returned.\n *\n * @param address - The address to query the balance for\n * @param tokenId - Optional token ID. If not provided, uses the gas token\n * @returns Promise resolving to the token balance as a bigint\n * @throws {SovereignClient.APIError} When the request fails for reasons other than missing account\n * @example\n * ```typescript\n * const bank = new Bank(rollup);\n * const balance = await bank.balance(\"0x123...\");\n * console.log(`Balance: ${balance}`);\n *\n * // Query specific token balance\n * const tokenBalance = await bank.balance(\"0x123...\", \"token_123\");\n * ```\n */\n async balance(address: string, tokenId?: string): Promise<bigint> {\n const token = await this.tokenIdOrElseGasTokenId(tokenId);\n\n try {\n const response: BalancePayload = await this.rollup.http.get(\n `/modules/bank/tokens/${token}/balances/${address}`,\n );\n\n return BigInt(response.amount);\n } catch (err) {\n if (this.isMissingAccountError(err)) return BigInt(0);\n\n throw err;\n }\n }\n\n /**\n * Gets the metadata of a specific token.\n *\n * If no token ID is provided, returns the metadata of the gas token.\n *\n * @param tokenId - Optional token ID. If not provided, uses the gas token\n * @returns Promise resolving to the token metadata\n * @throws {SovereignClient.APIError} When the request fails\n * @example\n * ```typescript\n * const bank = new Bank(rollup);\n * const metadata = await bank.tokenMetadata();\n * console.log(`Token name: ${metadata.name}, decimals: ${metadata.decimals}`);\n *\n * // Query specific token metadata\n * const tokenMeta = await bank.tokenMetadata(\"token_123\");\n * ```\n */\n async tokenMetadata(tokenId?: string): Promise<TokenMetadata> {\n const token = await this.tokenIdOrElseGasTokenId(tokenId);\n const response: TokenStatePayload = await this.rollup.http.get(\n `/modules/bank/state/tokens/items/${token}`,\n );\n\n // Decimals are encoded in the last byte of the token ID (byte 31 of the 32-byte hash)\n const decoded = bech32m.decode(token);\n const bytes = bech32m.fromWords(decoded.words);\n const decimals = bytes[31];\n\n return {\n name: response.value.name,\n decimals,\n totalSupply: BigInt(response.value.total_supply),\n supplyCap: BigInt(response.value.supply_cap),\n admins: response.value.admins.map(getHolderAddress),\n };\n }\n\n /**\n * Gets the gas token identifier.\n *\n * The gas token ID is cached after the first request to avoid repeated API calls.\n *\n * @returns Promise resolving to the gas token ID as a string\n * @throws {Error} When the gas token ID cannot be retrieved\n * @example\n * ```typescript\n * const bank = new Bank(rollup);\n * const gasTokenId = await bank.gasTokenId();\n * console.log(`Gas token ID: ${gasTokenId}`);\n * ```\n */\n async gasTokenId(): Promise<string> {\n if (this._gasTokenId) return this._gasTokenId;\n const response: GasTokenIdPayload = await this.rollup.http.get(\n \"/modules/bank/tokens/gas_token\",\n );\n this._gasTokenId = response.token_id;\n return this._gasTokenId;\n }\n\n private async tokenIdOrElseGasTokenId(tokenId?: string): Promise<string> {\n return tokenId ?? this.gasTokenId();\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: todo\n private isMissingAccountError(anyError: any): boolean {\n if (anyError?.status !== 404) return false;\n const body = anyError?.error as ErrorResponse;\n return (\n body?.message?.startsWith(\"Balance\") &&\n body?.message?.endsWith(\"not found\")\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,kBAAuB;AAEvB,oBAAwB;AA2BxB,SAAS,iBAAiB,QAAoC;AAC5D,MAAI,UAAU,OAAQ,QAAO,OAAO;AACpC,MAAI,YAAY,OAAQ,QAAO,OAAO;AACtC,SAAO,OAAO;AAChB;AAuBO,SAAS,WACd,YACA,WACA,UACY;AACZ,QAAM,YAAY,IAAI,YAAY,EAAE,OAAO,SAAS;AAEpD,QAAM,SAAS,IAAI,WAAW,WAAW,SAAS,UAAU,SAAS,CAAC;AACtE,SAAO,IAAI,YAAY,CAAC;AACxB,SAAO,IAAI,WAAW,WAAW,MAAM;AACvC,SAAO,WAAW,SAAS,UAAU,MAAM,IAAI;AAE/C,QAAM,WAAO,oBAAO,MAAM;AAC1B,OAAK,EAAE,IAAI;AACX,SAAO;AACT;AAKO,IAAM,OAAN,MAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWhB,YAAY,QAA0B;AACpC,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBA,MAAM,QAAQ,SAAiB,SAAmC;AAChE,UAAM,QAAQ,MAAM,KAAK,wBAAwB,OAAO;AAExD,QAAI;AACF,YAAM,WAA2B,MAAM,KAAK,OAAO,KAAK;AAAA,QACtD,wBAAwB,KAAK,aAAa,OAAO;AAAA,MACnD;AAEA,aAAO,OAAO,SAAS,MAAM;AAAA,IAC/B,SAAS,KAAK;AACZ,UAAI,KAAK,sBAAsB,GAAG,EAAG,QAAO,OAAO,CAAC;AAEpD,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,MAAM,cAAc,SAA0C;AAC5D,UAAM,QAAQ,MAAM,KAAK,wBAAwB,OAAO;AACxD,UAAM,WAA8B,MAAM,KAAK,OAAO,KAAK;AAAA,MACzD,oCAAoC,KAAK;AAAA,IAC3C;AAGA,UAAM,UAAU,sBAAQ,OAAO,KAAK;AACpC,UAAM,QAAQ,sBAAQ,UAAU,QAAQ,KAAK;AAC7C,UAAM,WAAW,MAAM,EAAE;AAEzB,WAAO;AAAA,MACL,MAAM,SAAS,MAAM;AAAA,MACrB;AAAA,MACA,aAAa,OAAO,SAAS,MAAM,YAAY;AAAA,MAC/C,WAAW,OAAO,SAAS,MAAM,UAAU;AAAA,MAC3C,QAAQ,SAAS,MAAM,OAAO,IAAI,gBAAgB;AAAA,IACpD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAM,aAA8B;AAClC,QAAI,KAAK,YAAa,QAAO,KAAK;AAClC,UAAM,WAA8B,MAAM,KAAK,OAAO,KAAK;AAAA,MACzD;AAAA,IACF;AACA,SAAK,cAAc,SAAS;AAC5B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAc,wBAAwB,SAAmC;AACvE,WAAO,WAAW,KAAK,WAAW;AAAA,EACpC;AAAA;AAAA,EAGQ,sBAAsB,UAAwB;AACpD,QAAI,UAAU,WAAW,IAAK,QAAO;AACrC,UAAM,OAAO,UAAU;AACvB,WACE,MAAM,SAAS,WAAW,SAAS,KACnC,MAAM,SAAS,SAAS,WAAW;AAAA,EAEvC;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/bank.ts"],"sourcesContent":["export * from \"./bank\";\n","import { sha256 } from \"@noble/hashes/sha2\";\nimport type { Rollup } from \"@sovereign-sdk/web3\";\nimport type { ErrorResponse } from \"./types\";\n\n// TokenHolder is an enum: { user: string } | { module: string } | { derived: string }\ntype TokenHolderPayload =\n | { user: string }\n | { module: string }\n | { derived: string };\n\ntype TokenMetadataResponse = {\n name: string;\n decimals: number;\n total_supply: string;\n supply_cap: string;\n admins: TokenHolderPayload[];\n};\n\nexport type TokenMetadata = {\n name: string;\n decimals: number;\n totalSupply: bigint;\n supplyCap: bigint;\n admins: string[];\n};\n\nfunction getHolderAddress(holder: TokenHolderPayload): string {\n if (\"user\" in holder) return holder.user;\n if (\"module\" in holder) return holder.module;\n return holder.derived;\n}\n\ntype GasTokenIdPayload = {\n token_id: string;\n};\n\ntype BalancePayload = {\n amount: string;\n token_id: string;\n};\n\n/**\n * Derives a token ID from the originator, token name, and decimals.\n *\n * Matches the Rust `get_token_id` implementation: SHA-256 hash of\n * originator bytes + token name bytes + decimals byte, with the\n * last byte of the hash replaced by the decimals value.\n *\n * @param originator - The raw bytes of the token originator (e.g. decoded address bytes)\n * @param tokenName - The name of the token\n * @param decimals - The number of decimal places for the token\n * @returns The 32-byte token ID\n */\nexport function getTokenId(\n originator: Uint8Array,\n tokenName: string,\n decimals: number,\n): Uint8Array {\n const nameBytes = new TextEncoder().encode(tokenName);\n\n const buffer = new Uint8Array(originator.length + nameBytes.length + 1);\n buffer.set(originator, 0);\n buffer.set(nameBytes, originator.length);\n buffer[originator.length + nameBytes.length] = decimals;\n\n const hash = sha256(buffer);\n hash[31] = decimals;\n return hash;\n}\n\n/**\n * Bank class for interacting with the Sovereign SDK Bank module.\n */\nexport class Bank {\n // biome-ignore lint/suspicious/noExplicitAny: types arent used\n private readonly rollup: Rollup<any, any>;\n private _gasTokenId?: string;\n\n /**\n * Creates a new Bank instance.\n *\n * @param rollup - The rollup instance to use for HTTP requests\n */\n // biome-ignore lint/suspicious/noExplicitAny: types arent used\n constructor(rollup: Rollup<any, any>) {\n this.rollup = rollup;\n }\n\n /**\n * Gets the balance of a specific token for a given address.\n *\n * If no token ID is provided, the gas token balance will be returned.\n *\n * @param address - The address to query the balance for\n * @param tokenId - Optional token ID. If not provided, uses the gas token\n * @returns Promise resolving to the token balance as a bigint\n * @throws {SovereignClient.APIError} When the request fails for reasons other than missing account\n * @example\n * ```typescript\n * const bank = new Bank(rollup);\n * const balance = await bank.balance(\"0x123...\");\n * console.log(`Balance: ${balance}`);\n *\n * // Query specific token balance\n * const tokenBalance = await bank.balance(\"0x123...\", \"token_123\");\n * ```\n */\n async balance(address: string, tokenId?: string): Promise<bigint> {\n const token = await this.tokenIdOrElseGasTokenId(tokenId);\n\n try {\n const response: BalancePayload = await this.rollup.http.get(\n `/modules/bank/tokens/${token}/balances/${address}`,\n );\n\n return BigInt(response.amount);\n } catch (err) {\n if (this.isMissingAccountError(err)) return BigInt(0);\n\n throw err;\n }\n }\n\n /**\n * Gets the metadata of a specific token.\n *\n * If no token ID is provided, returns the metadata of the gas token.\n *\n * @param tokenId - Optional token ID. If not provided, uses the gas token\n * @returns Promise resolving to the token metadata\n * @throws {SovereignClient.APIError} When the request fails\n * @example\n * ```typescript\n * const bank = new Bank(rollup);\n * const metadata = await bank.tokenMetadata();\n * console.log(`Token name: ${metadata.name}, decimals: ${metadata.decimals}`);\n *\n * // Query specific token metadata\n * const tokenMeta = await bank.tokenMetadata(\"token_123\");\n * ```\n */\n async tokenMetadata(tokenId?: string): Promise<TokenMetadata> {\n const token = await this.tokenIdOrElseGasTokenId(tokenId);\n // Decimals are resolved server-side, so the gas token's authoritative decimals (which are not\n // encoded in its id) are handled by the rollup rather than reconstructed here.\n const response: TokenMetadataResponse = await this.rollup.http.get(\n `/modules/bank/tokens/${token}/metadata`,\n );\n\n return {\n name: response.name,\n decimals: response.decimals,\n totalSupply: BigInt(response.total_supply),\n supplyCap: BigInt(response.supply_cap),\n admins: response.admins.map(getHolderAddress),\n };\n }\n\n /**\n * Gets the gas token identifier.\n *\n * The gas token ID is cached after the first request to avoid repeated API calls.\n *\n * @returns Promise resolving to the gas token ID as a string\n * @throws {Error} When the gas token ID cannot be retrieved\n * @example\n * ```typescript\n * const bank = new Bank(rollup);\n * const gasTokenId = await bank.gasTokenId();\n * console.log(`Gas token ID: ${gasTokenId}`);\n * ```\n */\n async gasTokenId(): Promise<string> {\n if (this._gasTokenId) return this._gasTokenId;\n const response: GasTokenIdPayload = await this.rollup.http.get(\n \"/modules/bank/tokens/gas_token\",\n );\n this._gasTokenId = response.token_id;\n return this._gasTokenId;\n }\n\n private async tokenIdOrElseGasTokenId(tokenId?: string): Promise<string> {\n return tokenId ?? this.gasTokenId();\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: todo\n private isMissingAccountError(anyError: any): boolean {\n if (anyError?.status !== 404) return false;\n const body = anyError?.error as ErrorResponse;\n return (\n body?.message?.startsWith(\"Balance\") &&\n body?.message?.endsWith(\"not found\")\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,kBAAuB;AA0BvB,SAAS,iBAAiB,QAAoC;AAC5D,MAAI,UAAU,OAAQ,QAAO,OAAO;AACpC,MAAI,YAAY,OAAQ,QAAO,OAAO;AACtC,SAAO,OAAO;AAChB;AAuBO,SAAS,WACd,YACA,WACA,UACY;AACZ,QAAM,YAAY,IAAI,YAAY,EAAE,OAAO,SAAS;AAEpD,QAAM,SAAS,IAAI,WAAW,WAAW,SAAS,UAAU,SAAS,CAAC;AACtE,SAAO,IAAI,YAAY,CAAC;AACxB,SAAO,IAAI,WAAW,WAAW,MAAM;AACvC,SAAO,WAAW,SAAS,UAAU,MAAM,IAAI;AAE/C,QAAM,WAAO,oBAAO,MAAM;AAC1B,OAAK,EAAE,IAAI;AACX,SAAO;AACT;AAKO,IAAM,OAAN,MAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWhB,YAAY,QAA0B;AACpC,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBA,MAAM,QAAQ,SAAiB,SAAmC;AAChE,UAAM,QAAQ,MAAM,KAAK,wBAAwB,OAAO;AAExD,QAAI;AACF,YAAM,WAA2B,MAAM,KAAK,OAAO,KAAK;AAAA,QACtD,wBAAwB,KAAK,aAAa,OAAO;AAAA,MACnD;AAEA,aAAO,OAAO,SAAS,MAAM;AAAA,IAC/B,SAAS,KAAK;AACZ,UAAI,KAAK,sBAAsB,GAAG,EAAG,QAAO,OAAO,CAAC;AAEpD,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,MAAM,cAAc,SAA0C;AAC5D,UAAM,QAAQ,MAAM,KAAK,wBAAwB,OAAO;AAGxD,UAAM,WAAkC,MAAM,KAAK,OAAO,KAAK;AAAA,MAC7D,wBAAwB,KAAK;AAAA,IAC/B;AAEA,WAAO;AAAA,MACL,MAAM,SAAS;AAAA,MACf,UAAU,SAAS;AAAA,MACnB,aAAa,OAAO,SAAS,YAAY;AAAA,MACzC,WAAW,OAAO,SAAS,UAAU;AAAA,MACrC,QAAQ,SAAS,OAAO,IAAI,gBAAgB;AAAA,IAC9C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAM,aAA8B;AAClC,QAAI,KAAK,YAAa,QAAO,KAAK;AAClC,UAAM,WAA8B,MAAM,KAAK,OAAO,KAAK;AAAA,MACzD;AAAA,IACF;AACA,SAAK,cAAc,SAAS;AAC5B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAc,wBAAwB,SAAmC;AACvE,WAAO,WAAW,KAAK,WAAW;AAAA,EACpC;AAAA;AAAA,EAGQ,sBAAsB,UAAwB;AACpD,QAAI,UAAU,WAAW,IAAK,QAAO;AACrC,UAAM,OAAO,UAAU;AACvB,WACE,MAAM,SAAS,WAAW,SAAS,KACnC,MAAM,SAAS,SAAS,WAAW;AAAA,EAEvC;AACF;","names":[]}
package/dist/index.mjs CHANGED
@@ -1,6 +1,5 @@
1
1
  // src/bank.ts
2
2
  import { sha256 } from "@noble/hashes/sha2";
3
- import { bech32m } from "bech32";
4
3
  function getHolderAddress(holder) {
5
4
  if ("user" in holder) return holder.user;
6
5
  if ("module" in holder) return holder.module;
@@ -78,17 +77,14 @@ var Bank = class {
78
77
  async tokenMetadata(tokenId) {
79
78
  const token = await this.tokenIdOrElseGasTokenId(tokenId);
80
79
  const response = await this.rollup.http.get(
81
- `/modules/bank/state/tokens/items/${token}`
80
+ `/modules/bank/tokens/${token}/metadata`
82
81
  );
83
- const decoded = bech32m.decode(token);
84
- const bytes = bech32m.fromWords(decoded.words);
85
- const decimals = bytes[31];
86
82
  return {
87
- name: response.value.name,
88
- decimals,
89
- totalSupply: BigInt(response.value.total_supply),
90
- supplyCap: BigInt(response.value.supply_cap),
91
- admins: response.value.admins.map(getHolderAddress)
83
+ name: response.name,
84
+ decimals: response.decimals,
85
+ totalSupply: BigInt(response.total_supply),
86
+ supplyCap: BigInt(response.supply_cap),
87
+ admins: response.admins.map(getHolderAddress)
92
88
  };
93
89
  }
94
90
  /**
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/bank.ts"],"sourcesContent":["import { sha256 } from \"@noble/hashes/sha2\";\nimport { type Rollup, SovereignClient } from \"@sovereign-sdk/web3\";\nimport { bech32m } from \"bech32\";\nimport type { ErrorResponse } from \"./types\";\n\n// TokenHolder is an enum: { user: string } | { module: string } | { derived: string }\ntype TokenHolderPayload =\n | { user: string }\n | { module: string }\n | { derived: string };\n\ntype TokenStatePayload = {\n key: string;\n value: {\n name: string;\n total_supply: string;\n supply_cap: string;\n admins: TokenHolderPayload[];\n };\n};\n\nexport type TokenMetadata = {\n name: string;\n decimals: number;\n totalSupply: bigint;\n supplyCap: bigint;\n admins: string[];\n};\n\nfunction getHolderAddress(holder: TokenHolderPayload): string {\n if (\"user\" in holder) return holder.user;\n if (\"module\" in holder) return holder.module;\n return holder.derived;\n}\n\ntype GasTokenIdPayload = {\n token_id: string;\n};\n\ntype BalancePayload = {\n amount: string;\n token_id: string;\n};\n\n/**\n * Derives a token ID from the originator, token name, and decimals.\n *\n * Matches the Rust `get_token_id` implementation: SHA-256 hash of\n * originator bytes + token name bytes + decimals byte, with the\n * last byte of the hash replaced by the decimals value.\n *\n * @param originator - The raw bytes of the token originator (e.g. decoded address bytes)\n * @param tokenName - The name of the token\n * @param decimals - The number of decimal places for the token\n * @returns The 32-byte token ID\n */\nexport function getTokenId(\n originator: Uint8Array,\n tokenName: string,\n decimals: number,\n): Uint8Array {\n const nameBytes = new TextEncoder().encode(tokenName);\n\n const buffer = new Uint8Array(originator.length + nameBytes.length + 1);\n buffer.set(originator, 0);\n buffer.set(nameBytes, originator.length);\n buffer[originator.length + nameBytes.length] = decimals;\n\n const hash = sha256(buffer);\n hash[31] = decimals;\n return hash;\n}\n\n/**\n * Bank class for interacting with the Sovereign SDK Bank module.\n */\nexport class Bank {\n // biome-ignore lint/suspicious/noExplicitAny: types arent used\n private readonly rollup: Rollup<any, any>;\n private _gasTokenId?: string;\n\n /**\n * Creates a new Bank instance.\n *\n * @param rollup - The rollup instance to use for HTTP requests\n */\n // biome-ignore lint/suspicious/noExplicitAny: types arent used\n constructor(rollup: Rollup<any, any>) {\n this.rollup = rollup;\n }\n\n /**\n * Gets the balance of a specific token for a given address.\n *\n * If no token ID is provided, the gas token balance will be returned.\n *\n * @param address - The address to query the balance for\n * @param tokenId - Optional token ID. If not provided, uses the gas token\n * @returns Promise resolving to the token balance as a bigint\n * @throws {SovereignClient.APIError} When the request fails for reasons other than missing account\n * @example\n * ```typescript\n * const bank = new Bank(rollup);\n * const balance = await bank.balance(\"0x123...\");\n * console.log(`Balance: ${balance}`);\n *\n * // Query specific token balance\n * const tokenBalance = await bank.balance(\"0x123...\", \"token_123\");\n * ```\n */\n async balance(address: string, tokenId?: string): Promise<bigint> {\n const token = await this.tokenIdOrElseGasTokenId(tokenId);\n\n try {\n const response: BalancePayload = await this.rollup.http.get(\n `/modules/bank/tokens/${token}/balances/${address}`,\n );\n\n return BigInt(response.amount);\n } catch (err) {\n if (this.isMissingAccountError(err)) return BigInt(0);\n\n throw err;\n }\n }\n\n /**\n * Gets the metadata of a specific token.\n *\n * If no token ID is provided, returns the metadata of the gas token.\n *\n * @param tokenId - Optional token ID. If not provided, uses the gas token\n * @returns Promise resolving to the token metadata\n * @throws {SovereignClient.APIError} When the request fails\n * @example\n * ```typescript\n * const bank = new Bank(rollup);\n * const metadata = await bank.tokenMetadata();\n * console.log(`Token name: ${metadata.name}, decimals: ${metadata.decimals}`);\n *\n * // Query specific token metadata\n * const tokenMeta = await bank.tokenMetadata(\"token_123\");\n * ```\n */\n async tokenMetadata(tokenId?: string): Promise<TokenMetadata> {\n const token = await this.tokenIdOrElseGasTokenId(tokenId);\n const response: TokenStatePayload = await this.rollup.http.get(\n `/modules/bank/state/tokens/items/${token}`,\n );\n\n // Decimals are encoded in the last byte of the token ID (byte 31 of the 32-byte hash)\n const decoded = bech32m.decode(token);\n const bytes = bech32m.fromWords(decoded.words);\n const decimals = bytes[31];\n\n return {\n name: response.value.name,\n decimals,\n totalSupply: BigInt(response.value.total_supply),\n supplyCap: BigInt(response.value.supply_cap),\n admins: response.value.admins.map(getHolderAddress),\n };\n }\n\n /**\n * Gets the gas token identifier.\n *\n * The gas token ID is cached after the first request to avoid repeated API calls.\n *\n * @returns Promise resolving to the gas token ID as a string\n * @throws {Error} When the gas token ID cannot be retrieved\n * @example\n * ```typescript\n * const bank = new Bank(rollup);\n * const gasTokenId = await bank.gasTokenId();\n * console.log(`Gas token ID: ${gasTokenId}`);\n * ```\n */\n async gasTokenId(): Promise<string> {\n if (this._gasTokenId) return this._gasTokenId;\n const response: GasTokenIdPayload = await this.rollup.http.get(\n \"/modules/bank/tokens/gas_token\",\n );\n this._gasTokenId = response.token_id;\n return this._gasTokenId;\n }\n\n private async tokenIdOrElseGasTokenId(tokenId?: string): Promise<string> {\n return tokenId ?? this.gasTokenId();\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: todo\n private isMissingAccountError(anyError: any): boolean {\n if (anyError?.status !== 404) return false;\n const body = anyError?.error as ErrorResponse;\n return (\n body?.message?.startsWith(\"Balance\") &&\n body?.message?.endsWith(\"not found\")\n );\n }\n}\n"],"mappings":";AAAA,SAAS,cAAc;AAEvB,SAAS,eAAe;AA2BxB,SAAS,iBAAiB,QAAoC;AAC5D,MAAI,UAAU,OAAQ,QAAO,OAAO;AACpC,MAAI,YAAY,OAAQ,QAAO,OAAO;AACtC,SAAO,OAAO;AAChB;AAuBO,SAAS,WACd,YACA,WACA,UACY;AACZ,QAAM,YAAY,IAAI,YAAY,EAAE,OAAO,SAAS;AAEpD,QAAM,SAAS,IAAI,WAAW,WAAW,SAAS,UAAU,SAAS,CAAC;AACtE,SAAO,IAAI,YAAY,CAAC;AACxB,SAAO,IAAI,WAAW,WAAW,MAAM;AACvC,SAAO,WAAW,SAAS,UAAU,MAAM,IAAI;AAE/C,QAAM,OAAO,OAAO,MAAM;AAC1B,OAAK,EAAE,IAAI;AACX,SAAO;AACT;AAKO,IAAM,OAAN,MAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWhB,YAAY,QAA0B;AACpC,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBA,MAAM,QAAQ,SAAiB,SAAmC;AAChE,UAAM,QAAQ,MAAM,KAAK,wBAAwB,OAAO;AAExD,QAAI;AACF,YAAM,WAA2B,MAAM,KAAK,OAAO,KAAK;AAAA,QACtD,wBAAwB,KAAK,aAAa,OAAO;AAAA,MACnD;AAEA,aAAO,OAAO,SAAS,MAAM;AAAA,IAC/B,SAAS,KAAK;AACZ,UAAI,KAAK,sBAAsB,GAAG,EAAG,QAAO,OAAO,CAAC;AAEpD,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,MAAM,cAAc,SAA0C;AAC5D,UAAM,QAAQ,MAAM,KAAK,wBAAwB,OAAO;AACxD,UAAM,WAA8B,MAAM,KAAK,OAAO,KAAK;AAAA,MACzD,oCAAoC,KAAK;AAAA,IAC3C;AAGA,UAAM,UAAU,QAAQ,OAAO,KAAK;AACpC,UAAM,QAAQ,QAAQ,UAAU,QAAQ,KAAK;AAC7C,UAAM,WAAW,MAAM,EAAE;AAEzB,WAAO;AAAA,MACL,MAAM,SAAS,MAAM;AAAA,MACrB;AAAA,MACA,aAAa,OAAO,SAAS,MAAM,YAAY;AAAA,MAC/C,WAAW,OAAO,SAAS,MAAM,UAAU;AAAA,MAC3C,QAAQ,SAAS,MAAM,OAAO,IAAI,gBAAgB;AAAA,IACpD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAM,aAA8B;AAClC,QAAI,KAAK,YAAa,QAAO,KAAK;AAClC,UAAM,WAA8B,MAAM,KAAK,OAAO,KAAK;AAAA,MACzD;AAAA,IACF;AACA,SAAK,cAAc,SAAS;AAC5B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAc,wBAAwB,SAAmC;AACvE,WAAO,WAAW,KAAK,WAAW;AAAA,EACpC;AAAA;AAAA,EAGQ,sBAAsB,UAAwB;AACpD,QAAI,UAAU,WAAW,IAAK,QAAO;AACrC,UAAM,OAAO,UAAU;AACvB,WACE,MAAM,SAAS,WAAW,SAAS,KACnC,MAAM,SAAS,SAAS,WAAW;AAAA,EAEvC;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/bank.ts"],"sourcesContent":["import { sha256 } from \"@noble/hashes/sha2\";\nimport type { Rollup } from \"@sovereign-sdk/web3\";\nimport type { ErrorResponse } from \"./types\";\n\n// TokenHolder is an enum: { user: string } | { module: string } | { derived: string }\ntype TokenHolderPayload =\n | { user: string }\n | { module: string }\n | { derived: string };\n\ntype TokenMetadataResponse = {\n name: string;\n decimals: number;\n total_supply: string;\n supply_cap: string;\n admins: TokenHolderPayload[];\n};\n\nexport type TokenMetadata = {\n name: string;\n decimals: number;\n totalSupply: bigint;\n supplyCap: bigint;\n admins: string[];\n};\n\nfunction getHolderAddress(holder: TokenHolderPayload): string {\n if (\"user\" in holder) return holder.user;\n if (\"module\" in holder) return holder.module;\n return holder.derived;\n}\n\ntype GasTokenIdPayload = {\n token_id: string;\n};\n\ntype BalancePayload = {\n amount: string;\n token_id: string;\n};\n\n/**\n * Derives a token ID from the originator, token name, and decimals.\n *\n * Matches the Rust `get_token_id` implementation: SHA-256 hash of\n * originator bytes + token name bytes + decimals byte, with the\n * last byte of the hash replaced by the decimals value.\n *\n * @param originator - The raw bytes of the token originator (e.g. decoded address bytes)\n * @param tokenName - The name of the token\n * @param decimals - The number of decimal places for the token\n * @returns The 32-byte token ID\n */\nexport function getTokenId(\n originator: Uint8Array,\n tokenName: string,\n decimals: number,\n): Uint8Array {\n const nameBytes = new TextEncoder().encode(tokenName);\n\n const buffer = new Uint8Array(originator.length + nameBytes.length + 1);\n buffer.set(originator, 0);\n buffer.set(nameBytes, originator.length);\n buffer[originator.length + nameBytes.length] = decimals;\n\n const hash = sha256(buffer);\n hash[31] = decimals;\n return hash;\n}\n\n/**\n * Bank class for interacting with the Sovereign SDK Bank module.\n */\nexport class Bank {\n // biome-ignore lint/suspicious/noExplicitAny: types arent used\n private readonly rollup: Rollup<any, any>;\n private _gasTokenId?: string;\n\n /**\n * Creates a new Bank instance.\n *\n * @param rollup - The rollup instance to use for HTTP requests\n */\n // biome-ignore lint/suspicious/noExplicitAny: types arent used\n constructor(rollup: Rollup<any, any>) {\n this.rollup = rollup;\n }\n\n /**\n * Gets the balance of a specific token for a given address.\n *\n * If no token ID is provided, the gas token balance will be returned.\n *\n * @param address - The address to query the balance for\n * @param tokenId - Optional token ID. If not provided, uses the gas token\n * @returns Promise resolving to the token balance as a bigint\n * @throws {SovereignClient.APIError} When the request fails for reasons other than missing account\n * @example\n * ```typescript\n * const bank = new Bank(rollup);\n * const balance = await bank.balance(\"0x123...\");\n * console.log(`Balance: ${balance}`);\n *\n * // Query specific token balance\n * const tokenBalance = await bank.balance(\"0x123...\", \"token_123\");\n * ```\n */\n async balance(address: string, tokenId?: string): Promise<bigint> {\n const token = await this.tokenIdOrElseGasTokenId(tokenId);\n\n try {\n const response: BalancePayload = await this.rollup.http.get(\n `/modules/bank/tokens/${token}/balances/${address}`,\n );\n\n return BigInt(response.amount);\n } catch (err) {\n if (this.isMissingAccountError(err)) return BigInt(0);\n\n throw err;\n }\n }\n\n /**\n * Gets the metadata of a specific token.\n *\n * If no token ID is provided, returns the metadata of the gas token.\n *\n * @param tokenId - Optional token ID. If not provided, uses the gas token\n * @returns Promise resolving to the token metadata\n * @throws {SovereignClient.APIError} When the request fails\n * @example\n * ```typescript\n * const bank = new Bank(rollup);\n * const metadata = await bank.tokenMetadata();\n * console.log(`Token name: ${metadata.name}, decimals: ${metadata.decimals}`);\n *\n * // Query specific token metadata\n * const tokenMeta = await bank.tokenMetadata(\"token_123\");\n * ```\n */\n async tokenMetadata(tokenId?: string): Promise<TokenMetadata> {\n const token = await this.tokenIdOrElseGasTokenId(tokenId);\n // Decimals are resolved server-side, so the gas token's authoritative decimals (which are not\n // encoded in its id) are handled by the rollup rather than reconstructed here.\n const response: TokenMetadataResponse = await this.rollup.http.get(\n `/modules/bank/tokens/${token}/metadata`,\n );\n\n return {\n name: response.name,\n decimals: response.decimals,\n totalSupply: BigInt(response.total_supply),\n supplyCap: BigInt(response.supply_cap),\n admins: response.admins.map(getHolderAddress),\n };\n }\n\n /**\n * Gets the gas token identifier.\n *\n * The gas token ID is cached after the first request to avoid repeated API calls.\n *\n * @returns Promise resolving to the gas token ID as a string\n * @throws {Error} When the gas token ID cannot be retrieved\n * @example\n * ```typescript\n * const bank = new Bank(rollup);\n * const gasTokenId = await bank.gasTokenId();\n * console.log(`Gas token ID: ${gasTokenId}`);\n * ```\n */\n async gasTokenId(): Promise<string> {\n if (this._gasTokenId) return this._gasTokenId;\n const response: GasTokenIdPayload = await this.rollup.http.get(\n \"/modules/bank/tokens/gas_token\",\n );\n this._gasTokenId = response.token_id;\n return this._gasTokenId;\n }\n\n private async tokenIdOrElseGasTokenId(tokenId?: string): Promise<string> {\n return tokenId ?? this.gasTokenId();\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: todo\n private isMissingAccountError(anyError: any): boolean {\n if (anyError?.status !== 404) return false;\n const body = anyError?.error as ErrorResponse;\n return (\n body?.message?.startsWith(\"Balance\") &&\n body?.message?.endsWith(\"not found\")\n );\n }\n}\n"],"mappings":";AAAA,SAAS,cAAc;AA0BvB,SAAS,iBAAiB,QAAoC;AAC5D,MAAI,UAAU,OAAQ,QAAO,OAAO;AACpC,MAAI,YAAY,OAAQ,QAAO,OAAO;AACtC,SAAO,OAAO;AAChB;AAuBO,SAAS,WACd,YACA,WACA,UACY;AACZ,QAAM,YAAY,IAAI,YAAY,EAAE,OAAO,SAAS;AAEpD,QAAM,SAAS,IAAI,WAAW,WAAW,SAAS,UAAU,SAAS,CAAC;AACtE,SAAO,IAAI,YAAY,CAAC;AACxB,SAAO,IAAI,WAAW,WAAW,MAAM;AACvC,SAAO,WAAW,SAAS,UAAU,MAAM,IAAI;AAE/C,QAAM,OAAO,OAAO,MAAM;AAC1B,OAAK,EAAE,IAAI;AACX,SAAO;AACT;AAKO,IAAM,OAAN,MAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWhB,YAAY,QAA0B;AACpC,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBA,MAAM,QAAQ,SAAiB,SAAmC;AAChE,UAAM,QAAQ,MAAM,KAAK,wBAAwB,OAAO;AAExD,QAAI;AACF,YAAM,WAA2B,MAAM,KAAK,OAAO,KAAK;AAAA,QACtD,wBAAwB,KAAK,aAAa,OAAO;AAAA,MACnD;AAEA,aAAO,OAAO,SAAS,MAAM;AAAA,IAC/B,SAAS,KAAK;AACZ,UAAI,KAAK,sBAAsB,GAAG,EAAG,QAAO,OAAO,CAAC;AAEpD,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,MAAM,cAAc,SAA0C;AAC5D,UAAM,QAAQ,MAAM,KAAK,wBAAwB,OAAO;AAGxD,UAAM,WAAkC,MAAM,KAAK,OAAO,KAAK;AAAA,MAC7D,wBAAwB,KAAK;AAAA,IAC/B;AAEA,WAAO;AAAA,MACL,MAAM,SAAS;AAAA,MACf,UAAU,SAAS;AAAA,MACnB,aAAa,OAAO,SAAS,YAAY;AAAA,MACzC,WAAW,OAAO,SAAS,UAAU;AAAA,MACrC,QAAQ,SAAS,OAAO,IAAI,gBAAgB;AAAA,IAC9C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAM,aAA8B;AAClC,QAAI,KAAK,YAAa,QAAO,KAAK;AAClC,UAAM,WAA8B,MAAM,KAAK,OAAO,KAAK;AAAA,MACzD;AAAA,IACF;AACA,SAAK,cAAc,SAAS;AAC5B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAc,wBAAwB,SAAmC;AACvE,WAAO,WAAW,KAAK,WAAW;AAAA,EACpC;AAAA;AAAA,EAGQ,sBAAsB,UAAwB;AACpD,QAAI,UAAU,WAAW,IAAK,QAAO;AACrC,UAAM,OAAO,UAAU;AACvB,WACE,MAAM,SAAS,WAAW,SAAS,KACnC,MAAM,SAAS,SAAS,WAAW;AAAA,EAEvC;AACF;","names":[]}
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "@sovereign-sdk/modules",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "A package providing convenient helpers for interacting with core Sovereign SDK modules",
5
- "repository": "github:Sovereign-Labs/sovereign-sdk",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/Sovereign-Labs/sovereign-sdk.git"
8
+ },
6
9
  "files": [
7
10
  "dist",
8
11
  "README.md",
@@ -22,7 +25,7 @@
22
25
  "tsup": "^8.3.0",
23
26
  "typescript": "^5.6.3",
24
27
  "vitest": "4.0.7",
25
- "@sovereign-sdk/signers": "^0.5.2"
28
+ "@sovereign-sdk/signers": "^0.6.0"
26
29
  },
27
30
  "peerDependencies": {
28
31
  "tsup": "^8.3.0",
@@ -34,8 +37,8 @@
34
37
  "dependencies": {
35
38
  "@noble/hashes": "^1.5.0",
36
39
  "bech32": "^2.0.0",
37
- "@sovereign-sdk/web3": "^0.14.0",
38
- "@sovereign-sdk/utils": "^0.0.4"
40
+ "@sovereign-sdk/web3": "^0.15.0",
41
+ "@sovereign-sdk/utils": "^0.1.0"
39
42
  },
40
43
  "scripts": {
41
44
  "build": "tsup",