Skip to main content

Permits

Payment Processor has support to use a permit processor like PermitC to handle approvals in a more advanced and secure way. To handle this, the taker must first give standard approval on their token to the permit processor contract before executing the order. A signature for the permit processor would utilize the PermitTransferFromWithAdditionalData on PermitC.

Features​

  • Time Bound Approvals: Approvals via PermitC include an expiration timestamp, allowing for an approval to be only set for a specific period of time. If it’s used past the expiration timestamp, it will be invalid.
  • One Click Approval Revoke: To quickly revoke all approvals, you can call a single function lockdown which will invalidate all outstanding approvals.
  • Signature Based Approvals: Signatures can be provided for operators to increase approval on chain for multi-use approvals, or permit transfer for one off signatures.
  • Additional Data Validation: Signatures can use the permitTransferFromWithAdditionalData calls to append additional validation logic, such as approving only a specific order or set of orders without opening approvals to other functions on the operator contract.
  • Unordered Execution: Nonces retain the same functionality as Permit2, meaning they can be approved and executed in any order and prevent replay.
  • Order Based Approvals: Reusable signatures scoped to a specific order ID to allow for partial trades / fills allowing for gasless outstanding orders to be leveraged. Useful in ERC1155 sales and ERC20 limit orders.

Frontend Integration​

Frontends can directly integrate by prompting the maker with a signature that includes the SaleApproval or OfferApproval as additional data to PermitC. This will be provided to the taker to execute the order, and PermitC will be used to validate the signature and move the tokens.

A mock JS integration would look like this:

const PERMIT_C_TYPE = {
PermitTransferFromWithAdditionalData: [
{ name: "token", type: "address" },
{ name: "id", type: "uint256" },
{ name: "amount", type: "uint256" },
{ name: "nonce", type: "uint256" },
{ name: "operator", type: "address" },
{ name: "expiration", type: "uint256" },
{ name: "masterNonce", type: "uint256" },
{ name: "approval", type: "SaleApproval" },
],
SaleApproval: [
{ name: "protocol", type: "uint8" },
{ name: "cosigner", type: "address" },
{ name: "seller", type: "address" },
{ name: "marketplace", type: "address" },
{ name: "fallbackRoyaltyRecipient", type: "address" },
{ name: "paymentMethod", type: "address" },
{ name: "tokenAddress", type: "address" },
{ name: "tokenId", type: "uint256" },
{ name: "amount", type: "uint256" },
{ name: "itemPrice", type: "uint256" },
{ name: "expiration", type: "uint256" },
{ name: "marketplaceFeeNumerator", type: "uint256" },
{ name: "maxRoyaltyFeeNumerator", type: "uint256" },
{ name: "nonce", type: "uint256" },
{ name: "masterNonce", type: "uint256" },
{ name: "protocolFeeVersion", type: "protocolFeeVersion" }
]
}

const permitNonce = getNextPermitNonce();

const typedData = JSON.stringify({
primaryType: 'PermitTransferFromWithAdditionalData',
domain: PERMIT_C_DOMAIN,
message: {
token: sa1.tokenAddress,
id: sa1.tokenId,
amount: sa1.amount,
nonce: permitNonce,
operator: PAYMENT_PROCESSOR_ADDRESS,
expiration: sa1.expiration,
masterNonce: sa1.masterNonce,
approval: sa1
},
types: {
EIP712Domain: [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' },
{ name: 'verifyingContract', type: 'address' }
],
... PERMIT_C_TYPE,
}
});

const params = [address, typedData];
const method = 'eth_signTypedData_v4';

const sig = await window.ethereum.request({ method, params });
setSignature(sig);

const order: AdvancedOrder = {
saleDetails: order1,
signature: sig,
cosignature: nullSig,
permitContext: {
permitProcessor: PERMIT_C_ADDRESS,
permitNonce: BigInt(permitNonce),
}
}
const nullFeeOnTop: FeeOnTop = {
recipient: zeroAddress,
amount: BigInt(0),
};
const nullBulkOrder: Proof = {
orderIndex: BigInt(0),
proof: [],
};

const calldata = await publicClient?.readContract({
address: PAYMENT_PROCESSOR_ENCODER_ADDRESS,
abi: paymentProcessorEncoderAbi.abi,
functionName: 'encodeBuyListingAdvancedCalldata',
args: [PAYMENT_PROCESSOR_ADDRESS, order, nullBulkOrder, nullFeeOnTop]
});

const {request} = await publicClient.simulateContract({
account: address,
address: PAYMENT_PROCESSOR_ADDRESS,
abi: paymentProcessorAbi.abi,
functionName: 'buyListingAdvanced',
args: [calldata]
});
await walletClient.writeContract(request);

Limit Break

TwitterLimitBreak.comMedium

© 2026 Limit Break International, Inc. All rights reserved.

Privacy PolicyTerms of ServiceCookie PolicyDo Not Sell My Info