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:
OrderBookApifor getting quotes and submitting ordersMetadataApifor creating app data documents (uses the configured adapter automatically)OrderSigningUtilsfor 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)
// 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.
// ...
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 documentappDataHex- the hash to use in the order'sappDatafieldappDataContent- the stringified document content
// 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.
- Accept the connection request in Rabby
- Press the "Run" button again
- Observe the
orderIdreturned 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 eitherbytes32or a stringified JSON object.AppDataHashMismatch: The hash of the app data document doesn't match theappDataHashfield provided in the order. This may be due to the app data document being modified after the order was signed.
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;
}
}