Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Implement TokenRejectTransaction #2411

Merged
merged 28 commits into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e4645f1
feat: TokenRejectTransaction js file
ivaylonikolov7 Jul 21, 2024
3062404
test: unit tests for TokenRejectTransaction
ivaylonikolov7 Jul 22, 2024
600d189
test: add integration tests for TokenRejectTransaction
ivaylonikolov7 Jul 24, 2024
643c2a7
feat: TokenRejectFlow
ivaylonikolov7 Jul 25, 2024
23e8ca5
feat: TokenRejectFlow and TokenReject example
ivaylonikolov7 Jul 25, 2024
7bbc8c8
fix: circular dependency
ivaylonikolov7 Jul 26, 2024
90f77a4
chore: lint change
ivaylonikolov7 Jul 29, 2024
6d2ded3
chore: remove client
ivaylonikolov7 Jul 30, 2024
7188d68
refactor: use to string for status codes
ivaylonikolov7 Jul 30, 2024
a0d81ad
refactor: use ? instead of | null
ivaylonikolov7 Jul 30, 2024
c9298be
refactor: avoid usage of token identifier
ivaylonikolov7 Jul 30, 2024
c46e907
refator: add PublicKey type for jsdocs in type definitions
ivaylonikolov7 Jul 30, 2024
bcfd88b
fix: should not support strings conversion in token reject tx
ivaylonikolov7 Jul 30, 2024
9b34993
fix(test): unit test should not expect token identifier property
ivaylonikolov7 Jul 30, 2024
2948f93
fix: use proper PublicKey path
ivaylonikolov7 Jul 30, 2024
497552c
feat: better console logging when executing examples
ivaylonikolov7 Jul 30, 2024
9524515
refactor: avoid using magical number in example script
ivaylonikolov7 Jul 31, 2024
c236004
chore: remove unused imports
ivaylonikolov7 Jul 31, 2024
7ac6eb8
chore: improve comments naming
ivaylonikolov7 Jul 31, 2024
75ed001
chore: improve token naming
ivaylonikolov7 Jul 31, 2024
2102c52
chore: max automatic token assoc should be 100 for consistency betwee…
ivaylonikolov7 Jul 31, 2024
652fe40
fix: reconnect to working node (#2417)
svetoslav-nikol0v Jul 31, 2024
25452f2
test(fix): update token reject outdated test
ivaylonikolov7 Aug 1, 2024
3ed0bd0
chore: comments typo and improvement
ivaylonikolov7 Aug 1, 2024
514e3e2
test: temporary skip allowance tests for NFT and FT
ivaylonikolov7 Aug 1, 2024
b7cf3dc
test: ignore eslint warnings for skipped tests
ivaylonikolov7 Aug 1, 2024
cada4d4
chore: add id-token permission to pages.yml workflow (#2418)
isavov Jul 31, 2024
4d58d55
Merge branch 'main' into feat/hedera-904-token-reject
agadzhalov Aug 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
241 changes: 241 additions & 0 deletions examples/token-reject.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
import {
AccountCreateTransaction,
PrivateKey,
TokenCreateTransaction,
TransferTransaction,
AccountId,
Client,
TokenType,
TokenMintTransaction,
TokenRejectTransaction,
TokenRejectFlow,
NftId,
AccountBalanceQuery,
TokenSupplyType,
} from "@hashgraph/sdk";
import dotenv from "dotenv";

dotenv.config();

async function main() {
if (
process.env.OPERATOR_ID == null ||
process.env.OPERATOR_KEY == null ||
process.env.HEDERA_NETWORK == null
) {
throw new Error(
"Environment variables OPERATOR_ID, HEDERA_NETWORK, and OPERATOR_KEY are required.",
);
}
const CID = [
"QmNPCiNA3Dsu3K5FxDPMG5Q3fZRwVTg14EXA92uqEeSRXn",
"QmZ4dgAgt8owvnULxnKxNe8YqpavtVCXmc1Lt2XajFpJs9",
"QmPzY5GxevjyfMUF5vEAjtyRoigzWp47MiKAtLBduLMC1T",
];
const operatorId = AccountId.fromString(process.env.OPERATOR_ID);
const operatorKey = PrivateKey.fromStringED25519(process.env.OPERATOR_KEY);
const network = process.env.HEDERA_NETWORK;
const client = Client.forName(network).setOperator(operatorId, operatorKey);

// create a treasury account
const treasuryPrivateKey = PrivateKey.generateED25519();
const treasuryAccountId = (
await (
await new AccountCreateTransaction()
.setKey(treasuryPrivateKey)
.setMaxAutomaticTokenAssociations(100)
.execute(client)
).getReceipt(client)
).accountId;

// create a receiver account with unlimited max auto associations
const receiverPrivateKey = PrivateKey.generateED25519();
const receiverAccountId = (
await (
await new AccountCreateTransaction()
.setKey(receiverPrivateKey)
.setMaxAutomaticTokenAssociations(-1)
.execute(client)
).getReceipt(client)
).accountId;

// create a nft collection
const nftCreationTx = await (
ivaylonikolov7 marked this conversation as resolved.
Show resolved Hide resolved
await new TokenCreateTransaction()
.setTokenType(TokenType.NonFungibleUnique)
.setTokenName("Example Fungible Token")
.setTokenSymbol("EFT")
.setMaxSupply(CID.length)
.setSupplyType(TokenSupplyType.Finite)
.setSupplyKey(operatorKey)
.setAdminKey(operatorKey)
.setTreasuryAccountId(treasuryAccountId)
.freezeWith(client)
.sign(treasuryPrivateKey)
).execute(client);

const nftId = (await nftCreationTx.getReceipt(client)).tokenId;
console.log("NFT ID: ", nftId.toString());

// create a fungible token
const ftCreationTx = await (
await new TokenCreateTransaction()
.setTokenName("Example Fungible Token")
.setTokenSymbol("EFT")
.setInitialSupply(100000000)
.setSupplyKey(operatorKey)
.setAdminKey(operatorKey)
.setTreasuryAccountId(treasuryAccountId)
.freezeWith(client)
.sign(treasuryPrivateKey)
).execute(client);

const ftId = (await ftCreationTx.getReceipt(client)).tokenId;
console.log("FT ID: ", ftId.toString());

// mint 3 NFTs to treasury
const nftSerialIds = [];
for (let i = 0; i < CID.length; i++) {
const { serials } = await (
await new TokenMintTransaction()
.setTokenId(nftId)
.addMetadata(Buffer.from(CID[i]))
.execute(client)
).getReceipt(client);
const [serial] = serials;
nftSerialIds.push(new NftId(nftId, serial));
}

// transfer nfts to receiver
await (
await (
await new TransferTransaction()
.addNftTransfer(
nftSerialIds[0],
treasuryAccountId,
receiverAccountId,
)
.addNftTransfer(
nftSerialIds[1],
treasuryAccountId,
receiverAccountId,
)
.addNftTransfer(
nftSerialIds[2],
treasuryAccountId,
receiverAccountId,
)
.freezeWith(client)
.sign(treasuryPrivateKey)
).execute(client)
).getReceipt(client);

// transfer fungible tokens to receiver
await (
await (
await new TransferTransaction()
.addTokenTransfer(ftId, treasuryAccountId, -1)
.addTokenTransfer(ftId, receiverAccountId, 1)
.freezeWith(client)
.sign(treasuryPrivateKey)
).execute(client)
).getReceipt(client);

console.log("=======================");
console.log("Before Token Reject");
console.log("=======================");
const receiverFTBalanceBefore = (
await new AccountBalanceQuery()
.setAccountId(receiverAccountId)
.execute(client)
).tokens.get(ftId);
const treasuryFTBalanceBefore = (
await new AccountBalanceQuery()
.setAccountId(treasuryAccountId)
.execute(client)
).tokens.get(ftId);
const receiverNFTBalanceBefore = (
await new AccountBalanceQuery()
.setAccountId(receiverAccountId)
.execute(client)
).tokens.get(nftId);
const treasuryNFTBalanceBefore = (
await new AccountBalanceQuery()
.setAccountId(treasuryAccountId)
.execute(client)
).tokens.get(nftId);
console.log("Receiver FT balance: ", receiverFTBalanceBefore.toInt());
console.log("Treasury FT balance: ", treasuryFTBalanceBefore.toInt());
console.log(
"Receiver NFT balance: ",
receiverNFTBalanceBefore ? receiverNFTBalanceBefore.toInt() : 0,
);
console.log("Treasury NFT balance: ", treasuryNFTBalanceBefore.toInt());

// reject fungible tokens back to treasury
const tokenRejectResponse = await (
await (
await new TokenRejectTransaction()
.setOwnerId(receiverAccountId)
.addTokenId(ftId)
.freezeWith(client)
.sign(receiverPrivateKey)
).execute(client)
).getReceipt(client);

// reject NFTs back to treasury
const rejectFlowResponse = await (
await (
await new TokenRejectFlow()
.setOwnerId(receiverAccountId)
.setNftIds(nftSerialIds)
.freezeWith(client)
.sign(receiverPrivateKey)
).execute(client)
).getReceipt(client);

const tokenRejectStatus = tokenRejectResponse.status.toString();
const tokenRejectFlowStatus = rejectFlowResponse.status.toString();

console.log("=======================");
console.log("After Token Reject Transaction and flow");
console.log("=======================");

const receiverFTBalanceAfter = (
await new AccountBalanceQuery()
.setAccountId(receiverAccountId)
.execute(client)
).tokens.get(ftId);

const treasuryFTBalanceAfter = (
await new AccountBalanceQuery()
.setAccountId(treasuryAccountId)
.execute(client)
).tokens.get(ftId);

const receiverNFTBalanceAfter = (
await new AccountBalanceQuery()
.setAccountId(receiverAccountId)
.execute(client)
).tokens.get(nftId);

const treasuryNFTBalanceAfter = (
await new AccountBalanceQuery()
.setAccountId(treasuryAccountId)
.execute(client)
).tokens.get(nftId);

console.log("TokenReject response:", tokenRejectStatus);
console.log("TokenRejectFlow response:", tokenRejectFlowStatus);
console.log("Receiver FT balance: ", receiverFTBalanceAfter.toInt());
console.log("Treasury FT balance: ", treasuryFTBalanceAfter.toInt());
console.log(
"Receiver NFT balance: ",
receiverNFTBalanceAfter ? receiverNFTBalanceAfter.toInt() : 0,
);
console.log("Treasury NFT balance: ", treasuryNFTBalanceAfter.toInt());

client.close();
}

void main();
2 changes: 2 additions & 0 deletions src/exports.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ export { default as Timestamp } from "./Timestamp.js";
export { default as TokenAllowance } from "./account/TokenAllowance.js";
export { default as TokenAssociateTransaction } from "./token/TokenAssociateTransaction.js";
export { default as TokenBurnTransaction } from "./token/TokenBurnTransaction.js";
export { default as TokenRejectTransaction } from "./token/TokenRejectTransaction.js";
export { default as TokenRejectFlow } from "./token/TokenRejectFlow.js";
export { default as TokenCreateTransaction } from "./token/TokenCreateTransaction.js";
export { default as TokenDeleteTransaction } from "./token/TokenDeleteTransaction.js";
export { default as TokenDissociateTransaction } from "./token/TokenDissociateTransaction.js";
Expand Down
40 changes: 40 additions & 0 deletions src/token/TokenReference.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import NftId from "./NftId.js";
import TokenId from "./TokenId.js";

/**
* @namespace proto
* @typedef {import("@hashgraph/proto").proto.TokenReference} HashgraphProto.proto.TokenReference
*/

export default class TokenReference {
constructor() {
/**
* @public
* @type {?TokenId}
*/
this.fungibleToken = null;
/**
* @public
* @type {?NftId}
*/
this.nft = null;
}

/**
* @public
* @param {HashgraphProto.proto.TokenReference} reference
* @returns {TokenReference}
*/
static _fromProtobuf(reference) {
return {
fungibleToken:
reference.fungibleToken != undefined
? TokenId._fromProtobuf(reference.fungibleToken)
: null,
nft:
reference.nft != undefined
? NftId._fromProtobuf(reference.nft)
: null,
};
}
}
Loading