import { createAddPoolShardsMessage, IdentifiableLiquidityPool, } from '@saturnbtcio/pool-serde-sdk'; import { CreatedPdaAccount, PubkeyUtil } from '@saturnbtcio/arch-sdk'; import { AddPoolShardsInstruction } from '@saturnbtcio/pool-serde-sdk'; import { getBitcoinNetwork, toXOnly } from '@saturnbtcio/psbt'; import { base64, hex } from '@scure/base'; import { Transaction } from '@scure/btc-signer'; import { PoolErrorException, PoolErrorType } from '../../error/pool.error'; import { SaturnSdkConfig } from '../../saturn-sdk'; import { DUST_LIMIT } from '../../util/constants'; import { checkFeeRate } from '../../util/fee'; import { buildUtxoInfoFromOutputs } from '../../util/utxo-info'; import { AddPoolShardsMessageRequest } from './add-pool-shards.dto'; import { validateCreateAccountsPsbt } from './manage-pool.validation'; import { validatePoolSdkData } from '../../util/validation'; import { createAccountsForAddPoolShards } from './add-pool-shards.utils'; import { getScriptPubkeyFromAddress } from '../../util/address'; import { createProtocolPda } from '../../account/pda-finder'; export class AddPoolShards { private readonly config: SaturnSdkConfig; constructor(config: SaturnSdkConfig) { this.config = config; } async addPoolShardsMessage(request: AddPoolShardsMessageRequest) { const scureNetwork = getBitcoinNetwork(this.config.network); validatePoolSdkData(request, scureNetwork); const wallet = await this.config.bitcoinProvider.getWallet( request.paymentAddress ?? request.runeAddress, ); const userArchWallet = await this.config.archProvider.getAccountAddress( PubkeyUtil.fromHex( hex.encode(toXOnly(hex.decode(request.runePublicKey))), ), ); const createdPool: IdentifiableLiquidityPool | undefined = await this.config.indexerProvider.getPoolById(request.poolId); if (!createdPool) { throw new PoolErrorException({ message: `Pool with id ${request.poolId} not found`, type: PoolErrorType.PoolNotFound, poolId: request.poolId, }); } await checkFeeRate(Number(request.feeRate), this.config.bitcoinProvider); const collection = await this.config.indexerProvider.getCollection( createdPool.config.token0, ); if (!collection) { throw new PoolErrorException({ message: `Collection ${createdPool.config.token0} not found`, type: PoolErrorType.InvalidToken, token: createdPool.config.token0, }); } const tx = Transaction.fromPSBT(base64.decode(request.signedPsbt)); const accountPromises: Promise[] = createAccountsForAddPoolShards( createdPool, request.shardsLength, this.config.programAccount, this.config.archProvider, ); const accounts = await Promise.all(accountPromises); await validateCreateAccountsPsbt( wallet, request.signedPsbt, accounts, BigInt(request.feeRate), accounts.length, scureNetwork, DUST_LIMIT, this.config.bitcoinProvider, ); tx.finalize(); const utxosInfo = buildUtxoInfoFromOutputs( tx, userArchWallet, accounts, scureNetwork, ); const btcChangeScriptPubkey = getScriptPubkeyFromAddress( request.paymentAddress ?? request.runeAddress, scureNetwork, ); const addPoolShardsData: AddPoolShardsInstruction = { utxos: { shardUtxos: utxosInfo.slice(0, 5), btcUtxo: utxosInfo[5], }, params: { btcChangeScriptPubkey, }, }; const message = createAddPoolShardsMessage( this.config.programAccount, hex.encode(toXOnly(hex.decode(request.runePublicKey))), hex.encode(toXOnly(hex.decode(request.feePayerPubkey))), this.config.mempoolInfoOracleAccount, this.config.feeRateOracleAccount, request.poolId, accounts.map((account) => account.pubkey), addPoolShardsData, request.recentBlockhash, hex.encode(createProtocolPda(hex.decode(this.config.programAccount))[0]), ); return { message, utxosInfo, }; } }