@ethersproject/wallet#Wallet TypeScript Examples

The following examples show how to use @ethersproject/wallet#Wallet. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: media.ts    From zora-v1-subgraph with MIT License 6 votes vote down vote up
export async function transfer(
  mediaAddress: string,
  wallet: Wallet,
  tokenId: BigNumber,
  to: string
): Promise<string> {
  const media = MediaFactory.connect(mediaAddress, wallet)
  const tx = await media.transferFrom(wallet.address, to, tokenId)
  await tx.wait()
  return tx.hash
}
Example #2
Source File: getTransactionReceipt.test.ts    From bodhi.js with Apache License 2.0 6 votes vote down vote up
it('getTransactionReceipt', async () => {
  const endpoint = process.env.ENDPOINT_URL || 'ws://127.0.0.1:9944';
  const account1 = evmAccounts[0];
  const account2 = evmAccounts[1];

  const provider = EvmRpcProvider.from(endpoint);

  const account1Wallet = new Wallet(account1.privateKey).connect(provider as any);

  const acaContract = new Contract(ADDRESS.ACA, ACAABI.abi, account1Wallet);

  await provider.isReady();
  const pairs = createTestPairs();
  const oneAca = 10n ** BigInt(provider.api.registry.chainDecimals[0]);
  const Alice = pairs.alice;

  /** transfer aca */
  console.log('transfer aca');
  const extrinsic = provider.api.tx.balances.transfer(account1.defaultSubstrateAddress, 100n * oneAca);
  await extrinsic.signAsync(Alice);
  await sendTx(provider.api, extrinsic);

  const result = await acaContract.functions.transfer(account2.evmAddress, 10n * oneAca, {
    gasLimit: BigNumber.from(34132001n),
    gasPrice: BigNumber.from(200786445289n),
    type: 0
  });

  const receipt = await provider.getTransactionReceiptAtBlock(result.hash, result.blockHash);

  expect(receipt.blockHash).equal(result.blockHash);
  expect(receipt.logs.length).equal(1);
  expect(receipt.logs[0].blockNumber).equal(result.blockNumber);
  expect(receipt.logs[0].topics.length).equal(3);

  await provider.disconnect();
});
Example #3
Source File: media.ts    From zora-v1-subgraph with MIT License 6 votes vote down vote up
export async function updateTokenMetadataURI(
  mediaAddress: string,
  wallet: Wallet,
  tokenId: BigNumber,
  tokenMetadataURI: string
) {
  const media = await MediaFactory.connect(mediaAddress, wallet)
  const tx = await media.updateTokenMetadataURI(tokenId, tokenMetadataURI)
  console.log(tx)
  await tx.wait()
}
Example #4
Source File: index.ts    From snapshot.js with MIT License 6 votes vote down vote up
async sign(web3: Web3Provider | Wallet, address: string, message, types) {
    // @ts-ignore
    const signer = web3?.getSigner ? web3.getSigner() : web3;
    if (!message.from) message.from = address;
    if (!message.timestamp)
      message.timestamp = parseInt((Date.now() / 1e3).toFixed());
    const data: any = { domain, types, message };
    const sig = await signer._signTypedData(domain, data.types, message);
    console.log('Sign', { address, sig, data });
    return await this.send({ address, sig, data });
  }
Example #5
Source File: index.ts    From pownft-miner with Apache License 2.0 6 votes vote down vote up
function loadConfig() : Config {

    const targetFile = join(__dirname, '..', 'config.json');
    const config = JSON.parse(readFileSync(targetFile) as any);
    const accountPath = `m/44'/60'/0'/0/${config.index}`;

    const provider = new JsonRpcProvider(config.provider);
    const wallet = Wallet.fromMnemonic(config.mnemonic, accountPath).connect(provider);

    const gasLimit = parseUnits(BigNumber.from(config.gasLimit).toString(), 'gwei');

    return {
        provider,
        signer: wallet,
        numCores: config.numCores,
        gasLimit,
        chunkSize: config.chunkSize,
        dryRun: config.dryRun,
    }    
}
Example #6
Source File: seed.ts    From zora-v1-subgraph with MIT License 6 votes vote down vote up
async function setUpNewCurrency(
  provider: JsonRpcProvider,
  masterWallet: Wallet,
  marketAddress: string,
  name: string,
  symbol: string
) {
  let currencyAddress = await deployCurrency(masterWallet, name, symbol)

  // mint 100,000 BRECK for each wallet
  console.log('Currency Address: ', currencyAddress)

  console.log('Minting Currency for Each Generated Wallet')
  for (const wallet of generatedWallets(provider)) {
    await mintCurrency(
      masterWallet,
      currencyAddress,
      wallet.address,
      BigNumber.from('100000000000000000000000')
    )
  }

  // for each address approve the market max uint256
  console.log('Granting Approval to Market Contract for each Generated Wallet')
  for (const wallet of generatedWallets(provider)) {
    await approveCurrency(wallet, currencyAddress, marketAddress)
  }
}
Example #7
Source File: createClaimSignature.ts    From bodhi.js with Apache License 2.0 6 votes vote down vote up
createClaimSignature = (privateKey: string, tx: ClaimPayload): string => {
  const payload = createClaimPayload(tx);

  const wallet = new Wallet(privateKey);

  return joinSignature(
    wallet._signingKey().signDigest(
      _TypedDataEncoder.hash(
        payload.domain,
        {
          Transaction: payload.types.Transaction
        },
        payload.message
      )
    )
  );
}
Example #8
Source File: HoprChannels.spec.ts    From hoprnet with GNU General Public License v3.0 6 votes vote down vote up
describe('funding HoprChannel catches failures', function () {
  let fixtures: Awaited<ReturnType<typeof useFixtures>>, channels: HoprChannels, accountA: Wallet
  before(async function () {
    // All of these tests revert, so we can rely on stateless single fixture.
    fixtures = await useFixtures()
    channels = fixtures.channels
    accountA = fixtures.accountA
    await fixtures.fundAndApprove(accountA, 100)
  })

  it('should fail to fund channel A->A', async function () {
    await expect(
      channels.connect(accountA).fundChannelMulti(ACCOUNT_A.address, ACCOUNT_A.address, '70', '30')
    ).to.be.revertedWith('source and destination must not be the same')
  })

  it('should fail to fund channel 0->A', async function () {
    await expect(
      channels.connect(accountA).fundChannelMulti(ethers.constants.AddressZero, ACCOUNT_B.address, '70', '30')
    ).to.be.revertedWith('source must not be empty')
  })

  it('should fail to fund channel A->0', async function () {
    await expect(
      channels.connect(accountA).fundChannelMulti(ACCOUNT_A.address, ethers.constants.AddressZero, '70', '30')
    ).to.be.revertedWith('destination must not be empty')
  })

  it('should fail to fund a channel with 0 amount', async function () {
    await expect(
      channels.connect(accountA).fundChannelMulti(ACCOUNT_A.address, ACCOUNT_B.address, '0', '0')
    ).to.be.revertedWith('amount must be greater than 0')
  })
})
Example #9
Source File: mediaInfo.ts    From core with GNU General Public License v3.0 6 votes vote down vote up
async function start() {
  const args = require('minimist')(process.argv.slice(2), {
    string: ['tokenURI', 'metadataURI', 'contentHash', 'metadataHash'],
  });

  if (!args.chainId) {
    throw new Error('--chainId chain ID is required');
  }
  if (!args.tokenId && args.tokenId !== 0) {
    throw new Error('--tokenId token ID is required');
  }
  const path = `${process.cwd()}/.env${
    args.chainId === 1 ? '.prod' : args.chainId === 4 ? '.dev' : '.local'
  }`;
  await require('dotenv').config({ path });
  const provider = new JsonRpcProvider(process.env.RPC_ENDPOINT);
  const wallet = new Wallet(`0x${process.env.PRIVATE_KEY}`, provider);
  const sharedAddressPath = `${process.cwd()}/addresses/${args.chainId}.json`;
  // @ts-ignore
  const addressBook = JSON.parse(await fs.readFileSync(sharedAddressPath));
  if (!addressBook.media) {
    throw new Error(`Media contract has not yet been deployed`);
  }

  const media = MediaFactory.connect(addressBook.media, wallet);

  const tokenURI = await media.tokenURI(args.tokenId);
  const contentHash = await media.tokenContentHashes(args.tokenId);
  const metadataURI = await media.tokenMetadataURI(args.tokenId);
  const metadataHash = await media.tokenMetadataHashes(args.tokenId);

  console.log(`Media Information for token ${args.tokenId}`);
  console.log({ tokenURI, contentHash, metadataURI, metadataHash });
}
Example #10
Source File: index.ts    From snapshot.js with MIT License 5 votes vote down vote up
async unsubscribe(
    web3: Web3Provider | Wallet,
    address: string,
    message: Unsubscribe
  ) {
    return await this.sign(web3, address, message, unsubscribeTypes);
  }
Example #11
Source File: bridge.test.ts    From bodhi.js with Apache License 2.0 5 votes vote down vote up
describe('e2e test', () => {
  const signer = new Wallet('0x5a214c9bcb10dfe58af9b349cad6f4564cd6f10d880bdfcf780e5812c3cbc855');
  const provider = EvmRpcProvider.from(endpoint);

  const bridge = new Eip1193Bridge(provider, signer as any);

  it('eth_getBlockByNumber latest', async () => {
    const result = await bridge.send('eth_getBlockByNumber', ['latest', false]);

    expect(typeof result.number).equal('string');
  });

  it('eth_getBlockByNumber u32', async () => {
    await expect(bridge.send('eth_getBlockByNumber', ['0xffffffff', false])).become(null);

    await expect(bridge.send('eth_getBlockByNumber', ['0x1ffffffff', false])).rejectedWith(
      'block number should be less than u32'
    );
  });

  it('eth_getBlockByHash', async () => {
    await expect(
      bridge.send('eth_getBlockByHash', ['0xff2d5d74f16df09b810225ffd9e1442250914ae6de9459477118d675713c732c', false])
    ).become(null);

    const latest = await bridge.send('eth_getBlockByNumber', ['latest', false]);
    const block = await bridge.send('eth_getBlockByHash', [latest.hash, false]);
    expect(block.hash).equal(latest.hash);
  });

  it('eth_getBalance', async () => {
    await expect(
      bridge.send('eth_getBalance', ['0xb00cB924ae22b2BBb15E10c17258D6a2af980421', '0xffffffff'])
    ).rejectedWith('header not found');
  });

  after(() => {
    provider.disconnect();
  });
});
Example #12
Source File: index.ts    From snapshot.js with MIT License 5 votes vote down vote up
async proposal(
    web3: Web3Provider | Wallet,
    address: string,
    message: Proposal
  ) {
    if (!message.discussion) message.discussion = '';
    return await this.sign(web3, address, message, proposalTypes);
  }
Example #13
Source File: index.ts    From sdk with ISC License 5 votes vote down vote up
makeWalletSignerWithProvider = (
    chainId: number,
    privKey: string
): Wallet => new Wallet(privKey, rpcProviderForChain(chainId))
Example #14
Source File: utils.ts    From hardhat-deploy with MIT License 5 votes vote down vote up
function transformNamedAccounts(
  configNamedAccounts: {[name: string]: any},
  chainIdGiven: string | number,
  accounts: string[],
  networkConfigName: string
): {
  namedAccounts: {[name: string]: string};
  unnamedAccounts: string[];
  unknownAccounts: string[];
  addressesToProtocol: {[address: string]: string};
} {
  const addressesToProtocol: {[address: string]: string} = {};
  const unknownAccountsDict: {[address: string]: boolean} = {};
  const knownAccountsDict: {[address: string]: boolean} = {};
  for (const account of accounts) {
    knownAccountsDict[account.toLowerCase()] = true;
  }
  const namedAccounts: {[name: string]: string} = {};
  const usedAccounts: {[address: string]: boolean} = {};
  // TODO transform into checksum  address
  if (configNamedAccounts) {
    const accountNames = Object.keys(configNamedAccounts);
    // eslint-disable-next-line no-inner-declarations
    function parseSpec(spec: any): string | undefined {
      let address: string | undefined;
      switch (typeof spec) {
        case 'string':
          // eslint-disable-next-line no-case-declarations
          const protocolSplit = spec.split('://');
          if (protocolSplit.length > 1) {
            if (protocolSplit[0].toLowerCase() === 'ledger') {
              address = protocolSplit[1];
              addressesToProtocol[address.toLowerCase()] =
                protocolSplit[0].toLowerCase();
              // knownAccountsDict[address.toLowerCase()] = true; // TODO ? this would prevent auto impersonation in fork/test
            } else if (protocolSplit[0].toLowerCase() === 'privatekey') {
              address = new Wallet(protocolSplit[1]).address;
              addressesToProtocol[address.toLowerCase()] =
                'privatekey://' + protocolSplit[1];
            } else {
              throw new Error(
                `unsupported protocol ${protocolSplit[0]}:// for named accounts`
              );
            }
          } else {
            if (spec.slice(0, 2).toLowerCase() === '0x') {
              if (!isAddress(spec)) {
                throw new Error(
                  `"${spec}" is not a valid address, if you used to put privateKey there, use the "privatekey://" prefix instead`
                );
              }
              address = spec;
            } else {
              address = parseSpec(configNamedAccounts[spec]);
            }
          }
          break;
        case 'number':
          if (accounts) {
            address = accounts[spec];
          }
          break;
        case 'undefined':
          break;
        case 'object':
          if (spec) {
            if (spec.type === 'object') {
              address = spec;
            } else {
              const newSpec = chainConfig(
                spec,
                chainIdGiven,
                networkConfigName
              );
              if (typeof newSpec !== 'undefined') {
                address = parseSpec(newSpec);
              }
            }
          }
          break;
      }
      if (address) {
        if (typeof address === 'string') {
          address = getAddress(address);
        }
      }
      return address;
    }

    for (const accountName of accountNames) {
      const spec = configNamedAccounts[accountName];
      const address = parseSpec(spec);
      if (address) {
        namedAccounts[accountName] = address;
        usedAccounts[address.toLowerCase()] = true;
        if (!knownAccountsDict[address.toLowerCase()]) {
          unknownAccountsDict[address.toLowerCase()] = true;
        }
      }
    }
  }
  const unnamedAccounts = [];
  for (const address of accounts) {
    if (!usedAccounts[address.toLowerCase()]) {
      unnamedAccounts.push(getAddress(address));
    }
  }
  return {
    namedAccounts,
    unnamedAccounts,
    unknownAccounts: Object.keys(unknownAccountsDict).map(getAddress),
    addressesToProtocol,
  };
}
Example #15
Source File: claim.test.ts    From bodhi.js with Apache License 2.0 5 votes vote down vote up
describe('create Claim Signature', () => {
  it('invalid substrateAddress', async () => {
    expect(() => {
      createClaimPayload({
        salt: '0x000',
        chainId: 55,
        substrateAddress: '5G'
      });
    }).throw('invalid substrateAddress');
  });

  it('missing salt', async () => {
    expect(() => {
      // @ts-ignore
      createClaimPayload({
        chainId: 55,
        substrateAddress: '5G'
      });
    }).throw('missing salt');
  });

  it('missing chainId', async () => {
    expect(() => {
      // @ts-ignore
      createClaimPayload({
        salt: '0x000',
        substrateAddress: '5G'
      });
    }).throw('missing chainId');
  });

  it('claim payload', async () => {
    const payload = createClaimPayload({
      salt: '0x702ba380a53e096363da1b73f2a05faff77c274fd356253eb877e6d221b7ffe7',
      chainId: 595,
      substrateAddress: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'
    });

    expect(payload.domain.name).equal('Acala EVM claim');
  });

  it('create claim signature', async () => {
    const wallet = new Wallet('0xc192608c543cda2e2c2a2ec33237d4e9d0922831bdac423c90af697dee017aaf');

    expect(wallet.address).equal('0x183d3DDcF0D69A84677ab1aC42a7014dA7971695');

    const signature = createClaimSignature(wallet.privateKey, {
      salt: '0x702ba380a53e096363da1b73f2a05faff77c274fd356253eb877e6d221b7ffe7',
      chainId: 595,
      substrateAddress: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'
    });

    expect(signature).equal(
      '0xd71741a11559335ce336daad28bb04bd0ed2977b9da31a1f7686162209a151676cbc475679844ff98febe3e439fec3c24ddf5560467a5f53cc1472d4eada8a211c'
    );
  });
});
Example #16
Source File: relayer.ts    From snapshot-hub with MIT License 5 votes vote down vote up
wallet = new Wallet(privateKey)
Example #17
Source File: HoprChannels.spec.ts    From hoprnet with GNU General Public License v3.0 5 votes vote down vote up
useFixtures = deployments.createFixture(
  async (_, ops: { skipAnnounceForAccountA: boolean; skipAnnounceForAccountB: boolean }) => {
    const deployer = ethers.provider.getSigner()

    const deployerPubKey = await recoverPublicKeyFromSigner(deployer)
    const accountA = new ethers.Wallet(ACCOUNT_A.privateKey).connect(ethers.provider)
    const accountAPubKey = PublicKey.fromPrivKeyString(accountA.privateKey)
    const accountB = new ethers.Wallet(ACCOUNT_B.privateKey).connect(ethers.provider)
    const accountBPubKey = PublicKey.fromPrivKeyString(accountB.privateKey)

    // run migrations
    const contracts = await deployments.fixture()
    const token = (await ethers.getContractFactory('HoprToken')).attach(contracts['HoprToken'].address) as HoprToken

    const channels = (await ethers.getContractFactory('HoprChannels')).attach(
      contracts['HoprChannels'].address
    ) as HoprChannels

    const mockChannels = (await (
      await ethers.getContractFactory('ChannelsMock', deployer)
    ).deploy(token.address, 0)) as ChannelsMock

    // create deployer the minter
    const minterRole = await token.MINTER_ROLE()
    await token.connect(deployer).grantRole(minterRole, await deployer.getAddress())

    const fundEther = async (addr: string, amount: BigNumberish) =>
      await deployer.sendTransaction({ to: addr, value: amount })

    const fund = async (addr: string, amount: BigNumberish) =>
      await token.connect(deployer).mint(addr, amount + '', ethers.constants.HashZero, ethers.constants.HashZero)

    const approve = async (account: Wallet, amount: BigNumberish) =>
      await token.connect(account).approve(channels.address, amount)

    const fundAndApprove = async (account: Wallet, amount: BigNumberish) => {
      await fund(account.address, amount)
      await approve(account, amount)
    }

    const TICKET_AB_WIN = await createTicket(
      {
        recipient: ACCOUNT_B.address,
        proofOfRelaySecret: PROOF_OF_RELAY_SECRET_0,
        ticketEpoch: '0',
        ticketIndex: '1',
        amount: '10',
        winProb: WIN_PROB_100.toString(),
        channelEpoch: '1'
      },
      ACCOUNT_A,
      SECRET_1
    )
    await fundEther(accountA.address, ethers.utils.parseEther(ChannelStatus.Open + ''))
    await fundEther(accountB.address, ethers.utils.parseEther(ChannelStatus.Open + ''))

    // announce
    if (!ops?.skipAnnounceForAccountA) {
      await channels
        .connect(accountA)
        .announce(accountAPubKey.toUncompressedPubKeyHex(), [], { gasLimit: BigNumber.from('100000') })
    }
    if (!ops?.skipAnnounceForAccountB) {
      await channels
        .connect(accountB)
        .announce(accountBPubKey.toUncompressedPubKeyHex(), [], { gasLimit: BigNumber.from('100000') })
    }

    return {
      token,
      channels,
      deployer,
      deployerPubKey,
      accountA,
      accountAPubKey,
      accountB,
      accountBPubKey,
      fund,
      approve,
      mockChannels,
      fundAndApprove,
      TICKET_AB_WIN
    }
  }
)
Example #18
Source File: media.ts    From zora-v1-subgraph with MIT License 5 votes vote down vote up
export async function totalSupply(mediaAddress: string, wallet: Wallet) {
  const media = MediaFactory.connect(mediaAddress, wallet)
  return await media.totalSupply()
}
Example #19
Source File: deploy.ts    From core with GNU General Public License v3.0 5 votes vote down vote up
async function start() {
  const args = require('minimist')(process.argv.slice(2));

  if (!args.chainId) {
    throw new Error('--chainId chain ID is required');
  }
  const path = `${process.cwd()}/.env${
    args.chainId === 1 ? '.prod' : args.chainId === 4 ? '.dev' : '.local'
  }`;
  await require('dotenv').config({ path });
  const provider = new JsonRpcProvider(process.env.RPC_ENDPOINT);
  const wallet = new Wallet(`0x${process.env.PRIVATE_KEY}`, provider);
  const sharedAddressPath = `${process.cwd()}/addresses/${args.chainId}.json`;
  // @ts-ignore
  const addressBook = JSON.parse(await fs.readFileSync(sharedAddressPath));
  if (addressBook.market) {
    throw new Error(
      `market already exists in address book at ${sharedAddressPath}. Please move it first so it is not overwritten`
    );
  }
  if (addressBook.media) {
    throw new Error(
      `media already exists in address book at ${sharedAddressPath}. Please move it first so it is not overwritten`
    );
  }

  console.log('Deploying Market...');
  const deployTx = await new MarketFactory(wallet).deploy();
  console.log('Deploy TX: ', deployTx.deployTransaction.hash);
  await deployTx.deployed();
  console.log('Market deployed at ', deployTx.address);
  addressBook.market = deployTx.address;

  console.log('Deploying Media...');
  const mediaDeployTx = await new MediaFactory(wallet).deploy(
    addressBook.market
  );
  console.log(`Deploy TX: ${mediaDeployTx.deployTransaction.hash}`);
  await mediaDeployTx.deployed();
  console.log(`Media deployed at ${mediaDeployTx.address}`);
  addressBook.media = mediaDeployTx.address;

  console.log('Configuring Market...');
  const market = MarketFactory.connect(addressBook.market, wallet);
  const tx = await market.configure(addressBook.media);
  console.log(`Market configuration tx: ${tx.hash}`);
  await tx.wait();
  console.log(`Market configured.`);

  await fs.writeFile(sharedAddressPath, JSON.stringify(addressBook, null, 2));
  console.log(`Contracts deployed and configured. ☼☽`);
}
Example #20
Source File: media.ts    From zora-v1-subgraph with MIT License 5 votes vote down vote up
export async function burn(mediaAddress: string, wallet: Wallet, tokenId: BigNumber) {
  const media = await MediaFactory.connect(mediaAddress, wallet)
  const tx = await media.burn(tokenId)
  console.log(tx)
  await tx.wait()
}
Example #21
Source File: mint.ts    From core with GNU General Public License v3.0 5 votes vote down vote up
async function start() {
  const args = require('minimist')(process.argv.slice(2), {
    string: ['tokenURI', 'metadataURI', 'contentHash', 'metadataHash'],
  });

  if (!args.chainId) {
    throw new Error('--chainId chain ID is required');
  }
  if (!args.tokenURI) {
    throw new Error('--tokenURI token URI is required');
  }
  if (!args.metadataURI) {
    throw new Error('--metadataURI metadata URI is required');
  }
  if (!args.contentHash) {
    throw new Error('--contentHash content hash is required');
  }
  if (!args.metadataHash) {
    throw new Error('--metadataHash content hash is required');
  }
  if (!args.creatorShare && args.creatorShare !== 0) {
    throw new Error('--creatorShare creator share is required');
  }
  const path = `${process.cwd()}/.env${
    args.chainId === 1 ? '.prod' : args.chainId === 4 ? '.dev' : '.local'
  }`;
  await require('dotenv').config({ path });
  const provider = new JsonRpcProvider(process.env.RPC_ENDPOINT);
  // const wallet = new Wallet(`0x${process.env.PRIVATE_KEY}`, provider);
  const wallet = new Wallet(`0x${process.env.PRIVATE_KEY}`, provider);
  const sharedAddressPath = `${process.cwd()}/addresses/${args.chainId}.json`;
  // @ts-ignore
  const addressBook = JSON.parse(await fs.readFileSync(sharedAddressPath));
  if (!addressBook.media) {
    throw new Error(`Media contract has not yet been deployed`);
  }

  const media = MediaFactory.connect(addressBook.media, wallet);

  console.log(
    'Minting... ',
    args.tokenURI,
    args.contentHash,
    args.metadataURI,
    args.metadataHash
  );

  await media.mint(
    {
      tokenURI: args.tokenURI,
      metadataURI: args.metadataURI,
      contentHash: Uint8Array.from(Buffer.from(args.contentHash, 'hex')),
      metadataHash: Uint8Array.from(Buffer.from(args.metadataHash, 'hex')),
    },
    {
      prevOwner: Decimal.new(0),
      creator: Decimal.new(args.creatorShare),
      owner: Decimal.new(100 - args.creatorShare),
    }
  );

  console.log(`New piece is minted ☼☽`);
}
Example #22
Source File: auctionHouse.ts    From zora-v1-subgraph with MIT License 5 votes vote down vote up
async function runAuction() {
  const args = require('minimist')(process.argv.slice(2))

  if (!args.chainId) {
    throw new Error('--chainId chain ID is required')
  }
  if(!args.tokenId && args.tokenId !==  0) {
    throw new Error('--tokenId is required')
  }
  const path = `${process.cwd()}/.env${
    args.chainId === 1 ? '.prod' : args.chainId === 4 ? '.dev' : '.local'
  }`
  await require('dotenv').config({path})

  const provider = new JsonRpcProvider(process.env.RPC_ENDPOINT)

  let [creator, curator, bidder1, bidder2] = privateKeys.map((pk) => new Wallet(pk, provider))

  const sharedAddressPath = `${process.cwd()}/config/${args.chainId}.json`
  // @ts-ignore
  const config = JSON.parse(await fs.readFile(sharedAddressPath))

  if (config.mediaAddress === null) {
    throw new Error('media address not specified in config')
  }

  if (config.marketAddress === null) {
    throw new Error('market address not specified in config')
  }

  if(config.auctionHouseAddress === null) {
    throw new Error('auctionHouse address not specified in config')
  }

  const { mediaAddress, marketAddress, auctionHouseAddress} = config;

  const TENTH_ETH = ethers.utils.parseUnits("0.1", "ether") as BigNumber;
  const ONE_ETH = ethers.utils.parseUnits("1", "ether") as BigNumber;
  const TWO_ETH = ethers.utils.parseUnits("2", "ether") as BigNumber;
  const ONE_DAY = 24 * 60 * 60;

  const media = MediaFactory.connect(mediaAddress, creator)
  // @ts-ignore
  const auction = AuctionHouse__factory.connect(auctionHouseAddress, creator)

  // Approve the auction
  console.log('approving transfer to auction')
  await (await media.approve(auctionHouseAddress, args.tokenId)).wait()


  // Create the auction
  console.log('creating auction')
  await (await auction.createAuction(args.tokenId, mediaAddress, ONE_DAY, TENTH_ETH.toString(), curator.address, 15, ethers.constants.AddressZero)).wait()

  console.log('approving auction as curator')
  // @ts-ignore
  await (await auction.connect(curator).setAuctionApproval(mediaAddress, args.tokenId, true)).wait()

  console.log('Creating first bid')
  await auction
    // @ts-ignore
    .connect(bidder1)
    .createBid(media.address, 0, ONE_ETH.toString(), { value: ONE_ETH.toString() });

  console.log('Creating second bid')
  await auction
    // @ts-ignore
    .connect(bidder2)
    .createBid(media.address, 0, TWO_ETH.toString(), { value: TWO_ETH.toString() });

  console.log('fast forwarding time')
  await provider.send("evm_increaseTime", [
    ONE_DAY
  ]);

  await auction.endAuction(media.address, args.tokenId);
}
Example #23
Source File: tx.test.ts    From bodhi.js with Apache License 2.0 4 votes vote down vote up
describe('transaction tests', () => {
  const endpoint = process.env.ENDPOINT_URL || 'ws://127.0.0.1:9944';
  const provider = EvmRpcProvider.from(endpoint);

  const account1 = evmAccounts[0];
  const account2 = evmAccounts[1];
  const account3 = evmAccounts[2];
  const account4 = evmAccounts[3];
  const wallet1 = new Wallet(account1.privateKey).connect(provider as any);
  const wallet2 = new Wallet(account2.privateKey).connect(provider as any);
  const wallet3 = new Wallet(account3.privateKey).connect(provider as any);
  const wallet4 = new Wallet(account4.privateKey).connect(provider as any);

  let chainId: number;
  let storageByteDeposit: bigint;
  let txFeePerGas: bigint;
  let txGasLimit: BigNumber;
  let txGasPrice: BigNumber;

  before('prepare common variables', async () => {
    await provider.isReady();

    chainId = await provider.chainId();
    storageByteDeposit = (provider.api.consts.evm.storageDepositPerByte as UInt).toBigInt();
    txFeePerGas = (provider.api.consts.evm.txFeePerGas as UInt).toBigInt();

    ({ txGasLimit, txGasPrice } = calcEthereumTransactionParams({
      gasLimit: 2100001n,
      validUntil: 360001n,
      storageLimit: 64001n,
      txFeePerGas,
      storageByteDeposit
    }));
  });

  after('clean up', async () => {
    await provider.disconnect();
  });

  describe('test eth gas', () => {
    it('getEthResources', async () => {
      const randomWallet = Wallet.createRandom().connect(provider);

      const amount = '1000000000000000000';
      const resources = await provider.getEthResources({
        type: 0,
        from: wallet3.address,
        to: randomWallet.address,
        value: BigNumber.from(amount)
      });

      await wallet3.sendTransaction({
        type: 0,
        to: randomWallet.address,
        value: BigNumber.from(amount),
        ...resources
      });

      expect((await randomWallet.getBalance()).toString()).eq(amount);
    });

    it('getPrice', async () => {
      const randomWallet = Wallet.createRandom().connect(provider);

      const amount = '1000000000000000000';

      const params = await wallet3.populateTransaction({
        type: 0,
        to: randomWallet.address,
        value: BigNumber.from(amount)
      });

      const data = provider.validSubstrateResources({
        gasLimit: params.gasLimit,
        gasPrice: params.gasPrice
      });

      console.log({
        gasLimit: data.gasLimit.toString(),
        storageLimit: data.storageLimit.toString(),
        validUntil: data.validUntil.toString()
      });

      // expect((await randomWallet.getBalance()).toString()).eq(amount);
    });
  });

  describe('test the error tx', () => {
    it('InvalidDecimals', async () => {
      await expect(
        wallet1.sendTransaction({
          type: 0,
          to: wallet2.address,
          value: 1000001,
          gasLimit: txGasLimit,
          gasPrice: txGasPrice
        })
      ).to.be.rejectedWith('InvalidDecimals');
    });

    it('OutOfFund', async () => {
      await expect(
        wallet1.sendTransaction({
          type: 0,
          to: wallet2.address,
          value: 1000000000n * 10n ** 18n,
          gasLimit: txGasLimit,
          gasPrice: txGasPrice
        })
      ).to.be.rejectedWith('OutOfFund');
    });

    it('ExistentialDeposit', async () => {
      await expect(
        wallet3.sendTransaction({
          type: 0,
          to: Wallet.createRandom().address,
          value: 1000000,
          gasLimit: txGasLimit,
          gasPrice: txGasPrice
        })
      ).to.be.rejectedWith('ExistentialDeposit');
    });
  });

  describe('test deploy contract (hello world)', () => {
    const deployHelloWorldData =
      '0x60806040526040518060400160405280600c81526020017f48656c6c6f20576f726c642100000000000000000000000000000000000000008152506000908051906020019061004f929190610062565b5034801561005c57600080fd5b50610166565b82805461006e90610134565b90600052602060002090601f01602090048101928261009057600085556100d7565b82601f106100a957805160ff19168380011785556100d7565b828001600101855582156100d7579182015b828111156100d65782518255916020019190600101906100bb565b5b5090506100e491906100e8565b5090565b5b808211156101015760008160009055506001016100e9565b5090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061014c57607f821691505b602082108114156101605761015f610105565b5b50919050565b61022e806101756000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063c605f76c14610030575b600080fd5b61003861004e565b6040516100459190610175565b60405180910390f35b6000805461005b906101c6565b80601f0160208091040260200160405190810160405280929190818152602001828054610087906101c6565b80156100d45780601f106100a9576101008083540402835291602001916100d4565b820191906000526020600020905b8154815290600101906020018083116100b757829003601f168201915b505050505081565b600081519050919050565b600082825260208201905092915050565b60005b838110156101165780820151818401526020810190506100fb565b83811115610125576000848401525b50505050565b6000601f19601f8301169050919050565b6000610147826100dc565b61015181856100e7565b93506101618185602086016100f8565b61016a8161012b565b840191505092915050565b6000602082019050818103600083015261018f818461013c565b905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806101de57607f821691505b602082108114156101f2576101f1610197565b5b5091905056fea26469706673582212204d363ed34111d1be492d4fd086e9f2df62b3c625e89ade31f30e63201ed1e24f64736f6c63430008090033';

    let partialDeployTx;

    before(() => {
      partialDeployTx = {
        chainId,
        gasLimit: txGasLimit,
        gasPrice: txGasPrice,
        data: deployHelloWorldData,
        value: BigNumber.from(0)
      };
    });

    describe('with wallet', () => {
      it('succeeds', async () => {
        const response = await wallet1.sendTransaction({
          ...partialDeployTx,
          type: 0
        });
        const receipt = await response.wait(0);

        expect(receipt.type).equal(0);
        expect(receipt.status).equal(1);
        expect(receipt.from).equal(wallet1.address);
      });
    });

    describe('with legacy EIP-155 signature', () => {
      it('serialize, parse, and send tx correctly', async () => {
        const unsignedTx: AcalaEvmTX = {
          ...partialDeployTx,
          nonce: await wallet1.getTransactionCount()
        };

        const rawTx = await wallet1.signTransaction(unsignedTx);
        const parsedTx = parseTransaction(rawTx);

        expect(parsedTx.gasPrice.eq(txGasPrice)).equal(true);
        expect(parsedTx.gasLimit.eq(txGasLimit)).equal(true);

        expect(parsedTx.from).equal(wallet1.address);
        expect(parsedTx.data).equal(deployHelloWorldData);
        expect(parsedTx.type).equal(null);
        expect(parsedTx.maxPriorityFeePerGas).equal(undefined);
        expect(parsedTx.maxFeePerGas).equal(undefined);

        const response = await provider.sendTransaction(rawTx);
        const receipt = await response.wait(0);

        expect(receipt.type).equal(0); // TODO: should be null, need to fix getPartialTransactionReceipt
        expect(receipt.status).equal(1);
        expect(receipt.from).equal(wallet1.address);
      });
    });

    describe('with EIP-1559 signature', () => {
      it('serialize, parse, and send tx correctly', async () => {
        const priorityFee = BigNumber.from(2);
        const unsignedTx: AcalaEvmTX = {
          ...partialDeployTx,
          nonce: await wallet1.getTransactionCount(),
          gasPrice: undefined,
          maxPriorityFeePerGas: priorityFee,
          maxFeePerGas: txGasPrice,
          type: 2
        };

        const rawTx = await wallet1.signTransaction(unsignedTx);
        const parsedTx = parseTransaction(rawTx);

        expect(parsedTx.maxFeePerGas.eq(txGasPrice)).equal(true);
        expect(parsedTx.maxPriorityFeePerGas.eq(priorityFee)).equal(true);
        expect(parsedTx.gasLimit.eq(txGasLimit)).equal(true);

        expect(parsedTx.from).equal(wallet1.address);
        expect(parsedTx.data).equal(deployHelloWorldData);
        expect(parsedTx.type).equal(2);
        expect(parsedTx.gasPrice).equal(null);

        const response = await provider.sendTransaction(rawTx);
        const receipt = await response.wait(0);

        expect(receipt.type).equal(0); // TODO: should be 2, need to fix getPartialTransactionReceipt
        expect(receipt.status).equal(1);
        expect(receipt.from).equal(wallet1.address);
      });
    });

    describe('with EIP-712 signature', () => {
      let rawTx1: string;
      let rawTx2: string;

      it('serialize, parse, and send tx correctly', async () => {
        const gasLimit = BigNumber.from('210000');
        const validUntil = 10000;
        const storageLimit = 100000;

        const unsignEip712Tx: AcalaEvmTX = {
          ...partialDeployTx,
          nonce: await wallet1.getTransactionCount(),
          salt: provider.genesisHash,
          gasLimit,
          validUntil,
          storageLimit,
          type: 0x60,
          accessList: []
        };

        const sig = signTransaction(account1.privateKey, unsignEip712Tx);
        rawTx1 = serializeTransaction(unsignEip712Tx, sig);
        const parsedTx = parseTransaction(rawTx1);

        expect(parsedTx.gasLimit.eq(gasLimit)).equal(true);
        expect(parsedTx.validUntil.eq(validUntil)).equal(true);
        expect(parsedTx.storageLimit.eq(storageLimit)).equal(true);

        expect(parsedTx.from).equal(wallet1.address);
        expect(parsedTx.data).equal(deployHelloWorldData);
        expect(parsedTx.type).equal(96);
        expect(parsedTx.maxPriorityFeePerGas).equal(undefined);
        expect(parsedTx.maxFeePerGas).equal(undefined);
      });

      it('eip712 tip', async () => {
        const gasLimit = BigNumber.from('210000');
        const validUntil = 10000;
        const storageLimit = 100000;

        const unsignEip712Tx: AcalaEvmTX = {
          ...partialDeployTx,
          nonce: (await wallet1.getTransactionCount()) + 1,
          salt: provider.genesisHash,
          gasLimit,
          validUntil,
          storageLimit,
          tip: 2,
          type: 0x60,
          accessList: []
        };

        const sig = signTransaction(account1.privateKey, unsignEip712Tx);
        rawTx2 = serializeTransaction(unsignEip712Tx, sig);
        const parsedTx = parseTransaction(rawTx2);

        expect(parsedTx.gasLimit.eq(gasLimit)).equal(true);
        expect(parsedTx.validUntil.eq(validUntil)).equal(true);
        expect(parsedTx.storageLimit.eq(storageLimit)).equal(true);

        expect(parsedTx.tip.eq(2)).equal(true);
        expect(parsedTx.from).equal(wallet1.address);
        expect(parsedTx.data).equal(deployHelloWorldData);
        expect(parsedTx.type).equal(96);
        expect(parsedTx.maxPriorityFeePerGas).equal(undefined);
        expect(parsedTx.maxFeePerGas).equal(undefined);
      });

      it('send eip712 tx', async () => {
        await provider.sendTransaction(rawTx1);
        await provider.sendTransaction(rawTx2);
      });
    });
  });

  describe('test call contract (transfer ACA)', () => {
    const ACADigits = provider.api.registry.chainDecimals[0];
    const acaContract = new Contract(ADDRESS.ACA, ACAABI.abi, wallet1);
    const iface = new Interface(ACAABI.abi);
    const queryBalance = (addr) => acaContract.balanceOf(addr) as BigNumber;
    const transferAmount = parseUnits('100', ACADigits);
    let partialTransferTX: any;

    before(() => {
      partialTransferTX = {
        chainId,
        to: ADDRESS.ACA,
        gasLimit: txGasLimit,
        gasPrice: txGasPrice,
        data: iface.encodeFunctionData('transfer', [account2.evmAddress, transferAmount]),
        value: BigNumber.from(0)
      };
    });

    it('evm address match', () => {
      expect(computeDefaultSubstrateAddress(account1.evmAddress)).to.equal(account1.defaultSubstrateAddress);
      expect(computeDefaultSubstrateAddress(account2.evmAddress)).to.equal(account2.defaultSubstrateAddress);
    });

    describe('with provider', () => {
      it('has correct balance after transfer', async () => {
        const pairs = createTestPairs();
        const alice = pairs.alice;

        const oneAca = 10n ** BigInt(ACADigits);
        const amount = 1000n * oneAca;
        const balance = await queryBalance(account1.evmAddress);

        const extrinsic = provider.api.tx.balances.transfer(account1.defaultSubstrateAddress, amount);
        await extrinsic.signAsync(alice);
        await sendTx(provider.api, extrinsic);

        const _balance = await queryBalance(account1.evmAddress);
        expect(_balance.sub(balance).toBigInt()).equal(amount);
      });
    });

    describe('with legacy EIP-155 signature', () => {
      it('has correct balance after transfer', async () => {
        const balance1 = await queryBalance(account1.evmAddress);
        const balance2 = await queryBalance(account2.evmAddress);

        const transferTX: AcalaEvmTX = {
          ...partialTransferTX,
          nonce: await wallet1.getTransactionCount()
        };

        const rawTx = await wallet1.signTransaction(transferTX);
        const parsedTx = parseTransaction(rawTx);

        const response = await provider.sendTransaction(rawTx);
        const receipt = await response.wait(0);

        const _balance1 = await queryBalance(account1.evmAddress);
        const _balance2 = await queryBalance(account2.evmAddress);

        // TODO: check sender's balance is correct
        // expect(balance1.sub(_balance1).toNumber()).equal(transferAmount.toNumber() + gasUsed);
        expect(_balance2.sub(balance2).toNumber()).equal(transferAmount.toNumber());
      });
    });

    describe('with EIP-1559 signature', () => {
      it('has correct balance after transfer', async () => {
        const balance1 = await queryBalance(account1.evmAddress);
        const balance2 = await queryBalance(account2.evmAddress);

        const priorityFee = BigNumber.from(2);
        const transferTX: AcalaEvmTX = {
          ...partialTransferTX,
          nonce: await wallet1.getTransactionCount(),
          gasPrice: undefined,
          maxPriorityFeePerGas: priorityFee,
          maxFeePerGas: txGasPrice,
          type: 2
        };

        const rawTx = await wallet1.signTransaction(transferTX);
        const parsedTx = parseTransaction(rawTx);

        const response = await provider.sendTransaction(rawTx);
        const receipt = await response.wait(0);

        const _balance1 = await queryBalance(account1.evmAddress);
        const _balance2 = await queryBalance(account2.evmAddress);

        // TODO: check sender's balance is correct
        // expect(balance1.sub(_balance1).toNumber()).equal(transferAmount.toNumber() + gasUsed);
        expect(_balance2.sub(balance2).toNumber()).equal(transferAmount.toNumber());
      });
    });

    describe('with EIP-712 signature', () => {
      it('has correct balance after transfer', async () => {
        const balance1 = await queryBalance(account1.evmAddress);
        const balance2 = await queryBalance(account2.evmAddress);

        const gasLimit = BigNumber.from('210000');
        const validUntil = 10000;
        const storageLimit = 100000;

        const transferTX: AcalaEvmTX = {
          ...partialTransferTX,
          nonce: await wallet1.getTransactionCount(),
          salt: provider.genesisHash,
          gasLimit,
          validUntil,
          storageLimit,
          type: 0x60,
          accessList: []
        };

        const sig = signTransaction(account1.privateKey, transferTX);
        const rawTx = serializeTransaction(transferTX, sig);
        const parsedTx = parseTransaction(rawTx);

        await provider.sendTransaction(rawTx);

        const _balance1 = await queryBalance(account1.evmAddress);
        const _balance2 = await queryBalance(account2.evmAddress);

        // TODO: check sender's balance is correct
        // expect(balance1.sub(_balance1).toNumber()).equal(transferAmount.toNumber() + gasUsed);
        expect(_balance2.sub(balance2).toNumber()).equal(transferAmount.toNumber());
      });
    });
  });
});
Example #24
Source File: deploy.ts    From zora-v1-subgraph with MIT License 4 votes vote down vote up
async function start() {
  const args = require('minimist')(process.argv.slice(2))

  if (!args.chainId) {
    throw new Error('--chainId chain ID is required')
  }
  const path = `${process.cwd()}/.env${
    args.chainId === 1 ? '.prod' : args.chainId === 4 ? '.dev' : '.local'
  }`

  await require('dotenv').config({ path })
  const provider = new JsonRpcProvider(process.env.RPC_ENDPOINT)
  const wallet = new Wallet(`0x${process.env.PRIVATE_KEY}`, provider)

  const sharedAddressPath = `${process.cwd()}/config/${args.chainId}.json`
  // @ts-ignore
  const config = JSON.parse(await fs.readFileSync(sharedAddressPath))
  if (config.marketAddress) {
    throw new Error(
      `market already exists in address book at ${sharedAddressPath}. Please move it first so it is not overwritten`
    )
  }
  if (config.mediaAddress) {
    throw new Error(
      `media already exists in address book at ${sharedAddressPath}. Please move it first so it is not overwritten`
    )
  } if (!config.wethAddress && args.chainId !== 50) {
    throw new Error(
      `weth Address missing in address book at ${sharedAddressPath}. Please add it first`
    )
  }

  console.log('Deploying Market...')
  const deployTx = await new MarketFactory(wallet).deploy()
  console.log('Deploy TX: ', deployTx.deployTransaction.hash)
  await deployTx.deployed()
  console.log('Market deployed at ', deployTx.address)
  config.marketAddress = deployTx.address.substring(2)
  const receipt = await provider.getTransactionReceipt(deployTx.deployTransaction.hash)
  config.marketStartBlock = receipt.blockNumber

  console.log('Deploying Media...')
  const mediaDeployTx = await new MediaFactory(wallet).deploy(config.marketAddress)
  console.log(`Deploy TX: ${mediaDeployTx.deployTransaction.hash}`)
  await mediaDeployTx.deployed()
  console.log(`Media deployed at ${mediaDeployTx.address}`)
  config.mediaAddress = mediaDeployTx.address.substring(2)
  const mediaReceipt = await provider.getTransactionReceipt(
    mediaDeployTx.deployTransaction.hash
  )
  config.mediaStartBlock = mediaReceipt.blockNumber

  console.log('Configuring Market...')
  const market = MarketFactory.connect(config.marketAddress, wallet)
  const tx = await market.configure(config.mediaAddress)
  console.log(`Market configuration tx: ${tx.hash}`)
  await tx.wait()
  console.log(`Market configured.`)

  // Deploy WETH locally if necessary
  if(args.chainId === 50) {
    console.log('Deploying WETH...')
    // @ts-ignore
    const wethDeploy = await new WETH__factory(wallet).deploy()
    await wethDeploy.deployed()
    config.wethAddress = wethDeploy.address
  }

  console.log('Deploying Auction House')
  // @ts-ignore
  const auctionHouseDeployTx = await new AuctionHouse__factory(wallet).deploy(config.mediaAddress, config.wethAddress)
  console.log(`Deploy TX: ${auctionHouseDeployTx.deployTransaction.hash}`)
  await auctionHouseDeployTx.deployed()
  console.log(`Auction house deployed at ${auctionHouseDeployTx.address}`)
  config.auctionHouseAddress = auctionHouseDeployTx.address.substring(2)
  const auctionHouseReceipt = await provider.getTransactionReceipt(
    auctionHouseDeployTx.deployTransaction.hash
  )
  config.auctionHouseStartBlock = auctionHouseReceipt.blockNumber

  config.network = args.chainId === 4 ? 'rinkeby' : 'mainnet'

  await fs.writeFile(sharedAddressPath, JSON.stringify(config, null, 2))
  console.log(`Contracts deployed and configured. ☼☽`)
}
Example #25
Source File: ProviderInteractions-test.ts    From sdk with ISC License 4 votes vote down vote up
describe("SynapseBridge - Provider Interactions tests", async function(this: Mocha.Suite) {

    interface TestOpts {
        executeSuccess: boolean,
        canBridge:      boolean,
    }

    interface TestCase extends BridgeSwapTestCase<TestOpts> {
        callStatic: boolean,
    }

    const executeFailAmt: BigNumber = parseEther("420.696969");

    const testCases: TestCase[] = [
        {
            args: {
                tokenFrom:   Tokens.ETH,
                tokenTo:     Tokens.WETH,
                chainIdFrom: ChainId.OPTIMISM,
                chainIdTo:   ChainId.ETH,
                amountFrom:  executeFailAmt,
            },
            expected: {
                executeSuccess: false,
                canBridge:      false,
            },
            callStatic:         false,
        },
        {
            args: {
                tokenFrom:   Tokens.ETH,
                tokenTo:     Tokens.WETH,
                chainIdFrom: ChainId.BOBA,
                chainIdTo:   ChainId.ETH,
                amountFrom:  executeFailAmt,
            },
            expected: {
                executeSuccess: false,
                canBridge:      false,
            },
            callStatic:         true,
        },
        {
            args: {
                tokenFrom:   Tokens.ETH,
                tokenTo:     Tokens.WETH_E,
                chainIdFrom: ChainId.ARBITRUM,
                chainIdTo:   ChainId.AVALANCHE,
                amountFrom:  parseEther("0.005"),
            },
            expected: {
                executeSuccess: true,
                canBridge:      true,
            },
            callStatic:         true,
        },
        // {
        //     args: {
        //         tokenFrom:   Tokens.WETH_E,
        //         tokenTo:     Tokens.ETH,
        //         chainIdFrom: ChainId.AVALANCHE,
        //         chainIdTo:   ChainId.ARBITRUM,
        //         amountFrom:  parseEther("0.05"),
        //     },
        //     expected: {
        //         executeSuccess: false,
        //         canBridge:      false,
        //     },
        //     callStatic:         true,
        // },
        {
            args: {
                tokenFrom:   Tokens.ETH,
                tokenTo:     Tokens.NETH,
                chainIdFrom: ChainId.ETH,
                chainIdTo:   ChainId.OPTIMISM,
                amountFrom:  executeFailAmt,
            },
            expected: {
                executeSuccess: false,
                canBridge:      false,
            },
            callStatic:         true,
        },
        {
            args: {
                tokenFrom:   Tokens.ETH,
                tokenTo:     Tokens.NETH,
                chainIdFrom: ChainId.ETH,
                chainIdTo:   ChainId.OPTIMISM,
                amountFrom:  executeFailAmt,
            },
            expected: {
                executeSuccess: false,
                canBridge:      false,
            },
            callStatic:         false,
        },
        {
            args: {
                tokenFrom:   Tokens.NUSD,
                tokenTo:     Tokens.USDT,
                chainIdFrom: ChainId.POLYGON,
                chainIdTo:   ChainId.FANTOM,
                amountFrom:  parseEther("666"),
            },
            expected: {
                executeSuccess: false,
                canBridge:      false,
            },
            callStatic:         false,
        },
    ];

    const getBridgeEstimate = async (
        tc: TestCase,
        {
            address,
            bridgeInstance,
        }: WalletArgs
    ): Promise<EstimateOutputs> =>
        bridgeInstance.estimateBridgeTokenOutput(tc.args)
            .then(res =>
                ({
                    outputEstimate: res,
                    bridgeArgs: {
                        ...tc.args,
                        amountTo: res.amountToReceive,
                        addressTo: address,
                    }
                })
            )
            .catch(rejectPromise)

    testCases.forEach(tc => {
        const
            describeNetFromTitle: string = `${tc.args.tokenFrom.symbol} on ${Networks.networkName(tc.args.chainIdFrom)}`,
            desribeNetToTitle:    string = `${tc.args.tokenTo.symbol} on ${Networks.networkName(tc.args.chainIdTo)}`,
            execModeTitle:        string = tc.callStatic ? "(CallStatic)" : "(Signer Sends)",
            describeTitle:        string = `Test ${describeNetFromTitle} to ${desribeNetToTitle} ${execModeTitle}`,
            executionTestSuffix:  string = `should ${tc.expected.executeSuccess ? "execute succesfully" : "fail"}`;

        const
            executeTxnTestTitle = (txnKind: string): string => `${txnKind} transaction ${executionTestSuffix}`,
            approvalTxnTestTitle: string = executeTxnTestTitle("ERC20.Approve"),
            bridgeTxnTestTitle:   string = executeTxnTestTitle("SynapseBridge token bridge");

        describe(describeTitle, function(this: Mocha.Suite) {
            let
                walletArgs:     WalletArgs,
                wallet:         Wallet,
                bridgeInstance: Bridge.SynapseBridge;

            before(async function(this: Mocha.Context) {
                this.timeout(DEFAULT_TEST_TIMEOUT);

                walletArgs = await buildWalletArgs(
                    tc.args.chainIdFrom,
                    bridgeInteractionsPrivkey.privkey
                );

                wallet         = walletArgs.wallet;
                bridgeInstance = walletArgs.bridgeInstance;
            })

            function executeTxnFunc(
                tc:       TestCase,
                prom:     Promise<TxnResponse>,
                approval: boolean=false
            ): (ctx: Mocha.Context) => PromiseLike<any> {
                return async function (ctx: Mocha.Context): Promise<void | any> {
                    if (approval && tc.args.tokenFrom.isEqual(Tokens.ETH)) return

                    ctx.timeout(20*1000);

                    let execProm = executeTransaction(prom);

                    return (await (tc.expected.executeSuccess
                            ? expectFulfilled(execProm)
                            : expectRejected(execProm)
                    ))
                }
            }

            function callStaticFunc(
                tc:       TestCase,
                prom:     Promise<StaticCallResult>,
                approval: boolean=false
            ): (ctx: Mocha.Context) => PromiseLike<any> {
                return async function (ctx: Mocha.Context): Promise<void | any> {
                    if (approval && tc.args.tokenFrom.isEqual(Tokens.ETH)) return

                    ctx.timeout(5*1000);

                    let execProm = callStatic(prom);

                    return (await (tc.expected.executeSuccess
                            ? expectFulfilled(execProm)
                            : expectRejected(execProm)
                    ))
                }
            }

            let
                outputEstimate: Bridge.BridgeOutputEstimate,
                doBridgeArgs:   Bridge.BridgeTransactionParams;

            step("acquire output estimate", async function(this: Mocha.Context) {
                this.timeout(DEFAULT_TEST_TIMEOUT);

                let prom = getBridgeEstimate(tc, walletArgs);

                await expectFulfilled(prom);

                const {outputEstimate: estimate, bridgeArgs: bridgeParams} = await prom;

                expectNotZero(estimate.amountToReceive);

                outputEstimate = estimate;
                doBridgeArgs = bridgeParams;

                return
            });

            describe("- checkCanBridge()", function(this: Mocha.Suite) {
                const canBridgeTestTitle: string = `should${tc.expected.canBridge ? "" : " not"} be able to bridge`;

                it(canBridgeTestTitle, function(this: Mocha.Context, done: Mocha.Done) {
                    this.timeout(3.5*1000);
                    this.slow(2*1000);

                    let prom = bridgeInstance.checkCanBridge({
                        token: tc.args.tokenFrom,
                        signer: wallet,
                        amount: tc.args.amountFrom,
                    }).then(({canBridge}) => canBridge)

                    expect(prom).to.eventually.equal(tc.expected.canBridge).notify(done);
                })
            });

            describe("- Transaction Builders", function(this: Mocha.Suite) {
                let
                    approvalTxn:     PopulatedTransaction,
                    bridgeTxn:       PopulatedTransaction;

                const
                    approveTitle: string = "approval transaction should be populated successfully",
                    bridgeTitle:  string = "bridge transaction should be populated successfully";

                step(approveTitle, async function(this: Mocha.Context) {
                    if (tc.args.tokenFrom.isEqual(Tokens.ETH)) return
                    this.timeout(DEFAULT_TEST_TIMEOUT);

                    return (await expectFulfilled(
                        bridgeInstance
                            .buildApproveTransaction({token: tc.args.tokenFrom})
                            .then((txn) => approvalTxn = txn)
                    ))
                });

                step(bridgeTitle, async function(this: Mocha.Context) {
                    this.timeout(DEFAULT_TEST_TIMEOUT);

                    return (await expectFulfilled(
                        bridgeInstance.buildBridgeTokenTransaction(doBridgeArgs)
                            .then((txn) => bridgeTxn = txn)
                    ))
                });

                const approval = true;

                step(approvalTxnTestTitle, async function(this: Mocha.Context) {
                    if (tc.callStatic) {
                        return await callStaticFunc(
                            tc,
                            staticCallPopulatedTransaction(approvalTxn, wallet),
                            approval
                        )(this)
                    } else {
                        return await executeTxnFunc(
                            tc,
                            wallet.sendTransaction(approvalTxn),
                            approval
                        )(this)
                    }
                });

                step(bridgeTxnTestTitle, async function(this: Mocha.Context) {
                    if (tc.callStatic) {
                        return await callStaticFunc(
                            tc,
                            staticCallPopulatedTransaction(bridgeTxn, wallet)
                        )(this)
                    } else {
                        return await executeTxnFunc(
                            tc,
                            wallet.sendTransaction(bridgeTxn)
                        )(this)
                    }
                });
            });

            (tc.callStatic ? describe.skip : describe)("- Magic Executors", function(this: Mocha.Suite) {
                const approval = true;

                step(approvalTxnTestTitle, async function(this: Mocha.Context) {
                    return await executeTxnFunc(
                        tc,
                        bridgeInstance.executeApproveTransaction({token: tc.args.tokenFrom}, wallet),
                        approval
                    )(this)
                });

                step(bridgeTxnTestTitle, async function (this: Mocha.Context) {
                    return await executeTxnFunc(
                        tc,
                        bridgeInstance.executeBridgeTokenTransaction(doBridgeArgs, wallet)
                    )(this)
                });
            })
        })
    })
})
Example #26
Source File: bridge.test.ts    From optimism-dai-bridge with GNU Affero General Public License v3.0 4 votes vote down vote up
describe('bridge', () => {
  let l1Signer: Wallet
  let l1Escrow: L1Escrow
  let l2Signer: Wallet
  let watcher: Watcher

  let l1Dai: Dai
  let l1DAITokenBridge: L1DAITokenBridge
  let l1DAITokenBridgeV2: L1DAITokenBridge
  let l1GovernanceRelay: L1GovernanceRelay
  let l2Dai: Dai
  let l2DAITokenBridge: L2DAITokenBridge
  let l2DAITokenBridgeV2: L2DAITokenBridge
  let l2GovernanceRelay: L2GovernanceRelay
  let l2UpgradeSpell: TestBridgeUpgradeSpell

  beforeEach(async () => {
    ;({ l1Signer, l2Signer, watcher } = await setupTest())
    l1Dai = await deployUsingFactory(l1Signer, await l1.getContractFactory('Dai'), [ZERO_GAS_OPTS])
    console.log('L1 DAI: ', l1Dai.address)
    await waitForTx(l1Dai.mint(l1Signer.address, initialL1DaiNumber))

    l2Dai = await deployUsingFactory(l2Signer, await l1.getContractFactory('Dai'), [ZERO_GAS_OPTS])
    console.log('L2 DAI: ', l2Dai.address)

    l1Escrow = await deployUsingFactory(l1Signer, await l1.getContractFactory('L1Escrow'), [ZERO_GAS_OPTS])
    console.log('L1 Escrow: ', l1Escrow.address)

    const futureL1DAITokenBridgeAddress = await getAddressOfNextDeployedContract(l1Signer)
    l2DAITokenBridge = await deployUsingFactory(l2Signer, await l1.getContractFactory('L2DAITokenBridge'), [
      optimismConfig._L2_OVM_L2CrossDomainMessenger,
      l2Dai.address,
      l1Dai.address,
      futureL1DAITokenBridgeAddress,
      ZERO_GAS_OPTS,
    ])
    console.log('L2 DAI Token Bridge: ', l2DAITokenBridge.address)

    l1DAITokenBridge = await deployUsingFactory(l1Signer, await l1.getContractFactory('L1DAITokenBridge'), [
      l1Dai.address,
      l2DAITokenBridge.address,
      l2Dai.address,
      optimismConfig.Proxy__OVM_L1CrossDomainMessenger,
      l1Escrow.address,
      ZERO_GAS_OPTS,
    ])
    await waitForTx(l1Escrow.approve(l1Dai.address, l1DAITokenBridge.address, ethers.constants.MaxUint256))
    expect(l1DAITokenBridge.address).to.be.eq(
      futureL1DAITokenBridgeAddress,
      'Predicted address of l1DAITokenBridge doesnt match actual address',
    )
    console.log('L1 DAI Deposit: ', l1DAITokenBridge.address)

    const futureL1GovRelayAddress = await getAddressOfNextDeployedContract(l1Signer)
    l2GovernanceRelay = await deployUsingFactory(l2Signer, await l1.getContractFactory('L2GovernanceRelay'), [
      optimismConfig._L2_OVM_L2CrossDomainMessenger,
      futureL1GovRelayAddress,
      ZERO_GAS_OPTS,
    ])
    console.log('L2 Governance Relay: ', l2DAITokenBridge.address)

    l1GovernanceRelay = await deployUsingFactory(l1Signer, await l1.getContractFactory('L1GovernanceRelay'), [
      l2GovernanceRelay.address,
      optimismConfig.Proxy__OVM_L1CrossDomainMessenger,
      ZERO_GAS_OPTS,
    ])
    expect(l1GovernanceRelay.address).to.be.eq(
      futureL1GovRelayAddress,
      'Predicted address of l1GovernanceRelay doesnt match actual address',
    )
    console.log('L1 Governance Relay: ', l1GovernanceRelay.address)

    await waitForTx(l2Dai.rely(l2DAITokenBridge.address, ZERO_GAS_OPTS))
    await waitForTx(l2Dai.rely(l2GovernanceRelay.address, ZERO_GAS_OPTS))
    await waitForTx(l2Dai.deny(l2Signer.address, ZERO_GAS_OPTS))
    await waitForTx(l2DAITokenBridge.rely(l2GovernanceRelay.address, ZERO_GAS_OPTS))
    await waitForTx(l2DAITokenBridge.deny(l2Signer.address, ZERO_GAS_OPTS))
    console.log('Permission sanity checks...')
    expect(await getActiveWards(l2Dai)).to.deep.eq([l2DAITokenBridge.address, l2GovernanceRelay.address])
    expect(await getActiveWards(l2DAITokenBridge)).to.deep.eq([l2GovernanceRelay.address])
    console.log('Permissions updated.')
  })

  it('moves l1 tokens to l2', async () => {
    await waitForTx(l1Dai.approve(l1DAITokenBridge.address, depositAmount))
    await waitToRelayTxsToL2(
      l1DAITokenBridge.depositERC20(l1Dai.address, l2Dai.address, depositAmount, defaultGasLimit, '0x'),
      watcher,
    )

    const balance = await l2Dai.balanceOf(l1Signer.address)
    expect(balance.toString()).to.be.eq(depositAmount)
  })

  it('moves l2 tokens to l1', async () => {
    await waitForTx(l1Dai.approve(l1DAITokenBridge.address, depositAmount))
    await waitToRelayTxsToL2(
      l1DAITokenBridge.depositERC20(l1Dai.address, l2Dai.address, depositAmount, defaultGasLimit, '0x'),
      watcher,
    )

    const balance = await l2Dai.balanceOf(l1Signer.address)
    expect(balance.toString()).to.be.eq(depositAmount)

    await relayMessagesToL1(
      l2DAITokenBridge.withdraw(l2Dai.address, depositAmount, defaultGasLimit, '0x', ZERO_GAS_OPTS),
      watcher,
      l1Signer,
    )

    const l2BalanceAfterWithdrawal = await l2Dai.balanceOf(l1Signer.address)
    expect(l2BalanceAfterWithdrawal.toString()).to.be.eq('0')
    const l1Balance = await l1Dai.balanceOf(l1Signer.address)
    expect(l1Balance.toString()).to.be.eq(initialL1DaiNumber)
  })

  it('upgrades the bridge through governance relay', async () => {
    const futureL2DAITokenBridgeV2Address = await getAddressOfNextDeployedContract(l1Signer)
    l2DAITokenBridgeV2 = await deployUsingFactory(l2Signer, await l1.getContractFactory('L2DAITokenBridge'), [
      optimismConfig._L2_OVM_L2CrossDomainMessenger,
      l2Dai.address,
      l1Dai.address,
      futureL2DAITokenBridgeV2Address,
      ZERO_GAS_OPTS,
    ])
    console.log('L2 DAI Token Bridge V2: ', l2DAITokenBridgeV2.address)

    l1DAITokenBridgeV2 = await deployUsingFactory(l1Signer, await l1.getContractFactory('L1DAITokenBridge'), [
      l1Dai.address,
      l2DAITokenBridgeV2.address,
      l2Dai.address,
      optimismConfig.Proxy__OVM_L1CrossDomainMessenger,
      l1Escrow.address,
      ZERO_GAS_OPTS,
    ])
    expect(l1DAITokenBridgeV2.address).to.be.eq(
      futureL2DAITokenBridgeV2Address,
      'Predicted address of l1DAITokenBridgeV2 doesnt match actual address',
    )
    await waitForTx(
      l1Escrow.approve(l1Dai.address, l1DAITokenBridgeV2.address, ethers.constants.MaxUint256, ZERO_GAS_OPTS),
    )
    console.log('L1 DAI Deposit V2: ', l1DAITokenBridgeV2.address)

    l2UpgradeSpell = await deployUsingFactory(l2Signer, await l1.getContractFactory('TestBridgeUpgradeSpell'), [
      ZERO_GAS_OPTS,
    ])
    console.log('L2 Bridge Upgrade Spell: ', l2UpgradeSpell.address)

    // Close L1 bridge V1
    await l1DAITokenBridge.connect(l1Signer).close(ZERO_GAS_OPTS)
    console.log('L1 Bridge Closed')

    // Close L2 bridge V1
    console.log('Executing spell to close L2 Bridge v1 and grant minting permissions to L2 Bridge v2')
    await waitToRelayTxsToL2(
      l1GovernanceRelay
        .connect(l1Signer)
        .relay(
          l2UpgradeSpell.address,
          l2UpgradeSpell.interface.encodeFunctionData('upgradeBridge', [
            l2DAITokenBridge.address,
            l2DAITokenBridgeV2.address,
          ]),
          spellGasLimit,
          ZERO_GAS_OPTS,
        ),
      watcher,
    )
    console.log('L2 Bridge Closed')

    console.log('Testing V2 bridge deposit/withdrawal...')
    await waitForTx(l1Dai.approve(l1DAITokenBridgeV2.address, depositAmount, ZERO_GAS_OPTS))
    await waitToRelayTxsToL2(
      l1DAITokenBridgeV2.depositERC20(
        l1Dai.address,
        l2Dai.address,
        depositAmount,
        defaultGasLimit,
        '0x',
        ZERO_GAS_OPTS,
      ),
      watcher,
    )

    const balance = await l2Dai.balanceOf(l1Signer.address)
    expect(balance.toString()).to.be.eq(depositAmount)

    await relayMessagesToL1(
      l2DAITokenBridgeV2.withdraw(l2Dai.address, depositAmount, defaultGasLimit, '0x', ZERO_GAS_OPTS),
      watcher,
      l1Signer,
    )

    const l2BalanceAfterWithdrawal = await l2Dai.balanceOf(l1Signer.address)
    expect(l2BalanceAfterWithdrawal.toString()).to.be.eq('0')
    const l1Balance = await l1Dai.balanceOf(l1Signer.address)
    expect(l1Balance.toString()).to.be.eq(initialL1DaiNumber)
  })
})
Example #27
Source File: endpoint.test.ts    From bodhi.js with Apache License 2.0 4 votes vote down vote up
describe('eth_sendRawTransaction', () => {
  const eth_sendRawTransaction = rpcGet('eth_sendRawTransaction');
  const eth_getTransactionCount = rpcGet('eth_getTransactionCount');
  const eth_getBalance = rpcGet('eth_getBalance');
  const eth_chainId = rpcGet('eth_chainId');
  const eth_gasPrice = rpcGet('eth_gasPrice');
  const eth_estimateGas = rpcGet('eth_estimateGas');

  const account1 = evmAccounts[0];
  const account2 = evmAccounts[1];
  const wallet1 = new Wallet(account1.privateKey);

  let chainId: number;
  let txGasLimit: BigNumber;
  let txGasPrice: BigNumber;
  let genesisHash: string;

  let api: ApiPromise;

  before('prepare common variables', async () => {
    chainId = BigNumber.from((await eth_chainId()).data.result).toNumber();

    txGasLimit = BigNumber.from(34132001);
    txGasPrice = BigNumber.from(200786445289);

    const endpoint = process.env.ENDPOINT_URL || 'ws://127.0.0.1:9944';
    const wsProvider = new WsProvider(endpoint);
    api = await ApiPromise.create({ provider: wsProvider });

    genesisHash = api.genesisHash.toHex(); // TODO: why EIP-712 salt has to be genesis hash?
  });

  after(async () => {
    await api.disconnect();
  });

  describe('deploy contract (hello world)', () => {
    const deployHelloWorldData =
      '0x60806040526040518060400160405280600c81526020017f48656c6c6f20576f726c642100000000000000000000000000000000000000008152506000908051906020019061004f929190610062565b5034801561005c57600080fd5b50610166565b82805461006e90610134565b90600052602060002090601f01602090048101928261009057600085556100d7565b82601f106100a957805160ff19168380011785556100d7565b828001600101855582156100d7579182015b828111156100d65782518255916020019190600101906100bb565b5b5090506100e491906100e8565b5090565b5b808211156101015760008160009055506001016100e9565b5090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061014c57607f821691505b602082108114156101605761015f610105565b5b50919050565b61022e806101756000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063c605f76c14610030575b600080fd5b61003861004e565b6040516100459190610175565b60405180910390f35b6000805461005b906101c6565b80601f0160208091040260200160405190810160405280929190818152602001828054610087906101c6565b80156100d45780601f106100a9576101008083540402835291602001916100d4565b820191906000526020600020905b8154815290600101906020018083116100b757829003601f168201915b505050505081565b600081519050919050565b600082825260208201905092915050565b60005b838110156101165780820151818401526020810190506100fb565b83811115610125576000848401525b50505050565b6000601f19601f8301169050919050565b6000610147826100dc565b61015181856100e7565b93506101618185602086016100f8565b61016a8161012b565b840191505092915050565b6000602082019050818103600083015261018f818461013c565b905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806101de57607f821691505b602082108114156101f2576101f1610197565b5b5091905056fea26469706673582212204d363ed34111d1be492d4fd086e9f2df62b3c625e89ade31f30e63201ed1e24f64736f6c63430008090033';

    let partialDeployTx;

    before(() => {
      partialDeployTx = {
        chainId,
        gasLimit: txGasLimit,
        gasPrice: txGasPrice,
        data: deployHelloWorldData,
        value: BigNumber.from(0)
      };
    });

    describe('with legacy EIP-155 signature', () => {
      it('serialize, parse, and send tx correctly', async () => {
        const unsignedTx: AcalaEvmTX = {
          ...partialDeployTx,
          nonce: (await eth_getTransactionCount([wallet1.address, 'pending'])).data.result
        };

        const rawTx = await wallet1.signTransaction(unsignedTx);
        const parsedTx = parseTransaction(rawTx);

        expect(parsedTx.gasPrice.eq(txGasPrice)).equal(true);
        expect(parsedTx.gasLimit.eq(txGasLimit)).equal(true);

        expect(parsedTx.from).equal(wallet1.address);
        expect(parsedTx.data).equal(deployHelloWorldData);
        expect(parsedTx.type).equal(null);
        expect(parsedTx.maxPriorityFeePerGas).equal(undefined);
        expect(parsedTx.maxFeePerGas).equal(undefined);

        const res = await eth_sendRawTransaction([rawTx]);
        expect(res.status).to.equal(200);
        expect(res.data.error?.message).to.equal(undefined); // for TX error RPC will still return 200
      });
    });

    describe('with EIP-1559 signature', () => {
      it('serialize, parse, and send tx correctly', async () => {
        const priorityFee = BigNumber.from(2);
        const unsignedTx: AcalaEvmTX = {
          ...partialDeployTx,
          nonce: (await eth_getTransactionCount([wallet1.address, 'pending'])).data.result,
          gasPrice: undefined,
          maxPriorityFeePerGas: priorityFee,
          maxFeePerGas: txGasPrice,
          type: 2
        };

        const rawTx = await wallet1.signTransaction(unsignedTx);
        const parsedTx = parseTransaction(rawTx);

        expect(parsedTx.maxFeePerGas.eq(txGasPrice)).equal(true);
        expect(parsedTx.maxPriorityFeePerGas.eq(priorityFee)).equal(true);
        expect(parsedTx.gasLimit.eq(txGasLimit)).equal(true);

        expect(parsedTx.from).equal(wallet1.address);
        expect(parsedTx.data).equal(deployHelloWorldData);
        expect(parsedTx.type).equal(2);
        expect(parsedTx.gasPrice).equal(null);

        const res = await eth_sendRawTransaction([rawTx]);
        expect(res.status).to.equal(200);
        expect(res.data.error?.message).to.equal(undefined); // for TX error RPC will still return 200
      });
    });

    describe('with EIP-712 signature', () => {
      it('serialize, parse, and send tx correctly', async () => {
        const gasLimit = BigNumber.from('210000');
        const validUntil = 10000;
        const storageLimit = 100000;

        const unsignEip712Tx: AcalaEvmTX = {
          ...partialDeployTx,
          nonce: (await eth_getTransactionCount([wallet1.address, 'pending'])).data.result,
          salt: genesisHash,
          gasLimit,
          validUntil,
          storageLimit,
          type: 0x60
        };

        const sig = signTransaction(account1.privateKey, unsignEip712Tx);
        const rawTx = serializeTransaction(unsignEip712Tx, sig);
        const parsedTx = parseTransaction(rawTx);

        expect(parsedTx.gasLimit.eq(gasLimit)).equal(true);
        expect(parsedTx.validUntil.eq(validUntil)).equal(true);
        expect(parsedTx.storageLimit.eq(storageLimit)).equal(true);

        expect(parsedTx.from).equal(wallet1.address);
        expect(parsedTx.data).equal(deployHelloWorldData);
        expect(parsedTx.type).equal(96);
        expect(parsedTx.maxPriorityFeePerGas).equal(undefined);
        expect(parsedTx.maxFeePerGas).equal(undefined);

        const res = await eth_sendRawTransaction([rawTx]);
        expect(res.status).to.equal(200);
        expect(res.data.error?.message).to.equal(undefined); // for TX error RPC will still return 200
      });
    });
  });

  describe('call contract (transfer ACA)', () => {
    const ETHDigits = 18;
    const ACADigits = 12;
    const acaContract = new Contract(ADDRESS.ACA, TokenABI.abi, wallet1);
    const iface = new Interface(TokenABI.abi);
    const queryBalance = async (addr) =>
      BigNumber.from((await eth_getBalance([addr, 'latest'])).data.result).div(10 ** (ETHDigits - ACADigits));
    const transferAmount = parseUnits('100', ACADigits);
    let partialTransferTX: Partial<AcalaEvmTX>;

    before(() => {
      partialTransferTX = {
        chainId,
        to: ADDRESS.ACA,
        gasLimit: txGasLimit,
        gasPrice: txGasPrice,
        data: iface.encodeFunctionData('transfer', [account2.evmAddress, transferAmount]),
        value: BigNumber.from(0)
      };
    });

    describe('with legacy EIP-155 signature', () => {
      it('has correct balance after transfer', async () => {
        const balance1 = await queryBalance(account1.evmAddress);
        const balance2 = await queryBalance(account2.evmAddress);

        const transferTX: AcalaEvmTX = {
          ...partialTransferTX,
          nonce: (await eth_getTransactionCount([wallet1.address, 'pending'])).data.result
        };

        const rawTx = await wallet1.signTransaction(transferTX);

        const res = await eth_sendRawTransaction([rawTx]);
        expect(res.status).to.equal(200);
        expect(res.data.error?.message).to.equal(undefined); // for TX error RPC will still return 200

        const _balance1 = await queryBalance(account1.evmAddress);
        const _balance2 = await queryBalance(account2.evmAddress);

        // TODO: check gasUsed is correct
        // const gasUsed = balance1.sub(_balance1).sub(transferAmount).toBigInt();
        expect(_balance2.sub(balance2).toBigInt()).equal(transferAmount.toBigInt());
      });
    });

    describe('with EIP-1559 signature', () => {
      it('has correct balance after transfer', async () => {
        const balance1 = await queryBalance(account1.evmAddress);
        const balance2 = await queryBalance(account2.evmAddress);

        const priorityFee = BigNumber.from(2);
        const transferTX: AcalaEvmTX = {
          ...partialTransferTX,
          nonce: (await eth_getTransactionCount([wallet1.address, 'pending'])).data.result,
          gasPrice: undefined,
          maxPriorityFeePerGas: priorityFee,
          maxFeePerGas: txGasPrice,
          type: 2
        };

        const rawTx = await wallet1.signTransaction(transferTX);
        const parsedTx = parseTransaction(rawTx);

        const res = await eth_sendRawTransaction([rawTx]);
        expect(res.status).to.equal(200);
        expect(res.data.error?.message).to.equal(undefined); // for TX error RPC will still return 200

        const _balance1 = await queryBalance(account1.evmAddress);
        const _balance2 = await queryBalance(account2.evmAddress);

        // TODO: check gasUsed is correct
        // const gasUsed = balance1.sub(_balance1).sub(transferAmount).toBigInt();
        expect(_balance2.sub(balance2).toBigInt()).equal(transferAmount.toBigInt());
      });
    });

    describe('with EIP-712 signature', () => {
      it('has correct balance after transfer', async () => {
        const balance1 = await queryBalance(account1.evmAddress);
        const balance2 = await queryBalance(account2.evmAddress);

        const gasLimit = BigNumber.from('210000');
        const validUntil = 10000;
        const storageLimit = 100000;

        const transferTX: AcalaEvmTX = {
          ...partialTransferTX,
          nonce: (await eth_getTransactionCount([wallet1.address, 'pending'])).data.result,
          salt: genesisHash,
          gasLimit,
          validUntil,
          storageLimit,
          type: 0x60
        };

        const sig = signTransaction(account1.privateKey, transferTX);
        const rawTx = serializeTransaction(transferTX, sig);
        const parsedTx = parseTransaction(rawTx);

        const res = await eth_sendRawTransaction([rawTx]);
        expect(res.status).to.equal(200);
        expect(res.data.error?.message).to.equal(undefined); // for TX error RPC will still return 200

        const _balance1 = await queryBalance(account1.evmAddress);
        const _balance2 = await queryBalance(account2.evmAddress);

        // TODO: check gasUsed is correct
        // const gasUsed = balance1.sub(_balance1).sub(transferAmount).toBigInt();
        expect(_balance2.sub(balance2).toBigInt()).equal(transferAmount.toBigInt());
      });
    });
  });

  describe('MetaMask send native ACA token', () => {
    const ETHDigits = 18;
    const ACADigits = 12;
    const queryBalance = async (addr) => BigNumber.from((await eth_getBalance([addr, 'latest'])).data.result);
    const transferAmount = parseUnits('16.8668', ETHDigits);
    let partialNativeTransferTX: Partial<AcalaEvmTX>;

    const estimateGas = async (): Promise<{
      gasPrice: string;
      gasLimit: string;
    }> => {
      const gasPrice = (await eth_gasPrice([])).data.result;
      const gasLimit = (
        await eth_estimateGas([
          {
            from: account1.evmAddress,
            to: account2.evmAddress,
            value: transferAmount,
            data: null,
            gasPrice
          }
        ])
      ).data.result;

      return {
        gasPrice,
        gasLimit
      };
    };

    before(() => {
      partialNativeTransferTX = {
        chainId,
        to: account2.evmAddress,
        data: '0x',
        value: transferAmount
      };
    });

    describe('with legacy EIP-155 signature', () => {
      it('has correct balance after transfer', async () => {
        const balance1 = await queryBalance(account1.evmAddress);
        const balance2 = await queryBalance(account2.evmAddress);

        const transferTX: AcalaEvmTX = {
          ...partialNativeTransferTX,
          ...(await estimateGas()),
          nonce: (await eth_getTransactionCount([wallet1.address, 'pending'])).data.result
        };

        const rawTx = await wallet1.signTransaction(transferTX);

        const res = await eth_sendRawTransaction([rawTx]);
        expect(res.status).to.equal(200);
        expect(res.data.error?.message).to.equal(undefined); // for TX error RPC will still return 200

        const _balance1 = await queryBalance(account1.evmAddress);
        const _balance2 = await queryBalance(account2.evmAddress);

        // TODO: check gasUsed is correct
        // const gasUsed = balance1.sub(_balance1).sub(transferAmount).toBigInt();
        expect(_balance2.sub(balance2).toBigInt()).equal(transferAmount.toBigInt());
      });
    });

    describe('with EIP-1559 signature', () => {
      it('has correct balance after transfer', async () => {
        const balance1 = await queryBalance(account1.evmAddress);
        const balance2 = await queryBalance(account2.evmAddress);

        const priorityFee = BigNumber.from(2);
        const { gasPrice, gasLimit } = await estimateGas();
        const transferTX: AcalaEvmTX = {
          ...partialNativeTransferTX,
          gasLimit,
          nonce: (await eth_getTransactionCount([wallet1.address, 'pending'])).data.result,
          gasPrice: undefined,
          maxPriorityFeePerGas: priorityFee,
          maxFeePerGas: gasPrice,
          type: 2
        };

        const rawTx = await wallet1.signTransaction(transferTX);
        const parsedTx = parseTransaction(rawTx);

        const res = await eth_sendRawTransaction([rawTx]);
        expect(res.status).to.equal(200);
        expect(res.data.error?.message).to.equal(undefined); // for TX error RPC will still return 200

        const _balance1 = await queryBalance(account1.evmAddress);
        const _balance2 = await queryBalance(account2.evmAddress);

        // TODO: check gasUsed is correct
        // const gasUsed = balance1.sub(_balance1).sub(transferAmount).toBigInt();
        expect(_balance2.sub(balance2).toBigInt()).equal(transferAmount.toBigInt());
      });
    });

    describe('with EIP-712 signature', () => {
      // TODO: EIP-712 doesn't use ETH gasLimit and gasPrice, do we need to support it?
      it.skip('has correct balance after transfer', async () => {
        // const balance1 = await queryBalance(account1.evmAddress);
        // const balance2 = await queryBalance(account2.evmAddress);
        // const gasLimit = BigNumber.from('210000');
        // const validUntil = 10000;
        // const storageLimit = 100000;
        // const transferTX: AcalaEvmTX = {
        //   ...partialNativeTransferTX,
        //   ...(await estimateGas()),
        //   nonce: (await eth_getTransactionCount([wallet1.address, 'pending'])).data.result,
        //   salt: genesisHash,
        //   gasLimit,
        //   validUntil,
        //   storageLimit,
        //   type: 0x60
        // };
        // const sig = signTransaction(account1.privateKey, transferTX);
        // const rawTx = serializeTransaction(transferTX, sig);
        // const parsedTx = parseTransaction(rawTx);
        // const res = await eth_sendRawTransaction([rawTx]);
        // expect(res.status).to.equal(200);
        // expect(res.data.error?.message).to.equal(undefined); // for TX error RPC will still return 200
        // const _balance1 = await queryBalance(account1.evmAddress);
        // const _balance2 = await queryBalance(account2.evmAddress);
        // // TODO: check gasUsed is correct
        // const gasUsed = balance1.sub(_balance1).sub(transferAmount).toBigInt();
        // expect(_balance2.sub(balance2).toBigInt()).equal(transferAmount.toBigInt());
      });
    });
  });
});
Example #28
Source File: batch-append.spec.ts    From integration-tests with MIT License 4 votes vote down vote up
describe('Transaction Ingestion', () => {
  let l1Provider: JsonRpcProvider
  let l1Signer: Wallet
  let l2Signer
  let l2Provider: JsonRpcProvider
  let addressResolver: Contract

  let canonicalTransactionChain: Contract
  let ctcAddress: string

  const mnemonic = Config.Mnemonic()

  let pre

  before(async () => {
    l1Provider = new JsonRpcProvider(Config.L1NodeUrlWithPort())
    l1Signer = new Wallet(Config.DeployerPrivateKey()).connect(l1Provider)
    const web3 = new Web3Provider(
      ganache.provider({
        mnemonic,
      })
    )
    l2Provider = new OptimismProvider(Config.L2NodeUrlWithPort(), web3)
    l2Signer = await l2Provider.getSigner()

    const addressResolverAddress = add0x(Config.AddressResolverAddress())
    const AddressResolverFactory = getContractFactory('Lib_AddressManager')
    addressResolver = AddressResolverFactory.connect(l1Signer).attach(
      addressResolverAddress
    )

    ctcAddress = await addressResolver.getAddress(
      'OVM_CanonicalTransactionChain'
    )

    const CanonicalTransactionChainFactory = getContractFactory(
      'OVM_CanonicalTransactionChain'
    )

    canonicalTransactionChain = CanonicalTransactionChainFactory.connect(
      l1Signer
    ).attach(ctcAddress)
  })

  // The transactions are enqueue'd with a `to` address of i.repeat(40)
  // meaning that the `to` value is different each iteration in a deterministic
  // way. They need to be inserted into the L2 chain in an ascending order.
  // Keep track of the receipts so that the blockNumber can be compared
  // against the `L1BlockNumber` on the tx objects.
  const receipts = []
  it('should enqueue some transactions', async () => {
    // Keep track of the L2 tip before submitting any transactions so that
    // the subsequent transactions can be queried for in the next test
    pre = await l2Provider.getBlock('latest')

    // Enqueue some transactions by building the calldata and then sending
    // the transaction to Layer 1
    for (let i = 0; i < 5; i++) {
      const input = ['0x' + `${i + 1}`.repeat(40), 500_000, `0x0${i}`]
      const calldata = await canonicalTransactionChain.interface.encodeFunctionData(
        'enqueue',
        input
      )

      const txResponse = await l1Signer.sendTransaction({
        data: calldata,
        to: ctcAddress,
      })

      const receipt = await txResponse.wait()
      receipts.push(receipt)
    }

    for (const receipt of receipts) {
      receipt.should.be.a('object')
    }
  })

  // The batch submitter will notice that there are transactions
  // that are in the queue and submit them. L2 will pick up the
  // sequencer batch appended event and play the transactions.
  it('should order transactions correctly', async () => {
    // Wait until each tx from the previous test has
    // been executed
    let tip
    do {
      tip = await l2Provider.getBlock('latest')
      await sleep(5000)
    } while (tip.number !== pre.number + 5)

    const from = await l1Signer.getAddress()

    // Keep track of an index into the receipts list and
    // increment it for each block fetched.
    let receiptIndex = 0

    // Fetch blocks
    for (let i = pre.number + 1; i < pre.number + 5; i++) {
      const block = await l2Provider.getBlock(i)
      const hash = block.transactions[0]
      assert(typeof hash === 'string')
      // Use as any hack because additional properties are
      // added to the transaction response
      const tx = (await l2Provider.getTransaction(hash)) as any
      // The `to` addresses are defined in the previous test and
      // increment sequentially.
      assert.equal(tx.to, '0x' + `${i}`.repeat(40))
      // The transaction type is EIP155
      assert.equal(tx.txType, 'EIP155')
      // The queue origin is Layer 1
      assert.equal(tx.queueOrigin, 'l1')
      // the L1TxOrigin is equal to the Layer one from
      assert.equal(tx.l1TxOrigin, from.toLowerCase())
      assert.equal(typeof tx.l1BlockNumber, 'number')
      // Get the receipt and increment the recept index
      const receipt = receipts[receiptIndex++]
      assert.equal(tx.l1BlockNumber, receipt.blockNumber)
    }
  }).timeout(100000)
})