Skip to content

Commit

Permalink
feat: Implement TokenRejectTransaction (#2411)
Browse files Browse the repository at this point in the history
* feat: TokenRejectTransaction js file

Signed-off-by: Ivaylo Nikolov <[email protected]>

* test: unit tests for TokenRejectTransaction

Signed-off-by: Ivaylo Nikolov <[email protected]>

* test: add integration tests for TokenRejectTransaction

Signed-off-by: Ivaylo Nikolov <[email protected]>

* feat: TokenRejectFlow

Signed-off-by: Ivaylo Nikolov <[email protected]>

* feat: TokenRejectFlow and TokenReject example

Signed-off-by: Ivaylo Nikolov <[email protected]>

* fix: circular dependency

Signed-off-by: Ivaylo Nikolov <[email protected]>

* chore: lint change

Signed-off-by: Ivaylo Nikolov <[email protected]>

* chore: remove client

Signed-off-by: Ivaylo Nikolov <[email protected]>

* refactor: use to string for status codes

Signed-off-by: Ivaylo Nikolov <[email protected]>

* refactor: use ? instead of | null

Signed-off-by: Ivaylo Nikolov <[email protected]>

* refactor: avoid usage of token identifier

Signed-off-by: Ivaylo Nikolov <[email protected]>

* refator: add PublicKey type for jsdocs in type definitions

Signed-off-by: Ivaylo Nikolov <[email protected]>

* fix: should not support strings conversion in token reject tx

Signed-off-by: Ivaylo Nikolov <[email protected]>

* fix(test): unit test should not expect token identifier property

Signed-off-by: Ivaylo Nikolov <[email protected]>

* fix: use proper PublicKey path

Signed-off-by: Ivaylo Nikolov <[email protected]>

* feat: better console logging when executing examples

Signed-off-by: Ivaylo Nikolov <[email protected]>
Signed-off-by: Alexander Gadzhalov <[email protected]>

* refactor: avoid using magical number in example script

Signed-off-by: Ivaylo Nikolov <[email protected]>

* chore: remove unused imports

Signed-off-by: Ivaylo Nikolov <[email protected]>

* chore: improve comments naming

Signed-off-by: Ivaylo Nikolov <[email protected]>

* chore: improve token naming

Signed-off-by: Ivaylo Nikolov <[email protected]>

* chore: max automatic token assoc should be 100 for consistency between all sdk

Signed-off-by: Ivaylo Nikolov <[email protected]>

* fix: reconnect to working node (#2417)

* chore: address initial pain points

Signed-off-by: Ivan Ivanov <[email protected]>

* chore: bump grpc-js version

Signed-off-by: Ivan Ivanov <[email protected]>

* fix

Signed-off-by: svetoslav-nikol0v <[email protected]>

* remove example

Signed-off-by: svetoslav-nikol0v <[email protected]>

* chore: formatting

Signed-off-by: svetoslav-nikol0v <[email protected]>

* fix

Signed-off-by: svetoslav-nikol0v <[email protected]>

* revert: grpc old version

Signed-off-by: svetoslav-nikol0v <[email protected]>

* revert: pnpm-lock file

Signed-off-by: svetoslav-nikol0v <[email protected]>

* revert: package-lock file

Signed-off-by: svetoslav-nikol0v <[email protected]>

---------

Signed-off-by: Ivan Ivanov <[email protected]>
Signed-off-by: svetoslav-nikol0v <[email protected]>
Co-authored-by: Ivan Ivanov <[email protected]>

* test(fix): update token reject outdated test

Signed-off-by: Ivaylo Nikolov <[email protected]>

* chore: comments typo and improvement

Signed-off-by: Ivaylo Nikolov <[email protected]>

* test: temporary skip allowance tests for NFT and FT

Signed-off-by: Ivaylo Nikolov <[email protected]>

* test: ignore eslint warnings for skipped tests

Signed-off-by: Ivaylo Nikolov <[email protected]>

* chore: add id-token permission to pages.yml workflow (#2418)

Signed-off-by: Iliya Savov <[email protected]>
Co-authored-by: Iliya Savov <[email protected]>
Co-authored-by: Svet <[email protected]>

---------

Signed-off-by: Ivaylo Nikolov <[email protected]>
Signed-off-by: Alexander Gadzhalov <[email protected]>
Signed-off-by: Ivan Ivanov <[email protected]>
Signed-off-by: svetoslav-nikol0v <[email protected]>
Signed-off-by: Iliya Savov <[email protected]>
Co-authored-by: Svet <[email protected]>
Co-authored-by: Ivan Ivanov <[email protected]>
Co-authored-by: Iliya Savov <[email protected]>
Co-authored-by: Iliya Savov <[email protected]>
Co-authored-by: Alexander Gadzhalov <[email protected]>
  • Loading branch information
6 people authored Aug 1, 2024
1 parent 81fb7d0 commit 8a22ce9
Show file tree
Hide file tree
Showing 10 changed files with 2,252 additions and 1 deletion.
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 (
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

0 comments on commit 8a22ce9

Please sign in to comment.