-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathAtomicHTS.sol
More file actions
182 lines (145 loc) · 11 KB
/
Copy pathAtomicHTS.sol
File metadata and controls
182 lines (145 loc) · 11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import "./IHRC719.sol";
import "./HederaTokenService.sol";
import "./IHederaTokenService.sol";
/**
* @dev This contract contains multiple examples highlighting the utilization of the
* HIP-551: Batch transactions using HTS calls via HederaTokenService Precompile contract.
* The batches rolls multiple HTS calls in one transaction that passes ACID test
* (atomicity, consistency, isolation, and durability).
* Read more about HIP-551 at https://hips.hedera.com/hip/hip-551
*/
contract AtomicHTS is HederaTokenService {
/// events ///
event BatchAssociateGrantKYCTransfer(int associateResponseCode, int grantKYCResponseCode, int transferTokenResponseCode);
event BatchApproveAssociateGrantKYCTransferFrom(int transferTokenResponseCode, int approveResponseCode, int associateResponseCode, int grantKYCResponseCode, int transferFromResponseCode);
event BatchUnfreezeGrantKYCTransferFreeze(int unfreezeTokenResponseCode, int grantKYCResponseCode, int transferTokenResponseCode, int freezeTokenResponseCode);
event BatchWipeMintTransfer(int wipeTokenResponseCode, int mintTokenResponseCode, int transferTokenResponseCode);
event BatchMintUnfreezeGrantKYCTransferFreeze(int mintTokenResponseCode, int unfreezeTokenResponseCode, int grantKYCResponseCode, int freezeTokenResponseCode);
event BatchAssociateMintGrantTransfer(int associateResponseCode, int mintTokenResponseCode, int grantKYCResponseCode, int transferTokenResponseCode);
/**
* @dev associates, grant token KYC, and send an amount of fungible token to a receiver.
* - associateToken() -> grantTokenKYC() -> transferToken()
*/
function batchAssociateGrantKYCTransfer(address token, address sender, address receiver, int64 amount) external {
(int associateResponseCode) = HederaTokenService.associateToken(receiver, token);
require(
associateResponseCode == HederaResponseCodes.SUCCESS ||
associateResponseCode == HederaResponseCodes.TOKEN_ALREADY_ASSOCIATED_TO_ACCOUNT,
"Failed to associate token."
);
/// @notice an account needs to be granted the KYC of the HTS token for it to receive the token
(int grantKYCResponseCode) = HederaTokenService.grantTokenKyc(token, receiver);
require(grantKYCResponseCode == HederaResponseCodes.SUCCESS, "Failed to grant token KYC.");
(int transferTokenResponseCode) = HederaTokenService.transferToken(token, sender, receiver, amount);
require(transferTokenResponseCode == HederaResponseCodes.SUCCESS, "Failed to transfer token.");
emit BatchAssociateGrantKYCTransfer(associateResponseCode, grantKYCResponseCode, transferTokenResponseCode);
}
/**
* @dev grant allowance and transfer the token on behalf of the token owner
* - approve() -> associateToken() -> grantTokenKyc() -> transferFrom()
*
* @notice because .approve() can only grant allowances to spender on behalf of the caller, and in this case
* `this contract` is the caller who makes transactions to the `precompile contract`. With the same reason,
* .transferFrom() will transfer the tokens from token owner to the receipient by the spender (this contract)
* on behalf of the token owner. Therefore, the spender in this particular case will also be the sender whose balance
* will be deducted by the .transferFrom() method.
*/
function batchApproveAssociateGrantKYCTransferFrom(address token, address owner, address receipient, int64 transferAmount, uint256 allowance) external {
/// top up the spender with initial fund
/// @notice it is necessary for the spender to be associated and granted token KYC to receive fund.
address spender = address(this);
(int associateContractResponseCode) = HederaTokenService.associateToken(spender, token);
require(
associateContractResponseCode == HederaResponseCodes.SUCCESS ||
associateContractResponseCode == HederaResponseCodes.TOKEN_ALREADY_ASSOCIATED_TO_ACCOUNT,
"Failed to associate token."
);
(int grantKYCContractResponseCode) = HederaTokenService.grantTokenKyc(token, spender);
require(grantKYCContractResponseCode == HederaResponseCodes.SUCCESS, "Failed to grant token KYC.");
(int transferTokenResponseCode) = HederaTokenService.transferToken(token, owner, spender, transferAmount);
require(transferTokenResponseCode == HederaResponseCodes.SUCCESS, "Failed to transfer token.");
/// main logics
(int approveResponseCode) = HederaTokenService.approve(token, spender, allowance);
require(approveResponseCode == HederaResponseCodes.SUCCESS, "Failed to grant token allowance.");
(int associateResponseCode) = HederaTokenService.associateToken(receipient, token);
require(
associateResponseCode == HederaResponseCodes.SUCCESS ||
associateResponseCode == HederaResponseCodes.TOKEN_ALREADY_ASSOCIATED_TO_ACCOUNT,
"Failed to associate token."
);
(int grantKYCResponseCode) = HederaTokenService.grantTokenKyc(token, receipient);
require(grantKYCResponseCode == HederaResponseCodes.SUCCESS, "Failed to grant token KYC.");
(int transferFromResponseCode) = this.transferFrom(token, spender, receipient, allowance);
require(transferFromResponseCode == HederaResponseCodes.SUCCESS, "Failed to transfer token.");
emit BatchApproveAssociateGrantKYCTransferFrom(transferTokenResponseCode, approveResponseCode, associateResponseCode, grantKYCResponseCode, transferFromResponseCode);
}
/**
* @dev unfreeze the token, transfers the token to receiver and freeze the token.
* - unfreezeToken() -> grantTokenKyc() -> transferToken() -> freezeToken()
*/
function batchUnfreezeGrantKYCTransferFreeze(address token, address sender, address receiver, int64 amount) external {
(int unfreezeTokenResponseCode) = HederaTokenService.unfreezeToken(token, receiver);
require(unfreezeTokenResponseCode == HederaResponseCodes.SUCCESS, "Failed to unfreeze token.");
(int grantKYCResponseCode) = HederaTokenService.grantTokenKyc(token, receiver);
require(grantKYCResponseCode == HederaResponseCodes.SUCCESS, "Failed to grant token KYC.");
(int transferTokenResponseCode) = HederaTokenService.transferToken(token, sender, receiver, amount);
require(transferTokenResponseCode == HederaResponseCodes.SUCCESS, "Failed to transfer token.");
(int freezeTokenResponseCode) = HederaTokenService.freezeToken(token, receiver);
require(freezeTokenResponseCode == HederaResponseCodes.SUCCESS, "Failed to freeze token.");
emit BatchUnfreezeGrantKYCTransferFreeze(unfreezeTokenResponseCode, grantKYCResponseCode, transferTokenResponseCode, freezeTokenResponseCode);
}
/**
* @dev wipes a token from token owner's balance, then mint more tokens to the treasury and finally transfer the token from treasury to the token owner.
* - wipeTokenAccount() -> mintToken() -> transferToken()
*/
function batchWipeMintTransfer(address token, address treasury, address owner, int64 wipedAmount, int64 mintAmount, int64 transferAmount) external {
bytes[] memory metadata;
(int wipeTokenResponseCode) = HederaTokenService.wipeTokenAccount(token, owner, wipedAmount);
require(wipeTokenResponseCode == HederaResponseCodes.SUCCESS, "Failed to wipe token.");
(int mintTokenResponseCode,,) = HederaTokenService.mintToken(token, mintAmount, metadata);
require(mintTokenResponseCode == HederaResponseCodes.SUCCESS, "Failed to mint token.");
(int transferTokenResponseCode) = HederaTokenService.transferToken(token, treasury, owner, transferAmount);
require(transferTokenResponseCode == HederaResponseCodes.SUCCESS, "Failed to transfer token.");
emit BatchWipeMintTransfer(wipeTokenResponseCode, mintTokenResponseCode, transferTokenResponseCode);
}
/**
* @dev mints new tokens to treasury, unfreeze the receiver on the token so the receiver can receive token, and finally reset the freeze status on the receiver and the token.
* - mintToken() -> unfreezeToken() -> grantTokenKyc() -> transferToken() -> freezeToken()
*/
function batchMintUnfreezeGrantKYCTransferFreeze(address token, address sender, address receiver, int64 mintAmount, int64 transferAmount) external {
bytes[] memory metadata;
(int mintTokenResponseCode,,) = HederaTokenService.mintToken(token, mintAmount, metadata);
require(mintTokenResponseCode == HederaResponseCodes.SUCCESS, "Failed to mint token.");
(int unfreezeTokenResponseCode) = HederaTokenService.unfreezeToken(token, receiver);
require(unfreezeTokenResponseCode == HederaResponseCodes.SUCCESS, "Failed to unfreeze token.");
(int grantKYCResponseCode) = HederaTokenService.grantTokenKyc(token, receiver);
require(grantKYCResponseCode == HederaResponseCodes.SUCCESS, "Failed to grant token KYC.");
(int transferTokenResponseCode) = HederaTokenService.transferToken(token, sender, receiver, transferAmount);
require(transferTokenResponseCode == HederaResponseCodes.SUCCESS, "Failed to transfer token.");
(int freezeTokenResponseCode) = HederaTokenService.freezeToken(token, receiver);
require(freezeTokenResponseCode == HederaResponseCodes.SUCCESS, "Failed to freeze token.");
emit BatchMintUnfreezeGrantKYCTransferFreeze(mintTokenResponseCode, unfreezeTokenResponseCode, grantKYCResponseCode, freezeTokenResponseCode);
}
/**
* @dev associate the token with receiver, then mint new token on treasury and transfer the new minted tokens to receiver
* - associateToken() -> mintToken() -> grantTokenKyc() -> transferToken()
*/
function batchAssociateMintGrantTransfer(address token, address sender, address receiver, int64 amount) external {
(int associateResponseCode) = HederaTokenService.associateToken(receiver, token);
require(
associateResponseCode == HederaResponseCodes.SUCCESS ||
associateResponseCode == HederaResponseCodes.TOKEN_ALREADY_ASSOCIATED_TO_ACCOUNT,
"Failed to associate token."
);
bytes[] memory metadata;
(int mintTokenResponseCode,,) = HederaTokenService.mintToken(token, amount, metadata);
require(mintTokenResponseCode == HederaResponseCodes.SUCCESS, "Failed to mint token.");
(int grantKYCResponseCode) = HederaTokenService.grantTokenKyc(token, receiver);
require(grantKYCResponseCode == HederaResponseCodes.SUCCESS, "Failed to grant token KYC.");
(int transferTokenResponseCode) = HederaTokenService.transferToken(token, sender, receiver, amount);
require(transferTokenResponseCode == HederaResponseCodes.SUCCESS, "Failed to transfer token.");
emit BatchAssociateMintGrantTransfer(associateResponseCode, mintTokenResponseCode, grantKYCResponseCode, transferTokenResponseCode);
}
}