Files
solisting/sdk/src/order.ts
2026-06-27 14:52:11 +02:00

95 lines
3.3 KiB
TypeScript

import { address, type Account, type Address, type Rpc } from '@solana/kit'
import { type GetProgramAccountsApi } from '@solana/rpc-api'
import { type GetAccountInfoApi } from '@solana/rpc-api'
import type { Base64EncodedBytes, Lamports } from '@solana/rpc-types'
import {
decodeSolistingStateOrderAccount,
fetchSolistingStateOrderAccount,
getSolistingStateOrderAccountDiscriminatorBytes,
type SolistingStateOrderAccount,
} from './generated/solisting/src/generated/index'
import { deriveEscrowId } from './pda'
const PROGRAM_ADDRESS = address('9uF64swnFqo34oFJtv8Qx9JDyXXJz73eDFF4cKRBfQBZ')
export type OrderAccountWithPda = Account<SolistingStateOrderAccount>
type GpaRpc = Rpc<GetProgramAccountsApi>
type GetRpc = Rpc<GetAccountInfoApi>
// OrderAccount byte layout (Sol-currency variant, the only one currently implemented):
// 0: discriminator (8)
// 8: listing (32)
// 40: buyer (32)
// 72: seller (32)
// 104: resolver Option<Pubkey> = 1 byte tag + 32 bytes = 33 bytes
// 137: paymentCurrency — Sol variant = 1 byte tag + 0 data = 1 byte total
// 138: amount (8)
// 146: escrowAccount (32)
const LISTING_OFFSET = 8n
const BUYER_OFFSET = 40n
// escrowAccount offset is only valid for Sol-currency orders.
// SPL orders (currently rejected by the program with SplNotImplemented) would sit at offset 179.
const ESCROW_ACCOUNT_OFFSET = 146n
type RawGpaResult = Array<{
pubkey: Address
account: {
executable: boolean
lamports: bigint
owner: Address
space: bigint
data: [string, 'base64']
}
}>
const DISC_BASE64 = Buffer.from(
getSolistingStateOrderAccountDiscriminatorBytes(),
).toString('base64') as Base64EncodedBytes
async function gpa(
rpc: GpaRpc,
addressFilter: { offset: bigint; addr: Address },
): Promise<OrderAccountWithPda[]> {
const results = await rpc
.getProgramAccounts(PROGRAM_ADDRESS, {
encoding: 'base64',
filters: [
{ memcmp: { offset: 0n, bytes: DISC_BASE64, encoding: 'base64' } },
{ memcmp: { offset: addressFilter.offset, bytes: addressFilter.addr as never, encoding: 'base58' } },
],
})
.send()
return (results as RawGpaResult).map((r) => {
const data = new Uint8Array(Buffer.from(r.account.data[0], 'base64'))
return decodeSolistingStateOrderAccount({
address: r.pubkey,
data,
executable: r.account.executable,
lamports: r.account.lamports as unknown as Lamports,
programAddress: r.account.owner,
space: r.account.space,
exists: true,
}) as OrderAccountWithPda
})
}
export function fetchOrdersForListing(rpc: GpaRpc, listingPk: Address): Promise<OrderAccountWithPda[]> {
return gpa(rpc, { offset: LISTING_OFFSET, addr: listingPk })
}
export function fetchOrdersByBuyer(rpc: GpaRpc, buyer: Address): Promise<OrderAccountWithPda[]> {
return gpa(rpc, { offset: BUYER_OFFSET, addr: buyer })
}
export async function fetchOrderByEscrow(rpc: GpaRpc, escrowPda: Address): Promise<OrderAccountWithPda | null> {
const results = await gpa(rpc, { offset: ESCROW_ACCOUNT_OFFSET, addr: escrowPda })
return results[0] ?? null
}
export async function fetchOrder(rpc: GetRpc, addr: Address): Promise<OrderAccountWithPda | null> {
return fetchSolistingStateOrderAccount(rpc, addr).catch(() => null)
}
export { deriveEscrowId }