We make available the following repository with a simple script as an example of how to generate the permits needed for the burn and swap endpoints: Permit Signer
Regarding using these endpoints without a permit, be it because the token you want to use does not support permit (e.g. USDT in Ethereum) or because you're not using an EOA (externally owned account) and thus does not have a private key to sign the payload, you must manually send an approve transaction to the blockchain before interacting with the endpoint, and must send the signature and signatureDeadline fields in the request body. Here, we explain how to do so.
Permit-less transactions for EOA
First, approve the usage of your tokens by our smart contracts. You'll have to send the transaction to the blockchain and pay the gas yourself.
Now, you have to sign a personal message which the content is the Keccak256 Hash of the deadline (in string format) for this signature in UNIX milliseconds. This deadline you must pass in the signatureDeadline field, and the signature itself must be passed in the signature field. The signature is generated according to EIP-191. Below is an example of how to produce the signature.
privateKey, _ := crypto.HexToECDSA("private key here")
deadline := time.Now().Add(60 * time.Second).UnixMilli() // Signature valid for 60 seconds, should be passed in the signatureDeadline field
msgData := fmt.Sprintf("%d", deadline) // Deadline to string
msgHash := crypto.Keccak256Hash([]byte(msgData)) // Hash the content
messagePrefix := fmt.Sprintf("\x19Ethereum Signed Message:\n32") // EIP-191
messageBytes := append([]byte(messagePrefix), msgHash.Bytes()...)
hash := crypto.Keccak256Hash(messageBytes)
signatureBytes, _ := crypto.Sign(hash.Bytes(), privateKey) // Sign using private key
signatureBytes[64] += 27
signature := hexutil.Encode(signatureBytes) // Signature ready to be sent
Permit-less transactions for Smart Contract Accounts
For this case, we expect your smart contract to implement EIP-1271. For example:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
contract SmartcontractWallet {
address public owner;
using SafeERC20 for IERC20;
using ECDSA for bytes32;
constructor(address _owner) {
owner = _owner;
}
function approve(address spender, uint256 amount, address token) public {
IERC20(token).forceApprove(spender, amount);
}
/**
* @notice Verifies that the signer is the owner of the signing contract.
*/
function isValidSignature(
bytes32 _hash,
bytes calldata _signature
) external view returns (bytes4) {
address result = _hash.recover(_signature);
// Validate signatures
if (result == owner) {
return 0x1626ba7e;
} else {
return 0xffffffff;
}
}
}
You still need to approve our contracts and send the signatureDeadline and the signature fields. We'll calculate the correct hash, and call the isValidSignature from your smart contract with the signature you sent in the request. If it returns 0x1626ba7e, we'll accept the signature and proceed with the operation. Validating the signature is a responsibility exclusively from your smart contract. The deadline is validated in our backend, meaning that, although an old signature might be correct, we'll ignore it if enough time has passed.