The deposited tokens can now be delegated to your validator.
## Script constants, repeated here for ease of referenceEXO_ETH_ADDR=0x83E6850591425e3C1E263c054f4466838B9Bd9e4WST_ETH_ADDR=0xB82381A3fBD3FaFA77B3a7bE693342618240067bGATEWAY_ADDR=0xDf9caDCfb027d9f6264Ecd5eAEc839a8335d8520## User choices, (some) repeated hereETH_PRIVATE_KEY=0xabcde.....ETH_ADDRESS=0x123....# The token to delegateTOKEN=$EXO_ETH_ADDR# Quantity of token to deposit (without the 1e18 multiplier)# Decimal values are supportedQUANTITY=0.5# Endpoint for Ethereum RPCETHEREUM_RPC_URL=https://rpc.ankr.com/eth_sepolia# Derived value, depending on $ACCOUNT_KEY_NAME and $HOMEDIR in prior stepsEXO_ADDRESS=$(exocored--home $HOMEDIR keysshow-a $ACCOUNT_KEY_NAME)BOOTSTRAPPED=$(castcall--rpc-url $ETHEREUM_RPC_URL $GATEWAY_ADDR "bootstrapped() returns (bool)")if [ "$BOOTSTRAPPED"="true" ]; then# Generate the LZ messsage to calculate the fees DELEGATE_PREFIX="0x03" TOKEN_B32=$(cast2b $TOKEN) ETH_ADDRESS_B32=$(cast2b $ETH_ADDRESS) EXO_ADDRESS_BYTES=$(castfu $EXO_ADDRESS) QUANTITY_B32=$(printf"%064s" $(cast2h $(cast2w $QUANTITY) |cut-c3-) |tr' ''0') LZ_MESSAGE=$(castch $DELEGATE_PREFIX $TOKEN_B32 $ETH_ADDRESS_B32 $EXO_ADDRESS_BYTES $QUANTITY_B32) VALUE=$(castcall--rpc-url $ETHEREUM_RPC_URL $GATEWAY_ADDR "quote(bytes)" $LZ_MESSAGE)else# No LZ fees, since we are in Bootstrap mode VALUE=0fi# Execute the transactioncastsend--rpc-url $ETHEREUM_RPC_URL \ $GATEWAY_ADDR \"delegateTo(string,address,uint256)" \ $EXO_ADDRESS \ $TOKEN \ $(cast2w $QUANTITY) \--private-key $ETH_PRIVATE_KEY \--value $(cast2d $VALUE)
If the above transaction was executed after the network bootstrap phase, again, LayerZeroScan may be used to query the status of the message. In addition, the withdrawable amount will change on Exocore once the message is delivered.
ETH_LZ_ID=40161 # Sepolia, where our $GATEWAY_ADDR lives
STAKER_ID=$(echo "${ETH_ADDRESS}" | tr '[:upper:]' '[:lower:]')_$(printf '0x%x\n' "${ETH_LZ_ID}")
# our public endpoint is available below, but you can use your own node too
EXOCORE_COS_GRPC_URL=https://api-cosmos-grpc.exocore-restaking.com:443
exocored query assets QueStakerAssetInfos \
$STAKER_ID --node $EXOCORE_COS_GRPC_URL \
--output json | jq
# looks like this
{
"asset_infos": [
{
"asset_id": "0x83e6850591425e3c1e263c054f4466838b9bd9e4_0x9ce1",
"info": {
// Total deposited is still the same
"total_deposit_amount": "5000000000000000000",
// But the "free" deposit is reduced because it is delegated
"withdrawable_amount": "0",
// This is the amount being undelegated, same as before
"pending_undelegation_amount": "0"
}
}
]
}
Marking a delegation as self-delegation (Post Bootstrap Phase)
This step only applies if the validator registration was done after the Bootstrap phase ended. Otherwise, it is assumed that the delegations made by the Ethereum address, which sent the validator creation transaction, are self-delegations for the validator. Most validators will need to undertake this step.
Send the transaction to associate the two addresses: each such association indicates that the delegations made by the Ethereum address are to be considered as self-delegations for the validator.
To prevent slashing for downtime, it is recommended to send the transaction below only after the node is synced.
# User inputs, the first two of which were previously providedETH_PRIVATE_KEY=0xabcdefg...ETH_ADDRESS=0xabcd...# ConstantsEXO_ETH_RPC_URL=https://api-eth.exocore-restaking.comEXO_COS_GRPC_URL=https://api-cosmos-grpc.exocore-restaking.com:443ETH_LZ_ID=40161# Sepolia# Address of gateway derived from the params of x/assetsEXO_GATEWAY_ADDR=$(exocored query assets Params --output json --node $EXO_COS_GRPC_URL | jq -r .params.exocore_lz_app_address | cast 2a)
# The validator gets only their EXO_ADDRESS funded using the faucet, while the ETH_ADDRESS (if it is different) may not have funds to pay for gas.
ETH_ADDR_BALANCE_IN_HUA=$(castbalance $ETH_ADDRESS --rpc-url $EXO_ETH_RPC_URL)# The faucet gives out 1exo, of which we will transfer 0.5exo (and retain the rest) to the ETH equivalent address. Note that the ETH equivalent address may be the same as the exo1 address if the same mnemonic is used; hence, the transfer would be pointless. To avoid spending gas on that transfer, we check the balance of the destination account first.
AMOUNT_TO_TRANSFER_IN_HUA=$(cast2w0.5ether)if [ "$ETH_ADDR_BALANCE_IN_HUA"-lt"$AMOUNT_TO_TRANSFER_IN_HUA" ]; then# execute the transfer via cast (using exocored is also possible) EXO_PRIVATE_KEY=$(exocored--home $HOMEDIR keysunsafe-export-eth-key $ACCOUNT_KEY_NAME)castsend--rpc-url $EXO_ETH_RPC_URL \ $ETH_ADDRESS \--value $AMOUNT_TO_TRANSFER_IN_HUA \--private-key $EXO_PRIVATE_KEYfi# Send the transaction for the associationcastsend--rpc-url $EXO_ETH_RPC_URL \ $EXO_GATEWAY_ADDR \"associateOperatorWithEVMStaker(uint32, string)" \ $ETH_LZ_ID \ $(exocored--home $HOMEDIR keysshow-a $ACCOUNT_KEY_NAME) \--private-key $ETH_PRIVATE_KEY
To check whether the above transaction went through, we can query the association.
ETH_LZ_ID=40161# Sepolia, where our $GATEWAY_ADDR livesSTAKER_ID=$(echo"${ETH_ADDRESS}"|tr'[:upper:]''[:lower:]')_$(printf'0x%x\n'"${ETH_LZ_ID}")# our public endpoint is available below, but you can use your own node tooEXOCORE_COS_GRPC_URL=https://api-cosmos-grpc.exocore-restaking.com:443exocoredquerydelegationQueryAssociatedOperatorByStaker \ $STAKER_ID --node $EXOCORE_COS_GRPC_URL \--outputjson|jq# expected output{"operator":"<your_exo_addr>"}
The validator should now be considered "eligible" to be part of the active validator set if the self-delegation exceeds 1,000 USD. The inclusion in the set happens in the beginning of the next epoch, which may be at most 1 hour from now. Only the top 50 validators (by total stake = self-stake + delegated stake) are included in the validator set.