Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(titles): add support for Titles create plugin #388

Merged
merged 8 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/giant-lions-search.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@rabbitholegg/questdk-plugin-registry": minor
"@rabbitholegg/questdk-plugin-titles": minor
---

add titles create plugin to questdk
3 changes: 2 additions & 1 deletion packages/registry/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
"@rabbitholegg/questdk-plugin-thruster": "workspace:*",
"@rabbitholegg/questdk-plugin-orbit": "workspace:*",
"@rabbitholegg/questdk-plugin-superbridge": "workspace:*",
"@rabbitholegg/questdk-plugin-neynar": "workspace:*"
"@rabbitholegg/questdk-plugin-neynar": "workspace:*",
"@rabbitholegg/questdk-plugin-titles": "workspace:*"
}
}
2 changes: 2 additions & 0 deletions packages/registry/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import { Base } from '@rabbitholegg/questdk-plugin-base'
import { Orbit } from '@rabbitholegg/questdk-plugin-orbit'
import { Superbridge } from '@rabbitholegg/questdk-plugin-superbridge'
import { Neynar } from '@rabbitholegg/questdk-plugin-neynar'
import { Titles } from '@rabbitholegg/questdk-plugin-titles'
// ^^^ New Imports Go Here ^^^
import {
type IntentParams,
Expand Down Expand Up @@ -111,6 +112,7 @@ export const plugins: Record<string, IActionPlugin> = {
[Orbit.pluginId]: Orbit,
[Superbridge.pluginId]: Superbridge,
[Neynar.pluginId]: Neynar,
[Titles.pluginId]: Titles,
}

export const getPlugin = (pluginId: string) => {
Expand Down
18 changes: 18 additions & 0 deletions packages/titles/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# TITLES Plugin

### About The Project

TITLES builds creative tools powered by artist-trained AI models. Titles helps artists and IP holders maintain ownership of their AI likeness by making it easy to train, distribute, and monetize their own custom models.

For the first time, artists can now take ownership and monetize their likeness through AI on TITLES.

- Documentation: https://titlesxyz.notion.site/TITLES-341ca0582707492ea4b048291edd4698

### Implementation Details

This is a simple `create` plugin which filters transaction based on the chain, contract and matching ABI function. The specific function we are watching for is `publishEdition` on the `TitlesPublisherV1` contract.

- [**TitlesPublisherV1**](https://basescan.org/address/0x04e4d53374a5e6259ce06cfc6850a839bd960d01#code)

#### Sample Transactions
- [**publishEdition**](https://basescan.org/tx/0x585edfb9b1899f37926c51274b8fbe0d62e1a00402a3cd084ae6a7af41226b9c)
35 changes: 35 additions & 0 deletions packages/titles/babel.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const sharedPresets = ['@babel/typescript']
const shared = {
ignore: ['src/**/*.spec.ts'],
presets: sharedPresets,
}

module.exports = {
env: {
esmUnbundled: shared,
esmBundled: {
...shared,
presets: [
[
'@babel/env',
{
targets: '> 0.25%, not dead',
},
],
...sharedPresets,
],
},
cjs: {
...shared,
presets: [
[
'@babel/env',
{
modules: 'commonjs',
},
],
...sharedPresets,
],
},
},
}
43 changes: 43 additions & 0 deletions packages/titles/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "@rabbitholegg/questdk-plugin-titles",
"private": false,
"version": "1.0.0-alpha.0",
"exports": {
"require": "./dist/cjs/index.js",
"import": "./dist/esm/index.js",
"types": "./dist/types/index.d.ts"
},
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
"description": "Plugin for Titles",
"scripts": {
"bench": "vitest bench",
"bench:ci": "CI=true vitest bench",
"build": "pnpm build:types && pnpm build:esm && pnpm postbuild:esm && pnpm build:cjs && pnpm postbuild:cjs && BABEL_ENV=esmBundled pnpm rollup -c",
"build:esm": "BABEL_ENV=esmUnbundled babel src --extensions '.ts' --out-dir 'dist/esm' --source-maps",
"build:cjs": "BABEL_ENV=cjs babel src --extensions '.ts' --out-dir 'dist/cjs' --source-maps",
"build:types": "tsc --project tsconfig.build.json --module esnext --declarationDir ./dist/types --emitDeclarationOnly --declaration --declarationMap",
"clean": "rimraf dist",
"postbuild:cjs": "echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json",
"postbuild:esm": "echo '{\"type\":\"module\"}' > dist/esm/package.json",
"format": "rome format . --write",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"test": "vitest dev",
"test:cov": "vitest dev --coverage",
"test:ci": "CI=true vitest --coverage",
"test:ui": "vitest dev --ui"
},
"keywords": [],
"author": "",
"license": "ISC",
"types": "./dist/types/index.d.ts",
"typings": "./dist/types/index.d.ts",
"devDependencies": {
"tsconfig": "workspace:*"
},
"dependencies": {
"@rabbitholegg/questdk-plugin-utils": "workspace:*",
"@rabbitholegg/questdk": "workspace:*"
}
}
52 changes: 52 additions & 0 deletions packages/titles/rollup.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import babel from '@rollup/plugin-babel'
import commonjs from '@rollup/plugin-commonjs'
import json from '@rollup/plugin-json'
import resolve from '@rollup/plugin-node-resolve'
import terser from '@rollup/plugin-terser'

const extensions = ['.js', '.ts']

export default {
input: 'src/index.ts',
output: [
{
inlineDynamicImports: true,
file: 'dist/bundles/bundle.esm.js',
format: 'esm',
sourcemap: true,
},
{
inlineDynamicImports: true,
file: 'dist/bundles/bundle.esm.min.js',
format: 'esm',
plugins: [terser()],
sourcemap: true,
},
{
inlineDynamicImports: true,
file: 'dist/bundles/bundle.umd.js',
format: 'umd',
name: 'myLibrary',
sourcemap: true,
},
{
inlineDynamicImports: true,
file: 'dist/bundles/bundle.umd.min.js',
format: 'umd',
name: 'myLibrary',
plugins: [terser()],
sourcemap: true,
},
],
plugins: [
json(),
resolve({ extensions, preferBuiltins: true }),
commonjs(),
babel({
babelHelpers: 'bundled',
include: ['src/**/*.ts'],
extensions,
exclude: './node_modules/**',
}),
],
}
53 changes: 53 additions & 0 deletions packages/titles/src/Titles.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { create } from './Titles'
import { failingTestCases, passingTestCases } from './test-transactions'
import { apply } from '@rabbitholegg/questdk'
import { describe, expect, test } from 'vitest'

describe('Given the titles plugin', () => {
describe('When handling the create action', () => {
describe('should return a valid action filter', () => {
test('when making a valid create action', async () => {
const filter = await create({
chainId: 8453,
})
expect(filter).toBeTypeOf('object')
expect(Number(filter.chainId)).toBe(8453)
if (typeof filter.to === 'string') {
expect(filter.to).toMatch(/^0x[a-fA-F0-9]{40}$/)
} else {
// if to is an object, it should have a logical operator as the only key
expect(filter.to).toBeTypeOf('object')
expect(Object.keys(filter.to)).toHaveLength(1)
expect(
['$or', '$and'].some((prop) =>
Object.hasOwnProperty.call(filter.to, prop),
),
).to.be.true
expect(Object.values(filter.to)[0]).to.satisfy((arr: string[]) =>
arr.every((val) => val.match(/^0x[a-fA-F0-9]{40}$/)),
)
}
})
})

describe('should pass filter with valid transactions', () => {
passingTestCases.forEach((testCase) => {
const { transaction, description, params } = testCase
test(description, async () => {
const filter = await create(params)
expect(apply(transaction, filter)).to.be.true
})
})
})

describe('should not pass filter with invalid transactions', () => {
failingTestCases.forEach((testCase) => {
const { transaction, description, params } = testCase
test(description, async () => {
const filter = await create(params)
expect(apply(transaction, filter)).to.be.false
})
})
})
})
})
32 changes: 32 additions & 0 deletions packages/titles/src/Titles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { TITLES_ABI_V1, TITLES_PUBLISHER_V1 } from './constants'
import {
type CreateActionParams,
type TransactionFilter,
compressJson,
} from '@rabbitholegg/questdk'
import { Chains } from '@rabbitholegg/questdk-plugin-utils'
import { type Address } from 'viem'

export const create = async (
create: CreateActionParams,
): Promise<TransactionFilter> => {
const { chainId, contractAddress } = create
return compressJson({
chainId,
to: contractAddress ?? TITLES_PUBLISHER_V1,
input: {
$abi: TITLES_ABI_V1,
},
})
}

export const getSupportedTokenAddresses = async (
_chainId: number,
): Promise<Address[]> => {
// Not used for create action
return []
}

export const getSupportedChainIds = async (): Promise<number[]> => {
return [Chains.BASE]
}
40 changes: 40 additions & 0 deletions packages/titles/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
export const TITLES_PUBLISHER_V1 = '0x04e4d53374a5e6259ce06cfc6850a839bd960d01'

export const TITLES_ABI_V1 = [
{
inputs: [
{ internalType: 'address', name: '_creator', type: 'address' },
{ internalType: 'string', name: '_name', type: 'string' },
{ internalType: 'string', name: '_symbol', type: 'string' },
{ internalType: 'string', name: '_uri', type: 'string' },
{
internalType: 'address[]',
name: 'creatorProceedAccounts',
type: 'address[]',
},
{
internalType: 'uint32[]',
name: 'creatorProceedAllocations',
type: 'uint32[]',
},
{
internalType: 'address[]',
name: 'derivativeFeeAccounts',
type: 'address[]',
},
{
internalType: 'uint32[]',
name: 'derivativeFeeAllocations',
type: 'uint32[]',
},
{ internalType: 'uint256', name: '_price', type: 'uint256' },
{ internalType: 'uint256', name: '_maxSupply', type: 'uint256' },
{ internalType: 'uint256', name: '_mintLimitPerWallet', type: 'uint256' },
{ internalType: 'uint256', name: '_saleEndTime', type: 'uint256' },
],
name: 'publishEdition',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
]
14 changes: 14 additions & 0 deletions packages/titles/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { type IActionPlugin } from '@rabbitholegg/questdk'

import {
create,
getSupportedChainIds,
getSupportedTokenAddresses,
} from './Titles'

export const Titles: IActionPlugin = {
pluginId: 'titles',
getSupportedTokenAddresses,
getSupportedChainIds,
create,
}
34 changes: 34 additions & 0 deletions packages/titles/src/test-transactions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { type CreateActionParams } from '@rabbitholegg/questdk'
import {
Chains,
type TestParams,
createTestCase,
} from '@rabbitholegg/questdk-plugin-utils'

export const PUBLISH_EDITION: TestParams<CreateActionParams> = {
transaction: {
chainId: 8453,
from: '0x624310638b9901933fab5f5b0deb2b33e13c87cc',
hash: '0x585edfb9b1899f37926c51274b8fbe0d62e1a00402a3cd084ae6a7af41226b9c',
input:
'0x9cc866bd000000000000000000000000624310638b9901933fab5f5b0deb2b33e13c87cc000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000002c00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000666ba076000000000000000000000000000000000000000000000000000000000000000c67616c207665727365205345000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000065449544c455300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d5848673953395862314e33466552356e394a587078657a32526d744d4248484e354b7744443676777667415100000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000624310638b9901933fab5f5b0deb2b33e13c87cc0000000000000000000000007f8b18bbbf77fafdee934eaa359da3193eada207000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000aae6000000000000000000000000000000000000000000000000000000000000493e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005a6fd0c361a2ed387fe6c8346b1ced733ebd2c4e0000000000000000000000007f8b18bbbf77fafdee934eaa359da3193eada207000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000927c00000000000000000000000000000000000000000000000000000000000061a80',
to: '0x04e4d53374a5e6259ce06cfc6850a839bd960d01',
value: '0',
},
params: {
chainId: Chains.BASE,
},
}

export const passingTestCases = [
createTestCase(PUBLISH_EDITION, 'when creating a new collection'),
]

export const failingTestCases = [
createTestCase(PUBLISH_EDITION, 'when chainId is not correct', {
chainId: 1,
}),
createTestCase(PUBLISH_EDITION, 'when contractAddress is not correct', {
contractAddress: '0x777777C338d93e2C7adf08D102d45CA7CC4Ed021',
}),
]
16 changes: 16 additions & 0 deletions packages/titles/tsconfig.build.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"extends": "tsconfig/base.json",
"include": ["src"],
"exclude": [
"src/**/*.test.ts",
"src/**/*.test-d.ts",
"src/**/*.bench.ts",
"src/_test",
"scripts/**/*"
],
"compilerOptions": {
"sourceMap": true,
"rootDir": "./src",

}
}
5 changes: 5 additions & 0 deletions packages/titles/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "tsconfig/base.json",
"include": ["src/**/*", "src/chain-data.ts"],
"exclude": ["dist", "build", "node_modules"]
}
Loading
Loading