Create action and transaction with GraphQL API

Currently, Nine Chronicles Headless provides GraphQL API to represents states and network, node’s state.

At first, open GraphQL playground of your headless.

It provides query.actionQuery query, an factory query to provide actions’ constructors. There are query.actionQuery.stake, query.actionQuery.claimStakeReward queries.

You can use them like the below GraphQL queries:

query {
  actionQuery {
    stake(amount: 10)   
    claimStakeReward(avatarAddress: "YOUR_AVATAR_ADDRES")
  }
}

And it will response with the result consists of actions’ hexadecimal string:

{
  "data": {
    "actionQuery": {
      "stake": "6475373a747970655f696475363a7374616b653275363a76616c7565736475323a616d6931306575323a696431363acf82975c8fa23240b8434e89aa3f3fa66565",
      "claimStakeReward": "6475373a747970655f69647531383a636c61696d5f7374616b655f72657761726475363a76616c7565736475323a616132303a558ea927b9449c69577ed1e28a7c14ae0ce550df75323a696431363a4cbaf0ed6a96ad40ba3846ffd93727816565"
    }
  },
  "extensions": {}
}

Then you should make unsigned transaction with the action and you can do it with query.transaction.unsignedTransaction query, the unsigned transaction constructor:

query {
  transaction {
    // unsignedTransaction(publicKey: "YOUR_PUBLIC_KEY", plainValue: "ACTION", nonce: 0)
    // For stake action:
    unsignedTransaction(publicKey: "YOUR_PUBLIC_KEY", plainValue: "6475373a747970655f696475363a7374616b653275363a76616c7565736475323a616d6931306575323a696431363acf82975c8fa23240b8434e89aa3f3fa66565", nonce: 0)
  }
}

And it will return like the result:

{
  "data": {
    "transaction": {
      "unsignedTransaction": "64313a616c6475373a747970655f696475363a7374616b653275363a76616c7565736475323a616d6931306575323a696431363acf82975c8fa23240b8434e89aa3f3fa6656565313a6733323a4582250d0da33b06779a8475d283d5dd210c683b9b999d74d03fac4f58fa6bce313a6e693065313a7036353a04663d6d5ea4bf4e87f66dc56a84beb738b6e61dbec925103ada5bb4163df7cc7c3bb4670065f9d7756fed61919ba8aa02daa02a5b6d48bc6310f5556f39f78b8b313a7332303acbfc996ad185c61a031f40ceee80a055e6d83005313a747532373a323032322d30392d30355430383a31333a34322e3530333337305a313a756c32303a336ac6aa132dbf3e5ae5fcf8cdbd82d3ec6b3d0532303acbfc996ad185c61a031f40ceee80a055e6d830056565"
    }
  },
  "extensions": {}
}

Then you should sign the transaction with your private key, KMS or wallet. If you got signature, you can build the transaction with query.transaction.attachSignature query.

query {
  transaction {
    signTransaction(unsignedTransaction: "TX", signature: "SIGNATURE")
  }
}

Then it’ll return the complete transaction and you can broadcast your transaction with mutation.stageTransaction mutation.

1 Like

Which part should I put inside ACTION?

You should put the result of actionQuery.* (e.g. actionQuery.stake, actionQuery.claimStakeReward. For example, the hexadecimal string of the stake action to stake as 10 NCG, is 6475373a747970655f696475363a7374616b653275363a76616c7565736475323a616d6931306575323a696431363acf82975c8fa23240b8434e89aa3f3fa66565. Then you can write the GraphQL query like:

query {
  transaction {
    unsignedTransaction(publicKey: "YOUR_PUBLIC_KEY", plainValue: "6475373a747970655f696475363a7374616b653275363a76616c7565736475323a616d6931306575323a696431363acf82975c8fa23240b8434e89aa3f3fa66565", nonce: 0)
  }
}

I change the title of topic (APi > API) and added headless, graghql tags.

er, how to open this GraphQL?

There’re several ways you can access to GraphQL endpoint.

Then you should sign the transaction with your private key, KMS or wallet.

It would be better there is an instruction about how to sign transaction. (if it’s too long to introduce here, new article is also good)

1 Like

Hm, something isn’t working for me.

So I’m trying to do a 1 Crystal transfer:

query{
  actionQuery{
    transferAsset(sender:"0xa49d64c31a2594e8fb452238c9a03befd1119963", recipient:"0x1012041FF2254f43d0a938aDF89c3f11867A2A58", amount:"1", currency:CRYSTAL, memo:"Test")
  }
}

This generates:

{
  "data": {
    "actionQuery": {
      "transferAsset": "6475373a747970655f69647531353a7472616e736665725f61737365743275363a76616c7565736475363a616d6f756e746c647531333a646563696d616c506c61636573313a1275373a6d696e746572736e75363a7469636b657275373a4352595354414c656931303030303030303030303030303030303030656575343a6d656d6f75343a5465737475393a726563697069656e7432303a1012041ff2254f43d0a938adf89c3f11867a2a5875363a73656e64657232303aa49d64c31a2594e8fb452238c9a03befd11199636565"
    }
  },
  "extensions": {}
}

So I then moved to unsignedTransaction:

query {
  transaction {
    unsignedTransaction(publicKey: "0xa49d64c31a2594e8fb452238c9a03befd1119963", plainValue: "6475373a747970655f69647531353a7472616e736665725f61737365743275363a76616c7565736475363a616d6f756e746c647531333a646563696d616c506c61636573313a1275373a6d696e746572736e75363a7469636b657275373a4352595354414c656931303030303030303030303030303030303030656575343a6d656d6f75343a5465737475393a726563697069656e7432303a1012041ff2254f43d0a938adf89c3f11867a2a5875363a73656e64657232303aa49d64c31a2594e8fb452238c9a03befd11199636565", nonce: 0)
  }
}

But this then resolved in an unable to resolve field:

{
  "errors": [
    {
      "message": "Error trying to resolve field 'unsignedTransaction'.",
      "locations": [
        {
          "line": 3,
          "column": 5
        }
      ],
      "path": [
        "transaction",
        "unsignedTransaction"
      ],
      "extensions": {
        "code": "FORMAT",
        "codes": [
          "FORMAT"
        ]
      }
    }
  ],
  "data": null,
  "extensions": {}
}

well, I don’t think I’ll run headless. it takes a long time to download the whole blockchain

It would be better there is an instruction about how to sign transaction. (if it’s too long to introduce here, new article is also good)

yeah, looking for this instruction

Ah, It’s possibly because you didn’t truncate 0x from public key. I’m pretty sure that’s also bencodex thing. could you try it again?

I’m afraid that truncating 0x from the public key is returning the same result.

query {
  transaction {
    unsignedTransaction(publicKey: "a49d64c31a2594e8fb452238c9a03befd1119963", plainValue: "6475373a747970655f69647531353a7472616e736665725f61737365743275363a76616c7565736475363a616d6f756e746c647531333a646563696d616c506c61636573313a1275373a6d696e746572736e75363a7469636b657275373a4352595354414c656931303030303030303030303030303030303030656575343a6d656d6f75343a5465737475393a726563697069656e7432303a1012041ff2254f43d0a938adf89c3f11867a2a5875363a73656e64657232303aa49d64c31a2594e8fb452238c9a03befd11199636565", nonce: 0)
  }
}
{
  "errors": [
    {
      "message": "Error trying to resolve field 'unsignedTransaction'.",
      "locations": [
        {
          "line": 3,
          "column": 5
        }
      ],
      "path": [
        "transaction",
        "unsignedTransaction"
      ],
      "extensions": {
        "code": "FORMAT",
        "codes": [
          "FORMAT"
        ]
      }
    }
  ],
  "data": null,
  "extensions": {}
}

@moreal wrote new post on transfer asset with GraphQL specifically, how about this method? I’m suspecting missing nextTxNonce query in this post might be the reason for returning an error. I want to confirm about this theory, but sadly I don’t have any NCG nor Crystals…

Sadly same issue, used @moreal 's query to get the none and it’s the same issue:

query {
  transaction {
    unsignedTransaction(publicKey: "a49d64c31a2594e8fb452238c9a03befd1119963", plainValue: "6475373a747970655f69647531353a7472616e736665725f61737365743275363a76616c7565736475363a616d6f756e746c647531333a646563696d616c506c61636573313a1275373a6d696e746572736e75363a7469636b657275373a4352595354414c656931303030303030303030303030303030303030656575393a726563697069656e7432303a1012041ff2254f43d0a938adf89c3f11867a2a5875363a73656e64657232303aa49d64c31a2594e8fb452238c9a03befd11199636565", nonce: 96866)
  }
}
{
  "errors": [
    {
      "message": "Error trying to resolve field 'unsignedTransaction'.",
      "locations": [
        {
          "line": 3,
          "column": 5
        }
      ],
      "path": [
        "transaction",
        "unsignedTransaction"
      ],
      "extensions": {
        "code": "FORMAT",
        "codes": [
          "FORMAT"
        ]
      }
    }
  ],
  "data": null,
  "extensions": {}
}

Aha, now I get it! public key field should be your actual public key, not your address!
for example, from this previous transaction you can see your public key is 035206890fd8736555ce667672b8183efacd9bf840b6c5ee8eb7f5703e7bddf38c,

I tried

query {
  transaction {
    unsignedTransaction(
    publicKey: "035206890fd8736555ce667672b8183efacd9bf840b6c5ee8eb7f5703e7bddf38c", 
    plainValue: "6475373a747970655f69647531353a7472616e736665725f61737365743275363a76616c7565736475363a616d6f756e746c647531333a646563696d616c506c61636573313a1275373a6d696e746572736e75363a7469636b657275373a4352595354414c656931303030303030303030303030303030303030656575393a726563697069656e7432303a1012041ff2254f43d0a938adf89c3f11867a2a5875363a73656e64657232303aa49d64c31a2594e8fb452238c9a03befd11199636565", 
    nonce: 96866)
  }
}

and It returned the right unsigned transaction!

TODO: the method how to get the public key from the address needed to be added.

I’m pretty sure you can get your public key directly from planet command, but just checking 9cscan and checking the transaction you previously signed seems a faster method.

Aha!

Now that makes sense, interesting as the address is called the public key pretty much everywhere :P.

Though, I’m now at the last query “signature”, it does state this would be the wallet/privatekey/KMS. I tried using the privatekey and it is returning “invalid signature”.

Considering a localnode already has the privatekey hooked to it a bit weird it needs the “privatekey” again, it gives the impression that “signature” might mean something else and not the private key?

        "code": "INVALID_TX_SIGNATURE",
        "codes": [
          "INVALID_TX_SIGNATURE"
        ]

EDIT: Okay, I found my “signature”, it’s actually also on 9CScan. This seems to be unique per transaction. I can see there’s a reference how to get the signature using planet, though I presume there’s a way to also do this via GQL? Otherwise we wouldn’t have 2 different guides :stuck_out_tongue:

Considering a localnode already has the privatekey hooked to it a bit weird it needs the “privatekey” again, it gives the impression that “signature” might mean something else and not the private key?

Yup, long story short, we sign your unsigned transaction (without signature data) through your private key with sha256sum. then we merge unsigned transaction to include signature for it’s own data.

Okay, I found my “signature”, it’s actually also on 9CScan. This seems to be unique per transaction. I can see there’s a reference how to get the signature using planet, though I presume there’s a way to also do this via GQL? Otherwise we wouldn’t have 2 different guides :stuck_out_tongue:

Put it more simply, all signTransaction do is just attatching prepared signature data into bencodex formatted transaction data and return it. It’s generally encouraged to sign your data on your local computer for security sake (you’ll never want to send private key through network), So, no GQL for signing transaction itself. planet command is currently the only way, maybe you can make simple transaction signer with libplanet by yourself. (all the interfaces are prepared)

FYI, when we make transactions to patch table, we do the same thing with planet command manually.

I see, I guess this brings back the issue I raised to Swen previously, as this limits the whole thing of providing a feature for users. Since simply put, you can’t do it as it requires the private key. I was hoping there was a way to do this against the local node, since it already has the private key against it automatically.

So it would be, user starts local node → GQL Query generates the tx and signs it → Send it to RPC Node. But this obviously isn’t possible. I guess it is what it is for the moment and hopefully we get some way to sign things with tokens so private keys aren’t exposed. Thanks Akamig, this was very helpful.

2 Likes

how to find publicKey?

You can derive public key from private key using planet cli tool.
Install planet following this article and check planet key derive command.

this command need privatekey. there is noway to get publickey via graphql or at least just use wallet id?