From ad5d0c3c49fd6fe2d23e710d72cb0f43376acf83 Mon Sep 17 00:00:00 2001 From: jipstavenuiter Date: Thu, 14 Dec 2023 18:11:02 +0100 Subject: [PATCH 1/4] (feat): add initial version of delete on order fulfilled script --- .../delete-order-on-order-fulfilled.ts | 1895 +++++++++++++++++ 1 file changed, 1895 insertions(+) create mode 100644 defender/src/auto-tasks/delete-order-on-order-fulfilled.ts diff --git a/defender/src/auto-tasks/delete-order-on-order-fulfilled.ts b/defender/src/auto-tasks/delete-order-on-order-fulfilled.ts new file mode 100644 index 00000000..9440950e --- /dev/null +++ b/defender/src/auto-tasks/delete-order-on-order-fulfilled.ts @@ -0,0 +1,1895 @@ +import { + AutotaskEvent, + BlockTriggerEvent, +} from "@openzeppelin/defender-autotask-utils"; +import { getNetworkConfigFromName } from "../networks"; +import { createClient } from "@supabase/supabase-js"; +import fetch from "node-fetch"; +import { ethers } from "ethers"; +import { MissingDataError, NotImplementedError } from "../errors"; + +export async function handler(event: AutotaskEvent) { + console.log( + "Event: ", + JSON.stringify( + { ...event, secrets: "HIDDEN", credentials: "HIDDEN" }, + null, + 2, + ), + ); + const network = getNetworkConfigFromName(event.autotaskName); + const { SUPABASE_URL, SUPABASE_SECRET_API_KEY } = event.secrets; + const ALCHEMY_KEY = event.secrets[network.alchemyKeyEnvName]; + + const client = createClient(SUPABASE_URL, SUPABASE_SECRET_API_KEY, { + global: { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + fetch: (...args) => fetch(...args), + }, + }); + + let provider; + + if (ALCHEMY_KEY) { + provider = new ethers.providers.AlchemyProvider( + network.networkKey, + ALCHEMY_KEY, + ); + } else if (network.rpc) { + provider = new ethers.providers.JsonRpcProvider(network.rpc); + } else { + throw new Error("No provider available"); + } + + // Check data availability + const body = event.request.body; + if (!("type" in body) || body.type !== "BLOCK") { + throw new NotImplementedError("Event body is not a BlockTriggerEvent"); + } + const blockTriggerEvent = body as BlockTriggerEvent; + const contractAddress = blockTriggerEvent.matchedAddresses[0]; + const fromAddress = blockTriggerEvent.transaction.from; + const txnLogs = blockTriggerEvent.transaction.logs; + const tx = await provider.getTransaction(blockTriggerEvent.hash); + + if (!contractAddress) { + throw new MissingDataError(`body.matchedAddresses is missing`); + } else if (!fromAddress) { + throw new MissingDataError(`body.transaction.from is missing`); + } else if (!txnLogs) { + throw new MissingDataError(`body.transaction.logs is missing`); + } else if (!tx) { + throw new MissingDataError(`tx is missing`); + } + + console.log("Contract address", contractAddress); + console.log("From address", fromAddress); + + // TODO: Update contracts so we can use ABI from the @hypercerts-org/contracts package + const contractInterface = new ethers.utils.Interface(HypercertExchangeABI); + + // Parse TransferSingle events + const transferSingleEvents = txnLogs + .map((l) => { + //Ignore unknown events + try { + return contractInterface.parseLog(l); + } catch (e) { + console.log("Failed to parse log", l); + return null; + } + }) + .filter((e) => e !== null && e.name === "TransferSingle"); + + console.log( + "TransferSingle Events: ", + JSON.stringify(transferSingleEvents, null, 2), + ); + + if (transferSingleEvents.length !== 1) { + throw new MissingDataError( + `Unexpected saw ${transferSingleEvents.length} TransferSingle events`, + ); + } + + // Get claimID + const signerAddress = transferSingleEvents[0].args["from"] as string; + const itemId = transferSingleEvents[0].args["id"] as string; + console.log("Signer Address: ", signerAddress, "Fraction ID: ", itemId); + + // Parse TakerBid events + const takerBidEvents = txnLogs + .map((l) => { + //Ignore unknown events + try { + return contractInterface.parseLog(l); + } catch (e) { + console.log("Failed to parse log", l); + return null; + } + }) + .filter((e) => e !== null && e.name === "TakerBid"); + + console.log("TakerBid Events: ", JSON.stringify(takerBidEvents, null, 2)); + + if (takerBidEvents.length !== 1) { + throw new MissingDataError( + `Unexpected saw ${takerBidEvents.length} TakerBid events`, + ); + } + + // Get claimID + const orderNonce = takerBidEvents[0].args[ + "nonceInvalidationParameters" + ][0] as string; + console.log("Order nonce", orderNonce); + + // Remove from DB + if (await tx.wait(5).then((receipt) => receipt.status === 1)) { + const deleteResult = await client + .from("marketplace-orders") + .delete() + .eq("signer", signerAddress) + .eq("chain_id", network.chainId) + .eq("orderNonce", orderNonce) + .containedBy("itemIds", [itemId]) + .select(); + console.log("Deleted", deleteResult); + + if (!deleteResult) { + throw new Error( + `Could not remove from database. Delete result: ${JSON.stringify( + deleteResult, + )}`, + ); + } + } +} + +const HypercertExchangeABI = [ + { + inputs: [ + { + internalType: "address", + name: "_owner", + type: "address", + }, + { + internalType: "address", + name: "_protocolFeeRecipient", + type: "address", + }, + { + internalType: "address", + name: "_transferManager", + type: "address", + }, + { + internalType: "address", + name: "_weth", + type: "address", + }, + ], + stateMutability: "nonpayable", + type: "constructor", + }, + { + inputs: [], + name: "CallerInvalid", + type: "error", + }, + { + inputs: [], + name: "ChainIdInvalid", + type: "error", + }, + { + inputs: [], + name: "CreatorFeeBpTooHigh", + type: "error", + }, + { + inputs: [], + name: "CurrencyInvalid", + type: "error", + }, + { + inputs: [], + name: "ERC20TransferFromFail", + type: "error", + }, + { + inputs: [], + name: "LengthsInvalid", + type: "error", + }, + { + inputs: [], + name: "MerkleProofInvalid", + type: "error", + }, + { + inputs: [ + { + internalType: "uint256", + name: "length", + type: "uint256", + }, + ], + name: "MerkleProofTooLarge", + type: "error", + }, + { + inputs: [], + name: "NewGasLimitETHTransferTooLow", + type: "error", + }, + { + inputs: [], + name: "NewProtocolFeeRecipientCannotBeNullAddress", + type: "error", + }, + { + inputs: [], + name: "NoOngoingTransferInProgress", + type: "error", + }, + { + inputs: [], + name: "NoSelectorForStrategy", + type: "error", + }, + { + inputs: [], + name: "NoncesInvalid", + type: "error", + }, + { + inputs: [], + name: "NotAContract", + type: "error", + }, + { + inputs: [], + name: "NotOwner", + type: "error", + }, + { + inputs: [], + name: "NotV2Strategy", + type: "error", + }, + { + inputs: [], + name: "NullSignerAddress", + type: "error", + }, + { + inputs: [], + name: "OutsideOfTimeRange", + type: "error", + }, + { + inputs: [], + name: "QuoteTypeInvalid", + type: "error", + }, + { + inputs: [], + name: "ReentrancyFail", + type: "error", + }, + { + inputs: [], + name: "RenouncementNotInProgress", + type: "error", + }, + { + inputs: [], + name: "SameDomainSeparator", + type: "error", + }, + { + inputs: [], + name: "SignatureEOAInvalid", + type: "error", + }, + { + inputs: [], + name: "SignatureERC1271Invalid", + type: "error", + }, + { + inputs: [ + { + internalType: "uint256", + name: "length", + type: "uint256", + }, + ], + name: "SignatureLengthInvalid", + type: "error", + }, + { + inputs: [], + name: "SignatureParameterSInvalid", + type: "error", + }, + { + inputs: [ + { + internalType: "uint8", + name: "v", + type: "uint8", + }, + ], + name: "SignatureParameterVInvalid", + type: "error", + }, + { + inputs: [], + name: "StrategyHasNoSelector", + type: "error", + }, + { + inputs: [ + { + internalType: "uint256", + name: "strategyId", + type: "uint256", + }, + ], + name: "StrategyNotAvailable", + type: "error", + }, + { + inputs: [], + name: "StrategyNotUsed", + type: "error", + }, + { + inputs: [], + name: "StrategyProtocolFeeTooHigh", + type: "error", + }, + { + inputs: [], + name: "TransferAlreadyInProgress", + type: "error", + }, + { + inputs: [], + name: "TransferNotInProgress", + type: "error", + }, + { + inputs: [], + name: "UnsupportedCollectionType", + type: "error", + }, + { + inputs: [], + name: "WrongPotentialOwner", + type: "error", + }, + { + anonymous: false, + inputs: [], + name: "CancelOwnershipTransfer", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "currency", + type: "address", + }, + { + indexed: false, + internalType: "bool", + name: "isAllowed", + type: "bool", + }, + ], + name: "CurrencyStatusUpdated", + type: "event", + }, + { + anonymous: false, + inputs: [], + name: "InitiateOwnershipRenouncement", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "previousOwner", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "potentialOwner", + type: "address", + }, + ], + name: "InitiateOwnershipTransfer", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "user", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "bidNonce", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "askNonce", + type: "uint256", + }, + ], + name: "NewBidAskNonces", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "creatorFeeManager", + type: "address", + }, + ], + name: "NewCreatorFeeManager", + type: "event", + }, + { + anonymous: false, + inputs: [], + name: "NewDomainSeparator", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "gasLimitETHTransfer", + type: "uint256", + }, + ], + name: "NewGasLimitETHTransfer", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "maxCreatorFeeBp", + type: "uint256", + }, + ], + name: "NewMaxCreatorFeeBp", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "newOwner", + type: "address", + }, + ], + name: "NewOwner", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "protocolFeeRecipient", + type: "address", + }, + ], + name: "NewProtocolFeeRecipient", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "strategyId", + type: "uint256", + }, + { + indexed: false, + internalType: "uint16", + name: "standardProtocolFeeBp", + type: "uint16", + }, + { + indexed: false, + internalType: "uint16", + name: "minTotalFeeBp", + type: "uint16", + }, + { + indexed: false, + internalType: "uint16", + name: "maxProtocolFeeBp", + type: "uint16", + }, + { + indexed: false, + internalType: "bytes4", + name: "selector", + type: "bytes4", + }, + { + indexed: false, + internalType: "bool", + name: "isMakerBid", + type: "bool", + }, + { + indexed: false, + internalType: "address", + name: "implementation", + type: "address", + }, + ], + name: "NewStrategy", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "user", + type: "address", + }, + { + indexed: false, + internalType: "uint256[]", + name: "orderNonces", + type: "uint256[]", + }, + ], + name: "OrderNoncesCancelled", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "strategyId", + type: "uint256", + }, + { + indexed: false, + internalType: "bool", + name: "isActive", + type: "bool", + }, + { + indexed: false, + internalType: "uint16", + name: "standardProtocolFeeBp", + type: "uint16", + }, + { + indexed: false, + internalType: "uint16", + name: "minTotalFeeBp", + type: "uint16", + }, + ], + name: "StrategyUpdated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "user", + type: "address", + }, + { + indexed: false, + internalType: "uint256[]", + name: "subsetNonces", + type: "uint256[]", + }, + ], + name: "SubsetNoncesCancelled", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + components: [ + { + internalType: "bytes32", + name: "orderHash", + type: "bytes32", + }, + { + internalType: "uint256", + name: "orderNonce", + type: "uint256", + }, + { + internalType: "bool", + name: "isNonceInvalidated", + type: "bool", + }, + ], + indexed: false, + internalType: "struct ILooksRareProtocol.NonceInvalidationParameters", + name: "nonceInvalidationParameters", + type: "tuple", + }, + { + indexed: false, + internalType: "address", + name: "askUser", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "bidUser", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "strategyId", + type: "uint256", + }, + { + indexed: false, + internalType: "address", + name: "currency", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "collection", + type: "address", + }, + { + indexed: false, + internalType: "uint256[]", + name: "itemIds", + type: "uint256[]", + }, + { + indexed: false, + internalType: "uint256[]", + name: "amounts", + type: "uint256[]", + }, + { + indexed: false, + internalType: "address[2]", + name: "feeRecipients", + type: "address[2]", + }, + { + indexed: false, + internalType: "uint256[3]", + name: "feeAmounts", + type: "uint256[3]", + }, + ], + name: "TakerAsk", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + components: [ + { + internalType: "bytes32", + name: "orderHash", + type: "bytes32", + }, + { + internalType: "uint256", + name: "orderNonce", + type: "uint256", + }, + { + internalType: "bool", + name: "isNonceInvalidated", + type: "bool", + }, + ], + indexed: false, + internalType: "struct ILooksRareProtocol.NonceInvalidationParameters", + name: "nonceInvalidationParameters", + type: "tuple", + }, + { + indexed: false, + internalType: "address", + name: "bidUser", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "bidRecipient", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "strategyId", + type: "uint256", + }, + { + indexed: false, + internalType: "address", + name: "currency", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "collection", + type: "address", + }, + { + indexed: false, + internalType: "uint256[]", + name: "itemIds", + type: "uint256[]", + }, + { + indexed: false, + internalType: "uint256[]", + name: "amounts", + type: "uint256[]", + }, + { + indexed: false, + internalType: "address[2]", + name: "feeRecipients", + type: "address[2]", + }, + { + indexed: false, + internalType: "uint256[3]", + name: "feeAmounts", + type: "uint256[3]", + }, + ], + name: "TakerBid", + type: "event", + }, + { + inputs: [], + name: "MAGIC_VALUE_ORDER_NONCE_EXECUTED", + outputs: [ + { + internalType: "bytes32", + name: "", + type: "bytes32", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "WETH", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint16", + name: "standardProtocolFeeBp", + type: "uint16", + }, + { + internalType: "uint16", + name: "minTotalFeeBp", + type: "uint16", + }, + { + internalType: "uint16", + name: "maxProtocolFeeBp", + type: "uint16", + }, + { + internalType: "bytes4", + name: "selector", + type: "bytes4", + }, + { + internalType: "bool", + name: "isMakerBid", + type: "bool", + }, + { + internalType: "address", + name: "implementation", + type: "address", + }, + ], + name: "addStrategy", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256[]", + name: "orderNonces", + type: "uint256[]", + }, + ], + name: "cancelOrderNonces", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "cancelOwnershipTransfer", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256[]", + name: "subsetNonces", + type: "uint256[]", + }, + ], + name: "cancelSubsetNonces", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "chainId", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "confirmOwnershipRenouncement", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "confirmOwnershipTransfer", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "creatorFeeManager", + outputs: [ + { + internalType: "contract ICreatorFeeManager", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "domainSeparator", + outputs: [ + { + internalType: "bytes32", + name: "", + type: "bytes32", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + components: [ + { + internalType: "address", + name: "recipient", + type: "address", + }, + { + internalType: "bytes", + name: "additionalParameters", + type: "bytes", + }, + ], + internalType: "struct OrderStructs.Taker[]", + name: "takerBids", + type: "tuple[]", + }, + { + components: [ + { + internalType: "enum QuoteType", + name: "quoteType", + type: "uint8", + }, + { + internalType: "uint256", + name: "globalNonce", + type: "uint256", + }, + { + internalType: "uint256", + name: "subsetNonce", + type: "uint256", + }, + { + internalType: "uint256", + name: "orderNonce", + type: "uint256", + }, + { + internalType: "uint256", + name: "strategyId", + type: "uint256", + }, + { + internalType: "enum CollectionType", + name: "collectionType", + type: "uint8", + }, + { + internalType: "address", + name: "collection", + type: "address", + }, + { + internalType: "address", + name: "currency", + type: "address", + }, + { + internalType: "address", + name: "signer", + type: "address", + }, + { + internalType: "uint256", + name: "startTime", + type: "uint256", + }, + { + internalType: "uint256", + name: "endTime", + type: "uint256", + }, + { + internalType: "uint256", + name: "price", + type: "uint256", + }, + { + internalType: "uint256[]", + name: "itemIds", + type: "uint256[]", + }, + { + internalType: "uint256[]", + name: "amounts", + type: "uint256[]", + }, + { + internalType: "bytes", + name: "additionalParameters", + type: "bytes", + }, + ], + internalType: "struct OrderStructs.Maker[]", + name: "makerAsks", + type: "tuple[]", + }, + { + internalType: "bytes[]", + name: "makerSignatures", + type: "bytes[]", + }, + { + components: [ + { + internalType: "bytes32", + name: "root", + type: "bytes32", + }, + { + components: [ + { + internalType: "bytes32", + name: "value", + type: "bytes32", + }, + { + internalType: "enum OrderStructs.MerkleTreeNodePosition", + name: "position", + type: "uint8", + }, + ], + internalType: "struct OrderStructs.MerkleTreeNode[]", + name: "proof", + type: "tuple[]", + }, + ], + internalType: "struct OrderStructs.MerkleTree[]", + name: "merkleTrees", + type: "tuple[]", + }, + { + internalType: "bool", + name: "isAtomic", + type: "bool", + }, + ], + name: "executeMultipleTakerBids", + // @ts-ignore + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + components: [ + { + internalType: "address", + name: "recipient", + type: "address", + }, + { + internalType: "bytes", + name: "additionalParameters", + type: "bytes", + }, + ], + internalType: "struct OrderStructs.Taker", + name: "takerAsk", + type: "tuple", + }, + { + components: [ + { + internalType: "enum QuoteType", + name: "quoteType", + type: "uint8", + }, + { + internalType: "uint256", + name: "globalNonce", + type: "uint256", + }, + { + internalType: "uint256", + name: "subsetNonce", + type: "uint256", + }, + { + internalType: "uint256", + name: "orderNonce", + type: "uint256", + }, + { + internalType: "uint256", + name: "strategyId", + type: "uint256", + }, + { + internalType: "enum CollectionType", + name: "collectionType", + type: "uint8", + }, + { + internalType: "address", + name: "collection", + type: "address", + }, + { + internalType: "address", + name: "currency", + type: "address", + }, + { + internalType: "address", + name: "signer", + type: "address", + }, + { + internalType: "uint256", + name: "startTime", + type: "uint256", + }, + { + internalType: "uint256", + name: "endTime", + type: "uint256", + }, + { + internalType: "uint256", + name: "price", + type: "uint256", + }, + { + internalType: "uint256[]", + name: "itemIds", + type: "uint256[]", + }, + { + internalType: "uint256[]", + name: "amounts", + type: "uint256[]", + }, + { + internalType: "bytes", + name: "additionalParameters", + type: "bytes", + }, + ], + internalType: "struct OrderStructs.Maker", + name: "makerBid", + type: "tuple", + }, + { + internalType: "bytes", + name: "makerSignature", + type: "bytes", + }, + { + components: [ + { + internalType: "bytes32", + name: "root", + type: "bytes32", + }, + { + components: [ + { + internalType: "bytes32", + name: "value", + type: "bytes32", + }, + { + internalType: "enum OrderStructs.MerkleTreeNodePosition", + name: "position", + type: "uint8", + }, + ], + internalType: "struct OrderStructs.MerkleTreeNode[]", + name: "proof", + type: "tuple[]", + }, + ], + internalType: "struct OrderStructs.MerkleTree", + name: "merkleTree", + type: "tuple", + }, + ], + name: "executeTakerAsk", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + components: [ + { + internalType: "address", + name: "recipient", + type: "address", + }, + { + internalType: "bytes", + name: "additionalParameters", + type: "bytes", + }, + ], + internalType: "struct OrderStructs.Taker", + name: "takerBid", + type: "tuple", + }, + { + components: [ + { + internalType: "enum QuoteType", + name: "quoteType", + type: "uint8", + }, + { + internalType: "uint256", + name: "globalNonce", + type: "uint256", + }, + { + internalType: "uint256", + name: "subsetNonce", + type: "uint256", + }, + { + internalType: "uint256", + name: "orderNonce", + type: "uint256", + }, + { + internalType: "uint256", + name: "strategyId", + type: "uint256", + }, + { + internalType: "enum CollectionType", + name: "collectionType", + type: "uint8", + }, + { + internalType: "address", + name: "collection", + type: "address", + }, + { + internalType: "address", + name: "currency", + type: "address", + }, + { + internalType: "address", + name: "signer", + type: "address", + }, + { + internalType: "uint256", + name: "startTime", + type: "uint256", + }, + { + internalType: "uint256", + name: "endTime", + type: "uint256", + }, + { + internalType: "uint256", + name: "price", + type: "uint256", + }, + { + internalType: "uint256[]", + name: "itemIds", + type: "uint256[]", + }, + { + internalType: "uint256[]", + name: "amounts", + type: "uint256[]", + }, + { + internalType: "bytes", + name: "additionalParameters", + type: "bytes", + }, + ], + internalType: "struct OrderStructs.Maker", + name: "makerAsk", + type: "tuple", + }, + { + internalType: "bytes", + name: "makerSignature", + type: "bytes", + }, + { + components: [ + { + internalType: "bytes32", + name: "root", + type: "bytes32", + }, + { + components: [ + { + internalType: "bytes32", + name: "value", + type: "bytes32", + }, + { + internalType: "enum OrderStructs.MerkleTreeNodePosition", + name: "position", + type: "uint8", + }, + ], + internalType: "struct OrderStructs.MerkleTreeNode[]", + name: "proof", + type: "tuple[]", + }, + ], + internalType: "struct OrderStructs.MerkleTree", + name: "merkleTree", + type: "tuple", + }, + ], + name: "executeTakerBid", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "root", + type: "bytes32", + }, + { + internalType: "uint256", + name: "proofLength", + type: "uint256", + }, + ], + name: "hashBatchOrder", + outputs: [ + { + internalType: "bytes32", + name: "batchOrderHash", + type: "bytes32", + }, + ], + stateMutability: "pure", + type: "function", + }, + { + inputs: [ + { + internalType: "bool", + name: "bid", + type: "bool", + }, + { + internalType: "bool", + name: "ask", + type: "bool", + }, + ], + name: "incrementBidAskNonces", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "initiateOwnershipRenouncement", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "newPotentialOwner", + type: "address", + }, + ], + name: "initiateOwnershipTransfer", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + name: "isCurrencyAllowed", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "maxCreatorFeeBp", + outputs: [ + { + internalType: "uint16", + name: "", + type: "uint16", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "owner", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "ownershipStatus", + outputs: [ + { + internalType: "enum IOwnableTwoSteps.Status", + name: "", + type: "uint8", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "potentialOwner", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "protocolFeeRecipient", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + components: [ + { + internalType: "address", + name: "recipient", + type: "address", + }, + { + internalType: "bytes", + name: "additionalParameters", + type: "bytes", + }, + ], + internalType: "struct OrderStructs.Taker", + name: "takerBid", + type: "tuple", + }, + { + components: [ + { + internalType: "enum QuoteType", + name: "quoteType", + type: "uint8", + }, + { + internalType: "uint256", + name: "globalNonce", + type: "uint256", + }, + { + internalType: "uint256", + name: "subsetNonce", + type: "uint256", + }, + { + internalType: "uint256", + name: "orderNonce", + type: "uint256", + }, + { + internalType: "uint256", + name: "strategyId", + type: "uint256", + }, + { + internalType: "enum CollectionType", + name: "collectionType", + type: "uint8", + }, + { + internalType: "address", + name: "collection", + type: "address", + }, + { + internalType: "address", + name: "currency", + type: "address", + }, + { + internalType: "address", + name: "signer", + type: "address", + }, + { + internalType: "uint256", + name: "startTime", + type: "uint256", + }, + { + internalType: "uint256", + name: "endTime", + type: "uint256", + }, + { + internalType: "uint256", + name: "price", + type: "uint256", + }, + { + internalType: "uint256[]", + name: "itemIds", + type: "uint256[]", + }, + { + internalType: "uint256[]", + name: "amounts", + type: "uint256[]", + }, + { + internalType: "bytes", + name: "additionalParameters", + type: "bytes", + }, + ], + internalType: "struct OrderStructs.Maker", + name: "makerAsk", + type: "tuple", + }, + { + internalType: "address", + name: "sender", + type: "address", + }, + { + internalType: "bytes32", + name: "orderHash", + type: "bytes32", + }, + ], + name: "restrictedExecuteTakerBid", + outputs: [ + { + internalType: "uint256", + name: "protocolFeeAmount", + type: "uint256", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + name: "strategyInfo", + outputs: [ + { + internalType: "bool", + name: "isActive", + type: "bool", + }, + { + internalType: "uint16", + name: "standardProtocolFeeBp", + type: "uint16", + }, + { + internalType: "uint16", + name: "minTotalFeeBp", + type: "uint16", + }, + { + internalType: "uint16", + name: "maxProtocolFeeBp", + type: "uint16", + }, + { + internalType: "bytes4", + name: "selector", + type: "bytes4", + }, + { + internalType: "bool", + name: "isMakerBid", + type: "bool", + }, + { + internalType: "address", + name: "implementation", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "transferManager", + outputs: [ + { + internalType: "contract TransferManager", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "newCreatorFeeManager", + type: "address", + }, + ], + name: "updateCreatorFeeManager", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "currency", + type: "address", + }, + { + internalType: "bool", + name: "isAllowed", + type: "bool", + }, + ], + name: "updateCurrencyStatus", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "updateDomainSeparator", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "newGasLimitETHTransfer", + type: "uint256", + }, + ], + name: "updateETHGasLimitForTransfer", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint16", + name: "newMaxCreatorFeeBp", + type: "uint16", + }, + ], + name: "updateMaxCreatorFeeBp", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "newProtocolFeeRecipient", + type: "address", + }, + ], + name: "updateProtocolFeeRecipient", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "strategyId", + type: "uint256", + }, + { + internalType: "bool", + name: "isActive", + type: "bool", + }, + { + internalType: "uint16", + name: "newStandardProtocolFee", + type: "uint16", + }, + { + internalType: "uint16", + name: "newMinTotalFee", + type: "uint16", + }, + ], + name: "updateStrategy", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + name: "userBidAskNonces", + outputs: [ + { + internalType: "uint256", + name: "bidNonce", + type: "uint256", + }, + { + internalType: "uint256", + name: "askNonce", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + name: "userOrderNonce", + outputs: [ + { + internalType: "bytes32", + name: "", + type: "bytes32", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + name: "userSubsetNonce", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "view", + type: "function", + }, +]; From f7f0805ccfee632f57c9488b4385c28e00f21887 Mon Sep 17 00:00:00 2001 From: jipstavenuiter Date: Wed, 20 Dec 2023 14:29:34 +0100 Subject: [PATCH 2/4] (feature): Delete order on execute taker bid autotask --- .../delete-order-on-order-fulfilled.ts | 1895 ----------------- defender/src/auto-tasks/execute-taker-bid.ts | 168 ++ defender/webpack.config.cjs | 2 + 3 files changed, 170 insertions(+), 1895 deletions(-) delete mode 100644 defender/src/auto-tasks/delete-order-on-order-fulfilled.ts create mode 100644 defender/src/auto-tasks/execute-taker-bid.ts diff --git a/defender/src/auto-tasks/delete-order-on-order-fulfilled.ts b/defender/src/auto-tasks/delete-order-on-order-fulfilled.ts deleted file mode 100644 index 9440950e..00000000 --- a/defender/src/auto-tasks/delete-order-on-order-fulfilled.ts +++ /dev/null @@ -1,1895 +0,0 @@ -import { - AutotaskEvent, - BlockTriggerEvent, -} from "@openzeppelin/defender-autotask-utils"; -import { getNetworkConfigFromName } from "../networks"; -import { createClient } from "@supabase/supabase-js"; -import fetch from "node-fetch"; -import { ethers } from "ethers"; -import { MissingDataError, NotImplementedError } from "../errors"; - -export async function handler(event: AutotaskEvent) { - console.log( - "Event: ", - JSON.stringify( - { ...event, secrets: "HIDDEN", credentials: "HIDDEN" }, - null, - 2, - ), - ); - const network = getNetworkConfigFromName(event.autotaskName); - const { SUPABASE_URL, SUPABASE_SECRET_API_KEY } = event.secrets; - const ALCHEMY_KEY = event.secrets[network.alchemyKeyEnvName]; - - const client = createClient(SUPABASE_URL, SUPABASE_SECRET_API_KEY, { - global: { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - fetch: (...args) => fetch(...args), - }, - }); - - let provider; - - if (ALCHEMY_KEY) { - provider = new ethers.providers.AlchemyProvider( - network.networkKey, - ALCHEMY_KEY, - ); - } else if (network.rpc) { - provider = new ethers.providers.JsonRpcProvider(network.rpc); - } else { - throw new Error("No provider available"); - } - - // Check data availability - const body = event.request.body; - if (!("type" in body) || body.type !== "BLOCK") { - throw new NotImplementedError("Event body is not a BlockTriggerEvent"); - } - const blockTriggerEvent = body as BlockTriggerEvent; - const contractAddress = blockTriggerEvent.matchedAddresses[0]; - const fromAddress = blockTriggerEvent.transaction.from; - const txnLogs = blockTriggerEvent.transaction.logs; - const tx = await provider.getTransaction(blockTriggerEvent.hash); - - if (!contractAddress) { - throw new MissingDataError(`body.matchedAddresses is missing`); - } else if (!fromAddress) { - throw new MissingDataError(`body.transaction.from is missing`); - } else if (!txnLogs) { - throw new MissingDataError(`body.transaction.logs is missing`); - } else if (!tx) { - throw new MissingDataError(`tx is missing`); - } - - console.log("Contract address", contractAddress); - console.log("From address", fromAddress); - - // TODO: Update contracts so we can use ABI from the @hypercerts-org/contracts package - const contractInterface = new ethers.utils.Interface(HypercertExchangeABI); - - // Parse TransferSingle events - const transferSingleEvents = txnLogs - .map((l) => { - //Ignore unknown events - try { - return contractInterface.parseLog(l); - } catch (e) { - console.log("Failed to parse log", l); - return null; - } - }) - .filter((e) => e !== null && e.name === "TransferSingle"); - - console.log( - "TransferSingle Events: ", - JSON.stringify(transferSingleEvents, null, 2), - ); - - if (transferSingleEvents.length !== 1) { - throw new MissingDataError( - `Unexpected saw ${transferSingleEvents.length} TransferSingle events`, - ); - } - - // Get claimID - const signerAddress = transferSingleEvents[0].args["from"] as string; - const itemId = transferSingleEvents[0].args["id"] as string; - console.log("Signer Address: ", signerAddress, "Fraction ID: ", itemId); - - // Parse TakerBid events - const takerBidEvents = txnLogs - .map((l) => { - //Ignore unknown events - try { - return contractInterface.parseLog(l); - } catch (e) { - console.log("Failed to parse log", l); - return null; - } - }) - .filter((e) => e !== null && e.name === "TakerBid"); - - console.log("TakerBid Events: ", JSON.stringify(takerBidEvents, null, 2)); - - if (takerBidEvents.length !== 1) { - throw new MissingDataError( - `Unexpected saw ${takerBidEvents.length} TakerBid events`, - ); - } - - // Get claimID - const orderNonce = takerBidEvents[0].args[ - "nonceInvalidationParameters" - ][0] as string; - console.log("Order nonce", orderNonce); - - // Remove from DB - if (await tx.wait(5).then((receipt) => receipt.status === 1)) { - const deleteResult = await client - .from("marketplace-orders") - .delete() - .eq("signer", signerAddress) - .eq("chain_id", network.chainId) - .eq("orderNonce", orderNonce) - .containedBy("itemIds", [itemId]) - .select(); - console.log("Deleted", deleteResult); - - if (!deleteResult) { - throw new Error( - `Could not remove from database. Delete result: ${JSON.stringify( - deleteResult, - )}`, - ); - } - } -} - -const HypercertExchangeABI = [ - { - inputs: [ - { - internalType: "address", - name: "_owner", - type: "address", - }, - { - internalType: "address", - name: "_protocolFeeRecipient", - type: "address", - }, - { - internalType: "address", - name: "_transferManager", - type: "address", - }, - { - internalType: "address", - name: "_weth", - type: "address", - }, - ], - stateMutability: "nonpayable", - type: "constructor", - }, - { - inputs: [], - name: "CallerInvalid", - type: "error", - }, - { - inputs: [], - name: "ChainIdInvalid", - type: "error", - }, - { - inputs: [], - name: "CreatorFeeBpTooHigh", - type: "error", - }, - { - inputs: [], - name: "CurrencyInvalid", - type: "error", - }, - { - inputs: [], - name: "ERC20TransferFromFail", - type: "error", - }, - { - inputs: [], - name: "LengthsInvalid", - type: "error", - }, - { - inputs: [], - name: "MerkleProofInvalid", - type: "error", - }, - { - inputs: [ - { - internalType: "uint256", - name: "length", - type: "uint256", - }, - ], - name: "MerkleProofTooLarge", - type: "error", - }, - { - inputs: [], - name: "NewGasLimitETHTransferTooLow", - type: "error", - }, - { - inputs: [], - name: "NewProtocolFeeRecipientCannotBeNullAddress", - type: "error", - }, - { - inputs: [], - name: "NoOngoingTransferInProgress", - type: "error", - }, - { - inputs: [], - name: "NoSelectorForStrategy", - type: "error", - }, - { - inputs: [], - name: "NoncesInvalid", - type: "error", - }, - { - inputs: [], - name: "NotAContract", - type: "error", - }, - { - inputs: [], - name: "NotOwner", - type: "error", - }, - { - inputs: [], - name: "NotV2Strategy", - type: "error", - }, - { - inputs: [], - name: "NullSignerAddress", - type: "error", - }, - { - inputs: [], - name: "OutsideOfTimeRange", - type: "error", - }, - { - inputs: [], - name: "QuoteTypeInvalid", - type: "error", - }, - { - inputs: [], - name: "ReentrancyFail", - type: "error", - }, - { - inputs: [], - name: "RenouncementNotInProgress", - type: "error", - }, - { - inputs: [], - name: "SameDomainSeparator", - type: "error", - }, - { - inputs: [], - name: "SignatureEOAInvalid", - type: "error", - }, - { - inputs: [], - name: "SignatureERC1271Invalid", - type: "error", - }, - { - inputs: [ - { - internalType: "uint256", - name: "length", - type: "uint256", - }, - ], - name: "SignatureLengthInvalid", - type: "error", - }, - { - inputs: [], - name: "SignatureParameterSInvalid", - type: "error", - }, - { - inputs: [ - { - internalType: "uint8", - name: "v", - type: "uint8", - }, - ], - name: "SignatureParameterVInvalid", - type: "error", - }, - { - inputs: [], - name: "StrategyHasNoSelector", - type: "error", - }, - { - inputs: [ - { - internalType: "uint256", - name: "strategyId", - type: "uint256", - }, - ], - name: "StrategyNotAvailable", - type: "error", - }, - { - inputs: [], - name: "StrategyNotUsed", - type: "error", - }, - { - inputs: [], - name: "StrategyProtocolFeeTooHigh", - type: "error", - }, - { - inputs: [], - name: "TransferAlreadyInProgress", - type: "error", - }, - { - inputs: [], - name: "TransferNotInProgress", - type: "error", - }, - { - inputs: [], - name: "UnsupportedCollectionType", - type: "error", - }, - { - inputs: [], - name: "WrongPotentialOwner", - type: "error", - }, - { - anonymous: false, - inputs: [], - name: "CancelOwnershipTransfer", - type: "event", - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: "address", - name: "currency", - type: "address", - }, - { - indexed: false, - internalType: "bool", - name: "isAllowed", - type: "bool", - }, - ], - name: "CurrencyStatusUpdated", - type: "event", - }, - { - anonymous: false, - inputs: [], - name: "InitiateOwnershipRenouncement", - type: "event", - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: "address", - name: "previousOwner", - type: "address", - }, - { - indexed: false, - internalType: "address", - name: "potentialOwner", - type: "address", - }, - ], - name: "InitiateOwnershipTransfer", - type: "event", - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: "address", - name: "user", - type: "address", - }, - { - indexed: false, - internalType: "uint256", - name: "bidNonce", - type: "uint256", - }, - { - indexed: false, - internalType: "uint256", - name: "askNonce", - type: "uint256", - }, - ], - name: "NewBidAskNonces", - type: "event", - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: "address", - name: "creatorFeeManager", - type: "address", - }, - ], - name: "NewCreatorFeeManager", - type: "event", - }, - { - anonymous: false, - inputs: [], - name: "NewDomainSeparator", - type: "event", - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: "uint256", - name: "gasLimitETHTransfer", - type: "uint256", - }, - ], - name: "NewGasLimitETHTransfer", - type: "event", - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: "uint256", - name: "maxCreatorFeeBp", - type: "uint256", - }, - ], - name: "NewMaxCreatorFeeBp", - type: "event", - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: "address", - name: "newOwner", - type: "address", - }, - ], - name: "NewOwner", - type: "event", - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: "address", - name: "protocolFeeRecipient", - type: "address", - }, - ], - name: "NewProtocolFeeRecipient", - type: "event", - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: "uint256", - name: "strategyId", - type: "uint256", - }, - { - indexed: false, - internalType: "uint16", - name: "standardProtocolFeeBp", - type: "uint16", - }, - { - indexed: false, - internalType: "uint16", - name: "minTotalFeeBp", - type: "uint16", - }, - { - indexed: false, - internalType: "uint16", - name: "maxProtocolFeeBp", - type: "uint16", - }, - { - indexed: false, - internalType: "bytes4", - name: "selector", - type: "bytes4", - }, - { - indexed: false, - internalType: "bool", - name: "isMakerBid", - type: "bool", - }, - { - indexed: false, - internalType: "address", - name: "implementation", - type: "address", - }, - ], - name: "NewStrategy", - type: "event", - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: "address", - name: "user", - type: "address", - }, - { - indexed: false, - internalType: "uint256[]", - name: "orderNonces", - type: "uint256[]", - }, - ], - name: "OrderNoncesCancelled", - type: "event", - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: "uint256", - name: "strategyId", - type: "uint256", - }, - { - indexed: false, - internalType: "bool", - name: "isActive", - type: "bool", - }, - { - indexed: false, - internalType: "uint16", - name: "standardProtocolFeeBp", - type: "uint16", - }, - { - indexed: false, - internalType: "uint16", - name: "minTotalFeeBp", - type: "uint16", - }, - ], - name: "StrategyUpdated", - type: "event", - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: "address", - name: "user", - type: "address", - }, - { - indexed: false, - internalType: "uint256[]", - name: "subsetNonces", - type: "uint256[]", - }, - ], - name: "SubsetNoncesCancelled", - type: "event", - }, - { - anonymous: false, - inputs: [ - { - components: [ - { - internalType: "bytes32", - name: "orderHash", - type: "bytes32", - }, - { - internalType: "uint256", - name: "orderNonce", - type: "uint256", - }, - { - internalType: "bool", - name: "isNonceInvalidated", - type: "bool", - }, - ], - indexed: false, - internalType: "struct ILooksRareProtocol.NonceInvalidationParameters", - name: "nonceInvalidationParameters", - type: "tuple", - }, - { - indexed: false, - internalType: "address", - name: "askUser", - type: "address", - }, - { - indexed: false, - internalType: "address", - name: "bidUser", - type: "address", - }, - { - indexed: false, - internalType: "uint256", - name: "strategyId", - type: "uint256", - }, - { - indexed: false, - internalType: "address", - name: "currency", - type: "address", - }, - { - indexed: false, - internalType: "address", - name: "collection", - type: "address", - }, - { - indexed: false, - internalType: "uint256[]", - name: "itemIds", - type: "uint256[]", - }, - { - indexed: false, - internalType: "uint256[]", - name: "amounts", - type: "uint256[]", - }, - { - indexed: false, - internalType: "address[2]", - name: "feeRecipients", - type: "address[2]", - }, - { - indexed: false, - internalType: "uint256[3]", - name: "feeAmounts", - type: "uint256[3]", - }, - ], - name: "TakerAsk", - type: "event", - }, - { - anonymous: false, - inputs: [ - { - components: [ - { - internalType: "bytes32", - name: "orderHash", - type: "bytes32", - }, - { - internalType: "uint256", - name: "orderNonce", - type: "uint256", - }, - { - internalType: "bool", - name: "isNonceInvalidated", - type: "bool", - }, - ], - indexed: false, - internalType: "struct ILooksRareProtocol.NonceInvalidationParameters", - name: "nonceInvalidationParameters", - type: "tuple", - }, - { - indexed: false, - internalType: "address", - name: "bidUser", - type: "address", - }, - { - indexed: false, - internalType: "address", - name: "bidRecipient", - type: "address", - }, - { - indexed: false, - internalType: "uint256", - name: "strategyId", - type: "uint256", - }, - { - indexed: false, - internalType: "address", - name: "currency", - type: "address", - }, - { - indexed: false, - internalType: "address", - name: "collection", - type: "address", - }, - { - indexed: false, - internalType: "uint256[]", - name: "itemIds", - type: "uint256[]", - }, - { - indexed: false, - internalType: "uint256[]", - name: "amounts", - type: "uint256[]", - }, - { - indexed: false, - internalType: "address[2]", - name: "feeRecipients", - type: "address[2]", - }, - { - indexed: false, - internalType: "uint256[3]", - name: "feeAmounts", - type: "uint256[3]", - }, - ], - name: "TakerBid", - type: "event", - }, - { - inputs: [], - name: "MAGIC_VALUE_ORDER_NONCE_EXECUTED", - outputs: [ - { - internalType: "bytes32", - name: "", - type: "bytes32", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [], - name: "WETH", - outputs: [ - { - internalType: "address", - name: "", - type: "address", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "standardProtocolFeeBp", - type: "uint16", - }, - { - internalType: "uint16", - name: "minTotalFeeBp", - type: "uint16", - }, - { - internalType: "uint16", - name: "maxProtocolFeeBp", - type: "uint16", - }, - { - internalType: "bytes4", - name: "selector", - type: "bytes4", - }, - { - internalType: "bool", - name: "isMakerBid", - type: "bool", - }, - { - internalType: "address", - name: "implementation", - type: "address", - }, - ], - name: "addStrategy", - outputs: [], - stateMutability: "nonpayable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint256[]", - name: "orderNonces", - type: "uint256[]", - }, - ], - name: "cancelOrderNonces", - outputs: [], - stateMutability: "nonpayable", - type: "function", - }, - { - inputs: [], - name: "cancelOwnershipTransfer", - outputs: [], - stateMutability: "nonpayable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint256[]", - name: "subsetNonces", - type: "uint256[]", - }, - ], - name: "cancelSubsetNonces", - outputs: [], - stateMutability: "nonpayable", - type: "function", - }, - { - inputs: [], - name: "chainId", - outputs: [ - { - internalType: "uint256", - name: "", - type: "uint256", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [], - name: "confirmOwnershipRenouncement", - outputs: [], - stateMutability: "nonpayable", - type: "function", - }, - { - inputs: [], - name: "confirmOwnershipTransfer", - outputs: [], - stateMutability: "nonpayable", - type: "function", - }, - { - inputs: [], - name: "creatorFeeManager", - outputs: [ - { - internalType: "contract ICreatorFeeManager", - name: "", - type: "address", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [], - name: "domainSeparator", - outputs: [ - { - internalType: "bytes32", - name: "", - type: "bytes32", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - components: [ - { - internalType: "address", - name: "recipient", - type: "address", - }, - { - internalType: "bytes", - name: "additionalParameters", - type: "bytes", - }, - ], - internalType: "struct OrderStructs.Taker[]", - name: "takerBids", - type: "tuple[]", - }, - { - components: [ - { - internalType: "enum QuoteType", - name: "quoteType", - type: "uint8", - }, - { - internalType: "uint256", - name: "globalNonce", - type: "uint256", - }, - { - internalType: "uint256", - name: "subsetNonce", - type: "uint256", - }, - { - internalType: "uint256", - name: "orderNonce", - type: "uint256", - }, - { - internalType: "uint256", - name: "strategyId", - type: "uint256", - }, - { - internalType: "enum CollectionType", - name: "collectionType", - type: "uint8", - }, - { - internalType: "address", - name: "collection", - type: "address", - }, - { - internalType: "address", - name: "currency", - type: "address", - }, - { - internalType: "address", - name: "signer", - type: "address", - }, - { - internalType: "uint256", - name: "startTime", - type: "uint256", - }, - { - internalType: "uint256", - name: "endTime", - type: "uint256", - }, - { - internalType: "uint256", - name: "price", - type: "uint256", - }, - { - internalType: "uint256[]", - name: "itemIds", - type: "uint256[]", - }, - { - internalType: "uint256[]", - name: "amounts", - type: "uint256[]", - }, - { - internalType: "bytes", - name: "additionalParameters", - type: "bytes", - }, - ], - internalType: "struct OrderStructs.Maker[]", - name: "makerAsks", - type: "tuple[]", - }, - { - internalType: "bytes[]", - name: "makerSignatures", - type: "bytes[]", - }, - { - components: [ - { - internalType: "bytes32", - name: "root", - type: "bytes32", - }, - { - components: [ - { - internalType: "bytes32", - name: "value", - type: "bytes32", - }, - { - internalType: "enum OrderStructs.MerkleTreeNodePosition", - name: "position", - type: "uint8", - }, - ], - internalType: "struct OrderStructs.MerkleTreeNode[]", - name: "proof", - type: "tuple[]", - }, - ], - internalType: "struct OrderStructs.MerkleTree[]", - name: "merkleTrees", - type: "tuple[]", - }, - { - internalType: "bool", - name: "isAtomic", - type: "bool", - }, - ], - name: "executeMultipleTakerBids", - // @ts-ignore - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - components: [ - { - internalType: "address", - name: "recipient", - type: "address", - }, - { - internalType: "bytes", - name: "additionalParameters", - type: "bytes", - }, - ], - internalType: "struct OrderStructs.Taker", - name: "takerAsk", - type: "tuple", - }, - { - components: [ - { - internalType: "enum QuoteType", - name: "quoteType", - type: "uint8", - }, - { - internalType: "uint256", - name: "globalNonce", - type: "uint256", - }, - { - internalType: "uint256", - name: "subsetNonce", - type: "uint256", - }, - { - internalType: "uint256", - name: "orderNonce", - type: "uint256", - }, - { - internalType: "uint256", - name: "strategyId", - type: "uint256", - }, - { - internalType: "enum CollectionType", - name: "collectionType", - type: "uint8", - }, - { - internalType: "address", - name: "collection", - type: "address", - }, - { - internalType: "address", - name: "currency", - type: "address", - }, - { - internalType: "address", - name: "signer", - type: "address", - }, - { - internalType: "uint256", - name: "startTime", - type: "uint256", - }, - { - internalType: "uint256", - name: "endTime", - type: "uint256", - }, - { - internalType: "uint256", - name: "price", - type: "uint256", - }, - { - internalType: "uint256[]", - name: "itemIds", - type: "uint256[]", - }, - { - internalType: "uint256[]", - name: "amounts", - type: "uint256[]", - }, - { - internalType: "bytes", - name: "additionalParameters", - type: "bytes", - }, - ], - internalType: "struct OrderStructs.Maker", - name: "makerBid", - type: "tuple", - }, - { - internalType: "bytes", - name: "makerSignature", - type: "bytes", - }, - { - components: [ - { - internalType: "bytes32", - name: "root", - type: "bytes32", - }, - { - components: [ - { - internalType: "bytes32", - name: "value", - type: "bytes32", - }, - { - internalType: "enum OrderStructs.MerkleTreeNodePosition", - name: "position", - type: "uint8", - }, - ], - internalType: "struct OrderStructs.MerkleTreeNode[]", - name: "proof", - type: "tuple[]", - }, - ], - internalType: "struct OrderStructs.MerkleTree", - name: "merkleTree", - type: "tuple", - }, - ], - name: "executeTakerAsk", - outputs: [], - stateMutability: "nonpayable", - type: "function", - }, - { - inputs: [ - { - components: [ - { - internalType: "address", - name: "recipient", - type: "address", - }, - { - internalType: "bytes", - name: "additionalParameters", - type: "bytes", - }, - ], - internalType: "struct OrderStructs.Taker", - name: "takerBid", - type: "tuple", - }, - { - components: [ - { - internalType: "enum QuoteType", - name: "quoteType", - type: "uint8", - }, - { - internalType: "uint256", - name: "globalNonce", - type: "uint256", - }, - { - internalType: "uint256", - name: "subsetNonce", - type: "uint256", - }, - { - internalType: "uint256", - name: "orderNonce", - type: "uint256", - }, - { - internalType: "uint256", - name: "strategyId", - type: "uint256", - }, - { - internalType: "enum CollectionType", - name: "collectionType", - type: "uint8", - }, - { - internalType: "address", - name: "collection", - type: "address", - }, - { - internalType: "address", - name: "currency", - type: "address", - }, - { - internalType: "address", - name: "signer", - type: "address", - }, - { - internalType: "uint256", - name: "startTime", - type: "uint256", - }, - { - internalType: "uint256", - name: "endTime", - type: "uint256", - }, - { - internalType: "uint256", - name: "price", - type: "uint256", - }, - { - internalType: "uint256[]", - name: "itemIds", - type: "uint256[]", - }, - { - internalType: "uint256[]", - name: "amounts", - type: "uint256[]", - }, - { - internalType: "bytes", - name: "additionalParameters", - type: "bytes", - }, - ], - internalType: "struct OrderStructs.Maker", - name: "makerAsk", - type: "tuple", - }, - { - internalType: "bytes", - name: "makerSignature", - type: "bytes", - }, - { - components: [ - { - internalType: "bytes32", - name: "root", - type: "bytes32", - }, - { - components: [ - { - internalType: "bytes32", - name: "value", - type: "bytes32", - }, - { - internalType: "enum OrderStructs.MerkleTreeNodePosition", - name: "position", - type: "uint8", - }, - ], - internalType: "struct OrderStructs.MerkleTreeNode[]", - name: "proof", - type: "tuple[]", - }, - ], - internalType: "struct OrderStructs.MerkleTree", - name: "merkleTree", - type: "tuple", - }, - ], - name: "executeTakerBid", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "bytes32", - name: "root", - type: "bytes32", - }, - { - internalType: "uint256", - name: "proofLength", - type: "uint256", - }, - ], - name: "hashBatchOrder", - outputs: [ - { - internalType: "bytes32", - name: "batchOrderHash", - type: "bytes32", - }, - ], - stateMutability: "pure", - type: "function", - }, - { - inputs: [ - { - internalType: "bool", - name: "bid", - type: "bool", - }, - { - internalType: "bool", - name: "ask", - type: "bool", - }, - ], - name: "incrementBidAskNonces", - outputs: [], - stateMutability: "nonpayable", - type: "function", - }, - { - inputs: [], - name: "initiateOwnershipRenouncement", - outputs: [], - stateMutability: "nonpayable", - type: "function", - }, - { - inputs: [ - { - internalType: "address", - name: "newPotentialOwner", - type: "address", - }, - ], - name: "initiateOwnershipTransfer", - outputs: [], - stateMutability: "nonpayable", - type: "function", - }, - { - inputs: [ - { - internalType: "address", - name: "", - type: "address", - }, - ], - name: "isCurrencyAllowed", - outputs: [ - { - internalType: "bool", - name: "", - type: "bool", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [], - name: "maxCreatorFeeBp", - outputs: [ - { - internalType: "uint16", - name: "", - type: "uint16", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [], - name: "owner", - outputs: [ - { - internalType: "address", - name: "", - type: "address", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [], - name: "ownershipStatus", - outputs: [ - { - internalType: "enum IOwnableTwoSteps.Status", - name: "", - type: "uint8", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [], - name: "potentialOwner", - outputs: [ - { - internalType: "address", - name: "", - type: "address", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [], - name: "protocolFeeRecipient", - outputs: [ - { - internalType: "address", - name: "", - type: "address", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - components: [ - { - internalType: "address", - name: "recipient", - type: "address", - }, - { - internalType: "bytes", - name: "additionalParameters", - type: "bytes", - }, - ], - internalType: "struct OrderStructs.Taker", - name: "takerBid", - type: "tuple", - }, - { - components: [ - { - internalType: "enum QuoteType", - name: "quoteType", - type: "uint8", - }, - { - internalType: "uint256", - name: "globalNonce", - type: "uint256", - }, - { - internalType: "uint256", - name: "subsetNonce", - type: "uint256", - }, - { - internalType: "uint256", - name: "orderNonce", - type: "uint256", - }, - { - internalType: "uint256", - name: "strategyId", - type: "uint256", - }, - { - internalType: "enum CollectionType", - name: "collectionType", - type: "uint8", - }, - { - internalType: "address", - name: "collection", - type: "address", - }, - { - internalType: "address", - name: "currency", - type: "address", - }, - { - internalType: "address", - name: "signer", - type: "address", - }, - { - internalType: "uint256", - name: "startTime", - type: "uint256", - }, - { - internalType: "uint256", - name: "endTime", - type: "uint256", - }, - { - internalType: "uint256", - name: "price", - type: "uint256", - }, - { - internalType: "uint256[]", - name: "itemIds", - type: "uint256[]", - }, - { - internalType: "uint256[]", - name: "amounts", - type: "uint256[]", - }, - { - internalType: "bytes", - name: "additionalParameters", - type: "bytes", - }, - ], - internalType: "struct OrderStructs.Maker", - name: "makerAsk", - type: "tuple", - }, - { - internalType: "address", - name: "sender", - type: "address", - }, - { - internalType: "bytes32", - name: "orderHash", - type: "bytes32", - }, - ], - name: "restrictedExecuteTakerBid", - outputs: [ - { - internalType: "uint256", - name: "protocolFeeAmount", - type: "uint256", - }, - ], - stateMutability: "nonpayable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint256", - name: "", - type: "uint256", - }, - ], - name: "strategyInfo", - outputs: [ - { - internalType: "bool", - name: "isActive", - type: "bool", - }, - { - internalType: "uint16", - name: "standardProtocolFeeBp", - type: "uint16", - }, - { - internalType: "uint16", - name: "minTotalFeeBp", - type: "uint16", - }, - { - internalType: "uint16", - name: "maxProtocolFeeBp", - type: "uint16", - }, - { - internalType: "bytes4", - name: "selector", - type: "bytes4", - }, - { - internalType: "bool", - name: "isMakerBid", - type: "bool", - }, - { - internalType: "address", - name: "implementation", - type: "address", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [], - name: "transferManager", - outputs: [ - { - internalType: "contract TransferManager", - name: "", - type: "address", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "address", - name: "newCreatorFeeManager", - type: "address", - }, - ], - name: "updateCreatorFeeManager", - outputs: [], - stateMutability: "nonpayable", - type: "function", - }, - { - inputs: [ - { - internalType: "address", - name: "currency", - type: "address", - }, - { - internalType: "bool", - name: "isAllowed", - type: "bool", - }, - ], - name: "updateCurrencyStatus", - outputs: [], - stateMutability: "nonpayable", - type: "function", - }, - { - inputs: [], - name: "updateDomainSeparator", - outputs: [], - stateMutability: "nonpayable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint256", - name: "newGasLimitETHTransfer", - type: "uint256", - }, - ], - name: "updateETHGasLimitForTransfer", - outputs: [], - stateMutability: "nonpayable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "newMaxCreatorFeeBp", - type: "uint16", - }, - ], - name: "updateMaxCreatorFeeBp", - outputs: [], - stateMutability: "nonpayable", - type: "function", - }, - { - inputs: [ - { - internalType: "address", - name: "newProtocolFeeRecipient", - type: "address", - }, - ], - name: "updateProtocolFeeRecipient", - outputs: [], - stateMutability: "nonpayable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint256", - name: "strategyId", - type: "uint256", - }, - { - internalType: "bool", - name: "isActive", - type: "bool", - }, - { - internalType: "uint16", - name: "newStandardProtocolFee", - type: "uint16", - }, - { - internalType: "uint16", - name: "newMinTotalFee", - type: "uint16", - }, - ], - name: "updateStrategy", - outputs: [], - stateMutability: "nonpayable", - type: "function", - }, - { - inputs: [ - { - internalType: "address", - name: "", - type: "address", - }, - ], - name: "userBidAskNonces", - outputs: [ - { - internalType: "uint256", - name: "bidNonce", - type: "uint256", - }, - { - internalType: "uint256", - name: "askNonce", - type: "uint256", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "address", - name: "", - type: "address", - }, - { - internalType: "uint256", - name: "", - type: "uint256", - }, - ], - name: "userOrderNonce", - outputs: [ - { - internalType: "bytes32", - name: "", - type: "bytes32", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "address", - name: "", - type: "address", - }, - { - internalType: "uint256", - name: "", - type: "uint256", - }, - ], - name: "userSubsetNonce", - outputs: [ - { - internalType: "bool", - name: "", - type: "bool", - }, - ], - stateMutability: "view", - type: "function", - }, -]; diff --git a/defender/src/auto-tasks/execute-taker-bid.ts b/defender/src/auto-tasks/execute-taker-bid.ts new file mode 100644 index 00000000..ccc2c040 --- /dev/null +++ b/defender/src/auto-tasks/execute-taker-bid.ts @@ -0,0 +1,168 @@ +import { + AutotaskEvent, + BlockTriggerEvent, +} from "@openzeppelin/defender-autotask-utils"; +import { getNetworkConfigFromName } from "../networks"; +import { createClient } from "@supabase/supabase-js"; +import fetch from "node-fetch"; +import { BigNumber, ethers } from "ethers"; +import { MissingDataError, NotImplementedError } from "../errors"; +import { + HypercertExchangeAbi, + HypercertMinterAbi, +} from "@hypercerts-org/contracts"; + +export async function handler(event: AutotaskEvent) { + console.log( + "Event: ", + JSON.stringify( + { ...event, secrets: "HIDDEN", credentials: "HIDDEN" }, + null, + 2, + ), + ); + const network = getNetworkConfigFromName(event.autotaskName); + const { SUPABASE_URL, SUPABASE_SECRET_API_KEY } = event.secrets; + const ALCHEMY_KEY = event.secrets[network.alchemyKeyEnvName]; + + const client = createClient(SUPABASE_URL, SUPABASE_SECRET_API_KEY, { + global: { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + fetch: (...args) => fetch(...args), + }, + }); + + let provider; + + if (ALCHEMY_KEY) { + provider = new ethers.providers.AlchemyProvider( + network.networkKey, + ALCHEMY_KEY, + ); + } else if (network.rpc) { + provider = new ethers.providers.JsonRpcProvider(network.rpc); + } else { + throw new Error("No provider available"); + } + + // Check data availability + const body = event.request.body; + if (!("type" in body) || body.type !== "BLOCK") { + throw new NotImplementedError("Event body is not a BlockTriggerEvent"); + } + const blockTriggerEvent = body as BlockTriggerEvent; + const contractAddress = blockTriggerEvent.matchedAddresses[0]; + const fromAddress = blockTriggerEvent.transaction.from; + const txnLogs = blockTriggerEvent.transaction.logs; + const tx = await provider.getTransaction(blockTriggerEvent.hash); + + if (!contractAddress) { + throw new MissingDataError(`body.matchedAddresses is missing`); + } else if (!fromAddress) { + throw new MissingDataError(`body.transaction.from is missing`); + } else if (!txnLogs) { + throw new MissingDataError(`body.transaction.logs is missing`); + } else if (!tx) { + throw new MissingDataError(`tx is missing`); + } + + console.log("Contract address", contractAddress); + console.log("From address", fromAddress); + + // TODO: Update contracts so we can use ABI from the @hypercerts-org/contracts package + const hypercertsMinterContractInterface = new ethers.utils.Interface( + HypercertMinterAbi, + ); + + // Parse TransferSingle events + const parsedLogs = txnLogs.map((l) => { + //Ignore unknown events + try { + return hypercertsMinterContractInterface.parseLog(l); + } catch (e) { + console.log("Failed to parse log", l); + return null; + } + }); + console.log("Parsed logs: ", JSON.stringify(parsedLogs, null, 2)); + const transferSingleEvents = parsedLogs.filter( + (e) => e !== null && e.name === "TransferSingle", + ); + + console.log( + "TransferSingle Events: ", + JSON.stringify(transferSingleEvents, null, 2), + ); + + if (transferSingleEvents.length !== 1) { + throw new MissingDataError( + `Unexpected saw ${transferSingleEvents.length} TransferSingle events`, + ); + } + + // Get claimID + const signerAddress = transferSingleEvents[0].args["from"] as string; + const itemId = BigNumber.from(transferSingleEvents[0].args["id"]).toString(); + + const hypercertExchangeContractInterface = new ethers.utils.Interface( + HypercertExchangeAbi, + ); + // Parse TakerBid events + const takerBidEvents = txnLogs + .map((l) => { + //Ignore unknown events + try { + return hypercertExchangeContractInterface.parseLog(l); + } catch (e) { + console.log("Failed to parse log", l); + return null; + } + }) + .filter((e) => e !== null && e.name === "TakerBid"); + + console.log("TakerBid Events: ", JSON.stringify(takerBidEvents, null, 2)); + + if (takerBidEvents.length !== 1) { + throw new MissingDataError( + `Unexpected saw ${takerBidEvents.length} TakerBid events`, + ); + } + + // Get claimID + const orderNonce = BigNumber.from( + takerBidEvents[0].args["nonceInvalidationParameters"][1], + ).toString(); + console.log( + "Signer Address: ", + signerAddress, + "Order nonce: ", + orderNonce, + "Fraction ID: ", + itemId, + "Chain ID: ", + network.chainId, + ); + + // Remove from DB + if (await tx.wait(5).then((receipt) => receipt.status === 1)) { + const deleteResult = await client + .from("marketplace-orders") + .delete() + .eq("signer", signerAddress) + .eq("chainId", network.chainId) + .eq("orderNonce", orderNonce) + .containedBy("itemIds", [itemId]) + .select() + .throwOnError(); + console.log("Deleted", deleteResult); + + if (!deleteResult) { + throw new Error( + `Could not remove from database. Delete result: ${JSON.stringify( + deleteResult, + )}`, + ); + } + } +} diff --git a/defender/webpack.config.cjs b/defender/webpack.config.cjs index bf701568..749abaf2 100644 --- a/defender/webpack.config.cjs +++ b/defender/webpack.config.cjs @@ -9,6 +9,8 @@ module.exports = { "on-allowlist-created": "./src/auto-tasks/on-allowlist-created.ts", "mint-claim-from-allowlist": "./src/auto-tasks/mint-claim-from-allowlist.ts", + "execute-taker-bid": + "./src/auto-tasks/execute-taker-bid.ts", }, target: "node", mode: "development", From 06193836ac13e45f45d65cce20e3bb1f1d5d99d0 Mon Sep 17 00:00:00 2001 From: jipstavenuiter Date: Wed, 20 Dec 2023 15:18:56 +0100 Subject: [PATCH 3/4] (feat): fix setup and rollouts, update scripts --- defender/package.json | 2 +- defender/src/create-sentinel.ts | 11 +++-- defender/src/networks.ts | 17 ++++--- defender/src/rollout.ts | 58 ++++++++++++++++++++--- defender/src/update.ts | 37 +++++++++++++-- pnpm-lock.yaml | 84 +++++++++++++++++++++++++++++++-- 6 files changed, 186 insertions(+), 23 deletions(-) diff --git a/defender/package.json b/defender/package.json index b682499d..5fbc4d36 100644 --- a/defender/package.json +++ b/defender/package.json @@ -15,7 +15,7 @@ }, "dependencies": { "@graphql-mesh/cache-localforage": "^0.95.7", - "@hypercerts-org/contracts": "0.8.11", + "@hypercerts-org/contracts": "1.0.0", "@openzeppelin/defender-autotask-client": "1.50.0", "@openzeppelin/defender-autotask-utils": "1.50.0", "@openzeppelin/defender-base-client": "1.49.0", diff --git a/defender/src/create-sentinel.ts b/defender/src/create-sentinel.ts index 58acfd90..b0f6a560 100644 --- a/defender/src/create-sentinel.ts +++ b/defender/src/create-sentinel.ts @@ -13,22 +13,27 @@ export const createSentinel = async ({ autotaskID, functionConditions = [], eventConditions = [], + overrideContractAddress, + overrideABI, }: { name: string; network: NetworkConfig; autotaskID: string; eventConditions?: EventCondition[]; functionConditions?: FunctionCondition[]; + overrideContractAddress?: string; + overrideABI?: any; }) => { const client = new SentinelClient(config.credentials); + const contractAddress = overrideContractAddress || network.contractAddress; await client .create({ type: "BLOCK", network: network.networkKey, confirmLevel: 1, // if not set, we pick the blockwatcher for the chosen network with the lowest offset name, - addresses: [network.contractAddress], - abi: abi, + addresses: [contractAddress], + abi: overrideABI || abi, paused: false, eventConditions, functionConditions, @@ -41,7 +46,7 @@ export const createSentinel = async ({ `Created sentinel`, res.name, "- monitoring address", - network.contractAddress, + contractAddress, "- linked to autotask", autotaskID, ); diff --git a/defender/src/networks.ts b/defender/src/networks.ts index 80aa187b..237d48f7 100644 --- a/defender/src/networks.ts +++ b/defender/src/networks.ts @@ -55,23 +55,28 @@ export const NETWORKS: SupportedNetworks = { * We'll then subsequently use `getNetworkConfigFromName` * to extract the network name from within the Autotask * @param network + * @param contract * @param name - name pre-encoding * @returns */ -export const encodeName = (network: NetworkConfig, name: string) => - `[${network.networkKey}] ${name}`; +export const encodeName = ( + network: NetworkConfig, + contract: "minter" | "exchange", + name: string, +) => `[${network.networkKey}][${contract}] ${name}`; export const decodeName = ( encodedName: string, -): { networkKey: string; name: string } => { - const regex = /^\[(.+)\]\s(.+)$/; +): { networkKey: string; contract: string; name: string } => { + const regex = /^\[(.+)\]\[(.+)\]\s(.+)$/; const match = encodedName.match(regex); if (!match) { throw new Error(`Invalid encoded name: ${encodedName}`); } const networkKey = match[1]; - const name = match[2]; - return { networkKey, name }; + const contract = match[2]; + const name = match[3]; + return { networkKey, contract, name }; }; /** diff --git a/defender/src/rollout.ts b/defender/src/rollout.ts index 3904cd36..6197cf75 100644 --- a/defender/src/rollout.ts +++ b/defender/src/rollout.ts @@ -2,25 +2,39 @@ import { createTask } from "./create-autotask"; import { createSentinel } from "./create-sentinel"; import { ApiError } from "./errors"; import { NetworkConfig, encodeName } from "./networks"; +import { + HypercertExchangeAbi, + deployments, + asDeployedChain, +} from "@hypercerts-org/contracts"; export const rollOut = async (networks: NetworkConfig[]) => { return await Promise.all( networks.map(async (network) => { + console.log( + "Contract address", + network.chainId.toString(), + asDeployedChain(network.chainId.toString()), + deployments[asDeployedChain(network.chainId.toString())], + deployments[asDeployedChain(network.chainId.toString())] + .HypercertExchange, + ); // On allowlist created const autoTaskOnAllowlistCreated = await createTask( - encodeName(network, "on-allowlist-created"), + encodeName(network, "minter", "on-allowlist-created"), "on-allowlist-created", ); if (!autoTaskOnAllowlistCreated) { throw new ApiError( encodeName( network, + "minter", "Could not create autoTask for on-allowlist-created", ), ); } await createSentinel({ - name: encodeName(network, "AllowlistCreated"), + name: encodeName(network, "minter", "AllowlistCreated"), network: network, eventConditions: [ { eventSignature: "AllowlistCreated(uint256,bytes32)" }, @@ -30,19 +44,20 @@ export const rollOut = async (networks: NetworkConfig[]) => { // On batch minted const autoTaskOnBatchMintClaimsFromAllowlists = await createTask( - encodeName(network, "batch-mint-claims-from-allowlists"), + encodeName(network, "minter", "batch-mint-claims-from-allowlists"), "batch-mint-claims-from-allowlists", ); if (!autoTaskOnBatchMintClaimsFromAllowlists) { throw new ApiError( encodeName( network, + "minter", "Could not create autoTask for batch-mint-claims-from-allowlists", ), ); } await createSentinel({ - name: encodeName(network, "batchMintClaimsFromAllowlists"), + name: encodeName(network, "minter", "batchMintClaimsFromAllowlists"), network: network, autotaskID: autoTaskOnBatchMintClaimsFromAllowlists.autotaskId, functionConditions: [ @@ -55,19 +70,20 @@ export const rollOut = async (networks: NetworkConfig[]) => { // On single minted from allowlist const autoTaskOnMintClaimFromAllowlist = await createTask( - encodeName(network, "mint-claim-from-allowlist"), + encodeName(network, "minter", "mint-claim-from-allowlist"), "mint-claim-from-allowlist", ); if (!autoTaskOnMintClaimFromAllowlist) { throw new ApiError( encodeName( network, + "minter", "Could not create autoTask for mint-claim-from-allowlist", ), ); } await createSentinel({ - name: encodeName(network, "mintClaimFromAllowlist"), + name: encodeName(network, "minter", "mintClaimFromAllowlist"), network: network, autotaskID: autoTaskOnMintClaimFromAllowlist.autotaskId, functionConditions: [ @@ -77,6 +93,36 @@ export const rollOut = async (networks: NetworkConfig[]) => { }, ], }); + + // On execute taker bid + const autoTaskExecuteTakerBid = await createTask( + encodeName(network, "exchange", "execute-taker-bid"), + "execute-taker-bid", + ); + if (!autoTaskExecuteTakerBid) { + throw new ApiError( + encodeName( + network, + "exchange", + "Could not create autoTask for execute-taker-bid", + ), + ); + } + await createSentinel({ + name: encodeName(network, "exchange", "executeTakerBid"), + network: network, + autotaskID: autoTaskExecuteTakerBid.autotaskId, + overrideContractAddress: + deployments[asDeployedChain(network.chainId.toString())] + .HypercertExchange, + overrideABI: HypercertExchangeAbi, + functionConditions: [ + { + functionSignature: + "executeTakerBid((address,bytes),(uint8,uint256,uint256,uint256,uint256,uint8,address,address,address,uint256,uint256,uint256,uint256[],uint256[],bytes),bytes,(bytes32,(bytes32,uint8)[]))", + }, + ], + }); }), ); }; diff --git a/defender/src/update.ts b/defender/src/update.ts index f7fbf463..dee78669 100644 --- a/defender/src/update.ts +++ b/defender/src/update.ts @@ -1,8 +1,13 @@ -import { abi } from "./HypercertMinterABI"; +import { abi as HypercertMinterAbi } from "./HypercertMinterABI"; import config from "./config"; import { NetworkConfig, decodeName } from "./networks"; import { AutotaskClient } from "@openzeppelin/defender-autotask-client"; import { SentinelClient } from "@openzeppelin/defender-sentinel-client"; +import { + asDeployedChain, + deployments, + HypercertExchangeAbi, +} from "@hypercerts-org/contracts"; export const updateAutotask = async (networks: NetworkConfig[]) => { const autotaskClient = new AutotaskClient(config.credentials); @@ -44,12 +49,17 @@ export const updateSentinel = async (networks: NetworkConfig[]) => { const targetNetworks = networks.map((network) => network.networkKey); const oldSentinels = await sentinelClient.list(); + return await Promise.all([ ...oldSentinels.items.map((sentinel) => { // Get name and network - const { name, networkKey } = decodeName(sentinel.name); + const { name, networkKey, contract } = decodeName(sentinel.name); // Validate if in target networks + + let address: string | undefined; + let abi: any; + if (!targetNetworks.includes(networkKey as NetworkConfig["networkKey"])) { return; } @@ -57,6 +67,27 @@ export const updateSentinel = async (networks: NetworkConfig[]) => { (network) => network.networkKey === networkKey, ); + if (contract === "minter") { + address = network?.contractAddress; + abi = HypercertMinterAbi; + } + + if (contract === "exchange") { + const deployment = + deployments[asDeployedChain(network.chainId.toString())]; + address = deployment.HypercertExchange; + abi = HypercertExchangeAbi; + } + + if (!address) { + console.error(`No address found for ${sentinel.subscriberId}`); + return; + } + if (!abi) { + console.error(`No abi found for ${sentinel.subscriberId}`); + return; + } + // Update sentinel console.log( `Updating ${sentinel.subscriberId} from ./build/relay/${name} on ${networkKey}`, @@ -65,7 +96,7 @@ export const updateSentinel = async (networks: NetworkConfig[]) => { sentinelClient .update(sentinel.subscriberId, { ...sentinel, - addresses: [network.contractAddress], + addresses: [address], abi, }) .then((res) => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1439bc98..d21a199f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -202,8 +202,8 @@ importers: specifier: ^0.95.7 version: 0.95.8(@graphql-mesh/types@0.95.8)(@graphql-mesh/utils@0.95.8)(graphql@16.8.1)(tslib@2.6.2) '@hypercerts-org/contracts': - specifier: 0.8.11 - version: 0.8.11 + specifier: 1.0.0 + version: 1.0.0(ts-node@10.9.1)(typescript@4.9.5) '@openzeppelin/defender-autotask-client': specifier: 1.50.0 version: 1.50.0 @@ -7318,8 +7318,16 @@ packages: /@humanwhocodes/object-schema@2.0.1: resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==} - /@hypercerts-org/contracts@0.8.11: - resolution: {integrity: sha512-n6fwMsaoR50VITM2upR4OOi4EZJmZvU6vvXrHWCSakP9e1OeDuAOk+kHiK+egqDRYj6uKtg9VTUFKZBYvu3jRg==} + /@hypercerts-org/contracts@1.0.0(ts-node@10.9.1)(typescript@4.9.5): + resolution: {integrity: sha512-pGdG3BGDz93IZXAAHcvwzRQOMgi9dOzy2ZLdRFKIdWMBoVz8uCQL03HCYutT5DOtVm2g+j2dcor+zSV5yUM5Dg==} + dependencies: + hardhat: 2.19.1(ts-node@10.9.1)(typescript@4.9.5) + transitivePeerDependencies: + - bufferutil + - supports-color + - ts-node + - typescript + - utf-8-validate dev: false /@hypercerts-org/contracts@1.0.0(ts-node@10.9.1)(typescript@5.3.2): @@ -20380,6 +20388,74 @@ packages: - supports-color - utf-8-validate + /hardhat@2.19.1(ts-node@10.9.1)(typescript@4.9.5): + resolution: {integrity: sha512-bsWa63g1GB78ZyMN08WLhFElLPA+J+pShuKD1BFO2+88g3l+BL3R07vj9deIi9dMbssxgE714Gof1dBEDGqnCw==} + hasBin: true + peerDependencies: + ts-node: '*' + typescript: '*' + peerDependenciesMeta: + ts-node: + optional: true + typescript: + optional: true + dependencies: + '@ethersproject/abi': 5.7.0 + '@metamask/eth-sig-util': 4.0.1 + '@nomicfoundation/ethereumjs-block': 5.0.2 + '@nomicfoundation/ethereumjs-blockchain': 7.0.2 + '@nomicfoundation/ethereumjs-common': 4.0.2 + '@nomicfoundation/ethereumjs-evm': 2.0.2 + '@nomicfoundation/ethereumjs-rlp': 5.0.2 + '@nomicfoundation/ethereumjs-statemanager': 2.0.2 + '@nomicfoundation/ethereumjs-trie': 6.0.2 + '@nomicfoundation/ethereumjs-tx': 5.0.2 + '@nomicfoundation/ethereumjs-util': 9.0.2 + '@nomicfoundation/ethereumjs-vm': 7.0.2 + '@nomicfoundation/solidity-analyzer': 0.1.1 + '@sentry/node': 5.30.0 + '@types/bn.js': 5.1.3 + '@types/lru-cache': 5.1.1 + adm-zip: 0.4.16 + aggregate-error: 3.1.0 + ansi-escapes: 4.3.2 + chalk: 2.4.2 + chokidar: 3.5.3 + ci-info: 2.0.0 + debug: 4.3.4(supports-color@8.1.1) + enquirer: 2.4.1 + env-paths: 2.2.1 + ethereum-cryptography: 1.2.0 + ethereumjs-abi: 0.6.8 + find-up: 2.1.0 + fp-ts: 1.19.3 + fs-extra: 7.0.1 + glob: 7.2.0 + immutable: 4.3.4 + io-ts: 1.10.4 + keccak: 3.0.4 + lodash: 4.17.21 + mnemonist: 0.38.5 + mocha: 10.2.0 + p-map: 4.0.0 + raw-body: 2.5.2 + resolve: 1.17.0 + semver: 6.3.1 + solc: 0.7.3(debug@4.3.4) + source-map-support: 0.5.21 + stacktrace-parser: 0.1.10 + ts-node: 10.9.1(@types/node@18.18.7)(typescript@4.9.5) + tsort: 0.0.1 + typescript: 4.9.5 + undici: 5.26.5 + uuid: 8.3.2 + ws: 7.5.9 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: false + /hardhat@2.19.1(ts-node@10.9.1)(typescript@5.3.2): resolution: {integrity: sha512-bsWa63g1GB78ZyMN08WLhFElLPA+J+pShuKD1BFO2+88g3l+BL3R07vj9deIi9dMbssxgE714Gof1dBEDGqnCw==} hasBin: true From 13a4852a632ff2d620d6a168238a3b86ecc01a85 Mon Sep 17 00:00:00 2001 From: jipstavenuiter Date: Wed, 20 Dec 2023 15:24:51 +0100 Subject: [PATCH 4/4] (feat): remove redundancy from create-sentinel.ts config --- defender/src/create-sentinel.ts | 12 +++++------- defender/src/rollout.ts | 11 +++++++++-- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/defender/src/create-sentinel.ts b/defender/src/create-sentinel.ts index b0f6a560..55274f0d 100644 --- a/defender/src/create-sentinel.ts +++ b/defender/src/create-sentinel.ts @@ -1,4 +1,3 @@ -import { abi } from "./HypercertMinterABI.js"; import config from "./config.js"; import { NetworkConfig } from "./networks"; import { SentinelClient } from "@openzeppelin/defender-sentinel-client"; @@ -13,19 +12,18 @@ export const createSentinel = async ({ autotaskID, functionConditions = [], eventConditions = [], - overrideContractAddress, - overrideABI, + contractAddress, + abi, }: { name: string; network: NetworkConfig; autotaskID: string; eventConditions?: EventCondition[]; functionConditions?: FunctionCondition[]; - overrideContractAddress?: string; - overrideABI?: any; + contractAddress: string; + abi: any; }) => { const client = new SentinelClient(config.credentials); - const contractAddress = overrideContractAddress || network.contractAddress; await client .create({ type: "BLOCK", @@ -33,7 +31,7 @@ export const createSentinel = async ({ confirmLevel: 1, // if not set, we pick the blockwatcher for the chosen network with the lowest offset name, addresses: [contractAddress], - abi: overrideABI || abi, + abi, paused: false, eventConditions, functionConditions, diff --git a/defender/src/rollout.ts b/defender/src/rollout.ts index 6197cf75..3139fadd 100644 --- a/defender/src/rollout.ts +++ b/defender/src/rollout.ts @@ -6,6 +6,7 @@ import { HypercertExchangeAbi, deployments, asDeployedChain, + HypercertMinterAbi, } from "@hypercerts-org/contracts"; export const rollOut = async (networks: NetworkConfig[]) => { @@ -36,6 +37,8 @@ export const rollOut = async (networks: NetworkConfig[]) => { await createSentinel({ name: encodeName(network, "minter", "AllowlistCreated"), network: network, + contractAddress: network.contractAddress, + abi: HypercertMinterAbi, eventConditions: [ { eventSignature: "AllowlistCreated(uint256,bytes32)" }, ], @@ -59,6 +62,8 @@ export const rollOut = async (networks: NetworkConfig[]) => { await createSentinel({ name: encodeName(network, "minter", "batchMintClaimsFromAllowlists"), network: network, + contractAddress: network.contractAddress, + abi: HypercertMinterAbi, autotaskID: autoTaskOnBatchMintClaimsFromAllowlists.autotaskId, functionConditions: [ { @@ -85,6 +90,8 @@ export const rollOut = async (networks: NetworkConfig[]) => { await createSentinel({ name: encodeName(network, "minter", "mintClaimFromAllowlist"), network: network, + contractAddress: network.contractAddress, + abi: HypercertMinterAbi, autotaskID: autoTaskOnMintClaimFromAllowlist.autotaskId, functionConditions: [ { @@ -112,10 +119,10 @@ export const rollOut = async (networks: NetworkConfig[]) => { name: encodeName(network, "exchange", "executeTakerBid"), network: network, autotaskID: autoTaskExecuteTakerBid.autotaskId, - overrideContractAddress: + contractAddress: deployments[asDeployedChain(network.chainId.toString())] .HypercertExchange, - overrideABI: HypercertExchangeAbi, + abi: HypercertExchangeAbi, functionConditions: [ { functionSignature: