solana-program-library/token-swap/js/test/token-swap-test.ts

725 lines
22 KiB
TypeScript

import {
Keypair,
Connection,
PublicKey,
SystemProgram,
Transaction,
sendAndConfirmTransaction
} from '@solana/web3.js';
import {approve, createMint, createAccount, createApproveInstruction, createInitializeAccountInstruction, getAccount, getMint, getMinimumBalanceForRentExemptAccount, mintTo, AccountLayout, TOKEN_PROGRAM_ID} from '@solana/spl-token';
import {TokenSwap, CurveType, TOKEN_SWAP_PROGRAM_ID} from '../src';
import {newAccountWithLamports} from '../src/util/new-account-with-lamports';
import {sleep} from '../src/util/sleep';
// The following globals are created by `createTokenSwap` and used by subsequent tests
// Token swap
let tokenSwap: TokenSwap;
// authority of the token and accounts
let authority: PublicKey;
// bump seed used to generate the authority public key
let bumpSeed: number;
// owner of the user accounts
let owner: Keypair;
// payer for transactions
let payer: Keypair;
// Token pool
let tokenPool: PublicKey;
let tokenAccountPool: PublicKey;
let feeAccount: PublicKey;
// Tokens swapped
let mintA: PublicKey;
const mintAProgramId: PublicKey = TOKEN_PROGRAM_ID;
let mintB: PublicKey;
const mintBProgramId: PublicKey = TOKEN_PROGRAM_ID;
let tokenAccountA: PublicKey;
let tokenAccountB: PublicKey;
// Hard-coded fee address, for testing production mode
const SWAP_PROGRAM_OWNER_FEE_ADDRESS =
process.env.SWAP_PROGRAM_OWNER_FEE_ADDRESS;
// Pool fees
const TRADING_FEE_NUMERATOR = 25n;
const TRADING_FEE_DENOMINATOR = 10000n;
const OWNER_TRADING_FEE_NUMERATOR = 5n;
const OWNER_TRADING_FEE_DENOMINATOR = 10000n;
const OWNER_WITHDRAW_FEE_NUMERATOR = SWAP_PROGRAM_OWNER_FEE_ADDRESS ? 0n : 1n;
const OWNER_WITHDRAW_FEE_DENOMINATOR = SWAP_PROGRAM_OWNER_FEE_ADDRESS ? 0n : 6n;
const HOST_FEE_NUMERATOR = 20n;
const HOST_FEE_DENOMINATOR = 100n;
// Initial amount in each swap token
let currentSwapTokenA = 1000000n;
let currentSwapTokenB = 1000000n;
let currentFeeAmount = 0n;
// Swap instruction constants
// Because there is no withdraw fee in the production version, these numbers
// need to get slightly tweaked in the two cases.
const SWAP_AMOUNT_IN = 100000n;
const SWAP_AMOUNT_OUT = SWAP_PROGRAM_OWNER_FEE_ADDRESS ? 90661n : 90674n;
const SWAP_FEE = SWAP_PROGRAM_OWNER_FEE_ADDRESS ? 22727n : 22730n;
const HOST_SWAP_FEE = SWAP_PROGRAM_OWNER_FEE_ADDRESS
? SWAP_FEE * HOST_FEE_NUMERATOR / HOST_FEE_DENOMINATOR
: 0n;
const OWNER_SWAP_FEE = SWAP_FEE - HOST_SWAP_FEE;
// Pool token amount minted on init
const DEFAULT_POOL_TOKEN_AMOUNT = 1000000000n;
// Pool token amount to withdraw / deposit
const POOL_TOKEN_AMOUNT = 10000000n;
function assert(condition: boolean, message?: string) {
if (!condition) {
console.log(Error().stack + ':token-test.js');
throw message || 'Assertion failed';
}
}
let connection: Connection;
async function getConnection(): Promise<Connection> {
if (connection) return connection;
const url = 'http://localhost:8899';
connection = new Connection(url, 'recent');
const version = await connection.getVersion();
console.log('Connection to cluster established:', url, version);
return connection;
}
export async function createTokenSwap(
curveType: number,
curveParameters?: Uint8Array,
): Promise<void> {
const connection = await getConnection();
payer = await newAccountWithLamports(connection, 1000000000);
owner = await newAccountWithLamports(connection, 1000000000);
const tokenSwapAccount = Keypair.generate();
[authority, bumpSeed] = await PublicKey.findProgramAddress(
[tokenSwapAccount.publicKey.toBuffer()],
TOKEN_SWAP_PROGRAM_ID,
);
console.log('creating pool mint');
tokenPool = await createMint(
connection,
payer,
authority,
null,
2,
Keypair.generate(),
undefined,
TOKEN_PROGRAM_ID,
);
console.log('creating pool account');
tokenAccountPool = await createAccount(connection, payer, tokenPool, owner.publicKey, Keypair.generate());
const ownerKey = SWAP_PROGRAM_OWNER_FEE_ADDRESS || owner.publicKey.toString();
feeAccount = await createAccount(connection, payer, tokenPool, new PublicKey(ownerKey), Keypair.generate());
console.log('creating token A');
mintA = await createMint(
connection,
payer,
owner.publicKey,
null,
2,
Keypair.generate(),
undefined,
mintAProgramId,
);
console.log('creating token A account');
tokenAccountA = await createAccount(connection, payer, mintA, authority, Keypair.generate());
console.log('minting token A to swap');
await mintTo(connection, payer, mintA, tokenAccountA, owner, currentSwapTokenA);
console.log('creating token B');
mintB = await createMint(
connection,
payer,
owner.publicKey,
null,
2,
Keypair.generate(),
undefined,
mintBProgramId,
);
console.log('creating token B account');
tokenAccountB = await createAccount(connection, payer, mintB, authority, Keypair.generate());
console.log('minting token B to swap');
await mintTo(connection, payer, mintB, tokenAccountB, owner, currentSwapTokenB);
console.log('creating token swap');
const swapPayer = await newAccountWithLamports(connection, 10000000000);
tokenSwap = await TokenSwap.createTokenSwap(
connection,
swapPayer,
tokenSwapAccount,
authority,
tokenAccountA,
tokenAccountB,
tokenPool,
mintA,
mintB,
feeAccount,
tokenAccountPool,
TOKEN_SWAP_PROGRAM_ID,
TOKEN_PROGRAM_ID,
TRADING_FEE_NUMERATOR,
TRADING_FEE_DENOMINATOR,
OWNER_TRADING_FEE_NUMERATOR,
OWNER_TRADING_FEE_DENOMINATOR,
OWNER_WITHDRAW_FEE_NUMERATOR,
OWNER_WITHDRAW_FEE_DENOMINATOR,
HOST_FEE_NUMERATOR,
HOST_FEE_DENOMINATOR,
curveType,
curveParameters,
);
console.log('loading token swap');
const fetchedTokenSwap = await TokenSwap.loadTokenSwap(
connection,
tokenSwapAccount.publicKey,
TOKEN_SWAP_PROGRAM_ID,
swapPayer,
);
assert(fetchedTokenSwap.poolTokenProgramId.equals(TOKEN_PROGRAM_ID));
assert(fetchedTokenSwap.tokenAccountA.equals(tokenAccountA));
assert(fetchedTokenSwap.tokenAccountB.equals(tokenAccountB));
assert(fetchedTokenSwap.mintA.equals(mintA));
assert(fetchedTokenSwap.mintB.equals(mintB));
assert(fetchedTokenSwap.poolToken.equals(tokenPool));
assert(fetchedTokenSwap.feeAccount.equals(feeAccount));
assert(
TRADING_FEE_NUMERATOR == fetchedTokenSwap.tradeFeeNumerator
);
assert(
TRADING_FEE_DENOMINATOR == fetchedTokenSwap.tradeFeeDenominator
);
assert(
OWNER_TRADING_FEE_NUMERATOR ==
fetchedTokenSwap.ownerTradeFeeNumerator
);
assert(
OWNER_TRADING_FEE_DENOMINATOR ==
fetchedTokenSwap.ownerTradeFeeDenominator
);
assert(
OWNER_WITHDRAW_FEE_NUMERATOR ==
fetchedTokenSwap.ownerWithdrawFeeNumerator
);
assert(
OWNER_WITHDRAW_FEE_DENOMINATOR ==
fetchedTokenSwap.ownerWithdrawFeeDenominator
);
assert(HOST_FEE_NUMERATOR == fetchedTokenSwap.hostFeeNumerator);
assert(
HOST_FEE_DENOMINATOR == fetchedTokenSwap.hostFeeDenominator
);
assert(curveType == fetchedTokenSwap.curveType);
}
export async function depositAllTokenTypes(): Promise<void> {
const poolMintInfo = await getMint(connection, tokenPool);
const supply = poolMintInfo.supply;
const swapTokenA = await getAccount(connection, tokenAccountA);
const tokenA = swapTokenA.amount * BigInt(POOL_TOKEN_AMOUNT) / supply;
const swapTokenB = await getAccount(connection, tokenAccountB);
const tokenB = swapTokenB.amount * BigInt(POOL_TOKEN_AMOUNT) / supply;
const userTransferAuthority = Keypair.generate();
console.log('Creating depositor token a account');
const userAccountA = await createAccount(connection, payer, mintA, owner.publicKey, Keypair.generate());
await mintTo(connection, payer, mintA, userAccountA, owner, tokenA);
await approve(
connection,
payer,
userAccountA,
userTransferAuthority.publicKey,
owner,
tokenA,
);
console.log('Creating depositor token b account');
const userAccountB = await createAccount(connection, payer, mintB, owner.publicKey, Keypair.generate());
await mintTo(connection, payer, mintB, userAccountB, owner, tokenB);
await approve(
connection,
payer,
userAccountB,
userTransferAuthority.publicKey,
owner,
tokenB,
);
console.log('Creating depositor pool token account');
const newAccountPool = await createAccount(connection, payer, tokenPool, owner.publicKey, Keypair.generate());
const confirmOptions = {
skipPreflight: true
}
console.log('Depositing into swap');
await tokenSwap.depositAllTokenTypes(
userAccountA,
userAccountB,
newAccountPool,
TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID,
userTransferAuthority,
POOL_TOKEN_AMOUNT,
tokenA,
tokenB,
confirmOptions
);
let info;
info = await getAccount(connection, userAccountA);
assert(info.amount == 0n);
info = await getAccount(connection, userAccountB);
assert(info.amount == 0n);
info = await getAccount(connection, tokenAccountA);
assert(info.amount == currentSwapTokenA + tokenA);
currentSwapTokenA += tokenA;
info = await getAccount(connection, tokenAccountB);
assert(info.amount == currentSwapTokenB + tokenB);
currentSwapTokenB += tokenB;
info = await getAccount(connection, newAccountPool);
assert(info.amount == POOL_TOKEN_AMOUNT);
}
export async function withdrawAllTokenTypes(): Promise<void> {
const poolMintInfo = await getMint(connection, tokenPool);
const supply = poolMintInfo.supply;
let swapTokenA = await getAccount(connection, tokenAccountA);
let swapTokenB = await getAccount(connection, tokenAccountB);
let feeAmount = 0n;
if (OWNER_WITHDRAW_FEE_NUMERATOR !== 0n) {
feeAmount =
(POOL_TOKEN_AMOUNT * OWNER_WITHDRAW_FEE_NUMERATOR) /
OWNER_WITHDRAW_FEE_DENOMINATOR;
}
const poolTokenAmount = POOL_TOKEN_AMOUNT - feeAmount;
const tokenA = swapTokenA.amount * BigInt(poolTokenAmount) / supply;
const tokenB = swapTokenB.amount * BigInt(poolTokenAmount) / supply;
console.log('Creating withdraw token A account');
let userAccountA = await createAccount(connection, payer, mintA, owner.publicKey, Keypair.generate());
console.log('Creating withdraw token B account');
let userAccountB = await createAccount(connection, payer, mintB, owner.publicKey, Keypair.generate());
const userTransferAuthority = Keypair.generate();
console.log('Approving withdrawal from pool account');
await approve(
connection,
payer,
tokenAccountPool,
userTransferAuthority.publicKey,
owner,
POOL_TOKEN_AMOUNT,
);
const confirmOptions = {
skipPreflight: true
}
console.log('Withdrawing pool tokens for A and B tokens');
await tokenSwap.withdrawAllTokenTypes(
userAccountA,
userAccountB,
tokenAccountPool,
TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID,
userTransferAuthority,
POOL_TOKEN_AMOUNT,
tokenA,
tokenB,
confirmOptions
);
//const poolMintInfo = await tokenPool.getMintInfo();
swapTokenA = await getAccount(connection, tokenAccountA);
swapTokenB = await getAccount(connection, tokenAccountB);
let info = await getAccount(connection, tokenAccountPool);
assert(
info.amount == DEFAULT_POOL_TOKEN_AMOUNT - POOL_TOKEN_AMOUNT,
);
assert(swapTokenA.amount == currentSwapTokenA - tokenA);
currentSwapTokenA -= tokenA;
assert(swapTokenB.amount == currentSwapTokenB - tokenB);
currentSwapTokenB -= tokenB;
info = await getAccount(connection, userAccountA);
assert(info.amount == tokenA);
info = await getAccount(connection, userAccountB);
assert(info.amount == tokenB);
info = await getAccount(connection, feeAccount);
assert(info.amount == feeAmount);
currentFeeAmount = feeAmount;
}
export async function createAccountAndSwapAtomic(): Promise<void> {
console.log('Creating swap token a account');
let userAccountA = await createAccount(connection, payer, mintA, owner.publicKey, Keypair.generate());
await mintTo(connection, payer, mintA, userAccountA, owner, SWAP_AMOUNT_IN);
// @ts-ignore
const balanceNeeded = await getMinimumBalanceForRentExemptAccount(
connection,
);
const newAccount = Keypair.generate();
const transaction = new Transaction();
transaction.add(
SystemProgram.createAccount({
fromPubkey: owner.publicKey,
newAccountPubkey: newAccount.publicKey,
lamports: balanceNeeded,
space: AccountLayout.span,
programId: mintBProgramId,
}),
);
transaction.add(
createInitializeAccountInstruction(
newAccount.publicKey,
mintB,
owner.publicKey,
mintBProgramId,
),
);
const userTransferAuthority = Keypair.generate();
transaction.add(
createApproveInstruction(
userAccountA,
userTransferAuthority.publicKey,
owner.publicKey,
SWAP_AMOUNT_IN,
[],
mintAProgramId,
),
);
transaction.add(
TokenSwap.swapInstruction(
tokenSwap.tokenSwap,
tokenSwap.authority,
userTransferAuthority.publicKey,
userAccountA,
tokenSwap.tokenAccountA,
tokenSwap.tokenAccountB,
newAccount.publicKey,
tokenSwap.poolToken,
tokenSwap.feeAccount,
null,
tokenSwap.mintA,
tokenSwap.mintB,
tokenSwap.swapProgramId,
TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID,
tokenSwap.poolTokenProgramId,
SWAP_AMOUNT_IN,
0n,
),
);
const confirmOptions = {
skipPreflight: true
}
// Send the instructions
console.log('sending big instruction');
await sendAndConfirmTransaction(
connection,
transaction,
[payer, owner, newAccount, userTransferAuthority],
confirmOptions
);
let info;
info = await getAccount(connection, tokenAccountA);
currentSwapTokenA = info.amount;
info = await getAccount(connection, tokenAccountB);
currentSwapTokenB = info.amount;
}
export async function swap(): Promise<void> {
console.log('Creating swap token a account');
let userAccountA = await createAccount(connection, payer, mintA, owner.publicKey, Keypair.generate());
await mintTo(connection, payer, mintA, userAccountA, owner, SWAP_AMOUNT_IN);
const userTransferAuthority = Keypair.generate();
await approve(
connection,
payer,
userAccountA,
userTransferAuthority.publicKey,
owner,
SWAP_AMOUNT_IN,
);
console.log('Creating swap token b account');
let userAccountB = await createAccount(connection, payer, mintB, owner.publicKey, Keypair.generate());
let poolAccount = SWAP_PROGRAM_OWNER_FEE_ADDRESS
? await createAccount(connection, payer, tokenPool, owner.publicKey, Keypair.generate())
: null;
const confirmOptions = {
skipPreflight: true
}
console.log('Swapping');
await tokenSwap.swap(
userAccountA,
tokenAccountA,
tokenAccountB,
userAccountB,
tokenSwap.mintA,
tokenSwap.mintB,
TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID,
poolAccount,
userTransferAuthority,
SWAP_AMOUNT_IN,
SWAP_AMOUNT_OUT,
confirmOptions
);
await sleep(500);
let info;
info = await getAccount(connection, userAccountA);
assert(info.amount == 0n);
info = await getAccount(connection, userAccountB);
assert(info.amount == SWAP_AMOUNT_OUT);
info = await getAccount(connection, tokenAccountA);
assert(info.amount == currentSwapTokenA + SWAP_AMOUNT_IN);
currentSwapTokenA += SWAP_AMOUNT_IN;
info = await getAccount(connection, tokenAccountB);
assert(info.amount == currentSwapTokenB - SWAP_AMOUNT_OUT);
currentSwapTokenB -= SWAP_AMOUNT_OUT;
info = await getAccount(connection, tokenAccountPool);
assert(
info.amount == DEFAULT_POOL_TOKEN_AMOUNT - POOL_TOKEN_AMOUNT,
);
info = await getAccount(connection, feeAccount);
assert(info.amount == currentFeeAmount + OWNER_SWAP_FEE);
if (poolAccount != null) {
info = await getAccount(connection, poolAccount);
assert(info.amount == HOST_SWAP_FEE);
}
}
function tradingTokensToPoolTokens(
sourceAmount: bigint,
swapSourceAmount: bigint,
poolAmount: bigint,
): bigint {
const tradingFee =
(sourceAmount / 2n) * (TRADING_FEE_NUMERATOR / TRADING_FEE_DENOMINATOR);
const ownerTradingFee =
(sourceAmount / 2n) * (OWNER_TRADING_FEE_NUMERATOR / OWNER_TRADING_FEE_DENOMINATOR);
const sourceAmountPostFee = sourceAmount - tradingFee - ownerTradingFee;
const root = Math.sqrt(Number(sourceAmountPostFee) / Number(swapSourceAmount) + 1);
return BigInt(Math.floor(Number(poolAmount) * (root - 1)));
}
export async function depositSingleTokenTypeExactAmountIn(): Promise<void> {
// Pool token amount to deposit on one side
const depositAmount = 10000n;
const poolMintInfo = await getMint(connection, tokenPool);
const supply = poolMintInfo.supply;
const swapTokenA = await getAccount(connection, tokenAccountA);
//const poolTokenA = tradingTokensToPoolTokens(
// depositAmount,
// swapTokenA.amount,
// supply,
//);
const poolTokenA = 0n; // maybe do this better eventually
const swapTokenB = await getAccount(connection, tokenAccountB);
//const poolTokenB = tradingTokensToPoolTokens(
// depositAmount,
// swapTokenB.amount,
// supply,
//;
const poolTokenB = 0n; // maybe do this better eventually
const userTransferAuthority = Keypair.generate();
console.log('Creating depositor token a account');
const userAccountA = await createAccount(connection, payer, mintA, owner.publicKey, Keypair.generate());
await mintTo(connection, payer, mintA, userAccountA, owner, depositAmount);
await approve(
connection,
payer,
userAccountA,
userTransferAuthority.publicKey,
owner,
depositAmount,
);
console.log('Creating depositor token b account');
const userAccountB = await createAccount(connection, payer, mintB, owner.publicKey, Keypair.generate());
await mintTo(connection, payer, mintB, userAccountB, owner, depositAmount);
await approve(
connection,
payer,
userAccountB,
userTransferAuthority.publicKey,
owner,
depositAmount,
);
console.log('Creating depositor pool token account');
const newAccountPool = await createAccount(connection, payer, tokenPool, owner.publicKey, Keypair.generate());
const confirmOptions = {
skipPreflight: true
}
console.log('Depositing token A into swap');
await tokenSwap.depositSingleTokenTypeExactAmountIn(
userAccountA,
newAccountPool,
tokenSwap.mintA,
TOKEN_PROGRAM_ID,
userTransferAuthority,
depositAmount,
poolTokenA,
confirmOptions
);
let info;
info = await getAccount(connection, userAccountA);
assert(info.amount == 0n);
info = await getAccount(connection, tokenAccountA);
assert(info.amount == currentSwapTokenA + depositAmount);
currentSwapTokenA += depositAmount;
console.log('Depositing token B into swap');
await tokenSwap.depositSingleTokenTypeExactAmountIn(
userAccountB,
newAccountPool,
tokenSwap.mintB,
TOKEN_PROGRAM_ID,
userTransferAuthority,
depositAmount,
poolTokenB,
confirmOptions
);
info = await getAccount(connection, userAccountB);
assert(info.amount == 0n);
info = await getAccount(connection, tokenAccountB);
assert(info.amount == currentSwapTokenB + depositAmount);
currentSwapTokenB += depositAmount;
info = await getAccount(connection, newAccountPool);
assert(info.amount >= poolTokenA + poolTokenB);
}
export async function withdrawSingleTokenTypeExactAmountOut(): Promise<void> {
// Pool token amount to withdraw on one side
const withdrawAmount = 50000n;
const poolMintInfo = await getMint(connection, tokenPool);
const supply = poolMintInfo.supply;
const swapTokenA = await getAccount(connection, tokenAccountA);
const swapTokenAPost = swapTokenA.amount - withdrawAmount;
//const poolTokenA = tradingTokensToPoolTokens(
// withdrawAmount,
// swapTokenAPost,
// supply,
//);
//if (OWNER_WITHDRAW_FEE_NUMERATOR !== 0n) {
// adjustedPoolTokenA *=
// 1n + OWNER_WITHDRAW_FEE_NUMERATOR / OWNER_WITHDRAW_FEE_DENOMINATOR;
//}
let adjustedPoolTokenA = 1_000_000_000_000n; // maybe do this better
const swapTokenB = await getAccount(connection, tokenAccountB);
const swapTokenBPost = swapTokenB.amount - withdrawAmount;
//const poolTokenB = tradingTokensToPoolTokens(
// withdrawAmount,
// swapTokenBPost,
// supply,
//);
//if (OWNER_WITHDRAW_FEE_NUMERATOR !== 0n) {
// adjustedPoolTokenB *=
// 1n + OWNER_WITHDRAW_FEE_NUMERATOR / OWNER_WITHDRAW_FEE_DENOMINATOR;
//}
let adjustedPoolTokenB = 1_000_000_000_000n; // maybe do this better
const userTransferAuthority = Keypair.generate();
console.log('Creating withdraw token a account');
const userAccountA = await createAccount(connection, payer, mintA, owner.publicKey, Keypair.generate());
console.log('Creating withdraw token b account');
const userAccountB = await createAccount(connection, payer, mintB, owner.publicKey, Keypair.generate());
console.log('Creating withdraw pool token account');
const poolAccount = await getAccount(connection, tokenAccountPool);
const poolTokenAmount = poolAccount.amount;
await approve(
connection,
payer,
tokenAccountPool,
userTransferAuthority.publicKey,
owner,
adjustedPoolTokenA + adjustedPoolTokenB,
);
const confirmOptions = {
skipPreflight: true
}
console.log('Withdrawing token A only');
await tokenSwap.withdrawSingleTokenTypeExactAmountOut(
userAccountA,
tokenAccountPool,
tokenSwap.mintA,
TOKEN_PROGRAM_ID,
userTransferAuthority,
withdrawAmount,
adjustedPoolTokenA,
confirmOptions
);
let info;
info = await getAccount(connection, userAccountA);
assert(info.amount == withdrawAmount);
info = await getAccount(connection, tokenAccountA);
assert(info.amount == currentSwapTokenA - withdrawAmount);
currentSwapTokenA += withdrawAmount;
info = await getAccount(connection, tokenAccountPool);
assert(info.amount >= poolTokenAmount - adjustedPoolTokenA);
console.log('Withdrawing token B only');
await tokenSwap.withdrawSingleTokenTypeExactAmountOut(
userAccountB,
tokenAccountPool,
tokenSwap.mintB,
TOKEN_PROGRAM_ID,
userTransferAuthority,
withdrawAmount,
adjustedPoolTokenB,
confirmOptions
);
info = await getAccount(connection, userAccountB);
assert(info.amount == withdrawAmount);
info = await getAccount(connection, tokenAccountB);
assert(info.amount == currentSwapTokenB - withdrawAmount);
currentSwapTokenB += withdrawAmount;
info = await getAccount(connection, tokenAccountPool);
assert(
info.amount >=
poolTokenAmount - adjustedPoolTokenA - adjustedPoolTokenB,
);
}