Skip to content

Commit

Permalink
Add Arbitrum (#157)
Browse files Browse the repository at this point in the history
* add utils script

* fix deployment script

* add arbitrum

* add deployments

* fix deployments

* clean up

* rm deployments

* fix verify-proof

* fix claim membership

* proof in .env

* clean up

* rm json

* use .env
  • Loading branch information
julienbrg authored Dec 7, 2024
1 parent 08efcf8 commit dcb4164
Show file tree
Hide file tree
Showing 42 changed files with 840 additions and 10,304 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ artifacts

.env*
!.env.template
NOTES.md
NOTES.md
/deployments
24 changes: 15 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,34 +62,40 @@ Then, you can go ahead and deploy:

```bash
pnpm crosschain:sepolia
pnpm crosschain:op-sepolia
pnpm crosschain:opSepolia
pnpm crosschain:baseSepolia
pnpm crosschain:arbitrumSepolia
```

Your DAO will be deployed on every networks at the same address.
Your DAO will be deployed on every networks at the same address (watch the [Asciinema video](https://asciinema.org/a/rc8bTqbBiW7e0xevewxCwCP7C)).

Then you can follow these steps to verify that proofs can be generated on home chain and claimed on foreign chain:

```bash

# Watch the [Asciinema video](https://asciinema.org/a/1iZZQVKU51U86hzYYLfjSVtw6)
npx hardhat run scripts/propose.ts --network sepolia
npx hardhat run scripts/verify-proof.ts --network sepolia
npx hardhat run scripts/claim-membership.ts --network op-sepolia
npx hardhat run scripts/claim-membership.ts --network opSepolia
npx hardhat run scripts/claim-membership.ts --network baseSepolia
npx hardhat run scripts/claim-membership.ts --network arbitrumSepolia

npx hardhat run scripts/gov-burn.ts --network sepolia
npx hardhat run scripts/verify-gov-burn-proof.ts --network sepolia
npx hardhat run scripts/claim-gov-burn.ts --network op-sepolia
npx hardhat run scripts/claim-gov-burn.ts --network opSepolia

npx hardhat run scripts/verify-metadata-proof.ts --network sepolia
npx hardhat run scripts/claim-metadata-update.ts --network op-sepolia
npx hardhat run scripts/claim-metadata-update.ts --network opSepolia

npx hardhat run scripts/verify-manifesto-proof.ts --network sepolia
npx hardhat run scripts/claim-manifesto-update.ts --network op-sepolia
npx hardhat run scripts/claim-manifesto-update.ts --network opSepolia

npx hardhat run scripts/gov-voting-delay.ts --network sepolia
npx hardhat run scripts/verify-voting-delay-proof.ts --network sepolia
npx hardhat run scripts/claim-voting-delay.ts --network op-sepolia
npx hardhat run scripts/claim-voting-delay.ts --network opSepolia

npx hardhat run scripts/verify-delegation-proof.ts --network sepolia
npx hardhat run scripts/claim-delegation.ts --network op-sepolia
npx hardhat run scripts/claim-delegation.ts --network opSepolia
```

## Security
Expand Down Expand Up @@ -117,7 +123,7 @@ The following functions are `onlyOwner`, and since the NFT contract ownership is
| Optimism Mainnet | 10 | [Documentation](https://docs.optimism.io/chain/networks#op-mainnet) |
| Base Mainnet | 8453 | [Documentation](https://docs.base.org/docs/network-information#base-mainnet) |
| Sepolia Testnet | 11155111 | [Documentation](https://ethereum.org/nb/developers/docs/networks/#sepolia) |
| OP Sepolia Testnet | 11155420 | [Documentation](https://docs.optimism.io/chain/networks#op-sepolia) |
| OP Sepolia Testnet | 11155420 | [Documentation](https://docs.optimism.io/chain/networks#opSepolia) |
| Base Sepolia Testnet | 84532 | [Documentation](https://docs.base.org/docs/network-information/#base-testnet-sepolia) |

## Core Dependencies
Expand Down
1 change: 0 additions & 1 deletion contracts/NFT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ contract NFT is
) ERC721(_name, _symbol) Ownable(initialOwner) EIP712(_name, "1") {
for (uint i; i < _firstMembers.length; i++) {
safeMint(_firstMembers[i], _uri);
_delegate(_firstMembers[i], _firstMembers[i]);
}
}

Expand Down
65 changes: 15 additions & 50 deletions contracts/variants/crosschain/NFT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -95,20 +95,15 @@ contract NFT is
home = _home;
for (uint i; i < _firstMembers.length; i++) {
_govMint(_firstMembers[i], _uri);
_delegate(_firstMembers[i], _firstMembers[i]);
}
}

/**
* @notice Adds a new member on home chain
* @dev Only callable by owner (governance) on home chain
* @param to Address to mint token to
* @param uri Token metadata URI
*/
/// @notice Adds a new member to the DAO
/// @dev Mints a new NFT to the specified address
/// @param to The address of the new member
/// @param uri The metadata URI for the new NFT
function safeMint(address to, string memory uri) public onlyOwner onlyHomeChain {
uint256 nonce = _proofStorage.incrementNonce(uint8(OperationType.MINT));
_govMint(to, uri);
emit MembershipClaimed(_nextTokenId - 1, to, nonce);
}

/**
Expand Down Expand Up @@ -162,29 +157,6 @@ contract NFT is
return ProofHandler.generateProof(address(this), operationType, params, nextNonce);
}

/**
* @notice Generates proof for delegation
* @dev Only callable on home chain
* @param delegator Address delegating voting power
* @param delegatee Address receiving delegation
* @return Encoded proof data
*/
function generateDelegationProof(
address delegator,
address delegatee
) external view returns (bytes memory) {
require(block.chainid == home, "Proofs only generated on home chain");
uint256 nextNonce = _proofStorage.getNextNonce(uint8(OperationType.DELEGATE));
bytes memory params = abi.encode(delegator, delegatee);
return
ProofHandler.generateProof(
address(this),
uint8(OperationType.DELEGATE),
params,
nextNonce
);
}

// Claim operations

/**
Expand All @@ -196,9 +168,17 @@ contract NFT is
.verifyAndClaimProof(proof, address(this), _proofStorage);

if (operationType == uint8(OperationType.MINT)) {
(address to, string memory uri) = abi.decode(params, (address, string));
_govMint(to, uri);
emit MembershipClaimed(_nextTokenId - 1, to, nonce);
(uint256 tokenId, address owner, string memory uri) = abi.decode(
params,
(uint256, address, string)
);

try this.ownerOf(tokenId) returns (address) {
revert("Token already exists");
} catch {
_govMint(owner, uri);
emit MembershipClaimed(_nextTokenId - 1, owner, nonce);
}
} else if (operationType == uint8(OperationType.BURN)) {
uint256 tokenId = abi.decode(params, (uint256));
address owner = ownerOf(tokenId);
Expand All @@ -215,21 +195,6 @@ contract NFT is
}
}

/**
* @notice Claims a delegation operation on a foreign chain
* @param proof Proof generated by home chain
*/
function claimDelegation(bytes memory proof) external {
(uint8 operationType, bytes memory params, uint256 nonce) = ProofHandler
.verifyAndClaimProof(proof, address(this), _proofStorage);

require(operationType == uint8(OperationType.DELEGATE), "Invalid operation type");
(address delegator, address delegatee) = abi.decode(params, (address, address));

_delegate(delegator, delegatee);
emit DelegationSynced(delegator, delegatee, nonce);
}

/**
* @notice Internal function for minting without proof verification
* @param to Address to receive token
Expand Down
20 changes: 12 additions & 8 deletions contracts/variants/crosschain/ProofHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pragma solidity ^0.8.20;
* @custom:security-contact [email protected]
*/
library ProofHandler {
/// @notice Tracks which proofs have been applied on each chain
/// @notice Tracks which proofs have been applied and nonces for operations that require them
struct ProofStorage {
mapping(bytes32 => bool) updateAppliedOnChain;
mapping(uint8 => uint256) currentNonce;
Expand All @@ -23,7 +23,7 @@ library ProofHandler {
* @param contractAddress Address of contract generating the proof
* @param operationType Type of operation being performed
* @param params Operation parameters
* @param nonce Current nonce for this operation type
* @param nonce Current nonce for this operation type (0 for nonce-free operations)
* @return proof Encoded proof data
*/
function generateProof(
Expand Down Expand Up @@ -60,10 +60,6 @@ library ProofHandler {
(uint8, bytes, uint256, bytes32)
);

bytes32 proofHash = keccak256(proof);
require(!storage_.updateAppliedOnChain[proofHash], "Proof already claimed");
require(nonce == storage_.currentNonce[operationType] + 1, "Invalid nonce");

bytes32 message = keccak256(
abi.encodePacked(contractAddress, operationType, params, nonce)
);
Expand All @@ -72,8 +68,14 @@ library ProofHandler {
);
require(digest == expectedDigest, "Invalid proof");

storage_.updateAppliedOnChain[proofHash] = true;
storage_.currentNonce[operationType] = nonce;
if (operationType > 1) {
bytes32 proofHash = keccak256(proof);
require(!storage_.updateAppliedOnChain[proofHash], "Proof already claimed");
require(nonce == storage_.currentNonce[operationType] + 1, "Invalid nonce");

storage_.updateAppliedOnChain[proofHash] = true;
storage_.currentNonce[operationType] = nonce;
}

emit ProofClaimed(operationType, params, nonce);

Expand All @@ -90,6 +92,7 @@ library ProofHandler {
ProofStorage storage storage_,
uint8 operationType
) public view returns (uint256 nonce) {
if (operationType <= 1) return 0; // MINT or BURN operations don't use nonces
return storage_.currentNonce[operationType] + 1;
}

Expand All @@ -103,6 +106,7 @@ library ProofHandler {
ProofStorage storage storage_,
uint8 operationType
) public returns (uint256 nonce) {
if (operationType <= 1) return 0; // MINT or BURN operations don't use nonces
storage_.currentNonce[operationType]++;
return storage_.currentNonce[operationType];
}
Expand Down
6 changes: 5 additions & 1 deletion dao.config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
///// Home chain /////

export const homeChain = 11155111

///// Membership NFT /////

export const firstMembers = [
Expand All @@ -16,6 +20,6 @@ export const manifesto =
"https://bafkreifnnreoxxgkhty7v2w3qwiie6cfxpv3vcco2xldekfvbiem3nm6dm.ipfs.w3s.link/"
export const daoName = "Test DAO"
export const votingDelay = 0
export const votingPeriod = 200
export const votingPeriod = 60
export const votingThreshold = 1
export const quorum = 5
48 changes: 34 additions & 14 deletions deploy/deploy-crosschain-gov.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { DeployFunction } from "hardhat-deploy/types"
import color from "cli-color"
var msg = color.xterm(39).bgXterm(128)
import {
homeChain,
firstMembers,
uri,
name,
Expand All @@ -19,6 +20,7 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { deployments, getNamedAccounts } = hre
const { deterministic } = deployments
const { deployer } = await getNamedAccounts()
const salt = "-v1"

function wait(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms))
Expand All @@ -30,7 +32,7 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
from: deployer,
contract:
"contracts/variants/crosschain/ProofHandler.sol:ProofHandler",
salt: hre.ethers.id("ProofHandler"),
salt: hre.ethers.id("ProofHandler" + salt),
log: true,
waitConfirmations: 1
})
Expand All @@ -44,13 +46,14 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
{
from: deployer,
contract: "contracts/variants/crosschain/NFT.sol:NFT",
args: [11155111, deployer, firstMembers, uri, name, symbol],
args: [homeChain, deployer, firstMembers, uri, name, symbol],
libraries: {
ProofHandler: proofHandlerAddress
},
salt: hre.ethers.id("NFT"),
salt: hre.ethers.id("NFT" + salt),
log: true,
waitConfirmations: 1
waitConfirmations: 1,
gasLimit: 10000000
}
)

Expand All @@ -64,7 +67,7 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
from: deployer,
contract: "contracts/variants/crosschain/Gov.sol:Gov",
args: [
11155111,
homeChain,
nftAddress,
manifesto,
daoName,
Expand All @@ -76,7 +79,7 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
libraries: {
ProofHandler: proofHandlerAddress
},
salt: hre.ethers.id("Gov"),
salt: hre.ethers.id("Gov" + salt),
log: true,
waitConfirmations: 5
}
Expand All @@ -86,12 +89,29 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
console.log("Gov contract address:", msg(govAddress))

// Transfer NFT ownership to Gov
const nft = await hre.ethers.getContractAt(
"contracts/variants/crosschain/NFT.sol:NFT",
nftAddress
)
await nft.transferOwnership(govAddress)
console.log("NFT ownership transferred to Gov")
try {
let txOptions = {}

switch (hre.network.name) {
case "arbitrum":
case "arbitrumSepolia":
case "sepolia":
case "opSepolia":
txOptions = { gasLimit: 500000 }
break
default:
txOptions = {}
}

const nft = await hre.ethers.getContractAt(
"contracts/variants/crosschain/NFT.sol:NFT",
nftAddress
)
await nft.transferOwnership(govAddress, txOptions)
console.log("NFT ownership transferred to Gov")
} catch (e: any) {
console.warn("error during ownership transfer", e)
}

if (hre.network.name !== "hardhat") {
console.log("\nVerifying ProofHandler library...")
Expand All @@ -112,7 +132,7 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
address: nftAddress,
contract: "contracts/variants/crosschain/NFT.sol:NFT",
constructorArguments: [
11155111,
homeChain,
deployer,
firstMembers,
uri,
Expand All @@ -131,7 +151,7 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
address: govAddress,
contract: "contracts/variants/crosschain/Gov.sol:Gov",
constructorArguments: [
11155111,
homeChain,
nftAddress,
manifesto,
daoName,
Expand Down
4 changes: 2 additions & 2 deletions deploy/deploy-gov.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ export default async ({ getNamedAccounts, deployments }: any) => {

break

case "op-sepolia":
case "opSepolia":
try {
console.log(
"NFT contract deployed:",
Expand Down Expand Up @@ -274,7 +274,7 @@ export default async ({ getNamedAccounts, deployments }: any) => {

break

case "base-sepolia":
case "baseSepolia":
try {
console.log(
"NFT contract deployed:",
Expand Down
1 change: 0 additions & 1 deletion deployments/op-sepolia/.chainId

This file was deleted.

Loading

0 comments on commit dcb4164

Please sign in to comment.