-
-
Save imthatcarlos/c4ad457635c630ecc38eacfe5f236dc5 to your computer and use it in GitHub Desktop.
hardhat scripts for rentable billboard open action
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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!"); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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!"); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
successful Lens post: https://hey.xyz/posts/0x01a6-0x038b