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

Add metadata cross-chain sync #165

Merged
merged 2 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 57 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,42 +48,6 @@ pnpm deploy:sepolia

Then you can add your DAO in [Tally](https://www.tally.xyz/) and/or spin up your own interface using [Gov UI](https://github.com/w3hc/gov-ui).

## Variants

### Crosschain

Make sure the main account has sufficient balance on OP Sepolia and Arbitrum Sepolia:

```
pnpm bal
```

Deploy:

```
pnpm deploy:all
```

Add a member (mint):

```
./scripts/mint.sh
```

Ban a member (burn):

```
./scripts/burn.sh
```

It will:

- Deploy to OP Sepolia
- Deploy to Arbitrum Sepolia
- Submit a proposal and add a member
- Generate a membership proof on OP Sepolia
- Claim that proof on Arbitrum Sepolia

## Security

Here are the differences between the Governor/ERC-721 implementations suggested by Open Zeppelin and ours:
Expand Down Expand Up @@ -126,6 +90,63 @@ The following functions are `onlyOwner`, and since the NFT contract ownership is
| Base Sepolia | https://sepolia.basescan.org | https://api-sepolia.basescan.org/api | BASE_ETHERSCAN_API_KEY |
| Arbitrum Sepolia | https://sepolia.arbiscan.io | https://api-sepolia.arbiscan.io/api | ARBITRUM_ETHERSCAN_API_KEY |

## Variants

### Crosschain

Make sure the main account has sufficient balance on OP Sepolia and Arbitrum Sepolia:

```
pnpm bal
```

Deploy:

```
pnpm deploy:all
```

It will:

- Deploy to OP Sepolia
- Deploy to Arbitrum Sepolia

Add a member (mint):

```
./scripts/mint.sh
```

It will:

- Submit a proposal and add a member on OP Sepolia
- Generate a membership proof on OP Sepolia
- Claim that proof on Arbitrum Sepolia

Ban a member (burn):

```
./scripts/burn.sh
```

It will:

- Submit a proposal and ban a member on OP Sepolia
- Generate a burn proof on OP Sepolia
- Claim that proof on Arbitrum Sepolia

Edit membership NFT metadata:

```
./scripts/metadata.sh
```

It will:

- Submit a proposal edit the NFT metadata of tokenId 1 on OP Sepolia
- Generate a metadata proof on OP Sepolia
- Claim that proof on Arbitrum Sepolia

## Core Dependencies

- Node [v20.9.0](https://nodejs.org/uk/blog/release/v20.9.0/)
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"scripts": {
"compile": "hardhat compile",
"test": "hardhat test",
"test:all": "./scripts/deploy.sh && ./scripts/mint.sh && ./scripts/burn.sh && ./scripts/metadata.sh",
"test:crosschain": "hardhat test test/Gov-crosschain.ts",
"deploy:optimism": "hardhat deploy --network optimism --reset",
"deploy:base": "hardhat deploy --network base --reset",
Expand Down
100 changes: 100 additions & 0 deletions scripts/claim-metadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import hre, { ethers } from "hardhat"
import { NFT__factory } from "../typechain-types/factories/contracts/variants/crosschain/NFT__factory"
import * as fs from "fs"
import * as path from "path"
import color from "cli-color"
var msg = color.xterm(39).bgXterm(128)

function getDeployedAddress(network: string, contractName: string): string {
try {
const deploymentPath = path.join(
__dirname,
"..",
"deployments",
network,
`${contractName}.json`
)
const deployment = JSON.parse(fs.readFileSync(deploymentPath, "utf8"))
return deployment.address
} catch (error) {
throw new Error(
`Failed to read deployment for ${contractName} on ${network}: ${error}`
)
}
}

function getProofFromData(): string {
try {
const dataPath = path.join(__dirname, "..", "data.json")
const data = JSON.parse(fs.readFileSync(dataPath, "utf8"))
return data.proof
} catch (error) {
throw new Error(`Failed to read proof from data.json: ${error}`)
}
}

async function main() {
const SIGNER_PRIVATE_KEY = process.env.SIGNER_PRIVATE_KEY
if (!SIGNER_PRIVATE_KEY) {
throw new Error("Please set SIGNER_PRIVATE_KEY in your .env file")
}

const networkName = hre.network.name
const NFT_ADDRESS = getDeployedAddress(networkName, "CrosschainNFT")
console.log("Using NFT contract address:", NFT_ADDRESS)

const provider = new ethers.JsonRpcProvider(
networkName === "op-sepolia"
? process.env.OP_SEPOLIA_RPC_ENDPOINT_URL
: process.env.ARBITRUM_SEPOLIA_RPC_ENDPOINT_URL
)
const signerZero = new ethers.Wallet(SIGNER_PRIVATE_KEY, provider)

console.log("Using address:", signerZero.address)

const nft = NFT__factory.connect(NFT_ADDRESS, signerZero)

const proof = getProofFromData()
console.log("\nUsing metadata proof:", proof)

try {
console.log("Simulating metadata update claim...")
await nft.claimMetadataUpdate.staticCall(proof)
console.log("✅ Simulation successful")

console.log("Submitting metadata update claim...")
const tx = await nft.claimMetadataUpdate(proof, {
gasLimit: 500000
})

console.log("Transaction submitted:", msg(tx.hash))
console.log("Waiting for confirmation...")

const receipt = await tx.wait()
console.log("Metadata update claimed successfully!")

const updateEvent = receipt?.logs.find(log => {
try {
return nft.interface.parseLog(log)?.name === "MetadataUpdated"
} catch {
return false
}
})

if (updateEvent) {
const parsedEvent = nft.interface.parseLog(updateEvent)
const tokenId = parsedEvent?.args?.tokenId
const newUri = parsedEvent?.args?.newUri
console.log("Updated token ID:", tokenId)
console.log("New metadata URI:", newUri)
}
} catch (error: any) {
console.error("\nError details:", error)
throw error
}
}

main().catch(error => {
console.error(error)
process.exitCode = 1
})
38 changes: 38 additions & 0 deletions scripts/metadata.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/bin/bash

# Color codes
GREEN='\033[0;32m'
RED='\033[0;31m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

echo -e "${BLUE}Starting cross-chain metadata update process...${NC}\n"

# Create proposal on OP Sepolia
echo -e "\n${BLUE}Creating metadata update proposal on OP Sepolia...${NC}"
if npx hardhat run scripts/propose-metadata.ts --network op-sepolia; then
echo -e "${GREEN}✓ Metadata update proposal creation successful${NC}"
else
echo -e "${RED}✗ Metadata update proposal creation failed${NC}"
exit 1
fi

# Generate metadata proof from OP Sepolia
echo -e "\n${BLUE}Generating metadata proof from OP Sepolia...${NC}"
if npx hardhat run scripts/verify-metadata-proof.ts --network op-sepolia; then
echo -e "${GREEN}✓ Metadata proof generation successful${NC}"
else
echo -e "${RED}✗ Metadata proof generation failed${NC}"
exit 1
fi

# Claim metadata update on Arbitrum Sepolia
echo -e "\n${BLUE}Claiming metadata update on Arbitrum Sepolia...${NC}"
if npx hardhat run scripts/claim-metadata.ts --network arbitrum-sepolia; then
echo -e "${GREEN}✓ Metadata update claim successful${NC}"
echo -e "\n${GREEN}✓ All metadata update steps completed successfully!${NC}"
exit 0
else
echo -e "${RED}✗ Metadata update claim failed${NC}"
exit 1
fi
Loading
Loading