From 5b44dfb31ce6ac591dd91d3979ebd74580854d12 Mon Sep 17 00:00:00 2001 From: Victor Galkin Date: Sun, 26 Mar 2023 04:49:01 +0300 Subject: [PATCH 1/5] Add test for FacetRegistry --- backend/contracts/FacetRegistry.sol | 8 +++++ backend/test/kimberliteTest.ts | 48 +++++++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/backend/contracts/FacetRegistry.sol b/backend/contracts/FacetRegistry.sol index 80b8973..c624f3e 100644 --- a/backend/contracts/FacetRegistry.sol +++ b/backend/contracts/FacetRegistry.sol @@ -80,4 +80,12 @@ contract FacetRegistry is ERC1155, IRegistry { return baseTokenDiamond + uint256(uint160(diamondAddress)); } + function facetAddressToTokenId(address facetAddress) external pure returns(uint256){ + return _facetAddressToTokenId(facetAddress); + } + + function diamondAddressToTokenId(address diamondAddress) external pure returns(uint256){ + return _diamondAddressToTokenId(diamondAddress); + } + } diff --git a/backend/test/kimberliteTest.ts b/backend/test/kimberliteTest.ts index 3e6aba7..05f2edd 100644 --- a/backend/test/kimberliteTest.ts +++ b/backend/test/kimberliteTest.ts @@ -1,9 +1,11 @@ import {DiamondLoupeFacet, FacetRegistry, Kimberlite} from "../typechain-types"; import {ethers} from "hardhat"; -const {deployKimberlite, deployRegistry} = require('../scripts/deploy.ts') +import { getSelectors, FacetCutAction, removeSelectors, findAddressPositionInFacets } from 'diamond-1-hardhat/scripts/libraries/diamond.js' -const {assert} = require('chai') +import { deployKimberlite, deployRegistry } from '../scripts/deploy.ts'; + +import { assert } from 'chai'; describe('KimerliteTest', async function () { let registry: FacetRegistry @@ -18,6 +20,48 @@ describe('KimerliteTest', async function () { kimberlite = await deployKimberlite(registry) }) + it('should register facet', async () => { + console.log("Extracting diamond") + const tx = await kimberlite.extractDiamond("metadata URI", {gasLimit: 800000}); + const receipt = await tx.wait() + diamondAddress = receipt.events![3].args!.diamond + const tokenId = await registry.diamondAddressToTokenId(diamondAddress); + console.log({tokenId}); + const [owner] = await ethers.getSigners(); + assert.equal(1, (await registry.balanceOf(owner.address, tokenId)).toNumber()); + } + ) + + + it('should init storage on cut', async () => { + const diamondCutFacet = await ethers.getContractAt('DiamondCutFacet', diamondAddress) + let action = FacetCutAction.Add; + for (const FacetName of ["TrivialCharactersSystem", "SimpleCharactersSystem"]) { + const Facet = await ethers.getContractFactory(FacetName); + const facet = await Facet.deploy(); + await facet.deployed(); + console.log(`${FacetName} deployed: ${facet.address}`); + const facetCuts = [{ + facetAddress: facet.address, + action, + functionSelectors: getSelectors(facet).get(["whatIs(uint256)", "isAlive(uint256 id)"]), + }] + console.log({ facetCuts }); + let functionCall = Facet.interface.encodeFunctionData('init'); + const tx = await diamondCutFacet.diamondCut( + facetCuts, + facet.address, + functionCall, + { gasLimit: 800000 } + ); + const receipt = await tx.wait(); + if (!receipt.status) { + throw Error(`Diamond upgrade failed: ${tx.hash}`); + } + action = FacetCutAction.Replace; + } + }) + it('should extract diamond', async () => { console.log("Extracting diamond") const tx = await kimberlite.extractDiamond("metadata URI", {gasLimit: 800000}); From 56d747a267a3f4f2f1f77c788016bcb38de66217 Mon Sep 17 00:00:00 2001 From: Victor Galkin Date: Sun, 26 Mar 2023 04:58:44 +0300 Subject: [PATCH 2/5] Verify utility contracts on deploy --- backend/scripts/deploy.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/backend/scripts/deploy.ts b/backend/scripts/deploy.ts index 9d2035c..a538920 100644 --- a/backend/scripts/deploy.ts +++ b/backend/scripts/deploy.ts @@ -1,7 +1,18 @@ -import { ethers } from "hardhat"; +import hre, { ethers } from "hardhat"; import { getSelectors, FacetCutAction } from 'diamond-1-hardhat/scripts/libraries/diamond.js'; import { FacetRegistry, Kimberlite } from "../typechain-types"; + +async function verify(address:string, constructorArguments:any[]) { + if (hre.network.name == 'hardhat') { + return + } + await hre.run("verify:verify", { + address, + constructorArguments + }); +} + async function deployKimberlite(facetRegistry: FacetRegistry): Promise { console.log('Deploying facets') @@ -16,6 +27,7 @@ async function deployKimberlite(facetRegistry: FacetRegistry): Promise { console.log('Deploying FacetRegistry') const registry = await Registry.deploy() console.log(`FacetRegistry deployed to ${registry.address}`); + await verify(registry.address, []) return registry } From bfabdd13e905286b3b697786a59c3ac0b843179f Mon Sep 17 00:00:00 2001 From: Victor Galkin Date: Sun, 26 Mar 2023 13:53:48 +0300 Subject: [PATCH 3/5] Fix naming in deploy facet sample --- backend/scripts/facets/deploy.ts | 2 +- backend/scripts/facets/registry_metadata_sample.json | 2 +- backend/test/diamondTest.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/scripts/facets/deploy.ts b/backend/scripts/facets/deploy.ts index 7bf83f7..6bcc083 100644 --- a/backend/scripts/facets/deploy.ts +++ b/backend/scripts/facets/deploy.ts @@ -6,7 +6,7 @@ import Arweave from 'arweave'; //import jwk_data from './../../cache/arweave-keyfile.json' const groupName = "Characters" -const systemName = "TrivialCharacterSystem" +const systemName = "TrivialCharactersSystem" const storageKey = "character.storage" const storageContents = `struct AliveState { mapping(uint256 => bool) alive; diff --git a/backend/scripts/facets/registry_metadata_sample.json b/backend/scripts/facets/registry_metadata_sample.json index 1a8965a..9de5312 100644 --- a/backend/scripts/facets/registry_metadata_sample.json +++ b/backend/scripts/facets/registry_metadata_sample.json @@ -4,6 +4,6 @@ "storage_slots": { "character.storage": "struct AliveState {\n mapping(uint256 => bool) alive;\n}" }, - "name": "TrivialCharacterSystem", + "name": "TrivialCharactersSystem", "group": "Characters" } diff --git a/backend/test/diamondTest.ts b/backend/test/diamondTest.ts index b89c5d7..da8a827 100644 --- a/backend/test/diamondTest.ts +++ b/backend/test/diamondTest.ts @@ -7,7 +7,7 @@ import { deployDiamond } from 'diamond-1-hardhat/scripts/deploy.js' import { assert } from 'chai' import { ethers } from 'hardhat' -import { DiamondCutFacet, DiamondLoupeFacet, OwnershipFacet, TrivialCharacterSystem } from '../typechain-types' +import { DiamondCutFacet, DiamondLoupeFacet, OwnershipFacet, TrivialCharactersSystem } from '../typechain-types' describe('DiamondTest', async function () { From f86af2c9b66ec56b44390c5ad84a0c6db48c5390 Mon Sep 17 00:00:00 2001 From: Victor Galkin Date: Sun, 26 Mar 2023 14:53:39 +0300 Subject: [PATCH 4/5] Verify facet on deploy. Customize network for arveawe --- backend/scripts/deploy.ts | 14 ++------------ backend/scripts/facets/deploy.ts | 18 ++++++++++-------- backend/scripts/verify.ts | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 20 deletions(-) create mode 100644 backend/scripts/verify.ts diff --git a/backend/scripts/deploy.ts b/backend/scripts/deploy.ts index a538920..96d31ae 100644 --- a/backend/scripts/deploy.ts +++ b/backend/scripts/deploy.ts @@ -1,17 +1,7 @@ -import hre, { ethers } from "hardhat"; +import { ethers } from "hardhat"; import { getSelectors, FacetCutAction } from 'diamond-1-hardhat/scripts/libraries/diamond.js'; import { FacetRegistry, Kimberlite } from "../typechain-types"; - - -async function verify(address:string, constructorArguments:any[]) { - if (hre.network.name == 'hardhat') { - return - } - await hre.run("verify:verify", { - address, - constructorArguments - }); -} +import { verify } from "./verify"; async function deployKimberlite(facetRegistry: FacetRegistry): Promise { diff --git a/backend/scripts/facets/deploy.ts b/backend/scripts/facets/deploy.ts index 6bcc083..12f4dde 100644 --- a/backend/scripts/facets/deploy.ts +++ b/backend/scripts/facets/deploy.ts @@ -3,6 +3,7 @@ import { BaseContract } from "ethers"; import fs from 'fs/promises'; import path from 'path'; import Arweave from 'arweave'; +import { verify, getEtherscanEndpoint, getEtherscanEndpointOrPolygon } from "../verify"; //import jwk_data from './../../cache/arweave-keyfile.json' const groupName = "Characters" @@ -26,23 +27,22 @@ const storageContents = `struct AliveState { async function deploySystem(systemName: string): Promise { - // 1. Deploy contract with ethers and hardhat + + const System = await ethers.getContractFactory(systemName) const system = await System.deploy() console.log(`${systemName} deployed to ${system.address}, ${Object.keys(system)}`); + await verify(system.address, []) - // TODO determine the network - - - - - + const etherScanEndpoint = await getEtherscanEndpointOrPolygon() + const explorer_url = `${etherScanEndpoint.urls.browserURL}/address/${system.address}#code` + // 2. the resulting metadata of deployed contract is saved locally let resultJson = { address: system.address, - explorer_url : `https://polygonscan.com/address/${system.address}#code`, + explorer_url, name: systemName, group: groupName, [storageKey]: storageContents @@ -95,6 +95,8 @@ async function deploySystem(systemName: string): Promise { } //TODO get public url of result + console.log(`https://viewblock.io/arweave/tx/${transactionA.id}`); + //TODO await uploaded return system } diff --git a/backend/scripts/verify.ts b/backend/scripts/verify.ts new file mode 100644 index 0000000..6e99e40 --- /dev/null +++ b/backend/scripts/verify.ts @@ -0,0 +1,32 @@ +import hre from "hardhat" + +export async function verify(address: string, constructorArguments: any[]) { + console.log("Network name", hre.network.name) + if (hre.network.name == 'hardhat') { + return + } + await hre.run("verify:verify", { + address, + constructorArguments + }); +} + +export async function getEtherscanEndpoint() { + if (hre.network.name == 'hardhat') { + return + } + return await hre.run("verify:get-etherscan-endpoint", {}); +} + +export async function getEtherscanEndpointOrPolygon() { + return (await getEtherscanEndpoint()) || { + network: 'polygon', + urls: { + apiURL: 'https://api.polygonscan.com/api', + browserURL: 'https://polygonscan.com' + } + } +} + + +// verify:get-etherscan-endpoint \ No newline at end of file From bd0b366b1e4e348207ec37dfb6e0b5ca9f801f5e Mon Sep 17 00:00:00 2001 From: Victor Galkin Date: Sun, 26 Mar 2023 18:33:59 +0300 Subject: [PATCH 5/5] Facet with metadata --- .../contracts/Diamond.sol | 23 ++++++++++- backend/contracts/FacetRegistry.sol | 1 + backend/scripts/deploy.ts | 7 +--- backend/scripts/extract.ts | 41 ++++++++++++++++--- backend/scripts/facets/deploy.ts | 40 ++++++++++++++---- 5 files changed, 92 insertions(+), 20 deletions(-) diff --git a/EIP2535-Diamonds-Reference-Implementation/contracts/Diamond.sol b/EIP2535-Diamonds-Reference-Implementation/contracts/Diamond.sol index d663b8d..f0c4388 100644 --- a/EIP2535-Diamonds-Reference-Implementation/contracts/Diamond.sol +++ b/EIP2535-Diamonds-Reference-Implementation/contracts/Diamond.sol @@ -12,6 +12,7 @@ import { LibDiamond } from "./libraries/LibDiamond.sol"; import { IDiamondCut } from "./interfaces/IDiamondCut.sol"; import { IDiamondLoupe } from "./interfaces/IDiamondLoupe.sol"; import { IERC173 } from "./interfaces/IERC173.sol"; +import "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Receiver.sol"; // When no function exists for function called error FunctionNotFound(bytes4 _functionSelector); @@ -25,7 +26,7 @@ struct DiamondArgs { bytes initCalldata; } -contract Diamond { +contract Diamond is ERC1155Receiver { constructor(IDiamondCut.FacetCut[] memory _diamondCut, DiamondArgs memory _args) payable { LibDiamond.setContractOwner(_args.owner); @@ -68,4 +69,24 @@ contract Diamond { } receive() external payable {} + + function onERC1155Received( + address operator, + address from, + uint256 id, + uint256 value, + bytes calldata data + ) external returns (bytes4) { + return this.onERC1155Received.selector; + } + + function onERC1155BatchReceived( + address operator, + address from, + uint256[] calldata ids, + uint256[] calldata values, + bytes calldata data + ) external returns (bytes4) { + return this.onERC1155BatchReceived.selector; + } } diff --git a/backend/contracts/FacetRegistry.sol b/backend/contracts/FacetRegistry.sol index 046b4e7..e1437f1 100644 --- a/backend/contracts/FacetRegistry.sol +++ b/backend/contracts/FacetRegistry.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; +import "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Receiver.sol"; import "./interfaces/IRegistry.sol"; contract FacetRegistry is ERC1155, IRegistry { diff --git a/backend/scripts/deploy.ts b/backend/scripts/deploy.ts index 02b9534..a95e218 100644 --- a/backend/scripts/deploy.ts +++ b/backend/scripts/deploy.ts @@ -3,7 +3,7 @@ import { getSelectors, FacetCutAction } from 'diamond-1-hardhat/scripts/librarie import { FacetRegistry, Kimberlite } from "../typechain-types"; import { verify } from "./verify"; -async function deployKimberlite(facetRegistry: FacetRegistry): Promise { +export async function deployKimberlite(facetRegistry: FacetRegistry): Promise { console.log('Deploying facets') // The `facetCuts` variable is the FacetCut[] that contains the functions to add during diamond deployment @@ -51,7 +51,7 @@ async function deployKimberlite(facetRegistry: FacetRegistry): Promise { +export async function deployRegistry(): Promise { const Registry = await ethers.getContractFactory("FacetRegistry") console.log('Deploying FacetRegistry') const registry = await Registry.deploy() @@ -77,6 +77,3 @@ if (require.main === module) { }) } - -exports.deployKimberlite = deployKimberlite -exports.deployRegistry = deployRegistry \ No newline at end of file diff --git a/backend/scripts/extract.ts b/backend/scripts/extract.ts index 0415073..4208d3a 100644 --- a/backend/scripts/extract.ts +++ b/backend/scripts/extract.ts @@ -1,10 +1,39 @@ -import { ethers } from "hardhat"; -async function extract(kimberliteAddress :string) { +import hre, { ethers } from "hardhat"; +import { deployKimberlite, deployRegistry } from "./deploy"; - console.log('Extract') +export async function getKimberlite() { + if (hre.network.name == "hardhat") { + const facetRegistry = await deployRegistry() + return deployKimberlite(facetRegistry) + } const factory = await ethers.getContractFactory("Kimberlite") - const contract = await factory.attach(kimberliteAddress) - await contract.extractDiamond("https://arweave.net/5S1NrCPfLJX7FgRx78kV8nDJCnwFkWRKSc18pDdD4Sw") + if (hre.network.name == "polygon") { + return await factory.attach("0xf78b989D3cF27EFc309887501a749fE2aEAAA277") + } + if (hre.network.name == "mantle_testnet") { + return await factory.attach("0x05c7df69BA4Be0F6483F8778606E7253Bc2254A4") + } + throw "Unknown network" +} + +export async function extract() { + console.log('Extract') + const kimberlite = await getKimberlite(); + const tx = await kimberlite.extractDiamond("https://arweave.net/5S1NrCPfLJX7FgRx78kV8nDJCnwFkWRKSc18pDdD4Sw") + const receipt = await tx.wait() + const diamondAddress = receipt.events![3].args!.diamond + console.log("Extracted diamond", diamondAddress); + return diamondAddress; } -extract("0x306DD94AdEc5D88383065C237Ed1958687Be4daf") +if (require.main === module) { + extract() + .then(() => process.exit(0)) + .catch(error => { + console.error(error) + process.exit(1) + }) + +} + + diff --git a/backend/scripts/facets/deploy.ts b/backend/scripts/facets/deploy.ts index 12f4dde..4170978 100644 --- a/backend/scripts/facets/deploy.ts +++ b/backend/scripts/facets/deploy.ts @@ -4,12 +4,15 @@ import fs from 'fs/promises'; import path from 'path'; import Arweave from 'arweave'; import { verify, getEtherscanEndpoint, getEtherscanEndpointOrPolygon } from "../verify"; +import { deployRegistry } from "../deploy" //import jwk_data from './../../cache/arweave-keyfile.json' +import hre from "hardhat" +import { extract } from "../extract"; const groupName = "Characters" const systemName = "TrivialCharactersSystem" const storageKey = "character.storage" -const storageContents = `struct AliveState { +const storageContents = `struct AliveState { mapping(uint256 => bool) alive; } @@ -25,11 +28,24 @@ const storageContents = `struct AliveState { return state.alive[id]; }` +async function getFacetRegistry() { + if (hre.network.name == "hardhat") { + return deployRegistry() + } + const factory = await ethers.getContractFactory("FacetRegistry") + if (hre.network.name == "polygon") { + return await factory.attach("0xE056F64f06e8D0F7Cd2a9501138D3aFa0eF3FF32") + } + if (hre.network.name == "mantle_testnet") { + return await factory.attach("0x56E427509b7dca569E8F20BE2F30B0206AB6289b") + } + throw "Unknown network" +} async function deploySystem(systemName: string): Promise { // 1. Deploy contract with ethers and hardhat - + const facetRegistry = await getFacetRegistry() const System = await ethers.getContractFactory(systemName) const system = await System.deploy() @@ -37,8 +53,8 @@ async function deploySystem(systemName: string): Promise { await verify(system.address, []) const etherScanEndpoint = await getEtherscanEndpointOrPolygon() - const explorer_url = `${etherScanEndpoint.urls.browserURL}/address/${system.address}#code` - + const explorer_url = `${etherScanEndpoint.urls.browserURL}/address/${system.address}#code` + // 2. the resulting metadata of deployed contract is saved locally let resultJson = { address: system.address, @@ -57,10 +73,10 @@ async function deploySystem(systemName: string): Promise { - + // 3. the resulting metadata of deployed contract is saved to public decentralized storage // here we've got arweave - + // NB! This is an example of saving meta to decentralized storage. // To make this demo working, we've put a real jwk file into project // Please, make your own arweave wallet and jwk file @@ -81,7 +97,7 @@ async function deploySystem(systemName: string): Promise { let transactionA = await arweave.createTransaction({ - data: JSON.stringify(resultJson, null, 4) + data: JSON.stringify(resultJson, null, 4) }, jwk_data); await arweave.transactions.sign(transactionA, jwk_data); @@ -97,7 +113,15 @@ async function deploySystem(systemName: string): Promise { //TODO get public url of result console.log(`https://viewblock.io/arweave/tx/${transactionA.id}`); //TODO await uploaded - + + //Replace with diamond address + + const diamondAddress = await extract(); + console.log('dddd', diamondAddress); + const tx = await facetRegistry.mintFacet(system.address, diamondAddress) + const receipt = await tx.wait() + console.log(receipt) + return system }