Skip to content

Commit

Permalink
refactor: remove circular dependency between plugin and web3.js instance
Browse files Browse the repository at this point in the history
docs: update README.md
build: add build step to CI and run on develop branch
test: update tests with new constructor change
  • Loading branch information
ffmcgee725 committed Nov 29, 2023
1 parent f00f0a1 commit fb9b152
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 44 deletions.
8 changes: 6 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ on:
push:
branches:
- main
- develop

jobs:
build-and-test:
install-test-and-build:
runs-on: ubuntu-latest
env:
P_KEY: ${{ secrets.P_KEY }}
Expand All @@ -27,4 +28,7 @@ jobs:
run: yarn test:e2e

- name: Run e2e tests in web browser environment
run: yarn test:browser
run: yarn test:browser

- name: Build lib
run: yarn build
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,18 @@ const thirdWebConfig = { secretKey: "PLACE_YOUR_SECRET_KEY_HERE"}
const thirdWebConfig = { clientId: "PLACE_YOUR_CLIENT_ID_HERE"}
```

3. Instantiate a Web3 instance and register the plugin
3. Use your existing Web3.js instance and register the plugin

```typescript
import { Web3 } from "web3";
import { IpfsPlugin } from "web3-ipfs-plugin";

web3 = new Web3("RPC_URL");
web3.registerPlugin(new IpfsPlugin(web3, thirdWebConfig));
// make sure to use library code to sign your main authenticated wallet address, as it will be necessary
// to pass it as a configuration to later call the registry contact store method
const providerAddress = web3.eth.wallet?.get(0)?.address;
const web3Config = { context: web3.getContextObject(), providerAddress };

web3.registerPlugin(new IpfsPlugin(thirdWebConfig, web3Config));
```

4. Use uploadFile() to upload a file to IPFS network and store the generated CID in a Registry Smart Contract `0xA683BF985BC560c5dc99e8F33f3340d1e53736EB`
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"ts-node": "^10.9.1",
"typescript": "^5.1.3",
"web3": "^4.2.2",
"web3-core": "^4.3.1",
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1"
Expand Down
36 changes: 25 additions & 11 deletions src/clients/web3.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,38 @@
import Web3, { Contract, EventLog, TransactionReceipt } from "web3";
import { REGISTRY_CONTRACT_ABI } from "../utils/constants";
import Web3, {
Contract,
EventLog,
TransactionReceipt,
Web3APISpec,
} from "web3";
import { RegisteredSubscription } from "web3/lib/commonjs/eth.exports";
import { Web3ContextObject } from "web3-core";
import {
REGISTRY_CONTRACT_ABI,
REGISTRY_CONTRACT_ADDRESS,
} from "../utils/constants";

export type Web3Config = {
context: Web3ContextObject<Web3APISpec, RegisteredSubscription>;
providerAddress?: string;
};

/**
* An abstraction for encapsulating Web3.js calls.
*/
export class Web3Client {
private readonly BLOCK_NUMBER_THRESHOLD = BigInt(50000);
private readonly CONTRACT_INCEPTION_BLOCK = BigInt(4546394); // https://sepolia.etherscan.io/tx/0x5d1fca9aff91aad286d468d6556ae50b85bf7d34a8c63d68d294045d85a3da6c
private readonly REGISTRY_ADDRESS =
"0xA683BF985BC560c5dc99e8F33f3340d1e53736EB";
private web3: Web3;
private registryContract: Contract<typeof REGISTRY_CONTRACT_ABI>;
private providerAddress?: string;

constructor(web3: Web3) {
this.web3 = web3;
this.registryContract = new web3.eth.Contract(
constructor({ context, providerAddress }: Web3Config) {
this.web3 = new Web3(context);
this.providerAddress = providerAddress;
this.registryContract = new this.web3.eth.Contract(
REGISTRY_CONTRACT_ABI,
this.REGISTRY_ADDRESS,
web3.getContextObject()
REGISTRY_CONTRACT_ADDRESS,
context
);
}

Expand All @@ -28,8 +43,7 @@ export class Web3Client {
*/
public async storeCID(cid: string): Promise<TransactionReceipt> {
try {
const account = this.web3.eth.wallet?.get(0);
const from = account?.address;
const from = this.providerAddress;
const contractCall = this.registryContract.methods.store(cid);

const gas = String(await contractCall.estimateGas({ from }));
Expand Down
8 changes: 4 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Web3, { EventLog, TransactionReceipt, Web3PluginBase } from "web3";
import { Web3Client } from "./clients/web3";
import { EventLog, TransactionReceipt, Web3PluginBase } from "web3";
import { Web3Client, Web3Config } from "./clients/web3";
import { IpfsClient, ThirdWebConfig } from "./clients/ipfs";

/**
Expand All @@ -11,10 +11,10 @@ export class IpfsPlugin extends Web3PluginBase {
private web3Client: Web3Client;
private ipfsClient: IpfsClient;

constructor(web3: Web3, thirdwebConfig: ThirdWebConfig) {
constructor(thirdwebConfig: ThirdWebConfig, web3Config: Web3Config) {
super();
this.web3Client = new Web3Client(web3);
this.ipfsClient = new IpfsClient(thirdwebConfig);
this.web3Client = new Web3Client(web3Config);
}

/**
Expand Down
2 changes: 2 additions & 0 deletions src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@ export const REGISTRY_CONTRACT_ABI = [
] as const;

export const RPC_URL: string = "https://ethereum-sepolia.publicnode.com/";
export const REGISTRY_CONTRACT_ADDRESS: string =
"0xA683BF985BC560c5dc99e8F33f3340d1e53736EB";
51 changes: 31 additions & 20 deletions test/e2e/index.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,26 @@ describe("IpfsPlugin E2E Tests", () => {

it("should register IpfsPlugin plugin on Web3Context instance", () => {
const web3Context = new core.Web3Context(RPC_URL);
const web3Provider = new Web3.providers.HttpProvider(RPC_URL);
web3Context.registerPlugin(
new IpfsPlugin(new Web3(web3Provider), thirdwebConfig)
new IpfsPlugin(thirdwebConfig, { context: web3Context })
);
expect(web3Context.ipfs).toBeDefined();
});

describe("IpfsPlugin method tests", () => {
let web3: Web3;
let providerAddress: string | undefined;

beforeAll(() => {
const web3Provider = new Web3.providers.HttpProvider(RPC_URL);
web3 = new Web3(web3Provider);

const signer = `0x${process.env.P_KEY ?? ""}`;
web3.eth.accounts.wallet.add(signer).get(0);
web3.registerPlugin(new IpfsPlugin(web3, thirdwebConfig));
web3.eth.accounts.wallet.add(signer);
providerAddress = web3.eth.wallet?.get(0)?.address;

const web3Config = { context: web3.getContextObject(), providerAddress };
web3.registerPlugin(new IpfsPlugin(thirdwebConfig, web3Config));
});

describe("uploadFile()", () => {
Expand All @@ -50,6 +54,9 @@ describe("IpfsPlugin E2E Tests", () => {
);

expect(receipt).toBeDefined();
expect(receipt.from.toLowerCase()).toEqual(
String(providerAddress?.toLowerCase())
);
},
DEFAULT_TIMEOUT
);
Expand All @@ -58,22 +65,26 @@ describe("IpfsPlugin E2E Tests", () => {
describe("listAllByAddress()", () => {
const walletAddress = "0xa719ffb8538720010f3473746ad378e80c4c7bba";

it("owner address property of every log should be the same as the provided wallet address", async () => {
const result = await web3.ipfs.listAllByAddress(walletAddress);

// we make sure the result from the call is properly type to later assert all event log owners are the expected address
const eventLogs = result
.map((log) => (typeof log !== "string" ? log : null))
.filter((t) => t);

expect(
eventLogs.every(
(eventLog) =>
String(eventLog?.returnValues.owner).toLowerCase() ===
walletAddress
)
);
});
it(
"owner address property of every log should be the same as the provided wallet address",
async () => {
const result = await web3.ipfs.listAllByAddress(walletAddress);

// we make sure the result from the call is properly type to later assert all event log owners are the expected address
const eventLogs = result
.map((log) => (typeof log !== "string" ? log : null))
.filter((t) => t);

expect(
eventLogs.every(
(eventLog) =>
String(eventLog?.returnValues.owner).toLowerCase() ===
walletAddress
)
);
},
DEFAULT_TIMEOUT
);
});
});
});
Expand Down
13 changes: 9 additions & 4 deletions test/unit/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,22 @@ import { Web3Client } from "../../src/clients/web3";

describe("IpfsPlugin", () => {
const mockSecretKey = "mock.secret.key";
const mockRpcUrl = "https://mock.rpc.io/";
const mockPath = "path/to/file/";
const mockWalletAddress = "0xe8e3c1a9b5f24e1894e905dc8e54de78";
const mockKeccakCID = "0x807a076c42934c3bbc1fcd1ffea67d00";
const mockTransactionReceipt = getMockTransactionReceipt();
const mockWeb3Config = {
context: new Web3("").getContextObject(),
};
let instance: IpfsPlugin;

beforeAll(() => {
instance = new IpfsPlugin(new Web3(mockRpcUrl), {
secretKey: mockSecretKey,
});
instance = new IpfsPlugin(
{
secretKey: mockSecretKey,
},
mockWeb3Config
);
});

afterAll(() => jest.clearAllMocks());
Expand Down

0 comments on commit fb9b152

Please sign in to comment.