缩放 UI 数量集成指南
在 Solana 上支持缩放 UI 数量扩展
背景
缩放 UI 数量扩展允许代币发行方指定一个乘数,用于计算用户代币余额的 UI 数量。这使得发行方可以创建可调整的代币,并实现类似股票拆分的功能。与利息代币扩展类似,该扩展提供了一个纯粹的视觉 UI 数量,这意味着团队需要额外的工作来提供良好的用户体验。底层计算和转账均使用程序中的原始数量进行。
资源:
简要说明
- 最终用户应尽可能使用 UIAmount(原始数量 * 乘数)来查看代币价格、代币余额和代币数量
- dApp 和服务提供商应使用原始数量和非缩放价格进行所有计算,并在边缘为用户转换
- 历史价格数据需要同时提供缩放和非缩放数量,以便于集成
- 历史乘数值需要可访问,以确保准确的历史数据
术语定义
- 乘数:用于 UI 数量计算的静态可更新乘数
- UIAmount:乘数 * 原始数量(又称:缩放数量)
- 原始数量:数量(又称:非缩放数量)
当前余额
用于显示的当前数量
- 每当向最终用户显示使用缩放 UI 数量扩展的代币数量时,您应使用以下之一:
- UIAmount/UIAmountString(首选)
- 手动计算原始数量 * 乘数
- 我们建议根据代币的小数位数截断此值。
- 示例:如果 yUSD 有 6 位小数,且用户的 UIAmount 为 1.123456789,您应显示“1.123456”
如何获取这些数据:
- 对于用户的实时余额,您可以通过调用 getTokenAccountBalance 或 getAccountInfo 获取上述金额的更新信息。
- 如果您需要知道任意金额的 UI Amount,您可以通过调用
amountToUiAmountForMintWithoutSimulation
(web3.js v1)函数或使用 amountToUiAmount 模拟交易来进行计算。- 注意:amountToUiAmount 需要进行交易模拟,这意味着它还需要一个具有余额的有效费用支付者。因此,这不应作为获取余额的默认方式。
RPC 调用
getTokenAccountBalance
- 返回代币账户余额和铸币信息
import { address, createSolanaRpc } from "@solana/kit";const rpc_url = "https://api.devnet.solana.com";const rpc = createSolanaRpc(rpc_url);let tokenAddress = address("2uuvxpnEKw52aTqNerHiQ3WeSqZriCMNVt8LhWfrkbPk");let tokenBalance = await rpc.getTokenAccountBalance(tokenAddress).send();console.log("Token Balance:", tokenBalance);/* Token Balance: {context: { apiVersion: '2.2.14', slot: 381132711n },value: {amount: '10000000',decimals: 6,uiAmount: 20,uiAmountString: '20'}} */
getAccountInfo
- 返回账户信息和铸币信息
import { address, createSolanaRpc } from "@solana/kit";const rpc_url = "https://api.devnet.solana.com";const rpc = createSolanaRpc(rpc_url);const publicKey = address("2uuvxpnEKw52aTqNerHiQ3WeSqZriCMNVt8LhWfrkbPk");const accountInfo = await rpc.getAccountInfo(publicKey).send();console.log("Account Info:",JSON.stringify(accountInfo,(key, value) => (typeof value === "bigint" ? value.toString() : value),2));/* Account Info: {"context": {"apiVersion": "2.2.14","slot": "381133640"},"value": {"data": {"parsed": {"info": {"extensions": [{"extension": "immutableOwner"},{"extension": "pausableAccount"}],"isNative": false,"mint": "BZCd6HfTbb5ZYJ8hQsm8282tG4QzLyeqFR6tdgQA9EAG","owner": "G7ARQSUCwNrfvTDUCZvM5xdiRdBJiN3qVw2PryD8Wnib","state": "initialized","tokenAmount": {"amount": "10000000","decimals": 6,"uiAmount": 20,"uiAmountString": "20"}},"type": "account"},"program": "spl-token-2022","space": "174"},"executable": false,"lamports": "2101920","owner": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb","rentEpoch": "18446744073709551615","space": "174"}} */
更新当前金额
由于发行方可以随时更新乘数,您可以考虑偶尔轮询以保持账户余额的更新。发行方不太可能每天更新乘数超过一次。如果为未来日期设置了乘数,您可以在此更新时间自动轮询。
交易中的代币金额(转账/交换等)
- 用户应输入被解释为缩放后的“UIAmount”的金额。需要处理此金额的应用程序应将其转换为交易所需的原始代币金额。
- 如果存在舍入问题,请向下舍入,并优先保留少量的尘埃金额,而不是冒着交易失败的风险。
- 要进行此转换,您可以使用
uiAmountToAmountForMintWithoutSimulation
(web3.js v1)函数或使用 amountToUiAmount 模拟交易。
web3js-uiAmountToAmountForMintWithoutSimulation.ts
import { uiAmountToAmountForMintWithoutSimulation } from "@solana/web3.js";import { Connection, PublicKey, clusterApiUrl } from "@solana/web3.js";const connection = new Connection(clusterApiUrl("devnet"), "confirmed");const mint = new PublicKey("BZCd6HfTbb5ZYJ8hQsm8282tG4QzLyeqFR6tdgQA9EAG");const uiAmount = "20.2";const rawAmount = await uiAmountToAmountForMintWithoutSimulation(connection as unknown as Connection,mint,uiAmount);console.log("Raw Amount:", rawAmount);/* Raw Amount: 20200000 */
- 应用程序在用户请求使用“最大”或“全部”余额执行操作时,应使用总的原始金额。这可以确保不会留下任何零散余额。
- 可选:您可以考虑在使用“最大”时自动关闭账户,以退还用户的存储押金。
代币价格
- 代币价格应尽可能以缩放后的价格显示。
- 如果您是价格数据提供服务商,例如预言机,您应同时提供缩放和未缩放的价格。
- 在可能的情况下,提供一个 SDK/API,以抽象掉缩放 UI 金额扩展的复杂性。
当前乘数
- 当前乘数可以随时通过调用代币铸造合约读取。
getAccountInfo
。此外,如果设置了未来的乘数,这些信息也可以从代币铸造合约中获取。我们建议不要显示此乘数,因为这可能会让用户体验感到困惑。
import { address, createSolanaRpc } from "@solana/kit";const rpc_url = "https://api.devnet.solana.com";const rpc = createSolanaRpc(rpc_url);const publicKey = address("BZCd6HfTbb5ZYJ8hQsm8282tG4QzLyeqFR6tdgQA9EAG");const accountInfo = await rpc.getAccountInfo(publicKey, { encoding: "jsonParsed" }).send();const mintData = accountInfo.value?.data as Readonly<{parsed: {info?: {extensions: {extension: string;state: object;}[];};type: string;};program: string;space: bigint;}>;const scaledUiAmountConfig = mintData.parsed.info?.extensions?.find((extension) => extension.extension === "scaledUiAmountConfig") as Readonly<{state: {newMultiplierEffectiveTimestamp: number;newMultiplier: number;multiplier: number;};}>;const currentMultiplier =scaledUiAmountConfig?.state &&Date.now() / 1000 >=scaledUiAmountConfig.state.newMultiplierEffectiveTimestamp? scaledUiAmountConfig.state.newMultiplier: scaledUiAmountConfig.state.multiplier;console.log("Scaled UI Amount Config:", scaledUiAmountConfig);console.log("Current Multiplier:", currentMultiplier);/*Scaled UI Amount Config: {extension: 'scaledUiAmountConfig',state: {authority: 'G7ARQSUCwNrfvTDUCZvM5xdiRdBJiN3qVw2PryD8Wnib',multiplier: '2',newMultiplier: '2',newMultiplierEffectiveTimestamp: 1743000000n}}Current Multiplier: 2*/
历史数据
价格数据的历史记录
- 提供历史数据的服务应存储并展示缩放和未缩放的价格,以支持缩放 UI 金额扩展。
- 我们预计缩放金额会被更频繁地使用,因为这与传统金融世界处理与股票分割相关的图表方式一致。
金额的历史记录
- 如果您希望显示过去转账的余额,您需要访问特定 slot 的乘数。您也可以在处理交易时保存转账的 UiAmount,以避免将来进行此计算。
向后兼容性
- 默认情况下,不支持缩放 UI 数量扩展的钱包和应用程序将通过将未缩放价格乘以原始数量来显示活动的正确总价格。
- 然而,它们会显示未缩放的价格,这可能会引起一些用户的困惑。
- 我们希望这能鼓励团队更新他们的 dapp,以兼容使用缩放 UI 数量扩展的代币,并且我们很乐意在此过程中提供支持。
每个平台的推荐集成优先级
通用要求
要求 | 描述 | 优先级 |
---|---|---|
支持使用 UiAmount 的用户操作 | 当应用程序中启用了 UiAmount 时,所有用户操作都应以 UiAmount 输入。如果应用程序中未显示 UiAmount,则应使用原始数量,直到应用程序更新为止。 | P0 |
钱包
要求 | 描述 | 优先级 |
---|---|---|
显示缩放余额 | 显示缩放金额(uiAmount)作为主要余额。 | P0 |
支持代币转账 | 最终用户应以其缩放余额(原始数量 * 余额)输入转账金额。 | P0 |
显示现货价格 | 为用户显示缩放的现货价格 | P0 |
交易历史元数据 | 尽可能在每次转账中显示缩放金额(UIAmount)。 | P1 |
在交易历史中显示乘数更新 | 当发生乘数更新时,在用户的交易历史中将其显示为一个事件,包括获得的金额 | P2 |
显示价格历史图表 | 在价格图表中反映缩放价格 | P1 |
入门/工具提示 | 提供工具提示或入门指南,教育用户关于使用缩放 UI 数量扩展的代币 | P2 |
浏览器
要求 | 描述 | 优先级 |
---|---|---|
代币详情页面增强 | 显示元数据,例如总缩放市值和当前乘数 | P0 |
显示余额的缩放余额 | 显示当前余额的缩放余额(UiAmount)。 | P0 |
显示交易的缩放余额 | 显示历史交易中转账金额的缩放余额(UiAmount)。 | P0 |
显示交易的缩放价格 | 显示历史交易的缩放价格 | P1 |
正确解析并显示乘数更新交易 | 正确显示有关乘数更新的详细信息 | P2 |
市场数据聚合器(例如:CoinGecko)
要求 | 描述 | 优先级 |
---|---|---|
缩放数据的 API 更新 | 扩展 API 功能以包括随时间变化的乘数更改以及缩放价格数据流。 | P0 |
考虑缩放调整的总供应量 | 在显示总供应量和总市值时,考虑缩放余额。 | P0 |
历史价格追踪 | 提供使用缩放价格随时间变化的历史价格图表。 | P1 |
历史乘数追踪 | 提供利息代币的乘数更新历史标记。 | P2 |
教育内容或解释 | 包括简要描述或工具提示,解释缩放代币的工作原理。 | P2 |
价格数据提供商
要求 | 描述 | 优先级 |
---|---|---|
缩放和非缩放价格数据流 | 提供缩放和非缩放价格的数据流。 | P0 |
历史乘数数据 | 提供包含历史乘数更改的 API。 | P0 |
历史价格数据 | 提供基于缩放和非缩放金额的历史价格数据的 API。 | P0 |
DEXes(去中心化交易所)
要求 | 描述 | 优先级 |
---|---|---|
显示重新基准化的代币余额 | 在用户界面上显示用于交易或流动性提供的缩放余额。(后端仍可使用原始金额) | P0 |
支持代币操作 | 最终用户应使用其 UiAmount 余额(乘数 * 原始金额)输入操作金额。 | P0 |
价格馈送适配 | 在任何使用价格馈送显示当前价格的地方,向最终用户提供缩放后的价格。 | P1 |
显示价格历史图表 | 在价格图表中反映缩放后的价格 | P1 |
Is this page helpful?