1import {EthUniqueHelper, usingEthPlaygrounds} from '../util';2import {readFile} from 'fs/promises';3import {ContractImports} from '../util/playgrounds/types';4import {Contract} from '@polkadot/api-contract/base';5import Web3 from 'web3';6import {IProperty, ITokenPropertyPermission} from '../../util/playgrounds/types';7import {addressToEvm, decodeAddress} from '@polkadot/util-crypto';8import nonFungibleAbi from '../nonFungibleAbi.json';9import {IKeyringPair} from '@polkadot/types/types';1011enum SponsoringMode {12 Disabled = 0,13 Allowlisted = 1,14 Generous = 2,15}1617const WS_ENDPOINT = 'wss://ws-rc.unique.network';18const CONTRACT_IMPORT: ContractImports[] = [19 {20 fsPath: `${__dirname}/../api/CollectionHelpers.sol`,21 solPath: 'api/CollectionHelpers.sol',22 },23 {24 fsPath: `${__dirname}/../api/ContractHelpers.sol`,25 solPath: 'api/ContractHelpers.sol',26 },27 {28 fsPath: `${__dirname}/../api/UniqueRefungibleToken.sol`,29 solPath: 'api/UniqueRefungibleToken.sol',30 },31 {32 fsPath: `${__dirname}/../api/UniqueRefungible.sol`,33 solPath: 'api/UniqueRefungible.sol',34 },35 {36 fsPath: `${__dirname}/../api/UniqueNFT.sol`,37 solPath: 'api/UniqueNFT.sol',38 },39];40const main = async () => {41 await usingEthPlaygrounds(async (helper, privateKey) => {42 const contract_source = (43 await readFile(`${__dirname}/EvmToSubstrateHelper.sol`)44 ).toString();45 46 47 const donor = await privateKey('//Alice'); 48 const myAccount = await privateKey('//Bob'); 49 50 const signer = await helper.eth.createAccountWithBalance(donor, 100n);5152 const collection = await helper.nft.mintCollection(donor, {53 name: 'test mintToSubstrate',54 description: 'EVMHelpers',55 tokenPrefix: 'ap',56 tokenPropertyPermissions: [57 {58 key: 'url',59 permission: {60 tokenOwner: true,61 collectionAdmin: true,62 mutable: true,63 },64 },65 ],66 limits: {sponsorTransferTimeout: 0, sponsorApproveTimeout: 0}, permissions: {mintMode: true},67 });68 69 await collection.addToAllowList(donor, {Ethereum: helper.address.substrateToEth(donor.address)});70 await collection.addToAllowList(donor, {Substrate: donor.address});71 await collection.addAdmin(donor, {Ethereum: signer});72 await collection.addAdmin(donor, {Ethereum: helper.address.substrateToEth(donor.address)});73 74 console.log('collection admin(s)): ', await collection.getAdmins());75 console.log('collection owner(s)): ', await collection.getAllowList());76 const fee = await helper.arrange.calculcateFee({Substrate: donor.address}, () => collection.mintToken(donor, {Substrate: myAccount.address}));77 console.log(`\ntoken mint from susbtrate : ${fee}`);78 79 const collectionEthAddress = helper.ethAddress.fromCollectionId(collection.collectionId);80 const collectionContract = helper.ethNativeContract.collection(81 collectionEthAddress,82 'nft',83 );84 85 const receiverEthAddress = helper.address.substrateToEth(myAccount.address);86 87 8889 let encodedCall = collectionContract.methods90 .mint(receiverEthAddress)91 .encodeABI();9293 const ethFee = await helper.arrange.calculcateFee(94 {Substrate: donor.address},95 async () => {96 await helper.eth.sendEVM(97 donor,98 collectionContract.options.address,99 encodedCall,100 '0',101 );102 },103 );104105 console.log(`token mint from eth : ${ethFee}`);106 107 const contract = await helper.ethContract.deployByCode(signer, 'EvmToSubstrate', contract_source, CONTRACT_IMPORT);108 console.log(`contract has been deployed`);109 110 await helper.eth.transferBalanceFromSubstrate(donor, contract.options.address, 100n);111 112 console.log(`transfer has been completed`);113 114 await collection.addToAllowList(donor, { Ethereum: contract.options.address });115 await collection.addAdmin(donor, {Ethereum: contract.options.address});116 117 console.log(`setup has been completed`);118 119 const feeForProxyContractMinting = await helper.arrange.calculcateFee(120 {Ethereum: signer},121 async () => {122 await contract.methods.mintToSubstrate(helper.ethAddress.fromCollectionId(collection.collectionId), myAccount.addressRaw).send({from: signer});123 },124 );125 126 console.log(`token mint from contract to the Substrate Id: ${feeForProxyContractMinting}\n`);127 128 129 130 131 console.log('\t\t\t *** Properties Fees ***\n');132133 const propertiesNumber = 20;134 135 const properties = Array(40).fill(0)136 .map((_, i) => { return {key: `key_${i}`, value: Uint8Array.from(Buffer.from(`value_${i}`))}; });137 138 const permissions: ITokenPropertyPermission[] = properties139 .map(p => {140 return {141 key: p.key, permission: {142 tokenOwner: true,143 collectionAdmin: true,144 mutable: true}}; });145 146 147 148 149 const collectionForSubstrateBench = await createCollectionForPropertiesBenchmarks(helper, privateKey, signer, contract.options.address, permissions);150 151 const mintWithPropSubstrate = await helper.arrange.calculcateFee({Substrate: donor.address}, async () => {152 await collectionForSubstrateBench153 .mintToken(donor, {Substrate: myAccount.address}, properties.slice(0, propertiesNumber).map(p => { return {key: p.key, value: Buffer.from(p.value).toString()}; }));154 });155 console.log(`token mint from susbtrate: ${mintWithPropSubstrate}`);156 157 158 159 const collectionForEvmBench = await createCollectionForPropertiesBenchmarks(helper, privateKey, signer, contract.options.address, permissions);160 const evmContract = helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collectionForEvmBench.collectionId), 'nft');161 162 let subTokenId = await evmContract.methods.nextTokenId().call();163 164 encodedCall = evmContract.methods165 .mint(receiverEthAddress)166 .encodeABI();167 168 const evmCallWithPropFee = await helper.arrange.calculcateFee(169 {Substrate: donor.address},170 async () => {171172 await helper.eth.sendEVM(173 donor,174 evmContract.options.address,175 encodedCall,176 '0',177 );178 179 for (const val of properties.slice(0, propertiesNumber)) {180 181 encodedCall = await evmContract.methods182 .setProperty(subTokenId, val.key, Buffer.from(val.value))183 .encodeABI();184 185 await helper.eth.sendEVM(186 donor,187 evmContract.options.address,188 encodedCall,189 '0',190 );191 }192 },193 );194 195 console.log(`token mint from eth : ${evmCallWithPropFee}`);196 197 198 199 200 201 const collectionForEvmBulkBench = await createCollectionForPropertiesBenchmarks(helper, privateKey, signer, contract.options.address, permissions);202 const evmBulkContract = helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collectionForEvmBulkBench.collectionId), 'nft');203 204 subTokenId = await evmBulkContract.methods.nextTokenId().call();205206 encodedCall = evmBulkContract.methods207 .mint(receiverEthAddress)208 .encodeABI();209 210 const evmCallWithBulkPropFee = await helper.arrange.calculcateFee(211 {Substrate: donor.address},212 async () => {213214 await helper.eth.sendEVM(215 donor,216 evmBulkContract.options.address,217 encodedCall,218 '0',219 );220 221 222 223 encodedCall = await evmBulkContract.methods224 .setProperties(225 subTokenId,226 properties.slice(0, propertiesNumber)227 .map(p => { return {field_0: p.key, field_1: p.value}; }),228 )229 .encodeABI();230 231 await helper.eth.sendEVM(232 donor,233 evmBulkContract.options.address,234 encodedCall,235 '0',236 );237 238 },239 );240 241 console.log(`token mint from eth (Bulk) : ${evmCallWithBulkPropFee}`);242 243 244 245 246 await collection.setTokenPropertyPermissions(donor, permissions);247 const mintWithPropProxyContractFee = await helper.arrange.calculcateFee({Ethereum: signer}, async () => {248 await contract.methods.mintToSubstrateWithProperty(helper.ethAddress249 .fromCollectionId(collection.collectionId), myAccount.addressRaw, properties.slice(0, propertiesNumber)).send({from: signer});250 });251 console.log(`token mint from contract to the Substrate Id: ${mintWithPropProxyContractFee}`);252 253 254 255 256 const collectionForProxyContractBulrProperties = await createCollectionForPropertiesBenchmarks(helper, privateKey, signer, contract.options.address, permissions);257 const evmContractProxyBulk = helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collectionForProxyContractBulrProperties.collectionId), 'nft');258 259 const mintWithBulkPropProxyContractFee = await helper.arrange.calculcateFee({Ethereum: signer}, async () => {260 await contract.methods.mintToSubstrateBulkProperty(evmContractProxyBulk.options.address, myAccount.addressRaw, properties.slice(0, propertiesNumber)261 .map(p => { return {field_0: p.key, field_1: p.value}; })).send({from: signer});262 });263 console.log(`token mint from contract(with bulk prop.) to the Substrate Id: ${mintWithBulkPropProxyContractFee}`);264 265 console.log('All done');266 });267};268 269main()270 .then(() => process.exit(0))271 .catch((e) => {272 console.log(e);273 process.exit(1);274 });275 276 277async function createCollectionForPropertiesBenchmarks(278 helper: EthUniqueHelper,279 privateKey: (seed: string) => Promise<IKeyringPair>,280 ethSigner: string,281 proxyContract: string,282 permissions: ITokenPropertyPermission[],283) {284 285 const donor = await privateKey('//Alice'); 286 287 288 const collection = await helper.nft.mintCollection(donor, {289 name: 'test mintToSubstrate',290 description: 'EVMHelpers',291 tokenPrefix: 'ap',292 tokenPropertyPermissions: [293 {294 key: 'url',295 permission: {296 tokenOwner: true,297 collectionAdmin: true,298 mutable: true,299 },300 },301 ],302 limits: {sponsorTransferTimeout: 0, sponsorApproveTimeout: 0}, permissions: {mintMode: true},303 });304 305 await collection.addToAllowList(donor, {Ethereum: helper.address.substrateToEth(donor.address)});306 await collection.addToAllowList(donor, {Substrate: donor.address});307 await collection.addAdmin(donor, {Ethereum: ethSigner});308 await collection.addAdmin(donor, {Ethereum: helper.address.substrateToEth(donor.address)});309 await collection.addToAllowList(donor, {Ethereum: proxyContract});310 await collection.addAdmin(donor, {Ethereum: proxyContract});311 await collection.setTokenPropertyPermissions(donor, permissions);312 313 return collection;314}