Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

import { zodResolver } from "@hookform/resolvers/zod";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { UnderlineLink } from "@workspace/ui/components/UnderlineLink";
import { formatDistanceToNow } from "date-fns";
import {
DotIcon,
InfoIcon,
MoreVerticalIcon,
PencilIcon,
PlusIcon,
Expand All @@ -25,6 +27,7 @@ import {
updateWebhook,
} from "@/api/universal-bridge/developer";
import { GenericLoadingPage } from "@/components/blocks/skeletons/GenericLoadingPage";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import { CopyTextButton } from "@/components/ui/CopyTextButton";
import { Checkbox, CheckboxWithLabel } from "@/components/ui/checkbox";
Expand Down Expand Up @@ -435,28 +438,30 @@ function BridgeWebhookModalContent(
)}
/>

<FormField
name="version"
render={({ field }) => (
<FormItem>
<FormLabel>Version</FormLabel>
<Select {...field} onValueChange={field.onChange}>
<SelectTrigger className="w-full">
<SelectValue placeholder="v2" />
</SelectTrigger>
<SelectContent>
<SelectItem value="2">v2</SelectItem>
<SelectItem value="1">v1</SelectItem>
</SelectContent>
</Select>
<FormDescription>
Select the data format of the webhook payload (v2 recommended,
v1 for legacy users).
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
{props.type === "edit" && props.webhook.version === 1 && (
<FormField
name="version"
render={({ field }) => (
<FormItem>
<FormLabel>Version</FormLabel>
<Select {...field} onValueChange={field.onChange}>
<SelectTrigger className="w-full">
<SelectValue placeholder="v2" />
</SelectTrigger>
<SelectContent>
<SelectItem value="2">v2</SelectItem>
<SelectItem value="1">v1</SelectItem>
</SelectContent>
</Select>
<FormDescription>
Select the data format of the webhook payload (v2 recommended,
v1 for legacy users).
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
)}

{props.type === "create" && (
<section>
Expand Down Expand Up @@ -486,6 +491,22 @@ function BridgeWebhookModalContent(
</section>
)}

<Alert variant="warning">
<InfoIcon className="size-4" />
<AlertTitle> Verify payload before processing </AlertTitle>
<AlertDescription>
Make sure to verify receiver, destination chain, token address and
amount to ensure that it represents the expected state of the
transaction before processing it in your backend.{" "}
<UnderlineLink
href="https://portal.thirdweb.com/bridge/webhooks#verify-payload"
target="_blank"
>
Learn more
</UnderlineLink>
</AlertDescription>
</Alert>

<DialogFooter>
<Button
className="gap-2"
Expand Down
52 changes: 44 additions & 8 deletions apps/portal/src/app/bridge/webhooks/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,14 @@ export const metadata = createMetadata({

Create webhooks to be notified each time a payment is completed in your app.

### Create a Webhook
## Create a Webhook

You can create a webhook in your project dashboard under the Payments tab. You'll be prompted to copy a secret key before saving the webhook. This will be used for verification on all webhook requests received by your backend.
You can create a webhook in your project dashboard under the Webhooks > Bridge tab. You'll be prompted to copy a secret key before saving the webhook. This will be used for verification on all webhook requests received by your backend.

## Parse Webhook Payloads

### Parse Webhook Payloads with the thirdweb SDK
## Parse Webhook Payload with thirdweb SDK

The thirdweb SDK has a built-in function for parsing payment webhooks:
The thirdweb SDK has a built-in function for parsing bridge webhook payloads

```typescript
import { Bridge } from "thirdweb";
Expand All @@ -51,7 +50,7 @@ export async function POST(request: Request) {
const headers = Object.fromEntries(request.headers.entries());

try {
const webhook = await Bridge.Webhook.parse(
const payload = await Bridge.Webhook.parse(
body,
headers,
process.env.WEBHOOK_SECRET // Your webhook secret
Expand All @@ -68,7 +67,44 @@ export async function POST(request: Request) {
}
```

### Example Payloads

## Verify Payload

<Callout variant="warning" title="Verify payload before processing">

Make sure to verify receiver, destination chain, token address and token amount in the payload before processing it further in your backend to ensure that payload represents expected state of the transaction.

</Callout>


You can verify certain fields in the payload by setting the `verify` parameter in the `Bridge.Webhook.parse` function.
By setting this, the `Bridge.Webhook.parse` function will throw an error if the payload does not match the expected values.

Apart from this you can add custom verification logic on the returned `payload` as per your needs.

```typescript
const payload = await Bridge.Webhook.parse(
body,
headers,
process.env.WEBHOOK_SECRET,
tolerance,
// You can set one or more of below shown example parameters
{
// if receiver address is not this, error will be thrown
receiverAddress: "0x1f846f6dae38e1c88d71eaa191760b15f38b7a37",
// if destination token address is not this, error will be thrown
destinationTokenAddress: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
// if destination chain id is not this, error will be thrown
destinationChainId: 8453,
// if destination token amount is less than this, error will be thrown
minDestinationAmount: 1000000000000000000n,
}
);

```


## Example Payloads

<Tabs defaultValue="bridge">
<TabsList>
Expand Down Expand Up @@ -156,7 +192,7 @@ export async function POST(request: Request) {
</TabsContent>
</Tabs>

### Manual Verification
## Manually Parse Payload

If you prefer to handle verification manually, you can verify webhooks using the Web Crypto API (same method as the SDK):

Expand Down
Loading