Skip to content

Commit

Permalink
Add test for sending userops (#714)
Browse files Browse the repository at this point in the history
## Changes

- Linear: https://linear.app/thirdweb/issue/INF-171/
- Here is the problem being solved.
- Here are the changes made.

## How this PR will be tested

- [ ] Open the dashboard and click X. Result: A modal should appear.
- [ ] Call the /foo/bar API. Result: Returns 200 with "baz" in the response body.

## Output

(Example: Screenshot/GIF for UI changes, cURL output for API changes)

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing the error handling in the `pollTransactionStatus` function and adding user operation tests in the `userop.test.ts` file. It introduces an `errorMessage` property and improves transaction error logging.

### Detailed summary
- Added `errorMessage` property to the `Timing` type in `transactions.ts`.
- Changed `timing` to be a constant in `pollTransactionStatus`.
- Improved error logging in `pollTransactionStatus`.
- Added a suite of user operation tests in `userop.test.ts`.
- Implemented tests for sending NFT claims and handling permission errors.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
  • Loading branch information
joaquim-verges authored Oct 9, 2024
1 parent d17c414 commit 69b9eb8
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 2 deletions.
107 changes: 107 additions & 0 deletions test/e2e/tests/userop.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { beforeAll, describe, expect, test } from "bun:test";
import { type Address, getAddress } from "thirdweb";
import { sepolia } from "thirdweb/chains";
import { DEFAULT_ACCOUNT_FACTORY_V0_6 } from "thirdweb/wallets/smart";
import type { ApiError } from "../../../sdk/dist/declarations/src/core/ApiError";
import type { setupEngine } from "../utils/engine";
import { pollTransactionStatus } from "../utils/transactions";
import { setup } from "./setup";

describe("Userop Tests", () => {
let engine: ReturnType<typeof setupEngine>;
let backendWallet: Address;
let accountAddress: Address;

const accountFactoryAddress = DEFAULT_ACCOUNT_FACTORY_V0_6;

beforeAll(async () => {
const { engine: _engine, backendWallet: _backendWallet } = await setup();
engine = _engine;
backendWallet = _backendWallet as Address;

const addr = await engine.accountFactory.predictAccountAddress(
backendWallet,
sepolia.id.toString(),
accountFactoryAddress,
);

accountAddress = getAddress(addr.result);
});

test("Should send a nft claim userop", async () => {
const writeRes = await engine.erc1155.erc1155ClaimTo(
sepolia.id.toString(),
"0xe2cb0eb5147b42095c2FfA6F7ec953bb0bE347D8",
backendWallet,
{
quantity: "1",
receiver: accountAddress,
tokenId: "0",
},
false,
undefined,
accountAddress,
accountFactoryAddress,
);

expect(writeRes.result.queueId).toBeDefined();

const writeTransactionStatus = await pollTransactionStatus(
engine,
writeRes.result.queueId,
true,
);

expect(writeTransactionStatus.minedAt).toBeDefined();
});

test("Should throw decoded error with simulate false", async () => {
const res = await engine.contractRoles.grantContractRole(
sepolia.id.toString(),
"0xe2cb0eb5147b42095c2FfA6F7ec953bb0bE347D8",
backendWallet,
{
address: accountAddress,
role: "minter",
},
false,
undefined,
accountAddress,
accountFactoryAddress,
);

expect(res.result.queueId).toBeDefined();

const writeTransactionStatus = await pollTransactionStatus(
engine,
res.result.queueId,
true,
);

expect(writeTransactionStatus.errorMessage).toBe(
`Error - Permissions: account ${accountAddress.toLowerCase()} is missing role 0x0000000000000000000000000000000000000000000000000000000000000000`,
);
});

test("Should throw decoded error with simulate true", async () => {
try {
await engine.contractRoles.grantContractRole(
sepolia.id.toString(),
"0xe2cb0eb5147b42095c2FfA6F7ec953bb0bE347D8",
backendWallet,
{
address: accountAddress,
role: "minter",
},
true,
undefined,
accountAddress,
accountFactoryAddress,
);
} catch (e) {
expect((e as ApiError).body?.error?.message).toBe(
`Simulation failed: TransactionError: Error - Permissions: account ${accountAddress.toLowerCase()} is missing role 0x0000000000000000000000000000000000000000000000000000000000000000`,
);
}
});
});
7 changes: 5 additions & 2 deletions test/e2e/utils/transactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ type Timing = {
queuedAt?: number;
sentAt?: number;
minedAt?: number;
errorMessage?: string;
};

export const sendNoOpTransaction = async (
Expand Down Expand Up @@ -65,7 +66,7 @@ export const pollTransactionStatus = async (
log = false,
) => {
let mined = false;
let timing = {} as Timing;
const timing: Timing = {};

while (!mined) {
await sleep(Math.random() * CONFIG.STAGGER_MAX);
Expand All @@ -77,7 +78,8 @@ export const pollTransactionStatus = async (
}

if (res.result.errorMessage) {
console.error(`Transaction Error:`, res.result.errorMessage);
console.error("Transaction Error:", res.result.errorMessage);
timing.errorMessage = res.result.errorMessage;
return timing;
}

Expand All @@ -96,6 +98,7 @@ export const pollTransactionStatus = async (

if (res.result.status === "errored") {
console.error("Transaction errored:", res.result.errorMessage);
timing.errorMessage = res.result.errorMessage ?? undefined;
return timing;
}

Expand Down

0 comments on commit 69b9eb8

Please sign in to comment.