From a7edda4b1acbbbb2e27e892715b6882b0be8e945 Mon Sep 17 00:00:00 2001 From: 0xCourtney <23440493+0xCourtney@users.noreply.github.com> Date: Thu, 10 Nov 2022 15:15:39 -0500 Subject: [PATCH 01/28] diamond utils draft --- lib/diamonds.ts | 327 +++++++++++++++++++++++++++++++++++++++++++++++ lib/index.ts | 1 + lib/package.json | 1 + 3 files changed, 329 insertions(+) create mode 100644 lib/diamonds.ts diff --git a/lib/diamonds.ts b/lib/diamonds.ts new file mode 100644 index 00000000..b8b0a4a1 --- /dev/null +++ b/lib/diamonds.ts @@ -0,0 +1,327 @@ +import { + IDiamondReadable, + IDiamondWritable, +} from '@solidstate/typechain-types'; +import { Contract, constants } from 'ethers'; + +const Table = require('cli-table3'); + +type Diamond = IDiamondReadable & IDiamondWritable; + +export interface Facet { + target: string; + selectors: string[]; +} + +enum FacetCutAction { + Add, + Replace, + Remove, +} + +export interface FacetCut extends Facet { + action: FacetCutAction; +} + +export function getSignatures(contract: Contract): string[] { + return Object.keys(contract.interface.functions); +} + +export function getSelectors(contract: Contract): string[] { + const signatures = getSignatures(contract); + return signatures.reduce((acc: string[], val: string) => { + acc.push(contract.interface.getSighash(val)); + return acc; + }, []); +} + +export function getFacets(contracts: Contract[]): Facet[] { + return contracts.map((contract) => { + return { + target: contract.address, + selectors: getSelectors(contract), + }; + }); +} + +export function selectorExistsInFacets( + selector: string, + facets: Facet[], +): boolean { + for (const facet of facets) { + if (facet.selectors.includes(selector)) return true; + } + + return false; +} + +// adds unregistered selectors +export async function addUnregisteredSelectors( + diamond: Diamond, + contracts: Contract[], + exclude: string[] = [], +): Promise { + const diamondFacets: Facet[] = await diamond.facets(); + const facets = getFacets(contracts); + let facetCuts: FacetCut[] = []; + + // if facet selector is unregistered then it should be added to the diamond. + for (const facet of facets) { + for (const selector of facet.selectors) { + const target = facet.target; + + if ( + target !== diamond.address && + selector.length > 0 && + !selectorExistsInFacets(selector, diamondFacets) && + !exclude.includes(selector) + ) { + facetCuts.push( + printFacetCuts(facet.target, [selector], FacetCutAction.Add), + ); + } + } + } + + return groupFacetCuts(facetCuts); +} + +// replace registered selectors +export async function replaceRegisteredSelectors( + diamond: Diamond, + contracts: Contract[], + exclude: string[] = [], +): Promise { + const diamondFacets: Facet[] = await diamond.facets(); + const facets = getFacets(contracts); + let facetCuts: FacetCut[] = []; + + // if a facet selector is registered with a different target address, the target will be replaced + for (const facet of facets) { + for (const selector of facet.selectors) { + const target = facet.target; + const oldTarget = await diamond.facetAddress(selector); + + if ( + target != oldTarget && + target != constants.AddressZero && + target != diamond.address && + selector.length > 0 && + selectorExistsInFacets(selector, diamondFacets) && + !exclude.includes(selector) + ) { + facetCuts.push( + printFacetCuts(target, [selector], FacetCutAction.Replace), + ); + } + } + } + + return groupFacetCuts(facetCuts); +} + +// removes registered selectors +export async function removeRegisteredSelectors( + diamond: Diamond, + contracts: Contract[], + exclude: string[] = [], +): Promise { + const diamondFacets: Facet[] = await diamond.facets(); + const facets = getFacets(contracts); + let facetCuts: FacetCut[] = []; + + // if a registered selector is not found in the facets then it should be removed from the diamond + for (const diamondFacet of diamondFacets) { + for (const selector of diamondFacet.selectors) { + const target = diamondFacet.target; + + if ( + target != constants.AddressZero && + target != diamond.address && + selector.length > 0 && + !selectorExistsInFacets(selector, facets) && + !exclude.includes(selector) + ) { + facetCuts.push( + printFacetCuts( + constants.AddressZero, + [selector], + FacetCutAction.Remove, + ), + ); + } + } + } + + return groupFacetCuts(facetCuts); +} + +export async function diamondCut( + diamond: Diamond, + facetCut: FacetCut[], + target: string = constants.AddressZero, + data: string = '0x', +) { + (await diamond.diamondCut(facetCut, target, data)).wait(1); +} + +// groups facet cuts by target address and action type +export function groupFacetCuts(facetCuts: FacetCut[]): FacetCut[] { + const cuts = facetCuts.reduce((acc: FacetCut[], facetCut: FacetCut) => { + if (acc.length == 0) acc.push(facetCut); + + let exists = false; + + acc.forEach((_, i) => { + if ( + acc[i].action == facetCut.action && + acc[i].target == facetCut.target + ) { + acc[i].selectors.push(...facetCut.selectors); + // removes duplicates, if there are any + acc[i].selectors = [...new Set(acc[i].selectors)]; + exists = true; + } + }); + + // push facet cut if it does not already exist + if (!exists) acc.push(facetCut); + + return acc; + }, []); + + let cache: any = {}; + + // checks if selector is used multiple times, emits warning + cuts.forEach((cut) => { + cut.selectors.forEach((selector: string) => { + if (cache[selector]) { + console.log( + `WARNING: selector: ${selector}, target: ${cut.target} is defined in multiple cuts`, + ); + } else { + cache[selector] = true; + } + }); + }); + + return cuts; +} + +export function printFacetCuts( + target: string, + selectors: string[], + action: number = 0, +): FacetCut { + return { + target: target, + action: action, + selectors: selectors, + }; +} + +// generates table of diamond and facet selectors +export async function printDiamond(diamond: Diamond, contracts: Contract[]) { + const padding = 2; + + const table = new Table({ + style: { + head: [], + border: [], + 'padding-left': padding, + 'padding-right': padding, + }, + chars: { + mid: '·', + 'top-mid': '|', + 'left-mid': ' ·', + 'mid-mid': '|', + 'right-mid': '·', + left: ' |', + 'top-left': ' ·', + 'top-right': '·', + 'bottom-left': ' ·', + 'bottom-right': '·', + middle: '·', + top: '-', + bottom: '-', + 'bottom-mid': '|', + }, + }); + + table.push([ + { + hAlign: 'center', + content: `Target`, + }, + { + hAlign: 'center', + content: `Signature`, + }, + { + hAlign: 'center', + content: `Selector`, + }, + { + hAlign: 'center', + content: `Registered`, + }, + ]); + + let diamondTable = []; + const signatures = await getSignatures(diamond); + + for (const signature of signatures) { + diamondTable.push({ + target: diamond.address, + signature: signature, + selector: diamond.interface.getSighash(signature), + registered: true, + }); + } + + for (const contract of contracts) { + const signatures = await getSignatures(contract); + for (const signature of signatures) { + diamondTable.push({ + target: contract.address, + signature: signature, + selector: contract.interface.getSighash(signature), + registered: false, + }); + } + } + + const diamondFacets: Facet[] = await diamond.facets(); + + for (const facet of diamondFacets) { + const target = facet.target; + + for (const selector of facet.selectors) { + for (const row of diamondTable) { + if (row.target == target && row.selector == selector) { + row.registered = true; + } + } + } + } + + for (const row of diamondTable) { + table.push([ + { + content: row.target, + }, + { + content: row.signature, + }, + { + content: row.selector, + }, + { + content: row.registered, + }, + ]); + } + + console.log(table.toString()); +} diff --git a/lib/index.ts b/lib/index.ts index e6459ee4..3da4ce2d 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -2,3 +2,4 @@ export * from './bn_conversion'; export * from './erc20_permit'; export * from './mocha_describe_filter'; export * from './sign_data'; +export * from './diamonds'; diff --git a/lib/package.json b/lib/package.json index 0faa17a2..89b4745a 100644 --- a/lib/package.json +++ b/lib/package.json @@ -25,6 +25,7 @@ "tsc-clean": "tsc --build --clean tsconfig.json" }, "dependencies": { + "cli-table3": "^0.6.3", "eth-permit": "^0.1.10" }, "files": [ From 047964efc5ae891831a15dd38d09cd2ca767c831 Mon Sep 17 00:00:00 2001 From: 0xCourtney <23440493+0xCourtney@users.noreply.github.com> Date: Sun, 11 Dec 2022 14:12:28 -0500 Subject: [PATCH 02/28] remove printDiamond --- lib/diamonds.ts | 108 ------------------------------------------------ 1 file changed, 108 deletions(-) diff --git a/lib/diamonds.ts b/lib/diamonds.ts index b8b0a4a1..f5b1e4e4 100644 --- a/lib/diamonds.ts +++ b/lib/diamonds.ts @@ -4,8 +4,6 @@ import { } from '@solidstate/typechain-types'; import { Contract, constants } from 'ethers'; -const Table = require('cli-table3'); - type Diamond = IDiamondReadable & IDiamondWritable; export interface Facet { @@ -219,109 +217,3 @@ export function printFacetCuts( selectors: selectors, }; } - -// generates table of diamond and facet selectors -export async function printDiamond(diamond: Diamond, contracts: Contract[]) { - const padding = 2; - - const table = new Table({ - style: { - head: [], - border: [], - 'padding-left': padding, - 'padding-right': padding, - }, - chars: { - mid: '·', - 'top-mid': '|', - 'left-mid': ' ·', - 'mid-mid': '|', - 'right-mid': '·', - left: ' |', - 'top-left': ' ·', - 'top-right': '·', - 'bottom-left': ' ·', - 'bottom-right': '·', - middle: '·', - top: '-', - bottom: '-', - 'bottom-mid': '|', - }, - }); - - table.push([ - { - hAlign: 'center', - content: `Target`, - }, - { - hAlign: 'center', - content: `Signature`, - }, - { - hAlign: 'center', - content: `Selector`, - }, - { - hAlign: 'center', - content: `Registered`, - }, - ]); - - let diamondTable = []; - const signatures = await getSignatures(diamond); - - for (const signature of signatures) { - diamondTable.push({ - target: diamond.address, - signature: signature, - selector: diamond.interface.getSighash(signature), - registered: true, - }); - } - - for (const contract of contracts) { - const signatures = await getSignatures(contract); - for (const signature of signatures) { - diamondTable.push({ - target: contract.address, - signature: signature, - selector: contract.interface.getSighash(signature), - registered: false, - }); - } - } - - const diamondFacets: Facet[] = await diamond.facets(); - - for (const facet of diamondFacets) { - const target = facet.target; - - for (const selector of facet.selectors) { - for (const row of diamondTable) { - if (row.target == target && row.selector == selector) { - row.registered = true; - } - } - } - } - - for (const row of diamondTable) { - table.push([ - { - content: row.target, - }, - { - content: row.signature, - }, - { - content: row.selector, - }, - { - content: row.registered, - }, - ]); - } - - console.log(table.toString()); -} From 9d3a62df2229dd10c9b2c07c1a98e2ba4f57379f Mon Sep 17 00:00:00 2001 From: 0xCourtney <23440493+0xCourtney@users.noreply.github.com> Date: Sun, 11 Dec 2022 14:15:35 -0500 Subject: [PATCH 03/28] removes composite type --- lib/diamonds.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/diamonds.ts b/lib/diamonds.ts index f5b1e4e4..5729dc67 100644 --- a/lib/diamonds.ts +++ b/lib/diamonds.ts @@ -4,8 +4,6 @@ import { } from '@solidstate/typechain-types'; import { Contract, constants } from 'ethers'; -type Diamond = IDiamondReadable & IDiamondWritable; - export interface Facet { target: string; selectors: string[]; @@ -55,7 +53,7 @@ export function selectorExistsInFacets( // adds unregistered selectors export async function addUnregisteredSelectors( - diamond: Diamond, + diamond: IDiamondReadable, contracts: Contract[], exclude: string[] = [], ): Promise { @@ -86,7 +84,7 @@ export async function addUnregisteredSelectors( // replace registered selectors export async function replaceRegisteredSelectors( - diamond: Diamond, + diamond: IDiamondReadable, contracts: Contract[], exclude: string[] = [], ): Promise { @@ -120,7 +118,7 @@ export async function replaceRegisteredSelectors( // removes registered selectors export async function removeRegisteredSelectors( - diamond: Diamond, + diamond: IDiamondReadable, contracts: Contract[], exclude: string[] = [], ): Promise { @@ -155,7 +153,7 @@ export async function removeRegisteredSelectors( } export async function diamondCut( - diamond: Diamond, + diamond: IDiamondWritable, facetCut: FacetCut[], target: string = constants.AddressZero, data: string = '0x', From 9d3b06061fdc6bc420fc00d11bec97ff7995c6b7 Mon Sep 17 00:00:00 2001 From: 0xCourtney <23440493+0xCourtney@users.noreply.github.com> Date: Sun, 11 Dec 2022 14:16:17 -0500 Subject: [PATCH 04/28] exports facet cut action --- lib/diamonds.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/diamonds.ts b/lib/diamonds.ts index 5729dc67..e86934cd 100644 --- a/lib/diamonds.ts +++ b/lib/diamonds.ts @@ -9,7 +9,7 @@ export interface Facet { selectors: string[]; } -enum FacetCutAction { +export enum FacetCutAction { Add, Replace, Remove, From 20bf78e129eb72162443c36f273a121a45ddd3cb Mon Sep 17 00:00:00 2001 From: 0xCourtney <23440493+0xCourtney@users.noreply.github.com> Date: Sun, 11 Dec 2022 14:18:34 -0500 Subject: [PATCH 05/28] removes cli-table3 package --- lib/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/package.json b/lib/package.json index 81954d81..1ec9c9e6 100644 --- a/lib/package.json +++ b/lib/package.json @@ -25,7 +25,6 @@ "tsc-clean": "tsc --build --clean tsconfig.json" }, "dependencies": { - "cli-table3": "^0.6.3", "eth-permit": "^0.1.10" }, "files": [ From b2eda5732ea11020a502eea1572d8f5d472de046 Mon Sep 17 00:00:00 2001 From: 0xCourtney <23440493+0xCourtney@users.noreply.github.com> Date: Sun, 11 Dec 2022 14:50:55 -0500 Subject: [PATCH 06/28] imports ethersproject packages --- lib/diamonds.ts | 15 ++++++--------- lib/package.json | 2 ++ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/diamonds.ts b/lib/diamonds.ts index e86934cd..c3d3edd4 100644 --- a/lib/diamonds.ts +++ b/lib/diamonds.ts @@ -1,8 +1,9 @@ +import { AddressZero } from '@ethersproject/constants'; +import { Contract } from '@ethersproject/contracts'; import { IDiamondReadable, IDiamondWritable, } from '@solidstate/typechain-types'; -import { Contract, constants } from 'ethers'; export interface Facet { target: string; @@ -100,7 +101,7 @@ export async function replaceRegisteredSelectors( if ( target != oldTarget && - target != constants.AddressZero && + target != AddressZero && target != diamond.address && selector.length > 0 && selectorExistsInFacets(selector, diamondFacets) && @@ -132,18 +133,14 @@ export async function removeRegisteredSelectors( const target = diamondFacet.target; if ( - target != constants.AddressZero && + target != AddressZero && target != diamond.address && selector.length > 0 && !selectorExistsInFacets(selector, facets) && !exclude.includes(selector) ) { facetCuts.push( - printFacetCuts( - constants.AddressZero, - [selector], - FacetCutAction.Remove, - ), + printFacetCuts(AddressZero, [selector], FacetCutAction.Remove), ); } } @@ -155,7 +152,7 @@ export async function removeRegisteredSelectors( export async function diamondCut( diamond: IDiamondWritable, facetCut: FacetCut[], - target: string = constants.AddressZero, + target: string = AddressZero, data: string = '0x', ) { (await diamond.diamondCut(facetCut, target, data)).wait(1); diff --git a/lib/package.json b/lib/package.json index 1ec9c9e6..d233ca38 100644 --- a/lib/package.json +++ b/lib/package.json @@ -25,6 +25,8 @@ "tsc-clean": "tsc --build --clean tsconfig.json" }, "dependencies": { + "@ethersproject/constants": "^5.7.1", + "@ethersproject/contracts": "^5.7.1", "eth-permit": "^0.1.10" }, "files": [ From 398ff37839fbe1e71f350cb3874fd0ba8f1e6f5d Mon Sep 17 00:00:00 2001 From: 0xCourtney <23440493+0xCourtney@users.noreply.github.com> Date: Wed, 14 Dec 2022 09:06:20 -0500 Subject: [PATCH 07/28] Revert "remove printDiamond" This reverts commit 9c37405629b7321e0725de8bf30de1e51eb5f685. --- lib/diamonds.ts | 111 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/lib/diamonds.ts b/lib/diamonds.ts index c3d3edd4..0553fc8b 100644 --- a/lib/diamonds.ts +++ b/lib/diamonds.ts @@ -5,6 +5,8 @@ import { IDiamondWritable, } from '@solidstate/typechain-types'; +const Table = require('cli-table3'); + export interface Facet { target: string; selectors: string[]; @@ -212,3 +214,112 @@ export function printFacetCuts( selectors: selectors, }; } + +// generates table of diamond and facet selectors +export async function printDiamond( + diamond: IDiamondReadable, + contracts: Contract[], +) { + const padding = 2; + + const table = new Table({ + style: { + head: [], + border: [], + 'padding-left': padding, + 'padding-right': padding, + }, + chars: { + mid: '·', + 'top-mid': '|', + 'left-mid': ' ·', + 'mid-mid': '|', + 'right-mid': '·', + left: ' |', + 'top-left': ' ·', + 'top-right': '·', + 'bottom-left': ' ·', + 'bottom-right': '·', + middle: '·', + top: '-', + bottom: '-', + 'bottom-mid': '|', + }, + }); + + table.push([ + { + hAlign: 'center', + content: `Target`, + }, + { + hAlign: 'center', + content: `Signature`, + }, + { + hAlign: 'center', + content: `Selector`, + }, + { + hAlign: 'center', + content: `Registered`, + }, + ]); + + let diamondTable = []; + const signatures = await getSignatures(diamond); + + for (const signature of signatures) { + diamondTable.push({ + target: diamond.address, + signature: signature, + selector: diamond.interface.getSighash(signature), + registered: true, + }); + } + + for (const contract of contracts) { + const signatures = await getSignatures(contract); + for (const signature of signatures) { + diamondTable.push({ + target: contract.address, + signature: signature, + selector: contract.interface.getSighash(signature), + registered: false, + }); + } + } + + const diamondFacets: Facet[] = await diamond.facets(); + + for (const facet of diamondFacets) { + const target = facet.target; + + for (const selector of facet.selectors) { + for (const row of diamondTable) { + if (row.target == target && row.selector == selector) { + row.registered = true; + } + } + } + } + + for (const row of diamondTable) { + table.push([ + { + content: row.target, + }, + { + content: row.signature, + }, + { + content: row.selector, + }, + { + content: row.registered, + }, + ]); + } + + console.log(table.toString()); +} From f9a6c2c2c162b15dd1c97c4f5fd9b01eba971469 Mon Sep 17 00:00:00 2001 From: 0xCourtney <23440493+0xCourtney@users.noreply.github.com> Date: Wed, 14 Dec 2022 09:07:29 -0500 Subject: [PATCH 08/28] removes printDiamond from external interface --- lib/diamonds.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/diamonds.ts b/lib/diamonds.ts index 0553fc8b..456cb6b3 100644 --- a/lib/diamonds.ts +++ b/lib/diamonds.ts @@ -216,10 +216,7 @@ export function printFacetCuts( } // generates table of diamond and facet selectors -export async function printDiamond( - diamond: IDiamondReadable, - contracts: Contract[], -) { +async function printDiamond(diamond: IDiamondReadable, contracts: Contract[]) { const padding = 2; const table = new Table({ From 45b2dc44e30d47ece990016ecc65e33dfa97900d Mon Sep 17 00:00:00 2001 From: 0xCourtney <23440493+0xCourtney@users.noreply.github.com> Date: Wed, 14 Dec 2022 09:57:23 -0500 Subject: [PATCH 09/28] throws if add/replace/remove fails --- lib/diamonds.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lib/diamonds.ts b/lib/diamonds.ts index 456cb6b3..9f8b1917 100644 --- a/lib/diamonds.ts +++ b/lib/diamonds.ts @@ -62,6 +62,8 @@ export async function addUnregisteredSelectors( ): Promise { const diamondFacets: Facet[] = await diamond.facets(); const facets = getFacets(contracts); + + let selectorsAdded = false; let facetCuts: FacetCut[] = []; // if facet selector is unregistered then it should be added to the diamond. @@ -78,10 +80,16 @@ export async function addUnregisteredSelectors( facetCuts.push( printFacetCuts(facet.target, [selector], FacetCutAction.Add), ); + + selectorsAdded = true; } } } + if (!selectorsAdded) { + throw new Error('No selectors were added to FacetCut'); + } + return groupFacetCuts(facetCuts); } @@ -93,6 +101,8 @@ export async function replaceRegisteredSelectors( ): Promise { const diamondFacets: Facet[] = await diamond.facets(); const facets = getFacets(contracts); + + let selectorsReplaced = false; let facetCuts: FacetCut[] = []; // if a facet selector is registered with a different target address, the target will be replaced @@ -112,10 +122,16 @@ export async function replaceRegisteredSelectors( facetCuts.push( printFacetCuts(target, [selector], FacetCutAction.Replace), ); + + selectorsReplaced = true; } } } + if (!selectorsReplaced) { + throw new Error('No selectors were replaced in FacetCut'); + } + return groupFacetCuts(facetCuts); } @@ -127,6 +143,8 @@ export async function removeRegisteredSelectors( ): Promise { const diamondFacets: Facet[] = await diamond.facets(); const facets = getFacets(contracts); + + let selectorsRemoved = false; let facetCuts: FacetCut[] = []; // if a registered selector is not found in the facets then it should be removed from the diamond @@ -144,10 +162,16 @@ export async function removeRegisteredSelectors( facetCuts.push( printFacetCuts(AddressZero, [selector], FacetCutAction.Remove), ); + + selectorsRemoved = true; } } } + if (!selectorsRemoved) { + throw new Error('No selectors were removed from FacetCut'); + } + return groupFacetCuts(facetCuts); } From 3d9d4568e952998dafc71baacaf4103b93bf1319 Mon Sep 17 00:00:00 2001 From: 0xCourtney <23440493+0xCourtney@users.noreply.github.com> Date: Mon, 19 Dec 2022 17:08:01 -0500 Subject: [PATCH 10/28] adds only filter, refactors exclude --- lib/diamonds.ts | 73 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 67 insertions(+), 6 deletions(-) diff --git a/lib/diamonds.ts b/lib/diamonds.ts index 9f8b1917..b2a28ed0 100644 --- a/lib/diamonds.ts +++ b/lib/diamonds.ts @@ -50,16 +50,71 @@ export function selectorExistsInFacets( for (const facet of facets) { if (facet.selectors.includes(selector)) return true; } + return false; +} + +interface FacetFilter { + contract: string; + selectors: string[]; +} + +function selectorIsFiltered( + only: FacetFilter[], + exclude: FacetFilter[], + contract: string, + selector: string, +): boolean { + if (only.length > 0) { + // include selectors found in only, exclude all others + return isFiltered(only, contract, selector); + } + + if (exclude.length > 0) { + // exclude selectors found in exclude, include all others + return !isFiltered(exclude, contract, selector); + } + + // if neither only or exclude are used, then include all selectors + return true; +} + +function isFiltered( + filters: FacetFilter[], + contract: string, + selector: string, +): boolean { + for (const filter of filters) { + if (filter.contract === contract || AddressZero === contract) { + return filter.selectors.includes(selector); + } + } return false; } +function validateFilters(only: FacetFilter[], exclude: FacetFilter[]) { + if (only.length > 0 && exclude.length > 0) { + for (const onlyFilter of only) { + for (const excludeFilter of exclude) { + if (onlyFilter.contract === excludeFilter.contract) { + throw new Error( + 'only and exclude filters cannot contain the same contract', + ); + } + } + } + } +} + // adds unregistered selectors export async function addUnregisteredSelectors( diamond: IDiamondReadable, contracts: Contract[], - exclude: string[] = [], + only: FacetFilter[] = [], + exclude: FacetFilter[] = [], ): Promise { + validateFilters(only, exclude); + const diamondFacets: Facet[] = await diamond.facets(); const facets = getFacets(contracts); @@ -75,7 +130,7 @@ export async function addUnregisteredSelectors( target !== diamond.address && selector.length > 0 && !selectorExistsInFacets(selector, diamondFacets) && - !exclude.includes(selector) + selectorIsFiltered(only, exclude, target, selector) ) { facetCuts.push( printFacetCuts(facet.target, [selector], FacetCutAction.Add), @@ -97,8 +152,11 @@ export async function addUnregisteredSelectors( export async function replaceRegisteredSelectors( diamond: IDiamondReadable, contracts: Contract[], - exclude: string[] = [], + only: FacetFilter[] = [], + exclude: FacetFilter[] = [], ): Promise { + validateFilters(only, exclude); + const diamondFacets: Facet[] = await diamond.facets(); const facets = getFacets(contracts); @@ -117,7 +175,7 @@ export async function replaceRegisteredSelectors( target != diamond.address && selector.length > 0 && selectorExistsInFacets(selector, diamondFacets) && - !exclude.includes(selector) + selectorIsFiltered(only, exclude, target, selector) ) { facetCuts.push( printFacetCuts(target, [selector], FacetCutAction.Replace), @@ -139,8 +197,11 @@ export async function replaceRegisteredSelectors( export async function removeRegisteredSelectors( diamond: IDiamondReadable, contracts: Contract[], - exclude: string[] = [], + only: FacetFilter[] = [], + exclude: FacetFilter[] = [], ): Promise { + validateFilters(only, exclude); + const diamondFacets: Facet[] = await diamond.facets(); const facets = getFacets(contracts); @@ -157,7 +218,7 @@ export async function removeRegisteredSelectors( target != diamond.address && selector.length > 0 && !selectorExistsInFacets(selector, facets) && - !exclude.includes(selector) + selectorIsFiltered(only, exclude, AddressZero, selector) ) { facetCuts.push( printFacetCuts(AddressZero, [selector], FacetCutAction.Remove), From e702c36dad50b8cea06135110ab87c1d206496e4 Mon Sep 17 00:00:00 2001 From: 0xCourtney <23440493+0xCourtney@users.noreply.github.com> Date: Mon, 19 Dec 2022 17:12:14 -0500 Subject: [PATCH 11/28] adds previewFacetCut --- lib/diamonds.ts | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/lib/diamonds.ts b/lib/diamonds.ts index b2a28ed0..2757e3db 100644 --- a/lib/diamonds.ts +++ b/lib/diamonds.ts @@ -236,6 +236,57 @@ export async function removeRegisteredSelectors( return groupFacetCuts(facetCuts); } +// previews the facet cuts +export async function previewFacetCut( + diamond: IDiamondReadable, + contracts: Contract[], + only: FacetFilter[][] = [[], [], []], + exclude: FacetFilter[][] = [[], [], []], +): Promise { + let addFacetCuts: FacetCut[] = []; + let replaceFacetCuts: FacetCut[] = []; + let removeFacetCuts: FacetCut[] = []; + + try { + addFacetCuts = await addUnregisteredSelectors( + diamond, + contracts, + only[0], + exclude[0], + ); + } catch (error) { + console.log(`WARNING: ${(error as Error).message}`); + } + + try { + replaceFacetCuts = await replaceRegisteredSelectors( + diamond, + contracts, + only[1], + exclude[1], + ); + } catch (error) { + console.log(`WARNING: ${(error as Error).message}`); + } + + try { + removeFacetCuts = await removeRegisteredSelectors( + diamond, + contracts, + only[2], + exclude[2], + ); + } catch (error) { + console.log(`WARNING: ${(error as Error).message}`); + } + + return groupFacetCuts([ + ...addFacetCuts, + ...replaceFacetCuts, + ...removeFacetCuts, + ]); +} + export async function diamondCut( diamond: IDiamondWritable, facetCut: FacetCut[], From 5f34d05f2b9b377ca7f276b35b62270c5fdc36eb Mon Sep 17 00:00:00 2001 From: 0xCourtney <23440493+0xCourtney@users.noreply.github.com> Date: Mon, 19 Dec 2022 18:49:34 -0500 Subject: [PATCH 12/28] removes printDiamond --- lib/diamonds.ts | 108 ------------------------------------------------ 1 file changed, 108 deletions(-) diff --git a/lib/diamonds.ts b/lib/diamonds.ts index 2757e3db..8452158e 100644 --- a/lib/diamonds.ts +++ b/lib/diamonds.ts @@ -5,8 +5,6 @@ import { IDiamondWritable, } from '@solidstate/typechain-types'; -const Table = require('cli-table3'); - export interface Facet { target: string; selectors: string[]; @@ -350,109 +348,3 @@ export function printFacetCuts( selectors: selectors, }; } - -// generates table of diamond and facet selectors -async function printDiamond(diamond: IDiamondReadable, contracts: Contract[]) { - const padding = 2; - - const table = new Table({ - style: { - head: [], - border: [], - 'padding-left': padding, - 'padding-right': padding, - }, - chars: { - mid: '·', - 'top-mid': '|', - 'left-mid': ' ·', - 'mid-mid': '|', - 'right-mid': '·', - left: ' |', - 'top-left': ' ·', - 'top-right': '·', - 'bottom-left': ' ·', - 'bottom-right': '·', - middle: '·', - top: '-', - bottom: '-', - 'bottom-mid': '|', - }, - }); - - table.push([ - { - hAlign: 'center', - content: `Target`, - }, - { - hAlign: 'center', - content: `Signature`, - }, - { - hAlign: 'center', - content: `Selector`, - }, - { - hAlign: 'center', - content: `Registered`, - }, - ]); - - let diamondTable = []; - const signatures = await getSignatures(diamond); - - for (const signature of signatures) { - diamondTable.push({ - target: diamond.address, - signature: signature, - selector: diamond.interface.getSighash(signature), - registered: true, - }); - } - - for (const contract of contracts) { - const signatures = await getSignatures(contract); - for (const signature of signatures) { - diamondTable.push({ - target: contract.address, - signature: signature, - selector: contract.interface.getSighash(signature), - registered: false, - }); - } - } - - const diamondFacets: Facet[] = await diamond.facets(); - - for (const facet of diamondFacets) { - const target = facet.target; - - for (const selector of facet.selectors) { - for (const row of diamondTable) { - if (row.target == target && row.selector == selector) { - row.registered = true; - } - } - } - } - - for (const row of diamondTable) { - table.push([ - { - content: row.target, - }, - { - content: row.signature, - }, - { - content: row.selector, - }, - { - content: row.registered, - }, - ]); - } - - console.log(table.toString()); -} From 1aa7db80bf92e886c301f9df7a040263da4a0771 Mon Sep 17 00:00:00 2001 From: 0xCourtney <23440493+0xCourtney@users.noreply.github.com> Date: Tue, 20 Dec 2022 11:30:41 -0500 Subject: [PATCH 13/28] adds comments --- lib/diamonds.ts | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/lib/diamonds.ts b/lib/diamonds.ts index 8452158e..718d23be 100644 --- a/lib/diamonds.ts +++ b/lib/diamonds.ts @@ -20,10 +20,12 @@ export interface FacetCut extends Facet { action: FacetCutAction; } +// returns a list of signatures for a contract export function getSignatures(contract: Contract): string[] { return Object.keys(contract.interface.functions); } +// returns a list of selectors for a contract export function getSelectors(contract: Contract): string[] { const signatures = getSignatures(contract); return signatures.reduce((acc: string[], val: string) => { @@ -32,6 +34,7 @@ export function getSelectors(contract: Contract): string[] { }, []); } +// returns a list of Facets for a contract export function getFacets(contracts: Contract[]): Facet[] { return contracts.map((contract) => { return { @@ -41,6 +44,7 @@ export function getFacets(contracts: Contract[]): Facet[] { }); } +// returns true if the selector is found in the facets export function selectorExistsInFacets( selector: string, facets: Facet[], @@ -56,6 +60,7 @@ interface FacetFilter { selectors: string[]; } +// returns true if the selector is found in the only or exclude filters function selectorIsFiltered( only: FacetFilter[], exclude: FacetFilter[], @@ -64,19 +69,20 @@ function selectorIsFiltered( ): boolean { if (only.length > 0) { // include selectors found in only, exclude all others - return isFiltered(only, contract, selector); + return includes(only, contract, selector); } if (exclude.length > 0) { // exclude selectors found in exclude, include all others - return !isFiltered(exclude, contract, selector); + return !includes(exclude, contract, selector); } // if neither only or exclude are used, then include all selectors return true; } -function isFiltered( +// returns true if the selector is found in the filters +function includes( filters: FacetFilter[], contract: string, selector: string, @@ -90,6 +96,7 @@ function isFiltered( return false; } +// validates that only and exclude filters do not contain the same contract function validateFilters(only: FacetFilter[], exclude: FacetFilter[]) { if (only.length > 0 && exclude.length > 0) { for (const onlyFilter of only) { @@ -104,7 +111,7 @@ function validateFilters(only: FacetFilter[], exclude: FacetFilter[]) { } } -// adds unregistered selectors +// preview FacetCut which adds unregistered selectors export async function addUnregisteredSelectors( diamond: IDiamondReadable, contracts: Contract[], @@ -146,7 +153,7 @@ export async function addUnregisteredSelectors( return groupFacetCuts(facetCuts); } -// replace registered selectors +// preview FacetCut which replaces registered selectors with unregistered selectors export async function replaceRegisteredSelectors( diamond: IDiamondReadable, contracts: Contract[], @@ -161,7 +168,8 @@ export async function replaceRegisteredSelectors( let selectorsReplaced = false; let facetCuts: FacetCut[] = []; - // if a facet selector is registered with a different target address, the target will be replaced + // if a facet selector is registered with a different target address, the target will + // be replaced for (const facet of facets) { for (const selector of facet.selectors) { const target = facet.target; @@ -191,7 +199,7 @@ export async function replaceRegisteredSelectors( return groupFacetCuts(facetCuts); } -// removes registered selectors +// preview FacetCut which removes registered selectors export async function removeRegisteredSelectors( diamond: IDiamondReadable, contracts: Contract[], @@ -206,7 +214,8 @@ export async function removeRegisteredSelectors( let selectorsRemoved = false; let facetCuts: FacetCut[] = []; - // if a registered selector is not found in the facets then it should be removed from the diamond + // if a registered selector is not found in the facets then it should be removed + // from the diamond for (const diamondFacet of diamondFacets) { for (const selector of diamondFacet.selectors) { const target = diamondFacet.target; @@ -234,7 +243,7 @@ export async function removeRegisteredSelectors( return groupFacetCuts(facetCuts); } -// previews the facet cuts +// preview a FacetCut which adds, replaces, or removes selectors, as needed export async function previewFacetCut( diamond: IDiamondReadable, contracts: Contract[], @@ -285,6 +294,7 @@ export async function previewFacetCut( ]); } +// executes a DiamondCut using the provided FacetCut export async function diamondCut( diamond: IDiamondWritable, facetCut: FacetCut[], From f00863ec33e058d0ee2fe03a7282bb533b8313f5 Mon Sep 17 00:00:00 2001 From: Nick Barry Date: Tue, 20 Dec 2022 18:41:36 +0200 Subject: [PATCH 14/28] capitalize FacetCutAction enum types --- lib/diamonds.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/diamonds.ts b/lib/diamonds.ts index 8452158e..75c59db7 100644 --- a/lib/diamonds.ts +++ b/lib/diamonds.ts @@ -11,9 +11,9 @@ export interface Facet { } export enum FacetCutAction { - Add, - Replace, - Remove, + ADD, + REPLACE, + REMOVE, } export interface FacetCut extends Facet { @@ -131,7 +131,7 @@ export async function addUnregisteredSelectors( selectorIsFiltered(only, exclude, target, selector) ) { facetCuts.push( - printFacetCuts(facet.target, [selector], FacetCutAction.Add), + printFacetCuts(facet.target, [selector], FacetCutAction.ADD), ); selectorsAdded = true; @@ -176,7 +176,7 @@ export async function replaceRegisteredSelectors( selectorIsFiltered(only, exclude, target, selector) ) { facetCuts.push( - printFacetCuts(target, [selector], FacetCutAction.Replace), + printFacetCuts(target, [selector], FacetCutAction.REPLACE), ); selectorsReplaced = true; @@ -219,7 +219,7 @@ export async function removeRegisteredSelectors( selectorIsFiltered(only, exclude, AddressZero, selector) ) { facetCuts.push( - printFacetCuts(AddressZero, [selector], FacetCutAction.Remove), + printFacetCuts(AddressZero, [selector], FacetCutAction.REMOVE), ); selectorsRemoved = true; From c8195bc74f3ac65fd90b65a3982066fe4b9cffc8 Mon Sep 17 00:00:00 2001 From: 0xCourtney <23440493+0xCourtney@users.noreply.github.com> Date: Tue, 20 Dec 2022 11:46:26 -0500 Subject: [PATCH 15/28] moves diamond utils to diamond subdirectory --- lib/diamond/filters.ts | 57 +++++++++++++++++++++++++++ lib/{diamonds.ts => diamond/utils.ts} | 57 +-------------------------- lib/index.ts | 2 +- 3 files changed, 59 insertions(+), 57 deletions(-) create mode 100644 lib/diamond/filters.ts rename lib/{diamonds.ts => diamond/utils.ts} (84%) diff --git a/lib/diamond/filters.ts b/lib/diamond/filters.ts new file mode 100644 index 00000000..17435cd1 --- /dev/null +++ b/lib/diamond/filters.ts @@ -0,0 +1,57 @@ +import { AddressZero } from '@ethersproject/constants'; + +export interface FacetFilter { + contract: string; + selectors: string[]; +} + +// returns true if the selector is found in the only or exclude filters +export function selectorIsFiltered( + only: FacetFilter[], + exclude: FacetFilter[], + contract: string, + selector: string, +): boolean { + if (only.length > 0) { + // include selectors found in only, exclude all others + return includes(only, contract, selector); + } + + if (exclude.length > 0) { + // exclude selectors found in exclude, include all others + return !includes(exclude, contract, selector); + } + + // if neither only or exclude are used, then include all selectors + return true; +} + +// returns true if the selector is found in the filters +export function includes( + filters: FacetFilter[], + contract: string, + selector: string, +): boolean { + for (const filter of filters) { + if (filter.contract === contract || AddressZero === contract) { + return filter.selectors.includes(selector); + } + } + + return false; +} + +// validates that only and exclude filters do not contain the same contract +export function validateFilters(only: FacetFilter[], exclude: FacetFilter[]) { + if (only.length > 0 && exclude.length > 0) { + for (const onlyFilter of only) { + for (const excludeFilter of exclude) { + if (onlyFilter.contract === excludeFilter.contract) { + throw new Error( + 'only and exclude filters cannot contain the same contract', + ); + } + } + } + } +} diff --git a/lib/diamonds.ts b/lib/diamond/utils.ts similarity index 84% rename from lib/diamonds.ts rename to lib/diamond/utils.ts index 718d23be..e25fdfa3 100644 --- a/lib/diamonds.ts +++ b/lib/diamond/utils.ts @@ -1,3 +1,4 @@ +import { FacetFilter, selectorIsFiltered, validateFilters } from './filters'; import { AddressZero } from '@ethersproject/constants'; import { Contract } from '@ethersproject/contracts'; import { @@ -55,62 +56,6 @@ export function selectorExistsInFacets( return false; } -interface FacetFilter { - contract: string; - selectors: string[]; -} - -// returns true if the selector is found in the only or exclude filters -function selectorIsFiltered( - only: FacetFilter[], - exclude: FacetFilter[], - contract: string, - selector: string, -): boolean { - if (only.length > 0) { - // include selectors found in only, exclude all others - return includes(only, contract, selector); - } - - if (exclude.length > 0) { - // exclude selectors found in exclude, include all others - return !includes(exclude, contract, selector); - } - - // if neither only or exclude are used, then include all selectors - return true; -} - -// returns true if the selector is found in the filters -function includes( - filters: FacetFilter[], - contract: string, - selector: string, -): boolean { - for (const filter of filters) { - if (filter.contract === contract || AddressZero === contract) { - return filter.selectors.includes(selector); - } - } - - return false; -} - -// validates that only and exclude filters do not contain the same contract -function validateFilters(only: FacetFilter[], exclude: FacetFilter[]) { - if (only.length > 0 && exclude.length > 0) { - for (const onlyFilter of only) { - for (const excludeFilter of exclude) { - if (onlyFilter.contract === excludeFilter.contract) { - throw new Error( - 'only and exclude filters cannot contain the same contract', - ); - } - } - } - } -} - // preview FacetCut which adds unregistered selectors export async function addUnregisteredSelectors( diamond: IDiamondReadable, diff --git a/lib/index.ts b/lib/index.ts index 3da4ce2d..6a4c3b7e 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -2,4 +2,4 @@ export * from './bn_conversion'; export * from './erc20_permit'; export * from './mocha_describe_filter'; export * from './sign_data'; -export * from './diamonds'; +export * from './diamond/utils'; From e5d19a2272fd96624098580ea0efce8d23be0c86 Mon Sep 17 00:00:00 2001 From: Nick Barry Date: Tue, 20 Dec 2022 19:56:48 +0200 Subject: [PATCH 16/28] use FacetCutAction enum in spec and test files --- .../diamond/SolidStateDiamond.behavior.ts | 32 +++-- .../writable/DiamondWritable.behavior.ts | 120 ++++++++++-------- test/proxy/diamond/SolidStateDiamond.ts | 3 +- test/proxy/diamond/base/DiamondBase.ts | 3 +- .../proxy/diamond/fallback/DiamondFallback.ts | 3 +- .../proxy/diamond/readable/DiamondReadable.ts | 3 +- 6 files changed, 96 insertions(+), 68 deletions(-) diff --git a/spec/proxy/diamond/SolidStateDiamond.behavior.ts b/spec/proxy/diamond/SolidStateDiamond.behavior.ts index 3326604b..aeffbf8c 100644 --- a/spec/proxy/diamond/SolidStateDiamond.behavior.ts +++ b/spec/proxy/diamond/SolidStateDiamond.behavior.ts @@ -24,7 +24,7 @@ import { MockContract, } from '@ethereum-waffle/mock-contract'; import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; -import { describeFilter } from '@solidstate/library'; +import { describeFilter, FacetCutAction } from '@solidstate/library'; import { ISolidStateDiamond } from '@solidstate/typechain-types'; import { expect } from 'chai'; import { ethers } from 'hardhat'; @@ -116,13 +116,17 @@ export function describeBehaviorOfSolidStateDiamond( const expectedSelectors = []; for (let selector of selectors) { - await instance - .connect(owner) - .diamondCut( - [{ target: facet.address, action: 0, selectors: [selector] }], - ethers.constants.AddressZero, - '0x', - ); + await instance.connect(owner).diamondCut( + [ + { + target: facet.address, + action: FacetCutAction.ADD, + selectors: [selector], + }, + ], + ethers.constants.AddressZero, + '0x', + ); expectedSelectors.push(selector); @@ -152,7 +156,7 @@ export function describeBehaviorOfSolidStateDiamond( await instance .connect(owner) .diamondCut( - [{ target: facet.address, action: 0, selectors }], + [{ target: facet.address, action: FacetCutAction.ADD, selectors }], ethers.constants.AddressZero, '0x', ); @@ -164,7 +168,7 @@ export function describeBehaviorOfSolidStateDiamond( [ { target: ethers.constants.AddressZero, - action: 2, + action: FacetCutAction.REMOVE, selectors: [selector], }, ], @@ -217,7 +221,7 @@ export function describeBehaviorOfSolidStateDiamond( await instance .connect(owner) .diamondCut( - [{ target: facet.address, action: 0, selectors }], + [{ target: facet.address, action: FacetCutAction.ADD, selectors }], ethers.constants.AddressZero, '0x', ); @@ -229,7 +233,7 @@ export function describeBehaviorOfSolidStateDiamond( [ { target: ethers.constants.AddressZero, - action: 2, + action: FacetCutAction.REMOVE, selectors: [selector], }, ], @@ -282,7 +286,7 @@ export function describeBehaviorOfSolidStateDiamond( await instance .connect(owner) .diamondCut( - [{ target: facet.address, action: 0, selectors }], + [{ target: facet.address, action: FacetCutAction.ADD, selectors }], ethers.constants.AddressZero, '0x', ); @@ -294,7 +298,7 @@ export function describeBehaviorOfSolidStateDiamond( [ { target: ethers.constants.AddressZero, - action: 2, + action: FacetCutAction.REMOVE, selectors: [selector], }, ], diff --git a/spec/proxy/diamond/writable/DiamondWritable.behavior.ts b/spec/proxy/diamond/writable/DiamondWritable.behavior.ts index e87447d4..387984c2 100644 --- a/spec/proxy/diamond/writable/DiamondWritable.behavior.ts +++ b/spec/proxy/diamond/writable/DiamondWritable.behavior.ts @@ -1,7 +1,7 @@ import { describeBehaviorOfERC165Base } from '../../../introspection'; import { deployMockContract } from '@ethereum-waffle/mock-contract'; import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; -import { describeFilter } from '@solidstate/library'; +import { describeFilter, FacetCutAction } from '@solidstate/library'; import { IDiamondWritable } from '@solidstate/typechain-types'; import { expect } from 'chai'; import { ethers } from 'hardhat'; @@ -68,7 +68,7 @@ export function describeBehaviorOfDiamondWritable( const facets: any = [ { target: facet.address, - action: 0, + action: FacetCutAction.ADD, selectors: [ethers.utils.hexlify(ethers.utils.randomBytes(4))], }, ]; @@ -107,13 +107,17 @@ export function describeBehaviorOfDiamondWritable( ); } - await instance - .connect(owner) - .diamondCut( - [{ target: facet.address, action: 0, selectors }], - ethers.constants.AddressZero, - '0x', - ); + await instance.connect(owner).diamondCut( + [ + { + target: facet.address, + action: FacetCutAction.ADD, + selectors, + }, + ], + ethers.constants.AddressZero, + '0x', + ); for (let fn of functions) { // call reverts, but with mock-specific message @@ -130,7 +134,7 @@ export function describeBehaviorOfDiamondWritable( [ { target: ethers.constants.AddressZero, - action: 0, + action: FacetCutAction.ADD, selectors: [ethers.utils.randomBytes(4)], }, ], @@ -147,7 +151,7 @@ export function describeBehaviorOfDiamondWritable( const facetCuts = [ { target: facet.address, - action: 0, + action: FacetCutAction.ADD, selectors: [ethers.utils.randomBytes(4)], }, ]; @@ -176,13 +180,17 @@ export function describeBehaviorOfDiamondWritable( ethers.provider, ); - await instance - .connect(owner) - .diamondCut( - [{ target: facet.address, action: 0, selectors }], - ethers.constants.AddressZero, - '0x', - ); + await instance.connect(owner).diamondCut( + [ + { + target: facet.address, + action: FacetCutAction.ADD, + selectors, + }, + ], + ethers.constants.AddressZero, + '0x', + ); for (let fn of functions) { // call reverts, but with mock-specific message @@ -197,13 +205,17 @@ export function describeBehaviorOfDiamondWritable( expect(facetReplacement[fn]).not.to.be.undefined; } - await instance - .connect(owner) - .diamondCut( - [{ target: facetReplacement.address, action: 1, selectors }], - ethers.constants.AddressZero, - '0x', - ); + await instance.connect(owner).diamondCut( + [ + { + target: facetReplacement.address, + action: FacetCutAction.REPLACE, + selectors, + }, + ], + ethers.constants.AddressZero, + '0x', + ); for (let fn of functions) { // call reverts, but with mock-specific message @@ -220,7 +232,7 @@ export function describeBehaviorOfDiamondWritable( [ { target: ethers.constants.AddressZero, - action: 1, + action: FacetCutAction.REPLACE, selectors: [ethers.utils.randomBytes(4)], }, ], @@ -239,7 +251,7 @@ export function describeBehaviorOfDiamondWritable( [ { target: facet.address, - action: 1, + action: FacetCutAction.REPLACE, selectors: [ethers.utils.randomBytes(4)], }, ], @@ -259,7 +271,7 @@ export function describeBehaviorOfDiamondWritable( [ { target: instance.address, - action: 0, + action: FacetCutAction.ADD, selectors: [selector], }, ], @@ -272,7 +284,7 @@ export function describeBehaviorOfDiamondWritable( [ { target: facet.address, - action: 1, + action: FacetCutAction.REPLACE, selectors: [selector], }, ], @@ -292,7 +304,7 @@ export function describeBehaviorOfDiamondWritable( [ { target: facet.address, - action: 0, + action: FacetCutAction.ADD, selectors: [selector], }, ], @@ -305,7 +317,7 @@ export function describeBehaviorOfDiamondWritable( [ { target: facet.address, - action: 1, + action: FacetCutAction.REPLACE, selectors: [selector], }, ], @@ -328,13 +340,17 @@ export function describeBehaviorOfDiamondWritable( ethers.provider, ); - await instance - .connect(owner) - .diamondCut( - [{ target: facet.address, action: 0, selectors }], - ethers.constants.AddressZero, - '0x', - ); + await instance.connect(owner).diamondCut( + [ + { + target: facet.address, + action: FacetCutAction.ADD, + selectors, + }, + ], + ethers.constants.AddressZero, + '0x', + ); for (let fn of functions) { // call reverts, but with mock-specific message @@ -343,13 +359,17 @@ export function describeBehaviorOfDiamondWritable( ); } - await instance - .connect(owner) - .diamondCut( - [{ target: ethers.constants.AddressZero, action: 2, selectors }], - ethers.constants.AddressZero, - '0x', - ); + await instance.connect(owner).diamondCut( + [ + { + target: ethers.constants.AddressZero, + action: FacetCutAction.REMOVE, + selectors, + }, + ], + ethers.constants.AddressZero, + '0x', + ); for (let fn of functions) { await expect( @@ -368,7 +388,7 @@ export function describeBehaviorOfDiamondWritable( [ { target: instance.address, - action: 2, + action: FacetCutAction.REMOVE, selectors: [ethers.utils.randomBytes(4)], }, ], @@ -387,7 +407,7 @@ export function describeBehaviorOfDiamondWritable( [ { target: ethers.constants.AddressZero, - action: 2, + action: FacetCutAction.REMOVE, selectors: [ethers.utils.randomBytes(4)], }, ], @@ -407,7 +427,7 @@ export function describeBehaviorOfDiamondWritable( [ { target: instance.address, - action: 0, + action: FacetCutAction.ADD, selectors: [selector], }, ], @@ -420,7 +440,7 @@ export function describeBehaviorOfDiamondWritable( [ { target: ethers.constants.AddressZero, - action: 2, + action: FacetCutAction.REMOVE, selectors: [selector], }, ], @@ -466,7 +486,7 @@ export function describeBehaviorOfDiamondWritable( [ { target: ethers.constants.AddressZero, - action: 0, + action: FacetCutAction.ADD, selectors: [], }, ], diff --git a/test/proxy/diamond/SolidStateDiamond.ts b/test/proxy/diamond/SolidStateDiamond.ts index 508e8e41..b9766414 100644 --- a/test/proxy/diamond/SolidStateDiamond.ts +++ b/test/proxy/diamond/SolidStateDiamond.ts @@ -1,4 +1,5 @@ import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; +import { FacetCutAction } from '@solidstate/library'; import { describeBehaviorOfSolidStateDiamond } from '@solidstate/spec'; import { SolidStateDiamond, @@ -30,7 +31,7 @@ describe('SolidStateDiamond', function () { facetCuts[0] = { target: instance.address, - action: 0, + action: FacetCutAction.ADD, selectors: facets[0].selectors, }; }); diff --git a/test/proxy/diamond/base/DiamondBase.ts b/test/proxy/diamond/base/DiamondBase.ts index f317eba6..cf874d68 100644 --- a/test/proxy/diamond/base/DiamondBase.ts +++ b/test/proxy/diamond/base/DiamondBase.ts @@ -1,3 +1,4 @@ +import { FacetCutAction } from '@solidstate/library'; import { describeBehaviorOfDiamondBase } from '@solidstate/spec'; import { DiamondBaseMock, @@ -18,7 +19,7 @@ describe('DiamondBase', function () { instance = await new DiamondBaseMock__factory(deployer).deploy([ { target: facetInstance.address, - action: 0, + action: FacetCutAction.ADD, selectors: [facetInstance.interface.getSighash('owner()')], }, ]); diff --git a/test/proxy/diamond/fallback/DiamondFallback.ts b/test/proxy/diamond/fallback/DiamondFallback.ts index 75404ead..6734f557 100644 --- a/test/proxy/diamond/fallback/DiamondFallback.ts +++ b/test/proxy/diamond/fallback/DiamondFallback.ts @@ -1,4 +1,5 @@ import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; +import { FacetCutAction } from '@solidstate/library'; import { describeBehaviorOfDiamondFallback } from '@solidstate/spec'; import { DiamondFallbackMock, @@ -25,7 +26,7 @@ describe('DiamondFallback', function () { instance = await new DiamondFallbackMock__factory(deployer).deploy([ { target: facetInstance.address, - action: 0, + action: FacetCutAction.ADD, selectors: [facetInstance.interface.getSighash('owner()')], }, ]); diff --git a/test/proxy/diamond/readable/DiamondReadable.ts b/test/proxy/diamond/readable/DiamondReadable.ts index 6c75969a..3e1eb98c 100644 --- a/test/proxy/diamond/readable/DiamondReadable.ts +++ b/test/proxy/diamond/readable/DiamondReadable.ts @@ -1,4 +1,5 @@ import { deployMockContract } from '@ethereum-waffle/mock-contract'; +import { FacetCutAction } from '@solidstate/library'; import { describeBehaviorOfDiamondReadable } from '@solidstate/spec'; import { DiamondReadableMock, @@ -35,7 +36,7 @@ describe('DiamondReadable', function () { facetCuts.push({ target: facet.address, - action: 0, + action: FacetCutAction.ADD, selectors, }); }); From dfac641402a030a1ecf54d2100c730cd824d0f28 Mon Sep 17 00:00:00 2001 From: Nick Barry Date: Tue, 20 Dec 2022 20:22:47 +0200 Subject: [PATCH 17/28] fix dependency versions --- lib/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/package.json b/lib/package.json index 30f6c047..b536e55e 100644 --- a/lib/package.json +++ b/lib/package.json @@ -25,8 +25,8 @@ "tsc-clean": "tsc --build --clean tsconfig.json" }, "dependencies": { - "@ethersproject/constants": "^5.7.1", - "@ethersproject/contracts": "^5.7.1", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/contracts": "^5.7.0", "eth-permit": "^0.1.10" }, "files": [ From cef5a3f119df937b4ac8226720e93e0e98225b49 Mon Sep 17 00:00:00 2001 From: Nick Barry Date: Tue, 20 Dec 2022 22:48:22 +0200 Subject: [PATCH 18/28] reorder declarations --- lib/diamond/utils.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/diamond/utils.ts b/lib/diamond/utils.ts index b8642b4e..c18d3f19 100644 --- a/lib/diamond/utils.ts +++ b/lib/diamond/utils.ts @@ -6,17 +6,17 @@ import { IDiamondWritable, } from '@solidstate/typechain-types'; -export interface Facet { - target: string; - selectors: string[]; -} - export enum FacetCutAction { ADD, REPLACE, REMOVE, } +export interface Facet { + target: string; + selectors: string[]; +} + export interface FacetCut extends Facet { action: FacetCutAction; } From ce286c65c33e693e847c0fdd3e77f34bde8a83ad Mon Sep 17 00:00:00 2001 From: 0xCourtney <23440493+0xCourtney@users.noreply.github.com> Date: Tue, 20 Dec 2022 16:04:24 -0500 Subject: [PATCH 19/28] uses except instead of exclude --- lib/diamond/filters.ts | 24 ++++++++++++------------ lib/diamond/utils.ts | 26 +++++++++++++------------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/lib/diamond/filters.ts b/lib/diamond/filters.ts index 17435cd1..6e69e29d 100644 --- a/lib/diamond/filters.ts +++ b/lib/diamond/filters.ts @@ -5,10 +5,10 @@ export interface FacetFilter { selectors: string[]; } -// returns true if the selector is found in the only or exclude filters +// returns true if the selector is found in the only or except filters export function selectorIsFiltered( only: FacetFilter[], - exclude: FacetFilter[], + except: FacetFilter[], contract: string, selector: string, ): boolean { @@ -17,12 +17,12 @@ export function selectorIsFiltered( return includes(only, contract, selector); } - if (exclude.length > 0) { - // exclude selectors found in exclude, include all others - return !includes(exclude, contract, selector); + if (except.length > 0) { + // exclude selectors found in except, include all others + return !includes(except, contract, selector); } - // if neither only or exclude are used, then include all selectors + // if neither only or except are used, then include all selectors return true; } @@ -41,14 +41,14 @@ export function includes( return false; } -// validates that only and exclude filters do not contain the same contract -export function validateFilters(only: FacetFilter[], exclude: FacetFilter[]) { - if (only.length > 0 && exclude.length > 0) { +// validates that only and except filters do not contain the same contract +export function validateFilters(only: FacetFilter[], except: FacetFilter[]) { + if (only.length > 0 && except.length > 0) { for (const onlyFilter of only) { - for (const excludeFilter of exclude) { - if (onlyFilter.contract === excludeFilter.contract) { + for (const exceptFilter of except) { + if (onlyFilter.contract === exceptFilter.contract) { throw new Error( - 'only and exclude filters cannot contain the same contract', + 'only and except filters cannot contain the same contract', ); } } diff --git a/lib/diamond/utils.ts b/lib/diamond/utils.ts index c18d3f19..5f491586 100644 --- a/lib/diamond/utils.ts +++ b/lib/diamond/utils.ts @@ -61,9 +61,9 @@ export async function addUnregisteredSelectors( diamond: IDiamondReadable, contracts: Contract[], only: FacetFilter[] = [], - exclude: FacetFilter[] = [], + except: FacetFilter[] = [], ): Promise { - validateFilters(only, exclude); + validateFilters(only, except); const diamondFacets: Facet[] = await diamond.facets(); const facets = getFacets(contracts); @@ -80,7 +80,7 @@ export async function addUnregisteredSelectors( target !== diamond.address && selector.length > 0 && !selectorExistsInFacets(selector, diamondFacets) && - selectorIsFiltered(only, exclude, target, selector) + selectorIsFiltered(only, except, target, selector) ) { facetCuts.push( printFacetCuts(facet.target, [selector], FacetCutAction.ADD), @@ -103,9 +103,9 @@ export async function replaceRegisteredSelectors( diamond: IDiamondReadable, contracts: Contract[], only: FacetFilter[] = [], - exclude: FacetFilter[] = [], + except: FacetFilter[] = [], ): Promise { - validateFilters(only, exclude); + validateFilters(only, except); const diamondFacets: Facet[] = await diamond.facets(); const facets = getFacets(contracts); @@ -126,7 +126,7 @@ export async function replaceRegisteredSelectors( target != diamond.address && selector.length > 0 && selectorExistsInFacets(selector, diamondFacets) && - selectorIsFiltered(only, exclude, target, selector) + selectorIsFiltered(only, except, target, selector) ) { facetCuts.push( printFacetCuts(target, [selector], FacetCutAction.REPLACE), @@ -149,9 +149,9 @@ export async function removeRegisteredSelectors( diamond: IDiamondReadable, contracts: Contract[], only: FacetFilter[] = [], - exclude: FacetFilter[] = [], + except: FacetFilter[] = [], ): Promise { - validateFilters(only, exclude); + validateFilters(only, except); const diamondFacets: Facet[] = await diamond.facets(); const facets = getFacets(contracts); @@ -170,7 +170,7 @@ export async function removeRegisteredSelectors( target != diamond.address && selector.length > 0 && !selectorExistsInFacets(selector, facets) && - selectorIsFiltered(only, exclude, AddressZero, selector) + selectorIsFiltered(only, except, AddressZero, selector) ) { facetCuts.push( printFacetCuts(AddressZero, [selector], FacetCutAction.REMOVE), @@ -193,7 +193,7 @@ export async function previewFacetCut( diamond: IDiamondReadable, contracts: Contract[], only: FacetFilter[][] = [[], [], []], - exclude: FacetFilter[][] = [[], [], []], + except: FacetFilter[][] = [[], [], []], ): Promise { let addFacetCuts: FacetCut[] = []; let replaceFacetCuts: FacetCut[] = []; @@ -204,7 +204,7 @@ export async function previewFacetCut( diamond, contracts, only[0], - exclude[0], + except[0], ); } catch (error) { console.log(`WARNING: ${(error as Error).message}`); @@ -215,7 +215,7 @@ export async function previewFacetCut( diamond, contracts, only[1], - exclude[1], + except[1], ); } catch (error) { console.log(`WARNING: ${(error as Error).message}`); @@ -226,7 +226,7 @@ export async function previewFacetCut( diamond, contracts, only[2], - exclude[2], + except[2], ); } catch (error) { console.log(`WARNING: ${(error as Error).message}`); From a2a3c1975434d720755553dbf8041cb375a1b43d Mon Sep 17 00:00:00 2001 From: 0xCourtney <23440493+0xCourtney@users.noreply.github.com> Date: Thu, 22 Dec 2022 11:07:19 -0500 Subject: [PATCH 20/28] Update lib/diamond/utils.ts Co-authored-by: Nick Barry --- lib/diamond/utils.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/diamond/utils.ts b/lib/diamond/utils.ts index 5f491586..d1ddd854 100644 --- a/lib/diamond/utils.ts +++ b/lib/diamond/utils.ts @@ -28,11 +28,7 @@ export function getSignatures(contract: Contract): string[] { // returns a list of selectors for a contract export function getSelectors(contract: Contract): string[] { - const signatures = getSignatures(contract); - return signatures.reduce((acc: string[], val: string) => { - acc.push(contract.interface.getSighash(val)); - return acc; - }, []); + return getSignatures(contract).map((signature) => contract.interface.getSighash(signature)); } // returns a list of Facets for a contract From c7df310be8868598134d6cf70ae4bfa6a3391a2f Mon Sep 17 00:00:00 2001 From: 0xCourtney <23440493+0xCourtney@users.noreply.github.com> Date: Thu, 22 Dec 2022 11:07:34 -0500 Subject: [PATCH 21/28] Update lib/diamond/utils.ts Co-authored-by: Nick Barry --- lib/diamond/utils.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/diamond/utils.ts b/lib/diamond/utils.ts index d1ddd854..590f46f6 100644 --- a/lib/diamond/utils.ts +++ b/lib/diamond/utils.ts @@ -46,10 +46,7 @@ export function selectorExistsInFacets( selector: string, facets: Facet[], ): boolean { - for (const facet of facets) { - if (facet.selectors.includes(selector)) return true; - } - return false; + return facets.some((facet) => facet.selectors.includes(selector)); } // preview FacetCut which adds unregistered selectors From 7d6598e8cdfa2285e71d89f0a4c8971490dde192 Mon Sep 17 00:00:00 2001 From: 0xCourtney <23440493+0xCourtney@users.noreply.github.com> Date: Thu, 22 Dec 2022 11:40:08 -0500 Subject: [PATCH 22/28] Update lib/diamond/filters.ts Co-authored-by: Nick Barry --- lib/diamond/filters.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/lib/diamond/filters.ts b/lib/diamond/filters.ts index 6e69e29d..ad13210c 100644 --- a/lib/diamond/filters.ts +++ b/lib/diamond/filters.ts @@ -32,13 +32,7 @@ export function includes( contract: string, selector: string, ): boolean { - for (const filter of filters) { - if (filter.contract === contract || AddressZero === contract) { - return filter.selectors.includes(selector); - } - } - - return false; + return filters.some((filter) => [filter.contract, AddressZero].includes(contract) && filter.selectors.includes(selector)); } // validates that only and except filters do not contain the same contract From 9d92ea0a7f5d5231717e8a2cd41de95fcb1f70c5 Mon Sep 17 00:00:00 2001 From: 0xCourtney <23440493+0xCourtney@users.noreply.github.com> Date: Thu, 22 Dec 2022 16:49:55 -0500 Subject: [PATCH 23/28] rename --- lib/diamond/utils.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/diamond/utils.ts b/lib/diamond/utils.ts index 590f46f6..960c2615 100644 --- a/lib/diamond/utils.ts +++ b/lib/diamond/utils.ts @@ -22,13 +22,15 @@ export interface FacetCut extends Facet { } // returns a list of signatures for a contract -export function getSignatures(contract: Contract): string[] { +export function getFuntionSignatures(contract: Contract): string[] { return Object.keys(contract.interface.functions); } // returns a list of selectors for a contract export function getSelectors(contract: Contract): string[] { - return getSignatures(contract).map((signature) => contract.interface.getSighash(signature)); + return getFuntionSignatures(contract).map((signature) => + contract.interface.getSighash(signature), + ); } // returns a list of Facets for a contract From a9b12a5c40339753676c382940f439476871da20 Mon Sep 17 00:00:00 2001 From: 0xCourtney <23440493+0xCourtney@users.noreply.github.com> Date: Thu, 22 Dec 2022 16:50:32 -0500 Subject: [PATCH 24/28] updates comments --- lib/diamond/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/diamond/utils.ts b/lib/diamond/utils.ts index 960c2615..53b94d0b 100644 --- a/lib/diamond/utils.ts +++ b/lib/diamond/utils.ts @@ -21,7 +21,7 @@ export interface FacetCut extends Facet { action: FacetCutAction; } -// returns a list of signatures for a contract +// returns a list of function signatures for a contract export function getFuntionSignatures(contract: Contract): string[] { return Object.keys(contract.interface.functions); } From f4c211b7883346956d256edcaac04fa951251fdc Mon Sep 17 00:00:00 2001 From: 0xCourtney <23440493+0xCourtney@users.noreply.github.com> Date: Thu, 22 Dec 2022 16:54:44 -0500 Subject: [PATCH 25/28] rename --- lib/diamond/utils.ts | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/lib/diamond/utils.ts b/lib/diamond/utils.ts index 53b94d0b..1522ecef 100644 --- a/lib/diamond/utils.ts +++ b/lib/diamond/utils.ts @@ -43,6 +43,19 @@ export function getFacets(contracts: Contract[]): Facet[] { }); } +// returns a FacetCut +export function getFacetCut( + target: string, + selectors: string[], + action: number = 0, +): FacetCut { + return { + target: target, + action: action, + selectors: selectors, + }; +} + // returns true if the selector is found in the facets export function selectorExistsInFacets( selector: string, @@ -78,7 +91,7 @@ export async function addUnregisteredSelectors( selectorIsFiltered(only, except, target, selector) ) { facetCuts.push( - printFacetCuts(facet.target, [selector], FacetCutAction.ADD), + getFacetCut(facet.target, [selector], FacetCutAction.ADD), ); selectorsAdded = true; @@ -123,9 +136,7 @@ export async function replaceRegisteredSelectors( selectorExistsInFacets(selector, diamondFacets) && selectorIsFiltered(only, except, target, selector) ) { - facetCuts.push( - printFacetCuts(target, [selector], FacetCutAction.REPLACE), - ); + facetCuts.push(getFacetCut(target, [selector], FacetCutAction.REPLACE)); selectorsReplaced = true; } @@ -168,7 +179,7 @@ export async function removeRegisteredSelectors( selectorIsFiltered(only, except, AddressZero, selector) ) { facetCuts.push( - printFacetCuts(AddressZero, [selector], FacetCutAction.REMOVE), + getFacetCut(AddressZero, [selector], FacetCutAction.REMOVE), ); selectorsRemoved = true; @@ -286,15 +297,3 @@ export function groupFacetCuts(facetCuts: FacetCut[]): FacetCut[] { return cuts; } - -export function printFacetCuts( - target: string, - selectors: string[], - action: number = 0, -): FacetCut { - return { - target: target, - action: action, - selectors: selectors, - }; -} From dbf11d58067b3f5b081ef0d26414905f825fc4c1 Mon Sep 17 00:00:00 2001 From: 0xCourtney <23440493+0xCourtney@users.noreply.github.com> Date: Thu, 22 Dec 2022 17:05:44 -0500 Subject: [PATCH 26/28] typo --- lib/diamond/utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/diamond/utils.ts b/lib/diamond/utils.ts index 1522ecef..99d9324d 100644 --- a/lib/diamond/utils.ts +++ b/lib/diamond/utils.ts @@ -22,13 +22,13 @@ export interface FacetCut extends Facet { } // returns a list of function signatures for a contract -export function getFuntionSignatures(contract: Contract): string[] { +export function getFunctionSignatures(contract: Contract): string[] { return Object.keys(contract.interface.functions); } // returns a list of selectors for a contract export function getSelectors(contract: Contract): string[] { - return getFuntionSignatures(contract).map((signature) => + return getFunctionSignatures(contract).map((signature) => contract.interface.getSighash(signature), ); } From 871f8686765295d69347c3971a6517ad8c35d20d Mon Sep 17 00:00:00 2001 From: 0xCourtney <23440493+0xCourtney@users.noreply.github.com> Date: Thu, 22 Dec 2022 17:06:26 -0500 Subject: [PATCH 27/28] diamonCut returns receipt --- lib/diamond/utils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/diamond/utils.ts b/lib/diamond/utils.ts index 99d9324d..0fb56d54 100644 --- a/lib/diamond/utils.ts +++ b/lib/diamond/utils.ts @@ -1,6 +1,6 @@ import { FacetFilter, selectorIsFiltered, validateFilters } from './filters'; import { AddressZero } from '@ethersproject/constants'; -import { Contract } from '@ethersproject/contracts'; +import { Contract, ContractReceipt } from '@ethersproject/contracts'; import { IDiamondReadable, IDiamondWritable, @@ -251,8 +251,8 @@ export async function diamondCut( facetCut: FacetCut[], target: string = AddressZero, data: string = '0x', -) { - (await diamond.diamondCut(facetCut, target, data)).wait(1); +): Promise { + return (await diamond.diamondCut(facetCut, target, data)).wait(); } // groups facet cuts by target address and action type From 54da442a2170c070c2403b7f395faf573b038120 Mon Sep 17 00:00:00 2001 From: 0xCourtney <23440493+0xCourtney@users.noreply.github.com> Date: Thu, 22 Dec 2022 18:15:41 -0500 Subject: [PATCH 28/28] consolidates only and execpt to single array, adds type and action to FacetFilter --- lib/diamond/filters.ts | 36 +++++++++++++++++++++++++++++------- lib/diamond/utils.ts | 35 ++++++++++++++++------------------- 2 files changed, 45 insertions(+), 26 deletions(-) diff --git a/lib/diamond/filters.ts b/lib/diamond/filters.ts index ad13210c..facaa3c0 100644 --- a/lib/diamond/filters.ts +++ b/lib/diamond/filters.ts @@ -1,6 +1,9 @@ +import { FacetCutAction } from './utils'; import { AddressZero } from '@ethersproject/constants'; export interface FacetFilter { + type: string; + action: FacetCutAction; contract: string; selectors: string[]; } @@ -32,20 +35,39 @@ export function includes( contract: string, selector: string, ): boolean { - return filters.some((filter) => [filter.contract, AddressZero].includes(contract) && filter.selectors.includes(selector)); + return filters.some( + (filter) => + [filter.contract, AddressZero].includes(contract) && + filter.selectors.includes(selector), + ); } -// validates that only and except filters do not contain the same contract +// validates that different filter types do not contain the same contract export function validateFilters(only: FacetFilter[], except: FacetFilter[]) { if (only.length > 0 && except.length > 0) { - for (const onlyFilter of only) { - for (const exceptFilter of except) { - if (onlyFilter.contract === exceptFilter.contract) { + only.forEach((o) => { + except.forEach((e) => { + if (o.contract === e.contract) { throw new Error( 'only and except filters cannot contain the same contract', ); } - } - } + }); + }); } } + +export function destructureFilters( + filters: FacetFilter[], + action?: FacetCutAction, +) { + const only = filters.filter((f) => f.type === 'only'); + const except = filters.filter((f) => f.type === 'except'); + + if (action !== undefined) { + only.filter((f) => f.action === action); + except.filter((f) => f.action === action); + } + + return { only, except }; +} diff --git a/lib/diamond/utils.ts b/lib/diamond/utils.ts index 0fb56d54..284b651e 100644 --- a/lib/diamond/utils.ts +++ b/lib/diamond/utils.ts @@ -1,4 +1,9 @@ -import { FacetFilter, selectorIsFiltered, validateFilters } from './filters'; +import { + FacetFilter, + destructureFilters, + selectorIsFiltered, + validateFilters, +} from './filters'; import { AddressZero } from '@ethersproject/constants'; import { Contract, ContractReceipt } from '@ethersproject/contracts'; import { @@ -68,9 +73,9 @@ export function selectorExistsInFacets( export async function addUnregisteredSelectors( diamond: IDiamondReadable, contracts: Contract[], - only: FacetFilter[] = [], - except: FacetFilter[] = [], + filters: FacetFilter[] = [], ): Promise { + const { only, except } = destructureFilters(filters, FacetCutAction.ADD); validateFilters(only, except); const diamondFacets: Facet[] = await diamond.facets(); @@ -110,9 +115,9 @@ export async function addUnregisteredSelectors( export async function replaceRegisteredSelectors( diamond: IDiamondReadable, contracts: Contract[], - only: FacetFilter[] = [], - except: FacetFilter[] = [], + filters: FacetFilter[] = [], ): Promise { + const { only, except } = destructureFilters(filters, FacetCutAction.REPLACE); validateFilters(only, except); const diamondFacets: Facet[] = await diamond.facets(); @@ -154,9 +159,9 @@ export async function replaceRegisteredSelectors( export async function removeRegisteredSelectors( diamond: IDiamondReadable, contracts: Contract[], - only: FacetFilter[] = [], - except: FacetFilter[] = [], + filters: FacetFilter[] = [], ): Promise { + const { only, except } = destructureFilters(filters, FacetCutAction.REMOVE); validateFilters(only, except); const diamondFacets: Facet[] = await diamond.facets(); @@ -198,20 +203,14 @@ export async function removeRegisteredSelectors( export async function previewFacetCut( diamond: IDiamondReadable, contracts: Contract[], - only: FacetFilter[][] = [[], [], []], - except: FacetFilter[][] = [[], [], []], + filters: FacetFilter[] = [], ): Promise { let addFacetCuts: FacetCut[] = []; let replaceFacetCuts: FacetCut[] = []; let removeFacetCuts: FacetCut[] = []; try { - addFacetCuts = await addUnregisteredSelectors( - diamond, - contracts, - only[0], - except[0], - ); + addFacetCuts = await addUnregisteredSelectors(diamond, contracts, filters); } catch (error) { console.log(`WARNING: ${(error as Error).message}`); } @@ -220,8 +219,7 @@ export async function previewFacetCut( replaceFacetCuts = await replaceRegisteredSelectors( diamond, contracts, - only[1], - except[1], + filters, ); } catch (error) { console.log(`WARNING: ${(error as Error).message}`); @@ -231,8 +229,7 @@ export async function previewFacetCut( removeFacetCuts = await removeRegisteredSelectors( diamond, contracts, - only[2], - except[2], + filters, ); } catch (error) { console.log(`WARNING: ${(error as Error).message}`);