Skip to content

Commit

Permalink
Add cross-chain delegation (#169)
Browse files Browse the repository at this point in the history
* add delegation

* clean up

* rm script

* rm empty script
  • Loading branch information
julienbrg authored Dec 17, 2024
1 parent e2cef6c commit fbb1e8f
Show file tree
Hide file tree
Showing 15 changed files with 655 additions and 332 deletions.
96 changes: 28 additions & 68 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,84 +94,44 @@ The following functions are `onlyOwner`, and since the NFT contract ownership is

### Crosschain

Make sure the main account has sufficient balance on OP Sepolia and Arbitrum Sepolia:
Make sure the main account, Bob and Alice have 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):
# Deploy to OP Sepolia and Arbitrum Sepolia
pnpm deploy:op-sepolia
pnpm deploy:arbitrum-sepolia
```
./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
# Add a new member
npx hardhat run scripts/propose.ts --network op-sepolia
npx hardhat run scripts/verify-proof.ts --network op-sepolia
npx hardhat run scripts/claim-membership.ts --network arbitrum-sepolia
Ban a member (burn):
# Ban a member
npx hardhat run scripts/propose-burn.ts --network op-sepolia
npx hardhat run scripts/verify-burn-proof.ts --network op-sepolia
npx hardhat run scripts/claim-burn.ts --network arbitrum-sepolia
```
./scripts/burn.sh
```
# Edit 1 membership NFT metadata
npx hardhat run scripts/propose-metadata.ts --network op-sepolia
npx hardhat run scripts/verify-metadata-proof.ts --network op-sepolia
npx hardhat run scripts/claim-metadata.ts --network arbitrum-sepolia
It will:
# Edit the manifesto
npx hardhat run scripts/propose-manifesto.ts --network op-sepolia
npx hardhat run scripts/verify-manifesto-proof.ts --network op-sepolia
npx hardhat run scripts/claim-manifesto.ts --network arbitrum-sepolia
- Submit a proposal and ban a member on OP Sepolia
- Generate a burn proof on OP Sepolia
- Claim that proof on Arbitrum Sepolia
# Change 1 voting parameter
npx hardhat run scripts/propose-voting-delay.ts --network op-sepolia
npx hardhat run scripts/verify-voting-delay-proof.ts --network op-sepolia
npx hardhat run scripts/claim-voting-delay.ts --network arbitrum-sepolia
Edit membership NFT metadata:

```
./scripts/metadata.sh
# Change delegation
npx hardhat run scripts/propose-delegation.ts --network op-sepolia
npx hardhat run scripts/verify-delegation-proof.ts --network op-sepolia
npx hardhat run scripts/claim-delegation.ts --network arbitrum-sepolia
```

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

Edit the manifesto:

```
./scripts/manifesto.sh
```

It will:

- Submit a proposal to edit the manifesto on OP Sepolia
- Generate a manifesto proof on OP Sepolia
- Claim that proof on Arbitrum Sepolia

Change the voting delay:

```
./scripts/voting-delay.sh
```

It will:

- Submit a proposal to change the voting delay on OP Sepolia
- Generate a voting delay 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
66 changes: 63 additions & 3 deletions contracts/variants/crosschain/NFT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,15 @@ contract NFT is
/// @notice Tracks token existence on each chain
mapping(uint256 => bool) public existsOnChain;

mapping(address => address) public crosschainDelegates;

/// @notice Operation types for cross-chain message verification
/// @dev Used to differentiate between different types of cross-chain operations
enum OperationType {
MINT, // Mint new token
BURN, // Burn existing token
SET_METADATA // Update token metadata
MINT,
BURN,
SET_METADATA,
SET_DELEGATION
}

/**
Expand Down Expand Up @@ -68,6 +71,13 @@ contract NFT is
*/
event MetadataUpdated(uint256 indexed tokenId, string newUri);

event DelegationUpdated(address indexed delegator, address indexed delegate);
event CrosschainDelegationClaimed(
address indexed delegator,
address indexed delegate,
address indexed claimer
);

/**
* @notice Restricts operations to the home chain
* @dev Used to ensure certain operations only occur on the chain where the contract was originally deployed
Expand Down Expand Up @@ -189,6 +199,32 @@ contract NFT is
return abi.encode(tokenId, uri, digest);
}

function delegate(address delegatee) public virtual override onlyHomeChain {
super.delegate(delegatee);
crosschainDelegates[msg.sender] = delegatee;
emit DelegationUpdated(msg.sender, delegatee);
}

function generateDelegationProof(
address delegator,
address delegatee
) external view returns (bytes memory) {
require(block.chainid == home, "Proofs can only be generated on home chain");
require(crosschainDelegates[delegator] == delegatee, "Invalid delegation state");

bytes32 message = keccak256(
abi.encodePacked(
address(this),
uint8(OperationType.SET_DELEGATION),
delegator,
delegatee
)
);
bytes32 digest = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", message));

return abi.encode(delegator, delegatee, digest);
}

/**
* @notice Claims a membership on a foreign chain
* @dev Verifies proof and mints token on foreign chain
Expand Down Expand Up @@ -260,6 +296,30 @@ contract NFT is
emit MetadataUpdated(tokenId, uri);
}

function claimDelegation(bytes memory proof) external {
(address delegator, address delegatee, bytes32 digest) = abi.decode(
proof,
(address, address, bytes32)
);

bytes32 message = keccak256(
abi.encodePacked(
address(this),
uint8(OperationType.SET_DELEGATION),
delegator,
delegatee
)
);
bytes32 expectedDigest = keccak256(
abi.encodePacked("\x19Ethereum Signed Message:\n32", message)
);
require(digest == expectedDigest, "Invalid delegation proof");

_delegate(delegator, delegatee);
crosschainDelegates[delegator] = delegatee;
emit CrosschainDelegationClaimed(delegator, delegatee, msg.sender);
}

// Internal Functions

/**
Expand Down
2 changes: 1 addition & 1 deletion deploy/deploy-crosschain-gov.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { deployments, getNamedAccounts } = hre
const { deterministic } = deployments
const { deployer } = await getNamedAccounts()
const salt = hre.ethers.id("Dec-12-v2")
const salt = hre.ethers.id("Dec-17-v1")
const homeChainId = 11155420

function wait(ms: number): Promise<void> {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
"scripts": {
"compile": "hardhat compile",
"test": "hardhat test",
"test:all": "./scripts/deploy.sh && ./scripts/mint.sh && ./scripts/burn.sh && ./scripts/metadata.sh && ./scripts/manifesto.sh && ./scripts/voting-delay.sh",
"test:crosschain": "hardhat test test/Gov-crosschain.ts",
"deploy:optimism": "hardhat deploy --network optimism --reset",
"deploy:base": "hardhat deploy --network base --reset",
Expand All @@ -16,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",
"crosschain:arbitrum-sepolia": "hardhat deploy --network arbitrum-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",
Expand Down
38 changes: 0 additions & 38 deletions scripts/burn.sh

This file was deleted.

Loading

0 comments on commit fbb1e8f

Please sign in to comment.