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
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