Skip to content

Commit

Permalink
Add cross-chain burn sync (#163)
Browse files Browse the repository at this point in the history
* split scripts

* add burn
  • Loading branch information
julienbrg authored Dec 12, 2024
1 parent c87adcf commit 3ce9f0f
Show file tree
Hide file tree
Showing 8 changed files with 380 additions and 28 deletions.
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,28 @@ Then you can add your DAO in [Tally](https://www.tally.xyz/) and/or spin up your

### Crosschain

Run the `scenario1.sh` bash script:
Make sure the main account has sufficient balance on OP Sepolia and Arbitrum Sepolia:

```
./scenario1.sh
pnpm bal
```

Deploy:

```
pnpm deploy:all
```

Add a member (mint):

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

Ban a member (burn):

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

It will:
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"deploy:base-sepolia": "hardhat deploy --network base-sepolia --reset",
"crosschain:sepolia": "hardhat deploy --network sepolia --tags CrosschainGov --reset",
"crosschain:op-sepolia": "hardhat deploy --network op-sepolia --tags CrosschainGov --reset",
"deploy:all": "./scripts/deploy.sh",
"bal": "npx hardhat run scripts/check-my-balance.ts",
"verify:setup": "hardhat run scripts/verify-crosschain-setup.ts",
"prettier": "prettier --write \"**/*.ts\"",
Expand Down
38 changes: 38 additions & 0 deletions scripts/burn.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 burn process...${NC}\n"

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

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

# Claim burn on Arbitrum Sepolia
echo -e "\n${BLUE}Claiming burn on Arbitrum Sepolia...${NC}"
if npx hardhat run scripts/claim-burn.ts --network arbitrum-sepolia; then
echo -e "${GREEN}✓ Burn claim successful${NC}"
echo -e "\n${GREEN}✓ All burn steps completed successfully!${NC}"
exit 0
else
echo -e "${RED}✗ Burn claim failed${NC}"
exit 1
fi
110 changes: 110 additions & 0 deletions scripts/claim-burn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
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)

let 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)

// Get proof from data.json
const proof = getProofFromData()
console.log("\nUsing burn proof:", proof)

try {
console.log("Simulating burn claim transaction...")
await nft.claimBurn.staticCall(proof)
console.log("✅ Simulation successful")

console.log("Submitting burn claim transaction...")
const tx = await nft.claimBurn(proof, {
gasLimit: 500000
})

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

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

// Get token ID from event
const revokeEvent = receipt?.logs.find(log => {
try {
return nft.interface.parseLog(log)?.name === "MembershipRevoked"
} catch {
return false
}
})

if (revokeEvent) {
const parsedEvent = nft.interface.parseLog(revokeEvent)
const tokenId = parsedEvent?.args?.tokenId
const member = parsedEvent?.args?.member
console.log("Burned token ID:", tokenId)
console.log("Former member address:", member)
}
} catch (error: any) {
console.error("\nError details:", error)
if (error.data) {
try {
const decodedError = nft.interface.parseError(error.data)
console.error("Decoded error:", decodedError)
} catch (e) {
console.error("Raw error data:", error.data)
}
}
throw error
}
}

main().catch(error => {
console.error(error)
process.exitCode = 1
})
42 changes: 42 additions & 0 deletions scripts/deploy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/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 deployment process...${NC}\n"

# Deploy to OP Sepolia
echo -e "${BLUE}Deploying to OP Sepolia...${NC}"
if pnpm crosschain:op-sepolia; then
echo -e "${GREEN}✓ OP Sepolia deployment successful${NC}"
else
echo -e "${RED}✗ OP Sepolia deployment failed${NC}"
exit 1
fi

# Wait a bit to ensure deployment is fully confirmed
echo -e "\n${BLUE}Waiting 30 seconds before proceeding to Arbitrum deployment...${NC}"
sleep 30

# Deploy to Arbitrum Sepolia
echo -e "\n${BLUE}Deploying to Arbitrum Sepolia...${NC}"
if pnpm crosschain:arbitrum-sepolia; then
echo -e "${GREEN}✓ Arbitrum Sepolia deployment successful${NC}"
else
echo -e "${RED}✗ Arbitrum Sepolia deployment failed${NC}"
exit 1
fi

# Run verification script
echo -e "\n${BLUE}Running cross-chain setup verification...${NC}"
if pnpm verify:setup; then
echo -e "${GREEN}✓ Cross-chain setup verification successful${NC}"
echo -e "\n${GREEN}✓ Deployment and verification completed successfully!${NC}"
exit 0
else
echo -e "${RED}✗ Cross-chain setup verification failed${NC}"
exit 1
fi
26 changes: 0 additions & 26 deletions scenario1.sh → scripts/mint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,6 @@ NC='\033[0m' # No Color

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

# Deploy to OP Sepolia
echo -e "${BLUE}Deploying to OP Sepolia...${NC}"
if pnpm crosschain:op-sepolia; then
echo -e "${GREEN}✓ OP Sepolia deployment successful${NC}"
else
echo -e "${RED}✗ OP Sepolia deployment failed${NC}"
exit 1
fi

# Wait a bit to ensure deployment is fully confirmed
echo -e "\n${BLUE}Waiting 30 seconds before proceeding to Arbitrum deployment...${NC}"
sleep 30

# Deploy to Arbitrum Sepolia
echo -e "\n${BLUE}Deploying to Arbitrum Sepolia...${NC}"
if pnpm crosschain:arbitrum-sepolia; then
echo -e "${GREEN}✓ Arbitrum Sepolia deployment successful${NC}"
else
echo -e "${RED}✗ Arbitrum Sepolia deployment failed${NC}"
exit 1
fi

# Wait for deployment to be fully confirmed
echo -e "\n${BLUE}Waiting 30 seconds before running verification...${NC}"
sleep 30

# Run verification script
echo -e "\n${BLUE}Running cross-chain setup verification...${NC}"
if pnpm verify:setup; then
Expand Down
116 changes: 116 additions & 0 deletions scripts/propose-burn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import hre, { ethers } from "hardhat"
import { Gov__factory } from "../typechain-types/factories/contracts/variants/crosschain/Gov__factory"
import { NFT__factory } from "../typechain-types/factories/contracts/variants/crosschain/NFT__factory"
import * as fs from "fs"
import * as path from "path"

function sleep(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms))
}

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}`
)
}
}

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

// Get the network from hardhat config
const networkName = hre.network.name

// Get deployed addresses from deployment files
const NFT_ADDRESS = getDeployedAddress(networkName, "CrosschainNFT")
const GOV_ADDRESS = getDeployedAddress(networkName, "CrosschainGov")

console.log("Using contract addresses:")
console.log("NFT:", NFT_ADDRESS)
console.log("Gov:", GOV_ADDRESS)

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

const aliceSigner = new ethers.Wallet(ALICE_PRIVATE_KEY, provider)
const signerZero = new ethers.Wallet(SIGNER_PRIVATE_KEY, provider)
console.log("Using address for proposals:", aliceSigner.address)
console.log("Using address for execution:", signerZero.address)

const gov = Gov__factory.connect(GOV_ADDRESS, aliceSigner)
const nft = NFT__factory.connect(NFT_ADDRESS, aliceSigner)

// Token ID to burn
const tokenIdToBurn = 2n // Adjust as needed

// Check current voting power
const votingPower = await nft.getVotes(aliceSigner.address)
console.log("Current voting power:", votingPower)

if (votingPower === 0n) {
console.log("Delegating voting power...")
const tx = await nft.delegate(aliceSigner.address)
await tx.wait(3)
console.log("Delegation completed")
console.log(
"New voting power:",
(await nft.getVotes(aliceSigner.address)).toString()
)
}

console.log("Creating proposal to burn member NFT...")

try {
const targets = [nft.target]
const values = [0]

// Prepare the NFT burn call through the Gov contract
const burnCall = nft.interface.encodeFunctionData("govBurn", [
tokenIdToBurn
])

const calldatas = [burnCall]
const description = "Burn member NFT " + Date.now()

console.log("Creating proposal with:")
console.log("- Target:", targets[0])
console.log("- Value:", values[0])
console.log("- Calldata:", calldatas[0])
console.log("- Description:", description)

// Rest of the proposal creation and execution logic remains the same as in propose.ts
// ... [Same voting and execution logic as in propose.ts]
} catch (error: any) {
console.error("\nError details:", error)
if (error.data) {
try {
const decodedError = gov.interface.parseError(error.data)
console.error("Decoded error:", decodedError)
} catch (e) {
console.error("Could not decode error data")
}
}
throw error
}
}
Loading

0 comments on commit 3ce9f0f

Please sign in to comment.