/**
* @notice Execute a transfer with a signed authorization
* @param from Payer's address (Authorizer)
* @param to Payee's address
* @param value Amount to be transferred
* @param validAfter The time after which this is valid (unix time)
* @param validBefore The time before which this is valid (unix time)
* @param nonce Unique nonce
* @param signature Unstructured bytes signature signed by an EOA wallet or a contract wallet
*/
function transferWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
bytes memory signature
) external;
/**
* @notice Receive a transfer with a signed authorization from the payer
* @dev This has an additional check to ensure that the payee's address matches
* the caller of this function to prevent front-running attacks. (See security
* considerations)
* @param from Payer's address (Authorizer)
* @param to Payee's address
* @param value Amount to be transferred
* @param validAfter The time after which this is valid (unix time)
* @param validBefore The time before which this is valid (unix time)
* @param nonce Unique nonce
* @param signature Unstructured bytes signature signed by an EOA wallet or a contract wallet
*/
function receiveWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
bytes memory signature
) external;
/**
* @notice Attempt to cancel an authorization
* @param authorizer Authorizer's address
* @param nonce Nonce of the authorization
* @param signature Unstructured bytes signature signed by an EOA wallet or a contract wallet
*/
function cancelAuthorization(
address authorizer,
bytes32 nonce,
bytes memory signature
) external;
/**
* @notice Execute a transfer with a signed authorization
* @dev EOA wallet signatures should be packed in the order of r, s, v.
* @param from Payer's address (Authorizer)
* @param to Payee's address
* @param value Amount to be transferred
* @param validAfter The time after which this is valid (unix time)
* @param validBefore The time before which this is valid (unix time)
* @param nonce Unique nonce
* @param signature Signature byte array produced by an EOA wallet or a contract wallet
*/
function _transferWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
bytes memory signature
) internal {
require(now > validAfter, "Authorization is not yet valid"); // 授权尚未生效
require(now < validBefore, "Authorization is expired"); // 授权已过期
require(!_authorizationStates[authorizer][nonce], "Authorization is used or canceled"); // 授权已使用或已取消
bytes32 digest = keccak256(abi.encodePacked(
hex"1901",
DOMAIN_SEPARATOR,
keccak256(abi.encode(
TRANSFER_WITH_AUTHORIZATION_TYPEHASH,
from,
to,
value,
validAfter,
validBefore,
nonce
))
));
require(
// Check for both ECDSA signature and and ERC-1271 signature. A sample SignatureChecker is available at
// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/7bd2b2a/contracts/utils/cryptography/SignatureChecker.sol
// 同时检查 ECDSA 签名和 ERC-1271 签名。SignatureChecker 示例可在以下网址获取
// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/7bd2b2a/contracts/utils/cryptography/SignatureChecker.sol
SignatureChecker.isValidSignatureNow(
owner,
typedDataHash,
signature
),
"Invalid signature" // 无效签名
);
_authorizationStates[authorizer][nonce] = true;
emit AuthorizationUsed(authorizer, nonce);
_transfer(from, to, value);
}