Skip to content

Instantly share code, notes, and snippets.

@imthatcarlos
Last active May 21, 2024 22:49
Show Gist options
  • Save imthatcarlos/c4ad457635c630ecc38eacfe5f236dc5 to your computer and use it in GitHub Desktop.
Save imthatcarlos/c4ad457635c630ecc38eacfe5f236dc5 to your computer and use it in GitHub Desktop.
hardhat scripts for rentable billboard open action
import { task } from 'hardhat/config';
import '@nomicfoundation/hardhat-ethers';
import { constants } from 'ethers';
import { production, LensClient } from '@lens-protocol/client';
import { encodeAbiParameters, formatEther } from 'viem';
import {
LENS_HUB_ADDRESS,
// LENS_HUB_TESTNET_ADDRESS,
getLensHubDeployed,
publicationBody,
} from '../helpers/lens';
import { addJson } from '../helpers/storj';
import { getActionModule, getContractWithLocalAbi } from '../helpers/madfi';
const ACTION_MODULE = 'RentableSpaceAction';
const BONSAI_CONTRACT_POLYGON = "0x3d2bD0e15829AA5C362a4144FdF4A1112fa29B5c";
interface ModuleActDataSchema {
adPubId?: string | null; // [optional] the pub id to pull contentUri and action module for the ad
duration: number; // the amount of time the advertiser wishes to pay for (in seconds)
costPerSecond: string; // the amount the advertisers is willing to pay per second (check getAdCost)
merkleProofIndex?: string | null; // [optional] proof index the space's category merkle
clientAddress?: string | null; // [optional] the whitelisted client address to receive fees
openActionModule?: string | null; // [optional] the linked open action module
adContentUri?: string; // [optional] if no pub id passed in, use this lens metadata uri
merkleProof?: string[] | null; // [optional] proof for the space's category merkle
}
const encodeModuleActData = async (actionModule: `0x${string}`, data: ModuleActDataSchema): Promise<string> => {
const lensClient = new LensClient({ environment: production });
const result = await lensClient.modules.fetchMetadata({ implementation: actionModule });
if (!result) throw new Error(`no metadata indexed for action module: ${actionModule}`);
// need to encode a struct (IRentableSpaceAction.RentParams)
console.log(JSON.parse(result.metadata.processCalldataABI))
console.log([{
adPubId: data.adPubId || "0",
duration: data.duration,
costPerSecond: data.costPerSecond,
merkleProofIndex: data.merkleProofIndex || "0",
clientAddress: data.clientAddress || constants.AddressZero,
openActionModule: data.openActionModule || constants.AddressZero,
adContentUri: data.adContentUri || "",
merkleProof: data.merkleProof || [],
}]);
return encodeAbiParameters(
[JSON.parse(result.metadata.processCalldataABI)],
[{
adPubId: data.adPubId || "0",
duration: data.duration.toString(),
costPerSecond: data.costPerSecond,
merkleProofIndex: data.merkleProofIndex || "0",
clientAddress: data.clientAddress || constants.AddressZero,
openActionModule: data.openActionModule || constants.AddressZero,
adContentUri: data.adContentUri || "",
merkleProof: data.merkleProof || [],
}]
);
}
task('act-rent-billboard', 'rent space on a post initialized with the rent space action module').setAction(async ({ }, { ethers }) => {
const [deployer, _] = await ethers.getSigners();
const deployerAddress = await deployer.getAddress();
const { lensHub } = await getLensHubDeployed(deployer, LENS_HUB_ADDRESS);
const actionModule = await getActionModule(ethers, deployer, ACTION_MODULE);
let tx;
const ADVERTISER_PROFILE_ID = 8640; // lens/madfinance
const CREATOR_PROFILE_ID = 422 // lens/carlosbeltran
const POINTED_PUB_ID = "0x03d0"; // https://hey.xyz/posts/0x01a6-0x03d0
const AD_DURATION = 600; // 10min
const [__, costWithFee, costPerSecond] = await actionModule.getAdCost(CREATOR_PROFILE_ID, AD_DURATION);
// get the active space in case of a merkle interest root
const activeSpace = await actionModule.activeSpaces(CREATOR_PROFILE_ID);
const interestMerkleRoot = activeSpace.interestMerkleRoot;
// query the current active ad (if any) - WILL THROW AN ERROR ON NO AD OR EXPIRED AD
try {
const activeAd = await actionModule.getActiveAd(CREATOR_PROFILE_ID, POINTED_PUB_ID);
console.log('activeAd:', activeAd);
} catch { console.log("no current active ad") }
console.log(`costPerSecond: ${formatEther(costPerSecond)}`);
const metadata = publicationBody('text', { content: "this is an ad" }, []);
const ipfsHash = await addJson(metadata);
console.log(`ad content: ${ipfsHash}`);
const adPubId = "0x7b"; // https://hey.xyz/posts/0x21c0-0x7b
const openActionModule = "0x0D90C58cBe787CD70B5Effe94Ce58185D72143fB"; // COLLECT
// need to approve the cost
console.log(`approving bonsai...`);
const token = await getContractWithLocalAbi(deployer, 'WMATIC', BONSAI_CONTRACT_POLYGON); // just need #approve()
tx = await token.approve(actionModule.address, costWithFee);
console.log(`tx: ${tx.hash}`);
await tx.wait();
// to fetch and use the relevant merkle proof (if any)
let merkleProof;
let merkleProofIndex;
if (interestMerkleRoot != constants.HashZero) {
const response = await fetch(`https://api.madfi.xyz/prod/bonsai/merkle_proofs?profileId=${ADVERTISER_PROFILE_ID}`);
const res = await response.json();
const proof = res.merkleProofs.find(({ root }) => root === interestMerkleRoot);
if (!proof) throw new Error("the given profile does not have the correct proof for this space");
merkleProof = proof.proof;
merkleProofIndex = proof.index;
}
const actionModuleData = await encodeModuleActData(actionModule.address as `0x${string}`, {
duration: AD_DURATION,
costPerSecond: costPerSecond.toString(),
// adContentUri: `ipfs://${ipfsHash}` // an ad with new content uri
adPubId,
openActionModule,
merkleProof,
merkleProofIndex
});
const inputStruct = {
publicationActedProfileId: CREATOR_PROFILE_ID,
publicationActedId: POINTED_PUB_ID,
actorProfileId: ADVERTISER_PROFILE_ID,
referrerProfileIds: [],
referrerPubIds: [],
referrerPubTypes: [],
actionModuleAddress: actionModule.address,
actionModuleData,
};
console.log(`lensHub.act (${lensHub.address})`);
console.log(JSON.stringify(inputStruct, null, 2));
tx = await lensHub.act(inputStruct, { gasLimit: 800_000 });
console.log(`tx: ${tx.hash}`);
await tx.wait();
console.log("done!");
});
import { task } from 'hardhat/config';
import '@nomicfoundation/hardhat-ethers';
import { constants } from 'ethers';
import { production, LensClient, encodeData } from '@lens-protocol/client';
import {
LENS_HUB_ADDRESS,
// LENS_HUB_TESTNET_ADDRESS,
getLensHubDeployed,
publicationBody,
} from '../helpers/lens';
import { addJson } from '../helpers/storj';
import { getActionModule } from '../helpers/madfi';
const ACTION_MODULE = 'RentableSpaceAction';
const BONSAI_CONTRACT_POLYGON = "0x3d2bD0e15829AA5C362a4144FdF4A1112fa29B5c";
interface ModuleInitData {
currency: string;
allowOpenAction: boolean;
costPerSecond: string;
expireAt?: string | null;
clientFeePerActBps?: number | null;
referralFeePerActBps?: number | null;
interestMerkleRoot?: string | null;
}
const encodeModuleInitData = async (actionModule: `0x${string}`, data: ModuleInitData): Promise<string> => {
const lensClient = new LensClient({ environment: production });
const result = await lensClient.modules.fetchMetadata({ implementation: actionModule });
if (!result) throw new Error(`no metadata indexed for action module: ${actionModule}`);
const expireAt = data.expireAt || "0";
const clientFeePerActBps = data.clientFeePerActBps?.toString() || "0";
const referralFeePerActBps = data.referralFeePerActBps?.toString() || "0"
const interestMerkleRoot = data.interestMerkleRoot || constants.HashZero;
return encodeData(
JSON.parse(result.metadata.initializeCalldataABI),
[
data.currency,
data.allowOpenAction,
data.costPerSecond,
expireAt,
clientFeePerActBps,
referralFeePerActBps,
interestMerkleRoot
]
);
}
task('create-post-rentable', 'creates a lens post with our rentable space action module').setAction(async ({ }, { ethers }) => {
const [_, deployer] = await ethers.getSigners();
const deployerAddress = await deployer.getAddress();
const { lensHub } = await getLensHubDeployed(deployer, LENS_HUB_ADDRESS);
const actionModule = await getActionModule(ethers, deployer, ACTION_MODULE);
let tx;
// const CREATOR_PROFILE_ID = 8640; // lens/madfinance
const CREATOR_PROFILE_ID = 422 // lens/carlosbeltran
// [ONLY ONCE] need to set the open action as a delegated executor to auto-mirror the post whenever an advertiser buys space
console.log('lensHub.changeDelegatedExecutorsConfig');
tx = await lensHub.functions["changeDelegatedExecutorsConfig(uint256,address[],bool[])"](CREATOR_PROFILE_ID, [actionModule.address], [true], { gasLimit: 500_000 });
// tx = await lensHub.changeDelegatedExecutorsConfig(CREATOR_PROFILE_ID, [actionModule.address], [true]);
console.log(`tx: ${tx.hash}`);
await tx.wait();
// to fetch and use the first merkle list available (reputation score > 7500)
const response = await fetch('https://api.madfi.xyz/prod/bonsai/merkle_roots');
const res = await response.json();
const interestMerkleRoot = res.merkleRoots[0].root;
const actionModuleInitData = await encodeModuleInitData(actionModule.address as `0x${string}`, {
currency: BONSAI_CONTRACT_POLYGON, // $bonsai
allowOpenAction: true, // allow collects & other open actions to be part of the ad
costPerSecond: ethers.utils.parseEther('1').toString(), // 1 $bonsai per second
referralFeePerActBps: 250, // 2.5% referral fee
clientFeePerActBps: 250, // 2.5% client integration fee
interestMerkleRoot // reputation score > 7500
});
const metadata = publicationBody('text', { content: "rentable billboard where only profiles with reputation score > 7500 can promote" }, []);
const ipfsHash = await addJson(metadata);
console.log(`ipfsHash: ${ipfsHash}`);
console.log(`creating post with action module ${ACTION_MODULE} (at: ${actionModule.address})`);
const inputStruct = {
profileId: CREATOR_PROFILE_ID,
contentURI: `ipfs://${ipfsHash}`,
actionModules: [actionModule.address],
actionModulesInitDatas: [actionModuleInitData],
referenceModule: ethers.constants.AddressZero,
referenceModuleInitData: "0x",
};
console.log(`lensHub.post (${lensHub.address})`);
console.log(JSON.stringify(inputStruct, null, 2));
tx = await lensHub.post(inputStruct, { gasLimit: 500_000 });
console.log(`tx: ${tx.hash}`);
await tx.wait();
console.log("done!");
});
@imthatcarlos
Copy link
Author

successful Lens post: https://hey.xyz/posts/0x01a6-0x038b

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment