スケールドUI金額統合ガイド

SolanaでスケールドUI金額拡張機能をサポートする

背景

Scaled UI Amount拡張機能により、トークン発行者はユーザーのトークン残高のUI金額を計算する際に使用される乗数を指定できます。これにより発行者はリベーシングトークンを作成したり、株式分割などを可能にしたりできます。この拡張機能は、利子付きトークン拡張機能と同様に、純粋に見た目上のUI金額を提供するため、チームは良好なユーザー体験を提供するために追加作業を行う必要があります。基礎となる計算や送金はすべてプログラム内の生の金額を使用して行われます。

リソース:

要約

  • エンドユーザーは可能な限り、トークン価格、トークン残高、トークン金額にUIAmount(生の金額 × 乗数)を使用するべきです
  • dAppやサービスプロバイダーはすべての計算に生の金額と非スケール価格を使用し、ユーザー向けの表示時に変換するべきです
  • より簡単な統合のために、スケールされた金額と非スケールの金額の両方の履歴価格フィードを提供する必要があります
  • 正確な履歴データのために、過去の乗数値にアクセスできるようにする必要があります

用語の定義

  • 乗数:UI Amount計算に使用される静的で更新可能な乗数
  • UIAmount:乗数 × 生の金額(別名:スケールされた金額)
  • 生の金額:金額(別名:非スケールの金額)

現在の残高

表示用の現在の金額

  • Scaled UI Amount拡張機能を使用するトークンの金額をエンドユーザーに表示する場合は、以下のいずれかを使用するべきです:
    • UIAmount/UIAmountString(推奨
    • 生の金額 × 乗数の手動計算
    • トークンの小数点以下の桁数に基づいてこの値を切り捨てることをお勧めします。
      • 例:yUSDが6桁の小数点を持ち、ユーザーのUIAmountが1.123456789の場合、「1.123456」と表示するべきです

このデータの入手先:

  • ユーザーのリアルタイム残高については、getTokenAccountBalanceまたはgetAccountInfoを呼び出すことで上記の金額に関する最新情報を取得できます
  • 任意の金額のUI金額を知る必要がある場合は、 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"
}
} */

現在の金額の更新

発行者はいつでも乗数を更新できるため、アカウント残高を最新の状態に保つために定期的にポーリングすることを検討できます。発行者がこの乗数を1日に複数回更新することはほとんどありません。将来の日付に乗数が設定されている場合は、この更新時間に自動的にポーリングすることができます

トランザクションでのトークン金額(転送/スワップなど)

  • ユーザーはスケーリングされた「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 */
  • ユーザーが残高の「最大」または「すべて」でアクションを実行するよう要求した場合、アプリは合計生金額を使用する必要があります。これにより、ダストが残らないようにします。
    • オプション:「最大」が使用されたときにアカウントを自動的に閉じて、ユーザーのストレージデポジットを返金することを検討できます

トークン価格

  • トークン価格は可能な限り常にスケーリングされた価格として表示されるべきです。
  • 価格フィードサービスプロバイダー(オラクルなど)である場合は、スケーリングされた価格と非スケーリングされた価格の両方を公開する必要があります。
    • 可能な限り、スケーリングされたUI金額拡張の複雑さを抽象化するSDK/APIを提供してください。

現在の乗数

  • 現在の乗数はいつでもトークンミントからgetAccountInfoを呼び出すことで読み取ることができます。さらに、将来の乗数が設定されている場合、この情報もトークンミントから入手できます。UXの混乱を避けるため、この乗数を表示しないことをお勧めします。
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アマウント拡張機能を理解していないウォレットやアプリケーションは、非スケーリング価格 * 生の金額を乗算することで、アクティビティの正確な合計価格を表示します。
  • ただし、非スケーリング価格が表示されるため、ユーザーに混乱を招く可能性があります。
  • チームがスケーリングされたUIアマウント拡張機能を使用するトークンと互換性のあるdappsに更新することを奨励し、このプロセス中のサポートを喜んで提供します。

プラットフォーム別の推奨統合優先事項

一般要件

要件説明優先度
UiAmountを使用したユーザーアクションのサポートUiAmountが有効になっているアプリ全体で、すべてのユーザーアクションはUiAmountで入力する必要があります。アプリでUiAmountが表示されていない場合、アプリが更新されるまで生の金額を使用する必要があります。P0

ウォレット

要件説明優先度
スケーリングされた残高の表示スケーリングされた金額(uiAmount)を主要な残高として表示する。P0
トークン送金のサポートエンドユーザーはスケーリングされた残高(生の金額 * 残高)で送金額を入力する必要がある。P0
スポット価格の表示ユーザー向けにスケーリングされた価格を表示するP0
取引履歴のメタデータ可能な限り、各送金のスケーリングされた金額(UIAmount)を表示する。P1
取引履歴での乗数更新の表示乗数の更新が発生した場合、獲得した金額を含めてこれをユーザーの取引履歴にイベントとして表示するP2
価格履歴グラフの表示価格グラフにスケーリングされた価格を反映させるP1
オンボーディング/ツールチップスケーリングされたui amount拡張機能を使用するトークンについて、ユーザーを教育するためのツールチップまたはオンボーディングを提供するP2

エクスプローラー

要件説明優先度
トークン詳細ページの拡張スケーリングされた時価総額や現在の乗数などのメタデータを表示するP0
残高のスケーリングされた残高の表示現在の残高にスケーリングされた残高(UiAmount)を表示する。P0
取引のスケーリングされた残高の表示過去の取引の送金額にスケーリングされた残高(UiAmount)を表示する。P0
取引のスケーリングされた価格の表示過去の取引のスケーリングされた価格を表示するP1
乗数更新取引の適切な解析と表示乗数更新に関する詳細を適切に表示するP2

市場データアグリゲーター(例:CoinGecko)

要件説明優先度
スケール済みデータ用APIの更新時間の経過に伴う乗数変更とスケール済み価格フィードを含むようにAPI機能を拡張する。P0
スケール調整を含む総供給量総供給量と時価総額を表示する際、スケール済み残高を考慮するP0
過去の価格追跡時間の経過に伴うスケール済み価格を使用した過去の価格チャートを提供する。P1
過去の乗数追跡利子付きトークンの乗数更新の履歴マーカーを提供する。P2
教育コンテンツまたは説明スケール済みトークンの仕組みを説明する簡単な説明やツールチップを含める。P2

価格フィードプロバイダー

要件説明優先度
スケール済みと非スケール価格フィードスケール済みと非スケールの両方の価格フィードを提供する。P0
過去の乗数データ過去の乗数変更を含むAPIを提供する。P0
過去の価格データスケール済みと非スケールの両方の金額に基づく過去の価格を含むAPIを提供する。P0

DEX(分散型取引所)

要件説明優先度
リベース済みトークン残高の表示UI上で取引または流動性提供のためのスケール済み残高を表示する(バックエンドは生の金額を使用可能)。P0
トークンアクションのサポートエンドユーザーはUiAmount残高(乗数 * 生の金額)でアクション量を入力する必要がある。P0
価格フィードの適応現在の価格を表示するために価格フィードが使用される場所では、エンドユーザーにスケール済み価格を提供する。P1
価格履歴グラフの表示価格グラフにスケール済み価格を反映させるP1

Is this page helpful?