Permit Extension
A standardized smart contract interface for approvals via signatures (CIP-712), enabling energyless approvals for CBC-20 tokens and similar standards.
Abstract
This standard defines a method for approvals via off-chain signatures (CIP-712), allowing users to approve token allowances for CBC-20 tokens without sending an on-chain transaction. CBC-2612 enables energyless approvals, improving UX and composability for wallets, dApps, and DeFi protocols.
Motivation
Traditional CBC-20 approvals require users to send an on-chain transaction, incurring energy costs and requiring two transactions for many workflows (approve + transferFrom). CBC-2612 introduces a permit function, allowing approvals to be made via signed messages, enabling energyless approvals and meta-transactions.
Specification
Compliant contracts MUST implement the following functions in addition to CBC-20:
// SPDX-License-Identifier: MIT
pragma solidity ^1.1.2;
interface ICBC2612 /* is ICBC20 */ {
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
bytes calldata signature
) external;
function nonces(address owner) external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
The semantics are as follows:
For all addresses owner, spender, uint256s value, deadline and nonce, and bytes signature, a call to permit(owner, spender, value, deadline, signature) MUST set allowance[owner][spender] to value, increment nonces[owner] by 1, and emit a corresponding Approval event, IF and ONLY IF all the following conditions are met:
- The current blocktime is less than or equal to
deadline. - Neither
ownernorspenderisaddress(0)orChecksum.zeroAddress(). nonces[owner](before the state update) is equal tononce.signatureis a valid 171-byte Core signature fromownerof the message:
keccak256(abi.encodePacked(
hex"1901",
DOMAIN_SEPARATOR,
keccak256(abi.encode(
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"),
owner,
spender,
value,
nonce,
deadline))
))
where DOMAIN_SEPARATOR is defined according to CIP-712. It MUST use the EIP712Domain type with name, version, networkId, and verifyingContract, making the separator unique to the contract and Core network. It can be calculated as follows:
DOMAIN_SEPARATOR = keccak256(
abi.encode(
keccak256('EIP712Domain(string name,string version,uint256 networkId,address verifyingContract)'),
keccak256(bytes(name)),
keccak256(bytes(version)),
block.chainid,
address(this)
));
The message is the CIP-712 typed structure:
{
"types": {
"EIP712Domain": [
{ "name": "name", "type": "string" },
{ "name": "version", "type": "string" },
{ "name": "networkId", "type": "uint256" },
{ "name": "verifyingContract", "type": "address" }
],
"Permit": [
{ "name": "owner", "type": "address" },
{ "name": "spender", "type": "address" },
{ "name": "value", "type": "uint256" },
{ "name": "nonce", "type": "uint256" },
{ "name": "deadline", "type": "uint256" }
]
},
"primaryType": "Permit",
"domain": {
"name": cbc20name,
"version": version,
"networkId": networkId,
"verifyingContract": tokenAddress
},
"message": {
"owner": owner,
"spender": spender,
"value": value,
"nonce": nonce,
"deadline": deadline
}
}
The caller of the permit function can be any address. If any of the above conditions are not met, the call MUST revert.
Method Behaviors
- The
permitfunction MUST setspender's allowance overowner's tokens tovalueif a valid signature is provided, and MUST increment the owner's nonce. - The
permitfunction MUST revert if the signature is invalid or expired. - The
DOMAIN_SEPARATORMUST be implemented according to CIP-712. - The
noncesfunction MUST return the current nonce for an address. - The
Approvalevent MUST be emitted on a successful permit.
Backward Compatibility
There are existing permit functions in some token contracts (e.g., DAI, Stake) with different semantics:
- DAI uses a
bool allowedinstead of avalueargument, andexpiryinstead ofdeadline. - Stake allows expiring approvals, only permitting
transferFromwhileexpiry >= block.timestamp.
CBC-2612 aligns with the Uniswap V2 implementation and the CBC-20 standard, using value and deadline as specified above. The requirement to revert if the permit is invalid is consistent with all major implementations.
Reference Implementation
A Core implementation is available in the xToken repository. It uses Core OpenZeppelin's EDDSA helpers, a single 171-byte signature, and the CIP-712 domain described above.
Rationale
CBC-2612 improves UX and composability by enabling energyless approvals, reducing friction for users and enabling new workflows for wallets and dApps.
Security Considerations
- Implementers must ensure signatures are unique and cannot be replayed (use nonces).
- The
permitfunction must be resistant to signature malleability. - The
DOMAIN_SEPARATORmust be unique per contract and Core network. - Upgradability and access control best practices should be followed.
Copyright
Copyright and related rights waived via CC0.