Skip to content
This repository has been archived by the owner on Nov 5, 2023. It is now read-only.

Commit

Permalink
Merge pull request #422 from web3well/bls-provider-json-rpc-provider-…
Browse files Browse the repository at this point in the history
…methods

Bls provider json rpc provider methods
  • Loading branch information
jacque006 authored Dec 14, 2022
2 parents 4d5c8d9 + 9a6612b commit da03830
Show file tree
Hide file tree
Showing 4 changed files with 258 additions and 15 deletions.
81 changes: 73 additions & 8 deletions contracts/clients-tests/BlsProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { ethers as hardhatEthers } from "hardhat";
import chai, { expect } from "chai";
import spies from "chai-spies";
import { ethers } from "ethers";
import { ethers, BigNumber } from "ethers";
import { parseEther, formatEther, id } from "ethers/lib/utils";
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";

Expand All @@ -13,6 +13,7 @@ import {
NetworkConfig,
} from "../clients/src";
import getNetworkConfig from "../shared/helpers/getNetworkConfig";
import BlsSigner, { UncheckedBlsSigner } from "../clients/src/BlsSigner";

// TODO: bls-wallet 414 Setup integration tests for BlsProvider & BlsSigner
// Can this be put in a test config/init file?
Expand Down Expand Up @@ -72,12 +73,22 @@ describe("BlsProvider", () => {
});
});

it("should return a valid signer", async () => {
it("should return a valid signer", () => {
// Arrange & Act
const blsSigner = blsProvider.getSigner(privateKey);

// Assert
expect(blsSigner._isSigner).to.be.true;
expect(blsSigner).to.be.instanceOf(BlsSigner);
});

it("should return a valid unchecked bls signer", () => {
// Arrange & Act
const uncheckedBlsSigner = blsProvider.getUncheckedSigner(privateKey);

// Assert
expect(uncheckedBlsSigner._isSigner).to.be.true;
expect(uncheckedBlsSigner).to.be.instanceOf(UncheckedBlsSigner);
});

it("should return a new signer if one has not been instantiated", async () => {
Expand Down Expand Up @@ -327,9 +338,9 @@ describe("BlsProvider", () => {
type: 2,
});

expect(transactionReceipt.gasUsed).to.equal(parseEther("0"));
expect(transactionReceipt.cumulativeGasUsed).to.equal(parseEther("0"));
expect(transactionReceipt.effectiveGasPrice).to.equal(parseEther("0"));
expect(transactionReceipt.gasUsed).to.equal(BigNumber.from("0"));
expect(transactionReceipt.cumulativeGasUsed).to.equal(BigNumber.from("0"));
expect(transactionReceipt.effectiveGasPrice).to.equal(BigNumber.from("0"));

expect(transactionReceipt).to.include.keys(
"transactionIndex",
Expand Down Expand Up @@ -365,9 +376,9 @@ describe("BlsProvider", () => {
type: 2,
});

expect(transactionReceipt.gasUsed).to.equal(parseEther("0"));
expect(transactionReceipt.cumulativeGasUsed).to.equal(parseEther("0"));
expect(transactionReceipt.effectiveGasPrice).to.equal(parseEther("0"));
expect(transactionReceipt.gasUsed).to.equal(BigNumber.from("0"));
expect(transactionReceipt.cumulativeGasUsed).to.equal(BigNumber.from("0"));
expect(transactionReceipt.effectiveGasPrice).to.equal(BigNumber.from("0"));

expect(transactionReceipt).to.include.keys(
"transactionIndex",
Expand Down Expand Up @@ -431,6 +442,60 @@ describe("BlsProvider", () => {
"wait",
);
});

it("should return the connection info for the provider", () => {
// Arrange
const expectedConnection = regularProvider.connection;

// Act
const connection = blsProvider.connection;

// Assert
expect(connection).to.deep.equal(expectedConnection);
});

it("should return the list of accounts managed by the provider", async () => {
// Arrange
const expectedAccounts = await regularProvider.listAccounts();

// Act
const accounts = await blsProvider.listAccounts();

// Assert
expect(accounts).to.deep.equal(expectedAccounts);
});

it("should send an rpc request to the provider", async () => {
// Arrange
const expectedBlockNumber = await regularProvider.send(
"eth_blockNumber",
[],
);
const expectedChainId = await regularProvider.send("eth_chainId", []);
const expectedAccounts = await regularProvider.send("eth_accounts", []);

const recipient = signers[1].address;
const transactionAmount = parseEther("1");
const hexTx = ethers.providers.JsonRpcProvider.hexlifyTransaction({
to: recipient,
value: transactionAmount,
});
const balanceBefore = await regularProvider.getBalance(recipient);

// Act
const blockNumber = await blsProvider.send("eth_blockNumber", []);
const chainId = await blsProvider.send("eth_chainId", []);
const accounts = await blsProvider.send("eth_accounts", []);
await blsProvider.send("eth_sendTransaction", [hexTx]);

// Assert
expect(blockNumber).to.equal(expectedBlockNumber);
expect(chainId).to.equal(expectedChainId);
expect(accounts).to.deep.equal(expectedAccounts);
expect(
(await regularProvider.getBalance(signers[1].address)).sub(balanceBefore),
).to.equal(transactionAmount);
});
});

describe("JsonRpcProvider", () => {
Expand Down
133 changes: 133 additions & 0 deletions contracts/clients-tests/BlsSigner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
MockERC20__factory,
} from "../clients/src";
import getNetworkConfig from "../shared/helpers/getNetworkConfig";
import { UncheckedBlsSigner } from "../clients/src/BlsSigner";

// TODO: bls-wallet 414 Setup integration tests for BlsProvider & BlsSigner
// Can this be put in a test config/init file?
Expand Down Expand Up @@ -370,6 +371,138 @@ describe("BlsSigner", () => {
expect(RLP.decode(signedMessage)).to.deep.equal(blsWalletSignerSignature);
});

it("should connect to an unchecked bls signer", () => {
// Arrange & Act
const uncheckedBlsSigner = blsSigner.connectUnchecked();

// Assert
expect(uncheckedBlsSigner._isSigner).to.be.true;
expect(uncheckedBlsSigner).to.be.instanceOf(UncheckedBlsSigner);
});

it("should await the init promise when connecting to an unchecked bls signer", async () => {
// Arrange & Act
// random private key
const newPrivateKey =
"0x35b5fe04e9c24433f0489e241c2678f429f226a4b8da520695631bb7af12d4f9";
const newBlsSigner = blsProvider.getSigner(newPrivateKey);
const uncheckedBlsSigner = newBlsSigner.connectUnchecked();

const recipient = signers[1].address;
const transactionAmount = parseEther("1");
const transaction = {
value: transactionAmount,
to: recipient,
};
const balanceBefore = await blsProvider.getBalance(recipient);

// Act
const uncheckedResponse = await uncheckedBlsSigner.sendTransaction(
transaction,
);
await uncheckedResponse.wait();

// Assert
expect(
(await blsProvider.getBalance(recipient)).sub(balanceBefore),
).to.equal(transactionAmount);
});

// TODO (merge-ok) https://github.com/web3well/bls-wallet/issues/427
// This test is identical to the above test except this one uses a new instance of a provider, yet fails to find the tx receipt
it.skip("should get the transaction receipt when using a new provider and connecting to an unchecked bls signer", async () => {
// Arrange & Act
const newBlsProvider = new Experimental.BlsProvider(
aggregatorUrl,
verificationGateway,
rpcUrl,
network,
);
// random private key
const newPrivateKey =
"0x35b5fe04e9c24433f0489e241c2678f429f226a4b8da520695631bb7af12d4f9";
const newBlsSigner = newBlsProvider.getSigner(newPrivateKey);
const uncheckedBlsSigner = newBlsSigner.connectUnchecked();

const recipient = signers[1].address;
const transactionAmount = parseEther("1");
const transaction = {
value: transactionAmount,
to: recipient,
};
const balanceBefore = await blsProvider.getBalance(recipient);

// Act
const uncheckedResponse = await uncheckedBlsSigner.sendTransaction(
transaction,
);
await uncheckedResponse.wait();

// Assert
expect(
(await blsProvider.getBalance(recipient)).sub(balanceBefore),
).to.equal(transactionAmount);
});

it("should send ETH (empty call) via an unchecked transaction", async () => {
// Arrange
const recipient = signers[1].address;
const transactionAmount = parseEther("1");
const transaction = {
value: transactionAmount,
to: recipient,
};
const balanceBefore = await blsProvider.getBalance(recipient);

// Act
const uncheckedTransactionHash = await blsSigner.sendUncheckedTransaction(
transaction,
);
await blsProvider.getTransactionReceipt(uncheckedTransactionHash);

// Assert
expect(
(await blsProvider.getBalance(recipient)).sub(balanceBefore),
).to.equal(transactionAmount);
});

it("should send ETH (empty call) using an unchecked bls signer", async () => {
// Arrange
const uncheckedBlsSigner = blsSigner.connectUnchecked();

const recipient = signers[1].address;
const transactionAmount = parseEther("1");
const transaction = {
value: transactionAmount,
to: recipient,
};
const balanceBefore = await blsProvider.getBalance(recipient);

// Act
const uncheckedResponse = await uncheckedBlsSigner.sendTransaction(
transaction,
);
await uncheckedResponse.wait();

// Assert
expect(uncheckedResponse).to.be.an("object").that.includes({
hash: uncheckedResponse.hash,
nonce: 1,
data: "",
chainId: 0,
confirmations: 0,
from: "",
});

expect(uncheckedResponse.gasLimit).to.equal(BigNumber.from("0"));
expect(uncheckedResponse.gasPrice).to.equal(BigNumber.from("0"));
expect(uncheckedResponse.value).to.equal(BigNumber.from("0"));

expect(
(await blsProvider.getBalance(recipient)).sub(balanceBefore),
).to.equal(transactionAmount);
});

it("should get the balance of an account", async () => {
// Arrange
const expectedBalance = await regularProvider.getBalance(
Expand Down
9 changes: 8 additions & 1 deletion contracts/clients/src/BlsProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { parseEther, Deferrable } from "ethers/lib/utils";

import { ActionDataDto, BundleDto } from "./signer/types";
import Aggregator, { BundleReceipt } from "./Aggregator";
import BlsSigner, { _constructorGuard } from "./BlsSigner";
import BlsSigner, { UncheckedBlsSigner, _constructorGuard } from "./BlsSigner";
import poll from "./helpers/poll";

export default class BlsProvider extends ethers.providers.JsonRpcProvider {
Expand Down Expand Up @@ -96,6 +96,13 @@ export default class BlsProvider extends ethers.providers.JsonRpcProvider {
return signer;
}

override getUncheckedSigner(
privateKey: string,
addressOrIndex?: string,
): UncheckedBlsSigner {
return this.getSigner(privateKey, addressOrIndex).connectUnchecked();
}

override async getTransactionReceipt(
transactionHash: string | Promise<string>,
): Promise<ethers.providers.TransactionReceipt> {
Expand Down
50 changes: 44 additions & 6 deletions contracts/clients/src/BlsSigner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default class BlsSigner extends Signer {
constructor(
constructorGuard: Record<string, unknown>,
provider: BlsProvider,
privateKey: string,
privateKey: string | Promise<string>,
readonly addressOrIndex?: string | number,
) {
super();
Expand Down Expand Up @@ -49,9 +49,10 @@ export default class BlsSigner extends Signer {
}
}

private async initializeWallet(privateKey: string) {
private async initializeWallet(privateKey: string | Promise<string>) {
const resolvedPrivateKey = await privateKey;
this.wallet = await BlsWalletWrapper.connect(
privateKey,
resolvedPrivateKey,
this.verificationGatewayAddress,
this.provider,
);
Expand Down Expand Up @@ -200,17 +201,54 @@ export default class BlsSigner extends Signer {
throw new Error("_signTypedData() is not implemented.");
}

connectUnchecked(): ethers.providers.JsonRpcSigner {
throw new Error("connectUnchecked() is not implemented.");
connectUnchecked(): BlsSigner {
return new UncheckedBlsSigner(
_constructorGuard,
this.provider,
this.wallet?.privateKey ??
(async (): Promise<string> => {
await this.initPromise;
return this.wallet.privateKey;
})(),
this._address || this._index,
);
}

async sendUncheckedTransaction(
transaction: Deferrable<ethers.providers.TransactionRequest>,
): Promise<string> {
throw new Error("sendUncheckedTransaction() is not implemented.");
const transactionResponse = await this.sendTransaction(transaction);
return transactionResponse.hash;
}

async _legacySignMessage(message: Bytes | string): Promise<string> {
throw new Error("_legacySignMessage() is not implemented.");
}
}

export class UncheckedBlsSigner extends BlsSigner {
override async sendTransaction(
transaction: Deferrable<ethers.providers.TransactionRequest>,
): Promise<ethers.providers.TransactionResponse> {
await this.initPromise;

const transactionResponse = await super.sendTransaction(transaction);
return {
hash: transactionResponse.hash,
nonce: 1,
gasLimit: BigNumber.from(0),
gasPrice: BigNumber.from(0),
data: "",
value: BigNumber.from(0),
chainId: 0,
confirmations: 0,
from: "",
wait: (confirmations?: number) => {
return this.provider.waitForTransaction(
transactionResponse.hash,
confirmations,
);
},
};
}
}

0 comments on commit da03830

Please sign in to comment.