Integrate Operations

Stake B3TR with Referral

Stakes the specified amount of B3TR tokens with a referral ID (your app ID):

Only apps that fill in the referral parameters correctly will be able to vote for your app when voting in VeBetterDAO.

Take VeStation as an example. The VeBetterDAO url of VeStation is https://governance.vebetterdao.org/apps/0x7eb0a488c56d79efe6fe74d10ddf89d3411f2b13b29c002ea5e50a1d3b89cdfb So the appId of VeStation is 0x7eb0a488c56d79efe6fe74d10ddf89d3411f2b13b29c002ea5e50a1d3b89cdfb

import find from "lodash.find";
import ABI_ERC20 from "path/to/erc20.json";
import ABI_VEDELEGATE from "path/to/VeDelegateV2.json";

import type { Connex } from "@vechain/connex";
type Receipt = Connex.Thor.Transaction.Receipt | null;

async function stakeB3TRWithReferral(amount: string, referral: string) {
  // Parameters:
  // - amount: The amount of B3TR to stake (in standard units, not wei)
  // - referral: The referral ID (typically the app ID)
  
  const baseUnit = 10n ** BigInt(TOKEN_B3TR.decimals);
  const amountInWei = (BigInt(amount) * baseUnit).toString();

  // Approve B3TR spending
  const approveB3trMethod = connex.thor
    .account(TOKEN_B3TR.address)
    .method(find(ABI_ERC20, { name: "approve" }));
  const approveB3trClause = approveB3trMethod.asClause(VEDELEGATE_CONTRACT_ADDRESS, amountInWei);

  // Stake B3TR with referral
  const stakeB3trMethod = connex.thor
    .account(VEDELEGATE_CONTRACT_ADDRESS)
    .method(find(ABI_VEDELEGATE, { name: "depositB3TRWithReferral" }));
  const stakeB3trClause = stakeB3trMethod.asClause(amountInWei, referral);

  try {
    const tx = await connex.vendor
      .sign("tx", [approveB3trClause, stakeB3trClause])
      .comment(`Stake ${amount} B3TR with referral`)
      .request();
    
    // Wait for transaction confirmation
    const receipt: Receipt = await poll(() => connex.thor.transaction(tx.txid).getReceipt());

    if (receipt) {
      const isSuccess = receipt.reverted === false;
      return {
        success: isSuccess,
        txid: tx.txid,
        receipt
      };
    } else {
      throw new Error(`Failed to fetch receipt for transaction: ${tx.txid}`);
    }
  } catch (err) {
    console.error("Staking failed:", err);
    throw err;
  }
}

Important Notes:

  • This function creates a multi-clause transaction that first approves token spending and then stakes the tokens

  • Users must have sufficient B3TR balance before staking

  • The referral parameter should be your app's unique identifier

  • Users will need to sign the transaction through their wallet

  • Always check the user's balance before calling this function to ensure they have enough tokens

  • Consider implementing a loading state while waiting for transaction confirmation

Unstake B3TR

Withdraws the specified amount of staked B3TR tokens:

import find from "lodash.find";
import ABI_VEDELEGATE from "path/to/VeDelegateV2.json";

import type { Connex } from "@vechain/connex";
type Receipt = Connex.Thor.Transaction.Receipt | null;

async function unstakeB3TR(amount: string) {
  // Parameters:
  // - amount: The amount of B3TR to unstake (in standard units, not wei)
  
  const baseUnit = 10n ** BigInt(TOKEN_B3TR.decimals);
  const amountInWei = (BigInt(amount) * baseUnit).toString();

  const unstakeB3trMethod = connex.thor
    .account(VEDELEGATE_CONTRACT_ADDRESS)
    .method(find(ABI_VEDELEGATE, { name: "withdrawInB3TR" }));
  const unstakeB3trClause = unstakeB3trMethod.asClause(amountInWei);

  try {
    const tx = await connex.vendor
      .sign("tx", [unstakeB3trClause])
      .comment(`Unstake ${amount} B3TR`)
      .request();
    
    // Wait for transaction confirmation
    const receipt: Receipt = await poll(() => connex.thor.transaction(tx.txid).getReceipt());

    if (receipt) {
      const isSuccess = receipt.reverted === false;
      return {
        success: isSuccess,
        txid: tx.txid,
        receipt
      };
    } else {
      throw new Error(`Failed to fetch receipt for transaction: ${tx.txid}`);
    }
  } catch (err) {
    console.error("Unstaking failed:", err);
    throw err;
  }
}

Important Notes:

  • Users can only unstake up to the amount they have previously staked

  • Always verify the user's staked amount before calling this function

  • The unstaked tokens will be returned to the user's wallet address

  • Consider implementing a loading state while waiting for transaction confirmation

  • After unstaking, refresh the user's balance and staked amount to show updated values

Claim Reward

Claims available rewards from a specific round:

import find from "lodash.find";
import ABI_VEDELEGATE from "path/to/VeDelegateV2.json";

import type { Connex } from "@vechain/connex";
type Receipt = Connex.Thor.Transaction.Receipt | null;

async function claimReward(roundId: string) {
  // Parameters:
  // - roundId: The ID of the round to claim rewards from
  
  const claimMethod = connex.thor
    .account(VEDELEGATE_CONTRACT_ADDRESS)
    .method(find(ABI_VEDELEGATE, { name: "claimReward" }));
  const claimClause = claimMethod.asClause(roundId);

  try {
    const tx = await connex.vendor
      .sign("tx", [claimClause])
      .comment(`Claim round ${roundId} reward`)
      .request();
    
    // Wait for transaction confirmation
    const receipt: Receipt = await poll(() => connex.thor.transaction(tx.txid).getReceipt());

    if (receipt) {
      const isSuccess = receipt.reverted === false;
      return {
        success: isSuccess,
        txid: tx.txid,
        receipt
      };
    } else {
      throw new Error(`Failed to fetch receipt for transaction: ${tx.txid}`);
    }
  } catch (err) {
    console.error("Claim reward failed:", err);
    throw err;
  }
}

Important Notes:

  • Users can only claim rewards from completed rounds

  • Always check if the user has unclaimed rewards before calling this function

  • Use getUserReward and getUserClaimedReward to determine if there are rewards to claim

  • Only call this function if the difference between total rewards and claimed rewards is greater than zero

  • After claiming, refresh the user's balance to show the updated value

  • Consider implementing a loading state while waiting for transaction confirmation

Transaction Status Management

For better user experience, track the status of ongoing transactions:

// Simple transaction status management
const [txStatus, setTxStatus] = useState({
  isPending: false,
  isSuccess: false,
  isError: false,
  txid: '',
  errorMessage: ''
});

// Example of using status in the UI
function renderTxStatus() {
  if (txStatus.isPending) {
    return <div>Transaction pending... <a href={`https://explore.vechain.org/transactions/${txStatus.txid}`} target="_blank">View on Explorer</a></div>;
  }
  if (txStatus.isSuccess) {
    return <div>Transaction successful! <a href={`https://explore.vechain.org/transactions/${txStatus.txid}`} target="_blank">View on Explorer</a></div>;
  }
  if (txStatus.isError) {
    return <div>Transaction failed: {txStatus.errorMessage}</div>;
  }
  return null;
}

Best Practices:

  • Always show transaction status to users for transparency

  • Include links to block explorers so users can verify transactions

  • Consider adding a loading spinner or progress indicator for pending transactions

  • Store transaction history for user reference

  • Implement auto-refresh of data after successful transactions

Error Handling

// Example of error handling for a staking operation
try {
  setTxStatus({ isPending: true, txid: '', isSuccess: false, isError: false, errorMessage: '' });
  const result = await stakeB3TRWithReferral('100', 'your-app-id');
  
  if (result.success) {
    setTxStatus({ 
      isPending: false,
      isSuccess: true,
      isError: false,
      txid: result.txid,
      errorMessage: ''
    });
    // Handle success (e.g., show success message, refresh balances)
  } else {
    setTxStatus({ 
      isPending: false,
      isSuccess: false,
      isError: true,
      txid: result.txid,
      errorMessage: 'Transaction reverted'
    });
  }
} catch (error) {
  setTxStatus({ 
    isPending: false,
    isSuccess: false,
    isError: true,
    txid: '',
    errorMessage: error.message || 'Unknown error occurred'
  });
  // Handle error (e.g., show error message)
}

Common Error Scenarios:

  • Insufficient balance: User doesn't have enough B3TR tokens

  • Rejection: User rejected the transaction in their wallet

  • Network issues: Connection problems during transaction

  • Smart contract errors: Various contract-specific errors

Handle these cases gracefully with user-friendly messages explaining what went wrong and how to resolve it.

Last updated