A js client library for DeversiFi - StarkWare orders
Note: This library is for the new DeversiFi 2.0 platform, scheduled to launch on Ethereum mainnet later in 2020. Currently a test version of it is live on the Ropsten test network at https://app.stg.deversifi.com // https://api.stg.deversifi.com
For the current production version of DeversiFi use the old client library.
- Installation
- Setup
- Placing an Order
- Cancelling Orders
- Account History
- Unlocking Tokens
- More examples
- Troubleshooting
- Developing
- Useful Links
- Developing
npm i dvf-client-js
Alternatively on the browser you can use the standalone build
<script src="http://path/to/dist/dvf.js"></script>
Authentication to make all the following requests is done by signing using an Ethereum private key. Signing is handled by the Deversifi Trustless client library if the account is available and unlocked. However if signing using a hardware wallet, or using a raw private key, the message and signature need to be prepared separately.
- An Ethereum wallet
- A web3 provider with your account or a private key
- Such as MetaMask, keystore file, hardware wallet or raw private key
// In case of MetaMask make sure you call ethereum.enable() before using it
const DVF = require('dvf-client-js')
const dvf = await DVF()
const DVF = require('dvf-client-js')
const web3 = new DVF.Web3("https://your-web3-provider")
const dvf = await DVF(web3)
const HDWalletProvider = require("truffle-hdwallet-provider");
const Web3 = require("Web3")
const privateKey = '8F085...' // Account's private key
const infuraKey = '9e28b...' // Your Infura API KEY
const infuraURL = 'https://mainnet.infura.io/v3/' + infuraKey
const provider = new HDWalletProvider(privateKey, infuraURL)
const web3 = new Web3(provider)
dvf = await DVF(web3)
View the full example: /examples/node_sell_eth_infura.js
It's possible to overwrite values on the configuration on a per instance basis.
The default configuration can be overwritten with an optional
parameter userConf
when calling the DVF function.
For instance:
dvf = await DVF(web3, {
api: 'https://your-custom-api-address',
gasStationApiKey: 'a1b2c3...
})
The configuration is also merged with the configuration provided by the exchange
on the HTTP endpoint /trustless/v1/r/get/conf
which at the moment looks similar
to this:
"0x":{
"minOrderTime":300,
"tokenRegistry":{
"ETH":{
"decimals":18,
"wrapperAddress":"0x965808e7f815cfffd4c018ef2ba4c5a65eba087e",
"minOrderSize":0.02
},
"USDT":{
"decimals":6,
"wrapperAddress":"0x83e42e6d1ac009285376340ef64bac1c7d106c89",
"tokenAddress":"0x0736d0c130b2ead47476cc262dbed90d7c4eeabd",
"minOrderSize":10,
"settleSpread": 0.002
}
},
"DeversifiAddress":"0x9faf5515f177f3a8a845d48c19032b33cc54c09c",
"exchangeAddress":"0x67799a5e640bc64ca24d3e6813842754e546d7b1",
"exchangeSymbols":[
"ETH:USDT"
]
}
The complete compiled configuration is accessible through dvf.config
, for instance:
const dvf = await DVF()
const config = dvf.config
You can setup a default custom gas price by setting up the 'defaultGasPrice' property
const dvf = await DVF()
dvf.set('defaultGasPrice', web3.utils.toWei('2', 'gwei'))
DVF Client calls https://ethgasstation.info API to get the current gas prices and calculate a safe gas price for Ethereum transactions. Access to the ETH Gas Station API is free, but rate limited if you are not using an API key. If a ETH Gas Station API key is not provided then a recommended gas price is used which is available in dvf.recommendedGasPrices
.
You can get an API Key from https://data.defipulse.com. To configure your api key with dvf client please pass this as a userConf
parameter when initialising DVF:
javascript
dvf = await DVF(web3, {
api: 'https://your-custom-api-address',
gasStationApiKey: 'a1b2c3...'
})
or by setting the 'gasStationApiKey' property:
dvf.set('gasStationApiKey', 'a1b2c3...')
Before placing an order, you are required to lock tokens into the Deversifi wrapper contracts. This allows for guaranteed execution and ensures Trustless orders can be added directly onto the centralised order book, and matched against trades from centralised users.
When locking an ERC20 Ethereum-based token for the first time from a specific account, you are required to approve it to interact with the time-lock smart contracts.
const token = 'ZRX'
dvf.contract.approve(token)
This step does not need to be repeated again, and subsequently you are required only to call the lock function. This transfers tokens into the wrapper token contract, ready to trade.
const token = 'ZRX'
const amount = 15 // Number of tokens to lock
const forTime = 48 // Time (in hours) after which unlocking does not require permission
const response = await dvf.contract.lock(token, amount, forTime)
The time limit specified when locking is a maximum - tokens can always be unlocked after this time limit (in hours) expires. In order to unlock tokens before this expires, you must request a signed permission from Deversifi.
This is always returned if you have no orders open involving those tokens.
const symbol = 'ZRXETH'
const amount = -15
const price = 0.0025
const orderId = await dvf.submitOrder(symbol, amount, price)
Orders are generated and submitted, returning either an orderId
or error. A
full list of possible errors and their associated explanation is available here.
When submitting this order we use the 3 first parameters:
symbol
is the pair which you wish to tradeamount
is specified in the first currency in the symbol (i.e. ZRXETH). For a sell, amount is negative. Amount accepts either maximum 8 d.p, or as many decimals as are available on the relevant token's smart contract if it is fewer than 8.price
is specified in the second currency in the symbol (i.e. ZRXETH). Prices should be specified to 5 s.f. maximum.
Warning: Trustless orders will always be settled at the exact price you specify, and can never be adjusted by Deversifi, even if it is at a worse price than the market.
For example, when placing a sell order, if the price
specified is below the highest bid available on the order book, the order will be executed instantly. However, the amount you receive will reflect only the price
that you entered, and not the market price at the time of execution.
The client library also provides methods for submitBuyOrder and submitSellOrder.
You can additionally provide
gid
- Group ID for your ordercid
- Client order IDsignedOrder
- A previously signed order, in case you're handling signingvalidFor
- optional amount of hours this order will be valid for, default to 3600 seconds as specified on the default configuration
The XXX/USDT markets on Trustless build on the liquidity of XXX/USD markets on the centralised exchanges of Bitfinex and Deversifi. However since there is often not a direct 1:1 rate between USD and USDT, a shift must be applied to the order books.
The configuration for Trustless returns a settleSpread
parameter:
"USDT":{
"decimals":6,
"wrapperAddress":"0x83e42e6d1ac009285376340ef64bac1c7d106c89",
"tokenAddress":"0x0736d0c130b2ead47476cc262dbed90d7c4eeabd",
"minOrderSize":10,
"settleSpread": 0.02
}
This settleSpread
is indicative of the current USDT/USD exchange rate. When orders are placed
on USDT markets, the settlement price in the signed order must be shifted by the settleSpread
parameter before the order is accepted.
For example, if placing a buy order on the ETH/USD(T) market at a price of 100 USD relative to the centralised exchange the order will be settled on Trustless at a price of 102 USDT. Equally a sell order at 100 USD would receive 102 USDT when settled on Trustless.
dvf.submitOrder(symbol, amount, price) // => settlementPrice = price * (1 + settleSpread)
The settleSpread
parameter is set dynamically as a 30 minute rolling mean of the USDT/USD
market exchange rate. When placing orders using submitOrder
or generating them with
createOrder
the shift is applied for you.
Cancelling orders requires the orderId
you wish to cancel to be signed by the
address which created and placed the order.
In case you're not signing the requests yourself
await dvf.cancelOrder(orderId)
In case you're signing the requests yourself:
const sig = await dvf.sign(parseInt(orderId).toString(16))
const sigConcat = ethUtils.toRpcSig(sig.v, ethUtils.toBuffer(sig.r), ethUtils.toBuffer(sig.s))
await dvf.cancelOrder(parseInt(orderId), sigConcat)
If you already have an unlocked wallet available to web3 to use for signing, you can simply get open orders and order history from the API as follows:
// Get all open orders
const openOrders = await dvf.getOrders()
// Get all historical orders
const historicalOrders = await dvf.getOrdersHist()
If an unlocked account is not available to sign with, for example when using a
raw private key or hardware wallet, authentication nonce
and signature
must be
pre-signed and passed into the calls. nonce
is required to be a timestamp less
than 3 hours in the future. signature
is the nonce
signed using the relevant
private key for the address who's orders you wish to view.
const ethUtils = require('ethereumjs-utils')
const privKey = /* Your Private Key */
const nonce = ((Date.now() / 1000) + 10800) + ''
const hash = ethUtils.hashPersonalMessage(ethUtils.toBuffer(nonce.toString(16)))
const signature = ethUtils.ecsign(hash, privKey)
// Get all open orders
const openOrders = await dvf.getOrders(null, null, nonce, signature)
// Get all historical orders
const historicalOrders = await dvf.getOrdersHist(null, null, nonce, signature)
If tokens are not used in active orders they can always be unlocked. If unlocking after the time specified when locking has expired, no permission is required. When unlocking before this, Deversifi must sign a release permission, after verifying that you have no orders currently active which require that token.
If you need permission the library will automatically call the expected endpoint on Deversifi API to ask for such permission.
const token = 'ZRX'
const amount = 15
const response = await dvf.contract.unlock(token, amount)
When a particular token's lock time has not yet expired, permission is required from Deversifi to unlock early. This permission can be requested directly from Deversifi using an API call.
The request must be authenticated using a nonce and signature, and the response contains a signed permission from Deversifi. This permission will always be granted if Deversifi is online and your address has no open orders involving those tokens. In case you're signing the requests yourself you could use the following code:
// This example shows how to generate the signature from a raw private key
// Signing using hardware wallets such as Ledger or Trezor can be done using their documentation
const ethUtils = require('ethereumjs-utils')
const privKey = /* Your Private Key */
const nonce = ((Date.now() / 1000) + 350) + ''
const hash = ethUtils.hashPersonalMessage(ethUtils.toBuffer(nonce.toString(16)))
const signature = ethUtils.ecsign(hash, privKey)
const response = await dvf.contract.unlock(token, amount, nonce, signature)
const token = 'ZRX'
const amount = 0.001
const response = await dvf.contract.unlock(token, amount, forTime)
Aside from these examples, there are complete examples in the examples folder
const symbol = 'ETHUSD'
const amount = 1
const price = 100
dvf.submitOrder(symbol, amount, price)
const symbol = 'ETHUSD'
const amount = -1
const price = 100
const orderId = await dvf.submitOrder(symbol, amount, price)
const id = 1
const order = await dvf.getOrder(id)
A list of error codes returned by the API and reasons are available here. Some more detailed explanations can also be found in the API Documentation.
If you have suggestions to improve this guide or any of the available documentation, please raise an issue on Github, or email feedback@Deversifi.com.
git clone
npm install
bash <(curl https://get.parity.io -L) # install parity
On kovan:
parity --chain kovan --jsonrpc-apis=all --geth
- note the jsonrpc set to all
- note the
--geth
in order to be compatible withgeth
's unlock 'duration' parameter
On ropsten:
geth --testnet --fast --bootnodes "enode://20c9ad97c081d63397d7b685a412227a40e23c8bdc6688c6f37e97cfbc22d2b4d1db1510d8f61e6a8866ad7f0e17c02b14182d37ea7c3c8b9c2683aeb6b733a1@52.169.14.227:30303,enode://6ce05930c72abc632c58e2e4324f7c7ea478cec0ed4fa2528982cf34483094e9cbc9216e7aa349691242576d552a2a56aaeae426c5303ded677ce455ba1acd9d@13.84.180.240:30303" --rpc --rpccorsdomain "*" --rpcapi "eth,web3,personal,net"
Alternatively, thanks to ganache-cli we can easily run an eth rpc node emulator. (NOTE: currently tests will fail using ganache)
npm test:rpc
Starting by watching the test files ( you will need a node running )
$ npm run test:watch
- Write the tests for your new features on the
./test/
- Add your tests to './test/index.js' file if necessary
- Create your features on ./src/ folder
- You will need a ropsten node to do blockchain related tests
$ npm run test
$ npm run test:web
- Very useful in case you want to issue commands from Google Chrome while using MetaMask !
$ npm run build:web:run
- Open your browser on http://localhost:2222
- This will build the whole library as one big ugly standalone js file ( uses browserify )
$ npm run build
-
Allow blockchain tests without relying on a local testnet node by using npm run test:rpc ( ganache ) and deploying mocked contracts at the beginning of the test.
-
Setup Travis-ci to test node.js, browser and standalone build. see this page
MIT