Staking
Truy vấn danh sách Validators hiện tại
Chúng ta có thể stake SOL và kiếm phần thưởng cho việc nâng cao an toàn mạng lưới. Để stake, bạn phải uỷ quyền SOL có các validators người sẽ thực hiện việc xử lý các giao dịch.
import { clusterApiUrl, Connection } from "@solana/web3.js";
(async () => {
const connection = new Connection(clusterApiUrl("devnet"), "confirmed");
// Get all validators, categorized by current (i.e. active) and deliquent (i.e. inactive)
const { current, delinquent } = await connection.getVoteAccounts();
console.log("current validators: ", current);
console.log("all validators: ", current.concat(delinquent));
})();
solana validators
Tạo một Stake Account
Tất cả các chỉ thị staking sẽ được đảm nhiệm bởi Stake Program. Để bắt đầu, chúng ta tạo ra một Stake Account. Stake Account được tạo ra và quản lý rất khác so với một System Account chuẩn. Thực tế, chúng ta phải đặt Stake Authority
và Withdrawal Authority
của account đó.
import {
clusterApiUrl,
Connection,
Keypair,
LAMPORTS_PER_SOL,
StakeProgram,
Authorized,
sendAndConfirmTransaction,
Lockup,
} from "@solana/web3.js";
(async () => {
// Setup our connection and wallet
const connection = new Connection(clusterApiUrl("devnet"), "confirmed");
const wallet = Keypair.generate();
// Fund our wallet with 1 SOL
const airdropSignature = await connection.requestAirdrop(
wallet.publicKey,
LAMPORTS_PER_SOL
);
await connection.confirmTransaction(airdropSignature);
// Create a keypair for our stake account
const stakeAccount = Keypair.generate();
// Calculate how much we want to stake
const minimumRent = await connection.getMinimumBalanceForRentExemption(
StakeProgram.space
);
const amountUserWantsToStake = LAMPORTS_PER_SOL / 2; // This is can be user input. For now, we'll hardcode to 0.5 SOL
const amountToStake = minimumRent + amountUserWantsToStake;
// Setup a transaction to create our stake account
// Note: `StakeProgram.createAccount` returns a `Transaction` preconfigured with the necessary `TransactionInstruction`s
const createStakeAccountTx = StakeProgram.createAccount({
authorized: new Authorized(wallet.publicKey, wallet.publicKey), // Here we set two authorities: Stake Authority and Withdrawal Authority. Both are set to our wallet.
fromPubkey: wallet.publicKey,
lamports: amountToStake,
lockup: new Lockup(0, 0, wallet.publicKey), // Optional. We'll set this to 0 for demonstration purposes.
stakePubkey: stakeAccount.publicKey,
});
const createStakeAccountTxId = await sendAndConfirmTransaction(
connection,
createStakeAccountTx,
[
wallet,
stakeAccount, // Since we're creating a new stake account, we have that account sign as well
]
);
console.log(`Stake account created. Tx Id: ${createStakeAccountTxId}`);
// Check our newly created stake account balance. This should be 0.5 SOL.
let stakeBalance = await connection.getBalance(stakeAccount.publicKey);
console.log(`Stake account balance: ${stakeBalance / LAMPORTS_PER_SOL} SOL`);
// Verify the status of our stake account. This will start as inactive and will take some time to activate.
let stakeStatus = await connection.getStakeActivation(stakeAccount.publicKey);
console.log(`Stake account status: ${stakeStatus.state}`);
})();
// Setup a transaction to create our stake account
// Note: `StakeProgram.createAccount` returns a `Transaction` preconfigured with the necessary `TransactionInstruction`s
const createStakeAccountTx = StakeProgram.createAccount({
authorized: new Authorized(wallet.publicKey, wallet.publicKey), // Here we set two authorities: Stake Authority and Withdrawal Authority. Both are set to our wallet.
fromPubkey: wallet.publicKey,
lamports: amountToStake,
lockup: new Lockup(0, 0, wallet.publicKey), // Optional. We'll set this to 0 for demonstration purposes.
stakePubkey: stakeAccount.publicKey,
});
const createStakeAccountTxId = await sendAndConfirmTransaction(
connection,
createStakeAccountTx,
[
wallet,
stakeAccount, // Since we're creating a new stake account, we have that account sign as well
]
);
console.log(`Stake account created. Tx Id: ${createStakeAccountTxId}`);
// Check our newly created stake account balance. This should be 0.5 SOL.
let stakeBalance = await connection.getBalance(stakeAccount.publicKey);
console.log(`Stake account balance: ${stakeBalance / LAMPORTS_PER_SOL} SOL`);
// Verify the status of our stake account. This will start as inactive and will take some time to activate.
let stakeStatus = await connection.getStakeActivation(stakeAccount.publicKey);
console.log(`Stake account status: ${stakeStatus.state}`);
Uỷ quyền Stake
Một khi stake account đã được cọc, Stake Authority
có thể uỷ quyền của nó cho một validator. Mỗi stake account có thể chỉ uỷ quyền cho một validator tại một thời điểm. Thêm nữa, tất cả token trong account hoặc là phải để được uỷ quyền, hoặc là phải không uỷ quyền cho bất kỳ ai. Khi đã uỷ quyền, sẽ phải tốn một và epoch để stake account có thể hoạt động.
import {
clusterApiUrl,
Connection,
Keypair,
LAMPORTS_PER_SOL,
StakeProgram,
Authorized,
sendAndConfirmTransaction,
Lockup,
PublicKey,
} from "@solana/web3.js";
(async () => {
// Setup our connection and wallet
const connection = new Connection(clusterApiUrl("devnet"), "confirmed");
const wallet = Keypair.generate();
// Fund our wallet with 1 SOL
const airdropSignature = await connection.requestAirdrop(
wallet.publicKey,
LAMPORTS_PER_SOL
);
await connection.confirmTransaction(airdropSignature);
// Create a keypair for our stake account
const stakeAccount = Keypair.generate();
// Calculate how much we want to stake
const minimumRent = await connection.getMinimumBalanceForRentExemption(
StakeProgram.space
);
const amountUserWantsToStake = LAMPORTS_PER_SOL / 2; // This is can be user input. For now, we'll hardcode to 0.5 SOL
const amountToStake = minimumRent + amountUserWantsToStake;
// Setup a transaction to create our stake account
// Note: `StakeProgram.createAccount` returns a `Transaction` preconfigured with the necessary `TransactionInstruction`s
const createStakeAccountTx = StakeProgram.createAccount({
authorized: new Authorized(wallet.publicKey, wallet.publicKey), // Here we set two authorities: Stake Authority and Withdrawal Authority. Both are set to our wallet.
fromPubkey: wallet.publicKey,
lamports: amountToStake,
lockup: new Lockup(0, 0, wallet.publicKey), // Optional. We'll set this to 0 for demonstration purposes.
stakePubkey: stakeAccount.publicKey,
});
const createStakeAccountTxId = await sendAndConfirmTransaction(
connection,
createStakeAccountTx,
[
wallet,
stakeAccount, // Since we're creating a new stake account, we have that account sign as well
]
);
console.log(`Stake account created. Tx Id: ${createStakeAccountTxId}`);
// Check our newly created stake account balance. This should be 0.5 SOL.
let stakeBalance = await connection.getBalance(stakeAccount.publicKey);
console.log(`Stake account balance: ${stakeBalance / LAMPORTS_PER_SOL} SOL`);
// Verify the status of our stake account. This will start as inactive and will take some time to activate.
let stakeStatus = await connection.getStakeActivation(stakeAccount.publicKey);
console.log(`Stake account status: ${stakeStatus.state}`);
// To delegate our stake, we first have to select a validator. Here we get all validators and select the first active one.
const validators = await connection.getVoteAccounts();
const selectedValidator = validators.current[0];
const selectedValidatorPubkey = new PublicKey(selectedValidator.votePubkey);
// With a validator selected, we can now setup a transaction that delegates our stake to their vote account.
const delegateTx = StakeProgram.delegate({
stakePubkey: stakeAccount.publicKey,
authorizedPubkey: wallet.publicKey,
votePubkey: selectedValidatorPubkey,
});
const delegateTxId = await sendAndConfirmTransaction(connection, delegateTx, [
wallet,
]);
console.log(
`Stake account delegated to ${selectedValidatorPubkey}. Tx Id: ${delegateTxId}`
);
// Check in on our stake account. It should now be activating.
stakeStatus = await connection.getStakeActivation(stakeAccount.publicKey);
console.log(`Stake account status: ${stakeStatus.state}`);
})();
// With a validator selected, we can now setup a transaction that delegates our stake to their vote account.
const delegateTx = StakeProgram.delegate({
stakePubkey: stakeAccount.publicKey,
authorizedPubkey: wallet.publicKey,
votePubkey: selectedValidatorPubkey,
});
const delegateTxId = await sendAndConfirmTransaction(connection, delegateTx, [
wallet,
]);
console.log(
`Stake account delegated to ${selectedValidatorPubkey}. Tx Id: ${delegateTxId}`
);
// Check in on our stake account. It should now be activating.
stakeStatus = await connection.getStakeActivation(stakeAccount.publicKey);
console.log(`Stake account status: ${stakeStatus.state}`);
Truy vấn các nhà uỷ quyền bằng validator
Nhiều account có thể stake và một validator cụ thể. Để truy vấn tất cả các staker, bạn sẽ sử dụng getProgramAccounts
hoặc getParsedProgramAccounts
API. Tham khảo Phần hướng dẫn cho nhiều thông tin hơn. Stake account có độ dài là 200 bytes với Voter Public Key
bắt đầu ở bytes thứ 124. Tham khảo.
import { clusterApiUrl, Connection, PublicKey } from "@solana/web3.js";
(async () => {
const STAKE_PROGRAM_ID = new PublicKey(
"Stake11111111111111111111111111111111111111"
);
const VOTE_PUB_KEY = "27MtjMSAQ2BGkXNuJEJkxFyCJT8dugGAaHJ9T7Gc6x4x";
const connection = new Connection(clusterApiUrl("mainnet-beta"), "confirmed");
const accounts = await connection.getParsedProgramAccounts(STAKE_PROGRAM_ID, {
filters: [
{
dataSize: 200, // number of bytes
},
{
memcmp: {
offset: 124, // number of bytes
bytes: VOTE_PUB_KEY, // base58 encoded string
},
},
],
});
console.log(`Accounts for program ${STAKE_PROGRAM_ID}: `);
console.log(
`Total number of delegators found for ${VOTE_PUB_KEY} is: ${accounts.length}`
);
if (accounts.length)
console.log(`Sample delegator:`, JSON.stringify(accounts[0]));
/*
// Output
Accounts for program Stake11111111111111111111111111111111111111:
Total number of delegators found for 27MtjMSAQ2BGkXNuJEJkxFyCJT8dugGAaHJ9T7Gc6x4x is: 184
Sample delegator:
{
"account": {
"data": {
"parsed": {
"info": {
"meta": {
"authorized": {
"staker": "3VDVh3rHTLkNJp6FVYbuFcaihYBFCQX5VSBZk23ckDGV",
"withdrawer": "EhYXq3ANp5nAerUpbSgd7VK2RRcxK1zNuSQ755G5Mtxx"
},
"lockup": {
"custodian": "3XdBZcURF5nKg3oTZAcfQZg8XEc5eKsx6vK8r3BdGGxg",
"epoch": 0,
"unixTimestamp": 1822867200
},
"rentExemptReserve": "2282880"
},
"stake": {
"creditsObserved": 58685367,
"delegation": {
"activationEpoch": "208",
"deactivationEpoch": "18446744073709551615",
"stake": "433005300621",
"voter": "27MtjMSAQ2BGkXNuJEJkxFyCJT8dugGAaHJ9T7Gc6x4x",
"warmupCooldownRate": 0.25
}
}
},
"type": "delegated"
},
"program": "stake",
"space": 200
},
"executable": false,
"lamports": 433012149261,
"owner": {
"_bn": "06a1d8179137542a983437bdfe2a7ab2557f535c8a78722b68a49dc000000000"
},
"rentEpoch": 264
},
"pubkey": {
"_bn": "0dc8b506f95e52c9ac725e714c7078799dd3268df562161411fe0916a4dc0a43"
}
}
*/
})();
const STAKE_PROGRAM_ID = new PublicKey(
"Stake11111111111111111111111111111111111111"
);
const VOTE_PUB_KEY = "27MtjMSAQ2BGkXNuJEJkxFyCJT8dugGAaHJ9T7Gc6x4x";
const connection = new Connection(clusterApiUrl("mainnet-beta"), "confirmed");
const accounts = await connection.getParsedProgramAccounts(STAKE_PROGRAM_ID, {
filters: [
{
dataSize: 200, // number of bytes
},
{
memcmp: {
offset: 124, // number of bytes
bytes: VOTE_PUB_KEY, // base58 encoded string
},
},
],
});
console.log(`Accounts for program ${STAKE_PROGRAM_ID}: `);
console.log(
`Total number of delegators found for ${VOTE_PUB_KEY} is: ${accounts.length}`
);
if (accounts.length)
console.log(`Sample delegator:`, JSON.stringify(accounts[0]));
/*
// Output
Accounts for program Stake11111111111111111111111111111111111111:
Total number of delegators found for 27MtjMSAQ2BGkXNuJEJkxFyCJT8dugGAaHJ9T7Gc6x4x is: 184
Sample delegator:
{
"account": {
"data": {
"parsed": {
"info": {
"meta": {
"authorized": {
"staker": "3VDVh3rHTLkNJp6FVYbuFcaihYBFCQX5VSBZk23ckDGV",
"withdrawer": "EhYXq3ANp5nAerUpbSgd7VK2RRcxK1zNuSQ755G5Mtxx"
},
"lockup": {
"custodian": "3XdBZcURF5nKg3oTZAcfQZg8XEc5eKsx6vK8r3BdGGxg",
"epoch": 0,
"unixTimestamp": 1822867200
},
"rentExemptReserve": "2282880"
},
"stake": {
"creditsObserved": 58685367,
"delegation": {
"activationEpoch": "208",
"deactivationEpoch": "18446744073709551615",
"stake": "433005300621",
"voter": "27MtjMSAQ2BGkXNuJEJkxFyCJT8dugGAaHJ9T7Gc6x4x",
"warmupCooldownRate": 0.25
}
}
},
"type": "delegated"
},
"program": "stake",
"space": 200
},
"executable": false,
"lamports": 433012149261,
"owner": {
"_bn": "06a1d8179137542a983437bdfe2a7ab2557f535c8a78722b68a49dc000000000"
},
"rentEpoch": 264
},
"pubkey": {
"_bn": "0dc8b506f95e52c9ac725e714c7078799dd3268df562161411fe0916a4dc0a43"
}
}
*/
Dừng stake
Bất kỳ lúc nào sau khi một stake account được uỷ quyền, Stake Authority
có thể được chọn để dừng account. Dừng stake có thể mất một khoảng thời gian để hoàn thành, và phải được hoàn thành trước có thể rút SOL.
import {
clusterApiUrl,
Connection,
Keypair,
LAMPORTS_PER_SOL,
StakeProgram,
Authorized,
sendAndConfirmTransaction,
Lockup,
PublicKey,
} from "@solana/web3.js";
(async () => {
// Setup our connection and wallet
const connection = new Connection(clusterApiUrl("devnet"), "confirmed");
const wallet = Keypair.generate();
// Fund our wallet with 1 SOL
const airdropSignature = await connection.requestAirdrop(
wallet.publicKey,
LAMPORTS_PER_SOL
);
await connection.confirmTransaction(airdropSignature);
// Create a keypair for our stake account
const stakeAccount = Keypair.generate();
// Calculate how much we want to stake
const minimumRent = await connection.getMinimumBalanceForRentExemption(
StakeProgram.space
);
const amountUserWantsToStake = LAMPORTS_PER_SOL / 2; // This is can be user input. For now, we'll hardcode to 0.5 SOL
const amountToStake = minimumRent + amountUserWantsToStake;
// Setup a transaction to create our stake account
// Note: `StakeProgram.createAccount` returns a `Transaction` preconfigured with the necessary `TransactionInstruction`s
const createStakeAccountTx = StakeProgram.createAccount({
authorized: new Authorized(wallet.publicKey, wallet.publicKey), // Here we set two authorities: Stake Authority and Withdrawal Authority. Both are set to our wallet.
fromPubkey: wallet.publicKey,
lamports: amountToStake,
lockup: new Lockup(0, 0, wallet.publicKey), // Optional. We'll set this to 0 for demonstration purposes.
stakePubkey: stakeAccount.publicKey,
});
const createStakeAccountTxId = await sendAndConfirmTransaction(
connection,
createStakeAccountTx,
[
wallet,
stakeAccount, // Since we're creating a new stake account, we have that account sign as well
]
);
console.log(`Stake account created. Tx Id: ${createStakeAccountTxId}`);
// Check our newly created stake account balance. This should be 0.5 SOL.
let stakeBalance = await connection.getBalance(stakeAccount.publicKey);
console.log(`Stake account balance: ${stakeBalance / LAMPORTS_PER_SOL} SOL`);
// Verify the status of our stake account. This will start as inactive and will take some time to activate.
let stakeStatus = await connection.getStakeActivation(stakeAccount.publicKey);
console.log(`Stake account status: ${stakeStatus.state}`);
// To delegate our stake, we first have to select a validator. Here we get all validators and select the first active one.
const validators = await connection.getVoteAccounts();
const selectedValidator = validators.current[0];
const selectedValidatorPubkey = new PublicKey(selectedValidator.votePubkey);
// With a validator selected, we can now setup a transaction that delegates our stake to their vote account.
const delegateTx = StakeProgram.delegate({
stakePubkey: stakeAccount.publicKey,
authorizedPubkey: wallet.publicKey,
votePubkey: selectedValidatorPubkey,
});
const delegateTxId = await sendAndConfirmTransaction(connection, delegateTx, [
wallet,
]);
console.log(
`Stake account delegated to ${selectedValidatorPubkey}. Tx Id: ${delegateTxId}`
);
// Check in on our stake account. It should now be activating.
stakeStatus = await connection.getStakeActivation(stakeAccount.publicKey);
console.log(`Stake account status: ${stakeStatus.state}`);
// At anytime we can choose to deactivate our stake. Our stake account must be inactive before we can withdraw funds.
const deactivateTx = StakeProgram.deactivate({
stakePubkey: stakeAccount.publicKey,
authorizedPubkey: wallet.publicKey,
});
const deactivateTxId = await sendAndConfirmTransaction(
connection,
deactivateTx,
[wallet]
);
console.log(`Stake account deactivated. Tx Id: ${deactivateTxId}`);
// Check in on our stake account. It should now be inactive.
stakeStatus = await connection.getStakeActivation(stakeAccount.publicKey);
console.log(`Stake account status: ${stakeStatus.state}`);
})();
// At anytime we can choose to deactivate our stake. Our stake account must be inactive before we can withdraw funds.
const deactivateTx = StakeProgram.deactivate({
stakePubkey: stakeAccount.publicKey,
authorizedPubkey: wallet.publicKey,
});
const deactivateTxId = await sendAndConfirmTransaction(
connection,
deactivateTx,
[wallet]
);
console.log(`Stake account deactivated. Tx Id: ${deactivateTxId}`);
// Check in on our stake account. It should now be inactive.
stakeStatus = await connection.getStakeActivation(stakeAccount.publicKey);
console.log(`Stake account status: ${stakeStatus.state}`);
Rút Stake
Một khi đã dừng stake, Withdrawal Authority
có thể rút SOL về lại một system account. Và khi một stake account không còn được uỷ quyền cũng như có số dư là 0 SOL, nó sẽ bị huỷ ngay lập tức.
import {
clusterApiUrl,
Connection,
Keypair,
LAMPORTS_PER_SOL,
StakeProgram,
Authorized,
sendAndConfirmTransaction,
Lockup,
PublicKey,
} from "@solana/web3.js";
(async () => {
// Setup our connection and wallet
const connection = new Connection(clusterApiUrl("devnet"), "confirmed");
const wallet = Keypair.generate();
// Fund our wallet with 1 SOL
const airdropSignature = await connection.requestAirdrop(
wallet.publicKey,
LAMPORTS_PER_SOL
);
await connection.confirmTransaction(airdropSignature);
// Create a keypair for our stake account
const stakeAccount = Keypair.generate();
// Calculate how much we want to stake
const minimumRent = await connection.getMinimumBalanceForRentExemption(
StakeProgram.space
);
const amountUserWantsToStake = LAMPORTS_PER_SOL / 2; // This is can be user input. For now, we'll hardcode to 0.5 SOL
const amountToStake = minimumRent + amountUserWantsToStake;
// Setup a transaction to create our stake account
// Note: `StakeProgram.createAccount` returns a `Transaction` preconfigured with the necessary `TransactionInstruction`s
const createStakeAccountTx = StakeProgram.createAccount({
authorized: new Authorized(wallet.publicKey, wallet.publicKey), // Here we set two authorities: Stake Authority and Withdrawal Authority. Both are set to our wallet.
fromPubkey: wallet.publicKey,
lamports: amountToStake,
lockup: new Lockup(0, 0, wallet.publicKey), // Optional. We'll set this to 0 for demonstration purposes.
stakePubkey: stakeAccount.publicKey,
});
const createStakeAccountTxId = await sendAndConfirmTransaction(
connection,
createStakeAccountTx,
[
wallet,
stakeAccount, // Since we're creating a new stake account, we have that account sign as well
]
);
console.log(`Stake account created. Tx Id: ${createStakeAccountTxId}`);
// Check our newly created stake account balance. This should be 0.5 SOL.
let stakeBalance = await connection.getBalance(stakeAccount.publicKey);
console.log(`Stake account balance: ${stakeBalance / LAMPORTS_PER_SOL} SOL`);
// Verify the status of our stake account. This will start as inactive and will take some time to activate.
let stakeStatus = await connection.getStakeActivation(stakeAccount.publicKey);
console.log(`Stake account status: ${stakeStatus.state}`);
// To delegate our stake, we first have to select a validator. Here we get all validators and select the first active one.
const validators = await connection.getVoteAccounts();
const selectedValidator = validators.current[0];
const selectedValidatorPubkey = new PublicKey(selectedValidator.votePubkey);
// With a validator selected, we can now setup a transaction that delegates our stake to their vote account.
const delegateTx = StakeProgram.delegate({
stakePubkey: stakeAccount.publicKey,
authorizedPubkey: wallet.publicKey,
votePubkey: selectedValidatorPubkey,
});
const delegateTxId = await sendAndConfirmTransaction(connection, delegateTx, [
wallet,
]);
console.log(
`Stake account delegated to ${selectedValidatorPubkey}. Tx Id: ${delegateTxId}`
);
// Check in on our stake account. It should now be activating.
stakeStatus = await connection.getStakeActivation(stakeAccount.publicKey);
console.log(`Stake account status: ${stakeStatus.state}`);
// At anytime we can choose to deactivate our stake. Our stake account must be inactive before we can withdraw funds.
const deactivateTx = StakeProgram.deactivate({
stakePubkey: stakeAccount.publicKey,
authorizedPubkey: wallet.publicKey,
});
const deactivateTxId = await sendAndConfirmTransaction(
connection,
deactivateTx,
[wallet]
);
console.log(`Stake account deactivated. Tx Id: ${deactivateTxId}`);
// Check in on our stake account. It should now be inactive.
stakeStatus = await connection.getStakeActivation(stakeAccount.publicKey);
console.log(`Stake account status: ${stakeStatus.state}`);
// Once deactivated, we can withdraw our SOL back to our main wallet
const withdrawTx = StakeProgram.withdraw({
stakePubkey: stakeAccount.publicKey,
authorizedPubkey: wallet.publicKey,
toPubkey: wallet.publicKey,
lamports: stakeBalance, // Withdraw the full balance at the time of the transaction
});
const withdrawTxId = await sendAndConfirmTransaction(connection, withdrawTx, [
wallet,
]);
console.log(`Stake account withdrawn. Tx Id: ${withdrawTxId}`);
// Confirm that our stake account balance is now 0
stakeBalance = await connection.getBalance(stakeAccount.publicKey);
console.log(`Stake account balance: ${stakeBalance / LAMPORTS_PER_SOL} SOL`);
})();
// Once deactivated, we can withdraw our SOL back to our main wallet
const withdrawTx = StakeProgram.withdraw({
stakePubkey: stakeAccount.publicKey,
authorizedPubkey: wallet.publicKey,
toPubkey: wallet.publicKey,
lamports: stakeBalance, // Withdraw the full balance at the time of the transaction
});
const withdrawTxId = await sendAndConfirmTransaction(connection, withdrawTx, [
wallet,
]);
console.log(`Stake account withdrawn. Tx Id: ${withdrawTxId}`);
// Confirm that our stake account balance is now 0
stakeBalance = await connection.getBalance(stakeAccount.publicKey);
console.log(`Stake account balance: ${stakeBalance / LAMPORTS_PER_SOL} SOL`);