Create a Storage Account
Step by Step
1
Initial Setup and Account Generation
async function main() {
const seed = KeetaNet.lib.Account.generateRandomSeed({ asString: true });
console.log("seed =", seed);
// Create liquidity provider account (token creator)
const liquidityProviderAccount = KeetaNet.lib.Account.fromSeed(seed, 0);
const liquidityProviderClient = KeetaNet.UserClient.fromNetwork("test", liquidityProviderAccount);
// Create two user accounts for shared storage
const accountA = KeetaNet.lib.Account.fromSeed(seed, 1);
const accountB = KeetaNet.lib.Account.fromSeed(seed, 2);
const userClientA = KeetaNet.UserClient.fromNetwork("test", accountA);
const userClientB = KeetaNet.UserClient.fromNetwork("test", accountB);
console.log("accountA.publicKey =", accountA.publicKeyString.toString());
console.log("accountB.publicKey =", accountB.publicKeyString.toString());
}
2
Token Creation and Minting
async function createToken(userClient: KeetaNet.UserClient) {
const builder = userClient.initBuilder();
// Create a new token account
const pendingTokenAccount = builder.generateIdentifier(KeetaNet.lib.Account.AccountKeyAlgorithm.TOKEN)
await builder.computeBlocks();
const tokenAccount = pendingTokenAccount.account;
console.log("tokenAccount.publicKey =", tokenAccount.publicKeyString.toString());
// Set token permissions and metadata
builder.setInfo({
name: '',
description: '',
metadata: '',
defaultPermission: new KeetaNet.lib.Permissions(['ACCESS']), // Public token
}, { account: tokenAccount });
// Mint 10,000 tokens
builder.modifyTokenSupply(10_000n, { account: tokenAccount });
builder.modifyTokenBalance(tokenAccount, 10_000n);
// Publish to blockchain
await userClient.publishBuilder(builder);
console.log("Token account created and minted.\n");
return tokenAccount;
}
// Call the function in main()
console.log("\nCreating liquidity provider account and token account...");
const tokenAccount = await createToken(liquidityProviderClient);
console.log("liquidityProviderClient.balances[] =", await liquidityProviderClient.allBalances());
3
Storage Account Creation with Permissions
// Check initial storage accounts (should be empty)
console.log("\nChecking storage accounts before creation:");
console.log("accountA.storageAccounts[] =", (await userClientA.listACLsByPrincipal()).filter(acl => acl.entity.isStorage()));
console.log("accountB.storageAccounts[] =", (await userClientB.listACLsByPrincipal()).filter(acl => acl.entity.isStorage()));
// Create storage account
const builder = userClientA.initBuilder();
const pendingStorageAccount = builder.generateIdentifier(KeetaNet.lib.Account.AccountKeyAlgorithm.STORAGE);
await builder.computeBlocks();
const storageAccount = pendingStorageAccount.account;
console.log("storageAccount.publicKey =", storageAccount.publicKeyString.toString());
// Set default permissions
builder.setInfo({
name: '',
description: '',
metadata: '',
defaultPermission: new KeetaNet.lib.Permissions([
'STORAGE_CAN_HOLD', // Allow holding any token
'STORAGE_DEPOSIT', // Allow anyone to deposit
])
}, { account: storageAccount });
// Grant SEND_ON_BEHALF permission to accountB
builder.updatePermissions(
accountB,
new KeetaNet.lib.Permissions(['SEND_ON_BEHALF']),
undefined,
undefined,
{ account: storageAccount }
);
// Publish storage account
await userClientA.publishBuilder(builder);
console.log("Storage account created and permissions updated.");
4
Token Distribution
// Check balances before distribution
console.log("\nChecking balances before deposit:");
console.log("accountA.balances[] =", await userClientA.allBalances());
console.log("accountB.balances[] =", await userClientB.allBalances());
console.log("storageAccount.balances[] =", await userClientA.allBalances({ account: storageAccount }));
// Distribute tokens from liquidity provider
console.log("\nDepositing tokens from the liquidity provider...");
const builderSend = await liquidityProviderClient.initBuilder();
builderSend.send(storageAccount, 1_000n, tokenAccount); // 1,000 to storage
builderSend.send(accountA, 5_000n, tokenAccount); // 5,000 to accountA
await liquidityProviderClient.publishBuilder(builderSend);
// Check balances after distribution
console.log("\nChecking balances after deposit:");
console.log("accountA.balances[] =", await userClientA.allBalances());
console.log("accountB.balances[] =", await userClientB.allBalances());
console.log("storageAccount.balances[] =", await userClientA.allBalances({ account: storageAccount }));
5
Delegated Operations Using SEND_ON_BEHALF
// accountB sends tokens from storageAccount using SEND_ON_BEHALF permission
await userClientB.send(accountA, 500n, tokenAccount, undefined, { account: storageAccount });
await userClientB.send(accountB, 300n, tokenAccount, undefined, { account: storageAccount });
// Check final balances
console.log("\nChecking balances after accountB sends tokens from storageAccount:");
console.log("accountA.balances[] =", await userClientA.allBalances());
console.log("accountB.balances[] =", await userClientB.allBalances());
console.log("storageAccount.balances[] =", await userClientA.allBalances({ account: storageAccount }));
Full Code Example
import * as KeetaNet from "@keetanetwork/keetanet-client";
// Token creation function
async function createToken(userClient: KeetaNet.UserClient) {
const builder = userClient.initBuilder();
// Create a new token account
const pendingTokenAccount = builder.generateIdentifier(KeetaNet.lib.Account.AccountKeyAlgorithm.TOKEN)
await builder.computeBlocks();
const tokenAccount = pendingTokenAccount.account;
console.log("tokenAccount.publicKey =", tokenAccount.publicKeyString.toString());
// Setting the token account default permissions
builder.setInfo(
{
name: '',
description: '',
metadata: '',
defaultPermission: new KeetaNet.lib.Permissions([
'ACCESS', // Public token
]),
},
{ account: tokenAccount },
)
// Minting the token
builder.modifyTokenSupply(10_000n, { account: tokenAccount });
builder.modifyTokenBalance(tokenAccount, 10_000n)
// Publish the blocks
await userClient.publishBuilder(builder);
console.log("Token account created and minted.\n");
return tokenAccount;
}
async function main() {
const seed = KeetaNet.lib.Account.generateRandomSeed({ asString: true });
console.log("seed =", seed);
/**
* Creating liquidity provider account and token account
*/
console.log("\nCreating liquidity provider account and token account...");
const liquidityProviderAccount = KeetaNet.lib.Account.fromSeed(seed, 0);
const liquidityProviderClient = KeetaNet.UserClient.fromNetwork("test", liquidityProviderAccount);
const tokenAccount = await createToken(liquidityProviderClient);
console.log("liquidityProviderClient.balances[] =", await liquidityProviderClient.allBalances());
/**
* Creating two user accounts (accountA and accountB)
* to demonstrate shared storage account creation.
*/
const accountA = KeetaNet.lib.Account.fromSeed(seed, 1);
const accountB = KeetaNet.lib.Account.fromSeed(seed, 2);
const userClientA = KeetaNet.UserClient.fromNetwork("test", accountA);
const userClientB = KeetaNet.UserClient.fromNetwork("test", accountB);
console.log("\nGetting accounts:");
console.log("accountA.publicKey =", accountA.publicKeyString.toString());
console.log("accountB.publicKey =", accountB.publicKeyString.toString());
/**
* Checking owned storage accounts
*/
console.log("\nChecking storage accounts before creation:");
console.log("accountA.storageAccounts[] =", (await userClientA.listACLsByPrincipal()).filter(acl => acl.entity.isStorage()));
console.log("accountB.storageAccounts[] =", (await userClientB.listACLsByPrincipal()).filter(acl => acl.entity.isStorage()));
/**
* Creating a storage account
*/
// Initialize the user client builder
const builder = userClientA.initBuilder();
// Create a new storage account
const pendingStorageAccount = builder.generateIdentifier(KeetaNet.lib.Account.AccountKeyAlgorithm.STORAGE);
// Compute the pending storage account
await builder.computeBlocks();
// Get the storage account
const storageAccount = pendingStorageAccount.account;
console.log("storageAccount.publicKey =", storageAccount.publicKeyString.toString());
// Setting the storage account default permissions
builder.setInfo({
name: '',
description: '',
metadata: '',
defaultPermission: new KeetaNet.lib.Permissions([
'STORAGE_CAN_HOLD', // Allow the storage account to hold any token
'STORAGE_DEPOSIT', // Allow everyone to deposit into the storage account
])
}, { account: storageAccount });
// Until here, only `accountA` has access to the storageAccount, his permission is "OWNER".
/**
* Adding permission for `accountB` on the `storageAccount`
*
* Here we can set "ADMIN" or "SEND_ON_BEHALF" permissions for `accountB`.
* "ADMIN" would allow `accountB` to manage the storage account, while
* "SEND_ON_BEHALF" would allow `accountB` to send tokens from the storage account
*/
builder.updatePermissions(
accountB,
new KeetaNet.lib.Permissions(['SEND_ON_BEHALF']),
undefined,
undefined,
{ account: storageAccount }
);
// Publish the blocks
await userClientA.publishBuilder(builder);
console.log("Storage account created and permissions updated.");
/**
* Checking owned storage accounts
*/
console.log("\nChecking storage accounts after creation:");
console.log("accountA.storageAccounts[] =", (await userClientA.listACLsByPrincipal()).filter(acl => acl.entity.isStorage()).map(acl => acl.entity.publicKeyString.toString()));
console.log("accountB.storageAccounts[] =", (await userClientB.listACLsByPrincipal()).filter(acl => acl.entity.isStorage()).map(acl => acl.entity.publicKeyString.toString()));
/**
* Checking balances before deposit
*/
console.log("\nChecking balances before deposit:");
console.log("accountA.balances[] =", await userClientA.allBalances());
console.log("accountB.balances[] =", await userClientB.allBalances());
console.log("accountA.storageAccount.balances[] =", await userClientA.allBalances({ account: storageAccount }));
console.log("accountB.storageAccount.balances[] =", await userClientB.allBalances({ account: storageAccount }));
/**
* Depositing tokens from the liquidity provider
* LP -> SEND 1_000 -> storageAccount
* LP -> SEND 5_000 -> accountA
*/
console.log("\nDepositing tokens from the liquidity provider...");
const builderSend = await liquidityProviderClient.initBuilder();
builderSend.send(storageAccount, 1_000n, tokenAccount);
builderSend.send(accountA, 5_000n, tokenAccount);
await liquidityProviderClient.publishBuilder(builderSend);
/**
* Checking balances after deposit
*/
console.log("\nChecking balances after deposit:");
console.log("accountA.balances[] =", await userClientA.allBalances());
console.log("accountB.balances[] =", await userClientB.allBalances());
console.log("accountA.storageAccount.balances[] =", await userClientA.allBalances({ account: storageAccount }));
console.log("accountB.storageAccount.balances[] =", await userClientB.allBalances({ account: storageAccount }));
/**
* Depositing tokens from the storage account
* accountB using storageAccount -> SEND 500 -> accountA
* accountB using storageAccount -> SEND 300 -> accountB
*/
await userClientB.send(accountA, 500n, tokenAccount, undefined, { account: storageAccount });
await userClientB.send(accountB, 300n, tokenAccount, undefined, { account: storageAccount });
console.log("\nChecking balances after accountB sends 500 tokens from storageAccount to accountA:");
console.log("accountA.balances[] =", await userClientA.allBalances());
console.log("accountB.balances[] =", await userClientB.allBalances());
console.log("accountA.storageAccount.balances[] =", await userClientA.allBalances({ account: storageAccount }));
console.log("accountB.storageAccount.balances[] =", await userClientB.allBalances({ account: storageAccount }));
}
main().then(() => {
console.log("Done");
process.exit(0);
}).catch((err) => {
console.error("Error:", err);
process.exit(1);
});
Last updated