Eternal-Tokens

Soulbonds on Solana

Eternal Tokens Suggest Enhancements Introduction Eternal tokens represent a distinct category of blockchain assets adhering to the SPL 1 standard. Notably, these tokens exhibit an inherent characteristic: they are impervious to transfers between different wallet addresses. Once an Eternal token is crafted for a specific wallet address, any attempt to transfer or exchange it to another address is categorically restricted. This unique feature finds utility in scenarios where the creation of exclusive digital items or rewards for designated users is paramount.

Benefits of Employing Eternal Tokens Several advantages accompany the utilization of Eternal tokens:

  1. Unparalleled Rewards: The creation of Eternal tokens ensures that each reward or item remains exclusively tied to a particular user. This exclusivity enhances the perceived value of the item, rendering it more meaningful to the user.

  2. Enhanced Security: By curbing the transferability of tokens to alternative wallet addresses, the risk of token theft or misuse is effectively mitigated.

  3. Unmatched Control: Eternal tokens furnish an elevated level of control over token distribution and utilization. This control ensures that tokens are strictly employed for their intended purpose and by their designated recipients.

Creating an Eternal Token via the Co:Create API To initiate the creation of an Eternal token using the Co:Create API, familiar methods for crafting SPL tokens are employed. However, distinct configurations must be applied to enforce the Eternal nature of the token.

Follow these steps to fabricate an Eternal token:

  1. Initiate a New SPL Token

    • Dispatch a POST request to the SPL API. Essential parameters include:

      • name: The designated name of your token.

      • symbol: The symbol representing your token, conventionally in uppercase.

      • base_uri: The off-chain file corresponding to your token (further details available here).

      • transfer_restricted: Set to true to inhibit token transfers, the defining characteristic of an Eternal token.

      • transfer_allowlist : Facilitates the addition of a comma-separated list of addresses authorized to engage in transfers of the token.

      Bulk distribute NFT items to holders

    import { Commitment, Keypair, LAMPORTS_PER_SOL, PublicKey, Signer, } from "@solana/web3.js"; import { ConnectionManager, TransactionWrapper, Logger, TransactionBuilder, } from "@solworks/soltoolkit-sdk"; import { getAssociatedTokenAddress, createTransferCheckedInstruction, createAssociatedTokenAccountInstruction } from "@solana/spl-token"; import { Metaplex } from "@metaplex-foundation/js"; import * as fs from "fs";

    // This script will: // 1. Iterate through a list of mint addresses // 2. Create an associated token account for each mint address // 3. Transfer 1 NFT to each associated token account // 4. Confirm the transaction // 5. Log the transaction hash and result along with any errors const rpcEndpoint = 'https://api.mainnet-beta.solana.com'; const commitment: Commitment = "max"; const skipSending = false; const sender = Keypair.fromSecretKey(Uint8Array.from([...])); const minters = [{ "address": "...", "items": 3 }, { "address": "...", "items": 3 }, { "address": "...", "items": 12 }];

    (async () => { const logger = new Logger("nft-transfer"); const cm = await ConnectionManager.getInstance({ commitment, endpoint: rpcEndpoint, mode: "single", network: "mainnet-beta", }); const mp = new Metaplex(cm._connection);

    // get SOL balance of sender logger.debug("Fetching SOL balance of", sender.publicKey.toBase58()); let senderSOLBal = await cm .connSync({ changeConn: false }) .getBalance(sender.publicKey, commitment); logger.debug(Sender balance: ${senderSOLBal / LAMPORTS_PER_SOL} SOL);

    let results: IResults = { success: [], failure: [], }; // iterate through mints for (let i = 0; i < minters.length; i++) { // get NFTs owned by sender const nftsOwnedBySender = await mp .nfts() .findAllByOwner({ owner: sender.publicKey }); logger.debug("NFTs owned by sender:", nftsOwnedBySender.length); const receivingOwner = new PublicKey(minters[i].address); const nftsToSend = minters[i].items;

    // find minted nfts to send
    for (let k = 0; k < nftsToSend; k++) {
      if (nftsOwnedBySender.length === 0) {
        logger.debug("No more NFTs to send");
        break;
      }
    
      const nftToSend = nftsOwnedBySender[k];
      logger.debug("NFT to send:", nftToSend);
      const sendingMint = (nftToSend as any).mintAddress;
      logger.debug("Sending mint:", sendingMint.toBase58());
    
      try {    
        let sendingAta = await getAssociatedTokenAddress(sendingMint, sender.publicKey);
        logger.debug("Sending ATA:", sendingAta.toBase58());
    
        let receivingAta = await getAssociatedTokenAddress(sendingMint, receivingOwner);
        logger.debug("Receiving ATA:", receivingAta.toBase58());
    
        // generate tx to transfer NFT to ATA
        // create associated token account
        const tx = TransactionBuilder
          .create()
          .addIx([
              createAssociatedTokenAccountInstruction(
                sender.publicKey,
                receivingAta,
                receivingOwner,
                sendingMint
              ),
              createTransferCheckedInstruction(
                sendingAta,
                sendingMint,
                receivingAta,
                sender.publicKey,
                1,
                0
              )
            ])
          .build();
    
    
        if (!skipSending) {
          // feed transaction into TransactionWrapper
          const wrapper = await TransactionWrapper
            .create({
              connectionManager: cm,
              transaction: tx,
              signer: sender.publicKey,
            })
            .addBlockhashAndFeePayer(sender.publicKey);
    
          // sign the transaction
          logger.debug(`Signing transaction ${i + 1}`);
          const signedTx = await wrapper.sign({ signer: sender as Signer });
    
          // send and confirm the transaction
          logger.debug(`Sending transaction ${i + 1}`);
          const transferSig = await wrapper.sendAndConfirm({ 
            serialisedTx: signedTx.serialize(), 
            commitment 
          });
          logger.debug("Transaction sent:", transferSig.toString());
    
          results.success.push({
            sentTicketMint: sendingMint.toBase58(),
            ticketHeldMint: receivingOwner,
          });
    
          await sleep(3_000);
        }
      } catch (e: any) {
        logger.error(e);
        results.failure.push({
          sentTicketMint: sendingMint.toBase58(),
          ticketHeldMint: receivingOwner.toBase58()
        });
      }
    }

    }

    fs.writeFileSync("results.json", JSON.stringify(results)); })();

    interface IResults { success: Array; failure: Array; } function sleep(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)); }

Last updated