-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
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:
- Ensures the token isn't already listed.
- Sanitizes the token metadata (name, symbol), telling the user ahead of time if there are name conflicts.
- Upload the token metadata to IPFS.
- Upload the proposal description to IPFS.
- Create a new transaction payload for the
createProposal(...)function on theExchangecontract, with arguments: token address, token metadata hash, proposal description hash. - Once the user signs, the transaction is submitted.
- Await until txReceipt is received.
- Parse events, extract the
TokenProposal.idemitted from the contract, and query the store for the full entity. - return
TokenProposalto 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...