Skip to content

GraphQL Mutation Support #1214

@dOrgJelli

Description

@dOrgJelli

EDIT: The below "spec" is outdated, please see comment below for the latest specification.

Do you want to request a feature or report a bug?
feature

What is the feature request?
GraphQL Mutation support, aka write semantics.

When a user queries the mutation, the resolver would execute the corresponding function. These functions would be defined by the protocol developer, and would include all of the logic we're used to seeing in a typical JS wrapper for a smart contract protocol: processing and fetching data, signature requests, external service interactions (IPFS, etc), multiple transactions.

For example, imagine we have an exchange with a token whitelist:

type Exchange @entity {
  id: ID!
  tokens: [Token!]!

  // new functionality
  proposeToken(
    token: Token!,
    description: String
  ): TokenProposal
}

type Token @entity {
  id: ID!
  symbol: String
  name: String
}

type TokenProposal @entity {
  id: ID!
  token: Token!
  descriptionHash: String!
  votesFor: BigInt!
  votesAgainst: BigInt!
  passed: Boolean
}

The proposeToken(...) mutation above would be tied to a backing function that could implement the following logic:

  1. Ensures the token isn't already listed.
  2. Sanitizes the token metadata (name, symbol), telling the user ahead of time if there are name conflicts.
  3. Upload the token metadata to IPFS.
  4. Upload the proposal description to IPFS.
  5. Create a new transaction payload for the createProposal(...) function on the Exchange contract, with arguments: token address, token metadata hash, proposal description hash.
  6. Once the user signs, the transaction is submitted.
  7. Await until txReceipt is received.
  8. Parse events, extract the TokenProposal.id emitted from the contract, and query the store for the full entity.
  9. return TokenProposal to the user.

And the psuedo code:

// Note: more info on the runtime environment
//       of this function further down.
function proposeToken(
  this: Exchange,
  token: Token,
  description: string
): TokenProposal {

  // off-chain sanitization
  if (store.get("Token", token.id)) {
    throw Error("Token already listed.");
  }

  const res = sanitizer(token.name, token.symbol);
  if (res) {
    throw Error(`Token Metadata Invalid: ${res.message}`);
  }

  // IPFS Interactions
  const { name, symbol } = token;
  const metadataHash = IPFS.add(JSON.stringify({ name, symbol}));
  const descHash = IPFS.add(description);

  // transacting
  const exchange = ExchangeContarct.bind(this.id);
  const receipt = await exchange.createProposal(
    token.id, metadataHash, descHash
  ).send();

  // get the emitted proposal ID
  const proposalId = receipt.events["NewProposal"].args.id;

  // pull from the store every 2 seconds until the entity is
  // available, and timeout after 1 minute.
  return await store.pull("TokenProposal", proposalId, 2, 60);
}

Open questions?
Where should the mutations run?
Ideally we'd be able to support client & server side.

What are the mutation's implemented in, AssemblyScript?
Since most smart contract developers build their wrapper libraries in JS, I think this should be the first supported runtime.

If the mutation runs server side, how can you communicate back to the client for signatures?
This could be done by injecting a custom web3 provider server side, which sends responses to a custom GraphQL client client-side.

How would these run client-side?
I believe the client's resolver can just thunk to the mutation implementation without sending a request to the server.

Exciting implications!?

  • Type safe, auto-generated, client APIs for Web3 protocols. The GraphQL community's wonderful work in this area enables this. This would cut down maintenance costs for Web3 developers tremendously.
  • Use smart contract protocols in any language, without having to rewrite a wrapper library, since GraphQL has implementations everywhere. If we wanted the mutations to run client side in the scenario, they could be in a WASM module that ran within a language specific runtime.

If you made it this far, thank you :) and apologies for the novel. Pretty stoked for the possibilities here...

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions