t-isol 0.0.8 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/contracts/ITransferAuthorize.sol +3 -27
- package/contracts/TransferAuthorize.sol +13 -72
- package/contracts/base/BasexERC20.sol +38 -0
- package/contracts/kit/ERC20WrappedxWithAuthorize.sol +112 -0
- package/contracts/kit/ERC20xTransferWithAuthorize.sol +34 -65
- package/contracts/kit/KitxERC20.sol +41 -0
- package/contracts/modular/ERC20xTransferWithAuthorize.sol +83 -0
- package/contracts copy/ITransferAuthorize.sol +49 -0
- package/contracts copy/TransferAuthorize.sol +100 -0
- package/contracts copy/kit/ERC20xTransferWithAuthorize.sol +74 -0
- package/package.json +1 -1
|
@@ -8,13 +8,13 @@ interface ITransferAuthorize {
|
|
|
8
8
|
/// @notice Emitted when an authorization (nonce) has been consumed for an authorizer
|
|
9
9
|
/// @param authorizer the account that signed the authorization
|
|
10
10
|
/// @param nonce unique nonce used by the authorization
|
|
11
|
-
event AuthorizeUsed(address
|
|
11
|
+
event AuthorizeUsed(address authorizer, bytes32 nonce);
|
|
12
12
|
|
|
13
13
|
/// @notice Emitted when a transfer with authorization is executed
|
|
14
|
-
event TransferWithAuthorize(address sender, address
|
|
14
|
+
event TransferWithAuthorize(address sender, address from, address to, uint256 value, uint256 createTime, uint256 expireTime, bytes32 nonce, bytes auth);
|
|
15
15
|
|
|
16
16
|
/// @notice Emitted when a burn with authorization is executed
|
|
17
|
-
event BurnWithAuthorize(address
|
|
17
|
+
event BurnWithAuthorize(address sender, address from, uint256 value, uint256 createTime, uint256 expireTime, bytes32 nonce, bytes auth);
|
|
18
18
|
|
|
19
19
|
/// @notice Returns whether a nonce has been used for a given authorizer
|
|
20
20
|
/// @param authorizer the account that signed the authorization
|
|
@@ -35,30 +35,6 @@ interface ITransferAuthorize {
|
|
|
35
35
|
bytes32 nonce,
|
|
36
36
|
bytes calldata auth
|
|
37
37
|
) external;
|
|
38
|
-
|
|
39
|
-
// ++
|
|
40
|
-
/// @notice Execute a "transfer with authorization" - typically any relayer may submit
|
|
41
|
-
function transferFromWithAuthorize(
|
|
42
|
-
address from,
|
|
43
|
-
address to,
|
|
44
|
-
uint256 value,
|
|
45
|
-
uint256 validAfter,
|
|
46
|
-
uint256 validBefore,
|
|
47
|
-
bytes32 nonce,
|
|
48
|
-
bytes calldata signature
|
|
49
|
-
) external;
|
|
50
|
-
|
|
51
|
-
// ++
|
|
52
|
-
/// @notice Execute a "approve with authorization" - typically any relayer may submit
|
|
53
|
-
function approveWithAuthorize(
|
|
54
|
-
address from,
|
|
55
|
-
address to,
|
|
56
|
-
uint256 value,
|
|
57
|
-
uint256 validAfter,
|
|
58
|
-
uint256 validBefore,
|
|
59
|
-
bytes32 nonce,
|
|
60
|
-
bytes calldata signature
|
|
61
|
-
) external;
|
|
62
38
|
|
|
63
39
|
/// @notice Execute a "approve with authorization" - typically any relayer may submit
|
|
64
40
|
function burnWithAuthorize(
|
|
@@ -17,14 +17,8 @@ abstract contract TransferAuthorize is ITransferAuthorize, EIP712 {
|
|
|
17
17
|
bytes32 public constant TRANSFER_TYPEHASH =
|
|
18
18
|
keccak256("TransferWithAuthorize(address from,address to,uint256 value,uint256 createTime,uint256 expireTime,bytes32 nonce)");
|
|
19
19
|
|
|
20
|
-
bytes32 public constant TRANSFERFROM_TYPEHASH =
|
|
21
|
-
keccak256("TransferFromWithAuthorize(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)");
|
|
22
|
-
|
|
23
|
-
bytes32 public constant APPROVE_TYPEHASH =
|
|
24
|
-
keccak256("ApproveWithAuthorize(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)");
|
|
25
|
-
|
|
26
20
|
bytes32 public constant BURN_TYPEHASH =
|
|
27
|
-
keccak256("BurnWithAuthorize(address from,uint256 value,uint256
|
|
21
|
+
keccak256("BurnWithAuthorize(address from,uint256 value,uint256 createTime,uint256 expireTime,bytes32 nonce)");
|
|
28
22
|
|
|
29
23
|
/// @param name EIP-712 domain name
|
|
30
24
|
/// @param version EIP-712 domain version
|
|
@@ -61,7 +55,7 @@ abstract contract TransferAuthorize is ITransferAuthorize, EIP712 {
|
|
|
61
55
|
uint256 expireTime,
|
|
62
56
|
bytes32 nonce,
|
|
63
57
|
bytes calldata auth
|
|
64
|
-
) internal
|
|
58
|
+
) internal returns (address signer) {
|
|
65
59
|
require(block.timestamp >= createTime, "TransferAuthorize: not yet created");
|
|
66
60
|
require(block.timestamp <= expireTime, "TransferAuthorize: not yet expired");
|
|
67
61
|
bytes32 structHash = keccak256(
|
|
@@ -79,81 +73,28 @@ abstract contract TransferAuthorize is ITransferAuthorize, EIP712 {
|
|
|
79
73
|
emit TransferWithAuthorize(msg.sender, from, to, value, createTime, expireTime, nonce, auth);
|
|
80
74
|
}
|
|
81
75
|
|
|
82
|
-
/// @dev Verify an EIP-712 typed signature for
|
|
83
|
-
function _verifyTransferFrom(
|
|
84
|
-
address from,
|
|
85
|
-
address to,
|
|
86
|
-
uint256 value,
|
|
87
|
-
uint256 validAfter,
|
|
88
|
-
uint256 validBefore,
|
|
89
|
-
bytes32 nonce,
|
|
90
|
-
bytes calldata signature
|
|
91
|
-
) internal view returns (address signer) {
|
|
92
|
-
require(block.timestamp >= validAfter, "TransferAuthorize: not yet valid");
|
|
93
|
-
require(block.timestamp <= validBefore, "TransferAuthorize: expired");
|
|
94
|
-
bytes32 structHash = keccak256(
|
|
95
|
-
abi.encode(
|
|
96
|
-
TRANSFERFROM_TYPEHASH,
|
|
97
|
-
from,
|
|
98
|
-
to,
|
|
99
|
-
value,
|
|
100
|
-
validAfter,
|
|
101
|
-
validBefore,
|
|
102
|
-
nonce
|
|
103
|
-
)
|
|
104
|
-
);
|
|
105
|
-
signer = ECDSA.recover(_hashTypedDataV4(structHash), signature);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/// @dev Verify an EIP-712 typed signature for ApproveWithAuthorize and return the recovered signer
|
|
109
|
-
function _verifyApprove(
|
|
110
|
-
address from,
|
|
111
|
-
address to,
|
|
112
|
-
uint256 value,
|
|
113
|
-
uint256 validAfter,
|
|
114
|
-
uint256 validBefore,
|
|
115
|
-
bytes32 nonce,
|
|
116
|
-
bytes calldata signature
|
|
117
|
-
) internal view returns (address signer) {
|
|
118
|
-
require(block.timestamp >= validAfter, "TransferAuthorize: not yet valid");
|
|
119
|
-
require(block.timestamp <= validBefore, "TransferAuthorize: expired");
|
|
120
|
-
bytes32 structHash = keccak256(
|
|
121
|
-
abi.encode(
|
|
122
|
-
APPROVE_TYPEHASH,
|
|
123
|
-
from,
|
|
124
|
-
to,
|
|
125
|
-
value,
|
|
126
|
-
validAfter,
|
|
127
|
-
validBefore,
|
|
128
|
-
nonce
|
|
129
|
-
)
|
|
130
|
-
);
|
|
131
|
-
signer = ECDSA.recover(_hashTypedDataV4(structHash), signature);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/// @dev Verify an EIP-712 typed signature for BurnWithAuthorize and return the recovered signer
|
|
76
|
+
/// @dev Verify an EIP-712 typed signature (auth) for BurnWithAuthorize and return the recovered signer
|
|
135
77
|
function _verifyBurn(
|
|
136
78
|
address from,
|
|
137
|
-
// address to,
|
|
138
79
|
uint256 value,
|
|
139
|
-
uint256
|
|
140
|
-
uint256
|
|
80
|
+
uint256 createTime,
|
|
81
|
+
uint256 expireTime,
|
|
141
82
|
bytes32 nonce,
|
|
142
|
-
bytes calldata
|
|
143
|
-
) internal
|
|
144
|
-
require(block.timestamp >=
|
|
145
|
-
require(block.timestamp <=
|
|
83
|
+
bytes calldata auth
|
|
84
|
+
) internal returns (address signer) {
|
|
85
|
+
require(block.timestamp >= createTime, "TransferAuthorize: not yet valid");
|
|
86
|
+
require(block.timestamp <= expireTime, "TransferAuthorize: expired");
|
|
146
87
|
bytes32 structHash = keccak256(
|
|
147
88
|
abi.encode(
|
|
148
89
|
BURN_TYPEHASH,
|
|
149
90
|
from,
|
|
150
|
-
// to,
|
|
151
91
|
value,
|
|
152
|
-
|
|
153
|
-
|
|
92
|
+
createTime,
|
|
93
|
+
expireTime,
|
|
154
94
|
nonce
|
|
155
95
|
)
|
|
156
96
|
);
|
|
157
|
-
signer = ECDSA.recover(_hashTypedDataV4(structHash),
|
|
97
|
+
signer = ECDSA.recover(_hashTypedDataV4(structHash), auth);
|
|
98
|
+
emit BurnWithAuthorize(msg.sender, from, value, createTime, expireTime, nonce, auth);
|
|
158
99
|
}
|
|
159
100
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @title BasexERC20 (Base)
|
|
8
|
+
* @notice Provides a simple and efficient way to deploy your own ERC20 token with custom parameters, saving time and reducing errors.
|
|
9
|
+
* @dev Inherit this contract to quickly deploy an ERC20 token using OpenZeppelin's implementation.
|
|
10
|
+
* Set name, symbol, and initial supply via constructor. Mints all supply to deployer.
|
|
11
|
+
*
|
|
12
|
+
* Arguments:
|
|
13
|
+
* - name (string): Token name
|
|
14
|
+
* - symbol (string): Token symbol
|
|
15
|
+
* - initialSupply (uint256): Initial token supply (Integer number)
|
|
16
|
+
*
|
|
17
|
+
* Example:
|
|
18
|
+
* contract MyToken is BasexERC20 {
|
|
19
|
+
* constructor()
|
|
20
|
+
* BasexERC20(
|
|
21
|
+
* "MyToken",
|
|
22
|
+
* "MTK",
|
|
23
|
+
* 1000000 // 1 million tokens
|
|
24
|
+
* )
|
|
25
|
+
* {}
|
|
26
|
+
* }
|
|
27
|
+
*/
|
|
28
|
+
abstract contract BasexERC20 is ERC20 {
|
|
29
|
+
constructor(
|
|
30
|
+
string memory name,
|
|
31
|
+
string memory symbol,
|
|
32
|
+
uint256 initialSupply
|
|
33
|
+
)
|
|
34
|
+
ERC20(name, symbol)
|
|
35
|
+
{
|
|
36
|
+
_mint(msg.sender, initialSupply * 10 ** decimals());
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
|
5
|
+
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Wrapper.sol";
|
|
6
|
+
import "../TransferAuthorize.sol";
|
|
7
|
+
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
|
|
8
|
+
import "@openzeppelin/contracts/access/Ownable.sol";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @title ERC20WrappedxWithAuthorize (Kit)
|
|
12
|
+
* @notice ERC20 wrapper with EIP-712 based off-chain authorized transfers and burns.
|
|
13
|
+
* @dev Wraps an existing ERC20 token, adding `transferWithAuthorize` and `burnWithAuthorize` functions.
|
|
14
|
+
* Enables gasless transfers and advanced flows using signatures. Integrates OpenZeppelin ERC20Wrapper, ReentrancyGuard, and Ownable.
|
|
15
|
+
*
|
|
16
|
+
* Arguments:
|
|
17
|
+
* - underlyingToken (address): Address of the underlying ERC20 token to wrap
|
|
18
|
+
* - name (string): Token name
|
|
19
|
+
* - symbol (string): Token symbol
|
|
20
|
+
* - domainName (string): EIP-712 domain name
|
|
21
|
+
* - domainVersion (string): EIP-712 domain version
|
|
22
|
+
*
|
|
23
|
+
* Example:
|
|
24
|
+
* contract MyWrappedToken is ERC20WrappedxWithAuthorize {
|
|
25
|
+
* constructor()
|
|
26
|
+
* ERC20WrappedxWithAuthorize(
|
|
27
|
+
* 0x123...abc, // underlying token address
|
|
28
|
+
* "MyWrappedToken",
|
|
29
|
+
* "MWTK",
|
|
30
|
+
* "MyWrappedToken",
|
|
31
|
+
* "1"
|
|
32
|
+
* )
|
|
33
|
+
* {}
|
|
34
|
+
* }
|
|
35
|
+
*/
|
|
36
|
+
abstract contract ERC20WrappedxWithAuthorize is ERC20, ERC20Wrapper, TransferAuthorize, ReentrancyGuard, Ownable {
|
|
37
|
+
constructor(
|
|
38
|
+
address _underlyingToken,
|
|
39
|
+
string memory _name,
|
|
40
|
+
string memory _symbol,
|
|
41
|
+
string memory _domainName,
|
|
42
|
+
string memory _domainVersion
|
|
43
|
+
)
|
|
44
|
+
ERC20(_name, _symbol)
|
|
45
|
+
ERC20Wrapper(IERC20(_underlyingToken))
|
|
46
|
+
TransferAuthorize(_domainName, _domainVersion)
|
|
47
|
+
Ownable(msg.sender)
|
|
48
|
+
{}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @notice Returns the token decimals
|
|
52
|
+
* @dev Overrides both ERC20 and ERC20Wrapper decimals function
|
|
53
|
+
* @return uint8 decimals of the token (usually matches underlying token)
|
|
54
|
+
*/
|
|
55
|
+
function decimals()
|
|
56
|
+
public
|
|
57
|
+
view
|
|
58
|
+
virtual
|
|
59
|
+
override(ERC20, ERC20Wrapper)
|
|
60
|
+
returns (uint8)
|
|
61
|
+
{
|
|
62
|
+
return super.decimals();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/// @notice Relayer-triggered transfer using an off-chain signature by `from`.
|
|
66
|
+
/// @dev This allows anyone (a relayer) to submit the signed authorization on-chain.
|
|
67
|
+
function transferWithAuthorize(
|
|
68
|
+
address from,
|
|
69
|
+
address to,
|
|
70
|
+
uint256 value,
|
|
71
|
+
uint256 createTime,
|
|
72
|
+
uint256 expireTime,
|
|
73
|
+
bytes32 nonce,
|
|
74
|
+
bytes calldata auth
|
|
75
|
+
) external override nonReentrant {
|
|
76
|
+
address signer = _verifyTransfer(
|
|
77
|
+
from,
|
|
78
|
+
to,
|
|
79
|
+
value,
|
|
80
|
+
createTime,
|
|
81
|
+
expireTime,
|
|
82
|
+
nonce,
|
|
83
|
+
auth
|
|
84
|
+
);
|
|
85
|
+
require(signer == from, "transferWithAuthorize: invalid signature");
|
|
86
|
+
_useAuthorize(from, nonce);
|
|
87
|
+
_transfer(from, to, value);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/// @notice Relayer-triggered transfer using an off-chain signature by `from`.
|
|
91
|
+
/// @dev This allows anyone (a relayer) to submit the signed authorization on-chain.
|
|
92
|
+
function burnWithAuthorize(
|
|
93
|
+
address from,
|
|
94
|
+
uint256 value,
|
|
95
|
+
uint256 createTime,
|
|
96
|
+
uint256 expireTime,
|
|
97
|
+
bytes32 nonce,
|
|
98
|
+
bytes calldata auth
|
|
99
|
+
) external override nonReentrant {
|
|
100
|
+
address signer = _verifyBurn(
|
|
101
|
+
from,
|
|
102
|
+
value,
|
|
103
|
+
createTime,
|
|
104
|
+
expireTime,
|
|
105
|
+
nonce,
|
|
106
|
+
auth
|
|
107
|
+
);
|
|
108
|
+
require(signer == from, "burnWithAuthorize: invalid signature");
|
|
109
|
+
_useAuthorize(from, nonce);
|
|
110
|
+
_burn(from, value);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -6,11 +6,33 @@ import "../TransferAuthorize.sol";
|
|
|
6
6
|
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
|
|
7
7
|
import "@openzeppelin/contracts/access/Ownable.sol";
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
/**
|
|
10
|
+
* @title ERC20xTransferWithAuthorize (Kit)
|
|
11
|
+
* @notice ERC20 extension for fast, secure, and flexible off-chain authorized transfers and burns (EIP-712).
|
|
12
|
+
* @dev Adds `transferWithAuthorize` and `burnWithAuthorize` for meta-transactions and relayer support.
|
|
13
|
+
* Enables gasless transfers and advanced flows using signatures. Integrates OpenZeppelin ERC20, ReentrancyGuard, and Ownable.
|
|
14
|
+
*
|
|
15
|
+
* Arguments:
|
|
16
|
+
* - name (string): Token name
|
|
17
|
+
* - symbol (string): Token symbol
|
|
18
|
+
* - initialSupply (uint256): Initial token supply (Integer number)
|
|
19
|
+
* - domainName (string): EIP-712 domain name
|
|
20
|
+
* - domainVersion (string): EIP-712 domain version
|
|
21
|
+
*
|
|
22
|
+
* Example:
|
|
23
|
+
* contract MyToken is ERC20xTransferWithAuthorize {
|
|
24
|
+
* constructor()
|
|
25
|
+
* ERC20xTransferWithAuthorize(
|
|
26
|
+
* "MyToken",
|
|
27
|
+
* "MTK",
|
|
28
|
+
* 1000000, // 1 million tokens
|
|
29
|
+
* "MyToken",
|
|
30
|
+
* "1"
|
|
31
|
+
* )
|
|
32
|
+
* {}
|
|
33
|
+
* }
|
|
34
|
+
*/
|
|
12
35
|
abstract contract ERC20xTransferWithAuthorize is ERC20, TransferAuthorize, ReentrancyGuard, Ownable {
|
|
13
|
-
|
|
14
36
|
constructor(
|
|
15
37
|
string memory name,
|
|
16
38
|
string memory symbol,
|
|
@@ -45,83 +67,30 @@ abstract contract ERC20xTransferWithAuthorize is ERC20, TransferAuthorize, Reent
|
|
|
45
67
|
nonce,
|
|
46
68
|
auth
|
|
47
69
|
);
|
|
48
|
-
require(signer == from, "
|
|
70
|
+
require(signer == from, "transferWithAuthorize: invalid signature");
|
|
49
71
|
_useAuthorize(from, nonce);
|
|
50
72
|
_transfer(from, to, value);
|
|
51
73
|
}
|
|
52
74
|
|
|
53
|
-
/// @notice Relayer-triggered transfer using an off-chain signature by `from`.
|
|
54
|
-
/// @dev This allows anyone (a relayer) to submit the signed authorization on-chain.
|
|
55
|
-
function transferFromWithAuthorize(
|
|
56
|
-
address from,
|
|
57
|
-
address to,
|
|
58
|
-
uint256 value,
|
|
59
|
-
uint256 validAfter,
|
|
60
|
-
uint256 validBefore,
|
|
61
|
-
bytes32 nonce,
|
|
62
|
-
bytes calldata signature
|
|
63
|
-
) external override nonReentrant {
|
|
64
|
-
address signer = _verifyTransferFrom(
|
|
65
|
-
from,
|
|
66
|
-
to,
|
|
67
|
-
value,
|
|
68
|
-
validAfter,
|
|
69
|
-
validBefore,
|
|
70
|
-
nonce,
|
|
71
|
-
signature
|
|
72
|
-
);
|
|
73
|
-
require(signer == from, "ERC20TransferAuthorize: invalid signature");
|
|
74
|
-
_useAuthorize(from, nonce);
|
|
75
|
-
_spendAllowance(from, to, value);
|
|
76
|
-
_transfer(from, to, value);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/// @notice Relayer-triggered transfer using an off-chain signature by `from`.
|
|
80
|
-
/// @dev This allows anyone (a relayer) to submit the signed authorization on-chain.
|
|
81
|
-
function approveWithAuthorize(
|
|
82
|
-
address from,
|
|
83
|
-
address to,
|
|
84
|
-
uint256 value,
|
|
85
|
-
uint256 validAfter,
|
|
86
|
-
uint256 validBefore,
|
|
87
|
-
bytes32 nonce,
|
|
88
|
-
bytes calldata signature
|
|
89
|
-
) external override nonReentrant {
|
|
90
|
-
address signer = _verifyApprove(
|
|
91
|
-
from,
|
|
92
|
-
to,
|
|
93
|
-
value,
|
|
94
|
-
validAfter,
|
|
95
|
-
validBefore,
|
|
96
|
-
nonce,
|
|
97
|
-
signature
|
|
98
|
-
);
|
|
99
|
-
require(signer == from, "ERC20TransferAuthorize: invalid signature");
|
|
100
|
-
_useAuthorize(from, nonce);
|
|
101
|
-
_approve(from, to, value);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
75
|
/// @notice Relayer-triggered transfer using an off-chain signature by `from`.
|
|
105
76
|
/// @dev This allows anyone (a relayer) to submit the signed authorization on-chain.
|
|
106
77
|
function burnWithAuthorize(
|
|
107
78
|
address from,
|
|
108
|
-
// address to,
|
|
109
79
|
uint256 value,
|
|
110
|
-
uint256
|
|
111
|
-
uint256
|
|
80
|
+
uint256 createTime,
|
|
81
|
+
uint256 expireTime,
|
|
112
82
|
bytes32 nonce,
|
|
113
|
-
bytes calldata
|
|
83
|
+
bytes calldata auth
|
|
114
84
|
) external override nonReentrant {
|
|
115
85
|
address signer = _verifyBurn(
|
|
116
86
|
from,
|
|
117
|
-
// to,
|
|
118
87
|
value,
|
|
119
|
-
|
|
120
|
-
|
|
88
|
+
createTime,
|
|
89
|
+
expireTime,
|
|
121
90
|
nonce,
|
|
122
|
-
|
|
91
|
+
auth
|
|
123
92
|
);
|
|
124
|
-
require(signer == from, "
|
|
93
|
+
require(signer == from, "burnWithAuthorize: invalid signature");
|
|
125
94
|
_useAuthorize(from, nonce);
|
|
126
95
|
_burn(from, value);
|
|
127
96
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
|
5
|
+
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
|
|
6
|
+
import "@openzeppelin/contracts/access/Ownable.sol";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @title KitxERC20 (Kit)
|
|
10
|
+
* @notice Fast ERC20 foundation with burnable and ownable features for rapid token creation.
|
|
11
|
+
* @dev Inherit to quickly deploy a custom ERC20 token with burn and ownership control.
|
|
12
|
+
* Set name, symbol, and initial supply via constructor. Mints all supply to deployer.
|
|
13
|
+
*
|
|
14
|
+
* Arguments:
|
|
15
|
+
* - name (string): Token name
|
|
16
|
+
* - symbol (string): Token symbol
|
|
17
|
+
* - initialSupply (uint256): Initial token supply (Integer number)
|
|
18
|
+
*
|
|
19
|
+
* Example:
|
|
20
|
+
* contract MyToken is KitxERC20 {
|
|
21
|
+
* constructor()
|
|
22
|
+
* KitxERC20(
|
|
23
|
+
* "MyToken",
|
|
24
|
+
* "MTK",
|
|
25
|
+
* 1000000 // 1 million tokens
|
|
26
|
+
* )
|
|
27
|
+
* {}
|
|
28
|
+
* }
|
|
29
|
+
*/
|
|
30
|
+
abstract contract KitxERC20 is ERC20, ERC20Burnable, Ownable {
|
|
31
|
+
constructor(
|
|
32
|
+
string memory name,
|
|
33
|
+
string memory symbol,
|
|
34
|
+
uint256 initialSupply
|
|
35
|
+
)
|
|
36
|
+
ERC20(name, symbol)
|
|
37
|
+
Ownable(msg.sender)
|
|
38
|
+
{
|
|
39
|
+
_mint(msg.sender, initialSupply * 10 ** decimals());
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
|
5
|
+
import "../TransferAuthorize.sol";
|
|
6
|
+
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @title ERC20xTransferWithAuthorize (Modular)
|
|
10
|
+
* @notice ERC20 extension for modular, off-chain authorized transfers and burns (EIP-712).
|
|
11
|
+
* @dev Adds `transferWithAuthorize` and `burnWithAuthorize` for meta-transactions and relayer support.
|
|
12
|
+
* Enables gasless transfers and advanced flows using signatures. Integrates OpenZeppelin ERC20 and ReentrancyGuard.
|
|
13
|
+
*
|
|
14
|
+
* Arguments:
|
|
15
|
+
* - domainName (string): EIP-712 domain name
|
|
16
|
+
* - domainVersion (string): EIP-712 domain version
|
|
17
|
+
*
|
|
18
|
+
* Example:
|
|
19
|
+
* contract MyToken is ERC20xTransferWithAuthorize {
|
|
20
|
+
* constructor()
|
|
21
|
+
* ERC20xTransferWithAuthorize(
|
|
22
|
+
* "MyToken",
|
|
23
|
+
* "1"
|
|
24
|
+
* )
|
|
25
|
+
* {}
|
|
26
|
+
* }
|
|
27
|
+
*/
|
|
28
|
+
abstract contract ERC20xTransferWithAuthorize is ERC20, TransferAuthorize, ReentrancyGuard {
|
|
29
|
+
constructor(
|
|
30
|
+
string memory domainName,
|
|
31
|
+
string memory domainVersion
|
|
32
|
+
)
|
|
33
|
+
TransferAuthorize(domainName, domainVersion)
|
|
34
|
+
{}
|
|
35
|
+
|
|
36
|
+
/// @notice Relayer-triggered transfer using an off-chain signature by `from`.
|
|
37
|
+
/// @dev This allows anyone (a relayer) to submit the signed authorization on-chain.
|
|
38
|
+
function transferWithAuthorize(
|
|
39
|
+
address from,
|
|
40
|
+
address to,
|
|
41
|
+
uint256 value,
|
|
42
|
+
uint256 createTime,
|
|
43
|
+
uint256 expireTime,
|
|
44
|
+
bytes32 nonce,
|
|
45
|
+
bytes calldata auth
|
|
46
|
+
) external override nonReentrant {
|
|
47
|
+
address signer = _verifyTransfer(
|
|
48
|
+
from,
|
|
49
|
+
to,
|
|
50
|
+
value,
|
|
51
|
+
createTime,
|
|
52
|
+
expireTime,
|
|
53
|
+
nonce,
|
|
54
|
+
auth
|
|
55
|
+
);
|
|
56
|
+
require(signer == from, "transferWithAuthorize: invalid signature");
|
|
57
|
+
_useAuthorize(from, nonce);
|
|
58
|
+
_transfer(from, to, value);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/// @notice Relayer-triggered transfer using an off-chain signature by `from`.
|
|
62
|
+
/// @dev This allows anyone (a relayer) to submit the signed authorization on-chain.
|
|
63
|
+
function burnWithAuthorize(
|
|
64
|
+
address from,
|
|
65
|
+
uint256 value,
|
|
66
|
+
uint256 createTime,
|
|
67
|
+
uint256 expireTime,
|
|
68
|
+
bytes32 nonce,
|
|
69
|
+
bytes calldata auth
|
|
70
|
+
) external override nonReentrant {
|
|
71
|
+
address signer = _verifyBurn(
|
|
72
|
+
from,
|
|
73
|
+
value,
|
|
74
|
+
createTime,
|
|
75
|
+
expireTime,
|
|
76
|
+
nonce,
|
|
77
|
+
auth
|
|
78
|
+
);
|
|
79
|
+
require(signer == from, "burnWithAuthorize: invalid signature");
|
|
80
|
+
_useAuthorize(from, nonce);
|
|
81
|
+
_burn(from, value);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
/// @title ITransferAuthorize
|
|
5
|
+
/// @notice Interface for EIP-712 based transfer/receive authorizations (signed approvals)
|
|
6
|
+
/// @dev Events and function signatures used by TransferAuthorize/implementations
|
|
7
|
+
interface ITransferAuthorize {
|
|
8
|
+
/// @notice Emitted when an authorization (nonce) has been consumed for an authorizer
|
|
9
|
+
/// @param authorizer the account that signed the authorization
|
|
10
|
+
/// @param nonce unique nonce used by the authorization
|
|
11
|
+
event AuthorizeUsed(address authorizer, bytes32 nonce);
|
|
12
|
+
|
|
13
|
+
/// @notice Emitted when a transfer with authorization is executed
|
|
14
|
+
event TransferWithAuthorize(address sender, address from, address to, uint256 value, uint256 createTime, uint256 expireTime, bytes32 nonce, bytes auth);
|
|
15
|
+
|
|
16
|
+
/// @notice Emitted when a burn with authorization is executed
|
|
17
|
+
event BurnWithAuthorize(address sender, address from, uint256 value, uint256 createTime, uint256 expireTime, bytes32 nonce, bytes auth);
|
|
18
|
+
|
|
19
|
+
/// @notice Returns whether a nonce has been used for a given authorizer
|
|
20
|
+
/// @param authorizer the account that signed the authorization
|
|
21
|
+
/// @param nonce the nonce to check
|
|
22
|
+
/// @return true if the nonce was already used/consumed, false otherwise
|
|
23
|
+
function authorizeState(
|
|
24
|
+
address authorizer,
|
|
25
|
+
bytes32 nonce
|
|
26
|
+
) external view returns (bool);
|
|
27
|
+
|
|
28
|
+
/// @notice Execute a "transfer with authorization" - typically any relayer may submit
|
|
29
|
+
function transferWithAuthorize(
|
|
30
|
+
address from,
|
|
31
|
+
address to,
|
|
32
|
+
uint256 value,
|
|
33
|
+
uint256 createTime,
|
|
34
|
+
uint256 expireTime,
|
|
35
|
+
bytes32 nonce,
|
|
36
|
+
bytes calldata auth
|
|
37
|
+
) external;
|
|
38
|
+
|
|
39
|
+
/// @notice Execute a "approve with authorization" - typically any relayer may submit
|
|
40
|
+
function burnWithAuthorize(
|
|
41
|
+
address from,
|
|
42
|
+
// address to,
|
|
43
|
+
uint256 value,
|
|
44
|
+
uint256 validAfter,
|
|
45
|
+
uint256 validBefore,
|
|
46
|
+
bytes32 nonce,
|
|
47
|
+
bytes calldata signature
|
|
48
|
+
) external;
|
|
49
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
|
|
5
|
+
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
|
|
6
|
+
import "./ITransferAuthorize.sol";
|
|
7
|
+
|
|
8
|
+
/// @title TransferAuthorize
|
|
9
|
+
/// @notice Abstract helper implementing EIP-712 verification and nonce tracking for off-chain signed authorizations
|
|
10
|
+
/// @dev Implementations should call `_verify` and `_useAuthorize` before performing state changes
|
|
11
|
+
abstract contract TransferAuthorize is ITransferAuthorize, EIP712 {
|
|
12
|
+
using ECDSA for bytes32;
|
|
13
|
+
|
|
14
|
+
/// @dev mapping to track used nonces per authorizer: authorizer => nonce => used
|
|
15
|
+
mapping(address => mapping(bytes32 => bool)) private _authorizeState;
|
|
16
|
+
|
|
17
|
+
bytes32 public constant TRANSFER_TYPEHASH =
|
|
18
|
+
keccak256("TransferWithAuthorize(address from,address to,uint256 value,uint256 createTime,uint256 expireTime,bytes32 nonce)");
|
|
19
|
+
|
|
20
|
+
bytes32 public constant BURN_TYPEHASH =
|
|
21
|
+
keccak256("BurnWithAuthorize(address from,uint256 value,uint256 createTime,uint256 expireTime,bytes32 nonce)");
|
|
22
|
+
|
|
23
|
+
/// @param name EIP-712 domain name
|
|
24
|
+
/// @param version EIP-712 domain version
|
|
25
|
+
constructor(string memory name, string memory version) EIP712(name, version) {}
|
|
26
|
+
|
|
27
|
+
/// @notice Check whether a nonce has been used for an authorizer
|
|
28
|
+
/// @param authorizer signer address
|
|
29
|
+
/// @param nonce nonce value
|
|
30
|
+
/// @return true if used
|
|
31
|
+
function authorizeState(address authorizer, bytes32 nonce)
|
|
32
|
+
public
|
|
33
|
+
view
|
|
34
|
+
override
|
|
35
|
+
returns (bool)
|
|
36
|
+
{
|
|
37
|
+
return _authorizeState[authorizer][nonce];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/// @dev Mark an authorization (authorizer + nonce) as used. Emits AuthorizeUsed.
|
|
41
|
+
/// @param authorizer the signer of the authorization
|
|
42
|
+
/// @param nonce the nonce used in the signed authorization
|
|
43
|
+
function _useAuthorize(address authorizer, bytes32 nonce) internal {
|
|
44
|
+
require(!_authorizeState[authorizer][nonce], "TransferAuthorize: authorization already used");
|
|
45
|
+
_authorizeState[authorizer][nonce] = true;
|
|
46
|
+
emit AuthorizeUsed(authorizer, nonce);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/// @dev Verify an EIP-712 typed signature (auth) for TransferWithAuthorize and return the recovered signer
|
|
50
|
+
function _verifyTransfer(
|
|
51
|
+
address from,
|
|
52
|
+
address to,
|
|
53
|
+
uint256 value,
|
|
54
|
+
uint256 createTime,
|
|
55
|
+
uint256 expireTime,
|
|
56
|
+
bytes32 nonce,
|
|
57
|
+
bytes calldata auth
|
|
58
|
+
) internal returns (address signer) {
|
|
59
|
+
require(block.timestamp >= createTime, "TransferAuthorize: not yet created");
|
|
60
|
+
require(block.timestamp <= expireTime, "TransferAuthorize: not yet expired");
|
|
61
|
+
bytes32 structHash = keccak256(
|
|
62
|
+
abi.encode(
|
|
63
|
+
TRANSFER_TYPEHASH,
|
|
64
|
+
from,
|
|
65
|
+
to,
|
|
66
|
+
value,
|
|
67
|
+
createTime,
|
|
68
|
+
expireTime,
|
|
69
|
+
nonce
|
|
70
|
+
)
|
|
71
|
+
);
|
|
72
|
+
signer = ECDSA.recover(_hashTypedDataV4(structHash), auth);
|
|
73
|
+
emit TransferWithAuthorize(msg.sender, from, to, value, createTime, expireTime, nonce, auth);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/// @dev Verify an EIP-712 typed signature (auth) for BurnWithAuthorize and return the recovered signer
|
|
77
|
+
function _verifyBurn(
|
|
78
|
+
address from,
|
|
79
|
+
uint256 value,
|
|
80
|
+
uint256 createTime,
|
|
81
|
+
uint256 expireTime,
|
|
82
|
+
bytes32 nonce,
|
|
83
|
+
bytes calldata auth
|
|
84
|
+
) internal returns (address signer) {
|
|
85
|
+
require(block.timestamp >= createTime, "TransferAuthorize: not yet valid");
|
|
86
|
+
require(block.timestamp <= expireTime, "TransferAuthorize: expired");
|
|
87
|
+
bytes32 structHash = keccak256(
|
|
88
|
+
abi.encode(
|
|
89
|
+
BURN_TYPEHASH,
|
|
90
|
+
from,
|
|
91
|
+
value,
|
|
92
|
+
createTime,
|
|
93
|
+
expireTime,
|
|
94
|
+
nonce
|
|
95
|
+
)
|
|
96
|
+
);
|
|
97
|
+
signer = ECDSA.recover(_hashTypedDataV4(structHash), auth);
|
|
98
|
+
emit BurnWithAuthorize(msg.sender, from, value, createTime, expireTime, nonce, auth);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
|
5
|
+
import "../TransferAuthorize.sol";
|
|
6
|
+
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
|
|
7
|
+
import "@openzeppelin/contracts/access/Ownable.sol";
|
|
8
|
+
|
|
9
|
+
/// @title ERC20xTransferWithAuthorize
|
|
10
|
+
/// @notice ERC20 extension that supports EIP-712 based `transferWithAuthorize` and `receiveWithAuthorize` flows
|
|
11
|
+
/// @dev `receiveWithAuthorize` restricts the caller to the `to` address (recipient submits the proof). `transferWithAuthorize` is open to relayers.
|
|
12
|
+
abstract contract ERC20xTransferWithAuthorize is ERC20, TransferAuthorize, ReentrancyGuard, Ownable {
|
|
13
|
+
constructor(
|
|
14
|
+
string memory name,
|
|
15
|
+
string memory symbol,
|
|
16
|
+
uint256 initialSupply,
|
|
17
|
+
string memory domainName,
|
|
18
|
+
string memory domainVersion
|
|
19
|
+
)
|
|
20
|
+
ERC20(name, symbol)
|
|
21
|
+
TransferAuthorize(domainName, domainVersion)
|
|
22
|
+
Ownable(msg.sender)
|
|
23
|
+
{
|
|
24
|
+
_mint(msg.sender, initialSupply * 10 ** decimals());
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// @notice Relayer-triggered transfer using an off-chain signature by `from`.
|
|
28
|
+
/// @dev This allows anyone (a relayer) to submit the signed authorization on-chain.
|
|
29
|
+
function transferWithAuthorize(
|
|
30
|
+
address from,
|
|
31
|
+
address to,
|
|
32
|
+
uint256 value,
|
|
33
|
+
uint256 createTime,
|
|
34
|
+
uint256 expireTime,
|
|
35
|
+
bytes32 nonce,
|
|
36
|
+
bytes calldata auth
|
|
37
|
+
) external override nonReentrant {
|
|
38
|
+
address signer = _verifyTransfer(
|
|
39
|
+
from,
|
|
40
|
+
to,
|
|
41
|
+
value,
|
|
42
|
+
createTime,
|
|
43
|
+
expireTime,
|
|
44
|
+
nonce,
|
|
45
|
+
auth
|
|
46
|
+
);
|
|
47
|
+
require(signer == from, "transferWithAuthorize: invalid signature");
|
|
48
|
+
_useAuthorize(from, nonce);
|
|
49
|
+
_transfer(from, to, value);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/// @notice Relayer-triggered transfer using an off-chain signature by `from`.
|
|
53
|
+
/// @dev This allows anyone (a relayer) to submit the signed authorization on-chain.
|
|
54
|
+
function burnWithAuthorize(
|
|
55
|
+
address from,
|
|
56
|
+
uint256 value,
|
|
57
|
+
uint256 createTime,
|
|
58
|
+
uint256 expireTime,
|
|
59
|
+
bytes32 nonce,
|
|
60
|
+
bytes calldata auth
|
|
61
|
+
) external override nonReentrant {
|
|
62
|
+
address signer = _verifyBurn(
|
|
63
|
+
from,
|
|
64
|
+
value,
|
|
65
|
+
createTime,
|
|
66
|
+
expireTime,
|
|
67
|
+
nonce,
|
|
68
|
+
auth
|
|
69
|
+
);
|
|
70
|
+
require(signer == from, "burnWithAuthorize: invalid signature");
|
|
71
|
+
_useAuthorize(from, nonce);
|
|
72
|
+
_burn(from, value);
|
|
73
|
+
}
|
|
74
|
+
}
|