Skip to content

Commit 554ebfc

Browse files
Merge pull request #169 from manishdait/token-client-ext
feat: Implement dissociateNft/Token and multi-token association
2 parents 1eea9a1 + 5d06df8 commit 554ebfc

File tree

15 files changed

+920
-18
lines changed

15 files changed

+920
-18
lines changed

hiero-enterprise-base/src/main/java/com/openelements/hiero/base/FungibleTokenClient.java

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import com.hedera.hashgraph.sdk.PrivateKey;
55
import com.hedera.hashgraph.sdk.TokenId;
66
import com.openelements.hiero.base.data.Account;
7+
8+
import java.util.List;
79
import java.util.Objects;
810
import org.jspecify.annotations.NonNull;
911

@@ -230,6 +232,91 @@ default void associateToken(@NonNull TokenId tokenId, @NonNull Account account)
230232
associateToken(tokenId, account.accountId(), account.privateKey());
231233
}
232234

235+
/**
236+
* Associate an account with token.
237+
*
238+
* @param tokenIds list of the ID of the token
239+
* @param accountId the accountId
240+
* @param accountKey the account privateKey
241+
* @throws HieroException if the account could not be associated with the token
242+
*/
243+
void associateToken(@NonNull List<TokenId> tokenIds, @NonNull AccountId accountId, @NonNull PrivateKey accountKey)
244+
throws HieroException;
245+
246+
/**
247+
* Associate an account with token.
248+
*
249+
* @param tokenIds list of the ID of the token
250+
* @param account the account
251+
* @throws HieroException if the account could not be associated with the token
252+
*/
253+
default void associateToken(@NonNull List<TokenId> tokenIds, @NonNull Account account) throws HieroException {
254+
Objects.requireNonNull(account, "accountId must not be null");
255+
associateToken(tokenIds, account.accountId(), account.privateKey());
256+
};
257+
258+
/**
259+
* Dissociate an account with token.
260+
*
261+
* @param tokenId the ID of the token
262+
* @param accountId the accountId
263+
* @param accountKey the account privateKey
264+
* @throws HieroException if the account could not be associated with the token
265+
*/
266+
void dissociateToken(@NonNull TokenId tokenId, @NonNull AccountId accountId, @NonNull PrivateKey accountKey)
267+
throws HieroException;
268+
269+
/**
270+
* Dissociate an account with token.
271+
*
272+
* @param tokenId the ID of the token
273+
* @param accountId the accountId
274+
* @param accountKey the account privateKey
275+
* @throws HieroException if the account could not be associated with the token
276+
*/
277+
default void dissociateToken(@NonNull String tokenId, @NonNull String accountId, @NonNull String accountKey)
278+
throws HieroException {
279+
Objects.requireNonNull(tokenId, "tokenId must not be null");
280+
Objects.requireNonNull(accountId, "accountId must not be null");
281+
Objects.requireNonNull(accountKey, "accountKey must not be null");
282+
dissociateToken(TokenId.fromString(tokenId), AccountId.fromString(accountId), PrivateKey.fromString(accountKey));
283+
};
284+
285+
/**
286+
* Dissociate an account with token.
287+
*
288+
* @param tokenId the ID of the token
289+
* @param account the account
290+
* @throws HieroException if the account could not be associated with the token
291+
*/
292+
default void dissociateToken(@NonNull TokenId tokenId, @NonNull Account account) throws HieroException {
293+
Objects.requireNonNull(account, "accountId must not be null");
294+
dissociateToken(tokenId, account.accountId(), account.privateKey());
295+
};
296+
297+
/**
298+
* Dissociate an account with token.
299+
*
300+
* @param tokenIds list of the ID of the token
301+
* @param accountId the accountId
302+
* @param accountKey the account privateKey
303+
* @throws HieroException if the account could not be associated with the token
304+
*/
305+
void dissociateToken(@NonNull List<TokenId> tokenIds, @NonNull AccountId accountId, @NonNull PrivateKey accountKey)
306+
throws HieroException;
307+
308+
/**
309+
* Dissociate an account with token.
310+
*
311+
* @param tokenIds list of the ID of the token
312+
* @param account the account
313+
* @throws HieroException if the account could not be associated with the token
314+
*/
315+
default void dissociateToken(@NonNull List<TokenId> tokenIds, @NonNull Account account) throws HieroException {
316+
Objects.requireNonNull(account, "accountId must not be null");
317+
dissociateToken(tokenIds, account.accountId(), account.privateKey());
318+
};
319+
233320
/**
234321
* Mint a Token.
235322
*

hiero-enterprise-base/src/main/java/com/openelements/hiero/base/NftClient.java

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import java.util.List;
88
import java.util.Objects;
99
import java.util.Set;
10+
11+
import com.openelements.hiero.base.data.Token;
1012
import org.jspecify.annotations.NonNull;
1113

1214
/**
@@ -203,6 +205,93 @@ default void associateNft(@NonNull TokenId tokenId, @NonNull Account account) th
203205
associateNft(tokenId, account.accountId(), account.privateKey());
204206
}
205207

208+
/**
209+
* Associate an account with an NFT type. If an account is associated with an NFT type, the account can hold NFTs of
210+
* that type. Otherwise, the account cannot hold NFTs of that type and tranfer NFTs of that type will fail.
211+
*
212+
* @param tokenIds the List of ID for NFT type
213+
* @param accountId the accountId
214+
* @param accountKey the account privateKey
215+
* @throws HieroException if the account could not be associated with the NFT type
216+
*/
217+
void associateNft(@NonNull List<TokenId> tokenIds, @NonNull AccountId accountId, @NonNull PrivateKey accountKey)
218+
throws HieroException;
219+
220+
/**
221+
* Associate an account with an NFT type. If an account is associated with an NFT type, the account can hold NFTs of
222+
* that type. Otherwise, the account cannot hold NFTs of that type and tranfer NFTs of that type will fail.
223+
*
224+
* @param tokenIds the List of ID for NFT type
225+
* @param account the account
226+
* @throws HieroException if the account could not be associated with the NFT type
227+
*/
228+
default void associateNft(@NonNull List<TokenId> tokenIds, @NonNull Account account) throws HieroException {
229+
Objects.requireNonNull(account, "accountId must not be null");
230+
associateNft(tokenIds, account.accountId(), account.privateKey());
231+
};
232+
233+
/**
234+
* Dissociate an account with an NFT type.
235+
*
236+
* @param tokenId the ID of the NFT type
237+
* @param accountId the accountId
238+
* @param accountKey the account privateKey
239+
* @throws HieroException if the account could not be associated with the NFT type
240+
*/
241+
void dissociateNft(@NonNull TokenId tokenId, @NonNull AccountId accountId, @NonNull PrivateKey accountKey)
242+
throws HieroException;
243+
244+
/**
245+
* Dissociate an account with an NFT type.
246+
*
247+
* @param tokenId the ID of the NFT type
248+
* @param accountId the accountId
249+
* @param accountKey the account privateKey
250+
* @throws HieroException if the account could not be associated with the NFT type
251+
*/
252+
default void dissociateNft(@NonNull String tokenId, @NonNull String accountId, @NonNull String accountKey)
253+
throws HieroException {
254+
Objects.requireNonNull(tokenId, "tokenId must not be null");
255+
Objects.requireNonNull(accountId, "accountId must not be null");
256+
Objects.requireNonNull(accountKey, "accountKey must not be null");
257+
dissociateNft(TokenId.fromString(tokenId), AccountId.fromString(accountId), PrivateKey.fromString(accountKey));
258+
};
259+
260+
/**
261+
* Dissociate an account with an NFT type.
262+
*
263+
* @param tokenId the ID of the NFT type
264+
* @param account the account
265+
* @throws HieroException if the account could not be associated with the NFT type
266+
*/
267+
default void dissociateNft(@NonNull TokenId tokenId, @NonNull Account account) throws HieroException {
268+
Objects.requireNonNull(account, "accountId must not be null");
269+
dissociateNft(tokenId, account.accountId(), account.privateKey());
270+
};
271+
272+
/**
273+
* Dissociate an account with an NFT type.
274+
*
275+
* @param tokenIds the List of ID for NFT type
276+
* @param accountId the accountId
277+
* @param accountKey the account privateKey
278+
* @throws HieroException if the account could not be associated with the NFT type
279+
*/
280+
void dissociateNft(@NonNull List<TokenId> tokenIds, @NonNull AccountId accountId, @NonNull PrivateKey accountKey)
281+
throws HieroException;
282+
283+
/**
284+
* Dissociate an account with an NFT type.
285+
*
286+
* @param tokenIds the List of ID for NFT type
287+
* @param account the account
288+
* @throws HieroException if the account could not be associated with the NFT type
289+
*/
290+
default void dissociateNft(@NonNull List<TokenId> tokenIds, @NonNull Account account) throws HieroException {
291+
Objects.requireNonNull(account, "accountId must not be null");
292+
dissociateNft(tokenIds, account.accountId(), account.privateKey());
293+
};
294+
206295
/**
207296
* Mint a new NFT of the given type. The NFT is minted by the operator account. The operator account is used as
208297
* supply account for the NFT.

hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/FungibleTokenClientImpl.java

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77
import com.hedera.hashgraph.sdk.TokenType;
88
import com.openelements.hiero.base.HieroException;
99
import com.openelements.hiero.base.data.Account;
10-
import com.openelements.hiero.base.protocol.*;
10+
import com.openelements.hiero.base.protocol.ProtocolLayerClient;
1111
import com.openelements.hiero.base.protocol.data.TokenAssociateRequest;
12+
import com.openelements.hiero.base.protocol.data.TokenDissociateRequest;
1213
import com.openelements.hiero.base.protocol.data.TokenBurnRequest;
1314
import com.openelements.hiero.base.protocol.data.TokenBurnResult;
1415
import com.openelements.hiero.base.protocol.data.TokenCreateRequest;
@@ -18,6 +19,7 @@
1819
import com.openelements.hiero.base.protocol.data.TokenTransferRequest;
1920
import org.jspecify.annotations.NonNull;
2021

22+
import java.util.List;
2123
import java.util.Objects;
2224

2325
public class FungibleTokenClientImpl implements FungibleTokenClient {
@@ -65,6 +67,39 @@ public void associateToken(@NonNull TokenId tokenId, @NonNull AccountId accountI
6567
client.executeTokenAssociateTransaction(request);
6668
}
6769

70+
@Override
71+
public void associateToken(@NonNull List<TokenId> tokenIds, @NonNull AccountId accountId, @NonNull PrivateKey accountKey) throws HieroException {
72+
Objects.requireNonNull(tokenIds, "tokenIds must not be null");
73+
Objects.requireNonNull(accountId, "accountId must not be null");
74+
Objects.requireNonNull(accountKey, "accountKey must not be null");
75+
if (tokenIds.isEmpty()) {
76+
throw new IllegalArgumentException("tokenIds must not be empty");
77+
}
78+
final TokenAssociateRequest request = TokenAssociateRequest.of(tokenIds, accountId, accountKey);
79+
client.executeTokenAssociateTransaction(request);
80+
}
81+
82+
@Override
83+
public void dissociateToken(@NonNull TokenId tokenId, @NonNull AccountId accountId, @NonNull PrivateKey accountKey) throws HieroException {
84+
Objects.requireNonNull(tokenId, "tokenId must not be null");
85+
Objects.requireNonNull(accountId, "accountId must not be null");
86+
Objects.requireNonNull(accountKey, "accountKey must not be null");
87+
final TokenDissociateRequest request = TokenDissociateRequest.of(tokenId, accountId, accountKey);
88+
client.executeTokenDissociateTransaction(request);
89+
}
90+
91+
@Override
92+
public void dissociateToken(@NonNull List<TokenId> tokenIds, @NonNull AccountId accountId, @NonNull PrivateKey accountKey) throws HieroException {
93+
Objects.requireNonNull(tokenIds, "tokenIds must not be null");
94+
Objects.requireNonNull(accountId, "accountId must not be null");
95+
Objects.requireNonNull(accountKey, "accountKey must not be null");
96+
if (tokenIds.isEmpty()) {
97+
throw new IllegalArgumentException("tokenIds must not be empty");
98+
}
99+
final TokenDissociateRequest request = TokenDissociateRequest.of(tokenIds, accountId, accountKey);
100+
client.executeTokenDissociateTransaction(request);
101+
}
102+
68103
@Override
69104
public long mintToken(@NonNull TokenId tokenId, long amount) throws HieroException {
70105
return mintToken(tokenId, operationalAccount.privateKey(), amount);

hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/NftClientImpl.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import com.openelements.hiero.base.NftClient;
1010
import com.openelements.hiero.base.protocol.ProtocolLayerClient;
1111
import com.openelements.hiero.base.protocol.data.TokenAssociateRequest;
12+
import com.openelements.hiero.base.protocol.data.TokenDissociateRequest;
1213
import com.openelements.hiero.base.protocol.data.TokenBurnRequest;
1314
import com.openelements.hiero.base.protocol.data.TokenCreateRequest;
1415
import com.openelements.hiero.base.protocol.data.TokenCreateResult;
@@ -67,6 +68,39 @@ public void associateNft(@NonNull final TokenId tokenId, @NonNull final AccountI
6768
client.executeTokenAssociateTransaction(request);
6869
}
6970

71+
@Override
72+
public void associateNft(@NonNull List<TokenId> tokenIds, @NonNull AccountId accountId, @NonNull PrivateKey accountKey) throws HieroException {
73+
Objects.requireNonNull(tokenIds, "tokenIds must not be null");
74+
Objects.requireNonNull(accountId, "accountId must not be null");
75+
Objects.requireNonNull(accountKey, "accountKey must not be null");
76+
if (tokenIds.isEmpty()) {
77+
throw new IllegalArgumentException("tokenIds must not be empty");
78+
}
79+
final TokenAssociateRequest request = TokenAssociateRequest.of(tokenIds, accountId, accountKey);
80+
client.executeTokenAssociateTransaction(request);
81+
}
82+
83+
@Override
84+
public void dissociateNft(@NonNull TokenId tokenId, @NonNull AccountId accountId, @NonNull PrivateKey accountKey) throws HieroException {
85+
Objects.requireNonNull(tokenId, "tokenId must not be null");
86+
Objects.requireNonNull(accountId, "accountId must not be null");
87+
Objects.requireNonNull(accountKey, "accountKey must not be null");
88+
final TokenDissociateRequest request = TokenDissociateRequest.of(tokenId, accountId, accountKey);
89+
client.executeTokenDissociateTransaction(request);
90+
}
91+
92+
@Override
93+
public void dissociateNft(@NonNull List<TokenId> tokenIds, @NonNull AccountId accountId, @NonNull PrivateKey accountKey) throws HieroException {
94+
Objects.requireNonNull(tokenIds, "tokenIds must not be null");
95+
Objects.requireNonNull(accountId, "accountId must not be null");
96+
Objects.requireNonNull(accountKey, "accountKey must not be null");
97+
if (tokenIds.isEmpty()) {
98+
throw new IllegalArgumentException("tokenIds must not be empty");
99+
}
100+
final TokenDissociateRequest request = TokenDissociateRequest.of(tokenIds, accountId, accountKey);
101+
client.executeTokenDissociateTransaction(request);
102+
}
103+
70104
@Override
71105
public long mintNft(@NonNull TokenId tokenId, @NonNull byte[] metadata) throws HieroException {
72106
return mintNft(tokenId, operationalAccount.privateKey(), metadata);

hiero-enterprise-base/src/main/java/com/openelements/hiero/base/implementation/ProtocolLayerClientImpl.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.hedera.hashgraph.sdk.Query;
2424
import com.hedera.hashgraph.sdk.SubscriptionHandle;
2525
import com.hedera.hashgraph.sdk.TokenAssociateTransaction;
26+
import com.hedera.hashgraph.sdk.TokenDissociateTransaction;
2627
import com.hedera.hashgraph.sdk.TokenBurnTransaction;
2728
import com.hedera.hashgraph.sdk.TokenCreateTransaction;
2829
import com.hedera.hashgraph.sdk.TokenMintTransaction;
@@ -67,6 +68,8 @@
6768
import com.openelements.hiero.base.protocol.ProtocolLayerClient;
6869
import com.openelements.hiero.base.protocol.data.TokenAssociateRequest;
6970
import com.openelements.hiero.base.protocol.data.TokenAssociateResult;
71+
import com.openelements.hiero.base.protocol.data.TokenDissociateRequest;
72+
import com.openelements.hiero.base.protocol.data.TokenDissociateResult;
7073
import com.openelements.hiero.base.protocol.data.TokenBurnRequest;
7174
import com.openelements.hiero.base.protocol.data.TokenBurnResult;
7275
import com.openelements.hiero.base.protocol.data.TokenCreateRequest;
@@ -452,7 +455,7 @@ public TokenAssociateResult executeTokenAssociateTransaction(@NonNull final Toke
452455
final TokenAssociateTransaction transaction = new TokenAssociateTransaction()
453456
.setMaxTransactionFee(request.maxTransactionFee())
454457
.setTransactionValidDuration(request.transactionValidDuration())
455-
.setTokenIds(List.of(request.tokenId()))
458+
.setTokenIds(request.tokenIds())
456459
.setAccountId(request.accountId());
457460
sign(transaction, request.accountPrivateKey());
458461
final TransactionReceipt receipt = executeTransactionAndWaitOnReceipt(transaction);
@@ -462,6 +465,24 @@ public TokenAssociateResult executeTokenAssociateTransaction(@NonNull final Toke
462465
}
463466
}
464467

468+
@Override
469+
public @NonNull TokenDissociateResult executeTokenDissociateTransaction(@NonNull TokenDissociateRequest request)
470+
throws HieroException {
471+
Objects.requireNonNull(request, "request must not be null");
472+
try {
473+
final TokenDissociateTransaction transaction = new TokenDissociateTransaction()
474+
.setMaxTransactionFee(request.maxTransactionFee())
475+
.setTransactionValidDuration(request.transactionValidDuration())
476+
.setAccountId(request.accountId())
477+
.setTokenIds(request.tokenIds());
478+
sign(transaction, request.accountKey());
479+
final TransactionReceipt receipt = executeTransactionAndWaitOnReceipt(transaction);
480+
return new TokenDissociateResult(receipt.transactionId, receipt.status);
481+
} catch (final Exception e) {
482+
throw new HieroException("Failed to execute dissociate token transaction", e);
483+
}
484+
}
485+
465486
public TokenBurnResult executeBurnTokenTransaction(@NonNull final TokenBurnRequest request) throws HieroException {
466487
Objects.requireNonNull(request, "request must not be null");
467488
try {

hiero-enterprise-base/src/main/java/com/openelements/hiero/base/protocol/ProtocolLayerClient.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import com.openelements.hiero.base.protocol.data.FileUpdateResult;
2929
import com.openelements.hiero.base.protocol.data.TokenAssociateRequest;
3030
import com.openelements.hiero.base.protocol.data.TokenAssociateResult;
31+
import com.openelements.hiero.base.protocol.data.TokenDissociateRequest;
32+
import com.openelements.hiero.base.protocol.data.TokenDissociateResult;
3133
import com.openelements.hiero.base.protocol.data.TokenBurnRequest;
3234
import com.openelements.hiero.base.protocol.data.TokenBurnResult;
3335
import com.openelements.hiero.base.protocol.data.TokenCreateRequest;
@@ -197,6 +199,17 @@ AccountCreateResult executeAccountCreateTransaction(@NonNull final AccountCreate
197199
TokenAssociateResult executeTokenAssociateTransaction(@NonNull final TokenAssociateRequest request)
198200
throws HieroException;
199201

202+
/**
203+
* Executes a token dissociate transaction.
204+
*
205+
* @param request the request containing the details of the token dissociate transaction
206+
* @return the result of the token dissociate transaction
207+
* @throws HieroException if the transaction could not be executed
208+
*/
209+
@NonNull
210+
TokenDissociateResult executeTokenDissociateTransaction(@NonNull final TokenDissociateRequest request)
211+
throws HieroException;
212+
200213
/**
201214
* Executes a token mint transaction.
202215
*

0 commit comments

Comments
 (0)