Skip to content

Commit

Permalink
Add metadata cross-chain sync (#165)
Browse files Browse the repository at this point in the history
* add content

* add metadata
  • Loading branch information
julienbrg authored Dec 12, 2024
1 parent 3ce9f0f commit f646461
Show file tree
Hide file tree
Showing 6 changed files with 551 additions and 36 deletions.
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

0 comments on commit f646461

Please sign in to comment.