Skip to content

Commit 0984e46

Browse files
[Engine] Add FAILED status to execution result and update error handling
1 parent e71e7d7 commit 0984e46

File tree

15 files changed

+217
-155
lines changed

15 files changed

+217
-155
lines changed

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/engine/cloud/analytics/tx-table/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ type ExecutionResult4337Serialized =
3232
| {
3333
status: "QUEUED";
3434
}
35+
| {
36+
status: "FAILED";
37+
error: string;
38+
}
3539
| {
3640
status: "SUBMITTED";
3741
monitoringStatus: "WILL_MONITOR" | "CANNOT_MONITOR";

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/engine/cloud/tx/[id]/transaction-details-ui.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,17 @@ export function TransactionDetailsUI({
3636
transactionHash,
3737
confirmedAt,
3838
createdAt,
39-
errorMessage,
4039
executionParams,
4140
executionResult,
4241
} = transaction;
4342

4443
const status = executionResult?.status as keyof typeof statusDetails;
44+
const errorMessage =
45+
executionResult && "error" in executionResult
46+
? executionResult.error
47+
: executionResult && "revertData" in executionResult
48+
? executionResult.revertData?.revertReason
49+
: null;
4550

4651
const chain = chainId ? idToChain.get(Number.parseInt(chainId)) : undefined;
4752
const explorer = chain?.explorers?.[0];

apps/playground-web/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
"@radix-ui/react-switch": "^1.2.2",
2929
"@radix-ui/react-tooltip": "1.2.3",
3030
"@tanstack/react-query": "5.74.4",
31-
"@thirdweb-dev/engine": "0.0.19",
3231
"class-variance-authority": "^0.7.1",
3332
"clsx": "^2.1.1",
3433
"date-fns": "4.1.0",

apps/playground-web/src/app/engine/_hooks/useEngineTxStatus.ts

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,64 @@ export function useEngineTxStatus(queueId: string | undefined) {
99
if (!queueId) throw new Error("No queue ID provided");
1010
const res = await get_engine_tx_status(queueId);
1111

12-
const txStatus: EngineTxStatus = {
13-
queueId: queueId,
14-
status: res.result.status,
15-
chainId: res.result.chainId,
16-
transactionHash: res.result.transactionHash,
17-
queuedAt: res.result.queuedAt,
18-
sentAt: res.result.sentAt,
19-
minedAt: res.result.minedAt,
20-
cancelledAt: res.result.cancelledAt,
21-
};
12+
let txStatus: EngineTxStatus;
13+
switch (res.status) {
14+
case "QUEUED": {
15+
txStatus = {
16+
queueId: queueId,
17+
status: "queued",
18+
chainId: res.chain.id.toString(),
19+
transactionHash: null,
20+
queuedAt: res.createdAt,
21+
sentAt: null,
22+
minedAt: null,
23+
cancelledAt: null,
24+
};
25+
break;
26+
}
27+
case "SUBMITTED": {
28+
txStatus = {
29+
queueId: queueId,
30+
status: "sent",
31+
chainId: res.chain.id.toString(),
32+
transactionHash: null,
33+
queuedAt: res.createdAt,
34+
sentAt: res.createdAt,
35+
minedAt: null,
36+
cancelledAt: null,
37+
};
38+
break;
39+
}
40+
case "CONFIRMED": {
41+
txStatus = {
42+
queueId: queueId,
43+
status: "mined",
44+
chainId: res.chain.id.toString(),
45+
transactionHash: res.transactionHash,
46+
queuedAt: res.createdAt,
47+
sentAt: res.confirmedAt,
48+
minedAt: res.confirmedAt,
49+
cancelledAt: null,
50+
};
51+
break;
52+
}
53+
case "FAILED": {
54+
txStatus = {
55+
queueId: queueId,
56+
status: "errored",
57+
chainId: res.chain.id.toString(),
58+
transactionHash: null,
59+
queuedAt: new Date().toISOString(),
60+
sentAt: null,
61+
minedAt: null,
62+
cancelledAt: new Date().toISOString(),
63+
};
64+
break;
65+
}
66+
default: {
67+
throw new Error(`Unknown engine tx status: ${res}`);
68+
}
69+
}
2270

2371
return txStatus;
2472
},
Lines changed: 62 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
"use server";
22

3-
import { Engine } from "@thirdweb-dev/engine";
4-
3+
import { Engine, defineChain, encode, getContract } from "thirdweb";
4+
import { multicall } from "thirdweb/extensions/common";
5+
import * as ERC20 from "thirdweb/extensions/erc20";
6+
import * as ERC1155 from "thirdweb/extensions/erc1155";
7+
import { THIRDWEB_CLIENT } from "../../lib/client";
58
const BACKEND_WALLET_ADDRESS = process.env.ENGINE_BACKEND_WALLET as string;
9+
const ENGINE_VAULT_ACCESS_TOKEN = process.env
10+
.ENGINE_VAULT_ACCESS_TOKEN as string;
611

7-
const engine = new Engine({
8-
url: process.env.ENGINE_URL as string,
9-
accessToken: process.env.ENGINE_ACCESS_TOKEN as string,
12+
const serverWallet = Engine.serverWallet({
13+
address: BACKEND_WALLET_ADDRESS,
14+
client: THIRDWEB_CLIENT,
15+
vaultAccessToken: ENGINE_VAULT_ACCESS_TOKEN,
1016
});
1117

1218
export async function airdrop_tokens_with_engine(params: {
@@ -17,20 +23,38 @@ export async function airdrop_tokens_with_engine(params: {
1723
amount: string;
1824
}[];
1925
}) {
20-
const res = await engine.erc20.mintBatchTo(
21-
params.chainId.toString(),
22-
params.contractAddress,
23-
BACKEND_WALLET_ADDRESS,
24-
{
25-
data: params.receivers,
26-
},
26+
const contract = getContract({
27+
address: params.contractAddress,
28+
chain: defineChain(params.chainId),
29+
client: THIRDWEB_CLIENT,
30+
});
31+
const data = await Promise.all(
32+
params.receivers.map((receiver) =>
33+
encode(
34+
ERC20.mintTo({
35+
contract,
36+
to: receiver.toAddress,
37+
amount: receiver.amount,
38+
}),
39+
),
40+
),
2741
);
42+
const tx = multicall({
43+
contract,
44+
data,
45+
});
46+
47+
const res = await serverWallet.enqueueTransaction({ transaction: tx });
2848

29-
return res.result;
49+
return res.transactionId;
3050
}
3151

3252
export async function get_engine_tx_status(queueId: string) {
33-
const status = await engine.transaction.status(queueId);
53+
const status = await Engine.getTransactionStatus({
54+
client: THIRDWEB_CLIENT,
55+
transactionId: queueId,
56+
});
57+
console.log("STATUS", status);
3458
return status;
3559
}
3660

@@ -49,17 +73,19 @@ type MintNFTParams = {
4973
};
5074

5175
export async function mint_erc1155_nft_with_engine(params: MintNFTParams) {
52-
const res = await engine.erc1155.mintTo(
53-
params.chainId.toString(),
54-
params.contractAddress,
55-
BACKEND_WALLET_ADDRESS,
56-
{
57-
receiver: params.toAddress,
58-
metadataWithSupply: params.metadataWithSupply,
59-
},
60-
);
76+
const tx = ERC1155.mintTo({
77+
contract: getContract({
78+
address: params.contractAddress,
79+
chain: defineChain(params.chainId),
80+
client: THIRDWEB_CLIENT,
81+
}),
82+
nft: params.metadataWithSupply.metadata,
83+
to: params.toAddress,
84+
supply: BigInt(params.metadataWithSupply.supply),
85+
});
86+
const res = await serverWallet.enqueueTransaction({ transaction: tx });
6187

62-
return res.result;
88+
return res.transactionId;
6389
}
6490

6591
type ClaimNFTParams = {
@@ -71,16 +97,17 @@ type ClaimNFTParams = {
7197
};
7298

7399
export async function claim_erc1155_nft_with_engine(params: ClaimNFTParams) {
74-
const res = await engine.erc1155.claimTo(
75-
params.chainId.toString(),
76-
params.contractAddress,
77-
BACKEND_WALLET_ADDRESS,
78-
{
79-
receiver: params.receiverAddress,
80-
quantity: params.quantity.toString(),
81-
tokenId: params.tokenId,
82-
},
83-
);
100+
const tx = ERC1155.claimTo({
101+
contract: getContract({
102+
address: params.contractAddress,
103+
chain: defineChain(params.chainId),
104+
client: THIRDWEB_CLIENT,
105+
}),
106+
to: params.receiverAddress,
107+
tokenId: BigInt(params.tokenId),
108+
quantity: BigInt(params.quantity),
109+
});
110+
const res = await serverWallet.enqueueTransaction({ transaction: tx });
84111

85-
return res.result;
112+
return res.transactionId;
86113
}

apps/playground-web/src/app/engine/airdrop/_components/airdrop-code.tsx

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -34,49 +34,49 @@ export function AirdropCode() {
3434
}
3535

3636
const engineAirdropSendCode = `\
37-
const chainId = ${airdropExample.chainId};
38-
const contractAddress = "${airdropExample.contractAddress}";
3937
const addresses = ${JSON.stringify(
4038
airdropExample.receivers.map((x) => ({
41-
address: x.toAddress,
42-
quantity: x.amount,
39+
recipient: x.toAddress,
40+
amount: x.amount,
4341
})),
4442
null,
4543
2,
4644
)};
4745
48-
const url = \`\${YOUR_ENGINE_URL}\/contract/\${chainId}/\${contractAddress}\/erc1155\/airdrop\`;
46+
const contract = getContract({
47+
address: ${airdropExample.contractAddress},
48+
chain: defineChain(${airdropExample.chainId}),
49+
client: THIRDWEB_CLIENT,
50+
});
51+
52+
const transaction = airdropERC20({
53+
contract,
54+
tokenAddress: ${airdropExample.contractAddress},
55+
contents: addresses,
56+
});
4957
50-
const response = await fetch(url, {
51-
method: "POST",
52-
headers: {
53-
"Authorization": "Bearer YOUR_SECRET_TOKEN",
54-
"Content-Type": "application/json",
55-
"X-Backend-Wallet-Address": "YOUR_BACKEND_WALLET_ADDRESS",
56-
},
57-
body: JSON.stringify({ addresses }),
58+
const serverWallet = Engine.serverWallet({
59+
address: BACKEND_WALLET_ADDRESS,
60+
client: THIRDWEB_CLIENT,
61+
vaultAccessToken: ENGINE_VAULT_ACCESS_TOKEN,
5862
});
5963
60-
const data = await response.json();
61-
const queueId = data.queueId;
64+
const { transactionId } = await serverWallet.enqueueTransaction({ transaction });
6265
`;
6366

6467
const engineAirdropGetStatus = `\
65-
function getEngineTxStatus(queueId: string) {
66-
const url = \`\${YOUR_ENGINE_URL}\/transaction/\${queueId}\`;
67-
const response = await fetch(url, {
68-
method: "GET",
69-
headers: {
70-
"Authorization": "Bearer YOUR_SECRET_TOKEN",
71-
},
72-
});
68+
const result = await Engine.getTransactionStatus({
69+
client: THIRDWEB_CLIENT,
70+
transactionId: transactionId,
71+
});
7372
74-
const data = await response.json();
75-
return data.result;
76-
}
73+
console.log(result.status);
7774
78-
// you can keep polling for the status until you get a status of either "mined" or "errored" or "cancelled"
79-
const result = await getEngineTxStatus(queueId);
75+
// or wait for the transaction to be mined (polls status until it's mined)
76+
const result = await Engine.waitForTransactionHash({
77+
client: THIRDWEB_CLIENT,
78+
transactionId: transactionId,
79+
});
8080
81-
console.log(result.status);
81+
console.log(result.transactionHash);
8282
`;

apps/playground-web/src/app/engine/airdrop/_components/airdrop-preview.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ export function EngineAirdropPreview() {
3232
const res = await airdropMutation.mutateAsync();
3333
updateEngineTxStatus({
3434
chainId: airdropExample.chainId,
35-
queueId: res.queueId,
35+
queueId: res,
3636
});
3737

38-
setQueueId(res.queueId);
38+
setQueueId(res);
3939
};
4040

4141
return (

apps/playground-web/src/app/engine/airdrop/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export const airdropExample = {
2-
contractAddress: "0xcB30dB8FB977e8b27ae34c86aF16C4F5E428c0bE",
2+
contractAddress: "0x6E238275023A2575136CF60f655B6B2C0C58b4ac",
33
chainId: 84532,
44
chainName: "Base Sepolia",
55
chainExplorer: "https://base-sepolia.blockscout.com",

0 commit comments

Comments
 (0)