difftreelog
test Initial eth playgrounds
in: master
4 files changed
tests/package.jsondiffbeforeafterboth79 "testBlockProduction": "mocha --timeout 9999999 -r ts-node/register ./**/block-production.test.ts",79 "testBlockProduction": "mocha --timeout 9999999 -r ts-node/register ./**/block-production.test.ts",80 "testEnableDisableTransfers": "mocha --timeout 9999999 -r ts-node/register ./**/enableDisableTransfer.test.ts",80 "testEnableDisableTransfers": "mocha --timeout 9999999 -r ts-node/register ./**/enableDisableTransfer.test.ts",81 "testLimits": "mocha --timeout 9999999 -r ts-node/register ./**/limits.test.ts",81 "testLimits": "mocha --timeout 9999999 -r ts-node/register ./**/limits.test.ts",82 "testEthCreateNFTCollection": "mocha --timeout 9999999 -r ts-node/register ./**/eth/createNFTCollection.test.ts",82 "testEthCreateCollection": "mocha --timeout 9999999 -r ts-node/register ./**/eth/createCollection.test.ts",83 "testEthCreateRFTCollection": "mocha --timeout 9999999 -r ts-node/register ./**/eth/createRFTCollection.test.ts",83 "testRFT": "mocha --timeout 9999999 -r ts-node/register ./**/refungible.test.ts",84 "testRFT": "mocha --timeout 9999999 -r ts-node/register ./**/refungible.test.ts",84 "testFT": "mocha --timeout 9999999 -r ts-node/register ./**/fungible.test.ts",85 "testFT": "mocha --timeout 9999999 -r ts-node/register ./**/fungible.test.ts",85 "testRPC": "mocha --timeout 9999999 -r ts-node/register ./**/rpc.test.ts",86 "testRPC": "mocha --timeout 9999999 -r ts-node/register ./**/rpc.test.ts",tests/src/eth/nesting/nest.test.tsdiffbeforeafterboth--- a/tests/src/eth/nesting/nest.test.ts
+++ b/tests/src/eth/nesting/nest.test.ts
@@ -1,217 +1,220 @@
-import {ApiPromise} from '@polkadot/api';
+import {IKeyringPair} from '@polkadot/types/types';
import {Contract} from 'web3-eth-contract';
-import {expect} from 'chai';
-import Web3 from 'web3';
-import {createEthAccountWithBalance, evmCollectionHelpers, GAS_ARGS, getCollectionAddressFromResult, itWeb3, tokenIdToAddress} from '../../eth/util/helpers';
-import nonFungibleAbi from '../nonFungibleAbi.json';
+import {itEth, EthUniqueHelper, usingEthPlaygrounds, expect} from '../util/playgrounds'
+
const createNestingCollection = async (
- api: ApiPromise,
- web3: Web3,
+ helper: EthUniqueHelper,
owner: string,
): Promise<{ collectionId: number, collectionAddress: string, contract: Contract }> => {
- const collectionHelper = evmCollectionHelpers(web3, owner);
-
- const result = await collectionHelper.methods
- .createNonfungibleCollection('A', 'B', 'C')
- .send();
- const {collectionIdAddress: collectionAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+ const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
- const contract = new web3.eth.Contract(nonFungibleAbi as any, collectionAddress, {from: owner, ...GAS_ARGS});
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
await contract.methods.setCollectionNesting(true).send({from: owner});
return {collectionId, collectionAddress, contract};
};
-describe('Integration Test: EVM Nesting', () => {
- itWeb3('NFT: allows an Owner to nest/unnest their token', async ({api, web3, privateKeyWrapper}) => {
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const {collectionId, contract} = await createNestingCollection(api, web3, owner);
- // Create a token to be nested
- const targetNFTTokenId = await contract.methods.nextTokenId().call();
- await contract.methods.mint(
- owner,
- targetNFTTokenId,
- ).send({from: owner});
-
- const targetNftTokenAddress = tokenIdToAddress(collectionId, targetNFTTokenId);
-
- // Create a nested token
- const firstTokenId = await contract.methods.nextTokenId().call();
- await contract.methods.mint(
- targetNftTokenAddress,
- firstTokenId,
- ).send({from: owner});
+describe('EVM nesting tests group', () => {
+ let donor: IKeyringPair;
- expect(await contract.methods.ownerOf(firstTokenId).call()).to.be.equal(targetNftTokenAddress);
-
- // Create a token to be nested and nest
- const secondTokenId = await contract.methods.nextTokenId().call();
- await contract.methods.mint(
- owner,
- secondTokenId,
- ).send({from: owner});
-
- await contract.methods.transfer(targetNftTokenAddress, secondTokenId).send({from: owner});
-
- expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(targetNftTokenAddress);
-
- // Unnest token back
- await contract.methods.transferFrom(targetNftTokenAddress, owner, secondTokenId).send({from: owner});
- expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(owner);
+ before(async function() {
+ await usingEthPlaygrounds(async (helper, privateKey) => {
+ donor = privateKey('//Alice');
+ });
});
- itWeb3('NFT: allows an Owner to nest/unnest their token (Restricted nesting)', async ({api, web3, privateKeyWrapper}) => {
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-
- const {collectionId: collectionIdA, collectionAddress: collectionAddressA, contract: contractA} = await createNestingCollection(api, web3, owner);
- const {collectionAddress: collectionAddressB, contract: contractB} = await createNestingCollection(api, web3, owner);
- await contractA.methods.setCollectionNesting(true, [collectionAddressA, collectionAddressB]).send({from: owner});
-
- // Create a token to nest into
- const targetNftTokenId = await contractA.methods.nextTokenId().call();
- await contractA.methods.mint(
- owner,
- targetNftTokenId,
- ).send({from: owner});
- const nftTokenAddressA1 = tokenIdToAddress(collectionIdA, targetNftTokenId);
-
- // Create a token for nesting in the same collection as the target
- const nftTokenIdA = await contractA.methods.nextTokenId().call();
- await contractA.methods.mint(
- owner,
- nftTokenIdA,
- ).send({from: owner});
-
- // Create a token for nesting in a different collection
- const nftTokenIdB = await contractB.methods.nextTokenId().call();
- await contractB.methods.mint(
- owner,
- nftTokenIdB,
- ).send({from: owner});
-
- // Nest
- await contractA.methods.transfer(nftTokenAddressA1, nftTokenIdA).send({from: owner});
- expect(await contractA.methods.ownerOf(nftTokenIdA).call()).to.be.equal(nftTokenAddressA1);
-
- await contractB.methods.transfer(nftTokenAddressA1, nftTokenIdB).send({from: owner});
- expect(await contractB.methods.ownerOf(nftTokenIdB).call()).to.be.equal(nftTokenAddressA1);
- });
-});
-
-describe('Negative Test: EVM Nesting', async() => {
- itWeb3('NFT: disallows to nest token if nesting is disabled', async ({api, web3, privateKeyWrapper}) => {
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-
- const {collectionId, contract} = await createNestingCollection(api, web3, owner);
- await contract.methods.setCollectionNesting(false).send({from: owner});
-
- // Create a token to nest into
- const targetNftTokenId = await contract.methods.nextTokenId().call();
- await contract.methods.mint(
- owner,
- targetNftTokenId,
- ).send({from: owner});
-
- const targetNftTokenAddress = tokenIdToAddress(collectionId, targetNftTokenId);
-
- // Create a token to nest
- const nftTokenId = await contract.methods.nextTokenId().call();
- await contract.methods.mint(
- owner,
- nftTokenId,
- ).send({from: owner});
-
- // Try to nest
- await expect(contract.methods
- .transfer(targetNftTokenAddress, nftTokenId)
- .call({from: owner})).to.be.rejectedWith('UserIsNotAllowedToNest');
- });
-
- itWeb3('NFT: disallows a non-Owner to nest someone else\'s token', async ({api, web3, privateKeyWrapper}) => {
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const malignant = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-
- const {collectionId, contract} = await createNestingCollection(api, web3, owner);
-
- // Mint a token
- const targetTokenId = await contract.methods.nextTokenId().call();
- await contract.methods.mint(
- owner,
- targetTokenId,
- ).send({from: owner});
- const targetTokenAddress = tokenIdToAddress(collectionId, targetTokenId);
-
- // Mint a token belonging to a different account
- const tokenId = await contract.methods.nextTokenId().call();
- await contract.methods.mint(
- malignant,
- tokenId,
- ).send({from: owner});
-
- // Try to nest one token in another as a non-owner account
- await expect(contract.methods
- .transfer(targetTokenAddress, tokenId)
- .call({from: malignant})).to.be.rejectedWith('UserIsNotAllowedToNest');
- });
-
- itWeb3('NFT: disallows a non-Owner to nest someone else\'s token (Restricted nesting)', async ({api, web3, privateKeyWrapper}) => {
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const malignant = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-
- const {collectionId: collectionIdA, collectionAddress: collectionAddressA, contract: contractA} = await createNestingCollection(api, web3, owner);
- const {collectionAddress: collectionAddressB, contract: contractB} = await createNestingCollection(api, web3, owner);
-
- await contractA.methods.setCollectionNesting(true, [collectionAddressA, collectionAddressB]).send({from: owner});
-
- // Create a token in one collection
- const nftTokenIdA = await contractA.methods.nextTokenId().call();
- await contractA.methods.mint(
- owner,
- nftTokenIdA,
- ).send({from: owner});
- const nftTokenAddressA = tokenIdToAddress(collectionIdA, nftTokenIdA);
-
- // Create a token in another collection belonging to someone else
- const nftTokenIdB = await contractB.methods.nextTokenId().call();
- await contractB.methods.mint(
- malignant,
- nftTokenIdB,
- ).send({from: owner});
-
- // Try to drag someone else's token into the other collection and nest
- await expect(contractB.methods
- .transfer(nftTokenAddressA, nftTokenIdB)
- .call({from: malignant})).to.be.rejectedWith('UserIsNotAllowedToNest');
+ describe('Integration Test: EVM Nesting', () => {
+ itEth('NFT: allows an Owner to nest/unnest their token', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const {collectionId, contract} = await createNestingCollection(helper, owner);
+
+ // Create a token to be nested
+ const targetNFTTokenId = await contract.methods.nextTokenId().call();
+ await contract.methods.mint(
+ owner,
+ targetNFTTokenId,
+ ).send({from: owner});
+
+ const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetNFTTokenId);
+
+ // Create a nested token
+ const firstTokenId = await contract.methods.nextTokenId().call();
+ await contract.methods.mint(
+ targetNftTokenAddress,
+ firstTokenId,
+ ).send({from: owner});
+
+ expect(await contract.methods.ownerOf(firstTokenId).call()).to.be.equal(targetNftTokenAddress);
+
+ // Create a token to be nested and nest
+ const secondTokenId = await contract.methods.nextTokenId().call();
+ await contract.methods.mint(
+ owner,
+ secondTokenId,
+ ).send({from: owner});
+
+ await contract.methods.transfer(targetNftTokenAddress, secondTokenId).send({from: owner});
+
+ expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(targetNftTokenAddress);
+
+ // Unnest token back
+ await contract.methods.transferFrom(targetNftTokenAddress, owner, secondTokenId).send({from: owner});
+ expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(owner);
+ });
+
+ itEth('NFT: allows an Owner to nest/unnest their token (Restricted nesting)', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+
+ const {collectionId: collectionIdA, collectionAddress: collectionAddressA, contract: contractA} = await createNestingCollection(helper, owner);
+ const {collectionAddress: collectionAddressB, contract: contractB} = await createNestingCollection(helper, owner);
+ await contractA.methods.setCollectionNesting(true, [collectionAddressA, collectionAddressB]).send({from: owner});
+
+ // Create a token to nest into
+ const targetNftTokenId = await contractA.methods.nextTokenId().call();
+ await contractA.methods.mint(
+ owner,
+ targetNftTokenId,
+ ).send({from: owner});
+ const nftTokenAddressA1 = helper.ethAddress.fromTokenId(collectionIdA, targetNftTokenId);
+
+ // Create a token for nesting in the same collection as the target
+ const nftTokenIdA = await contractA.methods.nextTokenId().call();
+ await contractA.methods.mint(
+ owner,
+ nftTokenIdA,
+ ).send({from: owner});
+
+ // Create a token for nesting in a different collection
+ const nftTokenIdB = await contractB.methods.nextTokenId().call();
+ await contractB.methods.mint(
+ owner,
+ nftTokenIdB,
+ ).send({from: owner});
+
+ // Nest
+ await contractA.methods.transfer(nftTokenAddressA1, nftTokenIdA).send({from: owner});
+ expect(await contractA.methods.ownerOf(nftTokenIdA).call()).to.be.equal(nftTokenAddressA1);
+
+ await contractB.methods.transfer(nftTokenAddressA1, nftTokenIdB).send({from: owner});
+ expect(await contractB.methods.ownerOf(nftTokenIdB).call()).to.be.equal(nftTokenAddressA1);
+ });
});
- itWeb3('NFT: disallows to nest token in an unlisted collection', async ({api, web3, privateKeyWrapper}) => {
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-
- const {collectionId: collectionIdA, collectionAddress: collectionAddressA, contract: contractA} = await createNestingCollection(api, web3, owner);
- const {contract: contractB} = await createNestingCollection(api, web3, owner);
-
- await contractA.methods.setCollectionNesting(true, [collectionAddressA]).send({from: owner});
-
- // Create a token in one collection
- const nftTokenIdA = await contractA.methods.nextTokenId().call();
- await contractA.methods.mint(
- owner,
- nftTokenIdA,
- ).send({from: owner});
- const nftTokenAddressA = tokenIdToAddress(collectionIdA, nftTokenIdA);
-
- // Create a token in another collection
- const nftTokenIdB = await contractB.methods.nextTokenId().call();
- await contractB.methods.mint(
- owner,
- nftTokenIdB,
- ).send({from: owner});
-
- // Try to nest into a token in the other collection, disallowed in the first
- await expect(contractB.methods
- .transfer(nftTokenAddressA, nftTokenIdB)
- .call()).to.be.rejectedWith('SourceCollectionIsNotAllowedToNest');
+ describe('Negative Test: EVM Nesting', async() => {
+ itEth('NFT: disallows to nest token if nesting is disabled', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+
+ const {collectionId, contract} = await createNestingCollection(helper, owner);
+ await contract.methods.setCollectionNesting(false).send({from: owner});
+
+ // Create a token to nest into
+ const targetNftTokenId = await contract.methods.nextTokenId().call();
+ await contract.methods.mint(
+ owner,
+ targetNftTokenId,
+ ).send({from: owner});
+
+ const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetNftTokenId);
+
+ // Create a token to nest
+ const nftTokenId = await contract.methods.nextTokenId().call();
+ await contract.methods.mint(
+ owner,
+ nftTokenId,
+ ).send({from: owner});
+
+ // Try to nest
+ await expect(contract.methods
+ .transfer(targetNftTokenAddress, nftTokenId)
+ .call({from: owner})).to.be.rejectedWith('UserIsNotAllowedToNest');
+ });
+
+ itEth('NFT: disallows a non-Owner to nest someone else\'s token', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const malignant = await helper.eth.createAccountWithBalance(donor);
+
+ const {collectionId, contract} = await createNestingCollection(helper, owner);
+
+ // Mint a token
+ const targetTokenId = await contract.methods.nextTokenId().call();
+ await contract.methods.mint(
+ owner,
+ targetTokenId,
+ ).send({from: owner});
+ const targetTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetTokenId);
+
+ // Mint a token belonging to a different account
+ const tokenId = await contract.methods.nextTokenId().call();
+ await contract.methods.mint(
+ malignant,
+ tokenId,
+ ).send({from: owner});
+
+ // Try to nest one token in another as a non-owner account
+ await expect(contract.methods
+ .transfer(targetTokenAddress, tokenId)
+ .call({from: malignant})).to.be.rejectedWith('UserIsNotAllowedToNest');
+ });
+
+ itEth('NFT: disallows a non-Owner to nest someone else\'s token (Restricted nesting)', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const malignant = await helper.eth.createAccountWithBalance(donor);
+
+ const {collectionId: collectionIdA, collectionAddress: collectionAddressA, contract: contractA} = await createNestingCollection(helper, owner);
+ const {collectionAddress: collectionAddressB, contract: contractB} = await createNestingCollection(helper, owner);
+
+ await contractA.methods.setCollectionNesting(true, [collectionAddressA, collectionAddressB]).send({from: owner});
+
+ // Create a token in one collection
+ const nftTokenIdA = await contractA.methods.nextTokenId().call();
+ await contractA.methods.mint(
+ owner,
+ nftTokenIdA,
+ ).send({from: owner});
+ const nftTokenAddressA = helper.ethAddress.fromTokenId(collectionIdA, nftTokenIdA);
+
+ // Create a token in another collection belonging to someone else
+ const nftTokenIdB = await contractB.methods.nextTokenId().call();
+ await contractB.methods.mint(
+ malignant,
+ nftTokenIdB,
+ ).send({from: owner});
+
+ // Try to drag someone else's token into the other collection and nest
+ await expect(contractB.methods
+ .transfer(nftTokenAddressA, nftTokenIdB)
+ .call({from: malignant})).to.be.rejectedWith('UserIsNotAllowedToNest');
+ });
+
+ itEth('NFT: disallows to nest token in an unlisted collection', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+
+ const {collectionId: collectionIdA, collectionAddress: collectionAddressA, contract: contractA} = await createNestingCollection(helper, owner);
+ const {contract: contractB} = await createNestingCollection(helper, owner);
+
+ await contractA.methods.setCollectionNesting(true, [collectionAddressA]).send({from: owner});
+
+ // Create a token in one collection
+ const nftTokenIdA = await contractA.methods.nextTokenId().call();
+ await contractA.methods.mint(
+ owner,
+ nftTokenIdA,
+ ).send({from: owner});
+ const nftTokenAddressA = helper.ethAddress.fromTokenId(collectionIdA, nftTokenIdA);
+
+ // Create a token in another collection
+ const nftTokenIdB = await contractB.methods.nextTokenId().call();
+ await contractB.methods.mint(
+ owner,
+ nftTokenIdB,
+ ).send({from: owner});
+
+ // Try to nest into a token in the other collection, disallowed in the first
+ await expect(contractB.methods
+ .transfer(nftTokenAddressA, nftTokenIdB)
+ .call()).to.be.rejectedWith('SourceCollectionIsNotAllowedToNest');
+ });
});
});
tests/src/eth/util/playgrounds/index.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/eth/util/playgrounds/index.ts
@@ -0,0 +1,67 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+import {IKeyringPair} from '@polkadot/types/types';
+
+import config from '../../../config';
+
+import {EthUniqueHelper} from './unique.dev';
+import {SilentLogger} from '../../../util/playgrounds/unique.dev';
+
+export {EthUniqueHelper} from './unique.dev';
+
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+chai.use(chaiAsPromised);
+export const expect = chai.expect;
+
+export const usingEthPlaygrounds = async (code: (helper: EthUniqueHelper, privateKey: (seed: string) => IKeyringPair) => Promise<void>) => {
+ // TODO: Remove, this is temporary: Filter unneeded API output
+ // (Jaco promised it will be removed in the next version)
+ const consoleErr = console.error;
+ const consoleLog = console.log;
+ const consoleWarn = console.warn;
+
+ const outFn = (printer: any) => (...args: any[]) => {
+ for (const arg of args) {
+ if (typeof arg !== 'string')
+ continue;
+ if (arg.includes('1000:: Normal connection closure') || arg.includes('Not decorating unknown runtime apis: UniqueApi/2, RmrkApi/1') || arg.includes('RPC methods not decorated:') || arg === 'Normal connection closure')
+ return;
+ }
+ printer(...args);
+ };
+
+ console.error = outFn(consoleErr.bind(console));
+ console.log = outFn(consoleLog.bind(console));
+ console.warn = outFn(consoleWarn.bind(console));
+ const helper = new EthUniqueHelper(new SilentLogger());
+
+ try {
+ await helper.connect(config.substrateUrl);
+ await helper.connectWeb3(config.substrateUrl);
+ const ss58Format = helper.chain.getChainProperties().ss58Format;
+ const privateKey = (seed: string) => helper.util.fromSeed(seed, ss58Format);
+ await code(helper, privateKey);
+ }
+ finally {
+ await helper.disconnect();
+ await helper.disconnectWeb3();
+ console.error = consoleErr;
+ console.log = consoleLog;
+ console.warn = consoleWarn;
+ }
+}
+
+export async function itEth(name: string, cb: (apis: { helper: EthUniqueHelper, privateKey: (seed: string) => IKeyringPair }) => any, opts: { only?: boolean, skip?: boolean } = {}) {
+ let i: any = it;
+ if (opts.only) i = i.only;
+ else if (opts.skip) i = i.skip;
+ i(name, async () => {
+ await usingEthPlaygrounds(async (helper, privateKey) => {
+ await cb({helper, privateKey});
+ });
+ });
+}
+itEth.only = (name: string, cb: (apis: { helper: EthUniqueHelper, privateKey: (seed: string) => IKeyringPair }) => any) => itEth(name, cb, {only: true});
+itEth.skip = (name: string, cb: (apis: { helper: EthUniqueHelper, privateKey: (seed: string) => IKeyringPair }) => any) => itEth(name, cb, {skip: true});
\ No newline at end of file
tests/src/eth/util/playgrounds/unique.dev.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/eth/util/playgrounds/unique.dev.ts
@@ -0,0 +1,162 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+/* eslint-disable function-call-argument-newline */
+
+import Web3 from 'web3';
+import {WebsocketProvider} from 'web3-core';
+import {Contract} from 'web3-eth-contract';
+
+import {evmToAddress} from '@polkadot/util-crypto';
+import {IKeyringPair} from '@polkadot/types/types';
+
+import {DevUniqueHelper} from '../../../util/playgrounds/unique.dev';
+
+// Native contracts ABI
+import collectionHelpersAbi from '../../collectionHelpersAbi.json';
+import fungibleAbi from '../../fungibleAbi.json';
+import nonFungibleAbi from '../../nonFungibleAbi.json';
+import refungibleAbi from '../../reFungibleAbi.json';
+import refungibleTokenAbi from '../../reFungibleTokenAbi.json';
+import contractHelpersAbi from './../contractHelpersAbi.json';
+
+class EthGroupBase {
+ helper: EthUniqueHelper;
+
+ constructor(helper: EthUniqueHelper) {
+ this.helper = helper;
+ }
+}
+
+
+class NativeContractGroup extends EthGroupBase {
+ DEFAULT_GAS = 2_500_000;
+
+ contractHelpers(caller: string): Contract {
+ const web3 = this.helper.getWeb3();
+ return new web3.eth.Contract(contractHelpersAbi as any, '0x842899ECF380553E8a4de75bF534cdf6fBF64049', {from: caller, gas: this.DEFAULT_GAS});
+ }
+
+ collectionHelpers(caller: string) {
+ const web3 = this.helper.getWeb3();
+ return new web3.eth.Contract(collectionHelpersAbi as any, '0x6c4e9fe1ae37a41e93cee429e8e1881abdcbb54f', {from: caller, gas: this.DEFAULT_GAS});
+ }
+
+ collection(address: string, mode: 'nft' | 'rft' | 'ft', caller?: string): Contract {
+ const abi = {
+ 'nft': nonFungibleAbi,
+ 'rft': refungibleAbi,
+ 'ft': fungibleAbi
+ }[mode];
+ const web3 = this.helper.getWeb3();
+ return new web3.eth.Contract(abi as any, address, {gas: this.DEFAULT_GAS, ...(caller ? {from: caller} : {})});
+ }
+
+ rftTokenByAddress(address: string, caller?: string): Contract {
+ const web3 = this.helper.getWeb3();
+ return new web3.eth.Contract(refungibleTokenAbi as any, address, {gas: this.DEFAULT_GAS, ...(caller ? {from: caller} : {})});
+ }
+
+ rftToken(collectionId: number, tokenId: number, caller?: string): Contract {
+ return this.rftTokenByAddress(this.helper.ethAddress.fromTokenId(collectionId, tokenId), caller);
+ }
+}
+
+
+class EthGroup extends EthGroupBase {
+ createAccount() {
+ const web3 = this.helper.getWeb3();
+ const account = web3.eth.accounts.create();
+ web3.eth.accounts.wallet.add(account.privateKey);
+ return account.address;
+ }
+
+ async createAccountWithBalance(donor: IKeyringPair, amount=1000n) {
+ const account = this.createAccount();
+ await this.transferBalanceFromSubstrate(donor, account, amount);
+
+ return account;
+ }
+
+ async transferBalanceFromSubstrate(donor: IKeyringPair, recepient: string, amount=1000n) {
+ return await this.helper.balance.transferToSubstrate(donor, evmToAddress(recepient), amount * this.helper.balance.getOneTokenNominal());
+ }
+
+ async createNonfungibleCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{collectionId: number, collectionAddress: string}> {
+ const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer);
+
+ const result = await collectionHelper.methods.createNonfungibleCollection(name, description, tokenPrefix).send();
+
+ const collectionAddress = this.helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);
+ const collectionId = this.helper.ethAddress.extractCollectionId(collectionAddress);
+
+ return {collectionId, collectionAddress};
+ }
+}
+
+
+class EthAddressGroup extends EthGroupBase {
+ extractCollectionId(address: string): number {
+ if (!(address.length === 42 || address.length === 40)) throw new Error('address wrong format');
+ return parseInt(address.substr(address.length - 8), 16);
+ }
+
+ fromCollectionId(collectionId: number): string {
+ if (collectionId >= 0xffffffff || collectionId < 0) throw new Error('collectionId overflow');
+ return Web3.utils.toChecksumAddress(`0x17c4e6453cc49aaaaeaca894e6d9683e${collectionId.toString(16).padStart(8, '0')}`);
+ }
+
+ extractTokenId(address: string): {collectionId: number, tokenId: number} {
+ if (!address.startsWith('0x'))
+ throw 'address not starts with "0x"';
+ if (address.length > 42)
+ throw 'address length is more than 20 bytes';
+ return {
+ collectionId: Number('0x' + address.substring(address.length - 16, address.length - 8)),
+ tokenId: Number('0x' + address.substring(address.length - 8)),
+ };
+ }
+
+ fromTokenId(collectionId: number, tokenId: number): string {
+ return this.helper.util.getNestingTokenAddress(collectionId, tokenId);
+ }
+
+ normalizeAddress(address: string): string {
+ return '0x' + address.substring(address.length - 40);
+ }
+}
+
+
+export class EthUniqueHelper extends DevUniqueHelper {
+ web3: Web3 | null = null;
+ web3Provider: WebsocketProvider | null = null;
+
+ eth: EthGroup;
+ ethAddress: EthAddressGroup;
+ ethNativeContract: NativeContractGroup;
+
+ constructor(logger: { log: (msg: any, level: any) => void, level: any }) {
+ super(logger);
+ this.eth = new EthGroup(this);
+ this.ethAddress = new EthAddressGroup(this);
+ this.ethNativeContract = new NativeContractGroup(this);
+ }
+
+ getWeb3(): Web3 {
+ if(this.web3 === null) throw Error('Web3 not connected');
+ return this.web3;
+ }
+
+ async connectWeb3(wsEndpoint: string) {
+ if(this.web3 !== null) return;
+ this.web3Provider = new Web3.providers.WebsocketProvider(wsEndpoint);
+ this.web3 = new Web3(this.web3Provider);
+ }
+
+ async disconnectWeb3() {
+ if(this.web3 === null) return;
+ this.web3Provider?.connection.close();
+ this.web3 = null;
+ }
+}
+
\ No newline at end of file