Block Accounts
The Keeta Network uses an Access Control List (ACL) system where token owners can grant or revoke ACCESS permissions for individual accounts.
This tutorial will guide you through creating a token, granting access to multiple accounts, blocking one specific account, and verifying that the block worked correctly.
Prerequisites
Before you begin, ensure you have Installed the KeetaNet SDK.
Set Up Your Accounts
We generate a random seed to create deterministic accounts for our tutorial. The tokenOwner account at index 0 will own and control the token throughout this process. addressA at index 1 will be the account we block later to demonstrate the blocking functionality. addressB at index 2 will keep access throughout the entire process to show selective blocking.
Finally, we create a UserClient for the token owner which allows us to perform blockchain operations.
import * as KeetaNet from "@keetanetwork/keetanet-client";
async function main() {
const seed = KeetaNet.lib.Account.generateRandomSeed({ asString: true });
// Create accounts
const tokenOwner = KeetaNet.lib.Account.fromSeed(seed, 0);
const addressA = KeetaNet.lib.Account.fromSeed(seed, 1);
const addressB = KeetaNet.lib.Account.fromSeed(seed, 2);
const tokenOwnerClient = KeetaNet.UserClient.fromNetwork("test", tokenOwner);
console.log("Token Owner:", tokenOwner.publicKeyString.toString());
console.log("Address A:", addressA.publicKeyString.toString());
console.log("Address B:", addressB.publicKeyString.toString());
}Create Your Token
We use the builder pattern to create a new token account on the Keeta Network. The generateIdentifier() method creates a new TOKEN-type account with a unique identifier. After calling computeBlocks(), we can access the generated token account. The setInfo() method defines the token metadata and crucially sets the default ACCESS permission, which allows public interaction with the token.
We then use modifyTokenSupply() to mint 10,000 tokens to the token account. Finally, publishBuilder() commits all these changes to the blockchain in a single transaction.
console.log("\n=== Creating Token ===");
const builder = tokenOwnerClient.initBuilder();
const pendingToken = builder.generateIdentifier(KeetaNet.lib.Account.AccountKeyAlgorithm.TOKEN);
await builder.computeBlocks();
const tokenAccount = pendingToken.account;
builder.setInfo({
name: '',
description: '',
metadata: '',
defaultPermission: new KeetaNet.lib.Permissions(['ACCESS'])
}, { account: tokenAccount });
builder.modifyTokenSupply(10000n, { account: tokenAccount });
await tokenOwnerClient.publishBuilder(builder);
console.log("✅ Token created with public ACCESS");Grant Access to Specific Addresses
The updatePermissions() method modifies access rights for specific accounts on our token. We use AdjustMethod.SET to grant the ACCESS permission to each address explicitly.
This ensures both addresses have clear, documented access to interact with the token. The { account: tokenAccount } parameter specifies which token we're modifying permissions for.
Each call to updatePermissions() creates a separate transaction that grants access to the specified address.
await tokenOwnerClient.updatePermissions(
addressA,
new KeetaNet.lib.Permissions(['ACCESS']),
tokenAccount,
KeetaNet.lib.Block.AdjustMethod.SET,
{ account: tokenAccount }
);
await tokenOwnerClient.updatePermissions(
addressB,
new KeetaNet.lib.Permissions(['ACCESS']),
tokenAccount,
KeetaNet.lib.Block.AdjustMethod.SET,
{ account: tokenAccount }
);Verify Initial Access
The listACLsByEntity() method retrieves all Access Control List entries for our token, showing us every account that has been granted specific permissions. We search through this list to find ACL entries matching each of our test addresses using the comparePublicKey() method for secure address comparison.
The permissions.has(['ACCESS']) method checks whether the ACCESS permission is present in each account's permission set. At this point in the tutorial, both addresses should show true, indicating they have access to the token.
console.log("\n=== Checking Initial Access ===");
const initialACLs = await tokenOwnerClient.listACLsByEntity({ account: tokenAccount });
const addressA_ACL = initialACLs.find(acl => acl.principal.comparePublicKey(addressA));
const addressB_ACL = initialACLs.find(acl => acl.principal.comparePublicKey(addressB));
const addressA_HasAccess = addressA_ACL && addressA_ACL.permissions.has(['ACCESS']);
const addressB_HasAccess = addressB_ACL && addressB_ACL.permissions.has(['ACCESS']);
console.log("Address A has access:", addressA_HasAccess);
console.log("Address B has access:", addressB_HasAccess);Block a Specific Address
We use the same updatePermissions() method as before, but this time with AdjustMethod.SUBTRACT instead of SET.
The SUBTRACT method removes the specified permissions from the target account rather than granting them. In this case, we're removing the ACCESS permission from Address A, effectively blocking that account from interacting with our token. Only Address A is affected by this operation, while Address B retains all its existing permissions.
The blocking takes effect immediately after the transaction is published to the network.
console.log("\n=== Blocking Address A ===");
await tokenOwnerClient.updatePermissions(
addressA,
new KeetaNet.lib.Permissions(['ACCESS']),
tokenAccount,
KeetaNet.lib.Block.AdjustMethod.SUBTRACT,
{ account: tokenAccount }
);
console.log("✅ Address A blocked");Verify the Block Worked
We perform the same ACL lookup and permission check as we did in Step 4, but now we can see the results of our blocking operation.
Address A should now show false when we check for ACCESS permission, indicating it has been successfully blocked.
Address B should still show true, demonstrating that our blocking was selective and didn't affect other accounts.
The summary section provides a clear confirmation that our selective blocking worked correctly by comparing the before and after states of both addresses.
console.log("\n=== Verifying Block ===");
const finalACLs = await tokenOwnerClient.listACLsByEntity({ account: tokenAccount });
const final_addressA_ACL = finalACLs.find(acl => acl.principal.comparePublicKey(addressA));
const final_addressB_ACL = finalACLs.find(acl => acl.principal.comparePublicKey(addressB));
const final_addressA_HasAccess = final_addressA_ACL && final_addressA_ACL.permissions.has(['ACCESS']);
const final_addressB_HasAccess = final_addressB_ACL && final_addressB_ACL.permissions.has(['ACCESS']);
console.log("Address A has access after blocking:", final_addressA_HasAccess);
console.log("Address B has access after blocking:", final_addressB_HasAccess);
console.log("\n=== Summary ===");
console.log("Address A blocked successfully:", addressA_HasAccess && !final_addressA_HasAccess);
console.log("Address B still has access:", addressB_HasAccess && final_addressB_HasAccess);Full Code Example
import * as KeetaNet from "@keetanetwork/keetanet-client";
async function main() {
const seed = KeetaNet.lib.Account.generateRandomSeed({ asString: true });
// Create accounts
const tokenOwner = KeetaNet.lib.Account.fromSeed(seed, 0);
const addressA = KeetaNet.lib.Account.fromSeed(seed, 1);
const addressB = KeetaNet.lib.Account.fromSeed(seed, 2);
const tokenOwnerClient = KeetaNet.UserClient.fromNetwork("test", tokenOwner);
console.log("Token Owner:", tokenOwner.publicKeyString.toString());
console.log("Address A:", addressA.publicKeyString.toString());
console.log("Address B:", addressB.publicKeyString.toString());
// 1. Create token with public access
console.log("\n=== Creating Token ===");
const builder = tokenOwnerClient.initBuilder();
const pendingToken = builder.generateIdentifier(KeetaNet.lib.Account.AccountKeyAlgorithm.TOKEN);
await builder.computeBlocks();
const tokenAccount = pendingToken.account;
builder.setInfo({
name: '',
description: '',
metadata: '',
defaultPermission: new KeetaNet.lib.Permissions(['ACCESS'])
}, { account: tokenAccount });
builder.modifyTokenSupply(10000n, { account: tokenAccount });
await tokenOwnerClient.publishBuilder(builder);
console.log("✅ Token created with public ACCESS");
// 2. Grant explicit access to both addresses
await tokenOwnerClient.updatePermissions(
addressA,
new KeetaNet.lib.Permissions(['ACCESS']),
tokenAccount,
KeetaNet.lib.Block.AdjustMethod.SET,
{ account: tokenAccount }
);
await tokenOwnerClient.updatePermissions(
addressB,
new KeetaNet.lib.Permissions(['ACCESS']),
tokenAccount,
KeetaNet.lib.Block.AdjustMethod.SET,
{ account: tokenAccount }
);
// 3. Verify both addresses have access
console.log("\n=== Checking Initial Access ===");
const initialACLs = await tokenOwnerClient.listACLsByEntity({ account: tokenAccount });
const addressA_ACL = initialACLs.find(acl => acl.principal.comparePublicKey(addressA));
const addressB_ACL = initialACLs.find(acl => acl.principal.comparePublicKey(addressB));
const addressA_HasAccess = addressA_ACL && addressA_ACL.permissions.has(['ACCESS']);
const addressB_HasAccess = addressB_ACL && addressB_ACL.permissions.has(['ACCESS']);
console.log("Address A has access:", addressA_HasAccess);
console.log("Address B has access:", addressB_HasAccess);
// 4. Block Address A
console.log("\n=== Blocking Address A ===");
await tokenOwnerClient.updatePermissions(
addressA,
new KeetaNet.lib.Permissions(['ACCESS']),
tokenAccount,
KeetaNet.lib.Block.AdjustMethod.SUBTRACT,
{ account: tokenAccount }
);
console.log("✅ Address A blocked");
// 5. Verify blocking worked - A is blocked, B still has access
console.log("\n=== Verifying Block ===");
const finalACLs = await tokenOwnerClient.listACLsByEntity({ account: tokenAccount });
const final_addressA_ACL = finalACLs.find(acl => acl.principal.comparePublicKey(addressA));
const final_addressB_ACL = finalACLs.find(acl => acl.principal.comparePublicKey(addressB));
const final_addressA_HasAccess = final_addressA_ACL && final_addressA_ACL.permissions.has(['ACCESS']);
const final_addressB_HasAccess = final_addressB_ACL && final_addressB_ACL.permissions.has(['ACCESS']);
console.log("Address A has access after blocking:", final_addressA_HasAccess);
console.log("Address B has access after blocking:", final_addressB_HasAccess);
console.log("\n=== Summary ===");
console.log("Address A blocked successfully:", addressA_HasAccess && !final_addressA_HasAccess);
console.log("Address B still has access:", addressB_HasAccess && final_addressB_HasAccess);
}
main().catch(console.error);
Last updated