Skip to content

Commit 5bc4c74

Browse files
[SDK] Support ERC-2612 permit for x402 payments
1 parent 9069351 commit 5bc4c74

File tree

8 files changed

+403
-66
lines changed

8 files changed

+403
-66
lines changed

.changeset/giant-suns-drive.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"thirdweb": patch
3+
---
4+
5+
Support ERC-2612 permit for x402 payments
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[
2+
"function transferWithAuthorization(address from, address to, uint256 value, uint256 validAfter, uint256 validBefore, bytes32 nonce, bytes signature)"
3+
]

packages/thirdweb/scripts/generate/abis/erc7702/MinimalAccount.json

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,4 @@
2424
"function getSessionStateForSigner(address signer) view returns (((uint256 remaining, address target, bytes4 selector, uint256 index)[] transferValue, (uint256 remaining, address target, bytes4 selector, uint256 index)[] callValue, (uint256 remaining, address target, bytes4 selector, uint256 index)[] callParams))",
2525
"function getTransferPoliciesForSigner(address signer) view returns ((address target, uint256 maxValuePerUse, (uint8 limitType, uint256 limit, uint256 period) valueLimit)[])",
2626
"function isWildcardSigner(address signer) view returns (bool)",
27-
"function onERC1155BatchReceived(address, address, uint256[], uint256[], bytes) returns (bytes4)",
28-
"function onERC1155Received(address, address, uint256, uint256, bytes) returns (bytes4)",
29-
"function onERC721Received(address, address, uint256, bytes) returns (bytes4)",
30-
"function supportsInterface(bytes4 interfaceId) view returns (bool)",
31-
"receive() external payable"
3227
]

packages/thirdweb/src/extensions/erc20/__generated__/USDC/write/transferWithAuthorization.ts

Lines changed: 215 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/thirdweb/src/x402/common.ts

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ import {
1212
type PaymentRequiredResult,
1313
x402Version,
1414
} from "./types.js";
15+
import { isTransferWithAuthorizationSupported } from "../extensions/erc20/__generated__/USDC/write/transferWithAuthorization.js";
16+
import { getContract } from "../contract/contract.js";
17+
import { resolveContractAbi } from "../contract/actions/resolve-abi.js";
18+
import type { Abi } from "abitype";
19+
import { getCachedChain } from "../chains/utils.js";
20+
import type { ThirdwebClient } from "../client/client.js";
21+
import { toFunctionSelector } from "viem/utils";
22+
import { isPermitSupported } from "../extensions/erc20/__generated__/IERC20Permit/write/permit.js";
1523

1624
type GetPaymentRequirementsResult = {
1725
status: 200;
@@ -106,7 +114,10 @@ export async function decodePaymentRequest(
106114
},
107115
output: outputSchema,
108116
},
109-
extra: (asset as ERC20TokenAmount["asset"]).eip712,
117+
extra: {
118+
facilitatorAddress: facilitator.address,
119+
...(asset as ERC20TokenAmount["asset"]).eip712,
120+
},
110121
});
111122

112123
// Check for payment header
@@ -234,3 +245,36 @@ async function getDefaultAsset(
234245
?.defaultAsset as ERC20TokenAmount["asset"];
235246
return assetConfig;
236247
}
248+
249+
export type SupportedAuthorizationMethods = {
250+
hasPermit: boolean,
251+
hasTransferWithAuthorization: boolean,
252+
}
253+
254+
export async function detectSupportedAuthorizationMethods(args: {
255+
client: ThirdwebClient,
256+
asset: string,
257+
chainId: number,
258+
}): Promise<SupportedAuthorizationMethods> {
259+
const abi = await resolveContractAbi<Abi>(
260+
getContract({
261+
client: args.client,
262+
address: args.asset,
263+
chain: getCachedChain(args.chainId),
264+
}),
265+
).catch((error) => {
266+
console.error("Error resolving contract ABI", error);
267+
return [] as Abi;
268+
});
269+
const selectors = abi
270+
.filter((f) => f.type === "function")
271+
.map((f) => toFunctionSelector(f));
272+
const hasPermit = isPermitSupported(selectors);
273+
const hasTransferWithAuthorization =
274+
isTransferWithAuthorizationSupported(selectors);
275+
276+
return {
277+
hasPermit,
278+
hasTransferWithAuthorization,
279+
};
280+
}

packages/thirdweb/src/x402/facilitator.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export function facilitator(config: ThirdwebX402FacilitatorConfig) {
6969
}
7070
const facilitator = {
7171
url: (config.baseUrl ?? DEFAULT_BASE_URL) as `${string}://${string}`,
72+
address: serverWalletAddress,
7273
createAuthHeaders: async () => {
7374
return {
7475
verify: {

packages/thirdweb/src/x402/fetchWithPayment.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ import { createPaymentHeader } from "./sign.js";
5050
*/
5151
export function wrapFetchWithPayment(
5252
fetch: typeof globalThis.fetch,
53-
_client: ThirdwebClient,
53+
client: ThirdwebClient,
5454
wallet: Wallet,
5555
maxValue: bigint = BigInt(1 * 10 ** 6), // Default to 1 USDC
5656
) {
@@ -103,8 +103,8 @@ export function wrapFetchWithPayment(
103103
}
104104

105105
const paymentHeader = await createPaymentHeader(
106+
client,
106107
account,
107-
x402Version,
108108
selectedPaymentRequirements,
109109
);
110110

0 commit comments

Comments
 (0)