Exits
Learn how to initiate the validator unstaking process
Via Intent
How voluntary exiting works
To initiate unstaking, validators are required to perform a SignedVoluntaryExit
transaction, where the transaction is signed using Luganodes' validator keys.
Exiting validators must submit a signed challenge with relevant validator information. Thereafter, the createAndSignChallenge
function generates the necessary signature for the exit API request, relying on validator indexes (found in the Get Delegation object response) and the withdrawal address.
We have created a script to perform the aforementioned operations with ease.
Script to create a Signed Challenge
// Install ethers package using a package manager (eg. NPM, YARN)
import {
BaseWallet,
SigningKey,
hashMessage,
hexlify,
toUtf8Bytes,
} from 'ethers';
const createAndSignChallenge = async (
validatorIndexes,
privateKeyOfWithdrawalAdd,
withdrawalAddress,
) => {
const wallet = new BaseWallet(new SigningKey(privateKeyOfWithdrawalAdd));
// challenge that needs to be signed
const challenge = JSON.stringify(
{
intentToExit:
'I am signing this message and requesting that Luganodes exit the following validators from the network',
validatorIndexes, // REQUIRED: list of validator indexes
date: Date.now().toString(), // optional, timestamp this was signed
address: withdrawalAddress, // REQUIRED: the signing address
},
null,
2,
);
const sign = wallet.signMessageSync(hashMessage(challenge));
console.log('signature: ', sign);
// packedChallenge
const packedChallenge = hexlify(
toUtf8Bytes(
'\u0019Ethereum Signed Message:\n' +
challenge.length.toString() +
challenge,
),
);
console.log('packed_challenge', packedChallenge);
};
await createAndSignChallenge(
validatorIndexes, //[0, 2]
privateKeyOfWithdrawalAdd,
withdrawalAddress,
);
Request Luganodes to submit exits
POST
/api/exit
Request Luganodes to exit a set of validators for a given withdrawal address. The list of validators comes from a challenge that must be signed with the withdrawal address. See the above code snippet for constructing the body for this method.
NOTE: If the challenge and signature check out, this submits the voluntary exits and will initiate the process for exiting validators. This method does not return the voluntary exits.
Headers
api-key*
String
API Key provided by Luganodes
Query Parameters
key*
String
The public address of the withdrawal key or the controller key to exit
Request Body
challenge*
String
The packed challenge formed using above snippet
signature*
String
The packed signature formed using above snippet
{
"message": "exit request is under processing for these validators",
"validators": [
{
"pubKey": "0xa8b4286511612eccb9f5834f278300fd389d5490d2c53c7674826959a1e2113e8dad87cdc357da16f9461473d82bff73",
"validatorIndex": 1123,
"provisionId": "a62c7c43-faf7-4fd8-908e-1791c6d8c257"
}
]
}
// Example request body
{
"signature":"0xba56185cea6b6f1bd9fc7bda51656a2136e19958d79718ff13c99cec82d36a9f5280784717a0579c5a7294a3778e7d0a00039b5a51d4e85cae25ebc14130dd691c",
"challenge":"0x19457468657265756d205369676e6564204d6573736167653a0a3234347b0a202022696e74656e74546f45786974223a20224920616d207369676e696e672074686973206d65737361676520616e642072657175657374696e672074686174205374616b656420657869742074686520666f6c6c6f77696e672076616c696461746f72732066726f6d20746865206e6574776f726b222c0a20202276616c696461746f72496e6465786573223a205b0a20202020300a20205d2c0a20202264617465223a202231363838373137333039383136222c0a20202261646472657373223a2022307861303863364339653364304532384530453945663137634536373839396538453036353031644432220a7d"
}
Generate Exit Message
POST
/api/exit/message
Request Luganodes to generate an exit message for a set of validators for a given withdrawal address. This message can be broadcasted over the network through an RPC call. The list of validators comes from a challenge that must be signed with the withdrawal address.
NOTE: This method requires the exit message to be broadcasted through an RPC call. If RPC response is "null", then the exit message has been successfully broadcasted.
Headers
api-key*
String
API Key provided by Luganodes
Query Parameters
key*
String
The public address of the withdrawal key or the controller key to exit
Request Body
challenge*
String
The packed challenge formed using above snippet
signature*
String
The packed signature formed using above snippet
{
"message": "Exit message(s) created",
"result": {
"validatorIndexes": [
1792413
],
"exitMessages": [
{
"message": {
"epoch": 0,
"validator_index": 1792413
},
"signature": "0x90415852edac40501f8c233149cf2c92d1604124c9cbb9b91b4beac0fac5a0d8376b75f0a3a6b7faede07af0dce1edf705bcdf3f9d1d8a124ca73d089ec777a3c15a033c4c8c13ffd9390da0cb1c511f881f0753e6afc724b922d33a76de2d6b"
}
]
},
"page": 1,
"per_page": 100,
"total": 1,
"pages": 1
}
// Example request body
{
"signature":"0xba56185cea6b6f1bd9fc7bda51656a2136e19958d79718ff13c99cec82d36a9f5280784717a0579c5a7294a3778e7d0a00039b5a51d4e85cae25ebc14130dd691c",
"challenge":"0x19457468657265756d205369676e6564204d6573736167653a0a3234347b0a202022696e74656e74546f45786974223a20224920616d207369676e696e672074686973206d65737361676520616e642072657175657374696e672074686174205374616b656420657869742074686520666f6c6c6f77696e672076616c696461746f72732066726f6d20746865206e6574776f726b222c0a20202276616c696461746f72496e6465786573223a205b0a20202020300a20205d2c0a20202264617465223a202231363838373137333039383136222c0a20202261646472657373223a2022307861303863364339653364304532384530453945663137634536373839396538453036353031644432220a7d"
}
Via Withdrawal Address
Partial Exit Request
POST
/api/exit/partial
Client can call this API to generate an unsigned transaction to perform partial withdrawals. This unsigned transaction needs to be signed by the validator withdrawal address and broadcasted to the network.
Headers
api-key*
String
API Key provided by Luganodes
Query Parameters
key*
String
The public address of the withdrawal key or the controller key to exit
Request Body
validatorPubKey*
String
Public address of validator
amountToWithdraw*
Number
Amount of stake to be withdrawn
"message": "Partial exit transaction created",
"result": {
"validatorPubKey": "0x8e44dac3019bc8128a86b16c5e7012a4dc763718dc9a31e3b2df6c18bb72b7cf5a08cabd03fea42c9f1aa9d2f234c1f8",
"amountToWithdraw": "1",
"withdrawalAddress": "0x8C24B68141552e0844354B0b638031918CAAFaeB",
"unsignedTx": "0x02f85083088bb0368403b0be13848360bf63830186a08001b8388e44dac3019bc8128a86b16c5e7012a4dc763718dc9a31e3b2df6c18bb72b7cf5a08cabd03fea42c9f1aa9d2f234c1f8000000003b9aca00c0",
"partialExitRequestTrx": {
"data": "0x8e44dac3019bc8128a86b16c5e7012a4dc763718dc9a31e3b2df6c18bb72b7cf5a08cabd03fea42c9f1aa9d2f234c1f8000000003b9aca00",
"value": 1,
"nonce": 54,
"gasLimit": 100000,
"maxFeePerGas": "2204155747",
"maxPriorityFeePerGas": "61914643",
"type": 2,
"chainId": "560048"
}
}
}
// Example request body
{
"validatorPubKey": "0x8e44dac3019bc8128a86b16c5e7012a4dc763718dc9a31e3b2df6c18bb72b7cf5a08cabd03fea42c9f1aa9d2f234c1f8",
"amountToWithdraw": "1"
}
Types
Partial Exit Response Object
message
Transaction generation status
String
result
Result Object
Object
Result Object
validatorPubKey
Public address of validator
String
amountToWithdraw
Amount of stake to be withdrawn NOTE: Amount resulting in the validator's balance below 32 ETH is not allowed.
Object
unsignedTx
Unsigned transaction object which needs to be signed wiith the validator withdrawal address
String
withdrawalAddress
Withdrawal Address of validator
String
partialRequestTrx
Transaction Object
Object
Last updated