-
Notifications
You must be signed in to change notification settings - Fork 25
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(zora): update plugin to be compatible with zora protocol upgrade [BOOST-4411] #502
Changes from all commits
deaa527
ad1f0c5
7a31014
a8ced09
6d796fc
bd5bcd4
8ec83e9
c163ded
2367409
a28858a
c00cd61
95c701d
ee30914
4903e4e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@rabbitholegg/questdk-plugin-zora": minor | ||
--- | ||
|
||
update for zora protocol upgrade |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,19 @@ | ||
import { | ||
CREATE_CONTRACT_ABI, | ||
FUNCTION_SELECTORS, | ||
UNIVERSAL_MINTER_ABI, | ||
ZORA_MINTER_ABI_721, | ||
ZORA_MINTER_ABI_1155, | ||
ZORA_MINTER_ABI_1155_LEGACY, | ||
V2_MINT_ABI, | ||
} from './abi' | ||
import { CHAIN_ID_ARRAY, CHAIN_ID_TO_ZORA_SLUG } from './chain-ids' | ||
import { | ||
FIXED_PRICE_SALE_STRATS, | ||
ZORA_1155_FACTORY, | ||
ZORA_TIMED_SALE_STRATEGY, | ||
} from './contract-addresses' | ||
import { AndArrayItem } from './types' | ||
import { checkBytecode, getNextTokenId } from './utils' | ||
import { validatePremint } from './validate' | ||
import { | ||
type MintActionParams, | ||
|
@@ -41,14 +43,10 @@ import { | |
type TransactionRequest, | ||
createPublicClient, | ||
encodeFunctionData, | ||
fromHex, | ||
getAddress, | ||
http, | ||
keccak256, | ||
pad, | ||
parseEther, | ||
stringToBytes, | ||
toHex, | ||
} from 'viem' | ||
|
||
export const validate = async ( | ||
|
@@ -91,12 +89,9 @@ export const create = async ( | |
}) | ||
} | ||
|
||
export const mint = async ( | ||
mint: MintActionParams, | ||
): Promise<TransactionFilter> => { | ||
const v1MintFilter = (mint: MintActionParams) => { | ||
const { chainId, contractAddress, tokenId, amount, recipient, referral } = | ||
mint | ||
|
||
const universalMinter = | ||
zoraUniversalMinterAddress[ | ||
chainId as keyof typeof zoraUniversalMinterAddress | ||
|
@@ -179,10 +174,39 @@ export const mint = async ( | |
$or: [ERC721_FILTER_ABSTRACT, ERC1155_FILTER_ABSTRACT], | ||
}, | ||
} | ||
return compressJson({ | ||
return { | ||
chainId, | ||
to: getExitAddresses(chainId, mintContracts), // We need this top level so the backend knows what contracts this applies to | ||
$or: [DIRECT_MINT_FILTER, UNIVERSAL_MINT_FILTER], | ||
} | ||
} | ||
|
||
const v2MintFilter = (mint: MintActionParams) => { | ||
const { chainId, contractAddress, tokenId, amount, recipient, referral } = | ||
mint | ||
|
||
return { | ||
chainId, | ||
to: getExitAddresses(chainId, ZORA_TIMED_SALE_STRATEGY), | ||
input: { | ||
$abi: V2_MINT_ABI, | ||
mintTo: recipient, | ||
quantity: formatAmountToFilterOperator(amount), | ||
collection: contractAddress, | ||
tokenId, | ||
mintReferral: referral, | ||
}, | ||
} | ||
} | ||
|
||
export const mint = async ( | ||
mint: MintActionParams, | ||
): Promise<TransactionFilter> => { | ||
const v1Filter = v1MintFilter(mint) | ||
const v2Filter = v2MintFilter(mint) | ||
|
||
return compressJson({ | ||
$or: [v1Filter, v2Filter], | ||
}) | ||
} | ||
|
||
|
@@ -235,56 +259,17 @@ export const getMintIntent = async ( | |
} | ||
} | ||
|
||
export const simulateMint = async ( | ||
export const simulateV1Mint = async ( | ||
mint: MintIntentParams, | ||
value: bigint, | ||
client: PublicClient, | ||
account?: Address, | ||
client?: PublicClient, | ||
): Promise<SimulateContractReturnType> => { | ||
const { chainId, contractAddress, tokenId, amount, recipient, referral } = | ||
mint | ||
const _client = | ||
client ?? | ||
createPublicClient({ | ||
chain: chainIdToViemChain(chainId), | ||
transport: http(), | ||
}) | ||
const from = account ?? DEFAULT_ACCOUNT | ||
let _tokenId = tokenId | ||
if (tokenId === null || tokenId === undefined) { | ||
const nextTokenId = (await _client.readContract({ | ||
address: contractAddress, | ||
abi: ZORA_MINTER_ABI_1155, | ||
functionName: 'nextTokenId', | ||
})) as bigint | ||
|
||
_tokenId = Number(nextTokenId) - 1 | ||
} | ||
|
||
// find implementation address for EIP-1967 proxy | ||
const slot = keccak256(stringToBytes('eip1967.proxy.implementation')) | ||
const slotValue = toHex(fromHex(slot, 'bigint') - 1n) | ||
const slotForImplementation = pad(slotValue, { size: 32 }) | ||
const implementationAddressRaw = await _client.getStorageAt({ | ||
address: contractAddress, | ||
slot: slotForImplementation, | ||
}) | ||
const implementationAddress: Address = `0x${implementationAddressRaw?.slice( | ||
-40, | ||
)}` | ||
|
||
// Check if the implementation contracts bytecode contains valid function selectors | ||
const bytecode = await _client.getBytecode({ address: implementationAddress }) | ||
|
||
const containsSelector = FUNCTION_SELECTORS.some((selector) => | ||
bytecode?.includes(selector), | ||
) | ||
|
||
if (!containsSelector) { | ||
throw new Error( | ||
'None of the specified function selectors are present in the contract bytecode.', | ||
) | ||
} | ||
const _tokenId = await getNextTokenId(client, contractAddress, tokenId) | ||
await checkBytecode(client, contractAddress) | ||
|
||
let fixedPriceSaleStratAddress = FIXED_PRICE_SALE_STRATS[chainId] | ||
|
||
|
@@ -304,7 +289,7 @@ export const simulateMint = async ( | |
[referral ?? ZORA_DEPLOYER_ADDRESS], | ||
pad(recipient), | ||
] | ||
const result = await _client.simulateContract({ | ||
const result = await client.simulateContract({ | ||
address: contractAddress, | ||
value, | ||
abi: ZORA_MINTER_ABI_1155, | ||
|
@@ -327,7 +312,7 @@ export const simulateMint = async ( | |
pad(recipient), | ||
] | ||
try { | ||
const result = await _client.simulateContract({ | ||
const result = await client.simulateContract({ | ||
address: contractAddress, | ||
value, | ||
abi: ZORA_MINTER_ABI_1155_LEGACY, | ||
|
@@ -344,7 +329,7 @@ export const simulateMint = async ( | |
return result | ||
} catch { | ||
// Assume it's a 721 mint | ||
const result = await _client.simulateContract({ | ||
const result = await client.simulateContract({ | ||
address: contractAddress, | ||
value, | ||
abi: ZORA_MINTER_ABI_721, | ||
|
@@ -363,6 +348,67 @@ export const simulateMint = async ( | |
} | ||
} | ||
|
||
export const simulateV2Mint = async ( | ||
mint: MintIntentParams, | ||
value: bigint, | ||
client: PublicClient, | ||
account?: Address, | ||
): Promise<SimulateContractReturnType> => { | ||
const { contractAddress, tokenId, amount, recipient, referral } = mint | ||
|
||
const from = account ?? DEFAULT_ACCOUNT | ||
|
||
const _tokenId = await getNextTokenId(client, contractAddress, tokenId) | ||
|
||
await checkBytecode(client, contractAddress) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need to move this now but I could see this being generally useful - nice little portable helper. |
||
|
||
const mintArgs = [ | ||
recipient ?? from, | ||
amount, | ||
contractAddress, | ||
_tokenId, | ||
referral ?? ZORA_DEPLOYER_ADDRESS, | ||
'', | ||
] | ||
|
||
const result = await client.simulateContract({ | ||
address: ZORA_TIMED_SALE_STRATEGY, | ||
value, | ||
abi: V2_MINT_ABI, | ||
functionName: 'mint', | ||
args: mintArgs, | ||
account: from, | ||
stateOverride: [ | ||
{ | ||
address: from, | ||
balance: parseEther('100'), | ||
}, | ||
], | ||
}) | ||
return result | ||
} | ||
|
||
export const simulateMint = async ( | ||
mint: MintIntentParams, | ||
value: bigint, | ||
account?: Address, | ||
client?: PublicClient, | ||
): Promise<SimulateContractReturnType> => { | ||
const _client = | ||
client ?? | ||
(createPublicClient({ | ||
chain: chainIdToViemChain(mint.chainId), | ||
transport: http(), | ||
}) as PublicClient) | ||
|
||
try { | ||
return await simulateV2Mint(mint, value, _client, account) | ||
} catch (error) { | ||
console.error('simulateV2Mint failed:', error) | ||
return await simulateV1Mint(mint, value, _client, account) | ||
mmackz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
|
||
const getSalesConfigAndTokenInfo = async ( | ||
chainId: number, | ||
tokenAddress: Address, | ||
|
@@ -406,7 +452,8 @@ export const getFees = async ( | |
return { actionFee: fee.tokenPurchaseCost, projectFee: fee.mintFee } | ||
} catch (err) { | ||
console.error(err) | ||
return { actionFee: parseEther('0'), projectFee: parseEther('0.000777') } // https://github.com/ourzora/zora-protocol/blob/e9fb5072112b4434cc649c95729f4bd8c6d5e0d0/packages/protocol-sdk/src/apis/chain-constants.ts#L27 | ||
// ! temp change to base fee of 0.000111 until we update for fee calulation | ||
return { actionFee: parseEther('0'), projectFee: parseEther('0.000111') } // https://github.com/ourzora/zora-protocol/blob/e9fb5072112b4434cc649c95729f4bd8c6d5e0d0/packages/protocol-sdk/src/apis/chain-constants.ts#L27 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we're simulating v1 mints but the sales config will only work for v2 -- this will likely break backwards compatibility There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. backwards compatibility might not be possible There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Havent fully looked into it yet, but I assume the updated zora sdk should have a way to deal with this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The SDK should provide a solve here - we also do cache this value still I think so the existing boosts shouldn't break and new boosts will fail on the creation simulation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does increase the need for updating the SDK though IMO |
||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,3 +12,5 @@ export const FIXED_PRICE_SALE_STRATS: { [chainId: number]: Address } = { | |
} | ||
|
||
export const ZORA_1155_FACTORY = '0x777777c338d93e2c7adf08d102d45ca7cc4ed021' | ||
export const ZORA_TIMED_SALE_STRATEGY = | ||
'0x777777722d078c97c6ad07d9f36801e653e356ae' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's nice they're actually deterministically deploying this now |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does look like it's still possible to go through
0x777777722D078c97c6ad07d9f36801e653E356Ae
in addition to0x777777722D078c97c6ad07d9f36801e653E356Ae
and the actual collection.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmmmmmmm - it looks like this address is
ZoraTimedSaleStrategy
on OP andZoraMintsManager
on Zora?