[Testnet] Open Action | Cross-chain Zora Mint (v2)

Mint Zora NFTs on Base, with Bonsai on Polygon

Overview

This is a technical guide on how to integrate our ZoraLzMintActionV2 open action. More details about the open action can be found here: Cross-chain Zora Mint (v2)

Our ZoraLzMintActionV2 action module is deployed and verified on Polygon

Our contract which receives the messages is ZoraLzCreatorV2 - deployed and verified on Base.

Initialize with the open action

To initialize a publication with the ZoraLzMintActionV2 module, you must provide the details for a Zora NFT where

  • the chain is Base

  • the currency is Bonsai (Base)

  • the sale is active

Process the open action

To process a publication with the ZoraLzMintActionV2 module, you must

  • Get the total sale price for the mint

  • Have the actor approve the $BONSAI token transfer to the module

  • Encode the data needed to process the act

  • Send the act transaction

Prepare the act transaction

// 1. Fetch module metadata
import { testnet, LensClient } from "@lens-protocol/client";
import { constants } from "viem";

const ZORA_LZ_MINT_ACTION_V2 = "0xA5F19D5953B7777537014653e6219983cE82001c";
const BONSAI = "0x3d2bD0e15829AA5C362a4144FdF4A1112fa29B5c";

const lensClient = new LensClient({ environment: testnet });
const data = await lensClient.modules.fetchMetadata({ implementation: ZORA_LZ_MINT_ACTION_V2 });
const { metadata } = data;

// 2. Get the total sale price
const pointedProfileId = 697; 
const pointedPubId = 3; 
const quantity = 1;

const actionModule = getActionModule(ZORA_LZ_MINT_ACTION_V2); // ethers Contract
const totalSalePrice = await actionModule.getTotalSalePrice(pointedProfileId, pointedPubId, qty);

// 3. Approve the token transfer
const tx = await getTokenContract(BONSAI).approve(ZORA_LZ_MINT_ACTION_V2, totalSalePrice);
await tx.wait();

// 4. Encode act data
const params = {
  quantity,
  clientAddress: constants.AddressZero, // client address to earn protocol fee
  comment: "minted with $BONSAI" // user-provided comment to surface on Zora UI
};

const actionModuleData = encodeData(
  JSON.parse(metadata.processCalldataABI),
  [params.quantity, params.clientAddress, params.comment]
);

Send the act transaction (gasless)

There's a lot of steps involved here, so we'll defer to the Lens docs - but here is the general code.

import { WalletClient } from "viem";
import { OnchainReferrer, RelaySuccessFragment, LensClient } from "@lens-protocol/client";
import { omit } from "lodash/object";

// NOTE: this assume the given `actionModule` has `metadata.sponsoredApproved` = true
// NOTE: this assumes that the passed in `lensClient` is authenticated (see: https://docs.lens.xyz/docs/login)
// NOTE: this assumes the app is whitelisted to use gasless
export const actWithSignedTypedata = async (
  lensClient: LensClient,
  walletClient: WalletClient,
  publicationId: string,
  actionModule: `0x${string}`,
  actionModuleData: string,
  referrers?: OnchainReferrer[] // profile to earn mint referral fees
): Promise<any> => {
  try {
    // get typed data
    const typedDataResult = await lensClient.publication.actions.createActOnTypedData({
      actOn: {
        unknownOpenAction: {
          address: actionModule,
          data: actionModuleData
        }
      },
      for: publicationId,
      referrers: referrers || []
    });

    const { id, typedData } = typedDataResult.unwrap();

    // sign it
    const [account] = await walletClient.getAddresses();
    const signedTypedData = await walletClient.signTypedData({
      account,
      domain: omit(typedData.domain, "__typename"),
      types: omit(typedData.types, "__typename"),
      primaryType: "Act",
      message: omit(typedData.value, "__typename"),
    });

    // broadcast onchain, gasless
    const broadcastResult = await lensClient.transaction.broadcastOnchain({ id, signature: signedTypedData });
    const broadcastResultValue = broadcastResult.unwrap();

    if (broadcastResultValue.__typename === "RelayError") throw new Error("RelayError");

    // return the tx hash to link to layerzero scan
    return (broadcastResultValue as RelaySuccessFragment).txHash;
  } catch (error) {
    console.log(error);
  }
}

Last updated