Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Feat: bridged nfts page #8

Merged
merged 9 commits into from
Apr 5, 2024
644 changes: 322 additions & 322 deletions apps/event-cache/abis/L1ERC721Bridge.ts

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion apps/event-cache/abis/L2ERC721Bridge.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const L1ERC721Bridge = [{"inputs":[{"internalType":"address","name":"_messenger","type":"address"},{"internalType":"address","name":"_otherBridge","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"localToken","type":"address"},{"indexed":true,"internalType":"address","name":"remoteToken","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"extraData","type":"bytes"}],"name":"ERC721BridgeFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"localToken","type":"address"},{"indexed":true,"internalType":"address","name":"remoteToken","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"extraData","type":"bytes"}],"name":"ERC721BridgeInitiated","type":"event"},{"inputs":[],"name":"MESSENGER","outputs":[{"internalType":"contract CrossDomainMessenger","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OTHER_BRIDGE","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_localToken","type":"address"},{"internalType":"address","name":"_remoteToken","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint32","name":"_minGasLimit","type":"uint32"},{"internalType":"bytes","name":"_extraData","type":"bytes"}],"name":"bridgeERC721","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_localToken","type":"address"},{"internalType":"address","name":"_remoteToken","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint32","name":"_minGasLimit","type":"uint32"},{"internalType":"bytes","name":"_extraData","type":"bytes"}],"name":"bridgeERC721To","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_localToken","type":"address"},{"internalType":"address","name":"_remoteToken","type":"address"},{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"bytes","name":"_extraData","type":"bytes"}],"name":"finalizeBridgeERC721","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"messenger","outputs":[{"internalType":"contract CrossDomainMessenger","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"otherBridge","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}] as const;
export const L2ERC721BridgeAbi = [{ "inputs": [{ "internalType": "address", "name": "_messenger", "type": "address" }, { "internalType": "address", "name": "_otherBridge", "type": "address" }], "stateMutability": "nonpayable", "type": "constructor" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "localToken", "type": "address" }, { "indexed": true, "internalType": "address", "name": "remoteToken", "type": "address" }, { "indexed": true, "internalType": "address", "name": "from", "type": "address" }, { "indexed": false, "internalType": "address", "name": "to", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "tokenId", "type": "uint256" }, { "indexed": false, "internalType": "bytes", "name": "extraData", "type": "bytes" }], "name": "ERC721BridgeFinalized", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "localToken", "type": "address" }, { "indexed": true, "internalType": "address", "name": "remoteToken", "type": "address" }, { "indexed": true, "internalType": "address", "name": "from", "type": "address" }, { "indexed": false, "internalType": "address", "name": "to", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "tokenId", "type": "uint256" }, { "indexed": false, "internalType": "bytes", "name": "extraData", "type": "bytes" }], "name": "ERC721BridgeInitiated", "type": "event" }, { "inputs": [], "name": "MESSENGER", "outputs": [{ "internalType": "contract CrossDomainMessenger", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "OTHER_BRIDGE", "outputs": [{ "internalType": "address", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_localToken", "type": "address" }, { "internalType": "address", "name": "_remoteToken", "type": "address" }, { "internalType": "uint256", "name": "_tokenId", "type": "uint256" }, { "internalType": "uint32", "name": "_minGasLimit", "type": "uint32" }, { "internalType": "bytes", "name": "_extraData", "type": "bytes" }], "name": "bridgeERC721", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_localToken", "type": "address" }, { "internalType": "address", "name": "_remoteToken", "type": "address" }, { "internalType": "address", "name": "_to", "type": "address" }, { "internalType": "uint256", "name": "_tokenId", "type": "uint256" }, { "internalType": "uint32", "name": "_minGasLimit", "type": "uint32" }, { "internalType": "bytes", "name": "_extraData", "type": "bytes" }], "name": "bridgeERC721To", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_localToken", "type": "address" }, { "internalType": "address", "name": "_remoteToken", "type": "address" }, { "internalType": "address", "name": "_from", "type": "address" }, { "internalType": "address", "name": "_to", "type": "address" }, { "internalType": "uint256", "name": "_tokenId", "type": "uint256" }, { "internalType": "bytes", "name": "_extraData", "type": "bytes" }], "name": "finalizeBridgeERC721", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "messenger", "outputs": [{ "internalType": "contract CrossDomainMessenger", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "otherBridge", "outputs": [{ "internalType": "address", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "version", "outputs": [{ "internalType": "string", "name": "", "type": "string" }], "stateMutability": "view", "type": "function" }] as const;
2 changes: 1 addition & 1 deletion apps/event-cache/abis/OptimismMintableERC721Factory.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const OptimismMintableERC721Factory = [
export const OptimismMintableERC721FactoryAbi = [
{
inputs: [
{
Expand Down
3 changes: 3 additions & 0 deletions apps/event-cache/abis/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./L1ERC721Bridge"
export * from "./L2ERC721Bridge"
export * from "./OptimismMintableERC721Factory"
21 changes: 21 additions & 0 deletions apps/event-cache/clients/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { createPublicClient, http } from 'viem';
import { mainnet, sepolia } from "viem/chains";


const mainnetPublicClient = createPublicClient({
chain: mainnet,
transport: http(process.env.PONDER_RPC_URL_1)
})

const sepoliaPublicClient = createPublicClient({
chain: sepolia,
transport: http(process.env.PONDER_RPC_URL_11155111)
})

export function getL1PublicClient(l2chainId: number) {
if (l2chainId === 11155420 || l2chainId === 84532) {
return sepoliaPublicClient;
} else {
return mainnetPublicClient;
}
}
3 changes: 2 additions & 1 deletion apps/event-cache/constants/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export const OPTIMISM_MINTABLE_ERC721_FACTORY_ADDRESS = "0x4200000000000000000000000000000000000017"
export const OPTIMISM_MINTABLE_ERC721_FACTORY_ADDRESS = "0x4200000000000000000000000000000000000017"
export const OPTIMISM_L2_ERC721_BRIDGE_ADDRESS = "0x4200000000000000000000000000000000000014"
74 changes: 66 additions & 8 deletions apps/event-cache/ponder.config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import { createConfig } from "@ponder/core";
import { http } from "viem";
import { OptimismMintableERC721Factory } from "./abis/OptimismMintableERC721Factory";
import { OPTIMISM_MINTABLE_ERC721_FACTORY_ADDRESS } from "./constants";
import { L1ERC721BridgeAbi, L2ERC721BridgeAbi, OptimismMintableERC721FactoryAbi } from "./abis";
import { OPTIMISM_L2_ERC721_BRIDGE_ADDRESS, OPTIMISM_MINTABLE_ERC721_FACTORY_ADDRESS } from "./constants";

export default createConfig({
networks: {
ethereum: {
chainId: 1,
transport: http(process.env.PONDER_RPC_URL_1),
},
sepolia: {
chainId: 11155111,
transport: http(process.env.PONDER_RPC_URL_11155111),
},
optimism: {
chainId: 10,
transport: http(process.env.PONDER_RPC_URL_10),
Expand All @@ -25,31 +33,81 @@ export default createConfig({

contracts: {
OptimismMintableERC721Factory: {
abi: OptimismMintableERC721FactoryAbi,
filter: { event: "OptimismMintableERC721Created" },
address: OPTIMISM_MINTABLE_ERC721_FACTORY_ADDRESS,
network: {
// Production
optimism: {
address: OPTIMISM_MINTABLE_ERC721_FACTORY_ADDRESS,
// Block number of the first transaction
startBlock: 116002023
},
base: {
address: OPTIMISM_MINTABLE_ERC721_FACTORY_ADDRESS,
// Block number of the first transaction
startBlock: 2297164
},
// // Testnet
// Testnet
optimismSepolia: {
address: OPTIMISM_MINTABLE_ERC721_FACTORY_ADDRESS,
// Block number of the first transaction
startBlock: 7074623
},
baseSepolia: {
address: OPTIMISM_MINTABLE_ERC721_FACTORY_ADDRESS,
// Block number of the first transaction
startBlock: 6932093
},
},
abi: OptimismMintableERC721Factory,
},
L1ERC721Bridge: {
abi: L1ERC721BridgeAbi,
filter: { event: ["ERC721BridgeInitiated", "ERC721BridgeFinalized"] },
network: {
// Production
ethereum: {
address: [
// Optimism
"0x5a7749f83b81B301cAb5f48EB8516B986DAef23D",
// Base
"0x608d94945A64503E642E6370Ec598e519a2C1E53",
],
// Block number of the first transaction
startBlock: 15677422,
},
sepolia: {
address: [
// Optimism Sepolia Testnet
"0xd83e03D576d23C9AEab8cC44Fa98d058D2176D1f",
// Base Sepolia Testnet
"0x21eFD066e581FA55Ef105170Cc04d74386a09190"
],
// Block number of the first transaction
startBlock: 5126689
},
},
},
L2ERC721Bridge: {
abi: L2ERC721BridgeAbi,
filter: { event: ["ERC721BridgeInitiated", "ERC721BridgeFinalized"] },
address: OPTIMISM_L2_ERC721_BRIDGE_ADDRESS,
network: {
// Production
optimism: {
// Block number of the first transaction
startBlock: 0
},
base: {
// Block number of the first transaction
startBlock: 0
},
// Testnet
optimismSepolia: {
// Block number of the first transaction
startBlock: 0
},
baseSepolia: {
// Block number of the first transaction
startBlock: 0
},
},
}
},
});
14 changes: 14 additions & 0 deletions apps/event-cache/ponder.schema.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createSchema } from "@ponder/core";

export default createSchema((p) => ({
BridgedErc721State: p.createEnum(["L1", "L2", "PENDING_TO_L1", "PENDING_TO_L2"]),
OptimismMintableERC721: p.createTable({
id: p.string(),
chainId: p.int(),
Expand All @@ -13,4 +14,17 @@ export default createSchema((p) => ({
remoteSymbol: p.string().optional(),
deployer: p.string(),
}),
BridgedErc721: p.createTable({
id: p.string(),
state: p.enum("BridgedErc721State"),
l1ChainId: p.int(),
l2ChainId: p.int(),
l1Token: p.string(),
l2Token: p.string(),
tokenId: p.string(),
timestamp: p.bigint(),
owner: p.string(),
txHash: p.string(),
txChainId: p.int(),
}),
}));
101 changes: 101 additions & 0 deletions apps/event-cache/src/L1ERC721Bridge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { ponder } from "@/generated";
import { base, baseSepolia, optimism, optimismSepolia } from "viem/chains";

function getL2ChainId(l1ContractAddress: string | null) {
if (!l1ContractAddress) {
throw new Error("Invalid l1 contract Address")
}
const l1ContractToChainId: Record<string, number> = {
"0x5a7749f83b81B301cAb5f48EB8516B986DAef23D": optimism.id,
"0x608d94945A64503E642E6370Ec598e519a2C1E53": base.id,
"0xd83e03D576d23C9AEab8cC44Fa98d058D2176D1f": optimismSepolia.id,
"0x21eFD066e581FA55Ef105170Cc04d74386a09190": baseSepolia.id

}
const l2ChainId = l1ContractToChainId?.[l1ContractAddress]
if (!l2ChainId) {
throw new Error("Invalid l1 contract Address: " + l1ContractAddress)
}
return l2ChainId
}


ponder.on("L1ERC721Bridge:ERC721BridgeInitiated", async ({ event, context }) => {
const { BridgedErc721 } = context.db;
const timestamp = event.block.timestamp
const chainId = context.network.chainId
const { localToken: l1Token, remoteToken: l2Token, tokenId, to } = event.args
const l1ContractAddress = event.log.address
const l1ChainId = chainId
const l2ChainId = getL2ChainId(l1ContractAddress)
const id = `${l1ChainId}:${l1Token}:${tokenId}`

const existingBridgedToken = await BridgedErc721.findUnique({
id,
});

// Only update the token if it is the most recent event
if (!existingBridgedToken || timestamp > existingBridgedToken.timestamp) {
await BridgedErc721.upsert({
id,
create: {
state: "PENDING_TO_L2",
l1ChainId,
owner: to,
l1Token,
timestamp,
l2Token,
tokenId: tokenId.toString(),
l2ChainId,
txHash: event.log.transactionHash,
txChainId: chainId
},
update: {
state: "PENDING_TO_L2",
txHash: event.log.transactionHash,
txChainId: chainId
}
});
}


});

ponder.on("L1ERC721Bridge:ERC721BridgeFinalized", async ({ event, context }) => {
const { BridgedErc721 } = context.db;
const timestamp = event.block.timestamp
const chainId = context.network.chainId
const l1ContractAddress = event.log.address
const { localToken: l1Token, remoteToken: l2Token, tokenId, to } = event.args
const l1ChainId = chainId
const l2ChainId = getL2ChainId(l1ContractAddress)
const id = `${l1ChainId}:${l1Token}:${tokenId}`

const existingBridgedToken = await BridgedErc721.findUnique({
id,
});

// Only update the token if it is the most recent event
if (!existingBridgedToken || timestamp > existingBridgedToken.timestamp) {
await BridgedErc721.upsert({
id,
create: {
state: "L1",
l1ChainId,
owner: to,
l1Token,
timestamp,
l2Token,
tokenId: tokenId.toString(),
l2ChainId,
txHash: event.log.transactionHash,
txChainId: chainId
},
update: {
state: "L1",
txHash: event.log.transactionHash,
txChainId: chainId
}
});
}
});
89 changes: 89 additions & 0 deletions apps/event-cache/src/L2ERC721Bridge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { ponder } from "@/generated";
import { base, baseSepolia, mainnet, optimism, optimismSepolia, sepolia } from "viem/chains";

function getL1ChainId(l2ChainId: number) {
const productionChains: number[] = [base.id, optimism.id]
const testnetChains: number[] = [baseSepolia.id, optimismSepolia.id]

if (productionChains.includes(l2ChainId)) {
return mainnet.id
}
if (testnetChains.includes(l2ChainId)) {
return sepolia.id
}
throw new Error("Invalid l2 chain id abc: " + l2ChainId)
}

ponder.on("L2ERC721Bridge:ERC721BridgeInitiated", async ({ event, context }) => {
const { BridgedErc721 } = context.db;
const chainId = context.network.chainId
const timestamp = event.block.timestamp
const { remoteToken: l1Token, localToken: l2Token, tokenId } = event.args
const l1ChainId = getL1ChainId(chainId)
const l2ChainId = chainId
const id = `${l1ChainId}:${l1Token}:${tokenId}`

const existingToken = await BridgedErc721.findUnique({
id,
});

if (!existingToken || timestamp > existingToken.timestamp) {
await BridgedErc721.upsert({
id,
update: {
state: "PENDING_TO_L1",
txHash: event.log.transactionHash,
txChainId: chainId
},
create: {
state: "PENDING_TO_L1",
l1ChainId,
l2ChainId,
l2Token,
owner: event.args.to,
l1Token,
timestamp,
tokenId: tokenId.toString(),
txHash: event.log.transactionHash,
txChainId: chainId
}
});
}
});

ponder.on("L2ERC721Bridge:ERC721BridgeFinalized", async ({ event, context }) => {
const { BridgedErc721 } = context.db;
const chainId = context.network.chainId
const timestamp = event.block.timestamp
const { remoteToken: l1Token, localToken: l2Token, tokenId } = event.args
const l1ChainId = getL1ChainId(chainId)
const l2ChainId = chainId
const id = `${l1ChainId}:${l1Token}:${tokenId}`

const existingToken = await BridgedErc721.findUnique({
id,
});

if (!existingToken || timestamp > existingToken.timestamp) {
await BridgedErc721.upsert({
id,
update: {
state: "L2",
txHash: event.log.transactionHash,
txChainId: chainId
},
create: {
state: "L2",
l1ChainId,
l2ChainId,
l2Token,
owner: event.args.to,
l1Token,
timestamp,
tokenId: tokenId.toString(),
txHash: event.log.transactionHash,
txChainId: chainId
}
});
}
});
Loading
Loading