1import { ApiPromise } from "@polkadot/api";2import { addressToEvm, evmToAddress } from "@polkadot/util-crypto";3import Web3 from "web3";4import usingApi, { submitTransactionAsync } from "../../substrate/substrate-api";5import { IKeyringPair } from '@polkadot/types/types';6import { expect } from "chai";7import { getGenericResult } from "../../util/helpers";89let web3Connected = false;10export async function usingWeb3<T>(cb: (web3: Web3) => Promise<T> | T): Promise<T> {11 if (web3Connected) throw new Error('do not nest usingWeb3 calls');12 web3Connected = true;1314 const provider = new Web3.providers.WebsocketProvider("http://localhost:9944");15 const web3 = new Web3(provider);1617 try {18 return await cb(web3);19 } finally {20 21 provider.connection.close();22 web3Connected = false;23 }24}2526export function collectionIdToAddress(address: number): string {27 if (address >= 0xffffffff || address < 0) throw new Error('id overflow');28 const buf = Buffer.from([0x17, 0xc4, 0xe6, 0x45, 0x3c, 0xc4, 0x9a, 0xaa, 0xae, 0xac, 0xa8, 0x94, 0xe6, 0xd9, 0x68, 0x3e,29 address >> 24,30 (address >> 16) & 0xff,31 (address >> 8) & 0xff,32 address & 0xff,33 ]);34 return Web3.utils.toChecksumAddress('0x' + buf.toString('hex'));35}3637export function createEthAccount(web3: Web3) {38 const account = web3.eth.accounts.create();39 web3.eth.accounts.wallet.add(account.privateKey);40 return account.address;41}4243export async function transferBalanceToEth(api: ApiPromise, source: IKeyringPair, target: string, amount: number) {44 const tx = api.tx.balances.transfer(evmToAddress(target), amount);45 const events = await submitTransactionAsync(source, tx);46 const result = getGenericResult(events);47 expect(result.success).to.be.true;48}4950export async function itWeb3(name: string, cb: (apis: { web3: Web3, api: ApiPromise }) => any, opts: { only?: boolean, skip?: boolean } = {}) {51 let i: any = it;52 if (opts.only) i = i.only;53 else if (opts.skip) i = i.skip;54 i(name, async () => {55 await usingApi(async api => {56 await usingWeb3(async web3 => {57 await cb({ api, web3 });58 });59 });60 });61}62itWeb3.only = (name: string, cb: (apis: { web3: Web3, api: ApiPromise }) => any) => itWeb3(name, cb, { only: true });63itWeb3.skip = (name: string, cb: (apis: { web3: Web3, api: ApiPromise }) => any) => itWeb3(name, cb, { skip: true });6465export async function generateSubstrateEthPair(web3: Web3) {66 let account = web3.eth.accounts.create();67 const evm = evmToAddress(account.address);68}6970type NormalizedEvent = {71 address: string,72 event: string,73 args: { [key: string]: string }74};7576export function normalizeEvents(events: any): NormalizedEvent[] {77 const output = [];78 for (let key of Object.keys(events)) {79 if (key.match(/^[0-9]+$/)) {80 output.push(events[key]);81 } else if (Array.isArray(events[key])) {82 output.push(...events[key]);83 } else {84 output.push(events[key]);85 }86 }87 output.sort((a, b) => a.logIndex - b.logIndex);88 return output.map(({ address, event, returnValues }) => {89 const args: { [key: string]: string } = {};90 for (let key of Object.keys(returnValues)) {91 if (!key.match(/^[0-9]+$/)) {92 args[key] = returnValues[key];93 }94 }95 return {96 address,97 event,98 args,99 };100 });101}102103export async function recordEvents(contract: any, action: () => Promise<void>): Promise<NormalizedEvent[]> {104 const out: any = [];105 contract.events.allEvents((_: any, event: any) => {106 out.push(event);107 });108 await action();109 return normalizeEvents(out);110}111112export function subToEth(eth: string): string {113 const bytes = addressToEvm(eth);114 const string = '0x' + Buffer.from(bytes).toString('hex');115 return Web3.utils.toChecksumAddress(string);116}