Skip to main content

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:

Instantiate the SDK

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

The key components we'll use are:

  • OrderBookApi for getting quotes and submitting orders
  • MetadataApi for creating app data documents (uses the configured adapter automatically)
  • OrderSigningUtils for signing orders

Quoting with app data

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

  • validate the app data document and its hash (appDataHex)
  • wrap the app data into the response object
  • determine any additional fees that may be required (if the app data document contains hooks)
run.ts
// After generating the app data document...
export async function run(provider: Web3Provider): Promise<unknown> {
	// Setup adapter first
	const { signer } = setupAdapter(provider)

	// Create app data document
	const { cid, appDataHex, appDataContent } = await metadataApi.getAppDataInfo(appDataDoc)

	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.

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

	// OrderSigningUtils automatically uses the configured adapter
	const orderSigningResult = await OrderSigningUtils.signOrder(order, chainId, signer)
	// ...
}

Processing app data

Note that we're using the updated getAppDataInfo() method instead of the deprecated appDataToCid(). This method returns:

  • cid - the content identifier for the document
  • appDataHex - the hash to use in the order's appData field
  • appDataContent - the stringified document content
run.ts
// Use the updated API method
const { cid, appDataHex, appDataContent } = await metadataApi.getAppDataInfo(appDataDoc);

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. 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 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.

Next: Upload to the API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import type { Web3Provider } from '@ethersproject/providers';
import {
	OrderBookApi,
	SupportedChainId,
	OrderQuoteRequest,
	OrderQuoteSideKindSell,
	OrderSigningUtils,
	UnsignedOrder,
	SigningScheme,
	MetadataApi,
	latest,
	setGlobalAdapter
} from '@cowprotocol/cow-sdk';
import { EthersV5Adapter } from '@cowprotocol/sdk-ethers-v5-adapter';
 
// Helper function to setup adapter (will be used in all tutorials from now on)
function setupAdapter(provider: Web3Provider) {
	const signer = provider.getSigner();
	const adapter = new EthersV5Adapter({ provider, signer });
	setGlobalAdapter(adapter);
	return { signer, adapter };
}
 
export async function run(provider: Web3Provider): Promise<unknown> {
	const { signer } = setupAdapter(provider);
 
	const chainId = +(await provider.send('eth_chainId', []));
	if (chainId !== SupportedChainId.GNOSIS_CHAIN) {
		throw new Error(`Please connect to the Gnosis chain. ChainId: ${chainId}`);
	}
 
	const orderBookApi = new OrderBookApi({ chainId: SupportedChainId.GNOSIS_CHAIN });
	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 { cid, appDataHex, appDataContent } = await metadataApi.getAppDataInfo(appDataDoc);
 
	const ownerAddress = await signer.getAddress();
 
	const sellToken = '0xe91d153e0b41518a2ce8dd3d7944fa863463a97d'; // wxDAI
	const buyToken = '0x177127622c4A00F3d409B75571e12cB3c8973d3c'; // COW
	const sellAmount = '1000000000000000000'; // 1 wxDAI
 
	const quoteRequest: OrderQuoteRequest = {
		sellToken,
		buyToken,
		from: ownerAddress,
		receiver: ownerAddress,
		sellAmountBeforeFee: sellAmount,
		kind: OrderQuoteSideKindSell.SELL
	};
 
	const { quote } = await orderBookApi.getQuote(quoteRequest);
 
	const order: UnsignedOrder = {
		...quote,
		receiver: ownerAddress
	};
 
	const orderSigningResult = await OrderSigningUtils.signOrder(order, chainId, signer);
 
	try {
		const orderId = await orderBookApi.sendOrder({
			...quote,
			...orderSigningResult,
			signingScheme: orderSigningResult.signingScheme as unknown as SigningScheme
		});
 
		return {
			orderId
		};
	} catch (e) {
		return e;
	}
}
 
initialising