Skip to content
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/brown-rabbits-dream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@rabbitholegg/questdk-plugin-registry": minor
"@rabbitholegg/questdk-plugin-coop": minor
---

add coop records
8 changes: 6 additions & 2 deletions packages/aerodrome/src/Aerodrome.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ describe('Given the aerodrome plugin', () => {
const { transaction, description, params } = testCase
test(description, async () => {
const filter = await swap(params)
expect(apply(transaction, filter)).to.be.true
expect(
apply(transaction as unknown as Record<string, unknown>, filter),
).to.be.true
})
})
})
Expand All @@ -52,7 +54,9 @@ describe('Given the aerodrome plugin', () => {
const { transaction, description, params } = testCase
test(description, async () => {
const filter = await swap(params)
expect(apply(transaction, filter)).to.be.false
expect(
apply(transaction as unknown as Record<string, unknown>, filter),
).to.be.false
})
})
})
Expand Down
1 change: 1 addition & 0 deletions packages/coop/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# @rabbitholegg/questdk-plugin-coop
8 changes: 8 additions & 0 deletions packages/coop/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Coop Records

Coop records is a web3 Music distribution platform that connects creators with their biggest fans.

https://cooprecords.xyz/


The protocol uses clones of the Zora 1155 implementation utilizing the fixed price strategy.
36 changes: 36 additions & 0 deletions packages/coop/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "@rabbitholegg/questdk-plugin-coop",
"version": "1.0.0-alpha.1",
"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",
"types": "./dist/types/index.d.ts",
"typings": "./dist/types/index.d.ts",
"scripts": {
"build": "vite build && tsc --project tsconfig.build.json --emitDeclarationOnly --declaration --declarationMap --declarationDir ./dist/types",
"bench": "vitest bench",
"bench:ci": "CI=true vitest bench",
"clean": "rimraf dist",
"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",
"devDependencies": {
"@rabbitholegg/questdk-plugin-utils": "workspace:*"
},
"dependencies": {
"@rabbitholegg/questdk-plugin-utils": "workspace:*",
"@rabbitholegg/questdk": "workspace:*"
}
}
175 changes: 175 additions & 0 deletions packages/coop/src/Coop.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import {
Chains,
type MintIntentParams,
} from '@rabbitholegg/questdk-plugin-utils'
import { apply } from '@rabbitholegg/questdk'
import { type Address, parseEther, getAddress, zeroAddress } from 'viem'
import { describe, expect, test } from 'vitest'
import {
getExternalUrl,
getFees,
getMintIntent,
mint,
simulateMint,
} from './Coop'
import {
failingTestCases,
noTokenIdTestCase,
passingTestCases,
} from './test-setup'
import { EXPECTED_ENCODED_DATA_1155 } from './test-transactions'

describe('Given the coop plugin', () => {
describe('When handling the mint', () => {
describe('should return a valid action filter', () => {
test('when making a valid mint action', async () => {
const filter = await mint({
chainId: 8453,
contractAddress: '0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF',
tokenId: 1,
})
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')
// @ts-expect-error "to exists on filter"
expect(Object.keys(filter.to)).toHaveLength(1)
expect(
['$or', '$and'].some((prop) =>
Object.hasOwnProperty.call(filter.to, prop),
),
).to.be.true
// @ts-expect-error "to exists on filter"
expect(Object.values(filter.to)[0]).to.satisfy((arr: string[]) =>
arr.every((val) => val.match(/^0x[a-fA-F0-9]{40}$/)),
)
}
// Check the input property is the correct type and has a valid filter operator
expect(filter.input).toBeTypeOf('object')
expect(
['$abi', '$abiParams', '$abiAbstract', '$or', '$and'].some((prop) =>
Object.hasOwnProperty.call(filter.input, prop),
),
).to.be.true
})
})

describe('should pass filter with valid transactions', () => {
passingTestCases.forEach((testCase) => {
const { transaction, params, description } = testCase
test(description, async () => {
const filter = await mint(params)
// @ts-expect-error "transaction is a Transaction"
expect(apply(transaction, filter)).to.be.true
})
})
})

describe('should not pass filter with invalid transactions', () => {
failingTestCases.forEach((testCase) => {
const { transaction, params, description } = testCase
test(description, async () => {
const filter = await mint(params)
// @ts-expect-error "transaction is a Transaction"
expect(apply(transaction, filter)).to.be.false
})
})
})

describe('should throw an error when tokenId is not provided', async () => {
const { params, description } = noTokenIdTestCase
test(description, async () => {
try {
await mint(params)
} catch (error) {
expect(error).toBeDefined()
expect((error as Error).message).toContain('tokenId is required')
}
})
})
})
})

describe('Given the getMintIntent function', () => {
// Define the constant for the contract address
const CONTRACT_ADDRESS = '0x7cac19a3ac8ad29f2c1cea4ad0a16135b969c52c'
const RECIPIENT_ADDRESS = '0x1234567890123456789012345678901234567890'

test('returns a TransactionRequest with correct properties when tokenId is set', async () => {
const mint: MintIntentParams = {
chainId: Chains.BASE,
tokenId: 1,
contractAddress: CONTRACT_ADDRESS,
amount: BigInt('3'),
recipient: RECIPIENT_ADDRESS,
}

const result = await getMintIntent(mint)

expect(result).toEqual({
from: mint.recipient,
to: mint.contractAddress,
data: EXPECTED_ENCODED_DATA_1155,
})
})
})

describe('Given the getFee function', () => {
test('should return the correct fee for an 1155 mint', async () => {
const contractAddress: Address =
'0x7cac19a3ac8ad29f2c1cea4ad0a16135b969c52c'
const mintParams = {
contractAddress,
tokenId: 1,
chainId: Chains.BASE,
}

const fee = await getFees(mintParams)
expect(fee.projectFee).toEqual(parseEther('0.0004'))
expect(fee.actionFee).toEqual(parseEther('0'))
})
})

describe('simulateMint function', () => {
test('should simulate a 1155 mint when tokenId is not 0', async () => {
const mint: MintIntentParams = {
chainId: Chains.BASE,
contractAddress: '0x7cac19a3ac8ad29f2c1cea4ad0a16135b969c52c',
tokenId: 1,
amount: BigInt(1),
recipient: '0xf70da97812CB96acDF810712Aa562db8dfA3dbEF',
}
const value = parseEther('0.0004')
const address = mint.recipient as Address

const result = await simulateMint(mint as MintIntentParams, value, address)

const request = result.request
expect(request.address).toBe(mint.contractAddress)
expect(request.value).toBe(value)
})
})

describe('getExternalUrl function', () => {
// test('should return correct url for mint w/tokenId', async () => {
// const params = {
// chainId: Chains.BASE,
// contractAddress: getAddress('0x7caC19A3aC8aD29F2C1CEA4ad0a16135b969C52c'),
// tokenId: 1,
// }
// const result = await getExternalUrl(params)
// expect(result).toBe('https://cooprecords.xyz')
// })

test('should return fallback url if error occurs', async () => {
const params = {
chainId: Chains.BASE,
contractAddress: zeroAddress,
}
const result = await getExternalUrl(params)
expect(result).toBe('https://cooprecords.xyz')
})
})
Loading