diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index f23e95e..c81eaa0 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -2,8 +2,8 @@ name: Build & Deploy on: pull_request: - branches: [ main, master, develop, stage ] - types: [ closed ] + branches: [main, master, develop, stage] + types: [closed] env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} @@ -258,7 +258,7 @@ jobs: --image-uri ${{ steps.login-ecr.outputs.registry }}/$ECR_REPOSITORY:$IMAGE_TAG env: IMAGE_TAG: v${{ needs.build-and-publish.outputs.package-version }} - + deploy-daily-summary-email-dev: runs-on: 'ubuntu-latest' environment: 'dev' @@ -275,22 +275,6 @@ jobs: env: IMAGE_TAG: v${{ needs.build-and-publish.outputs.package-version }} - deploy-billboard-clear-auction-and-distribute-tax-dev: - runs-on: 'ubuntu-latest' - environment: 'dev' - needs: build-and-publish - steps: - - name: Login to Amazon ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v1 - - - name: re-deploy Lambda to dev - run: | - aws lambda update-function-code --function-name billboard-clear-auction-and-distribute-tax-dev \ - --image-uri ${{ steps.login-ecr.outputs.registry }}/$ECR_REPOSITORY:$IMAGE_TAG - env: - IMAGE_TAG: v${{ needs.build-and-publish.outputs.package-version }} - deploy-check-nomad-badge-dev: runs-on: 'ubuntu-latest' environment: 'dev' diff --git a/bin/qf-calculate.ts b/bin/qf-calculate.ts index 63d64a3..2ccffc1 100644 --- a/bin/qf-calculate.ts +++ b/bin/qf-calculate.ts @@ -36,7 +36,6 @@ async function main() { fromBlock, toBlock, amountTotal, - write_gist: true, // for server side running output; }) console.log(new Date(), 'res:', res) } diff --git a/handlers/clear-auction-and-distribute-tax.ts b/handlers/clear-auction-and-distribute-tax.ts deleted file mode 100644 index f7e3675..0000000 --- a/handlers/clear-auction-and-distribute-tax.ts +++ /dev/null @@ -1,158 +0,0 @@ -import { SimulateContractErrorType, formatUnits } from 'viem' -import { Context, APIGatewayProxyResult, APIGatewayEvent } from 'aws-lambda' - -import { - billboardContract, - getClearableAuctions, - distributionContract, - publicClient, - walletClient, -} from '../lib/billboard/index.js' -import { SLACK_MESSAGE_STATE, Slack } from '../lib/utils/slack.js' - -const notifySlack = process.env.DO_NOTIFY_SLACK === 'true' - -type Step = 'clearAuctions' | 'withdrawTax' | 'drop' - -type RequestBody = { - accessToken: string // to access this API - fromTokenId: string // bigint - toTokenId: string // bigint - merkleRoot: string - fromStep?: Step - tax?: string // bigint -} - -export const handler = async ( - event: APIGatewayEvent, - context: Context -): Promise => { - console.log(`Event: ${JSON.stringify(event, null, 2)}`) - console.log(`Context: ${JSON.stringify(context, null, 2)}`) - - const slack = new Slack() - const body = (event.body ? JSON.parse(event.body) : {}) as RequestBody - const fromTokenId = BigInt(body.fromTokenId || 0) - const toTokenId = BigInt(body.toTokenId || 0) - const merkleRoot = body.merkleRoot as `0x${string}` - const treeId = merkleRoot - const fromStep: Step = body.fromStep || 'clearAuctions' - console.log(new Date(), `from input:`, { - fromTokenId, - toTokenId, - merkleRoot, - treeId, - fromStep, - }) - - if (body.accessToken !== process.env.ACCESS_TOKEN) { - return { - statusCode: 403, - body: JSON.stringify({ message: 'invalid access token' }), - } - } - - const taxAllocation = process.env.TAX_ALLOCATION - if (!taxAllocation) { - return { - statusCode: 400, - body: JSON.stringify({ message: 'no tax allocation' }), - } - } - - try { - // Step 1: clear auctions - let clearedAuctions: string[] = [] - if (fromStep === 'clearAuctions') { - const isInvalidTokenIds = - fromTokenId <= 0 || toTokenId <= 0 || toTokenId < fromTokenId - if (isInvalidTokenIds) { - return { - statusCode: 400, - body: JSON.stringify({ error: 'invalid params' }), - } - } - - // get auctions to be cleared - const auctions = await getClearableAuctions({ fromTokenId, toTokenId }) - if (!auctions || auctions.length <= 0) { - return { - statusCode: 200, - body: JSON.stringify({ message: 'no auction to clear.' }), - } - } - clearedAuctions = auctions.map(({ tokenId }) => tokenId.toString()) - - const clearAuctionsResult = await publicClient.simulateContract({ - ...billboardContract, - functionName: 'clearAuctions', - args: [auctions.map(({ tokenId }) => tokenId)], - }) - await walletClient.writeContract(clearAuctionsResult.request) - await delay((2 + Math.random() * 10) * 1e3) // wait the nonce to be set correctly for next call - } - - // Step 2: withdraw tax - let tax: bigint = BigInt(body.tax || 0) - if (fromStep === 'clearAuctions' || fromStep === 'withdrawTax') { - const withdrawTaxResult = await publicClient.simulateContract({ - ...billboardContract, - functionName: 'withdrawTax', - }) - await walletClient.writeContract(withdrawTaxResult.request) - tax = withdrawTaxResult.result - await delay((2 + Math.random() * 10) * 1e3) // wait the nonce to be set correctly for next call - } - - // Step 3: create new drop with merkle root and tax - const taxToDrop = (tax * BigInt(taxAllocation)) / BigInt(100) - if ( - fromStep === 'clearAuctions' || - fromStep === 'withdrawTax' || - fromStep === 'drop' - ) { - const dropResult = await publicClient.simulateContract({ - ...distributionContract, - functionName: 'drop', - args: [treeId, merkleRoot, taxToDrop], - }) - await walletClient.writeContract(dropResult.request) - } - - // response - const data = { - auctionIds: clearedAuctions, - totalTax: tax.toString(), - taxToDrop: taxToDrop.toString(), - merkleRoot, - } - await slack.sendStripeAlert({ - data, - message: `Drop ${treeId} with USDT ${formatUnits(taxToDrop, 6)}.`, - state: SLACK_MESSAGE_STATE.successful, - }) - return { - statusCode: 200, - body: JSON.stringify({ message: 'done.', data }), - } - } catch (err) { - const error = err as SimulateContractErrorType - console.error(error.name, err) - - const data = { - name: error.name, - message: error.message, - cause: error.cause, - } - notifySlack && (await slack.sendStripeAlert({ data, message: error.name })) // too long error stack is not showing well on Slack, better to read in CloudWatch logs... - - return { - statusCode: 500, - body: JSON.stringify({ data, message: error.name }), - } - } -} - -function delay(ms: number) { - return new Promise((fulfilled) => setTimeout(fulfilled, ms)) -} diff --git a/handlers/qf-calculate.ts b/handlers/qf-calculate.ts index 591c66c..b663086 100644 --- a/handlers/qf-calculate.ts +++ b/handlers/qf-calculate.ts @@ -4,6 +4,7 @@ import { formatUnits } from 'viem' import * as d3 from 'd3-array' import { calculateQFScore, + finalizeQFScore, MattersBillboardS3Bucket, isProd, s3FilePathPrefix, @@ -12,7 +13,7 @@ import { sendQfNotifications, // sendQfNotificationNEmails, } from '../lib/qf-notify.js' import { s3GetFile } from '../lib/utils/aws.js' -import { SLACK_MESSAGE_STATE, Slack } from '../lib/utils/slack.js' +// import { SLACK_MESSAGE_STATE, Slack } from '../lib/utils/slack.js' const ACCESS_TOKEN = `${process.env.ACCESS_TOKEN}` @@ -35,7 +36,7 @@ export const handler = async ( console.log(`Context: ${JSON.stringify(context, null, 2)}`) // const forceRun = !!("forceRun" in ((event?.queryStringParameters as any) || {})); - const slack = new Slack() + // const slack = new Slack() const { method, path } = ((event?.requestContext as any)?.http as any) || {} const queryStringParameters = (event?.queryStringParameters as any) || {} const { @@ -58,6 +59,7 @@ export const handler = async ( event?.forceRun || (path === '/qf-calculator' && accept?.startsWith('application/json')) || (path === '/send-notifications' && accept) || + (path === '/qf-finalize' && accept) || (path === '/get-rounds' && accept) ) ) { @@ -161,20 +163,20 @@ export const handler = async ( amountTotal, 6 )} USDT to ${sent?.length ?? 0} authors` - if (Array.isArray(sent) && sent?.length > 0) { - await slack.sendStripeAlert({ - data: { - amountTotal, - sent: sent.length, - distrib: sent.map( - ({ userName, displayName, amount }: any) => - `${displayName} @${userName} ${amount} USDT` - ), - }, - message, - state: SLACK_MESSAGE_STATE.successful, - }) - } + // if (Array.isArray(sent) && sent?.length > 0) { + // await slack.sendStripeAlert({ + // data: { + // amountTotal, + // sent: sent.length, + // distrib: sent.map( + // ({ userName, displayName, amount }: any) => + // `${displayName} @${userName} ${amount} USDT` + // ), + // }, + // message, + // state: SLACK_MESSAGE_STATE.successful, + // }) + // } return { statusCode: 200, body: message, @@ -191,11 +193,11 @@ export const handler = async ( path === '/qf-calculator' && accept?.startsWith('application/json') ) { - const { fromTime, toTime, fromBlock, toBlock, amountTotal, finalize } = ( + const { fromBlock, toBlock, amountTotal, finalize } = ( event?.forceRun ? event : queryStringParameters ) as InputBodyParameters - const { root, gist_url } = + const { root } = (await calculateQFScore({ // fromTime, toTime, fromBlock: BigInt(fromBlock), @@ -218,7 +220,47 @@ export const handler = async ( body: JSON.stringify({ message: 'done.', root, // tree - gist_url, + }), + } + } else if ( + method === 'POST' && + path === '/qf-finalize' && + accept?.startsWith('application/json') + ) { + const { fromBlock, toBlock } = ( + event?.forceRun ? event : queryStringParameters + ) as InputBodyParameters + + if (!fromBlock || !toBlock) { + return { + statusCode: 400, + body: JSON.stringify({ + message: 'bad parameters, fromBlock and toBlock are required.', + }), + } + } + + const { root } = + (await finalizeQFScore({ + fromBlock: BigInt(fromBlock), + toBlock: BigInt(toBlock), + })) || {} + + if (!root) { + return { + statusCode: 400, + body: JSON.stringify({ + message: 'bad parameters, no tree root, check logs for details.', + }), + } + } + + return { + statusCode: 200, + headers: { 'content-type': 'application/json; charset=utf-8' }, + body: JSON.stringify({ + message: 'done.', + root, }), } } diff --git a/lib/billboard/abi.ts b/lib/billboard/abi.ts deleted file mode 100644 index 324fdd0..0000000 --- a/lib/billboard/abi.ts +++ /dev/null @@ -1,2555 +0,0 @@ -export const billboardAbi = [ - { - inputs: [ - { - internalType: 'address', - name: 'token_', - type: 'address', - }, - { - internalType: 'address payable', - name: 'registry_', - type: 'address', - }, - { - internalType: 'uint256', - name: 'taxRate_', - type: 'uint256', - }, - { - internalType: 'uint64', - name: 'leaseTerm_', - type: 'uint64', - }, - { - internalType: 'string', - name: 'name_', - type: 'string', - }, - { - internalType: 'string', - name: 'symbol_', - type: 'string', - }, - ], - stateMutability: 'nonpayable', - type: 'constructor', - }, - { - inputs: [ - { - internalType: 'address', - name: 'value_', - type: 'address', - }, - ], - name: 'addToWhitelist', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [], - name: 'admin', - outputs: [ - { - internalType: 'address', - name: '', - type: 'address', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'amount_', - type: 'uint256', - }, - ], - name: 'calculateTax', - outputs: [ - { - internalType: 'uint256', - name: 'tax', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - ], - name: 'clearAuction', - outputs: [ - { - internalType: 'uint256', - name: 'price', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'tax', - type: 'uint256', - }, - ], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256[]', - name: 'tokenIds_', - type: 'uint256[]', - }, - ], - name: 'clearAuctions', - outputs: [ - { - internalType: 'uint256[]', - name: 'prices', - type: 'uint256[]', - }, - { - internalType: 'uint256[]', - name: 'taxes', - type: 'uint256[]', - }, - ], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'auctionId_', - type: 'uint256', - }, - ], - name: 'getAuction', - outputs: [ - { - components: [ - { - internalType: 'uint64', - name: 'startAt', - type: 'uint64', - }, - { - internalType: 'uint64', - name: 'endAt', - type: 'uint64', - }, - { - internalType: 'uint64', - name: 'leaseStartAt', - type: 'uint64', - }, - { - internalType: 'uint64', - name: 'leaseEndAt', - type: 'uint64', - }, - { - internalType: 'address', - name: 'highestBidder', - type: 'address', - }, - ], - internalType: 'struct IBillboardRegistry.Auction', - name: 'auction', - type: 'tuple', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'auctionId_', - type: 'uint256', - }, - { - internalType: 'address', - name: 'bidder_', - type: 'address', - }, - ], - name: 'getBid', - outputs: [ - { - components: [ - { - internalType: 'uint256', - name: 'price', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'tax', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'placedAt', - type: 'uint256', - }, - { - internalType: 'bool', - name: 'isWon', - type: 'bool', - }, - { - internalType: 'bool', - name: 'isWithdrawn', - type: 'bool', - }, - ], - internalType: 'struct IBillboardRegistry.Bid', - name: 'bid', - type: 'tuple', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'auctionId_', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'limit_', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'offset_', - type: 'uint256', - }, - ], - name: 'getBids', - outputs: [ - { - internalType: 'uint256', - name: 'total', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'limit', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'offset', - type: 'uint256', - }, - { - components: [ - { - internalType: 'uint256', - name: 'price', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'tax', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'placedAt', - type: 'uint256', - }, - { - internalType: 'bool', - name: 'isWon', - type: 'bool', - }, - { - internalType: 'bool', - name: 'isWithdrawn', - type: 'bool', - }, - ], - internalType: 'struct IBillboardRegistry.Bid[]', - name: 'bids', - type: 'tuple[]', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - ], - name: 'getBoard', - outputs: [ - { - components: [ - { - internalType: 'address', - name: 'creator', - type: 'address', - }, - { - internalType: 'string', - name: 'name', - type: 'string', - }, - { - internalType: 'string', - name: 'description', - type: 'string', - }, - { - internalType: 'string', - name: 'location', - type: 'string', - }, - { - internalType: 'string', - name: 'contentURI', - type: 'string', - }, - { - internalType: 'string', - name: 'redirectURI', - type: 'string', - }, - ], - internalType: 'struct IBillboardRegistry.Board', - name: 'board', - type: 'tuple', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'getTaxRate', - outputs: [ - { - internalType: 'uint256', - name: 'taxRate', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'isOpened', - outputs: [ - { - internalType: 'bool', - name: '', - type: 'bool', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'to_', - type: 'address', - }, - ], - name: 'mintBoard', - outputs: [ - { - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - ], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'amount_', - type: 'uint256', - }, - ], - name: 'placeBid', - outputs: [], - stateMutability: 'payable', - type: 'function', - }, - { - inputs: [], - name: 'registry', - outputs: [ - { - internalType: 'contract BillboardRegistry', - name: '', - type: 'address', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'value_', - type: 'address', - }, - ], - name: 'removeFromWhitelist', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - { - internalType: 'string', - name: 'contentURI_', - type: 'string', - }, - ], - name: 'setBoardContentURI', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - { - internalType: 'string', - name: 'description_', - type: 'string', - }, - ], - name: 'setBoardDescription', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - { - internalType: 'string', - name: 'location_', - type: 'string', - }, - ], - name: 'setBoardLocation', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - { - internalType: 'string', - name: 'name_', - type: 'string', - }, - ], - name: 'setBoardName', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - { - internalType: 'string', - name: 'redirectURI_', - type: 'string', - }, - ], - name: 'setBoardRedirectURI', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'bool', - name: 'value_', - type: 'bool', - }, - ], - name: 'setIsOpened', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'operator_', - type: 'address', - }, - ], - name: 'setRegistryOperator', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'taxRate_', - type: 'uint256', - }, - ], - name: 'setTaxRate', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: '', - type: 'address', - }, - ], - name: 'whitelist', - outputs: [ - { - internalType: 'bool', - name: '', - type: 'bool', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'auctionId_', - type: 'uint256', - }, - ], - name: 'withdrawBid', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [], - name: 'withdrawTax', - outputs: [ - { - internalType: 'uint256', - name: 'tax', - type: 'uint256', - }, - ], - stateMutability: 'nonpayable', - type: 'function', - }, -] as const - -export const billboardRegistryAbi = [ - { - inputs: [ - { - internalType: 'address', - name: 'token_', - type: 'address', - }, - { - internalType: 'address', - name: 'operator_', - type: 'address', - }, - { - internalType: 'uint256', - name: 'taxRate_', - type: 'uint256', - }, - { - internalType: 'uint64', - name: 'leaseTerm_', - type: 'uint64', - }, - { - internalType: 'string', - name: 'name_', - type: 'string', - }, - { - internalType: 'string', - name: 'symbol_', - type: 'string', - }, - ], - stateMutability: 'nonpayable', - type: 'constructor', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'address', - name: 'owner', - type: 'address', - }, - { - indexed: true, - internalType: 'address', - name: 'approved', - type: 'address', - }, - { - indexed: true, - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - ], - name: 'Approval', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'address', - name: 'owner', - type: 'address', - }, - { - indexed: true, - internalType: 'address', - name: 'operator', - type: 'address', - }, - { - indexed: false, - internalType: 'bool', - name: 'approved', - type: 'bool', - }, - ], - name: 'ApprovalForAll', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - { - indexed: true, - internalType: 'uint256', - name: 'auctionId', - type: 'uint256', - }, - { - indexed: true, - internalType: 'address', - name: 'highestBidder', - type: 'address', - }, - { - indexed: false, - internalType: 'uint64', - name: 'leaseStartAt', - type: 'uint64', - }, - { - indexed: false, - internalType: 'uint64', - name: 'leaseEndAt', - type: 'uint64', - }, - ], - name: 'AuctionCleared', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - { - indexed: true, - internalType: 'uint256', - name: 'auctionId', - type: 'uint256', - }, - { - indexed: false, - internalType: 'uint64', - name: 'startAt', - type: 'uint64', - }, - { - indexed: false, - internalType: 'uint64', - name: 'endAt', - type: 'uint64', - }, - ], - name: 'AuctionCreated', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - { - indexed: true, - internalType: 'uint256', - name: 'auctionId', - type: 'uint256', - }, - { - indexed: true, - internalType: 'address', - name: 'bidder', - type: 'address', - }, - { - indexed: false, - internalType: 'uint256', - name: 'price', - type: 'uint256', - }, - { - indexed: false, - internalType: 'uint256', - name: 'tax', - type: 'uint256', - }, - ], - name: 'BidCreated', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - { - indexed: true, - internalType: 'uint256', - name: 'auctionId', - type: 'uint256', - }, - { - indexed: true, - internalType: 'address', - name: 'bidder', - type: 'address', - }, - { - indexed: false, - internalType: 'uint256', - name: 'price', - type: 'uint256', - }, - { - indexed: false, - internalType: 'uint256', - name: 'tax', - type: 'uint256', - }, - ], - name: 'BidWithdrawn', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - { - indexed: true, - internalType: 'uint256', - name: 'auctionId', - type: 'uint256', - }, - { - indexed: true, - internalType: 'address', - name: 'bidder', - type: 'address', - }, - ], - name: 'BidWon', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - { - indexed: false, - internalType: 'string', - name: 'contentURI', - type: 'string', - }, - ], - name: 'BoardContentURIUpdated', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - { - indexed: false, - internalType: 'string', - name: 'description', - type: 'string', - }, - ], - name: 'BoardDescriptionUpdated', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - { - indexed: false, - internalType: 'string', - name: 'location', - type: 'string', - }, - ], - name: 'BoardLocationUpdated', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - { - indexed: false, - internalType: 'string', - name: 'name', - type: 'string', - }, - ], - name: 'BoardNameUpdated', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - { - indexed: false, - internalType: 'string', - name: 'redirectURI', - type: 'string', - }, - ], - name: 'BoardRedirectURIUpdated', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'address', - name: 'operator', - type: 'address', - }, - ], - name: 'OperatorUpdated', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: 'uint256', - name: 'taxRate', - type: 'uint256', - }, - ], - name: 'TaxRateUpdated', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'address', - name: 'owner', - type: 'address', - }, - { - indexed: false, - internalType: 'uint256', - name: 'amount', - type: 'uint256', - }, - ], - name: 'TaxWithdrawn', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'address', - name: 'from', - type: 'address', - }, - { - indexed: true, - internalType: 'address', - name: 'to', - type: 'address', - }, - { - indexed: true, - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - ], - name: 'Transfer', - type: 'event', - }, - { - inputs: [ - { - internalType: 'address', - name: 'to', - type: 'address', - }, - { - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - ], - name: 'approve', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - name: 'auctionBidders', - outputs: [ - { - internalType: 'address', - name: '', - type: 'address', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - { - internalType: 'address', - name: '', - type: 'address', - }, - ], - name: 'auctionBids', - outputs: [ - { - internalType: 'uint256', - name: 'price', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'tax', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'placedAt', - type: 'uint256', - }, - { - internalType: 'bool', - name: 'isWon', - type: 'bool', - }, - { - internalType: 'bool', - name: 'isWithdrawn', - type: 'bool', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'owner', - type: 'address', - }, - ], - name: 'balanceOf', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - name: 'boardAuctions', - outputs: [ - { - internalType: 'uint64', - name: 'startAt', - type: 'uint64', - }, - { - internalType: 'uint64', - name: 'endAt', - type: 'uint64', - }, - { - internalType: 'uint64', - name: 'leaseStartAt', - type: 'uint64', - }, - { - internalType: 'uint64', - name: 'leaseEndAt', - type: 'uint64', - }, - { - internalType: 'address', - name: 'highestBidder', - type: 'address', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - name: 'boards', - outputs: [ - { - internalType: 'address', - name: 'creator', - type: 'address', - }, - { - internalType: 'string', - name: 'name', - type: 'string', - }, - { - internalType: 'string', - name: 'description', - type: 'string', - }, - { - internalType: 'string', - name: 'location', - type: 'string', - }, - { - internalType: 'string', - name: 'contentURI', - type: 'string', - }, - { - internalType: 'string', - name: 'redirectURI', - type: 'string', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'auctionId_', - type: 'uint256', - }, - { - internalType: 'address', - name: 'highestBidder_', - type: 'address', - }, - { - internalType: 'uint64', - name: 'leaseStartAt_', - type: 'uint64', - }, - { - internalType: 'uint64', - name: 'leaseEndAt_', - type: 'uint64', - }, - ], - name: 'emitAuctionCleared', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'auctionId_', - type: 'uint256', - }, - { - internalType: 'address', - name: 'bidder_', - type: 'address', - }, - { - internalType: 'uint256', - name: 'price_', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'tax_', - type: 'uint256', - }, - ], - name: 'emitBidWithdrawn', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'owner_', - type: 'address', - }, - { - internalType: 'uint256', - name: 'amount_', - type: 'uint256', - }, - ], - name: 'emitTaxWithdrawn', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - ], - name: 'getApproved', - outputs: [ - { - internalType: 'address', - name: '', - type: 'address', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'auctionId_', - type: 'uint256', - }, - ], - name: 'getAuction', - outputs: [ - { - components: [ - { - internalType: 'uint64', - name: 'startAt', - type: 'uint64', - }, - { - internalType: 'uint64', - name: 'endAt', - type: 'uint64', - }, - { - internalType: 'uint64', - name: 'leaseStartAt', - type: 'uint64', - }, - { - internalType: 'uint64', - name: 'leaseEndAt', - type: 'uint64', - }, - { - internalType: 'address', - name: 'highestBidder', - type: 'address', - }, - ], - internalType: 'struct IBillboardRegistry.Auction', - name: 'auction', - type: 'tuple', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'auctionId_', - type: 'uint256', - }, - { - internalType: 'address', - name: 'bidder_', - type: 'address', - }, - ], - name: 'getBid', - outputs: [ - { - components: [ - { - internalType: 'uint256', - name: 'price', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'tax', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'placedAt', - type: 'uint256', - }, - { - internalType: 'bool', - name: 'isWon', - type: 'bool', - }, - { - internalType: 'bool', - name: 'isWithdrawn', - type: 'bool', - }, - ], - internalType: 'struct IBillboardRegistry.Bid', - name: 'bid', - type: 'tuple', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'auctionId_', - type: 'uint256', - }, - ], - name: 'getBidCount', - outputs: [ - { - internalType: 'uint256', - name: 'count', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - ], - name: 'getBoard', - outputs: [ - { - components: [ - { - internalType: 'address', - name: 'creator', - type: 'address', - }, - { - internalType: 'string', - name: 'name', - type: 'string', - }, - { - internalType: 'string', - name: 'description', - type: 'string', - }, - { - internalType: 'string', - name: 'location', - type: 'string', - }, - { - internalType: 'string', - name: 'contentURI', - type: 'string', - }, - { - internalType: 'string', - name: 'redirectURI', - type: 'string', - }, - ], - internalType: 'struct IBillboardRegistry.Board', - name: 'board', - type: 'tuple', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'owner', - type: 'address', - }, - { - internalType: 'address', - name: 'operator', - type: 'address', - }, - ], - name: 'isApprovedForAll', - outputs: [ - { - internalType: 'bool', - name: '', - type: 'bool', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'lastTokenId', - outputs: [ - { - internalType: 'uint256', - name: '_value', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'leaseTerm', - outputs: [ - { - internalType: 'uint64', - name: '', - type: 'uint64', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'to_', - type: 'address', - }, - ], - name: 'mintBoard', - outputs: [ - { - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - ], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [], - name: 'name', - outputs: [ - { - internalType: 'string', - name: '', - type: 'string', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - { - internalType: 'uint64', - name: 'startAt_', - type: 'uint64', - }, - { - internalType: 'uint64', - name: 'endAt_', - type: 'uint64', - }, - ], - name: 'newAuction', - outputs: [ - { - internalType: 'uint256', - name: 'newAuctionId', - type: 'uint256', - }, - ], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'auctionId_', - type: 'uint256', - }, - { - internalType: 'address', - name: 'bidder_', - type: 'address', - }, - { - internalType: 'uint256', - name: 'price_', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'tax_', - type: 'uint256', - }, - ], - name: 'newBid', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - name: 'nextBoardAuctionId', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'operator', - outputs: [ - { - internalType: 'address', - name: '', - type: 'address', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - ], - name: 'ownerOf', - outputs: [ - { - internalType: 'address', - name: '', - type: 'address', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'from_', - type: 'address', - }, - { - internalType: 'address', - name: 'to_', - type: 'address', - }, - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - ], - name: 'safeTransferByOperator', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'from', - type: 'address', - }, - { - internalType: 'address', - name: 'to', - type: 'address', - }, - { - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - ], - name: 'safeTransferFrom', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'from', - type: 'address', - }, - { - internalType: 'address', - name: 'to', - type: 'address', - }, - { - internalType: 'uint256', - name: 'tokenId', - type: 'uint256', - }, - { - internalType: 'bytes', - name: 'data', - type: 'bytes', - }, - ], - name: 'safeTransferFrom', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'operator', - type: 'address', - }, - { - internalType: 'bool', - name: 'approved', - type: 'bool', - }, - ], - name: 'setApprovalForAll', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'auctionId_', - type: 'uint256', - }, - { - internalType: 'uint64', - name: 'leaseStartAt_', - type: 'uint64', - }, - { - internalType: 'uint64', - name: 'leaseEndAt_', - type: 'uint64', - }, - ], - name: 'setAuctionLease', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'auctionId_', - type: 'uint256', - }, - { - internalType: 'address', - name: 'bidder_', - type: 'address', - }, - { - internalType: 'bool', - name: 'isWithdrawn_', - type: 'bool', - }, - ], - name: 'setBidWithdrawn', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'auctionId_', - type: 'uint256', - }, - { - internalType: 'address', - name: 'bidder_', - type: 'address', - }, - { - internalType: 'bool', - name: 'isWon_', - type: 'bool', - }, - ], - name: 'setBidWon', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - { - internalType: 'string', - name: 'contentURI_', - type: 'string', - }, - ], - name: 'setBoardContentURI', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - { - internalType: 'string', - name: 'description_', - type: 'string', - }, - ], - name: 'setBoardDescription', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - { - internalType: 'string', - name: 'location_', - type: 'string', - }, - ], - name: 'setBoardLocation', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - { - internalType: 'string', - name: 'name_', - type: 'string', - }, - ], - name: 'setBoardName', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - { - internalType: 'string', - name: 'redirectURI_', - type: 'string', - }, - ], - name: 'setBoardRedirectURI', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'operator_', - type: 'address', - }, - ], - name: 'setOperator', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'taxRate_', - type: 'uint256', - }, - ], - name: 'setTaxRate', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'owner_', - type: 'address', - }, - { - internalType: 'uint256', - name: 'accumulated_', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'withdrawn_', - type: 'uint256', - }, - ], - name: 'setTaxTreasury', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'bytes4', - name: 'interfaceId', - type: 'bytes4', - }, - ], - name: 'supportsInterface', - outputs: [ - { - internalType: 'bool', - name: '', - type: 'bool', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'symbol', - outputs: [ - { - internalType: 'string', - name: '', - type: 'string', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'taxRate', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: '', - type: 'address', - }, - ], - name: 'taxTreasury', - outputs: [ - { - internalType: 'uint256', - name: 'accumulated', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'withdrawn', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'token', - outputs: [ - { - internalType: 'contract IERC20', - name: '', - type: 'address', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - ], - name: 'tokenURI', - outputs: [ - { - internalType: 'string', - name: 'uri', - type: 'string', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'to_', - type: 'address', - }, - { - internalType: 'uint256', - name: 'amount_', - type: 'uint256', - }, - ], - name: 'transferAmount', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'from_', - type: 'address', - }, - { - internalType: 'address', - name: 'to_', - type: 'address', - }, - { - internalType: 'uint256', - name: 'tokenId_', - type: 'uint256', - }, - ], - name: 'transferFrom', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - stateMutability: 'payable', - type: 'receive', - }, -] as const - -export const distributionAbi = [ - { - inputs: [ - { - internalType: 'address', - name: 'token_', - type: 'address', - }, - { - internalType: 'address', - name: 'admin_', - type: 'address', - }, - ], - stateMutability: 'nonpayable', - type: 'constructor', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'address', - name: 'prevAccount_', - type: 'address', - }, - { - indexed: true, - internalType: 'address', - name: 'account_', - type: 'address', - }, - ], - name: 'AdminChanged', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'string', - name: 'cid_', - type: 'string', - }, - { - indexed: true, - internalType: 'address', - name: 'account_', - type: 'address', - }, - { - indexed: false, - internalType: 'uint256', - name: 'amount_', - type: 'uint256', - }, - ], - name: 'Claim', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'string', - name: 'treeId_', - type: 'string', - }, - { - indexed: false, - internalType: 'uint256', - name: 'amount_', - type: 'uint256', - }, - ], - name: 'Drop', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'address', - name: 'previousOwner', - type: 'address', - }, - { - indexed: true, - internalType: 'address', - name: 'newOwner', - type: 'address', - }, - ], - name: 'OwnershipTransferred', - type: 'event', - }, - { - inputs: [], - name: 'admin', - outputs: [ - { - internalType: 'address', - name: '', - type: 'address', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'string', - name: '', - type: 'string', - }, - ], - name: 'balances', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: 'share_', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'totalAmount_', - type: 'uint256', - }, - ], - name: 'calculateAmount', - outputs: [ - { - internalType: 'uint256', - name: 'amount', - type: 'uint256', - }, - ], - stateMutability: 'pure', - type: 'function', - }, - { - inputs: [ - { - internalType: 'string', - name: 'treeId_', - type: 'string', - }, - { - internalType: 'string', - name: 'cid_', - type: 'string', - }, - { - internalType: 'address', - name: 'account_', - type: 'address', - }, - { - internalType: 'uint256', - name: 'share_', - type: 'uint256', - }, - { - internalType: 'bytes32[]', - name: 'merkleProof_', - type: 'bytes32[]', - }, - ], - name: 'claim', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'string', - name: 'treeId_', - type: 'string', - }, - { - internalType: 'bytes32', - name: 'merkleRoot_', - type: 'bytes32', - }, - { - internalType: 'uint256', - name: 'amount_', - type: 'uint256', - }, - ], - name: 'drop', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'string', - name: '', - type: 'string', - }, - { - internalType: 'string', - name: '', - type: 'string', - }, - { - internalType: 'address', - name: '', - type: 'address', - }, - ], - name: 'hasClaimed', - outputs: [ - { - internalType: 'bool', - name: '', - type: 'bool', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'string', - name: '', - type: 'string', - }, - ], - name: 'merkleRoots', - outputs: [ - { - internalType: 'bytes32', - name: '', - type: 'bytes32', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'owner', - outputs: [ - { - internalType: 'address', - name: '', - type: 'address', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'renounceOwnership', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'account_', - type: 'address', - }, - ], - name: 'setAdmin', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'string', - name: 'treeId_', - type: 'string', - }, - { - internalType: 'address', - name: 'target_', - type: 'address', - }, - ], - name: 'sweep', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [], - name: 'token', - outputs: [ - { - internalType: 'address', - name: '', - type: 'address', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'string', - name: '', - type: 'string', - }, - ], - name: 'totalAmounts', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'newOwner', - type: 'address', - }, - ], - name: 'transferOwnership', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, -] as const diff --git a/lib/billboard/contract.ts b/lib/billboard/contract.ts deleted file mode 100644 index d9c9fda..0000000 --- a/lib/billboard/contract.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { billboardAbi, billboardRegistryAbi, distributionAbi } from './abi.js' -import { publicClient, account } from './client.js' - -// number of auctions to be cleared at once -const BATCH_SIZE = 2 - -export const billboardContract = { - address: process.env.BILLBOARD_CONTRACT_ADDRESS as `0x${string}`, - abi: billboardAbi, - account, -} as const - -export const billboardRegsitryContract = { - address: process.env.BILLBOARD_REGISTRY_CONTRACT_ADDRESS as `0x${string}`, - abi: billboardRegistryAbi, -} as const - -export const distributionContract = { - address: process.env.BILLBOARD_DISTRIBUTION_CONTRACT_ADDRESS as `0x${string}`, - abi: distributionAbi, - account, -} as const - -type Auction = { tokenId: bigint; auctionId: bigint } - -export const getClearableAuctions = async ({ - fromTokenId, - toTokenId, -}: { - // token id range to check - fromTokenId: bigint - toTokenId: bigint -}): Promise => { - // get auctions to be cleared - const auctionIdsResults = await publicClient.multicall({ - contracts: Array.from( - { length: Number(toTokenId - fromTokenId) + 1 }, - (_, i) => ({ - ...billboardRegsitryContract, - functionName: 'nextBoardAuctionId', - args: [BigInt(i) + fromTokenId], - }) - ), - }) - - const auctionIds: Array<{ tokenId: bigint; auctionId: bigint }> = - auctionIdsResults - .filter(({ result }) => !!result && BigInt(result) > 0) - .map(({ result }, i) => ({ - tokenId: BigInt(i) + fromTokenId, - auctionId: BigInt(result!), - })) - - console.log('auctionIds to check: ', auctionIds) - - if (auctionIds.length <= 0) { - return [] - } - - const autionsResults = await publicClient.multicall({ - contracts: auctionIds.map(({ tokenId, auctionId }) => ({ - ...billboardRegsitryContract, - functionName: 'getAuction', - args: [tokenId, auctionId], - })), - }) - - const blockNow = await publicClient.getBlockNumber() - const auctions = autionsResults - .map(({ result }, i) => ({ - tokenId: auctionIds[i].tokenId, - auctionId: auctionIds[i].auctionId, - result, - })) - .filter(({ result, ...rest }) => { - if (!result?.startAt) { - console.log(new Date(), `filter-out of no result:`, result) - return false - } - if (!result.startAt) { - console.log(new Date(), `filter-out of no startAt:`, result) - return false - } - - // already ended - if (result.endAt > blockNow) { - console.log(new Date(), `filter-out blockNow not pass yet:`, { - result, - blockNow, - }) - return false - } - - // already cleared - if (result.leaseEndAt) { - console.log(new Date(), `filter-out leaseEnded:`, { result }) - return false - } - - console.log(new Date(), `keep this auction:`, { result, ...rest }) - return true - }) - .map(({ tokenId, auctionId }) => ({ tokenId, auctionId })) - .slice(0, BATCH_SIZE) - - console.log('auctionIds to clear: ', auctions) - - return auctions -} diff --git a/lib/billboard/index.ts b/lib/billboard/index.ts index 779c912..44ee804 100644 --- a/lib/billboard/index.ts +++ b/lib/billboard/index.ts @@ -1,3 +1,2 @@ -export * from './abi.js' export * from './client.js' -export * from './contract.js' +export * from './qf-thresholds.js' diff --git a/lib/qf-calculate.ts b/lib/qf-calculate.ts index 26c7c9e..fa15b15 100644 --- a/lib/qf-calculate.ts +++ b/lib/qf-calculate.ts @@ -24,8 +24,8 @@ import { AddressMattersOPSepoliaCurationContract, MattersCurationEvent, // EtherScanAPI, -} from './billboard/client.js' -import { checkSendersTrustPoints } from './billboard/qf-thresholds.js' + checkSendersTrustPoints, +} from './billboard/index.js' import { s3GetFile, s3PutFile } from '../lib/utils/aws.js' const siteDomain = process.env.MATTERS_SITE_DOMAIN || '' @@ -45,8 +45,6 @@ const knownCollectiveSenders = new Set( // ['imo_treasury'] ) -const GITHUB_TOKEN = process.env.GITHUB_TOKEN || '' // for servide side run only; - const UINT256_MAX_BIGINT = 0x0_ffff_ffff_ffff_ffff_ffff_ffff_ffff_ffff_ffff_ffff_ffff_ffff_ffff_ffff_ffff_ffffn // uint256max @@ -59,7 +57,6 @@ export async function calculateQFScore({ finalize, amountTotal = 250_000_000n, // 250 USDT sharesTotal = 10_000, - write_gist = true, }: { // between: string[]; fromTime?: Date @@ -69,10 +66,7 @@ export async function calculateQFScore({ amountTotal?: bigint sharesTotal?: number finalize?: boolean - write_gist?: boolean // for server side run only; }) { - const started = new Date() - const blockNumbers = await Promise.all([ publicClientDefault.getBlockNumber(), publicClientPolygonMainnet.getBlockNumber(), @@ -85,12 +79,14 @@ export async function calculateQFScore({ ) const [latestOPBlockNumber] = blockNumbers + // correct `toBlock` and `toTime` if it's in the future if (toBlock > latestOPBlockNumber) { toBlock = latestOPBlockNumber - toTime = started // .toISOString() + toTime = new Date() finalize = false // cannot finalize for future block } + // make `fromTime` and `toTime` { const res1 = await // EtherScanAPI.getBlockReward({ blockno: fromBlock }) publicClientDefault.getBlock({ blockNumber: fromBlock }) @@ -126,12 +122,12 @@ export async function calculateQFScore({ toBlock, }) - const contractAddress = isProd + const curationContractAddress = isProd ? AddressMattersOPCurationContract - : AddressMattersOPSepoliaCurationContract // '0x5edebbdae7b5c79a69aacf7873796bb1ec664db8', + : AddressMattersOPSepoliaCurationContract const logs = (await publicClientDefault.getLogs({ - address: contractAddress, + address: curationContractAddress, event: MattersCurationEvent, fromBlock, // : isProd ? 117058632n : 8438904n, // the contract creation block; toBlock, @@ -142,7 +138,7 @@ export async function calculateQFScore({ new Date(), `there aren't enough logs (${logs.length}) between the two block numbers, try again with a larger range:`, { - address: contractAddress, + address: curationContractAddress, event: MattersCurationEvent, fromBlock, toBlock, @@ -392,13 +388,9 @@ SELECT * FROM ( fromTime as Date )}\` ~ \`${dateFormat.format(toTime as Date)}\` (UTC+8)` - const gist: any = { - description: `Quadratic-Funding Calculations (${runningBetween})`, - public: false, - files: { - 'README.md': { - content: `During MattersCuration contract runtime between (to overwrite later ...) ...`, - }, + const files: any = { + 'README.md': { + content: `During MattersCuration contract runtime between (to overwrite later ...) ...`, }, } @@ -601,9 +593,9 @@ WHERE lower(sender.eth_address) =ANY(${Array.from(senderAddresses)}) sendersOut.set(k, obj) }) - const sendersContent = (gist.files[`senders.tsv`] = { + files[`senders.tsv`] = { content: tsvFormat(Array.from(sendersOut.values())), // bufs.join('\n'), - }) + } } const seqQualifiedGroups = d3.group( @@ -745,9 +737,9 @@ WHERE lower(sender.eth_address) =ANY(${Array.from(senderAddresses)}) ]) }) - const distrib = (gist.files[`distrib.tsv`] = { + files[`distrib.tsv`] = { content: tsvFormat(values), // bufs.join('\n'), - }) + } const distribByAuthor = d3.rollup( values, @@ -772,7 +764,8 @@ WHERE lower(sender.eth_address) =ANY(${Array.from(senderAddresses)}) }, (d) => d.userName ) - const distribAuthors = (gist.files[`authors.tsv`] = { + + files[`authors.tsv`] = { content: tsvFormat( authors.map((aut) => { const { userName, displayName, ethAddress, email, isNew } = aut @@ -816,9 +809,9 @@ WHERE lower(sender.eth_address) =ANY(${Array.from(senderAddresses)}) } }) ), // bufs.join('\n'), - }) + } - const distribJSON = (gist.files[`distrib.json`] = { + files[`distrib.json`] = { content: '[ ' + values @@ -857,7 +850,7 @@ WHERE lower(sender.eth_address) =ANY(${Array.from(senderAddresses)}) ) .join(',\n') + ' ]', - }) + } } // (2) @@ -873,7 +866,7 @@ WHERE lower(sender.eth_address) =ANY(${Array.from(senderAddresses)}) // (4) // write out to somewhere S3 bucket? // fs.writeFileSync("out/tree.json", JSON.stringify(tree.dump(), null, 2)); // console.log("Merkle-Tree Dump:", JSON.stringify(tree.dump(), null, 2)); - gist.files[`treedump.json`] = { + files[`treedump.json`] = { content: JSON.stringify(tree.dump(), null, 2), } @@ -935,7 +928,7 @@ WHERE lower(sender.eth_address) =ANY(${Array.from(senderAddresses)}) draft: finalize ? undefined : true, // this item is a draft, to be replaced to final }, ]) - gist.files[`rounds.json`] = { + files[`rounds.json`] = { // JSON.stringify(rounds, null, 2), content: '[ ' + @@ -954,7 +947,7 @@ WHERE lower(sender.eth_address) =ANY(${Array.from(senderAddresses)}) ' ]', } - gist.files['README.md'].content = `${runningBetween}, + files['README.md'].content = `${runningBetween}, \`\`\`js // from Optimism onChain: @@ -989,37 +982,6 @@ Merkle Tree Root (with ${treeValues.length} entries): \`${tree.root}\` this is analyzing results with [Quadratic Funding score calcuation with Pairwise Mechanism](https://github.com/gitcoinco/quadratic-funding?tab=readme-ov-file#implementation-upgrade-the-pairwise-mechanism)` - let gist_url: string | undefined = undefined - // skip if no GITHUB_TOKEN - console.log('to write_gist', !!(write_gist && GITHUB_TOKEN)) - if (write_gist && GITHUB_TOKEN) { - try { - const resGist = await fetch('https://api.github.com/gists', { - method: 'POST', - headers: { - Accept: 'application/vnd.github+json', - Authorization: `Bearer ${GITHUB_TOKEN}`, - 'X-GitHub-Api-Version': '2022-11-28', - }, - body: JSON.stringify(gist), - }).then(async (res) => { - try { - return await res.json() - } catch (err) { - console.log(new Date(), `failed parse as json:`, res.ok, res.headers) - console.log(await res.text()) - } - }) - - // console.log('gist res:', resGist) - gist_url = resGist?.html_url - } catch (err) { - console.error(new Date(), `failed POST to gist:`, err, 'with:', gist) - } finally { - console.log('set gist url:', gist_url) - } - } - await Promise.all( [ 'README.md', @@ -1033,7 +995,7 @@ this is analyzing results with [Quadratic Funding score calcuation with Pairwise s3PutFile({ Bucket: MattersBillboardS3Bucket, // 'matters-billboard', Key: `${s3FilePathPrefix}/${currRoundPath}/${filename}`, - Body: gist.files[filename].content, + Body: files[filename].content, }) // .then((res) => console.log(new Date(), `s3 treedump:`, res)); ) ) @@ -1041,7 +1003,7 @@ this is analyzing results with [Quadratic Funding score calcuation with Pairwise await s3PutFile({ Bucket: MattersBillboardS3Bucket, // 'matters-billboard', Key: `${s3FilePathPrefix}/rounds.json`, - Body: gist.files[`rounds.json`].content, + Body: files[`rounds.json`].content, // ACL: "public-read", ContentType: 'application/json', }).then((res) => @@ -1052,7 +1014,7 @@ this is analyzing results with [Quadratic Funding score calcuation with Pairwise ) ) - return { root: tree.root, gist_url } + return { root: tree.root } /* * calculates the clr amount at the given threshold and total pot @@ -1214,6 +1176,80 @@ this is analyzing results with [Quadratic Funding score calcuation with Pairwise } } +export async function finalizeQFScore({ + fromBlock, + toBlock, +}: { + fromBlock: bigint + toBlock: bigint +}) { + let existingRounds: any[] = [] + try { + const res = await s3GetFile({ + bucket: MattersBillboardS3Bucket, + key: `${s3FilePathPrefix}/rounds.json`, + }) + console.log( + new Date(), + `s3 get existing rounds:`, + res.ContentLength, + res.ContentType + ) + if (res.Body && res.ContentLength! > 0) { + existingRounds = JSON.parse(await res.Body.transformToString()) as any[] + } + } catch (err) { + console.error(new Date(), 'ERROR in reading existing rounds:', err) + } + + const currRoundPath = `${ + isProd ? 'optimism' : 'opSepolia' + }-${fromBlock}-${toBlock}` + + const rounds = existingRounds.map((r) => { + // mark target round as finalized + if (r.dirpath === currRoundPath) { + r.draft = undefined + } + return r + }) + + const targetRound = rounds.find((r) => r.dirpath === currRoundPath) + + if (!targetRound) { + console.error(new Date(), `no round found for ${currRoundPath}`) + return + } + + const roundsFileContent = + '[ ' + + rounds + .map((row) => + JSON.stringify( + row, + (_, v) => + typeof v === 'bigint' + ? v.toString() // util.format("%i", v) // +Number(v) // current numbers are all < Number.MAX_SAFE_INTEGER // 9_007_199_254_740_991 + : v // replacer, + // 2 // omit to be compact + ) + ) + .join(',\n') + + ' ]' + + await s3PutFile({ + Bucket: MattersBillboardS3Bucket, + Key: `${s3FilePathPrefix}/rounds.json`, + Body: roundsFileContent, + // ACL: "public-read", + ContentType: 'application/json', + }).then((res) => + console.log(new Date(), `finalize round ${targetRound.root}`) + ) + + return { root: targetRound.root } +} + // https://github.com/d3/d3-array/blob/main/src/quantile.js#L23 export function quantileSorted( values: bigint[], diff --git a/lib/qf-notify.ts b/lib/qf-notify.ts index a6fbb4b..f8e935d 100644 --- a/lib/qf-notify.ts +++ b/lib/qf-notify.ts @@ -88,13 +88,12 @@ WHERE user_name = ANY (${Array.from(authorGroups.keys())}) } const items = authors.map( - ({ id, userName, displayName, email, language, walletChanges }) => ({ + ({ id, userName, displayName, email, language }) => ({ userId: id as string, userName: userName as string, displayName: displayName as string, email: email as string, language: language as Language, - walletChanges: walletChanges as boolean, amount: showAmount(authorGroups.get(userName)!), }) ) @@ -132,23 +131,16 @@ function showAmount(amount: bigint) { const getNoticeMessage = ( language: Language, - amount: string | number, - walletChanges?: boolean + amount: string | number ): string => { switch (language) { case 'en': - return walletChanges - ? `You've received a ${amount} USDT funding from Billboard. Due to recent wallet address changes, the funding will be split according to the proportion of support received. Switch wallets to collect funds separately. Click this notification to go to the claim page.` - : `You've received a ${amount} USDT funding from Billboard. Click this notification to go to the claim page.` + return `You've received a ${amount} USDT funding from Billboard. Click this notification to go to the claim page.` case 'zh_hans': - return walletChanges - ? `你已获得 Billboard 配捐共 ${amount} USDT,因过去 14 天中有更换钱包地址,配捐金额将会按各钱包收到的支持比例拆分,请切换钱包地址分别领取。点击此则通知前往领取页面` - : `你已获得 Billboard 配捐共 ${amount} USDT,点击此则通知前往领取页面` + return `你已获得 Billboard 配捐共 ${amount} USDT,点击此则通知前往领取页面` case 'zh_hant': default: - return walletChanges - ? `你已獲得 Billboard 配捐共 ${amount} USDT,因過去 14 天中有更換錢包地址,配捐金額將會按各錢包收到的支持比例拆分,請切換錢包地址分別領取。點擊此則通知前往領取頁面` - : `你已獲得 Billboard 配捐共 ${amount} USDT,點擊此則通知前往領取頁面` + return `你已獲得 Billboard 配捐共 ${amount} USDT,點擊此則通知前往領取頁面` } } @@ -160,18 +152,15 @@ async function sendQfNotifInsite( email: string language: Language amount: number | string - walletChanges?: boolean }>, doNotify = false ) { if (!doNotify) return - const allNotices = items.map( - ({ userId, language, amount, walletChanges, ...rest }) => ({ - userId, // language, - message: getNoticeMessage(language, amount, walletChanges), - }) - ) + const allNotices = items.map(({ userId, language, amount, ...rest }) => ({ + userId, // language, + message: getNoticeMessage(language, amount), + })) const allMessages = Array.from( new Set(allNotices.map(({ message }) => message)) ) @@ -251,55 +240,50 @@ export async function sendQfNotificationEmails( email: string language: Language amount?: number | string - walletChanges?: boolean }>, doNotify = false ) { // if (!doNotify) return return Promise.allSettled( - items.map( - ({ userName, displayName, email, language, amount, walletChanges }) => { - console.log(`send QF-fund mail notification to:`, { - userName, - displayName, - email, - language, - amount, - walletChanges, - }) - if (!email) { - return // can't send if no email - } - if (!doNotify) return + items.map(({ userName, displayName, email, language, amount }) => { + console.log(`send QF-fund mail notification to:`, { + userName, + displayName, + email, + language, + amount, + }) + if (!email) { + return // can't send if no email + } + if (!doNotify) return - return mail - .send({ - from: EMAIL_FROM_ASK, - templateId: getTemplateId(language), - personalizations: [ - { - to: email, - dynamicTemplateData: { - subject: getSubject(language), - displayName, - siteDomain, - amount, - walletChanges, - claimLink, - billboardUrl, - billboardAnnouncementLink: - language === 'en' - ? `https://matters.town/@web3/554164-test-lauch-of-on-chain-advertisment-protocol-with-80-revenue-back-to-creators-bafybeifsq4u5wewvwsogeo3nxilu4lycxjsed7lfilteikskbiig46qaei?locale=en` - : 'https://matters.town/@hi176/554162-matters-試驗全新鏈上廣告機制-收入-80-配捐創作者-bafybeih5wa5s2ndr5ahsxwj3rlwo25erjggmvvdnr6s5mnocngiqk6224e', - }, + return mail + .send({ + from: EMAIL_FROM_ASK, + templateId: getTemplateId(language), + personalizations: [ + { + to: email, + dynamicTemplateData: { + subject: getSubject(language), + displayName, + siteDomain, + amount, + claimLink, + billboardUrl, + billboardAnnouncementLink: + language === 'en' + ? `https://matters.town/@web3/554164-test-lauch-of-on-chain-advertisment-protocol-with-80-revenue-back-to-creators-bafybeifsq4u5wewvwsogeo3nxilu4lycxjsed7lfilteikskbiig46qaei?locale=en` + : 'https://matters.town/@hi176/554162-matters-試驗全新鏈上廣告機制-收入-80-配捐創作者-bafybeih5wa5s2ndr5ahsxwj3rlwo25erjggmvvdnr6s5mnocngiqk6224e', }, - ], - }) - .then((res: any) => console.log(`mail "${email}" res:`, res)) - .catch((err: Error) => console.error(`mail "${email}" ERROR:`, err)) - } - ) + }, + ], + }) + .then((res: any) => console.log(`mail "${email}" res:`, res)) + .catch((err: Error) => console.error(`mail "${email}" ERROR:`, err)) + }) ) } @@ -310,10 +294,9 @@ function getTemplateId(language: Language): string { en: 'd-6c33968152a14578918789241f63279a', } const templateIdsProd = { - // branch out when necessary - zh_hant: 'd-dd6f9660b30a40eaa831254275c4b0b6', - zh_hans: 'd-f33d89d33a72419dbfc504c09ca84f81', - en: 'd-6c33968152a14578918789241f63279a', + zh_hant: 'd-7a1f55f59fcf45a0a824904d6056b7ce', + zh_hans: 'd-d4abaa12250c4c80be8396fd18563930', + en: 'd-ac54ca1b20c04e079ccbb1c9102f87dd', } return (isProd ? templateIdsProd : templateIdsDev)[language] } diff --git a/package-lock.json b/package-lock.json index c0e26cd..2d3375d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "lambda-handlers-image", - "version": "0.8.14", + "version": "0.10.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "lambda-handlers-image", - "version": "0.8.14", + "version": "0.10.0", "dependencies": { "@aws-sdk/client-s3": "^3.504.0", "@aws-sdk/client-sqs": "^3.266.0",