Security News
RubyGems.org Adds New Maintainer Role
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
@cowprotocol/cow-sdk
Advanced tools
[![Styled With Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://prettier.io/) [![Coverage Status](https://coveralls.io/repos/github/cowprotocol/cow-sdk/badge.svg?branch=main)](https://coveralls.io/github/cowprotocol/cow-sdk?
Install the SDK:
yarn add @cowprotocol/cow-sdk
Instantiate the SDK:
import { CowSdk } from '@cowprotocol/cow-sdk'
const chainId = 4 // Rinkeby
const cowSdk = new CowSdk(chainId)
The SDK will expose:
cowSdk.cowApi
)cowSdk.cowSubgraphApi
)cowSdk.signOrder
)For a quick snippet with the full process on posting an order see the Post an Order Example
The SDK provides access to the CoW API. The CoW API allows you:
For example, you can easily get the last 5 order of a trader:
// i.e. Get last 5 orders for a given trader
const trades = await cowSdk.cowApi.getOrders({
owner: '0x00000000005ef87f8ca7014309ece7260bbcdaeb', // Trader
limit: 5,
offset: 0,
})
console.log(trades)
For more information about the API methods, you can check api.cow.fi/docs.
In order to trade, you will need to create a valid order first.
On the contraty to other decentralised exchanges, creating orders is free in CoW Protocol. This is because, one of the
most common ways to do it is by created offchain signed messages (meta-transactions, uses EIP-712
or EIP-1271
).
Posting orders is a three steps process:
OPEN
The next sections will guide you through the process of creating a valid order.
For a quick snippet with the full process on posting an order see the Post an Order Example.
Because of the use of off-chain signing (meta-transactions), users will need to Enable the sell token before signed orders can be considered as valid ones.
This enabling is technically an ERC-20
approval, and is something that needs to be done only once. After this all
order creation can be done for free using offchain signing.
For more details see https://docs.cow.fi/tutorials/how-to-submit-orders-via-the-api/1.-set-allowance-for-the-sell-token
Before you can sign any transaction, you have to instantiate the SDK with a Ethers.JS signer:
import { Wallet } from 'ethers'
import { CowSdk, OrderKind } from '@cowprotocol/cow-sdk'
const mnemonic = 'fall dirt bread cactus...'
const wallet = Wallet.fromMnemonic(mnemonic)
const cowSdk = new CowSdk(
4, { // Leaving chainId empty will default to MAINNET
signer: wallet // Provide a signer, so you can sign order
})
To create an order, you need to get a price/fee quote first:
Market Price
and the Fee
for any given trade you intent to do.Market Price
is not strictly needed, you can use your own pricing logic.
Fee
however is very important.
To get the quote, you simply specify the trade you intent to do:
const quoteResponse = await cowSdk.cowApi.getQuote({
kind: OrderKind.SELL, // Sell order (could also be BUY)
sellToken: '0xc778417e063141139fce010982780140aa0cd5ab', // WETH
buyToken: '0x4dbcdf9b62e891a7cec5a2568c3f4faf9e8abe2b', // USDC
amount: '1000000000000000000', // 1 WETH
userAddress: '0x1811be0994930fe9480eaede25165608b093ad7a', // Trader
validTo: 2524608000,
})
const { sellToken, buyToken, validTo, buyAmount, sellAmount, receiver, feeAmount } = quoteResponse.quote
Once you know the price and fee, we can create the order and sign it:
Limit Price
and Fee
.Limit Price
, but some general approach is to take the current Market Price
and apply some slippage tolerance to it. Received Amount = Expected Amount * (1 - Slippage Tolerance)
const { sellToken, buyToken, validTo, buyAmount, sellAmount, receiver, feeAmount } = quoteResponse.quote
// Prepare the RAW order
const order = {
kind: OrderKind.SELL, // SELL || BUY
receiver, // Your account or any other
sellToken,
buyToken,
partiallyFillable: false, // ("false" is for a "Fill or Kill" order, "true" for allowing "Partial execution" which is not supported yet)
// Deadline
validTo,
// Limit Price
// You can apply some slippage tolerance here to make sure the trade is executed.
// CoW protocol protects from MEV, so it can work with higher slippages
sellAmount,
buyAmount,
// Use the fee you received from the API
feeAmount,
// The appData allows you to attach arbitrary information (meta-data) to the order. Its explained in their own section. For now, you can use this 0x0 value
appData: '0x0000000000000000000000000000000000000000000000000000000000000000'
}
// Sign the order
const signedOrder = await cowSdk.signOrder(order)
At this point, you have a signed order. So next step will be to post it to the API so it's considered by the solvers and executed.
Once you have a signed order, last step is to send it to the API.
OPEN
until the specified validTo
date (expiration)EXECUTED
: you will pay the signed fee, and get at least the buyAmount
tokens you specified, although you will probably get more! (you will probably get a so-called Surplus).EXPIRES
: If your price is not good enough, and the order is out of the market price before
expiration, your order will expire. This doesn't have any cost to the user, which only pays the fee if the trade is executed.CANCELLED
. Cancelling an order can be done both as a free meta-transaction
(soft cancelations) or as a regular on-chain transaction (hard cancelations).orderId
which identifies the order, and is created as a summary (hash) of it. In other words, the orderId
is deterministic given all the order parameters.Post an order using the SDK:
const orderId = await cowSdk.cowApi.sendOrder({
order: { ...order, ...signedOrder },
owner: '0x1811be0994930fe9480eaede25165608b093ad7a',
})
Once the order is posted, its good to allow to check the state of it.
One easy is to check in the CoW Explorer. You can create a CoW Explorer link if you have the orderId
:
// View order in explorer
console.log(`https://explorer.cow.fi/rinkeby/orders/${orderId}`)
Orders in CoW Protocol can contain arbitrary data in a field called AppData
.
The SDK facilitates the creation of these documents, and getting the AppData
Hex number that summarizes it.
The most important thing to define in the meta-data is the name of your app, so the order-flow can be credited to it.
const appDataDoc = cowSdk.metadataApi.generateAppDataDoc({
appDataParams: { appCode: "YourApp" }
})
This will create a document similar to:
{
"version": "0.4.0",
"appCode": "YourApp",
"metadata": {}
}
After creating the most basic document, you can see how to attach additional meta-data items.
For example, you could give information about who referred the user creating the order.
const appDataDoc = cowSdk.metadataApi.generateAppDataDoc({
appDataParams: { appCode: "YourApp" },
metadataParas: {
referrerParams: {
address: '0x1f5B740436Fc5935622e92aa3b46818906F416E9'
}
}
})
This will create a document similar to:
{
"version": "0.4.0",
"appCode": "YourApp",
"metadata": {
"referrer": {
"address": "0x1f5B740436Fc5935622e92aa3b46818906F416E9",
"version": "0.1.0"
}
}
}
The method cowSdk.metadataApi.generateAppDataDoc
will always create the latest schema version.
For a complete list of metadata that can be attached and for previous versions
check @cowprotocol/app-data
repository
and NPM package
The AppData
Hex points to an IPFS document with the meta-data attached to the order.
You can calculate the AppData
Hex, and its corresponding cidV0
using the SDK:
const { appDataHash, cidv0 } = await cowSdk.metadataApi.calculateAppDataHash(appDataDoc)
Note how this operation is deterministic, so given the same document, it will always generate the same hash.
This method can be used to calculate the actual hash before uploading the document to IPFS.
Given the AppData
of a document that has been uploaded to IPFS, you can easily retrieve the document.
const appDataDoc = await cowSdk.metadataApi.decodeAppData('0x5ddb2c8207c10b96fac92cb934ef9ba004bc007a073c9e5b13edc422f209ed80')
This will return a document similar to:
{
"version": "0.1.0",
"appCode": "YourApp",
"metadata": {
"referrer": {
"address": "0x1f5B740436Fc5935622e92aa3b46818906F416E9",
"version": "0.1.0"
}
}
}
The SDK uses Pinata to upload it to IPFS, so you will need an API Key in order to upload it using the SDK.
Alternatively, you can upload the document on your own using any other service.
// Make sure you provide the IPFS params when instantiating the SDK
const cowSdk = new CowSdk(4, {
ipfs: {
pinataApiKey: 'YOUR_PINATA_API_KEY',
pinataApiSecret: 'YOUR_PINATA_API_SECRET'
},
})
// Upload to IPFS
const uploadedAppDataHash = await cowSdk.metadataApi.uploadMetadataDocToIpfs(appDataDoc)
Given an IPFS CIDv0 you can convert it to an AppData
const decodedAppDataHex = await cowSdk.metadataApi.cidToAppDataHex('QmUf2TrpSANVXdgcYfAAACe6kg551cY3rAemB7xfEMjYvs')
This will return an AppData
hex: 0x5ddb2c8207c10b96fac92cb934ef9ba004bc007a073c9e5b13edc422f209ed80
This might be handy if you decide to upload the document to IPFS yourself and then you need the AppData to post your order
Similarly, you can do the opposite and convert an AppData
into an IPFS document:
const decodedAppDataHex = await cowSdk.metadataApi.appDataHexToCid(hash)
This will return an IPFS CIDv0: QmUf2TrpSANVXdgcYfAAACe6kg551cY3rAemB7xfEMjYvs
It's possible to get schema files directly for each exported version using getAppDataSchema
const schema = await cowSdk.metadataApi.getAppDataSchema('0.4.0')
If for some reason you decide to create an appDataDoc
without using the helper function, you can use validateAppDataDoc
to validate it against the schema
const { success, error } = await cowSdk.metadataApi.validateAppDataDoc({ version: '0.1.0', ... })
You can query the Cow Subgraph either by running some common queries exposed by the CowSubgraphApi
or by building your own ones:
const chainId = 1 // Mainnet
const cowSdk = new CowSdk(chainId)
// Get Cow Protocol totals
const { tokens, orders, traders, settlements, volumeUsd, volumeEth, feesUsd, feesEth } =
await cowSdk.cowSubgraphApi.getTotals()
console.log({ tokens, orders, traders, settlements, volumeUsd, volumeEth, feesUsd, feesEth })
// Get last 24 hours volume in usd
const { hourlyTotals } = await cowSdk.cowSubgraphApi.getLastHoursVolume(24)
console.log(hourlyTotals)
// Get last week volume in usd
const { dailyTotals } = await cowSdk.cowSubgraphApi.getLastDaysVolume(7)
console.log(dailyTotals)
// Get the last 5 batches
const query = `
query LastBatches($n: Int!) {
settlements(orderBy: firstTradeTimestamp, orderDirection: desc, first: $n) {
txHash
firstTradeTimestamp
}
}
`
const variables = { n: 5 }
const response = await cowSdk.cowSubgraphApi.runQuery(query, variables)
console.log(response)
yarn
yarn build
# Build in watch mode
yarn start
yarn test
FAQs
## 📚 [Docs website](https://docs.cow.fi/)
We found that @cowprotocol/cow-sdk demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
Security News
Node.js will be enforcing stricter semver-major PR policies a month before major releases to enhance stability and ensure reliable release candidates.
Security News
Research
Socket's threat research team has detected five malicious npm packages targeting Roblox developers, deploying malware to steal credentials and personal data.