🏨Transaction construction

Most of accounts used by the program are PDAs. The SDK automatically derives these accounts from the provided arguments, reducing the account management.

Presently, my setup involves a server responsible for delivering serialized transactions to the client or application. When the client sends specific parameters through a request, the server responds by providing a pre-built and serialized transaction using the SDK. Check code.

Create a product:

app.get('/initProductTree', async (req: Request, res: Response) => {
    try {
        if (!rpc) res.status(500).send({error: 'Error: Server rpc not configured'});
        const connection = new Connection(rpc);
        const { signer, marketplace, paymentMint, params } = req.query;
        if (!signer || !marketplace || !paymentMint) {
            res.status(500).send({error: 'Error: Missing account'});
        }
        const accounts = {
            signer: new PublicKey(signer),
            marketplace: new PublicKey(marketplace),
            paymentMint: new PublicKey(paymentMint)
        };
        const normalizedParams = {
            id: String(params.id),
            productPrice: Number(params.productPrice),
            feeBasisPoints: Number(params.feeBasisPoints),
            height: Number(params.height),
            buffer: Number(params.buffer),
            canopy: Number(params.canopy),
            name: String(params.name), 
            metadataUrl: String(params.metadataUrl),
        };
        const transaction = await createInitProductTreeTransaction(
            connection, 
            accounts,
            normalizedParams
        );

        res.status(200).send({ 
            transaction: Buffer.from(transaction.serialize()).toString('base64') 
        });
    } catch (error) {
        console.log(error)
        res.status(500).send({ error: error.message });
    }
});

Registering a purchase:

app.get('/registerBuy', async (req: Request, res: Response) => {
    try {
        if (!rpc) throw new Error('Error: Server rpc not configured');
        const connection = new Connection(rpc);

        const { signer, marketplace, productId, paymentMint, seller, marketplaceAuth, params } = req.query;
        if (!signer || !marketplace || !paymentMint || !productId || !seller || !marketplaceAuth || !params) {
          throw new Error('Error: Missing required information');
        }        
        if (!messagesKey) throw new Error('Error: MessagesKey not configured');
        const messagesSigner = ImportAccountFromPrivateKey(Uint8Array.from(JSON.parse(messagesKey)));

        if (!indexerApi) throw new Error('Error: Indexer server not configured');

        const [firstId, secondId] = getSplitId(productId);
        const marketKey = new PublicKey(marketplace);
        const [product] = PublicKey.findProgramAddressSync(
            [
              Buffer.from("product", "utf-8"), 
              firstId, 
              secondId,
              marketKey.toBuffer()
            ],
            BRICK_PROGRAM_ID_PK
        );
        const productResponse = await queryAccounts(indexerApi, { accounts: [product.toString()] });
        const productInfo = productResponse[0].data as Product;
        const itemHash = await generateAlephMessage({ 
            product: product.toString(), 
            seller, signer, 
            units: Number(params.amount), 
            paymentMint, 
            totalAmount: Number(productInfo.sellerConfig.productPrice) * params.amount 
        }, messagesSigner);
        const accounts = {
            signer: new PublicKey(signer),
            marketplace: new PublicKey(marketplace),
            product: new PublicKey(product),
            paymentMint: new PublicKey(paymentMint),
            seller: new PublicKey(seller),
            marketplaceAuth: new PublicKey(marketplaceAuth),
            merkleTree: new PublicKey(productInfo.merkleTree),
        };
        const parsedParams = {
            rewardsActive: params.rewardsActive === 'true' ? true : false,
            amount: Number(params.amount),
            name: params.name,
            uri: `https://api1.aleph.im/api/v0/messages.json?hashes=${itemHash}`,
        };
        const transaction = await createRegisterBuyCnftTransaction(connection, accounts, parsedParams);     

        res.status(200).send({ transaction: Buffer.from(transaction.serialize()).toString('base64') });
    } catch (error) {
        res.status(500).send({ error: error.message });
    }
});

function getSplitId(str: string): [Buffer, Buffer]{
    const bytes = new TextEncoder().encode(str);
  
    const data = new Uint8Array(64);
    data.fill(32);
    data.set(bytes);
  
    const firstId = Buffer.from(data.slice(0, 32));
    const secondId = Buffer.from(data.slice(32));
  
    return [firstId, secondId];
}

Create a marketplace:

I am using a solana playground repo with more scripts calling more Brick program instructions.

With the following script you can create a marketplace:

import { PaymentFeePayer } from "./utils/solita/brick/index.js";
import { Connection, Keypair } from "@solana/web3.js";
import { FNET_MINT } from "./constants.js";
import dotenv from 'dotenv';
import { createInitMarketplaceTransaction } from "brick-protocol";
dotenv.config();

async function initMarketplace() {
    const rpc = process.env.rpc || '';
    if (rpc === '') throw new Error("RPC not found in environment variables.");

    const secret = JSON.parse(process.env.secret || '');
    if (secret === '') throw new Error("Secret not found in environment variables.");

    const secretUint8Array = new Uint8Array(secret);
    const signer = Keypair.fromSecretKey(secretUint8Array);
    const connection: Connection = new Connection(rpc);

    const accounts = {
        signer: signer.publicKey,
        rewardMint: FNET_MINT,
        discountMint: FNET_MINT,
    };
    const args = {
        fee: 100,
        feeReduction: 20,
        sellerReward: 10,
        buyerReward: 10,
        useCnfts: true,
        deliverToken: false,
        transferable: false,
        chainCounter: false,
        permissionless: false,
        rewardsEnabled: false,
        feePayer: PaymentFeePayer.Buyer,
    };

    const transaction = await createInitMarketplaceTransaction(connection, accounts, args);
    transaction.sign([signer]);
    console.log(transaction)
    const txid = await connection.sendTransaction(transaction);
    console.log(`https://explorer.solana.com/tx/${txid}?cluster=mainnet`);
}

initMarketplace();

Last updated