DEV Community

Max Daunarovich for Flow Blockchain

Posted on • Edited on

Build on Flow | Learn FCL - 15. How to Add and Revoke Public Keys

Preface

One of the problems you might encounter, when you need to sign with a lot of transactions is inability to sign more than single transaction by account in single block. This happens because Proposer role is used to provide a nounce, which will be updated on account key only after transaction is sealed.

The solution - we need to add more keys 🔑✨!

Overview

After you go through the code in this article you will know:

  • how to discover public keys on account using Flow View Source
  • how to add new PublicKey with set weight

Wallet Limitations

While Blocto or Lilico will not allow you to use key with different index - other than 0 - in some cases it’s still valuable to add public key you can use programmatically to automate some tasks. The key with full weight will give you access to the account storage and ability to manipulate it.

Investigation

Before we continue, let’s check our current account on Flow View Source - 0x5593df7d286bcdb8

😅 Considering how many times Codesandbox has been run, you probably will see multiple keys here, when you read it.

Image description

You should see at least a single key with weight equal to 1000 (full weight).

Step 1 - Installation

💡You can go and fork your work from previous article as it will be mostly the same :)

Add "@onflow/fcl", elliptic and sha3 packages as your dependencies.

Step 2 - Create a signer

You can copy/paste the content of signer.js file from previous article or follow the instructions and make a new one.

Step 3 - Configure FCL

We are not gonna use wallets in this example - consider this as homework assignment 😉 - so our config only needs access node url:

config({ "accessNode.api": "https://rest-testnet.onflow.org" });
Enter fullscreen mode Exit fullscreen mode

Step 4 - Implement addPublicKey

Let’s make a new function called addPublicKey, which will:

  • accept PublicKey as a string,
  • prepare transaction to pass public key and weight as arguments
  • sign and send aforementioned transaction to network

The process is similar to how we were cooking transactions in previous article:

const addPublicKey = async (publicKey) => {
  const cadence = `
  transaction(publicKey: String, weight: UFix64) {
    prepare(signer: AuthAccount) {
      let bytes = publicKey.decodeHex()

      let key = PublicKey(
        publicKey: bytes,
        signatureAlgorithm: SignatureAlgorithm.ECDSA_P256
      )

      var clampledWeight = weight
      // weight should be in range 0 to 1000
      if(clampledWeight > 1000.0){
        clampledWeight = 1000.0
      }

      signer.keys.add(
        publicKey: key,
        hashAlgorithm: HashAlgorithm.SHA3_256,
        weight: clampledWeight
      )
    }
  }
  `;

  // List of arguments
    const weight = (0).toFixed(1) // zero weight keys are perfectly fine for Proposer role
  const args = (arg, t) => [
    arg(publicKey, t.String),
    arg(weight, t.UFix64)
  ];

  // Roles
  const proposer = signer;
  const payer = signer;
  const authorizations = [signer];

    // Execution limit
    const limit = 1000

  // "mutate" method will return us transaction id
  const txId = await mutate({
    proposer, payer, authorizations,
    cadence, args, limit
  });

    // Then we subscribe to status updates and return, when it's sealed
  return tx(txId).onceSealed();
};
Enter fullscreen mode Exit fullscreen mode

Step 5 - Implement revokePublicKey

Another function we would like to prepare is revokePublicKey. It will prepare and submit transaction, which will revoke the key on account:

const revokePublicKey = async (keyIndex) => {
  const cadence = `
    transaction(keyIndex: Int){
      prepare(signer: AuthAccount){
        signer.keys.revoke(keyIndex:keyIndex)
      }
    }
  `;
  const args = (arg, t) => [arg(keyIndex.toString(), t.Int)];

  const proposer = signer;
  const payer = signer;
  const authorizations = [signer];

    const limit = 1000;

  // "mutate" method will return us transaction id
  const txId = await mutate({
    cadence, args, limit,
    proposer, payer, authorizations
  });

    // Then we subscribe to status updates and return, when it's sealed
  return tx(txId).onceSealed();
};
Enter fullscreen mode Exit fullscreen mode

Finally -

Let's add an IIFE at the end of the file and populate it with methods we have just defined:

(async () => {
  console.clear();

  // PublicKey doesn't need to be unique, so we are gonna use exactly the same one
  // that already exist on account. This way we can control it with the same private key.
  const key =
    "790a6849decbc179e9904f7f601fbd629f1687f371484998ceb8c587303e05ae4f859c7aa91f8493642de1039039d2da9650b4b7d9d44d2486e7a2adabf602bc";

  // Uncomment this block to add public key
  /*
  const txDetails = await addPublicKey(key);
  console.log({ txDetails });
  */

  // Uncomment this block to revoke key
  /*
  const txDetails = await revokePublicKey(2);
  console.log({ txDetails });
  */
})();
Enter fullscreen mode Exit fullscreen mode

When the dust settles, your console should have the output, showcasing that transaction succesfully updated your account:

txDetails: {
    blockId: "b9ddfac94c968864ca369ec890e8025c0144375f334bd0e2ddea1965b175fc82",
    status: 4,
    statusString: "SEALED",
    statusCode: 0,
    errorMessage: "",
    events: Array(4),
    0: Object {
        type: "flow.AccountKeyAdded"
        transactionId: "fa2640f428a3317b656196ef7f069a744b2455569e492f8112d37abca31e273d"
        transactionIndex: 0
        eventIndex: 0
        data: Object
    },
    1: Object,
    2: Object,
    3: Object,
}
Enter fullscreen mode Exit fullscreen mode

Checking Flow View Source will show there are now multiple keys that can be used:

If you uncomment the revokePublicKey usage block and wait for execution, you will be able to see that key is marked as revoked.

Image description

💡 Please note, that keys are not removed, but revoked - you won’t be able to use this key index on this account anymore 🤷‍♂️

Hope you’ve found this article useful. And if you have gone through it with us - you’ve grown stronger! 💪

https://c.tenor.com/TgDOSZ0PpNsAAAAd/zoolander-boss.gif

Until next time 👋

Resources

Other resources you might find useful:

Top comments (0)