Skip to content

Commit febcc34

Browse files
committed
fix claim membership
1 parent 241a320 commit febcc34

File tree

13 files changed

+264
-234
lines changed

13 files changed

+264
-234
lines changed

contracts/NFT.sol

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ contract NFT is
3838
) ERC721(_name, _symbol) Ownable(initialOwner) EIP712(_name, "1") {
3939
for (uint i; i < _firstMembers.length; i++) {
4040
safeMint(_firstMembers[i], _uri);
41-
_delegate(_firstMembers[i], _firstMembers[i]);
4241
}
4342
}
4443

contracts/variants/crosschain/NFT.sol

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -95,20 +95,15 @@ contract NFT is
9595
home = _home;
9696
for (uint i; i < _firstMembers.length; i++) {
9797
_govMint(_firstMembers[i], _uri);
98-
_delegate(_firstMembers[i], _firstMembers[i]);
9998
}
10099
}
101100

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

114109
/**
@@ -173,9 +168,17 @@ contract NFT is
173168
.verifyAndClaimProof(proof, address(this), _proofStorage);
174169

175170
if (operationType == uint8(OperationType.MINT)) {
176-
(address to, string memory uri) = abi.decode(params, (address, string));
177-
_govMint(to, uri);
178-
emit MembershipClaimed(_nextTokenId - 1, to, nonce);
171+
(uint256 tokenId, address owner, string memory uri) = abi.decode(
172+
params,
173+
(uint256, address, string)
174+
);
175+
176+
try this.ownerOf(tokenId) returns (address) {
177+
revert("Token already exists");
178+
} catch {
179+
_govMint(owner, uri);
180+
emit MembershipClaimed(_nextTokenId - 1, owner, nonce);
181+
}
179182
} else if (operationType == uint8(OperationType.BURN)) {
180183
uint256 tokenId = abi.decode(params, (uint256));
181184
address owner = ownerOf(tokenId);

contracts/variants/crosschain/ProofHandler.sol

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pragma solidity ^0.8.20;
99
* @custom:security-contact [email protected]
1010
*/
1111
library ProofHandler {
12-
/// @notice Tracks which proofs have been applied on each chain
12+
/// @notice Tracks which proofs have been applied and nonces for operations that require them
1313
struct ProofStorage {
1414
mapping(bytes32 => bool) updateAppliedOnChain;
1515
mapping(uint8 => uint256) currentNonce;
@@ -23,7 +23,7 @@ library ProofHandler {
2323
* @param contractAddress Address of contract generating the proof
2424
* @param operationType Type of operation being performed
2525
* @param params Operation parameters
26-
* @param nonce Current nonce for this operation type
26+
* @param nonce Current nonce for this operation type (0 for nonce-free operations)
2727
* @return proof Encoded proof data
2828
*/
2929
function generateProof(
@@ -60,10 +60,6 @@ library ProofHandler {
6060
(uint8, bytes, uint256, bytes32)
6161
);
6262

63-
bytes32 proofHash = keccak256(proof);
64-
require(!storage_.updateAppliedOnChain[proofHash], "Proof already claimed");
65-
require(nonce == storage_.currentNonce[operationType] + 1, "Invalid nonce");
66-
6763
bytes32 message = keccak256(
6864
abi.encodePacked(contractAddress, operationType, params, nonce)
6965
);
@@ -72,8 +68,14 @@ library ProofHandler {
7268
);
7369
require(digest == expectedDigest, "Invalid proof");
7470

75-
storage_.updateAppliedOnChain[proofHash] = true;
76-
storage_.currentNonce[operationType] = nonce;
71+
if (operationType > 1) {
72+
bytes32 proofHash = keccak256(proof);
73+
require(!storage_.updateAppliedOnChain[proofHash], "Proof already claimed");
74+
require(nonce == storage_.currentNonce[operationType] + 1, "Invalid nonce");
75+
76+
storage_.updateAppliedOnChain[proofHash] = true;
77+
storage_.currentNonce[operationType] = nonce;
78+
}
7779

7880
emit ProofClaimed(operationType, params, nonce);
7981

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

@@ -103,6 +106,7 @@ library ProofHandler {
103106
ProofStorage storage storage_,
104107
uint8 operationType
105108
) public returns (uint256 nonce) {
109+
if (operationType <= 1) return 0; // MINT or BURN operations don't use nonces
106110
storage_.currentNonce[operationType]++;
107111
return storage_.currentNonce[operationType];
108112
}

dao.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ export const manifesto =
2020
"https://bafkreifnnreoxxgkhty7v2w3qwiie6cfxpv3vcco2xldekfvbiem3nm6dm.ipfs.w3s.link/"
2121
export const daoName = "Test DAO"
2222
export const votingDelay = 0
23-
export const votingPeriod = 30
23+
export const votingPeriod = 60
2424
export const votingThreshold = 1
2525
export const quorum = 5

deploy/deploy-crosschain-gov.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
2020
const { deployments, getNamedAccounts } = hre
2121
const { deterministic } = deployments
2222
const { deployer } = await getNamedAccounts()
23-
const salt = "-v10"
23+
const salt = "-v1"
2424

2525
function wait(ms: number): Promise<void> {
2626
return new Promise(resolve => setTimeout(resolve, ms))
@@ -52,7 +52,8 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
5252
},
5353
salt: hre.ethers.id("NFT" + salt),
5454
log: true,
55-
waitConfirmations: 1
55+
waitConfirmations: 1,
56+
gasLimit: 10000000
5657
}
5758
)
5859

hardhat.config.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,51 +37,58 @@ const config: HardhatUserConfig = {
3737
SEPOLIA_RPC_ENDPOINT_URL ||
3838
"https://ethereum-sepolia.publicnode.com",
3939
accounts:
40-
SIGNER_PRIVATE_KEY !== undefined ? [SIGNER_PRIVATE_KEY] : []
40+
SIGNER_PRIVATE_KEY !== undefined ? [SIGNER_PRIVATE_KEY] : [],
41+
allowUnlimitedContractSize: true
4142
},
4243
optimism: {
4344
chainId: 10,
4445
url:
4546
OPTIMISM_MAINNET_RPC_ENDPOINT_URL ||
4647
"https://mainnet.optimism.io",
4748
accounts:
48-
SIGNER_PRIVATE_KEY !== undefined ? [SIGNER_PRIVATE_KEY] : []
49+
SIGNER_PRIVATE_KEY !== undefined ? [SIGNER_PRIVATE_KEY] : [],
50+
allowUnlimitedContractSize: true
4951
},
5052
base: {
5153
chainId: 8453,
5254
url: BASE_MAINNET_RPC_ENDPOINT_URL || "https://mainnet.base.org",
5355
accounts:
54-
SIGNER_PRIVATE_KEY !== undefined ? [SIGNER_PRIVATE_KEY] : []
56+
SIGNER_PRIVATE_KEY !== undefined ? [SIGNER_PRIVATE_KEY] : [],
57+
allowUnlimitedContractSize: true
5558
},
5659
arbitrum: {
5760
chainId: 42161,
5861
url:
5962
ARBITRUM_MAINNET_RPC_ENDPOINT_URL ||
6063
"https://endpoints.omniatech.io/v1/arbitrum/one/public",
6164
accounts:
62-
SIGNER_PRIVATE_KEY !== undefined ? [SIGNER_PRIVATE_KEY] : []
65+
SIGNER_PRIVATE_KEY !== undefined ? [SIGNER_PRIVATE_KEY] : [],
66+
allowUnlimitedContractSize: true
6367
},
6468
opSepolia: {
6569
chainId: 11155420,
6670
url:
6771
OP_SEPOLIA_RPC_ENDPOINT_URL ||
6872
"https://ethereum-sepolia.publicnode.com",
6973
accounts:
70-
SIGNER_PRIVATE_KEY !== undefined ? [SIGNER_PRIVATE_KEY] : []
74+
SIGNER_PRIVATE_KEY !== undefined ? [SIGNER_PRIVATE_KEY] : [],
75+
allowUnlimitedContractSize: true
7176
},
7277
baseSepolia: {
7378
chainId: 84532,
7479
url: BASE_SEPOLIA_RPC_ENDPOINT_URL || "https://sepolia.base.org",
7580
accounts:
76-
SIGNER_PRIVATE_KEY !== undefined ? [SIGNER_PRIVATE_KEY] : []
81+
SIGNER_PRIVATE_KEY !== undefined ? [SIGNER_PRIVATE_KEY] : [],
82+
allowUnlimitedContractSize: true
7783
},
7884
arbitrumSepolia: {
7985
chainId: 421614,
8086
url:
8187
ARBITRUM_SEPOLIA_RPC_ENDPOINT_URL ||
8288
"https://sepolia-rollup.arbitrum.io/rpc",
8389
accounts:
84-
SIGNER_PRIVATE_KEY !== undefined ? [SIGNER_PRIVATE_KEY] : []
90+
SIGNER_PRIVATE_KEY !== undefined ? [SIGNER_PRIVATE_KEY] : [],
91+
allowUnlimitedContractSize: true
8592
}
8693
},
8794
solidity: {
@@ -90,7 +97,8 @@ const config: HardhatUserConfig = {
9097
optimizer: {
9198
enabled: true,
9299
runs: 200
93-
}
100+
},
101+
viaIR: false
94102
}
95103
},
96104
sourcify: {

proofs.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
[
22
{
3-
"tokenId": 2,
4-
"proof": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001479e565e88c0cbb734674eed176f8240c7c5119ff57026359ae3e30fd043d01d00000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000bdc0e420ab9ba144213588a95fa1e5e63ceff1be0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005268747470733a2f2f6261666b726569636a36326c35787536706b3278783778376e3662377270756e78623465686c6837666576796a6170696433353536736d757a34792e697066732e7733732e6c696e6b2f0000000000000000000000000000",
3+
"tokenId": 4,
4+
"proof": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000001b166dfd5dc554d84d8799c90435ab210f94c8c624aa98d0c47305f5ece2168e00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000004000000000000000000000000bdc0e420ab9ba144213588a95fa1e5e63ceff1be0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000005268747470733a2f2f6261666b726569636a36326c35787536706b3278783778376e3662377270756e78623465686c6837666576796a6170696433353536736d757a34792e697066732e7733732e6c696e6b2f0000000000000000000000000000",
55
"owner": "0xBDC0E420aB9ba144213588A95fa1E5e63CEFf1bE",
6-
"nonce": 1
6+
"nonce": 0
77
}
88
]

scenario.sh

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,22 @@
11
#!/bin/bash
22

3-
# echo "Running unit tests..."
4-
# pnpm test
3+
pnpm test
54

6-
# echo "Deploying to Sepolia..."
7-
# pnpm crosschain:sepolia
5+
pnpm crosschain:sepolia
86

9-
# echo "Deploying to Optimism Sepolia..."
10-
# pnpm crosschain:opSepolia
7+
pnpm crosschain:opSepolia
118

12-
# echo "Checking token existence..."
13-
# npx hardhat run scripts/check-token-existence.ts
9+
echo "Checking token existence..."
10+
npx hardhat run scripts/check-token-existence.ts
1411

15-
# echo "Creating proposal..."
16-
# npx hardhat run scripts/propose.ts --network sepolia
12+
echo "Creating proposal..."
13+
npx hardhat run scripts/propose.ts --network sepolia
1714

18-
export TOKENID=2
15+
# export TOKENID=2
1916

2017
npx hardhat run scripts/check-token-existence.ts
2118

22-
# The propose.ts script writes TOKENID to .env
23-
# Load the TOKENID from .env
19+
Load the TOKENID from .env
2420
if [ -f .env ]; then
2521
source .env
2622
if [ -z "$TOKENID" ]; then
@@ -46,5 +42,14 @@ echo "Claiming proof..."
4642
npx hardhat run scripts/claim-membership.ts --network opSepolia
4743

4844
# Check token existence
49-
echo "Checking token existence..."
50-
npx hardhat run scripts/check-token-existence.ts --network sepolia
45+
npx hardhat run scripts/check-token-existence.ts --network sepolia
46+
47+
sed -i.bak '/^TOKENID=/d' .env
48+
echo "TOKENID=0" >> .env
49+
source .env
50+
51+
echo "Using Token ID: $TOKENID"
52+
53+
npx hardhat run scripts/verify-metadata-proof.ts --network sepolia
54+
55+
npx hardhat run scripts/claim-metadata-update.ts --network opSepolia

scripts/claim-membership.ts

Lines changed: 14 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ async function main() {
1212
const NFT_ADDRESS = deploymentsNFT.address
1313
const network = hre.network.name
1414

15-
// Setup provider and signer for target chain
15+
// Setup provider and signer
1616
const provider = new ethers.JsonRpcProvider(getRpcUrl(network))
1717
const signer = new ethers.Wallet(process.env.SIGNER_PRIVATE_KEY, provider)
1818

@@ -21,20 +21,7 @@ async function main() {
2121

2222
const nft = NFT__factory.connect(NFT_ADDRESS, signer)
2323

24-
// Get current nonce state on target chain
25-
const OPERATION_TYPE = 0 // MINT operation
26-
const proofSlot = ethers.keccak256(
27-
ethers.solidityPacked(
28-
["uint8", "uint256"],
29-
[OPERATION_TYPE, 0] // mapping key and base slot
30-
)
31-
)
32-
const nonceData = await provider.getStorage(nft.target, proofSlot)
33-
const currentNonce = parseInt(nonceData, 16)
34-
console.log("\nCurrent nonce on target chain:", currentNonce)
35-
const nextNonce = currentNonce + 1
36-
37-
// Load proofs from file
24+
// Load proof from file
3825
const proofsPath = path.resolve(__dirname, "../proofs.json")
3926
if (!fs.existsSync(proofsPath)) {
4027
throw new Error(
@@ -43,24 +30,8 @@ async function main() {
4330
}
4431
const proofs = JSON.parse(fs.readFileSync(proofsPath, "utf8"))
4532

46-
// Check which tokens already exist on target chain
47-
const existingTokens = new Set()
48-
for (const proof of proofs) {
49-
try {
50-
const owner = await nft.ownerOf(proof.tokenId)
51-
existingTokens.add(proof.tokenId)
52-
console.log(
53-
`Token ${proof.tokenId} already exists, owned by ${owner}`
54-
)
55-
} catch (e) {
56-
// Token doesn't exist
57-
}
58-
}
59-
60-
// Claim tokens that don't exist yet
33+
// Try to claim each proof
6134
for (const proof of proofs) {
62-
if (existingTokens.has(proof.tokenId)) continue
63-
6435
console.log(`\nClaiming token ${proof.tokenId}...`)
6536
try {
6637
// Simulate first
@@ -89,39 +60,28 @@ async function main() {
8960
} catch (error: any) {
9061
console.error(`\nFailed to claim token ${proof.tokenId}:`)
9162
if (error.data) {
92-
const decodedError = nft.interface.parseError(error.data)
93-
console.error("Error reason:", decodedError?.args[0])
94-
} else {
95-
console.error(error)
63+
try {
64+
const decodedError = nft.interface.parseError(error.data)
65+
console.error("Error reason:", decodedError)
66+
} catch (e) {
67+
console.error(error)
68+
}
9669
}
9770
}
9871
}
9972
}
10073

74+
// Helper to get RPC URL
10175
function getRpcUrl(network: string): string {
10276
switch (network) {
10377
case "opSepolia":
104-
if (!process.env.OP_SEPOLIA_RPC_ENDPOINT_URL) {
105-
throw new Error("OP_SEPOLIA_RPC_ENDPOINT_URL not set in .env")
106-
}
107-
return process.env.OP_SEPOLIA_RPC_ENDPOINT_URL
78+
return process.env.OP_SEPOLIA_RPC_ENDPOINT_URL || ""
10879
case "baseSepolia":
109-
if (!process.env.BASE_SEPOLIA_RPC_ENDPOINT_URL) {
110-
throw new Error("BASE_SEPOLIA_RPC_ENDPOINT_URL not set in .env")
111-
}
112-
return process.env.BASE_SEPOLIA_RPC_ENDPOINT_URL
80+
return process.env.BASE_SEPOLIA_RPC_ENDPOINT_URL || ""
11381
case "arbitrumSepolia":
114-
if (!process.env.ARBITRUM_SEPOLIA_RPC_ENDPOINT_URL) {
115-
throw new Error(
116-
"ARBITRUM_SEPOLIA_RPC_ENDPOINT_URL not set in .env"
117-
)
118-
}
119-
return process.env.ARBITRUM_SEPOLIA_RPC_ENDPOINT_URL
82+
return process.env.ARBITRUM_SEPOLIA_RPC_ENDPOINT_URL || ""
12083
case "sepolia":
121-
if (!process.env.SEPOLIA_RPC_ENDPOINT_URL) {
122-
throw new Error("SEPOLIA_RPC_ENDPOINT_URL not set in .env")
123-
}
124-
return process.env.SEPOLIA_RPC_ENDPOINT_URL
84+
return process.env.SEPOLIA_RPC_ENDPOINT_URL || ""
12585
default:
12686
throw new Error(`Unsupported network: ${network}`)
12787
}

0 commit comments

Comments
 (0)