Skip to content

Commit

Permalink
feat: appdata tutorials
Browse files Browse the repository at this point in the history
  • Loading branch information
mfw78 committed Jan 5, 2024
1 parent 3e2a278 commit 078e686
Show file tree
Hide file tree
Showing 91 changed files with 1,137 additions and 61 deletions.
5 changes: 0 additions & 5 deletions content/tutorial/01-orders/02-app-data/02-app-data/README.md

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,4 @@ In the above case, we can see that:
- the `feeAmount` is `1881812051493698` atomic units of `wxDAI` (which is `0.001881812051493698` `wxDAI`)
- the `kind` is `sell`

The above `OrderQuoteResponse` object actually maps to the [`GPv2Order.Data`](https://beta.docs.cow.fi/cow-protocol/reference/contracts/core/settlement#gpv2orderdata-struct) struct for the smart contract, so this is what we will sign in the [next tutorial](/tutorial/sign) for our swap.
The above `OrderQuoteResponse` object actually maps to the [`GPv2Order.Data`](https://beta.docs.cow.fi/cow-protocol/reference/contracts/core/settlement#gpv2orderdata-struct) struct for the smart contract, so this is what we will sign in the [next tutorial](/tutorial/sign-order) for our swap.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Signing
---

Here we will build on the previous tutorial and sign the quote we got from the [quote tutorial](/tutorial/quote), so that we can place an order on CoW Protocol.
Here we will build on the previous tutorial and sign the quote we got from the [quote tutorial](/tutorial/quote-order), so that we can place an order on CoW Protocol.

## Intents and signatures

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Submitting
---

Further building on the previous tutorial, we will now submit the order we signed in the [sign order tutorial](/tutorial/sign) to CoW Protocol.
Further building on the previous tutorial, we will now submit the order we signed in the [sign order tutorial](/tutorial/sign-order) to CoW Protocol.

## Submitting an order

Expand Down Expand Up @@ -58,4 +58,4 @@ An example `orderId` should look like:
A couple of errors may easily result when running this code:

- **`InsufficientBalance`**: The wallet you have signed with does not have enough balance for the `sellToken`. A reminder in this example, the `sellToken` is `wxDai` on Gnosis chain.
- **`InsufficientAllowance`**: In this case, the wallet has enough balance, however you have missed out a step in the [approve tutorial](/tutorial/approve) and have not approved the `relayerAddress` to spend the `sellToken` on your behalf.
- **`InsufficientAllowance`**: In this case, the wallet has enough balance, however you have missed out a step in the [approve tutorial](/tutorial/approve-sell-token-order) and have not approved the `relayerAddress` to spend the `sellToken` on your behalf.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ With CoW Protocol, the only way to view the status of an order that has not yet

## Instantiate the SDK

We will start from the basic setup from the [quote tutorial](/tutorial/quote) after we have instantiated the `OrderBookApi`.
We will start from the basic setup from the [quote tutorial](/tutorial/quote-order) after we have instantiated the `OrderBookApi`.

### Query parameters

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ To cancel an order, we need to know the `orderUid` of the order we want to cance

### Instantiate the SDK

We will start from the basic setup from the [quote tutorial](/tutorial/quote) after we have instantiated the `OrderBookApi` and configured the `signer`.
We will start from the basic setup from the [quote tutorial](/tutorial/quote-order) after we have instantiated the `OrderBookApi` and configured the `signer`.

### Cancellation parameters

Expand All @@ -36,7 +36,7 @@ export async function run(provider: Web3Provider): Promise<unknown> {

### Signing the cancellation

Just like we did in the [sign order tutorial](/tutorial/sign), we need to sign the cancellation. To do this, we will use the `OrderSigningUtils` utility.
Just like we did in the [sign order tutorial](/tutorial/sign-order), we need to sign the cancellation. To do this, we will use the `OrderSigningUtils` utility.

```typescript
/// file: run.ts
Expand Down Expand Up @@ -83,7 +83,7 @@ export async function run(provider: Web3Provider): Promise<unknown> {
}
```

Just as we did in the [submit order tutorial](/tutorial/submit), we are using a `try/catch` block to handle errors.
Just as we did in the [submit order tutorial](/tutorial/submit-order), we are using a `try/catch` block to handle errors.

## Run the code

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
---
title: Simple app data
---

So, we have created a simple order in the previous section, but what if you're a wallet provider, and you want to add some extra data to the order? For example, you may want to associate the order with your app in order to brag about how many orders on CoW Protocol are created by your app.

## `app-data` SDK

The `app-data` is documented in [JSON Schema](https://beta.docs.cow.fi/cow-protocol/reference/core/intents/app-data#schema). Writing to a schema is not very convenient, so we have a special SDK for that. It's called the `app-data` SDK.

To install it, run: `npm install @cowprotocol/app-data`

> It is highly suggested to use the `app-data` SDK to generate the `app-data` document. There are many subtle nuances to the `app-data` document, making it easy to get wrong.
### Instantiate the SDK

To instantiate the SDK, we simply call it's constructor:

```typescript
/// file: run.ts
import type { Web3Provider } from '@ethersproject/providers';
+++import { MetadataApi } from '@cowprotocol/app-data';+++

export async function run(provider: Web3Provider): Promise<unknown> {
// ...
const metadataApi = new MetadataApi();
// ...
}
```

### App data parameters

As an example, if we were developing a wallet, we may want to add some metadata to the order. In doing so, we will provide:

- `appCode` - the name of our app
- `environment` - the environment we're running on (e.g. `prod`, `staging`)
- `referrer` - the ethereum address for the referrer of the order
- `quote` - the quote parameters, nominally the slippage applied to the order
- `orderClass` - the order class, eg. `market`, `limit`, `twap` etc.

```typescript
/// file: run.ts
import type { Web3Provider } from '@ethersproject/providers';
+++import { MetadataApi, latest } from '@cowprotocol/app-data';+++

export async function run(provider: Web3Provider): Promise<unknown> {
// ...

const appCode = 'Decentralized CoW';
const environment = 'prod';
const referrer = { address: '0xcA771eda0c70aA7d053aB1B25004559B918FE662' };

const quoteAppDoc: latest.Quote = { slippageBips: '50' };
const orderClass: latest.OrderClass = { orderClass: 'market' };

// ...
}
```

> We use the `latest` namespace to get the latest types. The schema is versioned, so you may alternatively use the version namespace.
### App data document

Now that we have the parameters, we can create the app data document:

```typescript
/// file: run.ts
import type { Web3Provider } from '@ethersproject/providers';
import { MetadataApi, latest } from '@cowprotocol/app-data';

export async function run(provider: Web3Provider): Promise<unknown> {
// ...

const appDataDoc = await metadataApi.generateAppDataDoc({
appCode,
environment,
metadata: {
referrer,
quote: quoteAppDoc,
orderClass,
},
});

// ...
}
```

### Processing the document

Now that we have the document, we can process it. In doing this, we will:

- determine the CID of the document
- determine the appDataContent, which is passed in the order's `appData` field when sent to the API
- determine the appDataHex, which is passed in the order's `appDataHash` field when sent to the API

```typescript
/// file: run.ts
import type { Web3Provider } from '@ethersproject/providers';
import { MetadataApi, latest } from '@cowprotocol/app-data';

export async function run(provider: Web3Provider): Promise<unknown> {
// ...

const { appDataHex, appDataContent } = await metadataApi.appDataToCid(appDataDoc);

return {
appDataDoc,
appDataHex,
appDataContent,
};
}
```

## Run the code

To run the code, we can press the "Run" button in the bottom right panel (the web container).

1. Press the "Run" button
2. Observe the respective data returned to the output panel

An example output should look like:

```json
/// file: output.json
{
"appDataDoc": {
"appCode": "Decentralized CoW",
"metadata": {
"referrer": {
"address": "0xcA771eda0c70aA7d053aB1B25004559B918FE662"
},
"quote": {
"slippageBips": "50"
},
"orderClass": {
"orderClass": "market"
}
},
"version": "0.11.0",
"environment": "prod"
},
"cid": "f01551b20b4b4561d26cfe084594ddbb4cf6af5397c1bf1cb31997ae4d2a82325eeda8f6d",
"appDataHex": "0xb4b4561d26cfe084594ddbb4cf6af5397c1bf1cb31997ae4d2a82325eeda8f6d",
"appDataContent": "{\"appCode\":\"Decentralized CoW\",\"environment\":\"prod\",\"metadata\":{\"orderClass\":{\"orderClass\":\"market\"},\"quote\":{\"slippageBips\":\"50\"},\"referrer\":{\"address\":\"0xcA771eda0c70aA7d053aB1B25004559B918FE662\"}},\"version\":\"0.11.0\"}"
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type { Web3Provider } from '@ethersproject/providers'

export async function run(provider: Web3Provider): Promise<unknown> {
// TODO: Implement
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { Web3Provider } from '@ethersproject/providers'
import { MetadataApi, latest } from '@cowprotocol/app-data'

export async function run(provider: Web3Provider): Promise<unknown> {
const metadataApi = new MetadataApi()

const appCode = 'Decentralized CoW'
const environment = 'production'
const referrer = { address: `0xcA771eda0c70aA7d053aB1B25004559B918FE662` }

const quoteAppDoc: latest.Quote = { slippageBips: '50' }
const orderClass: latest.OrderClass = { orderClass: 'market' }

const appDataDoc = await metadataApi.generateAppDataDoc({
appCode,
environment,
metadata: {
referrer,
quote: quoteAppDoc,
orderClass
},
})

const { appDataHex, appDataContent } = await metadataApi.appDataToCid(appDataDoc)

return {
appDataDoc,
appDataHex,
appDataContent
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
---
title: Create order with appData
---

Now, from the previous tutorial we have created a simple app data document. We will now use this document to create an order.

In doing so, we will be making use of:

- [Submit order](/tutorial/submit-order)
- [Simple app data](/tutorial/simple-app-data)

## Instantiate the SDK

We will start from the basic setup from the [submit order](/tutorial/submit-order) tutorial and the [simple app data](/tutorial/simple-app-data) tutorial. This has been populated in the code editor for you.


### Quoting with app data

The keen eye-ed among you will notice that `appDataHex` and `appDataContent` are not used. Let's fix that. When we request a quote, we will pass `appDataHex` and `appDataContent` to the API. This allows the API to:

- validate the app data document and it's has (`appDataHex`)
- wrap the app data into the response object
- determine any additional fees that may be required (if the app data document contains hooks)

```typescript
/// file: run.ts
// ...
export async function run(provider: Web3Provider): Promise<unknown> {
// ...

const quoteRequest: OrderQuoteRequest = {
sellToken,
buyToken,
from: ownerAddress,
receiver: ownerAddress,
sellAmountBeforeFee: sellAmount,
kind: OrderQuoteSideKindSell.SELL,
+++appData: appDataContent,+++
+++appDataHash: appDataHex,+++
};

// ...
}
```

### Signing with app data

When signing an order, we need to make sure that we have set the `appData` correctly. In this case, the `UnsignedOrder` used by the `OrderSigningUtils` class has an `appData` field which should be set to the `appDataHex` value.

```typescript
/// file: run.ts
// ...
export async function run(provider: Web3Provider): Promise<unknown> {
// ...
const order: UnsignedOrder = {
...quote,
receiver: ownerAddress,
+++appData: appDataHex,+++
}
// ...
}
```

## Run the code

To run the code, we can press the "Run" button in the bottom right panel (the web container).

When running the script, we may be asked to connect a wallet. We can use Rabby for this.

1. Accept the connection request in Rabby
2. Press the "Run" button again
3. Observe the `orderId` returned to the output panel

You can now use the `orderId` to check the status of the order on [CoW Explorer](https://explorer.cow.fi/). Within the order details page, you can also see the app data document that was used to create the order.

### Errors

The usual API errors from the [submit order](/tutorial/submit-order) tutorial may occur. In addition, the following errors may occur:

- **`InvalidAppData`**: The app data passed to the API is not either `bytes32` or a stringified JSON object.
- **`AppDataHashMismatch`**: The hash of the app data document doesn't match the `appDataHash` field provided in the order. This may be due to the app data document being modified after the order was signed.
Loading

0 comments on commit 078e686

Please sign in to comment.