Trust Score Snap:コードカラーでトランザクションを簡素化

ETHLisbonハッカソン受賞作品

8分で読める
Trust Score Snap:コードカラーでトランザクションを簡素化

MetaMask Snapsは、MetaMaskを世界で最も拡張性の高いウォレットにするためのロードマップです。開発者として、まったく新しい方法でMetaMaskに機能やAPIを追加することができます。Web3開発者はこの成長の中核を担っており、本シリーズは現在開発中の革新的なMetaMask Snapsを紹介することを目的としています。

Trust Score Snap

Snap リポジトリ: https://github.com/Tbaut/template-snap-monorepo

なぜ作ったのですか?

Ethereumでのトランザクション署名において、見て見ぬふりをされがちな問題があります。それは、ほとんどのユーザーが自分が何に署名しているのかをほとんど理解していないということです。メソッドや引数をデコードすることは良い第一歩ですが、ユーザーが悪意のあるコントラクトとやり取りする場合、表示される情報が誤解を招く可能性があります。これに対抗するために、コードカラー 🟥 🟧 🟩 を使って、非常にシンプルな方法で定量的なデータを表示することを考えました。アイデアは、複数のソースから簡単に検証できるデータに基づいてトラストスコアを表示することです。4つの基準から始めることにしました:

  • スマートコントラクトとやり取りしたユーザーの総数

  • 現在のアカウントがこのコントラクトとやり取りした頻度

  • このコントラクトの経過年数

  • このコントラクトのコードが検証済みかどうか

技術的な実装について説明していただけますか?

インサイトのメイン関数

このSnapは非常にシンプルで、拡張も容易です。これから説明する完全なコードはこちらです。

MetaMaskでインサイトタブがクリックされると、onTransaction関数が呼び出されます。その中で、考慮すると決めた各基準を提供する関数をそれぞれ呼び出します。

type ScoreResult = {
  score: number;
  description: string;
};

const onTransaction: OnTransactionHandler = async ({
  transaction,
  chainId,
}) => {
  // call all the insights criteria
  // they all return a ScoreResult
}
コピー

これらの関数は通常、APIを呼び出して結果を処理します。理解を深めるために、そのうちの一つを詳しく見てみましょう。

全体的なインタラクションに基づくトラストスコアの決定

コントラクトが受け取ったトランザクションの総数に基づいてスコアを算出する関数を詳しく見てみましょう。ここではEtherscan APIを使用していますが、同じデータを検証するために他のAPIも使用できます。

/**
 * Gets a trust score for a contract based on the overall tx count
 * 3 -> the contract had overall >=100 txs
 * 2 -> the contract had overall 50 <= tx < 99
 * 1 -> the contract had overall tx < 50
 *
 *@param options0
 * @param options0.chainId - the chain id to call the api with
 * @param options0.contractAddress - the contract address to check
 * @returns the score and a description
 */
export async function getContractTransactionCountScore({
  chainId,
  contractAddress,
}: IContractTransactionCountScore): Promise<ScoreResult> {
  // get the api url asking for the 100th transaction
  // it will return undefined if there is none
  const url100 = getEtherscanContractTxsUrl({
    txNumber: 100,
    chainId,
    contractAddress,
  });
  const { result: result100 } = await fetchUrl<EtherscanResponse>(url100);

  // if there is a 100th tx it means the contract
  // has received at least 100 txs. We have all we need
  // we can return with the best score of 3
  if (result100.length === 1) {
    return {
      score: 3,
      description: 'more than 100 txs',
    };
  }

  // this will be call if there are less than 100 txs
  // we check if there is a 50th tx.
  const url50 = getEtherscanContractTxsUrl({
    txNumber: 50,
    chainId,
    contractAddress,
  });
  const { result: result50 } = await fetchUrl<EtherscanResponse>(url50);

  // if there is a 50th tx we can return with a score of 2
  if (result50.length === 1) {
    return {
      score: 2,
      description: 'more than 50 txs',
    };
  }

  // otherwise, return with the lowest score
  return {
    score: 1,
    description: 'fewer than 50 txs',
  };
}
コピー

スコアは一律ではない

各基準のTrustResultが揃ったので、次は重み付けを適用します。実際、すべての基準のトラストスコアが同じ重みを持つべきではありません。例えば、コントラクトとの過去のトランザクション数には費用がかかります。詐欺師がこれを操作しようとすれば、ガス代としてETHを費やす必要があります。その結果、この基準は操作コストが高いと見なされ、より重要であるため、重みを3としました。

同様に、コントラクトの経過年数は攻撃者が回避することができません。高いトラストスコアを得たい場合、デプロイしてから攻撃を開始するまで待つ必要があります。これには重みを2としました。

その他の基準は相対的に弱いと見なされ、重みは1としています。

// arbitrary weight for each score
const Weights = {
  contractTransactionCountScore: 3,
  contractAgeScore: 2,
  contractVerificationScore: 1,
  contractUserTransactionScore: 1,
};

// calculate the overall trust score after applying the weights
const calculateOverallScoreWithWeight = ({
  contractTransactionCountScore,
  contractUserTransactionScore,
  contractAgeScore,
  contractVerificationScore,
}: CalculateOverallScoreWithWeightArgs) => {
  const totalWeitght = Object.values(Weights).reduce(
    (acc: number, curr: number) => acc + curr,
    0,
  );

  const overallScoreWithWeight =
    (contractUserTransactionScore.score * Weights.contractUserTransactionScore +
      contractTransactionCountScore.score *
        Weights.contractTransactionCountScore +
      contractAgeScore.score * Weights.contractAgeScore +
      contractVerificationScore.score * Weights.contractVerificationScore) /
    totalWeitght;

  return Math.floor(overallScoreWithWeight);
};
コピー

最終スコアの表示

スマートコントラクトのトラストスコアが算出できたので、シンプルな方法で表示する必要があります。MetaMaskのインサイトAPIはキーと値のペアの表示のみを許可しています。キーは太字で、値は通常のフォントウェイトで表示されます。絵文字を使用することで、ユーザーが圧倒されることなく、収集したすべての情報を表示できるインターフェースを作れると考えました。

トラストスコアを絵文字に変換する関数:

const getColor = (result: number) => {
  switch (result) {
    case 3:
      return '🟩';
    case 2:
      return '🟧';
    default:
      return '🟥';
  }
};
コピー

そして、キーと値をきれいに表示するためのハックを含むonTransaction関数のreturn部分:

return {
    insights: {
      '': `Trust score: ${getColor(overallScore)}`,
      ' ': '-----------------------------',
      'Contract popularity': `${getColor(
        contractTransactionCountScore.score,
      )} ${contractTransactionCountScore.description}`,
      'Previous interactions': `${getColor(
        contractUserTransactionScore.score,
      )} ${contractUserTransactionScore.description}`,
      'Contract age': `${getColor(contractAgeScore.score)} ${
        contractAgeScore.description
      }`,
      'Contract verification': `${getColor(contractVerificationScore.score)} ${
        contractVerificationScore.description
      }`,
    },
  };
コピー

最後に、2種類のコントラクトを呼び出すための非常にシンプルなインターフェースを作成しました。一つは非常によく知られ使用されているUniswapコントラクト、もう一つはRemixでデフォルトとして表示される非常に最近デプロイされたストレージコントラクトです。このSnapを使用してUniswapルーターコントラクトとやり取りすると、以下のように表示されます:

実装する場合の次のステップは何ですか?

これは、ユーザーに信頼感を与えたり、フィッシングを防いだりするために活用できる情報のほんの一部に過ぎません。明らかな最初のステップは、データにより多くのソースを使用することです。コントラクトの検証にはすでにSourceifyとEtherscanを使用していますが、各基準に複数のソースを使用することが望ましいでしょう。

次に、詐欺師がスコアを簡単に操作できないようにするため、より高度なアルゴリズムを適用する必要があります。例えば、コントラクトとトランザクションを行ったユニークなアカウント数をカウントすべきです。また、過去にやり取りしたアカウントの分析を行い、すべてが一つのソースから発生していないことを確認することもできます。デメリットは、このような分析を複数の信頼できるソースを使って行うことが難しくなることです。

スマートコントラクトの詐欺検出に関する興味深い研究もあり、それらを統合することができます。詳細はこちら

総じて、このハックに対して受け取った圧倒的に多くのポジティブな反応は、ユーザーがより多くの情報を求めていることを示しており、理解しやすい形で提供されていることが重要な役割を果たしたと考えています。

ご自身について少し教えていただけますか?

私はChainSafeのソフトウェアエンジニアで、以前はParityで働いていました。優れたUXに焦点を当てたプロダクト開発が大好きです。Web3はすべてが非常に複雑なため、できるだけ多くの人に理解してもらえるようにすることは大きなチャレンジであり、私にとって素晴らしい活躍の場です。この分野に携わって約5年になりますが、多くのハッカソンに参加してきたものの、ハッカーとしてではなく、ボランティア、メンター、または審査員として参加することがほとんどです。主な理由は、私が完璧主義者で、場当たり的な解決策があまり好きではないからです :)。

MetaMask Snapsを初めて知ったのはいつで、その経験はどのようなものでしたか?

興味深いことに、ChainSafeの同僚が最初期のSnapの一つ、Filecoin用のSnapを開発しました。当時コードを見たことはあったかもしれませんが、それ以上深く掘り下げませんでした。

ハッキング当時、インサイトAPIは実際には開発者に公開されていませんでした。リスボンでMetaMaskチームと話していたとき、MetaMaskにユーザーインターフェースを追加できるとは思っていませんでした。彼らはまもなくリリースされる予定で、今すぐ開発できると教えてくれました。実際、これはカスタムビルドでのみ利用可能で、ドキュメントもまだリリースされていませんでした。

MetaMask Snapsが他のウォレットと異なる点は何ですか?

タブにカスタムコードを追加しながら、そのコードが安全に実行できることを保証する機能は、膨大な範囲の機能強化への扉を開きます。これから生まれるものがとても楽しみです。

MetaMask SnapsでSnapを開発することはどのような体験ですか?

テンプレートのmonorepoのおかげで、非常にスムーズに開始できました。すべてのコードを一つのリポジトリで書け、テスト用dappとSnapの両方を一つのコマンドで起動でき、どちらも自動リフレッシュ付きというのは、素晴らしい開発者体験でした。

Snapsはあなたにとって何を意味しますか?

拡張機能への拡張機能です。どんなプロダクトもすべてのユーザーのニーズを満たすことはできません。このコンポーザブルなレイヤーを追加することは、何らかの機能を求めている人が自分で構築できる素晴らしい機会です。

MetaMask SnapsとそれがWeb3空間に解放する可能性についてどのような機会を見ていますか?

特にインサイトについて言えば、ニーズに応じた無限のカスタマイズ性を解放しています。将来的にはさらに多くのセキュリティ関連のインサイトが開発されると確信しています。

MetaMask Snapsを試したい開発者へのアドバイスはありますか?

monorepoをクローンして、非常に素早いスタートをお楽しみください。

MetaMask Snapsで開発を始める

MetaMask Snapsを始めるには:

  1. 開発者ドキュメントをご確認ください

  2. MetaMask Flaskをインストールしてください

  3. MetaMask Snapsガイドをご覧ください

  4. TwitterGitHubディスカッションDiscordでつながり続けましょう

お近くで開催される次のハッカソンでチームに注目してください!Happy BUIDLing ⚒️

免責事項:MetaMask Snapsは一般的にConsensys Software以外のサードパーティによって開発されています。サードパーティが開発したMetaMask Snapsの使用は、お客様自身の判断とリスクで行われ、そのような活動から生じる損失や損害についてはお客様が単独で責任を負うことに同意したものとみなされます。Consensysはサードパーティが開発したMetaMask Snapsについて、口頭または書面による明示的または黙示的な保証を行わず、サードパーティが開発したSnapsに関するすべての責任を否認します。ブロックチェーン関連ソフトウェアの使用にはリスクが伴い、MetaMask Snapsを使用する際にはそのリスクをすべて負うものとします。

AIによる翻訳です。エラーが含まれている可能性があります。常に情報を確認してください。

翻訳を評価する
  • MetaMask
    MetaMask

    ConsenSysが構築した、Web3への入口となる主要なセルフカストディ型暗号資産ウォレット。

    すべての記事を読む