4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Berachain Reward Vaultsのためのガバナンスプロポーザルの作成 [Berachain翻訳]

Last updated at Posted at 2024-09-11

本記事は下記の翻訳となります。
『Creating a Governance Proposal for Berachain Reward Vaults』

image.png

Berachain の Proof-of-Liquidity コンセンサス メカニズムの最大の利点の 1 つは、プロトコルが Berachain Governance Tokens($BGT)の発行を通じて、自身の流動性をブートストラップできることです。この$BGTの蓄積プロセスは、RewardsVaultsスマートコントラクトで表現される Reward Vaults を通じて行われます。

*注意:** 私たちのテストネットで、Berps Rewards Vaultスマートコントラクトがどのように機能しているかを確認できます。*

Reward Vaults に$BGTが蓄積されるプロセスには、バリデーターがブロックを提案するために選ばれ、選ばれた時に$BGTを報酬として受け取り、その$BGTの大部分を BeraChef と呼ばれる分配コントラクトを通じて 1 つまたは複数の Reward Vaults に分配することが含まれます。バリデーターの報酬は、ブロックを提案する時点で彼らに委任されている$BGTの量によって影響を受けます。

$BGT でできること

$BGTには主に 3 つの機能があります:

  1. $BERAを得るためにバーンする
  2. バリデーターに委任して報酬を増やす
  3. ガバナンスで提案を行ったり投票したりするために使用する

BEX の Rewards Vault 例

BEXは AMM プロトコルであり、Berachain ネイティブの PoL dApp の 1 つで、複数の Reward Vaults を持っています。ユーザーがホワイトリストに登録されたプールに流動性を提供するたびに、彼らの総貢献度に比例した LP トークンを受け取ります。

例えば、ユーザーがHONEY <> WBTC Poolに流動性を追加すると、その見返りとして **$HONEY-WBTC LP トークン** を受け取ります。

$HONEY-WBTC Reward Vault

その後、LP トークンを BEX の$HONEY-WBTC Rewards Vault にステーキングすることができます。LP トークンがステーキングされている間、バリデーターが Rewards Vault に向けて$BGTの発行を行うと、時間の経過とともに$BGTが蓄積されていきます。これが BEX がユーザーに対して、プロトコルに流動性を提供するよう促すインセンティブの仕組みです。

プロトコルのトークン(ここでは$BBT、Black Bera Token とします)の取引流動性に BEX を活用する場合、$WBERAとペアにすると、ユーザーは流動性を提供する際に$BBT-WBERA-LPトークンを受け取ります。この LP トークンがホワイトリストに登録されている場合(つまり、そのトークンをステーキング用に受け入れる Rewards Vault がある場合)、$BGT報酬の対象となり、Proof of Liquidity を通じてプロトコルの流動性をブートストラップできます。

これらの Rewards Vault はRewardsVault Factoryによって作成されます。これは Berachain 上のスマートコントラクトで、Rewards Vault の作成を担当します。

自分で RewardsVault を作成することはパーミッションレスであり誰でも可能ですが、バリデーターがあなたの Rewards Vault に$BGTを発行するためには、まずガバナンスプロポーザルとして提出し、承認を受けてBGT Stationに Rewards Vault を追加する必要があります。プロポーザルが承認されると、あなたの Rewards Vault は「friendOfTheChef」となり、バリデーターが$BGTを発行できる適格な Rewards Vault のコレクションに承認されます。このプロセスは「ホワイトリスト登録」とも呼ばれます。

Berachain のホワイトリスト登録のためのガバナンスプロセスを理解する

これから構築するものを理解するために、reward vault の作成、プロポーザルの作成、プロポーザルへの投票、reward vault の有効化というガバナンスプロセスについて説明しましょう。

ガバナンスの要件

成功するプロポーザルを作成する前に、考慮すべきいくつかの要件とステップがあります。

  1. min 1000 $BGT - すべてのプロポーザルには最低 1000 $BGTが必要です。これは直接獲得するか、他の人に提案者へ$BGTを委任してもらうことで満たすことができます。

  2. Delegated/Self-Delegated Voting Power (委任された/自己委任された voting power) - 最低$BGTが満たされている場合、個人所有の$BGTで提案したり既存のプロポーザルに投票したりするには、それを提案者(提案者自身の場合も含む)に委任する必要があります。

  3. Quorum Met(定足数の達成) - 「賛成」票の過半数が 20 億の定足数を満たす必要があります。これはメインネットに近づくにつれて変更される可能性がありますが、テストネットでは、Berachain チームに定足数達成の支援を求めることができます。

ガバナンスのライフサイクル

以下は、ガバナンスプロセスのタイムライン全体を通じてプロポーザルが経る異なる状態を表しています。


Lifecycle of a Berachain Governance Proposal

1. プロポーザル作成(保留状態)

  • 待機期間:投票開始まで 3 時間
  • この状態ではプロポーザルをキャンセルすることも可能

2. 投票開始(アクティブ状態)

  • 期間:3 時間
  • BGT 保有者が投票を行う
  • 定足数を満たす必要あり:最低 20 億 BGT

3. 投票終了

  • プロポーザルは成功または否決される
  • 否決された場合:そのように記録され、懸念事項に対処した後に新たなプロポーザルを作成可能

4. タイムロック(成功した場合)

  • 3 時間のタイムロック遅延を伴うキューに入る

5. 実行または期限切れ

  • 実行:Berachain に承認されたガバナンス EOA のみが実行可能
  • 期限切れ:期間内に実行されなかった場合

注意: 時間枠はプレースホルダーであり、メインネットローンチに近づくにつれて変更される可能性があります

reward vault 用の BEX プールの作成

このチュートリアルでは、ユーザーがステーキングすることで$BGTの発行を受け取る資格を得られる LP トークンを受け入れる reward vault を作成します。この reward vault を LP トークンと共に作成したら、ガバナンスに提出して Berachef の「フレンド」として追加することができます。

まず、BEXプールとそのステーキングトークンを作成します。


Example of Pool Creation on BEX

新しいプールを作成するには、あなたのプロトコルトークン(この場合は新しく作成された$BBTというトークン)と$WBERAトークンを提供して、BEX 上にカスタムプールを作成する必要があります。

プールが作成され、預入が行われると、LP トークンを受け取るはずです。さらに預入を行うと、より多くの LP トークンが発行されます。

デプロイされたプールと LP トークンの例

reward vault とガバナンスプロポーザル作成のためのプロジェクトセットアップ

$BBT <> $WBERA BEX プールとそれに対応する LP トークン($BBT-WBERA-LP)ができたので、次は Berachef に reward vault を追加するガバナンスプロポーザルを作成する前に、reward vault を作成する必要があります。

以下の作業を行うスクリプトを実行するプロジェクトを作成します:

  1. LP トークン用の reward vault を作成する
  2. 新しい reward vault を Berachef に追加するためのプロポーザルを Berachain ガバナンスに提出する
  3. reward vault に対して投票を行う
  4. reward vault を Berachef に追加して有効化する

プロジェクトの要件

スクリプトを構築するために、以下のものがコンピューターにインストールされているか、これらの要件の一部を満たしていることを確認してください。

  • NVM または NodeJS v20.16.0 以上
  • $BGTが蓄積されているウォレット
  • トランザクションを処理するための$BERAトークン

1. 新しい JavaScript プロジェクトの初期化

LP トークン用の reward vault を作成する前に、開発環境をセットアップしましょう。ターミナルを開き、以下の手順に従って始めてください:

mkdir berachain-rewards-vault
cd berachain-rewards-vault
npm init -y

2. 依存関係をインストールする

# FROM: ./berachain-rewards-vault

npm install ethers dotenv yargs

3. .gitignore ファイルを作成する

# FROM: ./berachain-rewards-vault

echo "node_modules\n.env" > .gitignore

4. ABI ファイルのセットアップ

プロジェクトのルートディレクトリに abi フォルダを作成してください:

# FROM: ./berachain-rewards-vault

mkdir abi

以下の JSON ファイルをダウンロードし、このフォルダに追加する必要があります:

  • ERC20.json - ABI ファイル:LP トークンの ABI です。これは単なる一般的な ERC20 トークンです。
  • BerachainRewardsVaultFactory.json - ABI ファイル:Berachain reward vault ファクトリーコントラクトの ABI です。
  • BerachainGovernance.json - ABI ファイル:Berachain ガバナンスコントラクトの ABI です。これは、ガバナンスプロポーザルの提出、投票、実行を行うメインのコントラクトです。
  • BGT.json - ABI ファイル$BGTの ABI です。
  • BeraChef.json - ABI ファイル:BeraChef の ABI です。これは$BBT <> $WBERA LP トークンの reward vault をホワイトリストに登録するコントラクトです。
  • BerachainRewardsVault.json - ABI ファイル:reward vault の ABI です。

5. 環境変数のセットアップ

次に、プロジェクトのルートに.envファイルを作成し、以下の内容を追加します:

ルートディレクトリから以下のコマンドを実行してください:

cat << EOF > .env
RPC=https://bartio.rpc.berachain.com/
PRIVATE_KEY=your_private_key_here
FACTORY_ADDRESS=0x2B6e40f65D82A0cB98795bC7587a71bfa49fBB2B
LP_TOKEN_ADDRESS=insert_your_lp_token_address_here
GOVERNANCE_ADDRESS=0xE3EDa03401Cf32010a9A9967DaBAEe47ed0E1a0b
BERACHEF_ADDRESS=0xfb81E39E3970076ab2693fA5C45A07Cc724C93c2
BGT_ADDRESS=0xbDa130737BDd9618301681329bF2e46A016ff9Ad
EOF

これらは追加した値です。

  • RPC - Public RPC または自分の RPC に置き換えてください
  • PRIVATE_KEY - BGT を保有しているあなたのウォレット
  • FACTORY_ADDRESS - bArtio reward vault Factroy のアドレス
  • LP_TOKEN_ADDRESS - あなたの LP トークンのアドレス
  • GOVERNANCE_ADDRESS - Berachain のガバナンスコントラクトのアドレス。BeraChef のupdateFriendsOfTheChefを呼び出す排他的な権利を持っています
  • BERACHEF_ADDRESS - BeraChef のアドレス。ホワイトリストに登録された reward vault とバリデータの設定を保存するコントラクトです
  • BGT_ADDRESS - Berachain ガバナンストークンのアドレス

PRIVATE_KEYLP_TOKEN_ADDRESS(例:$BBT-WBERA-LP)を実際の値に置き換えてください。

6. ガバナンススクリプトの作成とセットアップ

プロジェクトのルートにgovernance.jsという新しいファイルを作成し、以下のコードを追加してください:
File: ./governance.js

// Import required libraries
const ethers = require('ethers');
require('dotenv').config();

// Import ABI (Application Binary Interface) for various contracts
const BeraChefABI = require('./abi/BeraChef.json');
const BerachainGovernanceABI = require('./abi/BerachainGovernance.json');
const BGTABI = require('./abi/BGT.json');
const BerachainRewardsVaultABI = require('./abi/BerachainRewardsVault.json');
const ERC20ABI = require('./abi/ERC20.json');
const BerachainRewardsVaultFactoryABI = require('./abi/BerachainRewardsVaultFactory.json');

// Set up the Ethereum provider using the RPC URL from the .env file
const provider = new ethers.JsonRpcProvider(`${process.env.RPC}`, {
  chainId: 80084, // Chain ID for Berachain
  name: 'Berachain',
  ensAddress: null,
});

// Initialize the wallet using the private key from the .env file
let wallet;
try {
  wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
} catch (error) {
  console.error('Error creating wallet:', error.message);
  process.exit(1);
}

// Helper function to create contract instances
function createContract(address, abi, signer) {
  return new ethers.Contract(ethers.getAddress(address), abi, signer);
}

// Create instances of various contracts
const governance = createContract(process.env.GOVERNANCE_ADDRESS, BerachainGovernanceABI, wallet);
const beraChef = createContract(process.env.BERACHEF_ADDRESS, BeraChefABI, wallet);
const bgt = createContract(process.env.BGT_ADDRESS, BGTABI, wallet);
const factory = createContract(
  process.env.FACTORY_ADDRESS,
  BerachainRewardsVaultFactoryABI,
  wallet
);
const token = createContract(process.env.LP_TOKEN_ADDRESS, ERC20ABI, wallet);
let rewardsVault; // This will be initialized later when creating or retrieving a vault

7. helper 関数を追加する

次に、helper 関数をコピー&ペーストしてください:

File: ./governance.js

// Function to check the current state of a proposal
async function checkProposalState(proposalId) {
  // Get the numerical state of the proposal
  const state = await governance.state(proposalId);
  // Define an array of state names corresponding to their numerical values
  const stateNames = [
    'Pending',
    'Active',
    'Canceled',
    'Defeated',
    'Succeeded',
    'Queued',
    'Expired',
    'Executed',
  ];
  // Return both the numerical state and its corresponding name
  return { state, stateName: stateNames[state] };
}

// Function to determine the next stage in the governance process
async function getNextStage(currentState) {
  // Define the order of stages in the governance process
  const stageOrder = ['Pending', 'Active', 'Succeeded', 'Queued', 'Executed'];
  // Find the index of the current state in the stage order
  const currentIndex = stageOrder.indexOf(currentState);
  // Return the next stage if it exists, otherwise return 'End'
  return currentIndex < stageOrder.length - 1 ? stageOrder[currentIndex + 1] : 'End';
}

// Function to ensure the user has sufficient voting power to create a proposal
async function ensureSufficientVotingPower() {
  // Get the user's BGT balance
  const balance = await bgt.balanceOf(wallet.address);
  console.log('BGT balance:', balance.toString());

  // Check who the current delegatee is for the user's BGT
  const currentDelegatee = await bgt.delegates(wallet.address);
  console.log('Current delegatee:', currentDelegatee);

  // Get the user's current voting power
  const votingPower = await governance.getVotes(
    wallet.address,
    (await provider.getBlockNumber()) - 1
  );
  console.log('Your voting power:', votingPower.toString());

  // Get the proposal threshold (minimum voting power required to create a proposal)
  const proposalThreshold = await governance.proposalThreshold();
  console.log('Proposal threshold:', proposalThreshold.toString());

  // If voting power is less than the threshold
  if (votingPower < proposalThreshold) {
    // If BGT is not self-delegated, delegate it to self
    if (currentDelegatee !== wallet.address) {
      console.log('Delegating all BGT to self...');
      await (await bgt.delegate(wallet.address)).wait();
      console.log('Delegation complete');
    } else {
      // If already self-delegated but still not enough voting power
      console.log('Already delegated to self, but still not enough voting power');
      console.log('Please acquire more BGT tokens to meet the proposal threshold');
      return false;
    }
  }

  // Check updated voting power after potential delegation
  const updatedVotingPower = await governance.getVotes(
    wallet.address,
    (await provider.getBlockNumber()) - 1
  );
  console.log('Updated voting power:', updatedVotingPower.toString());

  // If still not enough voting power, return false
  if (updatedVotingPower < proposalThreshold) {
    console.log('Voting power is still less than proposal threshold, cannot create proposal');
    return false;
  }

  // Sufficient voting power achieved
  return true;
}

// Function to check if a proposal with given parameters already exists
async function checkExistingProposal(targets, values, calldatas, descriptionHash) {
  // Generate a proposal ID based on the given parameters
  const proposalId = await governance.hashProposal(targets, values, calldatas, descriptionHash);
  try {
    // Try to get the state of the proposal
    const state = await governance.state(proposalId);
    // If state is not 3 (Defeated), the proposal exists and is not defeated
    return state !== 3;
  } catch (error) {
    // If the error indicates the proposal doesn't exist, return false
    // Otherwise, propagate the error
    return error.reason === 'GovernorNonexistentProposal(uint256)' ? false : Promise.reject(error);
  }
}

8. Rewards Vault を作成するためのコードを追加する

File: ./governance.js

// Function to get an existing rewards vault or create a new one
async function getOrCreateVault() {
  console.log('Checking for existing rewards vault...');
  try {
    // Check if a vault already exists for the token
    const existingVaultAddress = await factory.getVault(process.env.LP_TOKEN_ADDRESS);

    // If a vault exists (address is not zero)
    if (existingVaultAddress !== ethers.ZeroAddress) {
      console.log('A rewards vault already exists for this token.');
      console.log(`Existing rewards vault address: ${existingVaultAddress}`);

      // Provide instructions to view vault details
      console.log('\nTo view details about the existing vault:');
      console.log('1. Go to https://bartio.beratrail.io');
      console.log(`2. Search for the rewards vault address: ${existingVaultAddress}`);
      console.log('3. Look for the "Create Rewards Vault" method in the transaction history');

      console.log('\nUsing the existing vault for this operation.');
      console.log(
        '\nAdd this rewards vault address to your .env file under REWARDS_VAULT_ADDRESS:'
      );
      console.log(`REWARDS_VAULT_ADDRESS=${existingVaultAddress}`);

      // Create a contract instance for the existing vault
      rewardsVault = new ethers.Contract(existingVaultAddress, BerachainRewardsVaultABI, wallet);
      return existingVaultAddress;
    }

    // If no existing vault, create a new one
    console.log('No existing vault found. Creating new rewards vault...');
    const tx = await factory.createRewardsVault(process.env.LP_TOKEN_ADDRESS);
    console.log('Transaction sent. Waiting for confirmation...');
    const receipt = await tx.wait();
    console.log('Rewards vault created. Transaction hash:', receipt.transactionHash);
    console.log();

    // Get the address of the newly created vault
    const newVaultAddress = await factory.getVault(process.env.LP_TOKEN_ADDRESS);
    console.log('New rewards vault created at:', newVaultAddress);

    // Provide instructions to view new vault details
    console.log('\nTo view details about the new vault:');
    console.log('1. Go to https://bartio.beratrail.io');
    console.log(`2. Search for the rewards vault address: ${newVaultAddress}`);
    console.log('3. Look for the "Create Rewards Vault" method in the transaction history');
    console.log('\nAdd this rewards vault address to your .env file under REWARDS_VAULT_ADDRESS:');
    console.log(`REWARDS_VAULT_ADDRESS=${newVaultAddress}`);

    // Create a contract instance for the new vault
    rewardsVault = new ethers.Contract(newVaultAddress, BerachainRewardsVaultABI, wallet);
    return newVaultAddress;
  } catch (error) {
    console.error('Error getting or creating rewards vault:', error);
    throw error;
  }
}

// Main function to handle command-line arguments
async function main() {
  const args = process.argv.slice(2);
  const flag = args[0];

  switch (flag) {
    case '--create-vault':
      // Call getOrCreateVault when the --create-vault flag is used
      await getOrCreateVault();
      break;
    // ... other cases ...
  }
}

main().catch((error) => {
  console.error(error);
  process.exit(1);
});

LP_TOKEN_ADDRESSPRIVATE_KEY.envファイルに追加したら、以下のコードをgovernance.jsファイルに貼り付け、スクリプトを実行して reward vault を作成してください:

# FROM: ./berachain-rewards-vault

node governance.js --create-vault

このコマンドは、私たちの LP トークン用の新しい reward vault を作成し、作成された vault のアドレスをログに記録します。


Output of node governance.js --create-vault command

File: ./.env
この新しい値を .envファイルに追加してください:

REWARDS_VAULT_ADDRESS=value from terminal

注意: あなたの reward vault のアドレスは、このスクリーンショットで見られるものとは異なります。

ガバナンスプロポーザルの提出

reward vault を作成したので、今度はそれをホワイトリストに登録するためのガバナンスプロポーザルを提出する時です。このプロセスのために新しいスクリプトを作成します。

1. 環境のセットアップ

このプロセスの時点で、あなたの.envファイルには以下の値が含まれているはずです:
File: ./.env

RPC=https://bartio.rpc.berachain.com/
PRIVATE_KEY=your_private_key
FACTORY_ADDRESS=0x2B6e40f65D82A0cB98795bC7587a71bfa49fBB2B
LP_TOKEN_ADDRESS=your_lp_token_address
GOVERNANCE_ADDRESS=0xE3EDa03401Cf32010a9A9967DaBAEe47ed0E1a0b
BERACHEF_ADDRESS=0xfb81E39E3970076ab2693fA5C45A07Cc724C93c2
BGT_ADDRESS=0xbDa130737BDd9618301681329bF2e46A016ff9Ad
REWARDS_VAULT_ADDRESS=your_rewards_vault_address

2. Proposal 関数を作成する

同じファイルgovernance.jsで、関数getOrCreateVaultの下に次の関数を追加します。

File: ./governance.js

async function getOrCreateVault() {
  // previous function we implemented
  // ...
}

//🚨COPY THIS FUNCTION🚨
async function createProposal(targets, values, calldatas, description) {
  // Generate a hash of the proposal description
  const hash = ethers.id(description);

  // Check if a proposal with these parameters already exists
  const proposalExists = await checkExistingProposal(targets, values, calldatas, hash);

  if (proposalExists) {
    // If the proposal exists, get its ID
    const proposalId = await governance.hashProposal(targets, values, calldatas, hash);
    // Check the current state of the existing proposal
    const { stateName } = await checkProposalState(proposalId);
    // Determine the next stage in the proposal process
    const nextStage = await getNextStage(stateName);

    // Log information about the existing proposal
    console.log('\nA proposal with these parameters already exists.');
    console.log(`Proposal ID: ${proposalId.toString()}`);
    console.log(`Current state: ${stateName}`);

    // Inform about the next stage or if it's the final stage
    if (nextStage !== 'End') {
      console.log(`Next stage: ${nextStage}`);
    } else {
      console.log('This is the final stage of the proposal.');
    }

    // Provide instructions to add the proposal ID to the .env file
    console.log('\nAdd this proposal ID to your .env file under PROPOSAL_ID:');
    console.log(`PROPOSAL_ID=${proposalId.toString()}`);

    return proposalId.toString();
  }

  try {
    // If no existing proposal, create a new one
    console.log('Creating new proposal...');
    const tx = await governance.propose(targets, values, calldatas, description);
    const receipt = await tx.wait();
    console.log('Proposal transaction confirmed. Transaction hash:', receipt.transactionHash);
    console.log();

    // Get the ID of the newly created proposal
    const proposalId = await governance.hashProposal(targets, values, calldatas, hash);
    console.log('New proposal created with ID:', proposalId.toString());

    // Provide instructions to add the new proposal ID to the .env file
    console.log('\nAdd this proposal ID to your .env file under PROPOSAL_ID:');
    console.log(`PROPOSAL_ID=${proposalId.toString()}`);

    return proposalId.toString();
  } catch (error) {
    // Handle any errors that occur during proposal creation
    console.error('Error creating proposal:', error);
    if (error.error?.data) {
      try {
        console.error('Decoded error:', governance.interface.parseError(error.error.data));
      } catch (parseError) {
        console.error('Could not parse error. Raw error data:', error.error.data);
      }
    }
    throw error;
  }
}

3. 3. main 関数にコマンドを追加

次に、governance.js ファイルの main 関数に、以下の switch case を--create-vault ケースの直後に追加してください。これはガバナンスプロポーザルの作成を処理します:

File: ./governance.js

async function main() {
  // Get command-line arguments, skipping the first two (node and script name)
  const args = process.argv.slice(2);
  // The first argument is our flag/command
  const flag = args[0];

  switch (flag) {
    case '--create-vault':
      // If the flag is to create a vault, call the getOrCreateVault function
      await getOrCreateVault();
      break;
    //🚨COPY THIS CASE🚨
    case '--create-proposal':
      // Check if the user has sufficient voting power to create a proposal
      if (!(await ensureSufficientVotingPower())) return;

      // Get or create a rewards vault
      const vaultAddress = await getOrCreateVault();

      // Get the address of the BeraChef contract
      const beraChefAddress = await beraChef.getAddress();

      // Set up the proposal parameters
      const targets = [beraChefAddress]; // The contract to call
      const values = [0]; // No BERA being sent with the call
      // Encode the function call to updateFriendsOfTheChef
      const calldatas = [
        beraChef.interface.encodeFunctionData('updateFriendsOfTheChef', [vaultAddress, true]),
      ];
      const description = 'Update friends of the chef'; // Description of the proposal

      // Create the proposal with the specified parameters
      await createProposal(targets, values, calldatas, description);
      break;
    //🚨END COPY HERE🚨
  }
}

このmain関数の新しいスイッチケースにより、新しく作成した Rewards Vault を"friends of the chef"に追加するためのガバナンスプロポーザルを作成できるようになります。十分な voting power があるかをチェックし、vault アドレスを取得(または作成)し、必要なパラメータを使ってcreateProposal関数を呼び出します。

4. create-proposal コマンドを実行する

main関数に--create-proposalケースを追加したので、Rewards Vault を BeraChef に追加するためのガバナンスプロポーザルを作成するコマンドを実行できます。ターミナルで以下を実行してください:

# FROM: ./berachain-rewards-vault

node governance.js --create-proposal

このコマンドは、あなたの Rewards Vault をホワイトリストに追加するための新しいガバナンスプロポーザルを作成します。スクリプトは Proposal ID を出力します。今後のステップのために、この ID を.envファイルに追加する必要があります。

コマンドを実行すると、以下のような出力が表示されるはずです:


Output of the node governance.js --create-proposal command

出力の指示に従って、.envファイルを更新してください。PROPOSAL_IDの値を含む新しい行を追加します:
File: ./.env

PROPOSAL_ID=your_proposal_id

このPROPOSAL_IDは、投票、queueing(キューイング)、提案の実行など、ガバナンスプロセスの後続のステップで使用されます。これは、あなたの Vault を BeraChef に追加するためのガバナンスプロポーザルの ID を表しています。

5. 投票と実行のための関数を作成する

以下の関数をgovernance.jsファイルのcreateProposal関数の下に追加してください:

File: ./governance.js

// Function to cast a vote on a proposal
async function castVote(proposalId) {
  // Check if the wallet has already voted
  const hasVoted = await governance.hasVoted(proposalId, wallet.address);
  if (hasVoted) {
    console.log('Vote already cast for this proposal. Proceeding to next steps.');
    return;
  }

  console.log('Casting vote...');
  try {
    // Cast a vote in favor of the proposal (1 = yes)
    const voteTx = await governance.castVote(proposalId, 1);
    const receipt = await voteTx.wait();
    console.log('Vote cast successfully. Transaction hash:', receipt.transactionHash);
  } catch (error) {
    console.error('Error casting vote:', error);
    if (error.error?.data) {
      try {
        console.error('Decoded error:', governance.interface.parseError(error.error.data));
      } catch (parseError) {
        console.error('Could not parse error. Raw error data:', error.error.data);
      }
    }
    throw error;
  }
}

// Function to execute a queued proposal
async function executeProposal(proposalId) {
  console.log('Executing proposal...');
  try {
    const executeTx = await governance.execute(proposalId);
    const receipt = await executeTx.wait();
    console.log('Proposal executed successfully. Transaction hash:', receipt.transactionHash);
  } catch (error) {
    console.error('Error executing proposal:', error);
    throw error;
  }
}

// Function to cancel a proposal
async function cancelProposal(proposalId) {
  console.log('Cancelling proposal...');
  try {
    const cancelTx = await governance.cancel(proposalId);
    const receipt = await cancelTx.wait();
    console.log('Proposal cancelled successfully. Transaction hash:', receipt.transactionHash);
  } catch (error) {
    console.error('Error cancelling proposal:', error);
    if (error.error?.data) {
      try {
        console.error('Decoded error:', governance.interface.parseError(error.error.data));
      } catch (parseError) {
        console.error('Could not parse error. Raw error data:', error.error.data);
      }
    }
    throw error;
  }
}

これらの関数をガバナンスプロセスの異なる段階で処理します:

  • castVote: この関数を使用してプロポーザルに投票できます。まず、すでに投票したかどうかをチェックし、まだ投票していない場合は「賛成」票(1で表される)を投じます。

  • executeProposal: プロポーザルがキューに入れられ、タイムロック期間が経過した後、この関数を呼び出してプロポーザルを実行できます。これは、ガバナンスプロセスで提案された変更を実施する最終ステップです。

  • cancelProposal: プロポーザルが実行のためにキューに入れられる前にキャンセルすることができます。これは、間違ったアドレスや説明を入力してしまった場合や、新しいプロポーザルを作成したい場合に使用します。

次に、governance.jsファイルのmain関数を以下のコードに置き換えてください:

File: ./governance.js

async function main() {
  // Get command-line arguments, skipping the first two (node and script name)
  const args = process.argv.slice(2);
  // The first argument is our flag/command
  const flag = args[0];

  // Get the proposal ID from the environment variables
  const proposalId = process.env.PROPOSAL_ID;

  switch (flag) {
    case '--create-vault':
      // Create or retrieve an existing rewards vault
      await getOrCreateVault();
      break;

    case '--create-proposal':
      // Check if there's an existing proposal
      if (proposalId) {
        const { stateName } = await checkProposalState(proposalId);
        // Only allow creating a new proposal if the current one is defeated
        if (stateName !== 'Defeated') {
          console.log(
            `A proposal (ID: ${proposalId}) already exists and is in ${stateName} state.`
          );
          console.log('You can only create a new proposal if the current one is defeated.');
          return;
        }
      }
      // Ensure the user has enough voting power to create a proposal
      if (!(await ensureSufficientVotingPower())) return;
      // Get or create a rewards vault
      const vaultAddress = await getOrCreateVault();
      // Get the BeraChef contract address
      const beraChefAddress = await beraChef.getAddress();
      // Set up proposal parameters
      const targets = [beraChefAddress];
      const values = [0];
      const calldatas = [
        beraChef.interface.encodeFunctionData('updateFriendsOfTheChef', [vaultAddress, true]),
      ];
      const description = 'Update friends of the chef';
      // Create the proposal
      await createProposal(targets, values, calldatas, description);
      break;

    case '--vote':
      // Ensure a proposal ID is set
      if (!proposalId) {
        console.error('Please set the PROPOSAL_ID in your .env file');
        return;
      }
      // Check the current state of the proposal
      const voteState = await checkProposalState(proposalId);
      // Only allow voting if the proposal is in the Active state
      if (voteState.stateName !== 'Active') {
        console.log(
          `Proposal is in ${voteState.stateName} state. Please wait until it reaches Active state to vote.`
        );
        return;
      }
      // Cast a vote on the proposal
      await castVote(proposalId);
      break;

    case '--execute':
      // Ensure a proposal ID is set
      if (!proposalId) {
        console.error('Please set the PROPOSAL_ID in your .env file');
        return;
      }
      // Check the current state of the proposal
      const executeState = await checkProposalState(proposalId);
      // Only allow execution if the proposal is queued
      if (executeState.stateName !== 'Queued') {
        console.log(
          `Proposal is in ${executeState.stateName} state. Please wait until it reaches Queued state to execute.`
        );
        return;
      }
      // Execute the proposal
      await executeProposal(proposalId);
      break;

    case '--check-state':
      // Ensure a proposal ID is set
      if (!proposalId) {
        console.error('Please set the PROPOSAL_ID in your .env file');
        return;
      }
      // Check and display the current state of the proposal
      const { stateName } = await checkProposalState(proposalId);
      console.log(`Current proposal state: ${stateName}`);
      // Get and display the next stage of the proposal
      const nextStage = await getNextStage(stateName);
      if (nextStage !== 'End') {
        console.log(`Next stage: ${nextStage}`);
      } else {
        console.log('This is the final stage of the proposal.');
      }
      break;

    case '--cancel':
      // Ensure a proposal ID is set
      if (!proposalId) {
        console.error('Please set the PROPOSAL_ID in your .env file');
        return;
      }
      // Check the current state of the proposal
      const cancelState = await checkProposalState(proposalId);
      // Allow cancellation if the proposal is in a cancellable state
      if (!['Pending', 'Active', 'Succeeded'].includes(cancelState.stateName)) {
        console.log(`Proposal is in ${cancelState.stateName} state and cannot be cancelled.`);
        return;
      }
      // Cancel the proposal
      await cancelProposal(proposalId);
      break;

    default:
      // If an invalid flag is provided, show usage instructions
      console.log('Please provide a valid flag:');
      console.log('--create-vault: Create a new rewards vault');
      console.log('--create-proposal: Create a new governance proposal');
      console.log('--vote: Vote on the proposal specified in .env');
      console.log('--queue: Queue the proposal specified in .env');
      console.log('--execute: Execute the proposal specified in .env');
      console.log('--check-state: Check the current state of the proposal');
  }
}

// Run the main function and catch any errors
main().catch((error) => {
  console.error(error);
  process.exit(1);
});

main関数のこれらの新しいケースにより、.envファイルのPROPOSAL_IDを使用してガバナンスプロポーザルに投票し、実行することができます。各ケースは、それぞれのアクションを実行する前にPROPOSAL_IDが設定されているかどうかをチェックするので、.envが更新されていることを確認してください。

プロポーザルの進捗をモニタリングする

プロポーザルを作成したら、その進捗をモニタリングし、適切なタイミングで行動を起こす必要があります。以下のコマンドを使用して、プロポーザルの現在の状態を確認してください:

# FROM: ./berachain-rewards-vault

node governance.js --check-state

プロポーザルの状態に応じて、コマンドを実行するか、プロセスの次の段階を待つ必要があります。

  1. Pending: This is the initial state. This stage takes three hours and you cannot vote or execute your proposal in this stage.

  2. Active: This is the voting period. Cast your vote by running:

  3. Pending(保留中): これは初期状態です。この段階は 3 時間続き、この間はプロポーザルに投票したり実行したりすることはできません。

  4. Active(アクティブ): これは投票期間です。以下のコマンドを実行して投票してください:

# FROM: ./berachain-rewards-vault

node governance.js --vote
  1. Succeeded(成功): プロポーザルが投票期間を通過しました。これで実行のためにキューに入れられました。

  2. Queued(キュー済み): プロポーザルはタイムロック期間にあります。この期間が終了すると、プロポーザルを実行できます。この時点で、あなたの LP Rewards Vault が Berachef に正式に追加されます:

# FROM: ./berachain-rewards-vault

node governance.js --execute
  1. Executed(実行済み): プロポーザルが正常に実施されました。これ以上のアクションは必要ありません。

プロポーザルの進捗を監視するために、--check-stateコマンドを頻繁に使用してください。

⚠️ 警告: ガバナンスプロセスに含まれる待機期間のため、このプロセスには数時間かかる場合があります。辛抱強く待ち、状態を確認し続けてください。

もしプロポーザルがDefeated(否決)またはExpired(期限切れ)の状態になった場合は、以下のコマンドを実行して新しいプロポーザルを作成する必要があります:

# FROM: ./berachain-rewards-vault

node governance.js --create-proposal

新しいプロポーザルを作成する場合は、.envファイルのPROPOSAL_IDを削除し、新しいものを追加することを忘れないでください。

プロポーザルのキャンセル

プロセスのどの段階でも、以下のコマンドを実行することでプロポーザルをキャンセルすることができます。

# FROM: ./berachain-rewards-vault

node governance.js --cancel

これは取り消しできないアクションですが、新しい ID でガバナンスプロポーザルを作成することはできます。

完全な GitHub コードリポジトリ

最終的なコードを確認したり、他のガイドを見たい場合は、Berachain Guides GitHub Repositoryをチェックしてください。

以下のコマンドを実行してください:

git clone https://github.com/berachain/guides.git
cd guides/apps/berachain-governance-proposal
npm install

README に記載されている指示に従って、このスクリプトをダウンロードし実行してください。

次のステップは?

🛠️ もっとビルドしたいですか?

Berachain 上でさらに開発を進めたり、より多くの例を見たい場合は、Berachain GitHub Guides Repo をご覧ください。NextJS、Hardhat、Viem、Foundry など、様々な実装例が幅広く用意されています。

開発者サポートをお探しですか?

質問をするために、Berachain Discordに参加し、開発者チャンネルをチェックしてください。



【Sunrise とは】
Sunrise は Proof of Liquidity(PoL)と Fee Abstraction(手数料抽象化)を備えたデータ可用性レイヤーです。 私たちは DA の体験を再構築し、多様なエコシステムからのモジュラー型流動性を活用してロールアップを立ち上げています。

【Social Links】

【お問合せ】
Sunrise へのお問い合わせはこちらから 👉 Google Form

1080x360.jpeg

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?