NOTE: If at any point during this tutorial you feel stuck, you can see the completed project here. Additionally please feel free to reach out to us on Discord if you have any questions, or need some hands on help.
Have installed:
- Node.JS
- NVM (Node Version Manager)
- Yarn
- Docker
- Docker-Compose
Have a basic understanding of:
- GraphQL
- AssemblyScript
- Uniswap
From within this directory, run the following commands:
nvm install
- install the version of node listed in the.nvmrc
file.nvm use
- use the version of node listed in the.nvmrc
file.yarn
- install all package.json dependencies.
In GraphQL & Polywrap, operations are seperated in Read & Write, or rather Query & Mutation. We'll start with first developing our Query module.
For each Polywrap module, there is a GraphQL schema & an Assemblyscript (or other wasm-compatible language) implementation. The GraphQL serves as the "public interface" for your webassembly code.
Navigate to the file:
./src/query/schema.graphql
And add the following code:
#import { Query, ChainId, TokenAmount } into Uni from "w3://ens/v2.uniswap.web3api.eth"
type Query {
fetchTokenTotalSupply(
chainId: Uni_ChainId!
address: String!
symbol: String
name: String
): Uni_TokenAmount!
}
In this schema, you'll see that first we import some types from the Uniswap polywrapper. Next, we define the fetchTokenTotalSupply
query function.
Let's write the "implementation" of this function in AssemblyScript, which will later be compiled down to WebAssembly :)
In the file:
./src/query/index.ts
Add the following code:
import {
Uni_Query,
Uni_TokenAmount,
Input_fetchTokenTotalSupply
} from "./w3";
export function fetchTokenTotalSupply(input: Input_fetchTokenTotalSupply): Uni_TokenAmount {
const token = Uni_Query.fetchTokenData({
chainId: input.chainId,
address: input.address,
symbol: input.symbol,
name: input.name
});
const amount = Uni_Query.fetchTotalSupply({
token: token
});
return amount;
}
In the code above, you can see that we're querying the Uniswap polywrapper from within our own custom wrapper. This shows just how easy it is to integrate and compose polywrappers together.
Additionally, you'll notice that we're importing the same types that are used in our GraphQL schema from the ./w3
folder. This is our "magic code-generation folder", where Polywrap's CLI generates "language bindings" for your interface's types. This generated code does all the a lot of heavy lifting for you, so all you have to worry about is implementing the business logic that makes your wrapper unique and useful.
Next, we'll create a Mutation (write) function. This function will make it a bit easier for a user to swap tokens using Uniswap.
In the file:
./src/mutation/schema.graphql
Add the following schema:
#import { Mutation, Query, ChainId, TokenAmount, TradeOptions } into Uni from "w3://ens/v2.uniswap.web3api.eth"
type Mutation {
simpleSwap(
chainId: Uni_ChainId!
tokenInAddress: String!
tokenOutAddress: String!
tokenInAmount: BigInt!
tradeOptions: Uni_TradeOptions!
): SwapOutput!
}
type SwapOutput {
txHash: String!
}
As you can see, we define another function named simpleSwap
, as well as a new custom type SwapOutput
.
And finally, the mutation's implementation:
./src/mutation/index.ts
import {
Uni_Query,
Uni_Mutation,
Input_simpleSwap,
SwapOutput,
Uni_TradeType
} from "./w3";
export function simpleSwap(input: Input_simpleSwap): SwapOutput {
const tokenIn = Uni_Query.fetchTokenData({
chainId: input.chainId,
address: input.tokenInAddress,
symbol: null,
name: null
});
const tokenOut = Uni_Query.fetchTokenData({
chainId: input.chainId,
address: input.tokenOutAddress,
symbol: null,
name: null
});
const txResponse = Uni_Mutation.swap({
tokenIn: tokenIn,
tokenOut: tokenOut,
amount: input.tokenInAmount,
tradeType: Uni_TradeType.EXACT_INPUT,
tradeOptions: input.tradeOptions,
txOverrides: null
});
return {
txHash: txResponse.hash
}
}
Now that our implementation is done, let's build this thing and see it running!
From within this directory, run the following commands:
yarn build
This command may take a while the first time it's run. What it is doing (in the background) is asking the Polywrap CLI to build your polywrapper.
In order to make sure polywrappers can always be rebuilt on different machines, we use Docker. Docker allows us to create a "fresh build image" where your source files will be built into WebAssembly (Wasm) output files.
Docker helps you and your team better collaborate, and in the future will provide "source code verification" so users can see what your polywrapper's code looks like, helping increase trust in what they're running inside their apps.
After the build command finishes, you should see a ./build
folder. Take a peek inside! You'll find the fully-built GraphQL schema, Query & Mutation Wasm modules, and some web3api YAML manifest files.
Alright enough chit-chat, let's test it!
Luckily with Polywrap, testing is extremely easy to get up & running with. Since wrappers can be queried on-demand, we created a useful "query recipe" utility.
Checking the query recipe at:
./recipes/e2e.json
This query recipe instructs the CLI to:
- Connect to the polywrapper @
/ens/testnet/uni-integration.eth
- Execute the provided query
Let's try it. Run the following commands from within this folder:
yarn test:env:up
yarn deploy
yarn test
You should see the following output:
-----------------------------------
query {
fetchTokenTotalSupply(
chainId: $chainId
address: $address
)
}
{
"chainId": "MAINNET",
"address": "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984"
}
-----------------------------------
-----------------------------------
{
"fetchTokenTotalSupply": {
"token": {
"chainId": 0,
"address": "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984",
"currency": {
"decimals": 18,
"symbol": "UNI",
"name": "Uniswap"
}
},
"amount": "1000000000000000000000000000"
}
}
-----------------------------------
The first section is the query, and the second section is the result returned from the polywrapper you've just developed!
After you're done, be sure to run yarn test:env:down
to teardown our testing docker environment.
As you can see above, we've only tested the Query method we built, but not the mutation method.
Well, in order to test the mutation method, we'll have to create our own instance of the Web3ApiClient class pointed at the mainnet ganache fork running at 8546.
An example of how to do this can be found here:
https://github.com/polywrap/monorepo/blob/prealpha/packages/apis/uniswapv2/src/__tests__/e2e/swap_e2e.spec.ts
Based on the project setup linked above, try to setup this project similarly with Jest testing. Use that setup to test the mutation method we added above.
Building WebAssembly based SDKs has never been so easy. We've shown you how to define your own schemas, implementations, and import existing polywrappers into your own wrapper.
Polywrap is Web3 composability on steroids for dApp developers, and we hope that this simple tutorial is starting to give you a better idea of why this is so.
If you'd like to learn more, checkout our landing page & documentation: https://polywrap.io
If you have any questions, don't hesitate to reach out: http://discord.polywrap.io
Try The Uniswap <> Polywrap Demo App:
https://demo.uniswap.polywrap.io/
Checkout the Uniswap Polywrapper's source-code:
https://github.com/polywrap/monorepo/tree/prealpha/packages/apis/uniswapv2