Last active
June 3, 2024 21:27
-
-
Save Yuripetusko/e995b199aee8d12f000dd9ff22d8ae5d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import fs from 'fs'; | |
const fsPromises = fs.promises; | |
export const getOrCreateNdjosnLogFile = async <T>(path: string) => { | |
let response: T[] = []; | |
if (fs.existsSync(path)) { | |
const rawResponse = fs.readFileSync(path, 'utf-8'); | |
if (rawResponse.length !== 0) { | |
response = rawResponse | |
.toString() | |
.trim() | |
.split('\n') | |
.map((s) => JSON.parse(s)); | |
} | |
} else { | |
// Init empty log | |
await fsPromises.writeFile(path, ''); | |
} | |
return response; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import fs from 'fs'; | |
const fsPromises = fs.promises; | |
export const mintinResultFilePaths = [ | |
`${process.cwd()}/scripts/data/minting-results/minting-status.ndjson`, | |
`${process.cwd()}/scripts/data/minting-results/minted-gems-kusama-to-evm-mapping.ndjson`, | |
`${process.cwd()}/scripts/data/minting-results/minted-items-other-kusama-to-evm-mapping.ndjson`, | |
`${process.cwd()}/scripts/data/minting-results/minted-items-banners-kusama-to-evm-mapping.ndjson`, | |
`${process.cwd()}/scripts/data/minting-results/minted-items-khala-kusama-to-evm-mapping.ndjson`, | |
`${process.cwd()}/scripts/data/minting-results/minted-birds-kusama-to-evm-mapping.ndjson`, | |
`${process.cwd()}/scripts/data/minting-results/existing-gem-assets.ndjson`, | |
`${process.cwd()}/scripts/data/minting-results/existing-item-assets.ndjson`, | |
`${process.cwd()}/scripts/data/minting-results/minted-gems-kusama-to-evm-mapping.ndjson`, | |
`${process.cwd()}/scripts/data/minting-results/debugging-gems.ndjson`, | |
]; | |
export const initMintingResultsFiles = async () => { | |
try { | |
for (const filePath of mintinResultFilePaths) { | |
const fileExists = fs.existsSync(filePath); | |
if (!fileExists) { | |
await fsPromises.writeFile(filePath, ''); | |
} | |
} | |
} catch (error) { | |
console.log('Error', error); | |
process.exit(1); | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const fsPromises = fs.promises; | |
type AddedAssetEntryMapping = Record< | |
string, | |
Record< | |
string, | |
{ | |
slotId: string | null; | |
assetMetadata: string; | |
nftIds: string[]; | |
evmAssetId: BigNumber; | |
} | |
> | |
>; | |
export const mintKanariaItemsByType = async ({ | |
deployedCollections, | |
mintedResultsLogPath, | |
addedAssetsResultsLogPath, | |
kusamaItemsDataPath, | |
}: { | |
deployedCollections: DeployedCollectionsReturnType; | |
mintedResultsLogPath: string; | |
addedAssetsResultsLogPath: string; | |
kusamaItemsDataPath: string; | |
}) => { | |
try { | |
const accounts: SignerWithAddress[] = await ethers.getSigners(); | |
const signer = accounts[0]; | |
const deployerAddress = signer.address; | |
const accumulatorAddress = | |
process.env.ACCUMULATOR_ADDRESS !== undefined | |
? process.env.ACCUMULATOR_ADDRESS | |
: deployerAddress; | |
// Get minted items data, in case this script was already run and we are resuming | |
const alreadyMintedItems = await getOrCreateNdjosnLogFile<PremintedToken>(mintedResultsLogPath); | |
// Get added items assets entries data from previous step | |
const addedItemsAssetsEntriesResponse = await fsPromises.readFile(addedAssetsResultsLogPath); | |
const addedItemsAssetsEntries: AddedAssetEntryMapping = JSON.parse( | |
addedItemsAssetsEntriesResponse.toString(), | |
); | |
// Get original Kusama Items Data | |
const kusamaItemsDataResponse = await fsPromises.readFile(kusamaItemsDataPath); | |
const kusamaItemsDataArray: NftDataWithMetadata[] = JSON.parse( | |
kusamaItemsDataResponse.toString(), | |
); | |
const itemsCollections = deployedCollections.kanariaItems; | |
// Get kusama ids of nfts that were already minted on EVM | |
const alreadyMintedNftIds = alreadyMintedItems.map((n) => n.kusamaId); | |
// Filter Kusama gems, by those that were not yet minted on evm, also validate that its owner field which is a Kanaria id, was minted on EVM | |
const unmintedNfts = kusamaItemsDataArray.filter( | |
(n) => | |
!alreadyMintedNftIds.includes(n.id) && | |
!!n.collectionId && | |
KANARIA_ITEMS_COLLECTIONS.includes(n.collectionId), | |
); | |
// Group all unminted nfts by collection id, because when we use batchMint function, we need to do it per collection | |
const unmintedNftsGroupedByCollection = groupBy((nft) => nft.collectionId, unmintedNfts); | |
// Get collection ids of yet to be minted grouped nfts, for easier iteration | |
const collectionIds: (keyof typeof unmintedNftsGroupedByCollection)[] = Object.keys( | |
unmintedNftsGroupedByCollection, | |
); | |
// We need to do batch mints in a context of a single collection | |
for (const collectionId of collectionIds) { | |
const collectionNfts = unmintedNftsGroupedByCollection[collectionId] || []; | |
// Batch collection nfts to be minted into chunks of 30, as contract might throw if we try to mint more than 20 at once | |
const batches = chunkArray(collectionNfts, 30); | |
// Find EVM address of deployed contract from previous step, by kusama collection id | |
const contractAddress = itemsCollections.find( | |
(g) => g.kusamaCollectionId === collectionId, | |
)?.address; | |
if (!contractAddress) { | |
console.error( | |
`Something went wrong! Contract address for collection ${collectionId} not found in deployed array`, | |
itemsCollections, | |
); | |
throw new Error( | |
`Contract address for collection ${collectionId} not found in deployed array`, | |
); | |
} | |
// Initialise contract instance for the collection in scope | |
const nftsContract = KanariaItems__factory.connect( | |
contractAddress, | |
(await ethers.getSigners())[0], | |
); | |
for (const batch of batches) { | |
const batchAssetIds = batch.map((k) => { | |
// Go through all pre-added asset entries mapping, and return asset ids that are meant for each nft in a batch | |
return Object.values(addedItemsAssetsEntries[contractAddress]) | |
.map((a) => (a.nftIds.includes(k.id) ? a.evmAssetId : null)) | |
.filter((a): a is BigNumber => a !== null); | |
}); | |
if (batchAssetIds.length !== batch.length) { | |
throw new Error(`Something went wrong. batchAssetIds and batch length mismatch`); | |
} | |
console.log( | |
`Minting Items batch from collection ${collectionId}. ${batch.length} Items into accumulator account ${accumulatorAddress}`, | |
); | |
const tx = await nftsContract.batchMintWithAssets(accumulatorAddress, batchAssetIds); | |
const transactionReceipt = await tx.wait(); | |
// Extract token ids of minted nfts from transaction receipt | |
const mintedTokenIds: BigNumber[] = uniq( | |
transactionReceipt.events | |
?.filter((event) => event.event === 'NestTransfer') | |
?.map((event) => event.args?.tokenId || null) | |
.flat() | |
.filter((r) => r !== null) || [], | |
); | |
if (mintedTokenIds.length !== batch.length) { | |
throw new Error(`Something went wrong. mintedTokenIds and batch length mismatch`); | |
} | |
console.log( | |
`Minted batch. found ${mintedTokenIds.length} minted token ids in tx receipt: `, | |
mintedTokenIds.map((t) => t.toNumber()), | |
); | |
// Save minted tokens to a log file, so we can resume from this point if something goes wrong | |
for (const [i, nft] of batch.entries()) { | |
await fsPromises.appendFile( | |
mintedResultsLogPath, | |
`${JSON.stringify({ | |
address: contractAddress, | |
id: mintedTokenIds[i].toNumber(), | |
kusamaId: nft.id, | |
})}\n`, | |
); | |
} | |
} | |
} | |
console.log('Finished minting Kanaria items'); | |
} catch (e: any) { | |
console.log('Error minting Kanaria birds', e); | |
throw e; | |
} | |
}; | |
export const mintAllKanariaItems = async (deployedCollections: DeployedCollectionsReturnType) => { | |
console.log('Minting Kanaria items'); | |
// Mint all Kanaria items except for banners and khala glasses | |
console.log('Minting Kanaria items: ITEMS OTHER THAN BANNERS AND KHALA GLASSES'); | |
await mintKanariaItemsByType({ | |
deployedCollections: deployedCollections, | |
mintedResultsLogPath: `${process.cwd()}/scripts/data/minting-results/minted-items-other-kusama-to-evm-mapping.ndjson`, | |
addedAssetsResultsLogPath: `${process.cwd()}/scripts/data/minting-results/added-assets-mapping-items.json`, | |
kusamaItemsDataPath: `${process.cwd()}/scripts/data/kanaria-items-other.json`, | |
}); | |
// // Mint all Kanaria Khala glasses | |
// console.log('Minting Kanaria items: ITEMS KHALA GLASSES'); | |
// await mintKanariaItemsByType({ | |
// deployedCollections: deployedCollections, | |
// mintedResultsLogPath: `${process.cwd()}/scripts/data/minting-results/minted-items-khala-kusama-to-evm-mapping.ndjson`, | |
// addedAssetsResultsLogPath: `${process.cwd()}/scripts/data/minting-results/added-assets-mapping-items-khala.json`, | |
// kusamaItemsDataPath: `${process.cwd()}/scripts/data/kanaria-items-khala.json`, | |
// }); | |
// | |
// // Mint all Kanaria banners | |
// console.log('Minting Kanaria items: ITEMS BANNERS'); | |
// await mintKanariaItemsByType({ | |
// deployedCollections: deployedCollections, | |
// mintedResultsLogPath: `${process.cwd()}/scripts/data/minting-results/minted-items-banners-kusama-to-evm-mapping.ndjson`, | |
// addedAssetsResultsLogPath: `${process.cwd()}/scripts/data/minting-results/added-assets-mapping-banners.json`, | |
// kusamaItemsDataPath: `${process.cwd()}/scripts/data/kanaria-banners.json`, | |
// }); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
async function main() { | |
try { | |
if (!isProd) { | |
console.warn( | |
'Warning: Not running in prod mode, check isProd value as well as any incorrect addresses in constants.ts', | |
); | |
} | |
await initMintingResultsFiles(); | |
const network = hre.network.name as EVM_NETWORKS; | |
console.log(`START Kanaria minting scripts on network: ${network}`); | |
const feeData = await hre.ethers.provider.getFeeData(); | |
console.log('feeData', feeData); | |
// Deploy all contracts or get them from the log file if they were already deployed | |
const collections = await performActionIfNotComplete<DeployedCollectionsReturnType>({ | |
statusStepKey: 'deployAllContracts', | |
statusLogPath: `${process.cwd()}/scripts/data/minting-results/minting-status.ndjson`, | |
actionCallback: () => deployAllContracts(), | |
resultOutputPath: `${process.cwd()}/scripts/data/minting-results/collections.json`, | |
}); | |
invariant(collections); | |
// Set all items and gems to be auto accepted into Kanaria | |
await performActionIfNotComplete<void>({ | |
statusStepKey: 'setAutoAccept', | |
statusLogPath: `${process.cwd()}/scripts/data/minting-results/minting-status.ndjson`, | |
actionCallback: () => setAutoAccept(collections), | |
}); | |
// Set valid equippable group ids on collections | |
await performActionIfNotComplete<void>({ | |
statusStepKey: 'setAllValidParentForEquippableGroup', | |
statusLogPath: `${process.cwd()}/scripts/data/minting-results/minting-status.ndjson`, | |
actionCallback: () => setAllValidParentForEquippableGroup(collections), | |
}); | |
// Deploy Kanaria catalog contract or get it from the log file if it was already deployed | |
const catalogAddress = await performActionIfNotComplete<string>({ | |
statusStepKey: 'deployCatalog', | |
statusLogPath: `${process.cwd()}/scripts/data/minting-results/minting-status.ndjson`, | |
actionCallback: () => deployCatalog(), | |
resultOutputPath: `${process.cwd()}/scripts/data/minting-results/catalog-address.json`, | |
resultOutputKey: 'catalogAddress', | |
}); | |
invariant(catalogAddress); | |
// Configure Catalog parts | |
await performActionIfNotComplete<void>({ | |
statusStepKey: 'addPartsToCatalog', | |
statusLogPath: `${process.cwd()}/scripts/data/minting-results/minting-status.ndjson`, | |
actionCallback: () => addPartsToCatalog(catalogAddress, collections), | |
}); | |
// Add all asset entries in advance to all contracts | |
await performActionIfNotComplete<void>({ | |
statusStepKey: 'addedAssetEntries', | |
statusLogPath: `${process.cwd()}/scripts/data/minting-results/minting-status.ndjson`, | |
actionCallback: () => addAllItemsAssetEntries(collections, catalogAddress), | |
}); | |
// Mint all Kanaria birds | |
await performActionIfNotComplete<void>({ | |
statusStepKey: 'mintAllKanariaBirds', | |
statusLogPath: `${process.cwd()}/scripts/data/minting-results/minting-status.ndjson`, | |
actionCallback: () => mintAllKanariaBirds(catalogAddress, collections), | |
}); | |
// Mint all gems | |
await performActionIfNotComplete<void>({ | |
statusStepKey: 'mintAllKanariaGems', | |
statusLogPath: `${process.cwd()}/scripts/data/minting-results/minting-status.ndjson`, | |
actionCallback: () => mintAllKanariaGems(collections), | |
}); | |
// Mint all items | |
await performActionIfNotComplete<void>({ | |
statusStepKey: 'mintAllKanariaItems', | |
statusLogPath: `${process.cwd()}/scripts/data/minting-results/minting-status.ndjson`, | |
actionCallback: () => mintAllKanariaItems(collections), | |
}); | |
console.log('ALL DONE!'); | |
process.exit(0); | |
} catch (error) { | |
console.log('KANARIA MINTING MAIN SCRIPT ERROR', error); | |
process.exit(1); | |
} | |
} | |
main().catch((error) => { | |
console.error(error); | |
process.exitCode = 1; | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Signer } from 'ethers'; | |
import { ethers, run } from 'hardhat'; | |
import { delay } from '@nomiclabs/hardhat-etherscan/dist/src/etherscan/EtherscanService'; | |
import { KanariaCatalog__factory } from '../../typechain-types'; | |
import { isVerificationPossible } from '../constants'; | |
import { catalogMetadata } from '../utils/constants'; | |
import fs from 'fs'; | |
const fsPromises = fs.promises; | |
export const deployCatalog = async () => { | |
try { | |
console.log("Deploying Kanaria's Catalog contract"); | |
const accounts: Signer[] = await ethers.getSigners(); | |
const deployer = accounts[0]; | |
console.log('Catalog deployer address: ' + (await deployer.getAddress())); | |
const catalogFactory = (await ethers.getContractFactory( | |
'KanariaCatalog', | |
)) as KanariaCatalog__factory; | |
const kanariaCatalog = await catalogFactory.deploy(catalogMetadata, 'svg'); | |
await kanariaCatalog.deployed(); | |
console.log('KanariaCatalog deployed to:', kanariaCatalog.address); | |
console.log('isVerificationPossible', isVerificationPossible); | |
if (isVerificationPossible) { | |
await delay(6000); | |
console.log('Etherscan contract verification starting now.'); | |
try { | |
await run('verify:verify', { | |
address: kanariaCatalog.address, | |
contract: 'contracts/KanariaCatalog.sol:KanariaCatalog', | |
constructorArguments: [catalogMetadata, 'svg'], | |
}); | |
} catch (e) { | |
console.log('Verification error', e); | |
} | |
} | |
await fsPromises.writeFile( | |
`${process.cwd()}/scripts/data/minting-results/catalog-address.json`, | |
JSON.stringify({ catalogAddress: kanariaCatalog.address }), | |
); | |
console.log('Done!'); | |
return kanariaCatalog.address; | |
} catch (e: any) { | |
console.log('Error deploying Kanaria birds', e); | |
throw e; | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { getOrCreateNdjosnLogFile } from './get-or-create-ndjosn-log-file'; | |
import fs from 'fs'; | |
const fsPromises = fs.promises; | |
interface ScriptStep { | |
step: string; | |
status: boolean; | |
} | |
export const performActionIfNotComplete = async <T>({ | |
statusStepKey, | |
statusLogPath, | |
resultOutputPath, | |
actionCallback, | |
resultOutputKey, | |
}: { | |
statusLogPath: string; | |
statusStepKey: string; | |
resultOutputPath?: string; | |
resultOutputKey?: string; | |
actionCallback: () => Promise<T>; | |
}) => { | |
const status = await getOrCreateNdjosnLogFile<ScriptStep>(statusLogPath); | |
const shouldPerformAction = status.find((r) => r.step === statusStepKey)?.status !== true; | |
if (shouldPerformAction) { | |
const result = await actionCallback(); | |
await fsPromises.appendFile( | |
statusLogPath, | |
`${JSON.stringify({ step: statusStepKey, status: true })}\n`, | |
); | |
return result; | |
} else { | |
console.log(`Skipping ${statusStepKey} step`); | |
if (resultOutputPath) { | |
const response = await fsPromises.readFile(resultOutputPath, 'utf-8'); | |
const responseJson = JSON.parse(response); | |
const result: T = resultOutputKey ? responseJson?.[resultOutputKey] : responseJson; | |
return result; | |
} | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment