diff --git a/scripts/data.ts b/scripts/data.ts deleted file mode 100644 index 35b25870..00000000 --- a/scripts/data.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const domains = []; - -export const owners = []; - -export const voltaPrivateKey = ''; - -export const ewcPrivateKey = ''; diff --git a/scripts/transfer_domains.ts b/scripts/transfer_domains.ts index 603954b2..537594ef 100644 --- a/scripts/transfer_domains.ts +++ b/scripts/transfer_domains.ts @@ -1,9 +1,10 @@ import { EWC_CHAIN_ID } from '@energyweb/credential-governance'; import { Wallet } from 'ethers'; -import { transferDomain } from '../src/utils/domains'; -import { domains, ewcPrivateKey } from './data'; +import { transferDomain } from '../src/utils/transfer-domains'; -export const newOwner = '0x3451aaEDD3f25204D483aADCF060e344155DEB02'; +export const domains = ['energyweb.iam.ewc']; +export const newOwner = '0x205801aAbf81B65B3b7C52041991994d6c2e9F9d'; +export const ewcPrivateKey = Wallet.createRandom().privateKey; (async function () { try { @@ -16,8 +17,8 @@ export const newOwner = '0x3451aaEDD3f25204D483aADCF060e344155DEB02'; dryRun: true, }); } - console.log('Domains are transferred'); + process.stdout.write('Domains are transferred\n'); } catch (e) { - console.error('Error transferring domains:', e); + process.stderr.write(`Error transferring domains: ${e}\n`); } })(); diff --git a/src/utils/domains.ts b/src/utils/domains.ts deleted file mode 100644 index d27ff840..00000000 --- a/src/utils/domains.ts +++ /dev/null @@ -1,333 +0,0 @@ -import { - IRoleDefinitionV2, - emptyAddress, - NamespaceType, - castToV2, - ChainId, - chainConfigs, -} from '..'; -import { - DomainHierarchy, - DomainReader, - DomainTransactionFactoryV2, - ResolverContractType, -} from './domains-build.js'; -import { BigNumber, providers } from 'ethers'; -import { Methods } from '@ew-did-registry/did'; -import { Signer } from 'ethers'; -import { isValidDID } from './did'; -import { namehash, labelhash } from './ens-hash'; -import { ENSRegistry__factory } from '../../ethers/factories/ENSRegistry__factory'; -import { DomainNotifier__factory } from '../../ethers/factories/DomainNotifier__factory'; -import { getLogger } from '../config/logger.config'; - -const { JsonRpcProvider } = providers; - -/** - * @description - Checks that role issuers of all roles under `rootDomain` contains method-specific-id and adds it if missing - * `signer` must own `rootDomain` on `targetChain` - */ -export const updateLegacyDomains = async ({ - rootDomain, - signer, - chainId, - dryRun = true, -}: { - rootDomain: string; - signer: Signer; - chainId: ChainId; - dryRun?: boolean; -}) => { - const logger = getLogger(); - const { - transactionFactory, - chainName, - provider, - domainHierarchy, - domainNotifier, - domainReader, - ensRegistry, - ensResolverV2Address, - } = await initDomains(signer, chainId); - - const updated: Record[] = []; - const update = async (domain: string) => { - const domainHash = namehash(domain); - - let def; - try { - def = await domainReader.read({ node: domainHash }); - } catch (e) { - // 'apps' and 'roles' - logger.warn(`Unable to read ${domain}: ${(e).message}`); - if ( - !( - domain.startsWith(NamespaceType.Application) || - domain.startsWith(NamespaceType.Role) || - domain.startsWith(NamespaceType.Organization) - ) - ) { - logger.info(`deleting malformed ${domain}...`); - await ( - await ensRegistry.setRecord( - domainHash, - emptyAddress, - emptyAddress, - BigNumber.from(0) - ) - ).wait(); - return; - } - } - - // const resolver = await ensRegistry.resolver(domainHash); - if (!dryRun /*&& resolver !== ensResolverV2Address*/) { - await ( - await ensRegistry.setResolver(domainHash, ensResolverV2Address) - ).wait(); - // migrate domain definition - let updateDomainTx; - if (def) { - if (DomainReader.isRoleDefinition(def)) { - // in some legacy roles issuer.issuerType = {} - if (def.issuer?.did?.length) { - def.issuer.issuerType = 'DID'; - } else if (def.issuer.roleName) { - def.issuer.issuerType = 'ROLE'; - } else { - throw new Error(`Unable to set issuer type of ${domain}`); - } - const { - issuer: { did }, - version, - } = def; - const updatedDef: IRoleDefinitionV2 = castToV2({ - ...def, - issuer: { - ...def.issuer, - did: did?.map((d) => - isValidDID(d) - ? d - : `did:${Methods.Erc1056}:${chainName}:${d - .split(':') - .slice(-1)}` - ), - }, - version: parseInt(version.toString(), 10), - }); - updated.push({ domain, legacyDef: def, updatedDef }); - updateDomainTx = transactionFactory.newRole({ - domain, - roleDefinition: updatedDef, - }); - } else { - updateDomainTx = transactionFactory.newDomain({ - domain, - domainDefinition: def, - }); - } - await ( - await signer.connect(provider).sendTransaction(updateDomainTx) - ).wait(); - } - } - if (!dryRun) { - await ( - await signer - .connect(provider) - .sendTransaction(transactionFactory.setDomainNameTx({ domain })) - ).wait(); - logger.info(`set name ${await domainReader.readName(domainHash)}`); - } - - const subnodes = await domainHierarchy.getSubdomainsUsingResolver({ - domain, - mode: 'FIRSTLEVEL', - }); - if (def) { - if ( - DomainReader.isOrgDefinition(def) || - DomainReader.isAppDefinition(def) - ) { - subnodes.push(`roles.${domain}`); - } - if (DomainReader.isOrgDefinition(def)) { - subnodes.push(`apps.${domain}`); - } - } - logger.info(`subnodes of ${domain} are: ${subnodes}`); - for await (const nodeName of subnodes) { - console.group(); - const label = nodeName.split('.')[0]; - const labelHash = labelhash(label); - const nodeHash = namehash(nodeName); - const owner = await ensRegistry.owner(nodeHash); - if (!dryRun) { - await ( - await ensRegistry.setSubnodeOwner( - domainHash, - labelHash, - await signer.getAddress() - ) - ).wait(); - } - await update(nodeName); - if (!dryRun) { - await ( - await ensRegistry.setSubnodeOwner(domainHash, labelHash, owner) - ).wait(); - } - console.groupEnd(); - } - await (await domainNotifier.domainUpdated(domainHash)).wait(); - }; - - await update(rootDomain); - return updated; -}; - -export const transferDomain = async ({ - rootDomain, - signer, - newOwner, - chainId, - dryRun = true, -}: { - rootDomain: string; - signer: Signer; - newOwner: string; - chainId: ChainId; - dryRun?: boolean; -}) => { - const logger = getLogger(); - const { domainHierarchy, domainReader, ensRegistry } = await initDomains( - signer, - chainId - ); - const transferred: Record[] = []; - const transfer = async (domain: string) => { - logger.info(`> transferring ${domain}`); - const domainHash = namehash(domain); - - let def; - try { - def = await domainReader.read({ node: domainHash }); - } catch (e) { - // 'apps' and 'roles' - logger.warn(`Unable to read ${domain}: ${(e).message}`); - } - - const subnodes = await domainHierarchy.getSubdomainsUsingResolver({ - domain, - mode: 'FIRSTLEVEL', - }); - if (def) { - if ( - DomainReader.isOrgDefinition(def) || - DomainReader.isAppDefinition(def) - ) { - subnodes.push(`roles.${domain}`); - } - if (DomainReader.isOrgDefinition(def)) { - subnodes.push(`apps.${domain}`); - } - if (domain === 'engie.auth.ewc') { - subnodes.push(`orgs.${domain}`); - } - } - logger.info(`subnodes of ${domain} are ${subnodes}`); - for await (const nodeName of subnodes) { - console.group(); - const label = nodeName.split('.')[0]; - const labelHash = labelhash(label); - if (!dryRun) { - await ( - await ensRegistry.setSubnodeOwner( - domainHash, - labelHash, - await signer.getAddress() - ) - ).wait(); - } - await transfer(nodeName); - console.groupEnd(); - } - if (!dryRun) { - await (await ensRegistry.setOwner(domainHash, newOwner)).wait(); - } - return transferred; - }; - - await transfer(rootDomain); -}; - -export const initDomains = async (signer: Signer, chainId: ChainId) => { - const { - rpcUrl, - ensRegistryAddress, - ensResolverV2Address, - ensResolverAddress, - ensPublicResolverAddress, - domainNotifierAddress, - chainName, - } = chainConfigs()[chainId]; - const provider = new JsonRpcProvider(rpcUrl); - - const ensRegistry = new ENSRegistry__factory( - ENSRegistry__factory.createInterface(), - ENSRegistry__factory.bytecode - ) - .attach(ensRegistryAddress) - .connect(signer.connect(provider)); - const domainReader = new DomainReader({ - ensRegistryAddress, - provider, - }); - ensResolverV2Address && - domainReader.addKnownResolver({ - chainId, - address: ensResolverV2Address, - type: ResolverContractType.RoleDefinitionResolver_v2, - }); - ensResolverAddress && - domainReader.addKnownResolver({ - chainId, - address: ensResolverAddress, - type: ResolverContractType.RoleDefinitionResolver_v1, - }); - ensPublicResolverAddress && - domainReader.addKnownResolver({ - chainId, - address: ensPublicResolverAddress, - type: ResolverContractType.PublicResolver, - }); - - const transactionFactory = new DomainTransactionFactoryV2({ - domainResolverAddress: ensResolverV2Address, - }); - const domainHierarchy = new DomainHierarchy({ - domainReader: domainReader, - ensRegistryAddress: ensRegistryAddress, - provider, - domainNotifierAddress, - publicResolverAddress: ensPublicResolverAddress, - }); - const domainNotifier = new DomainNotifier__factory( - DomainNotifier__factory.createInterface(), - DomainNotifier__factory.bytecode - ) - .attach(domainNotifierAddress) - .connect(signer.connect(provider)); - return { - provider, - ensRegistry, - domainReader, - transactionFactory, - domainHierarchy, - domainNotifier, - ensResolverV2Address, - ensResolverAddress, - ensPublicResolverAddress, - chainName, - }; -}; diff --git a/src/utils/init-domains.ts b/src/utils/init-domains.ts new file mode 100644 index 00000000..7bca63cd --- /dev/null +++ b/src/utils/init-domains.ts @@ -0,0 +1,83 @@ +import { + DomainHierarchy, + DomainReader, + DomainTransactionFactoryV2, + ResolverContractType, +} from '@energyweb/credential-governance'; +import { providers, Wallet } from 'ethers'; +import { DomainNotifier__factory } from '../../ethers/factories/DomainNotifier__factory'; +import { ENSRegistry__factory } from '../../ethers/factories/ENSRegistry__factory'; +import { chainConfigs, ChainId } from '../config'; + +const { JsonRpcProvider } = providers; + +export const initDomains = async (signer: Wallet, chainId: ChainId) => { + const { + rpcUrl, + ensRegistryAddress, + ensResolverV2Address, + ensResolverAddress, + ensPublicResolverAddress, + domainNotifierAddress, + chainName, + } = chainConfigs()[chainId]; + const provider = new JsonRpcProvider(rpcUrl); + + const ensRegistry = new ENSRegistry__factory( + ENSRegistry__factory.createInterface(), + ENSRegistry__factory.bytecode + ) + .attach(ensRegistryAddress) + .connect(signer.connect(provider)); + const domainReader = new DomainReader({ + ensRegistryAddress, + provider, + }); + ensResolverV2Address && + domainReader.addKnownResolver({ + chainId, + address: ensResolverV2Address, + type: ResolverContractType.RoleDefinitionResolver_v2, + }); + ensResolverAddress && + domainReader.addKnownResolver({ + chainId, + address: ensResolverAddress, + type: ResolverContractType.RoleDefinitionResolver_v1, + }); + ensPublicResolverAddress && + domainReader.addKnownResolver({ + chainId, + address: ensPublicResolverAddress, + type: ResolverContractType.PublicResolver, + }); + + const transactionFactory = new DomainTransactionFactoryV2({ + domainResolverAddress: ensResolverV2Address, + }); + const domainHierarchy = new DomainHierarchy({ + domainReader: domainReader, + ensRegistryAddress: ensRegistryAddress, + provider, + domainNotifierAddress, + publicResolverAddress: ensPublicResolverAddress, + }); + const domainNotifier = new DomainNotifier__factory( + DomainNotifier__factory.createInterface(), + DomainNotifier__factory.bytecode + ) + .attach(domainNotifierAddress) + .connect(signer.connect(provider)); + return { + provider, + ensRegistry, + domainReader, + transactionFactory, + domainHierarchy, + domainNotifier, + ensResolverV2Address, + ensResolverAddress, + ensPublicResolverAddress, + chainName, + }; +}; diff --git a/src/utils/transfer-domains.ts b/src/utils/transfer-domains.ts new file mode 100644 index 00000000..3a389554 --- /dev/null +++ b/src/utils/transfer-domains.ts @@ -0,0 +1,92 @@ +import { ChainId } from '..'; +// import { +// DomainHierarchy, +// DomainReader, +// DomainTransactionFactoryV2, +// ResolverContractType, +// } from './domains-build.js'; +import { Wallet } from 'ethers'; +import { namehash, labelhash } from './ens-hash'; +import { getLogger } from '../config/logger.config'; +import { DomainReader } from '@energyweb/credential-governance'; +import { initDomains } from './init-domains'; + +/** + * @description - Checks that role issuers of all roles under `rootDomain` contains method-specific-id and adds it if missing + * `signer` must own `rootDomain` on `targetChain` + */ + +export const transferDomain = async ({ + rootDomain, + signer, + newOwner, + chainId, + dryRun = true, +}: { + rootDomain: string; + signer: Wallet; + newOwner: string; + chainId: ChainId; + dryRun?: boolean; +}) => { + const logger = getLogger(); + const { domainHierarchy, domainReader, ensRegistry } = await initDomains( + signer, + chainId + ); + const transferred: Record[] = []; + const transfer = async (domain: string) => { + logger.info(`Transferring ${domain}`); + const domainHash = namehash(domain); + + let def; + try { + def = await domainReader.read({ node: domainHash }); + } catch (e) { + // 'apps' and 'roles' + logger.warn(`Unable to read ${domain}: ${(e).message}`); + } + + const subnodes = await domainHierarchy.getSubdomainsUsingResolver({ + domain, + mode: 'FIRSTLEVEL', + }); + if (def) { + if ( + DomainReader.isOrgDefinition(def) || + DomainReader.isAppDefinition(def) + ) { + subnodes.push(`roles.${domain}`); + } + if (DomainReader.isOrgDefinition(def)) { + subnodes.push(`apps.${domain}`); + } + if (domain === 'engie.auth.ewc') { + subnodes.push(`orgs.${domain}`); + } + } + logger.info(`Subnodes of ${domain} are ${subnodes}`); + for await (const nodeName of subnodes) { + console.group(); + const label = nodeName.split('.')[0]; + const labelHash = labelhash(label); + if (!dryRun) { + await ( + await ensRegistry.setSubnodeOwner( + domainHash, + labelHash, + await signer.getAddress() + ) + ).wait(); + } + await transfer(nodeName); + console.groupEnd(); + } + if (!dryRun) { + await (await ensRegistry.setOwner(domainHash, newOwner)).wait(); + } + return transferred; + }; + + await transfer(rootDomain); +}; diff --git a/src/utils/update-domains.ts b/src/utils/update-domains.ts new file mode 100644 index 00000000..cbbe0718 --- /dev/null +++ b/src/utils/update-domains.ts @@ -0,0 +1,190 @@ +import { + DomainReader, + IRoleDefinitionV2, +} from '@energyweb/credential-governance'; +import { Methods } from '@ew-did-registry/did'; +import { Wallet, constants, BigNumber } from 'ethers'; +import { castToV2 } from '../modules/domains'; +import { ChainId, getLogger } from '../config'; +import { NamespaceType } from '../modules/domains'; +import { isValidDID } from './did'; +import { labelhash, namehash } from './ens-hash'; +import { initDomains } from './init-domains'; + +const { AddressZero } = constants; + +export const updateLegacyDomains = async ({ + rootDomain, + signer, + chainId, + dryRun = true, +}: { + rootDomain: string; + signer: Wallet; + chainId: ChainId; + dryRun?: boolean; +}) => { + const logger = getLogger(); + const { + transactionFactory, + chainName, + provider, + domainHierarchy, + domainNotifier, + domainReader, + ensRegistry, + ensResolverV2Address, + } = await initDomains(signer, chainId); + + const updated: Record[] = []; + const update = async (domain: string) => { + const domainHash = namehash(domain); + + let def; + try { + def = await domainReader.read({ node: domainHash }); + } catch (e) { + // 'apps' and 'roles' + logger.warn(`Unable to read ${domain}: ${(e).message}`); + if ( + !( + domain.startsWith(NamespaceType.Application) || + domain.startsWith(NamespaceType.Role) || + domain.startsWith(NamespaceType.Organization) + ) + ) { + logger.info( + `${dryRun ? 'Would delete' : 'Deleting'} malformed ${domain}...` + ); + !dryRun && + (await ( + await ensRegistry.setRecord( + domainHash, + AddressZero, + AddressZero, + BigNumber.from(0) + ) + ).wait()); + return; + } + } + + // const resolver = await ensRegistry.resolver(domainHash); + if (!dryRun /*&& resolver !== ensResolverV2Address*/) { + await ( + await ensRegistry.setResolver(domainHash, ensResolverV2Address) + ).wait(); + // migrate domain definition + let updateDomainTx; + if (def) { + if (DomainReader.isRoleDefinition(def)) { + // in some legacy roles issuer.issuerType = {} + if (def.issuer?.did?.length) { + def.issuer.issuerType = 'DID'; + } else if (def.issuer.roleName) { + def.issuer.issuerType = 'ROLE'; + } else { + throw new Error(`Unable to set issuer type of ${domain}`); + } + const { + issuer: { did }, + version, + } = def; + const updatedDef: IRoleDefinitionV2 = castToV2({ + ...def, + issuer: { + ...def.issuer, + did: did?.map((d) => + isValidDID(d) + ? d + : `did:${Methods.Erc1056}:${chainName}:${d + .split(':') + .slice(-1)}` + ), + }, + version: parseInt(version.toString(), 10), + }); + if (JSON.stringify(def) !== JSON.stringify(updatedDef)) { + process.stdout.write( + `${dryRun ? 'Would fix' : 'Fixing'} definition of role ${domain}` + ); + updated.push({ domain, legacyDef: def, updatedDef }); + updateDomainTx = transactionFactory.newRole({ + domain, + roleDefinition: updatedDef, + }); + } + } else { + process.stdout.write( + `${dryRun ? 'Would create' : 'Creating'} new role ${domain}}` + ); + updateDomainTx = transactionFactory.newDomain({ + domain, + domainDefinition: def, + }); + } + !dryRun && + (await ( + await signer.connect(provider).sendTransaction(updateDomainTx) + ).wait()); + } + } + process.stdout.write( + `${dryRun ? 'Would set' : 'Setting'} name of ${domain}` + ); + if (!dryRun) { + await ( + await signer + .connect(provider) + .sendTransaction(transactionFactory.setDomainNameTx({ domain })) + ).wait(); + } + + const subnodes = await domainHierarchy.getSubdomainsUsingResolver({ + domain, + mode: 'FIRSTLEVEL', + }); + if (def) { + if ( + DomainReader.isOrgDefinition(def) || + DomainReader.isAppDefinition(def) + ) { + subnodes.push(`roles.${domain}`); + } + if (DomainReader.isOrgDefinition(def)) { + subnodes.push(`apps.${domain}`); + } + } + logger.info(`subnodes of ${domain} are: ${subnodes}`); + for await (const nodeName of subnodes) { + console.group(); + const label = nodeName.split('.')[0]; + const labelHash = labelhash(label); + const nodeHash = namehash(nodeName); + const owner = await ensRegistry.owner(nodeHash); + process.stdout.write( + `${dryRun ? 'Would set' : 'Setting'} owner of ${domain}` + ); + if (!dryRun) { + await ( + await ensRegistry.setSubnodeOwner( + domainHash, + labelHash, + await signer.getAddress() + ) + ).wait(); + } + await update(nodeName); + if (!dryRun) { + await ( + await ensRegistry.setSubnodeOwner(domainHash, labelHash, owner) + ).wait(); + } + console.groupEnd(); + } + await (await domainNotifier.domainUpdated(domainHash)).wait(); + }; + + await update(rootDomain); + return updated; +}; diff --git a/tsconfig.json b/tsconfig.json index 32489710..7aa6b052 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,11 @@ "experimentalDecorators": true, "forceConsistentCasingInFileNames": true, "importHelpers": true, - "lib": ["es2019", "dom", "esnext.asynciterable"], + "lib": [ + "es2019", + "dom", + "esnext.asynciterable" + ], "module": "esnext", "moduleResolution": "node", "noFallthroughCasesInSwitch": true, @@ -32,6 +36,8 @@ "target": "es6" }, "ts-node": { - "compilerOptions": { "module": "CommonJS" } + "compilerOptions": { + "module": "CommonJS" + } } }