Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 81 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ OPS_NETWORK := $(subst ",,$(OPS_NETWORK))
OPS_CHAIN_ID := $(subst ",,$(OPS_CHAIN_ID))
OPS_KYC_TOKEN_SUFFIX := $(subst ",,$(OPS_KYC_TOKEN_SUFFIX))
OPS_DEPLOYMENT_METHOD := $(subst ",,$(OPS_DEPLOYMENT_METHOD))
OPS_SKIP_VERIFY := $(subst ",,$(OPS_SKIP_VERIFY))
OPS_MINT_TO := $(subst ",,$(OPS_MINT_TO))
OPS_MINT_TOKEN_ID := $(subst ",,$(OPS_MINT_TOKEN_ID))
OPS_CONTRACT_ADDRESS := $(subst ",,$(OPS_CONTRACT_ADDRESS))
OPS_NEW_OWNER := $(subst ",,$(OPS_NEW_OWNER))

CURRENT_DIR := $(shell pwd)

Expand All @@ -19,6 +24,8 @@ FILE_DEPLOY_POWER_POD := $(CURRENT_DIR)/deploy/deploy-power-pod.js
FILE_DEPLOY_WHITELIST_REGISTRY := $(CURRENT_DIR)/deploy/deploy-whitelist-registry.js
FILE_DEPLOY_CROSSCHAIN_WHITELIST := $(CURRENT_DIR)/deploy/deploy-crosschain-whitelist.js
FILE_DEPLOY_RESOLVER_METADATA := $(CURRENT_DIR)/deploy/deploy-resolver-metadata.js
FILE_MINT_KYC := $(CURRENT_DIR)/scripts/mint-kyc.js
FILE_TRANSFER_OWNERSHIP := $(CURRENT_DIR)/scripts/transfer-ownership.js
FILE_CONSTANTS_JSON := $(CURRENT_DIR)/config/constants.json

ALL_DEPLOY_FILES := $(FILE_DEPLOY_KYC_NFT) $(FILE_DEPLOY_SETTLEMENT) $(FILE_DEPLOY_POWER_POD) $(FILE_DEPLOY_WHITELIST_REGISTRY) $(FILE_DEPLOY_CROSSCHAIN_WHITELIST) $(FILE_DEPLOY_RESOLVER_METADATA)
Expand All @@ -27,22 +34,22 @@ IS_ZKSYNC := $(findstring zksync,$(OPS_NETWORK))

# Deployment targets
deploy-access-token:
@$(MAKE) OPS_CURRENT_DEP_FILE=$(FILE_DEPLOY_KYC_NFT) validate-access-token deploy-skip-all deploy-noskip deploy-impl deploy-skip
@$(MAKE) OPS_SKIP_VERIFY=$(OPS_SKIP_VERIFY) OPS_CURRENT_DEP_FILE=$(FILE_DEPLOY_KYC_NFT) validate-access-token deploy-skip-all deploy-noskip deploy-impl deploy-skip

deploy-settlement:
@$(MAKE) OPS_CURRENT_DEP_FILE=$(FILE_DEPLOY_SETTLEMENT) validate-settlement deploy-skip-all deploy-noskip deploy-impl deploy-skip
@$(MAKE) OPS_SKIP_VERIFY=$(OPS_SKIP_VERIFY) OPS_CURRENT_DEP_FILE=$(FILE_DEPLOY_SETTLEMENT) validate-settlement deploy-skip-all deploy-noskip deploy-impl deploy-skip

deploy-power-pod:
@$(MAKE) OPS_CURRENT_DEP_FILE=$(FILE_DEPLOY_POWER_POD) validate-power-pod deploy-skip-all deploy-noskip deploy-impl deploy-skip
@$(MAKE) OPS_SKIP_VERIFY=$(OPS_SKIP_VERIFY) OPS_CURRENT_DEP_FILE=$(FILE_DEPLOY_POWER_POD) validate-power-pod deploy-skip-all deploy-noskip deploy-impl deploy-skip

deploy-whitelist-registry:
@$(MAKE) OPS_CURRENT_DEP_FILE=$(FILE_DEPLOY_WHITELIST_REGISTRY) validate-whitelist-registry deploy-skip-all deploy-noskip deploy-impl deploy-skip
@$(MAKE) OPS_SKIP_VERIFY=$(OPS_SKIP_VERIFY) OPS_CURRENT_DEP_FILE=$(FILE_DEPLOY_WHITELIST_REGISTRY) validate-whitelist-registry deploy-skip-all deploy-noskip deploy-impl deploy-skip

deploy-crosschain-whitelist:
@$(MAKE) OPS_CURRENT_DEP_FILE=$(FILE_DEPLOY_CROSSCHAIN_WHITELIST) validate-crosschain-whitelist deploy-skip-all deploy-noskip deploy-impl deploy-skip
@$(MAKE) OPS_SKIP_VERIFY=$(OPS_SKIP_VERIFY) OPS_CURRENT_DEP_FILE=$(FILE_DEPLOY_CROSSCHAIN_WHITELIST) validate-crosschain-whitelist deploy-skip-all deploy-noskip deploy-impl deploy-skip

deploy-resolver-metadata:
@$(MAKE) OPS_CURRENT_DEP_FILE=$(FILE_DEPLOY_RESOLVER_METADATA) validate-resolver-metadata deploy-skip-all deploy-noskip deploy-impl deploy-skip
@$(MAKE) OPS_SKIP_VERIFY=$(OPS_SKIP_VERIFY) OPS_CURRENT_DEP_FILE=$(FILE_DEPLOY_RESOLVER_METADATA) validate-resolver-metadata deploy-skip-all deploy-noskip deploy-impl deploy-skip

deploy-impl:
@{ \
Expand Down Expand Up @@ -114,6 +121,47 @@ validate-resolver-metadata:
$(MAKE) process-power-pod || exit 1; \
}

validate-mint-kyc:
@{ \
$(MAKE) validate-base || exit 1; \
$(MAKE) ID=OPS_ACCESS_TOKEN_ADDRESS validate || exit 1; \
$(MAKE) ID=OPS_MINT_TO validate || exit 1; \
$(MAKE) ID=OPS_MINT_TOKEN_ID validate || exit 1; \
NETWORK_KEY=$$(echo $(OPS_NETWORK) | tr 'a-z-' 'A-Z_')_PRIVATE_KEY; \
eval "VALUE=\$${$$NETWORK_KEY}"; \
if [ -z "$$VALUE" ]; then echo "$$NETWORK_KEY is not set!"; exit 1; fi; \
$(MAKE) process-access-token-address process-mint-to process-mint-token-id || exit 1; \
}

# Transfer ownership targets
validate-transfer-ownership:
@{ \
$(MAKE) validate-base || exit 1; \
$(MAKE) ID=OPS_CONTRACT_ADDRESS validate || exit 1; \
$(MAKE) ID=OPS_NEW_OWNER validate || exit 1; \
NETWORK_KEY=$$(echo $(OPS_NETWORK) | tr 'a-z-' 'A-Z_')_PRIVATE_KEY; \
eval "VALUE=\$${$$NETWORK_KEY}"; \
if [ -z "$$VALUE" ]; then echo "$$NETWORK_KEY is not set!"; exit 1; fi; \
$(MAKE) process-contract-address process-new-owner || exit 1; \
}

transfer-ownership:
@$(MAKE) validate-transfer-ownership transfer-ownership-impl

transfer-ownership-impl:
@{ \
yarn hardhat run $(FILE_TRANSFER_OWNERSHIP) --network $(OPS_NETWORK) || exit 1; \
}

# Mint targets
mint-kyc:
@$(MAKE) validate-mint-kyc mint-kyc-impl

mint-kyc-impl:
@{ \
yarn hardhat run $(FILE_MINT_KYC) --network $(OPS_NETWORK) || exit 1; \
}

# Process constant functions
process-access-token-owner:
@$(MAKE) OPS_GEN_KEY='accessTokenOwner' OPS_GEN_VAL='$(OPS_KYC_TOKEN_OWNER_ADDRESS)' upsert-constant
Expand Down Expand Up @@ -147,7 +195,7 @@ process-settlement-owner:

process-settlement-salt:
@{ \
if [ -n "$$OPS_FEE_TAKER_SALT" ]; then \
if [ -n "$$OPS_SETTLEMENT_SALT" ]; then \
$(MAKE) OPS_GEN_KEY='settlementSalt' OPS_GEN_VAL='$(OPS_SETTLEMENT_SALT)' upsert-constant; \
fi \
}
Expand All @@ -159,13 +207,29 @@ process-create3-deployer:
fi \
}

process-mint-to:
@$(MAKE) OPS_GEN_KEY='mintTo' OPS_GEN_VAL='$(OPS_MINT_TO)' upsert-constant

process-mint-token-id:
@$(MAKE) OPS_GEN_KEY='mintTokenId' OPS_GEN_VAL='$(OPS_MINT_TOKEN_ID)' upsert-constant

process-contract-address:
@$(MAKE) OPS_GEN_KEY='contractAddress' OPS_GEN_VAL='$(OPS_CONTRACT_ADDRESS)' upsert-constant

process-new-owner:
@$(MAKE) OPS_GEN_KEY='newOwner' OPS_GEN_VAL='$(OPS_NEW_OWNER)' upsert-constant

upsert-constant:
@{ \
$(MAKE) ID=OPS_GEN_VAL validate || exit 1; \
$(MAKE) ID=OPS_GEN_KEY validate || exit 1; \
$(MAKE) ID=OPS_CHAIN_ID validate || exit 1; \
tmpfile=$$(mktemp); \
jq '.$(OPS_GEN_KEY)."$(OPS_CHAIN_ID)" = $(OPS_GEN_VAL)' $(FILE_CONSTANTS_JSON) > $$tmpfile && mv $$tmpfile $(FILE_CONSTANTS_JSON); \
if echo '$(OPS_GEN_VAL)' | jq type >/dev/null 2>&1; then \
jq --argjson val '$(OPS_GEN_VAL)' '.$(OPS_GEN_KEY)."$(OPS_CHAIN_ID)" = $$val' $(FILE_CONSTANTS_JSON) > $$tmpfile; \
else \
jq --arg val '$(OPS_GEN_VAL)' '.$(OPS_GEN_KEY)."$(OPS_CHAIN_ID)" = $$val' $(FILE_CONSTANTS_JSON) > $$tmpfile; \
fi && mv $$tmpfile $(FILE_CONSTANTS_JSON); \
echo "Updated $(OPS_GEN_KEY)[$(OPS_CHAIN_ID)] = $(OPS_GEN_VAL)"; \
}

Expand Down Expand Up @@ -292,7 +356,15 @@ help:
@echo " install-dependencies Install yarn dependencies"
@echo " clean Remove deployment files"
@echo " get PARAMETER=... Get deployed contract address"
@echo " mint-kyc Mint a single KycNFT token (requires OPS_ACCESS_TOKEN_ADDRESS, OPS_MINT_TO, OPS_MINT_TOKEN_ID)"
@echo " validate-mint-kyc Validate KycNFT mint variables"
@echo " process-mint-to Update mintTo constant"
@echo " process-mint-token-id Update mintTokenId constant"
@echo " transfer-ownership Transfer ownership of any Ownable contract (requires OPS_CONTRACT_ADDRESS, OPS_NEW_OWNER)"
@echo " validate-transfer-ownership Validate transfer ownership variables"
@echo " process-contract-address Update contractAddress constant"
@echo " process-new-owner Update newOwner constant"
@echo " help Show this help message"


.PHONY: help deploy-access-token deploy-settlement deploy-power-pod deploy-whitelist-registry deploy-crosschain-whitelist deploy-resolver-metadata deploy-impl validate-base validate-access-token validate-settlement validate-power-pod validate-whitelist-registry validate-crosschain-whitelist validate-resolver-metadata process-access-token-owner process-access-token-salt process-access-token-address process-settlement-owner process-settlement-salt process-router-v6 process-weth process-st1inch process-power-pod process-dao process-whitelist-registry process-create3-deployer upsert-constant deploy-skip-all deploy-skip deploy-noskip install install-utils install-dependencies clean get get-outputs validate
.PHONY: help deploy-access-token deploy-settlement deploy-power-pod deploy-whitelist-registry deploy-crosschain-whitelist deploy-resolver-metadata deploy-impl validate-base validate-access-token validate-settlement validate-power-pod validate-whitelist-registry validate-crosschain-whitelist validate-resolver-metadata validate-mint-kyc validate-transfer-ownership process-access-token-owner process-access-token-salt process-access-token-address process-settlement-owner process-settlement-salt process-router-v6 process-weth process-st1inch process-power-pod process-dao process-whitelist-registry process-create3-deployer process-mint-to process-mint-token-id process-contract-address process-new-owner upsert-constant deploy-skip-all deploy-skip deploy-noskip install install-utils install-dependencies clean get get-outputs validate mint-kyc mint-kyc-impl transfer-ownership transfer-ownership-impl
4 changes: 4 additions & 0 deletions config/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,8 @@ module.exports = {
DAO_ADDRESS: constants.daoAddress || {},
POWER_POD_ADDRESS: constants.powerPodAddress || {},
WHITELIST_REGISTRY_ADDRESS: constants.whitelistRegistryAddress || {},
MINT_TO: constants.mintTo || {},
MINT_TOKEN_ID: constants.mintTokenId || {},
CONTRACT_ADDRESS: constants.contractAddress || {},
NEW_OWNER: constants.newOwner || {},
};
1 change: 1 addition & 0 deletions deploy/deploy-crosschain-whitelist.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ module.exports = async ({ getNamedAccounts, deployments }) => {
deployments,
deployer,
deploymentName: 'CrosschainWhitelistRegistry',
skipVerify: process.env.OPS_SKIP_VERIFY === 'true',
});

const tx = await whitelist.transferOwnership(constants.DAO_ADDRESS[chainId]);
Expand Down
15 changes: 11 additions & 4 deletions deploy/deploy-kyc-nft.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const hre = require('hardhat');
const { ethers } = hre;
const { deployAndGetContractWithCreate3, deployAndGetContract } = require('@1inch/solidity-utils');
const constants = require('../config/constants');

Expand All @@ -11,7 +12,7 @@ module.exports = async ({ getNamedAccounts, deployments, config }) => {
console.log(`running ${networkName} deploy script: kyc-nft`);
const chainId = await hre.getChainId();
console.log('network id ', chainId);

if (chainId !== hre.config.networks[networkName].chainId?.toString()) {
console.log(`network chain id: ${hre.config.networks[networkName].chainId}, your chain id ${chainId}`);
console.log('skipping wrong chain id deployment');
Expand All @@ -36,19 +37,25 @@ module.exports = async ({ getNamedAccounts, deployments, config }) => {
constructorArgs,
deployments,
deployer,
skipVerify: process.env.OPS_SKIP_VERIFY === 'true',
});
} else {
let salt = constants.ACCESS_TOKEN_SALT[chainId];
salt = salt?.startsWith('0x') ? salt : ethers.keccak256(ethers.toUtf8Bytes(salt));

console.log(`Using salt: ${salt}`);

// Deploy with create3
await deployAndGetContractWithCreate3({
contractName: 'KycNFT',
deploymentName,
constructorArgs,
create3Deployer: constants.CREATE3_DEPLOYERS[chainId],
salt: constants.ACCESS_TOKEN_SALT[chainId],
salt,
deployments,
skipVerify: networkName === 'klaytn',
skipVerify: networkName === 'klaytn' || process.env.OPS_SKIP_VERIFY === 'true',
});
}
};

module.exports.skip = async () => true;
module.exports.skip = async () => false;
1 change: 1 addition & 0 deletions deploy/deploy-power-pod.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ module.exports = async ({ getNamedAccounts, deployments }) => {
constructorArgs: ['Delegated st1INCH', 'dst1INCH', constants.ST1INCH_ADDR[chainId]],
deployments,
deployer,
skipVerify: process.env.OPS_SKIP_VERIFY === 'true',
});
};

Expand Down
1 change: 1 addition & 0 deletions deploy/deploy-resolver-metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ module.exports = async ({ getNamedAccounts, deployments }) => {
constructorArgs: [constants.POWER_POD_ADDRESS[chainId]],
deployments,
deployer,
skipVerify: process.env.OPS_SKIP_VERIFY === 'true',
});
};

Expand Down
2 changes: 2 additions & 0 deletions deploy/deploy-settlement.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ module.exports = async ({ getNamedAccounts, deployments, config }) => {
create3Deployer: constants.CREATE3_DEPLOYERS[chainId],
salt,
deployments,
skipVerify: process.env.OPS_SKIP_VERIFY === 'true',
});
} else {
// Deploy on zkSync-like networks without create3
Expand All @@ -57,6 +58,7 @@ module.exports = async ({ getNamedAccounts, deployments, config }) => {
constructorArgs,
deployments,
deployer,
skipVerify: process.env.OPS_SKIP_VERIFY === 'true',
});
}
};
Expand Down
1 change: 1 addition & 0 deletions deploy/deploy-whitelist-registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ module.exports = async ({ getNamedAccounts, deployments }) => {
constructorArgs: [constants.POWER_POD_ADDRESS[chainId], '1000'], // 1000 = 10% threshold
deployments,
deployer,
skipVerify: process.env.OPS_SKIP_VERIFY === 'true',
});

const tx = await whitelist.transferOwnership(constants.DAO_ADDRESS[chainId]);
Expand Down
5 changes: 4 additions & 1 deletion hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ if (getNetwork().indexOf('zksync') !== -1) {
require('@nomicfoundation/hardhat-verify');
}

const { networks, etherscan } = (new Networks()).registerAll();
const n = new Networks();
const { networks } = n.registerAll();
const etherscan = n.getEtherscanConfig(getNetwork());

networks.hardhat = Object.assign(networks.hardhat, {
initialBaseFeePerGas: 1,
gasPrice: 1,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"dependencies": {
"@1inch/delegating": "1.1.0",
"@1inch/limit-order-protocol-contract": "4.3.2",
"@1inch/solidity-utils": "6.8.0",
"@1inch/solidity-utils": "6.9.4",
"@1inch/st1inch": "2.2.0",
"@openzeppelin/contracts": "5.1.0"
},
Expand Down
48 changes: 48 additions & 0 deletions scripts/mint-kyc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
const hre = require('hardhat');
const { ethers } = hre;
const constants = require('../config/constants');

async function main () {

Check failure on line 5 in scripts/mint-kyc.js

View workflow job for this annotation

GitHub Actions / lint

Unexpected space before function parentheses
const networkName = hre.network.name;
const chainId = (await ethers.provider.getNetwork()).chainId.toString();
console.log(`running ${networkName} (chainId: ${chainId}) mint-kyc script`);

const kycAddress = constants.ACCESS_TOKEN_ADDRESS[chainId];
if (!kycAddress) {
throw new Error(`KycNFT address not found in constants for chainId ${chainId}`);
}

const mintTo = constants.MINT_TO[chainId];
if (!mintTo) {
throw new Error(`mintTo not found in constants for chainId ${chainId}`);
}

const mintTokenId = constants.MINT_TOKEN_ID[chainId];
if (mintTokenId === undefined || mintTokenId === null || mintTokenId === '') {
throw new Error(`mintTokenId not found in constants for chainId ${chainId}`);
}

const privateKeyEnvVar = networkName.toUpperCase().replace(/-/g, '_') + '_PRIVATE_KEY';
const privateKey = process.env[privateKeyEnvVar];
if (!privateKey) {
throw new Error(`${privateKeyEnvVar} is not set`);
}

const signer = new ethers.Wallet(privateKey, ethers.provider);

console.log(`KycNFT address: ${kycAddress}`);
console.log(`Minting to: ${mintTo}`);
console.log(`Token ID: ${mintTokenId}`);
console.log(`Owner signer: ${signer.address}`);

const kyc = await ethers.getContractAt('KycNFT', kycAddress, signer);
const tx = await kyc.mint(mintTo, mintTokenId);
console.log(`Transaction sent: ${tx.hash}`);
await tx.wait();
console.log(`Mint confirmed: tokenId ${mintTokenId} → ${mintTo}`);
}

main().catch((err) => {
console.error(err);
process.exitCode = 1;
});
73 changes: 73 additions & 0 deletions scripts/transfer-ownership.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
const hre = require('hardhat');
const { ethers } = hre;
const constants = require('../config/constants');

const OWNABLE_ABI = [
'function owner() view returns (address)',
'function transferOwnership(address newOwner)',
];

// OZ v5 Ownable custom error selector: OwnableUnauthorizedAccount(address)
const OWNABLE_UNAUTHORIZED_SELECTOR = ethers.id('OwnableUnauthorizedAccount(address)').slice(0, 10);

async function main () {

Check failure on line 13 in scripts/transfer-ownership.js

View workflow job for this annotation

GitHub Actions / lint

Unexpected space before function parentheses
const networkName = hre.network.name;
const chainId = (await ethers.provider.getNetwork()).chainId.toString();
console.log(`running ${networkName} (chainId: ${chainId}) transfer-ownership script`);

const contractAddress = constants.CONTRACT_ADDRESS[chainId];
if (!contractAddress) {
throw new Error(`contractAddress not found in constants for chainId ${chainId}`);
}

const newOwner = constants.NEW_OWNER[chainId];
if (!newOwner) {
throw new Error(`newOwner not found in constants for chainId ${chainId}`);
}

const privateKeyEnvVar = networkName.toUpperCase().replace(/-/g, '_') + '_PRIVATE_KEY';
const privateKey = process.env[privateKeyEnvVar];
if (!privateKey) {
throw new Error(`${privateKeyEnvVar} is not set`);
}

const signer = new ethers.Wallet(privateKey, ethers.provider);

console.log(`Contract address: ${contractAddress}`);
console.log(`New owner: ${newOwner}`);
console.log(`Caller (signer): ${signer.address}`);

const contract = new ethers.Contract(contractAddress, OWNABLE_ABI, signer);

// Verify the target is a valid Ownable contract and check current owner before sending a tx.
let currentOwner;
try {
currentOwner = await contract.owner();
} catch {
throw new Error(`[Error type 2] Target is not a valid Ownable contract at ${contractAddress}`);
}

console.log(`Current owner: ${currentOwner}`);

if (currentOwner.toLowerCase() !== signer.address.toLowerCase()) {
throw new Error(`[Error type 1] Caller ${signer.address} is not the owner (owner is ${currentOwner})`);
}

try {
const tx = await contract.transferOwnership(newOwner);
console.log(`Transaction sent: ${tx.hash}`);
await tx.wait();
console.log(`Ownership transferred: ${currentOwner} → ${newOwner}`);
} catch (err) {
const data = err?.data ?? err?.revert?.data ?? err?.info?.error?.data;
if (typeof data === 'string' && data.startsWith(OWNABLE_UNAUTHORIZED_SELECTOR)) {
throw new Error(`[Error type 1] Caller ${signer.address} is not the owner`);
}
throw new Error(`[Error type 2] transferOwnership failed: ${err.message}`);
}
}

main().catch((err) => {
console.error(err.message);
process.exitCode = 1;
});
Loading
Loading