├── release-info └── latest-release-title ├── src ├── lib │ ├── dvf │ │ ├── token │ │ │ ├── splitSymbol.js │ │ │ ├── toQuantizedAmount.js │ │ │ ├── maxQuantizedDecimalPlaces.js │ │ │ ├── fromBaseUnitAmount.js │ │ │ ├── toBaseUnitAmount.js │ │ │ ├── getTokenInfo.js │ │ │ ├── getTokenInfoForChainOrThrow.js │ │ │ ├── fromQuantizedAmount.js │ │ │ ├── getTokenInfoOrThrow.js │ │ │ ├── getTokenInfoByTokenId.js │ │ │ ├── getTokenAddressFromTokenInfoOrThrow.js │ │ │ └── getSafeQuantizedAmountOrThrow.js │ │ ├── generateRandomNonceV2.js │ │ ├── bfxToDvfSymbol.js │ │ ├── dvfToBfxSymbol.js │ │ ├── generateRandomNonce.js │ │ ├── getExpirationTimestampInHours.js │ │ ├── computeBuySellData.js │ │ ├── delete-generic.js │ │ ├── delete-authenticated.js │ │ ├── post-authenticated.js │ │ ├── get-authenticated.js │ │ ├── get-generic.js │ │ ├── post-generic.js │ │ ├── addNonceAndExpirationTimestamp.js │ │ ├── getBridgeContractAddressOrThrow.js │ │ ├── makeAuthHeaders.js │ │ ├── createSignedTransaction.js │ │ ├── request.js │ │ ├── createSignedTransfer.js │ │ ├── addAuthHeadersOrData.js │ │ ├── DVFError.js │ │ ├── makeCreateSignedTransferTx.js │ │ ├── createOrderMetaData.js │ │ └── createMarketOrderMetaData.js │ ├── validators │ │ ├── cid.js │ │ ├── id.js │ │ ├── price.js │ │ ├── orderId.js │ │ ├── deFiSignature.js │ │ ├── starkPublicKey.js │ │ ├── starkKeyPair.js │ │ ├── starkPrivateKey.js │ │ ├── withdrawalId.js │ │ ├── token.js │ │ ├── amount.js │ │ ├── ethAddress.js │ │ ├── nonce.js │ │ ├── symbol.js │ │ ├── validateWithJoi.js │ │ ├── validateProps.js │ │ └── validateAssertions.js │ ├── keystore │ │ └── createWithNewKey.js │ ├── util │ │ ├── BN.js │ │ ├── prepareDepositAmount.js │ │ ├── permitParamsToArgs.js │ │ ├── extractOrderIdsInput.js │ │ └── createPromiseAndCallbackFn.js │ ├── ledger │ │ ├── generateTestNetworkTokenData.js │ │ ├── selectTransport.js │ │ └── makeCreateSignedTransferTxLedger.js │ ├── stark │ │ ├── createPrivateKey.js │ │ ├── formatStarkPublicKey.js │ │ ├── ledger │ │ │ ├── getBits.js │ │ │ ├── createDepositData.js │ │ │ ├── createWithdrawalData.js │ │ │ ├── getPath.test.js │ │ │ ├── getPublicKey.js │ │ │ ├── getPath.js │ │ │ └── getPublicKey.test.js │ │ ├── calculateFact.js │ │ ├── signAmmFundingOrder.js │ │ ├── createTransferMessage.js │ │ ├── createOrderMessage.js │ │ ├── starkSign.js │ │ ├── starkSignAuth.js │ │ └── calculateFact.test.js │ ├── schemas │ │ └── permitParamsSchema.js │ └── wallet │ │ └── attachStarkProvider.js ├── api │ ├── account │ │ ├── balance.js │ │ ├── getReferralRewards.js │ │ ├── getReferralId.js │ │ ├── getRemainingSpins.js │ │ ├── unlock.js │ │ ├── postReferralSpin.js │ │ ├── tokenBalance.js │ │ ├── permissions.js │ │ ├── select.js │ │ └── permissions.test.js │ ├── sign │ │ ├── request.js │ │ ├── nonceSignature.js │ │ └── sign.js │ ├── migrationStampede │ │ ├── getPotValue.js │ │ ├── getStampedeConfig.js │ │ ├── getMissionsConfig.js │ │ ├── getUserReward.js │ │ └── getUserMissions.js │ ├── fastWithdrawalFee.js │ ├── getBalanceUsd.js │ ├── contract │ │ ├── getStarkKey.js │ │ ├── sendToStarkExContract.js │ │ ├── getNameForAddress.js │ │ ├── sendToDVFInterface.js │ │ ├── getPermitNonceForAddress.js │ │ ├── getPermitNonceWithUnderscoreForAddress.js │ │ ├── depositCancel.js │ │ ├── depositReclaim.js │ │ ├── withdraw.js │ │ ├── abi │ │ │ ├── WithdrawalBalanceReader.abi.js │ │ │ └── tokenFaucet.abi.js │ │ ├── fullWithdrawalRequest.js │ │ ├── depositFromSidechainBridge.js │ │ ├── register.js │ │ ├── getAllWithdrawalBalancesEthAddress.js │ │ ├── isApproved.js │ │ ├── getAllWithdrawalBalances.js │ │ ├── getWithdrawalBalance.js │ │ ├── getWithdrawalBalanceEthAddress.js │ │ ├── depositFromStarkTx.js │ │ ├── depositFromProxiedStarkTx.js │ │ └── deposit.js │ ├── get30DaysVolume.js │ ├── getUserConfig.js │ ├── getUserConfigFromServer.js │ ├── getVaultIdFromServer.js │ ├── fastWithdrawalMaxAmount.js │ ├── getPublicPermissions.js │ ├── fastWithdrawal.js │ ├── userVerification │ │ ├── isUserVerified.js │ │ ├── verifyCode.js │ │ └── setEmailOrPhone.js │ ├── ledger │ │ ├── transferAndWithdraw.js │ │ ├── transferUsingVaultIdAndStarkKey.js │ │ ├── withdraw.js │ │ ├── deposit.js │ │ ├── signEIP712Data.js │ │ └── transfer.js │ ├── getTokenHolders.js │ ├── getTokenLiquidityLeft.js │ ├── getTokenSaleStartEnd.js │ ├── transferUsingVaultIdAndStarkKey.js │ ├── getTickers.js │ ├── test │ │ ├── helpers │ │ │ ├── makeQueryValidator.js │ │ │ ├── instance.js │ │ │ └── compile.js │ │ └── fixtures │ │ │ └── feeRate.js │ ├── eth │ │ ├── getWeb3ForChain.js │ │ ├── getGasPrice.js │ │ ├── call.js │ │ ├── getGasStationPrice.js │ │ ├── getNetwork.js │ │ └── send.js │ ├── getMinMaxOrderSize.js │ ├── cancelWithdrawal.js │ ├── estimatedNextBatchTime.js │ ├── getWithdrawal.js │ ├── getDeposits.js │ ├── getOrders.js │ ├── getOrdersHist.js │ ├── getConfig.js │ ├── submitBuyOrder.js │ ├── fullWithdrawalRequest.js │ ├── submitSellOrder.js │ ├── withdrawOnchain.js │ ├── getVaultId.js │ ├── cancelOpenOrders.js │ ├── getBalance.js │ ├── amm │ │ ├── poolAPY.js │ │ ├── poolTVL.js │ │ ├── poolSwapFees.js │ │ ├── poolTokensRate.js │ │ ├── poolStoredTokens.js │ │ ├── poolVolume24Hours.js │ │ ├── poolsData.js │ │ ├── getRewardsLockedState.js │ │ ├── poolUserRewards.js │ │ ├── poolUserLpBalance.js │ │ ├── poolUserAccruedFees.js │ │ ├── poolTvlHistory.js │ │ ├── poolsUserData.js │ │ ├── postRewardsLockedState.js │ │ ├── postAmmFundingOrders.js │ │ ├── getAmmFundingOrderData.js │ │ ├── applyFundingOrderDataSlippage.js │ │ ├── getAmmFundingOrders.js │ │ └── schemas.js │ ├── getRegistrationStatuses.js │ ├── airdropEligibility.js │ ├── getOrder.js │ ├── cancelOrder.js │ ├── walletFailedEvent.js │ ├── walletSuccessEvent.js │ ├── withdrawOnchain.test.js │ ├── walletConnect │ │ └── getPublicKey.js │ ├── getVaultIdAndStarkKey.js │ ├── topPerformersTokens.js │ ├── getGasPrice.js │ ├── getFeeRate.js │ ├── getTokenHolders.test.js │ ├── getTokenLiquidityLeft.test.js │ ├── getGasPrice.test.js │ ├── register.js │ ├── getTokenSaleStartEnd.test.js │ ├── bitfinex │ │ └── transfers.js │ ├── airdropEligibility.test.js │ ├── getBalanceUsd.test.js │ ├── transfer.js │ ├── get30DaysVolume.test.js │ ├── withdrawV2.js │ ├── transferAndWithdraw.js │ ├── estimatedNextBatchTime.test.js │ └── getMinMaxOrderSize.test.js └── config.js ├── examples ├── src │ ├── fastWithdrawalFee.js │ ├── fastWithdrawalMaxAmount.js │ ├── getVaultId.js │ ├── fullWithdrawalRequest.js │ ├── withdrawOnChain.js │ ├── register.js │ ├── transfer.js │ ├── getRegistrationStatuses.js │ ├── getWithdrawals.js │ ├── transferAndWithdraw.js │ ├── fastWithdrawal.js │ ├── getOrders.js │ ├── withdraw.js │ ├── authWithTradingKey.js │ ├── ledgerWithdraw.js │ ├── deposit.js │ ├── getOrder.js │ ├── getOrders_forSymbolPair.js │ ├── ledgerDeposit.js │ ├── publicPermissions.js │ ├── ledgerSubmitOrder.js │ ├── getWithdrawal.js │ ├── cancelWithdrawal.js │ ├── submitOrder.js │ ├── cancelOrder.js │ └── ammDeposit.js ├── helpers │ ├── methodWithNoArgsTemplate.js.tmpl │ ├── saveAsJson.js │ ├── makeExampleFileName.js │ ├── getPriceFromOrderBook.js │ ├── getWeb3.js │ ├── logMyIP.js │ ├── logExampleResult.js │ ├── waitForDepositCreditedOnChain.js │ ├── getOrCreateActiveOrder.js │ ├── examplesList.js │ ├── example.js.tmpl │ ├── loadFromEnvOrConfig.js │ └── buildExamples.js ├── checkEthAccountBalance.js ├── 09.getConfig.js ├── 04.getBalance.js ├── 25.fastWithdrawalFee.js ├── 03.getDeposits.js ├── 26.fastWithdrawalMaxAmount.js ├── 11.getOrdersHist.js ├── 12.getUserConfig.js ├── 19.getVaultId.js ├── 18.fullWithdrawalRequest.js ├── 17.withdrawOnChain.js ├── 01.register.js ├── 31.transfer.js ├── 32.getRegistrationStatuses.js ├── 29.transferAndWithdraw.js ├── 15.getWithdrawals.js ├── 14.fastWithdrawal.js ├── 06.getOrders.js └── 13.withdraw.js ├── .envrc ├── .editorconfig ├── shell.nix ├── .gitignore ├── shell-private.nix ├── nix ├── packages │ ├── append-dev-to-node-package-version.nix │ ├── gh-md-toc.nix │ └── yarn-berry.nix ├── pkgs.nix ├── overlays.nix └── netrc-create.sh ├── .eslintrc.js ├── env └── test ├── .yarnrc.yml └── package.json /release-info/latest-release-title: -------------------------------------------------------------------------------- 1 | Major release for cross-chain -------------------------------------------------------------------------------- /src/lib/dvf/token/splitSymbol.js: -------------------------------------------------------------------------------- 1 | module.exports = symbol => symbol.split(':') -------------------------------------------------------------------------------- /examples/src/fastWithdrawalFee.js: -------------------------------------------------------------------------------- 1 | const response = await rhinofi.fastWithdrawalFee('ETH') 2 | 3 | logExampleResult(response) 4 | -------------------------------------------------------------------------------- /src/lib/dvf/generateRandomNonceV2.js: -------------------------------------------------------------------------------- 1 | module.exports = () => { 2 | return Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER) 3 | } 4 | -------------------------------------------------------------------------------- /examples/src/fastWithdrawalMaxAmount.js: -------------------------------------------------------------------------------- 1 | const response = await rhinofi.fastWithdrawalMaxAmount('ETH') 2 | 3 | logExampleResult(response) 4 | -------------------------------------------------------------------------------- /src/lib/dvf/bfxToDvfSymbol.js: -------------------------------------------------------------------------------- 1 | const { bfxToDvfSymbol } = require('@rhino.fi/dvf-utils') 2 | 3 | module.exports = (bfxSymbol) => bfxToDvfSymbol(bfxSymbol) 4 | -------------------------------------------------------------------------------- /src/lib/dvf/dvfToBfxSymbol.js: -------------------------------------------------------------------------------- 1 | const { dvfToBfxSymbol } = require('@rhino.fi/dvf-utils') 2 | 3 | module.exports = (dvfSymbol) => dvfToBfxSymbol(dvfSymbol) 4 | -------------------------------------------------------------------------------- /examples/src/getVaultId.js: -------------------------------------------------------------------------------- 1 | const token = 'ETH' 2 | 3 | const getVaultIdResponse = await rhinofi.getVaultId(token) 4 | 5 | logExampleResult(getVaultIdResponse) 6 | -------------------------------------------------------------------------------- /src/api/account/balance.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Returns ETH balance 3 | */ 4 | module.exports = (dvf) => { 5 | return dvf.web3.eth.getBalance(dvf.get('account')) 6 | } 7 | -------------------------------------------------------------------------------- /examples/helpers/methodWithNoArgsTemplate.js.tmpl: -------------------------------------------------------------------------------- 1 | const {{{METHOD_NAME}}}Response = await rhinofi.{{{METHOD_NAME}}}() 2 | 3 | logExampleResult({{{METHOD_NAME}}}Response) 4 | -------------------------------------------------------------------------------- /examples/src/fullWithdrawalRequest.js: -------------------------------------------------------------------------------- 1 | const token = 'ETH' 2 | 3 | const withdrawalResponse = await rhinofi.fullWithdrawalRequest(token) 4 | 5 | logExampleResult(withdrawalResponse) 6 | -------------------------------------------------------------------------------- /src/lib/validators/cid.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../dvf/DVFError') 2 | 3 | module.exports = (dvf, cid) => { 4 | if (!cid) { 5 | throw new DVFError('ERR_INVALID_CID') 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/lib/validators/id.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../dvf/DVFError') 2 | 3 | module.exports = (dvf, id) => { 4 | if (!id) { 5 | throw new DVFError('ERR_INVALID_ID') 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /examples/src/withdrawOnChain.js: -------------------------------------------------------------------------------- 1 | const token = 'ETH' 2 | 3 | const withdrawalResponse = await rhinofi.withdrawOnchain(token, rhinofi.config.ethAddress) 4 | 5 | logExampleResult(withdrawalResponse) -------------------------------------------------------------------------------- /src/lib/validators/price.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../dvf/DVFError') 2 | 3 | module.exports = (dvf, price) => { 4 | if (!price) { 5 | throw new DVFError('ERR_PRICE_MISSING') 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/lib/dvf/generateRandomNonce.js: -------------------------------------------------------------------------------- 1 | // This is max nonce value accepted by sw. 2 | const nonceMax = Math.pow(2, 31) - 1 3 | module.exports = () => { 4 | return Math.ceil(Math.random() * nonceMax) 5 | } 6 | -------------------------------------------------------------------------------- /src/lib/keystore/createWithNewKey.js: -------------------------------------------------------------------------------- 1 | const keystore = require('./index') 2 | const createPrivateKey = require('../stark/createPrivateKey') 3 | 4 | module.exports = sw => keystore(sw)(createPrivateKey()) 5 | -------------------------------------------------------------------------------- /src/lib/util/BN.js: -------------------------------------------------------------------------------- 1 | const BigNumber = require('bignumber.js') 2 | 3 | BigNumber.config({ 4 | DECIMAL_PLACES: 50, 5 | ROUNDING_MODE: BigNumber.ROUND_HALF_UP 6 | }) 7 | 8 | module.exports = BigNumber 9 | -------------------------------------------------------------------------------- /src/lib/ledger/generateTestNetworkTokenData.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = (tokenAddress, chainId) => ({ 3 | data: Buffer.from( 4 | `00${tokenAddress}000000000000000${chainId}`, 5 | 'hex' 6 | ) 7 | }) 8 | -------------------------------------------------------------------------------- /src/lib/validators/orderId.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../dvf/DVFError') 2 | 3 | module.exports = (dvf, orderId) => { 4 | if (!orderId) { 5 | throw new DVFError('ERR_INVALID_ORDER_ID') 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /examples/src/register.js: -------------------------------------------------------------------------------- 1 | const keyPair = await rhinofi.stark.createKeyPair(starkPrivKey) 2 | 3 | const registerResponse = await rhinofi.register(keyPair.starkPublicKey) 4 | 5 | logExampleResult(registerResponse) 6 | -------------------------------------------------------------------------------- /src/lib/validators/deFiSignature.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../dvf/DVFError') 2 | 3 | module.exports = (dvf, signature) => { 4 | if (!signature) { 5 | throw new DVFError('ERR_SIGNATURE_MISSING') 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /examples/src/transfer.js: -------------------------------------------------------------------------------- 1 | const transferResponse = await rhinofi.transfer({ 2 | recipientEthAddress: rhinofi.config.DVF.deversifiAddress, 3 | token: 'USDT', 4 | amount: 5 5 | }) 6 | 7 | logExampleResult(transferResponse) -------------------------------------------------------------------------------- /src/lib/validators/starkPublicKey.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../dvf/DVFError') 2 | 3 | module.exports = (dvf, tradingKey) => { 4 | if (!tradingKey) { 5 | throw new DVFError('ERR_STARK_KEY_MISSING') 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | # NOTE: this requires access to some private repos 2 | # remove --shell-file argument to use ./shell.nix which 3 | # does not depend on any public repositories. 4 | eval "$(lorri direnv --shell-file ./shell-private.nix)" 5 | -------------------------------------------------------------------------------- /src/api/sign/request.js: -------------------------------------------------------------------------------- 1 | module.exports = (dvf, contents) => { 2 | return { 3 | // headers: new window.Headers(), 4 | // mode: 'cors', 5 | // cache: 'default' 6 | json: JSON.stringify({contents}) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/lib/validators/starkKeyPair.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../dvf/DVFError') 2 | 3 | module.exports = (dvf, starkKeyPair) => { 4 | if (!starkKeyPair) { 5 | throw new DVFError('ERR_STARK_KEY_PAIR_MISSING') 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/lib/validators/starkPrivateKey.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../dvf/DVFError') 2 | 3 | module.exports = (dvf, tradingKey) => { 4 | if (!tradingKey) { 5 | throw new DVFError('ERR_STARK_PRIVATE_KEY_MISSING') 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/lib/validators/withdrawalId.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../dvf/DVFError') 2 | 3 | module.exports = (dvf, withdrawalId) => { 4 | if (!withdrawalId) { 5 | throw new DVFError('ERR_INVALID_WITHDRAWAL_ID') 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/lib/dvf/getExpirationTimestampInHours.js: -------------------------------------------------------------------------------- 1 | const msPerHour = 1000 * 3600 2 | 3 | module.exports = expireInHours => { 4 | const nowHours = Math.ceil(Date.now() / msPerHour) 5 | 6 | return nowHours + parseInt(expireInHours) 7 | } 8 | -------------------------------------------------------------------------------- /examples/src/getRegistrationStatuses.js: -------------------------------------------------------------------------------- 1 | const registrationStatusesResponse = await rhinofi.getRegistrationStatuses({ 2 | targetEthAddress: '0x08152c1265dbc218ccc8ab5c574e6bd52279b3b7' 3 | }) 4 | 5 | logExampleResult(registrationStatusesResponse) -------------------------------------------------------------------------------- /src/api/migrationStampede/getPotValue.js: -------------------------------------------------------------------------------- 1 | const getGeneric = require('../../lib/dvf/get-generic') 2 | 3 | module.exports = async (dvf) => { 4 | const endpoint = '/v1/trading/r/stampedePotValue' 5 | return getGeneric(dvf, endpoint) 6 | } 7 | -------------------------------------------------------------------------------- /src/api/migrationStampede/getStampedeConfig.js: -------------------------------------------------------------------------------- 1 | const getGeneric = require('../../lib/dvf/get-generic') 2 | 3 | module.exports = async (dvf) => { 4 | const endpoint = '/v1/trading/r/stampedeConfig' 5 | return getGeneric(dvf, endpoint) 6 | } 7 | -------------------------------------------------------------------------------- /src/api/fastWithdrawalFee.js: -------------------------------------------------------------------------------- 1 | const { get } = require('request-promise') 2 | 3 | module.exports = async (dvf, token) => { 4 | const url = dvf.config.api + '/v1/trading/r/fastWithdrawalFee' 5 | return get(url, { json: true, qs: { token } }) 6 | } 7 | -------------------------------------------------------------------------------- /src/api/getBalanceUsd.js: -------------------------------------------------------------------------------- 1 | const get = require('../lib/dvf/get-authenticated') 2 | 3 | module.exports = async (dvf, nonce, signature) => { 4 | const endpoint = '/v1/trading/r/getBalanceUsd' 5 | return get(dvf, endpoint, nonce, signature) 6 | } 7 | -------------------------------------------------------------------------------- /src/api/contract/getStarkKey.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../../lib/dvf/DVFError') 2 | const BN = require('bignumber.js') 3 | 4 | module.exports = async dvf => { 5 | throw new Error('getStarkKey contract method has been removed in StarkExV2') 6 | } 7 | -------------------------------------------------------------------------------- /src/api/get30DaysVolume.js: -------------------------------------------------------------------------------- 1 | const get = require('../lib/dvf/get-authenticated') 2 | 3 | module.exports = async (dvf, nonce, signature) => { 4 | const endpoint = '/v1/trading/r/30DaysVolume' 5 | 6 | return get(dvf, endpoint, nonce, signature) 7 | } 8 | -------------------------------------------------------------------------------- /src/api/migrationStampede/getMissionsConfig.js: -------------------------------------------------------------------------------- 1 | const getGeneric = require('../../lib/dvf/get-generic') 2 | 3 | module.exports = async (dvf) => { 4 | const endpoint = '/v1/trading/r/stampedeMissionsConfig' 5 | return getGeneric(dvf, endpoint) 6 | } 7 | -------------------------------------------------------------------------------- /src/lib/util/prepareDepositAmount.js: -------------------------------------------------------------------------------- 1 | const { prepareAmount, BN } = require('@rhino.fi/dvf-utils') 2 | 3 | module.exports = (dvf, amount, token) => prepareAmount( 4 | amount, 5 | dvf.token.maxQuantizedDecimalPlaces(token), 6 | BN.ROUND_FLOOR 7 | ) 8 | -------------------------------------------------------------------------------- /src/api/contract/sendToStarkExContract.js: -------------------------------------------------------------------------------- 1 | module.exports = dvf => action => (args, value, options) => dvf.eth.send( 2 | dvf.contract.abi.getStarkEx(), 3 | dvf.config.DVF.starkExContractAddress, 4 | action, 5 | args, 6 | value, 7 | options 8 | ) 9 | -------------------------------------------------------------------------------- /src/api/getUserConfig.js: -------------------------------------------------------------------------------- 1 | module.exports = async (dvf, nonce, signature) => { 2 | const exchangeUserConf = await dvf.getUserConfigFromServer(nonce, signature) 3 | 4 | Object.assign(dvf.config, exchangeUserConf) 5 | 6 | return exchangeUserConf 7 | } 8 | -------------------------------------------------------------------------------- /src/lib/stark/createPrivateKey.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto') 2 | 3 | // TODO: Add check to ensure private key generated 4 | // is less than penderson EC_ORDER 5 | module.exports = () => { 6 | return crypto.randomBytes(31).toString('hex') 7 | } 8 | -------------------------------------------------------------------------------- /src/lib/stark/formatStarkPublicKey.js: -------------------------------------------------------------------------------- 1 | /* 2 | ensure stark Key is always 64 bits 3 | */ 4 | module.exports = (starkPublicKey) => { 5 | return { 6 | x: starkPublicKey.x.padStart(64, '0').substr(-64), 7 | y: starkPublicKey.y 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/helpers/saveAsJson.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | 3 | module.exports = (filePath, content, stringifyOpts = [null, 2 ]) => { 4 | return fs.writeFileSync( 5 | filePath, 6 | JSON.stringify.apply(JSON, [content].concat(stringifyOpts)) 7 | ) 8 | } -------------------------------------------------------------------------------- /src/api/getUserConfigFromServer.js: -------------------------------------------------------------------------------- 1 | const post = require('../lib/dvf/post-authenticated') 2 | 3 | module.exports = async (dvf, nonce, signature) => { 4 | const endpoint = '/v1/trading/r/getUserConf' 5 | 6 | return post(dvf, endpoint, nonce, signature) 7 | } 8 | -------------------------------------------------------------------------------- /src/lib/schemas/permitParamsSchema.js: -------------------------------------------------------------------------------- 1 | const { Joi } = require('@rhino.fi/dvf-utils') 2 | 3 | module.exports = Joi.object({ 4 | permitValue: Joi.string(), 5 | deadline: Joi.number(), 6 | v: Joi.any(), 7 | r: Joi.any(), 8 | s: Joi.any() 9 | }) 10 | -------------------------------------------------------------------------------- /src/lib/util/permitParamsToArgs.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = permitParams => { 3 | if (!permitParams) { 4 | return [] 5 | } 6 | const {permitValue, deadline, v, r, s} = permitParams 7 | return [ 8 | permitValue, deadline, v, r, s 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /src/api/account/getReferralRewards.js: -------------------------------------------------------------------------------- 1 | const getGeneric = require('../../lib/dvf/get-generic') 2 | 3 | module.exports = async dvf => { 4 | const endpoint = '/v1/trading/r/referralRewards' 5 | const response = await getGeneric(dvf, endpoint) 6 | return response 7 | } 8 | -------------------------------------------------------------------------------- /src/api/getVaultIdFromServer.js: -------------------------------------------------------------------------------- 1 | const validateAssertions = require('../lib/validators/validateAssertions') 2 | 3 | module.exports = (dvf, token, nonce, signature) => dvf.postAuthenticated( 4 | '/v1/trading/r/getVaultId', 5 | nonce, 6 | signature, 7 | { token } 8 | ) 9 | -------------------------------------------------------------------------------- /src/lib/dvf/computeBuySellData.js: -------------------------------------------------------------------------------- 1 | const { computeBuySellData } = require('@rhino.fi/dvf-utils') 2 | 3 | const DVFError = require('./DVFError') 4 | 5 | module.exports = (dvf, order) => computeBuySellData({ 6 | DVFError, 7 | getTokenInfo: dvf.token.getTokenInfo 8 | })(order) 9 | -------------------------------------------------------------------------------- /src/lib/dvf/token/toQuantizedAmount.js: -------------------------------------------------------------------------------- 1 | const { toQuantizedAmountString } = require('@rhino.fi/dvf-utils') 2 | 3 | module.exports = (dvf, token, amount) => { 4 | const tokenInfo = dvf.token.getTokenInfo(token) 5 | 6 | return toQuantizedAmountString(tokenInfo, amount) 7 | } 8 | -------------------------------------------------------------------------------- /src/api/contract/getNameForAddress.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Gets token name 3 | */ 4 | module.exports = (dvf, tokenAddress) => { 5 | const action = 'name' 6 | return dvf.eth.call( 7 | dvf.contract.abi.token, 8 | tokenAddress, 9 | action, 10 | [] 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /src/api/fastWithdrawalMaxAmount.js: -------------------------------------------------------------------------------- 1 | const get = require('../lib/dvf/get-authenticated') 2 | 3 | const endpoint = '/v1/trading/r/fastWithdrawalMaxAmount' 4 | 5 | module.exports = async (dvf, token, nonce, signature) => { 6 | return get(dvf, endpoint, nonce, signature, { token }) 7 | } 8 | -------------------------------------------------------------------------------- /examples/src/getWithdrawals.js: -------------------------------------------------------------------------------- 1 | // Get withtrawals for all tokens 2 | const token = undefined 3 | // And current user 4 | const userAddress = rhinofi.get('account') 5 | const getWithdrawalsResponse = await rhinofi.getWithdrawals(token, userAddress) 6 | 7 | logExampleResult(getWithdrawalsResponse) -------------------------------------------------------------------------------- /examples/src/transferAndWithdraw.js: -------------------------------------------------------------------------------- 1 | const token = 'ETH' 2 | const amount = 0.1 3 | 4 | const withdrawalResponse = await rhinofi.transferAndWithdraw({ 5 | recipientEthAddress: rhinofi.get('account'), 6 | token, 7 | amount, 8 | }) 9 | 10 | logExampleResult(withdrawalResponse) 11 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs ? import ./nix/pkgs.nix {} 3 | }: 4 | let 5 | shellPackage = { 6 | niv, 7 | nodejs, 8 | yarn-berry, 9 | gh-md-toc, 10 | }@deps: pkgs.mkShell { 11 | packages = builtins.attrValues deps; 12 | }; 13 | in 14 | pkgs.callPackage shellPackage {} -------------------------------------------------------------------------------- /examples/helpers/makeExampleFileName.js: -------------------------------------------------------------------------------- 1 | module.exports = (exampleName, index) => { 2 | // Offset by 1 since example 00 is reserved for the setup script. 3 | const idx = index + 1 4 | const indexString = idx < 10 ? `0${idx}` : idx.toString() 5 | return `${indexString}.${exampleName}.js` 6 | } -------------------------------------------------------------------------------- /src/api/getPublicPermissions.js: -------------------------------------------------------------------------------- 1 | const getGeneric = require('../lib/dvf/get-generic') 2 | 3 | /** 4 | * Get public permissions descriptor 5 | */ 6 | module.exports = (dvf) => { 7 | const endpoint = '/v1/trading/r/getPublicPermissions' 8 | 9 | return getGeneric(dvf, endpoint) 10 | } 11 | -------------------------------------------------------------------------------- /src/api/migrationStampede/getUserReward.js: -------------------------------------------------------------------------------- 1 | const getAuthenticated = require('../../lib/dvf/get-authenticated') 2 | 3 | module.exports = async (dvf, nonce, signature) => { 4 | const endpoint = '/v1/trading/r/userStampedeReward' 5 | return getAuthenticated(dvf, endpoint, nonce, signature) 6 | } 7 | -------------------------------------------------------------------------------- /src/api/contract/sendToDVFInterface.js: -------------------------------------------------------------------------------- 1 | module.exports = dvf => action => (sendArgsArray = [], value = null, options) => dvf.eth.send( 2 | dvf.contract.abi.getDVFInterface(), 3 | dvf.config.DVF.registrationAndDepositInterfaceAddress, 4 | action, 5 | sendArgsArray, 6 | value, 7 | options 8 | ) 9 | -------------------------------------------------------------------------------- /src/api/migrationStampede/getUserMissions.js: -------------------------------------------------------------------------------- 1 | const getAuthenticated = require('../../lib/dvf/get-authenticated') 2 | 3 | module.exports = async (dvf, nonce, signature) => { 4 | const endpoint = '/v1/trading/r/userStampedeMissions' 5 | return getAuthenticated(dvf, endpoint, nonce, signature) 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | package-lock.json 4 | yarn-error.log 5 | test/contracts/*.json 6 | .coverage 7 | examples/config.json 8 | *.sublime-project 9 | *.sublime-workspace 10 | examples/config*.json 11 | .idea/ 12 | 13 | # yarn berry 14 | .yarn/* 15 | !.yarn/plugins 16 | .pnp.* 17 | -------------------------------------------------------------------------------- /src/api/fastWithdrawal.js: -------------------------------------------------------------------------------- 1 | const { post } = require('request-promise') 2 | 3 | module.exports = async (dvf, withdrawalData) => { 4 | const url = dvf.config.api + '/v1/trading/w/fastWithdrawal' 5 | const json = await dvf.createFastWithdrawalPayload(withdrawalData) 6 | return post(url, { json }) 7 | } 8 | -------------------------------------------------------------------------------- /src/api/userVerification/isUserVerified.js: -------------------------------------------------------------------------------- 1 | const getAuthenticated = require('../../lib/dvf/get-authenticated') 2 | 3 | module.exports = async (dvf, nonce, signature) => { 4 | const endpoint = `/v1/trading/userVerification/isUserVerified` 5 | return getAuthenticated(dvf, endpoint, nonce, signature) 6 | } 7 | -------------------------------------------------------------------------------- /src/lib/dvf/delete-generic.js: -------------------------------------------------------------------------------- 1 | const { request } = require('@rhino.fi/dvf-utils') 2 | 3 | module.exports = async (dvf, endpoint, headers = {}) => { 4 | const url = dvf.config.api + endpoint 5 | 6 | const options = { 7 | headers 8 | } 9 | 10 | return request.delete(url, options) 11 | } 12 | -------------------------------------------------------------------------------- /examples/src/fastWithdrawal.js: -------------------------------------------------------------------------------- 1 | const fastWithdrawalResponse = await rhinofi.fastWithdrawal( 2 | // recipientEthAddress could be added here to send the withdrawal to address 3 | // other then users registered address. 4 | { token: 'ETH', amount: 0.005 } 5 | ) 6 | 7 | logExampleResult(fastWithdrawalResponse) 8 | -------------------------------------------------------------------------------- /src/lib/validators/token.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../dvf/DVFError') 2 | 3 | module.exports = (dvf, token) => { 4 | if (!token) { 5 | throw new DVFError('ERR_TOKEN_MISSING') 6 | } 7 | 8 | if (!dvf.token.getTokenInfo(token)) { 9 | throw new DVFError('ERR_INVALID_TOKEN') 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/api/account/getReferralId.js: -------------------------------------------------------------------------------- 1 | const getAuthenticated = require('../../lib/dvf/get-authenticated') 2 | 3 | module.exports = async (dvf, nonce, signature) => { 4 | const endpoint = '/v1/trading/r/referralId' 5 | const response = await getAuthenticated(dvf, endpoint, nonce, signature) 6 | return response 7 | } 8 | -------------------------------------------------------------------------------- /src/api/userVerification/verifyCode.js: -------------------------------------------------------------------------------- 1 | const postAuthenticated = require('../../lib/dvf/post-authenticated') 2 | 3 | module.exports = async (dvf, nonce, signature, data) => { 4 | const endpoint = '/v1/trading/userVerification/verifyCode' 5 | return postAuthenticated(dvf, endpoint, nonce, signature, data) 6 | } 7 | -------------------------------------------------------------------------------- /examples/src/getOrders.js: -------------------------------------------------------------------------------- 1 | const getOrCreateActiveOrder = require('./helpers/getOrCreateActiveOrder') 2 | 3 | // Ensure that there is at least one order to get. 4 | await getOrCreateActiveOrder(rhinofi, starkPrivKey) 5 | 6 | const getOrdersResponse = await rhinofi.getOrders() 7 | 8 | logExampleResult(getOrdersResponse) 9 | -------------------------------------------------------------------------------- /src/api/ledger/transferAndWithdraw.js: -------------------------------------------------------------------------------- 1 | const makeCreateSignedTransferTxLedger = require('../../lib/ledger/makeCreateSignedTransferTxLedger') 2 | 3 | module.exports = async (dvf, data, path, nonce, signature) => { 4 | return dvf.transferAndWithdraw(data, nonce, signature, makeCreateSignedTransferTxLedger(dvf)(path)) 5 | } 6 | -------------------------------------------------------------------------------- /src/api/getTokenHolders.js: -------------------------------------------------------------------------------- 1 | const { get } = require('request-promise') 2 | 3 | module.exports = async (dvf, token) => { 4 | if (token) { 5 | const response = await get( 6 | `${dvf.config.api}/v1/trading/r/getTokenHolders?token=${token}` 7 | ) 8 | return response 9 | } 10 | return null 11 | } 12 | -------------------------------------------------------------------------------- /src/api/userVerification/setEmailOrPhone.js: -------------------------------------------------------------------------------- 1 | const postAuthenticated = require('../../lib/dvf/post-authenticated') 2 | 3 | module.exports = async (dvf, nonce, signature, data) => { 4 | const endpoint = '/v1/trading/userVerification/setEmailOrPhone' 5 | return postAuthenticated(dvf, endpoint, nonce, signature, data) 6 | } 7 | -------------------------------------------------------------------------------- /src/lib/dvf/token/maxQuantizedDecimalPlaces.js: -------------------------------------------------------------------------------- 1 | const BN = require('bignumber.js') 2 | 3 | module.exports = (dvf, token) => { 4 | const tokenInfo = dvf.token.getTokenInfo(token) 5 | const quantizedUnit = new BN(10).pow(tokenInfo.decimals).div(tokenInfo.quantization).toString() 6 | return (quantizedUnit.length-1) 7 | } 8 | -------------------------------------------------------------------------------- /src/lib/ledger/selectTransport.js: -------------------------------------------------------------------------------- 1 | module.exports = isBrowser => { 2 | if (isBrowser) { 3 | const Transport = require('@ledgerhq/hw-transport-webhid').default 4 | return Transport 5 | } else { 6 | const Transport = require('@ledgerhq/hw-transport-node-hid').default 7 | return Transport 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/src/withdraw.js: -------------------------------------------------------------------------------- 1 | const token = 'ETH' 2 | const amount = 0.1 3 | 4 | // NOTE: withdraw method as been deprecated 5 | const withdrawalResponse = await rhinofi.transferAndWithdraw({ 6 | recipientEthAddress: rhinofi.get('account'), 7 | token, 8 | amount, 9 | }) 10 | 11 | logExampleResult(withdrawalResponse) 12 | -------------------------------------------------------------------------------- /src/api/account/getRemainingSpins.js: -------------------------------------------------------------------------------- 1 | const getAuthenticated = require('../../lib/dvf/get-authenticated') 2 | 3 | module.exports = async (dvf, nonce, signature) => { 4 | const endpoint = '/v1/trading/r/referralRemainingSpins' 5 | const response = await getAuthenticated(dvf, endpoint, nonce, signature) 6 | return response 7 | } 8 | -------------------------------------------------------------------------------- /src/api/account/unlock.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Unlocks an account for given duration 3 | **/ 4 | module.exports = (dvf, password, duration = 60) => { 5 | const {web3} = dvf 6 | 7 | // TODO: can we improve this somehow? 8 | return web3.eth.personal.unlockAccount( 9 | dvf.get('account'), 10 | password 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /src/api/getTokenLiquidityLeft.js: -------------------------------------------------------------------------------- 1 | const { get } = require('request-promise') 2 | 3 | module.exports = async (dvf, token) => { 4 | if (token) { 5 | const response = await get( 6 | `${dvf.config.api}/v1/trading/r/getTokenLiquidityLeft?token=${token}` 7 | ) 8 | return response 9 | } 10 | return null 11 | } 12 | -------------------------------------------------------------------------------- /src/api/getTokenSaleStartEnd.js: -------------------------------------------------------------------------------- 1 | const { get } = require('request-promise') 2 | 3 | module.exports = async (dvf, token) => { 4 | if (token) { 5 | const response = await get( 6 | `${dvf.config.api}/v1/trading/r/getTokenSaleStartEnd?token=${token}` 7 | ) 8 | return response 9 | } 10 | return null 11 | } 12 | -------------------------------------------------------------------------------- /src/lib/dvf/delete-authenticated.js: -------------------------------------------------------------------------------- 1 | const makeAuthHeaders = require('./makeAuthHeaders') 2 | const deleteGeneric = require('./delete-generic') 3 | 4 | module.exports = async (dvf, endpoint, nonce, signature) => { 5 | const headers = makeAuthHeaders(dvf, nonce, signature) 6 | 7 | return deleteGeneric(dvf, endpoint, headers) 8 | } 9 | -------------------------------------------------------------------------------- /src/lib/dvf/token/fromBaseUnitAmount.js: -------------------------------------------------------------------------------- 1 | const BN = require('bignumber.js') 2 | 3 | module.exports = (dvf, token, baseUnitAmount) => { 4 | const tokenInfo = dvf.token.getTokenInfo(token) 5 | 6 | return new BN(baseUnitAmount) 7 | .shiftedBy(-1 * tokenInfo.decimals) 8 | .decimalPlaces(3) 9 | .toString() 10 | } 11 | -------------------------------------------------------------------------------- /src/lib/dvf/token/toBaseUnitAmount.js: -------------------------------------------------------------------------------- 1 | const BN = require('bignumber.js') 2 | 3 | module.exports = (dvf, token, amount) => { 4 | const tokenInfo = dvf.token.getTokenInfo(token) 5 | 6 | return new BN(10) 7 | .pow(tokenInfo.decimals) 8 | .times(amount) 9 | .integerValue(BN.ROUND_FLOOR) 10 | .toString() 11 | } 12 | -------------------------------------------------------------------------------- /src/api/account/postReferralSpin.js: -------------------------------------------------------------------------------- 1 | const postAuthenticated = require('../../lib/dvf/post-authenticated') 2 | 3 | module.exports = async (dvf, user, nonce, signature) => { 4 | const endpoint = '/v1/trading/w/referralSpin' 5 | const response = await postAuthenticated(dvf, endpoint, nonce, signature, { user }) 6 | return response 7 | } 8 | -------------------------------------------------------------------------------- /src/api/transferUsingVaultIdAndStarkKey.js: -------------------------------------------------------------------------------- 1 | const { post } = require('request-promise') 2 | 3 | module.exports = async (dvf, transferData, feeRecipient) => { 4 | const url = dvf.config.api + '/v1/trading/w/transfer' 5 | 6 | const json = await dvf.createTransferPayload(transferData, feeRecipient) 7 | return post(url, { json }) 8 | } 9 | -------------------------------------------------------------------------------- /src/api/contract/getPermitNonceForAddress.js: -------------------------------------------------------------------------------- 1 | const permitTokenAbi = require('./abi/MintablePermitERC20.abi') 2 | /** 3 | * Gets token permission nonce for owner 4 | */ 5 | module.exports = (dvf, tokenAddress, ownerAddress) => 6 | dvf.eth.call( 7 | permitTokenAbi, 8 | tokenAddress, 9 | 'nonces', 10 | [ownerAddress] 11 | ) 12 | -------------------------------------------------------------------------------- /src/api/getTickers.js: -------------------------------------------------------------------------------- 1 | const { get } = require('request-promise') 2 | 3 | module.exports = async (dvf, symbols) => { 4 | if (symbols.constructor !== Array) { 5 | symbols = [symbols] 6 | } 7 | 8 | const response = await get(`${dvf.config.api}/market-data/tickers?symbols=${symbols.join(',')}`) 9 | return JSON.parse(response) 10 | } 11 | -------------------------------------------------------------------------------- /examples/src/authWithTradingKey.js: -------------------------------------------------------------------------------- 1 | rhinofi.config.useTradingKey = true 2 | 3 | const nonce = Date.now() / 1000 4 | const keyPair = sw.ec.keyFromPrivate(starkPrivKey, 'hex') 5 | const starkSignature = sw.sign(keyPair, nonce) 6 | 7 | const getDepositsResponse = await rhinofi.getDeposits(null, nonce, starkSignature) 8 | 9 | logExampleResult(getDepositsResponse) -------------------------------------------------------------------------------- /examples/src/ledgerWithdraw.js: -------------------------------------------------------------------------------- 1 | const path = `44'/60'/0'/0'/0` 2 | const token = 'ETH' 3 | const amount = 0.10 4 | 5 | const withdrawResponse = await rhinofi.ledger.transferAndWithdraw( 6 | { 7 | recipientEthAddress: getAddress(dvf), 8 | token, 9 | amount, 10 | }, 11 | path 12 | ) 13 | 14 | logExampleResult(withdrawResponse) 15 | -------------------------------------------------------------------------------- /src/lib/dvf/token/getTokenInfo.js: -------------------------------------------------------------------------------- 1 | const bfxTodvf = require('@rhino.fi/dvf-utils').BfxToDvfToken 2 | 3 | // TODO: Deprecated 4 | module.exports = (dvf, token) => { 5 | const tokenInfo = dvf.config.tokenRegistry[bfxTodvf(token)] 6 | // Returning the symbol along with the info 7 | if (tokenInfo) { 8 | return {token, ...tokenInfo} 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/lib/dvf/token/getTokenInfoForChainOrThrow.js: -------------------------------------------------------------------------------- 1 | const { merge } = require('lodash') 2 | 3 | module.exports = (dvf, token, chain) => { 4 | const result = dvf.token.getTokenInfoOrThrow(token) 5 | 6 | if (chain && result?.chainOverride?.[chain]) { 7 | return merge(result, result?.chainOverride[chain]) 8 | } 9 | 10 | return result 11 | } 12 | -------------------------------------------------------------------------------- /src/api/test/helpers/makeQueryValidator.js: -------------------------------------------------------------------------------- 1 | const url = require('url') 2 | 3 | module.exports = apiResponse => jest.fn((uri, body) => { 4 | const parsed = new url.URL(uri, 'http://example.com') 5 | expect(typeof parseInt(parsed.searchParams.get('nonce'))).toBe('number') 6 | expect(parsed.searchParams.get('signature')).toMatch(/[\da-f]/i) 7 | return [200, apiResponse] 8 | }) 9 | -------------------------------------------------------------------------------- /examples/src/deposit.js: -------------------------------------------------------------------------------- 1 | const waitForDepositCreditedOnChain = require('./helpers/waitForDepositCreditedOnChain') 2 | 3 | const depositResponse = await rhinofi.depositV2({ token: 'ETH', amount: 0.3 }) 4 | 5 | if (process.env.WAIT_FOR_DEPOSIT_READY === 'true') { 6 | await waitForDepositCreditedOnChain(rhinofi, depositResponse) 7 | } 8 | 9 | logExampleResult(depositResponse) 10 | -------------------------------------------------------------------------------- /shell-private.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs ? import ./nix/pkgs-private.nix {} 3 | }: 4 | let 5 | baseShell = import ./shell.nix { inherit pkgs; }; 6 | in 7 | pkgs.mkShell { 8 | inputsFrom = [ 9 | (baseShell.override { niv = pkgs.niv-with-gh-token; }) 10 | pkgs.dev-shell-base 11 | ]; 12 | packages = with pkgs; [ 13 | npm-publish 14 | ]; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /nix/packages/append-dev-to-node-package-version.nix: -------------------------------------------------------------------------------- 1 | { 2 | writers, 3 | bash, 4 | lib, 5 | moreutils, 6 | jq, 7 | name ? "append-dev-to-node-package-version" 8 | }: 9 | writers.writeBashBin name '' 10 | set -ueo pipefail 11 | 12 | ${lib.getExe jq} \ 13 | '.version = .version + "-dev"' \ 14 | package.json \ 15 | | ${moreutils}/bin/sponge package.json 16 | '' -------------------------------------------------------------------------------- /examples/src/getOrder.js: -------------------------------------------------------------------------------- 1 | const getOrCreateActiveOrder = require('./helpers/getOrCreateActiveOrder') 2 | 3 | const order = await getOrCreateActiveOrder(rhinofi, starkPrivKey) 4 | 5 | const response = await rhinofi.getOrder( 6 | // Can be queried with cid (if defined) or order._id 7 | order.cid ? { cid: order.cid } : { orderId: order._id } 8 | ) 9 | 10 | logExampleResult(response) 11 | -------------------------------------------------------------------------------- /examples/helpers/getPriceFromOrderBook.js: -------------------------------------------------------------------------------- 1 | const getPriceFromOrderBook = (data = []) => { 2 | try { 3 | if (!data.length || !data[0] || !data[0][1]) { 4 | console.log("Error getting order book from api") 5 | return null; 6 | } 7 | 8 | return data[0][1] 9 | } catch (e) { 10 | return null 11 | } 12 | }; 13 | 14 | 15 | module.exports = getPriceFromOrderBook; 16 | -------------------------------------------------------------------------------- /examples/src/getOrders_forSymbolPair.js: -------------------------------------------------------------------------------- 1 | const getOrCreateActiveOrder = require('./helpers/getOrCreateActiveOrder') 2 | 3 | const symbol = 'ETH:USDT' 4 | 5 | // Ensure that there is at least one order to get. 6 | await getOrCreateActiveOrder(rhinofi, starkPrivKey, { symbol }) 7 | 8 | const getOrdersResponse = await rhinofi.getOrders(symbol) 9 | 10 | logExampleResult(getOrdersResponse) 11 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | node: true, 4 | browser: true, 5 | commonjs: true, 6 | es6: true, 7 | jest: true 8 | }, 9 | extends: ['standard'], 10 | globals: { 11 | Atomics: 'readonly', 12 | SharedArrayBuffer: 'readonly', 13 | BigInt: 'readonly' 14 | }, 15 | parserOptions: { 16 | ecmaVersion: 2020 17 | }, 18 | rules: {} 19 | } 20 | -------------------------------------------------------------------------------- /src/lib/validators/amount.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../dvf/DVFError') 2 | 3 | module.exports = (dvf, amount) => { 4 | if (amount && typeof amount === 'string') { 5 | amount = +(amount) 6 | } 7 | 8 | if (!amount) { 9 | throw new DVFError('ERR_AMOUNT_MISSING') 10 | } 11 | 12 | if (amount && amount == 0) { 13 | throw new DVFError('ERR_INVALID_AMOUNT') 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/lib/validators/ethAddress.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../dvf/DVFError') 2 | 3 | const isAddress = string => /([0-9abcdefABCDEF]){40}/.test(string) 4 | 5 | module.exports = (dvf, ethAdress) => { 6 | if (!ethAdress) { 7 | throw new DVFError('ERR_ETH_ADDRESS_MISSING') 8 | } 9 | 10 | if (!isAddress(ethAdress)) { 11 | throw new DVFError('ERR_INVALID_ETH_ADDRESS') 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/lib/validators/nonce.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../dvf/DVFError') 2 | 3 | module.exports = (dvf, nonce) => { 4 | if (typeof nonce === 'string') nonce = +nonce 5 | 6 | if (!nonce || isNaN(nonce)) { 7 | throw new DVFError('ERR_INVALID_NONCE') 8 | } 9 | 10 | if (Date.now() / 1000 - nonce > dvf.defaultNonceAge) { 11 | throw new DVFError('NONCE_IS_TOO_OLD') 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/src/ledgerDeposit.js: -------------------------------------------------------------------------------- 1 | const path = `44'/60'/0'/0'/0` 2 | const token = 'ETH' 3 | const amount = 0.70 4 | 5 | const starkDepositData = await rhinofi.stark.ledger.createDepositData( 6 | path, 7 | token, 8 | amount 9 | ) 10 | 11 | const depositResponse = await rhinofi.ledger.deposit( 12 | token, 13 | amount, 14 | starkDepositData 15 | ) 16 | 17 | logExampleResult(depositResponse) 18 | -------------------------------------------------------------------------------- /src/api/eth/getWeb3ForChain.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../../lib/dvf/DVFError') 2 | 3 | module.exports = (dvf, chain = 'DEFAULT') => { 4 | if (chain === 'DEFAULT') { 5 | return dvf.web3 6 | } 7 | const web3ForChain = dvf.web3PerChain && dvf.web3PerChain[chain] 8 | if (!web3ForChain) { 9 | throw new DVFError('NO_WEB3_FOR_CHAIN', { chain }) 10 | } 11 | return web3ForChain 12 | } 13 | -------------------------------------------------------------------------------- /src/lib/dvf/post-authenticated.js: -------------------------------------------------------------------------------- 1 | const addAuthHeadersOrData = require('./addAuthHeadersOrData') 2 | const postGeneric = require('./post-generic') 3 | 4 | module.exports = async (dvf, endpoint, nonce, signature, data = {}) => { 5 | const { headers, data: json } = await addAuthHeadersOrData( 6 | dvf, nonce, signature, { data } 7 | ) 8 | return postGeneric(dvf, endpoint, json, headers) 9 | } 10 | -------------------------------------------------------------------------------- /examples/helpers/getWeb3.js: -------------------------------------------------------------------------------- 1 | const HDWalletProvider = require('@truffle/hdwallet-provider') 2 | const Web3 = require('web3') 3 | 4 | module.exports = (ethPrivKey, rpcUrl) => { 5 | const provider = new HDWalletProvider({ 6 | privateKeys: [ethPrivKey], 7 | providerOrUrl: rpcUrl 8 | }) 9 | 10 | const web3 = new Web3(provider) 11 | provider.engine.stop() 12 | 13 | return { web3, provider } 14 | } 15 | -------------------------------------------------------------------------------- /src/api/getMinMaxOrderSize.js: -------------------------------------------------------------------------------- 1 | const get = require('../lib/dvf/get-generic') 2 | const validateAssertions = require('../lib/validators/validateAssertions') 3 | 4 | /** 5 | * @param {String} symbol 'ETH' 6 | */ 7 | module.exports = async (dvf, symbol) => { 8 | validateAssertions(dvf, {symbol}) 9 | 10 | const endpoint = `/market-data/ticker/${symbol}/order-size` 11 | 12 | return get(dvf, endpoint) 13 | } 14 | -------------------------------------------------------------------------------- /env/test: -------------------------------------------------------------------------------- 1 | # Add Web3 url 2 | RPC_URL=https://ropsten.infura.io/v3/7bfab7398ae84af3b1b70c955cfd9491 3 | # Add GAS STATION API KEY 4 | # Ethereum private key prefixed with 0x 5 | PRIVATE_ETH_KEY=0x49e4d1e2aa7d026188251392dd2d335c176d846d8a894a8092c835f3b345e2ad 6 | PUBLIC_ETH_ADDRESS=0x49e4d1e2aa7d026188251392dd2d335c176d846d8a894a8092c835f3b345e2ad 7 | PRIVATE_STARK_KEY=0x49e4d1e2aa7d026188251392dd2d335c176d846d8a894a8092c835f3b345e2ad 8 | -------------------------------------------------------------------------------- /src/lib/stark/ledger/getBits.js: -------------------------------------------------------------------------------- 1 | /* 2 | Returns 2 least significant bit blocks of 31 bits each 3 | */ 4 | 5 | module.exports = (address) => { 6 | // address to bits 7 | const hBits = BigInt(address).toString(2) 8 | // last and second last 31 bit blocks 9 | const first31Bits = parseInt(hBits.slice(-31),2) 10 | const second31Bits = parseInt(hBits.slice(-62,-31),2) 11 | 12 | return [first31Bits,second31Bits] 13 | } 14 | -------------------------------------------------------------------------------- /src/api/cancelWithdrawal.js: -------------------------------------------------------------------------------- 1 | const post = require('../lib/dvf/post-authenticated') 2 | 3 | const validateAssertions = require('../lib/validators/validateAssertions') 4 | 5 | module.exports = async (dvf, id, nonce, signature) => { 6 | validateAssertions(dvf, { id }) 7 | 8 | const endpoint = '/v1/trading/w/cancelWithdrawal' 9 | 10 | const data = {id} 11 | 12 | return post(dvf, endpoint, nonce, signature, data) 13 | } 14 | -------------------------------------------------------------------------------- /src/api/estimatedNextBatchTime.js: -------------------------------------------------------------------------------- 1 | const { get } = require('request-promise') 2 | 3 | module.exports = async dvf => { 4 | // avoid browser cache with timestamp as querystring 5 | const t = Date.now() 6 | 7 | const url = `${dvf.config.api}/v1/trading/r/estimatedNextBatchTime?t=${t}` 8 | try { 9 | const data = await get(url) 10 | return JSON.parse(data) 11 | } 12 | catch(e) { 13 | return null; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/api/eth/getGasPrice.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Provides a gas price 3 | */ 4 | const recommendedGasPrice = (dvf) => { 5 | return dvf.recommendedGasPrices 6 | ? parseFloat(dvf.recommendedGasPrices.fast) * 1.02 7 | : false 8 | } 9 | 10 | module.exports = async (dvf) => { 11 | return dvf.config.gasStationApiKey 12 | ? await dvf.eth.getGasStationPrice() 13 | : recommendedGasPrice(dvf) || dvf.config.defaultGasPrice 14 | } 15 | -------------------------------------------------------------------------------- /src/lib/dvf/get-authenticated.js: -------------------------------------------------------------------------------- 1 | const getGeneric = require('./get-generic') 2 | const addAuthHeadersOrData = require('./addAuthHeadersOrData') 3 | 4 | module.exports = async (dvf, endpoint, nonce, signature, data = {}, headersOverride = {}) => { 5 | const { headers, data: qs } = await addAuthHeadersOrData( 6 | dvf, nonce, signature, { data } 7 | ) 8 | return getGeneric(dvf, endpoint, qs, { ...headers, ...headersOverride }) 9 | } 10 | -------------------------------------------------------------------------------- /examples/helpers/logMyIP.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S yarn node 2 | 3 | const P = require('aigle') 4 | 5 | const request = require('./request') 6 | 7 | module.exports = useTor => P.retry( 8 | { times: 10, interval: 100 }, 9 | () => request(useTor, 'http://ifconfig.me') 10 | ) 11 | .then(r => console.log('current IP is:', r.body)) 12 | .catch(error => console.error( 13 | 'error while obtaining current IP:', error.message || error.error 14 | )) 15 | -------------------------------------------------------------------------------- /src/api/getWithdrawal.js: -------------------------------------------------------------------------------- 1 | const post = require('../lib/dvf/post-authenticated') 2 | const validateAssertions = require('../lib/validators/validateAssertions') 3 | 4 | module.exports = async (dvf, withdrawalId, nonce, signature) => { 5 | validateAssertions(dvf, { withdrawalId }) 6 | 7 | const endpoint = '/v1/trading/r/getWithdrawal' 8 | 9 | const data = { withdrawalId } 10 | 11 | return post(dvf, endpoint, nonce, signature, data) 12 | } 13 | -------------------------------------------------------------------------------- /src/lib/validators/symbol.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../dvf/DVFError') 2 | 3 | module.exports = (dvf, symbol) => { 4 | if (!symbol) { 5 | throw new DVFError('ERR_INVALID_SYMBOL') 6 | } 7 | 8 | const from = symbol.toString().split(':')[0] 9 | const to = symbol.toString().split(':')[1] 10 | 11 | if (!dvf.token.getTokenInfo(from) || !dvf.token.getTokenInfo(to)) { 12 | throw new DVFError('ERR_INVALID_SYMBOL') 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /nix/pkgs.nix: -------------------------------------------------------------------------------- 1 | { 2 | sources ? import ./sources.nix, 3 | config ? {}, 4 | system ? builtins.currentSystem, 5 | overlays ? [] 6 | }: 7 | let 8 | allOverlays = 9 | # These overlays augment centrally defined packages with things specific 10 | # to this service. 11 | (import ./overlays.nix { inherit sources; }) 12 | ++ 13 | overlays 14 | ; 15 | in 16 | import sources.nixpkgs { inherit config system; overlays = allOverlays; } 17 | -------------------------------------------------------------------------------- /src/api/getDeposits.js: -------------------------------------------------------------------------------- 1 | const post = require('../lib/dvf/post-authenticated') 2 | 3 | const validateAssertions = require('../lib/validators/validateAssertions') 4 | 5 | module.exports = async (dvf, token, nonce, signature) => { 6 | if (token) { 7 | validateAssertions(dvf, {token}) 8 | } 9 | 10 | const endpoint = '/v1/trading/r/getDeposits' 11 | 12 | const data = {token} 13 | 14 | return post(dvf, endpoint, nonce, signature, data) 15 | } 16 | -------------------------------------------------------------------------------- /src/api/getOrders.js: -------------------------------------------------------------------------------- 1 | const post = require('../lib/dvf/post-authenticated') 2 | 3 | const validateAssertions = require('../lib/validators/validateAssertions') 4 | 5 | module.exports = async (dvf, symbol, nonce, signature) => { 6 | const endpoint = '/v1/trading/r/openOrders' 7 | 8 | if (symbol) { 9 | validateAssertions(dvf, {symbol}) 10 | } 11 | 12 | const data = {symbol} 13 | 14 | return post(dvf, endpoint, nonce, signature, data) 15 | } 16 | -------------------------------------------------------------------------------- /src/lib/dvf/token/fromQuantizedAmount.js: -------------------------------------------------------------------------------- 1 | const BN = require('bignumber.js') 2 | 3 | module.exports = (dvf, token, quantizedAmount, rounded = true) => { 4 | const tokenInfo = dvf.token.getTokenInfo(token) 5 | 6 | let value = new BN(quantizedAmount) 7 | .times(tokenInfo.quantization) 8 | .shiftedBy(-1 * tokenInfo.decimals) 9 | 10 | if (rounded) { 11 | value = value.decimalPlaces(3) 12 | } 13 | 14 | return value.toString() 15 | } 16 | -------------------------------------------------------------------------------- /src/lib/stark/calculateFact.js: -------------------------------------------------------------------------------- 1 | const { keccak256, encodePacked } = require('web3-utils') 2 | 3 | module.exports = (recipient, baseUnitsAmount, tokenAddress, salt) => keccak256( 4 | encodePacked( 5 | { t: 'address', v: recipient }, 6 | { t: 'uint256', v: baseUnitsAmount }, 7 | { t: 'address', v: tokenAddress }, 8 | { t: 'uint256', v: salt } 9 | ) 10 | // Remove 0x prefix as starkware seems to expect it to be absent. 11 | ).substring(2) 12 | -------------------------------------------------------------------------------- /src/api/getOrdersHist.js: -------------------------------------------------------------------------------- 1 | const post = require('../lib/dvf/post-authenticated') 2 | 3 | const validateAssertions = require('../lib/validators/validateAssertions') 4 | 5 | module.exports = async (dvf, symbol, nonce, signature) => { 6 | if (symbol) { 7 | validateAssertions(dvf, { symbol }) 8 | } 9 | 10 | const endpoint = '/v1/trading/r/orderHistory' 11 | 12 | const data = { symbol } 13 | 14 | return post(dvf, endpoint, nonce, signature, data) 15 | } 16 | -------------------------------------------------------------------------------- /src/lib/dvf/get-generic.js: -------------------------------------------------------------------------------- 1 | const { request } = require('@rhino.fi/dvf-utils') 2 | const _omitBy = require('lodash/omitBy') 3 | const _isNil = require('lodash/isNil') 4 | 5 | module.exports = async (dvf, endpoint, qs = {}, headers = {}) => { 6 | const url = dvf.config.api + endpoint 7 | 8 | const options = { 9 | headers, 10 | // removes null and undefined values 11 | qs: _omitBy(qs, _isNil) 12 | } 13 | 14 | return request.get(url, options) 15 | } 16 | -------------------------------------------------------------------------------- /src/lib/util/extractOrderIdsInput.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {Object} orderIdOrCid 'xxx' or { orderId: 'xxx' } or { cid: 'yyy' } 4 | */ 5 | module.exports = orderIdOrCid => { 6 | if (typeof orderIdOrCid === 'object' && orderIdOrCid !== null) { 7 | const { cid, orderId } = orderIdOrCid 8 | return cid ? { cid } : { orderId } 9 | } else { 10 | // Supporting input to be orderId as original behavior 11 | return { orderId: orderIdOrCid } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | # note setting yarnPath as yarn 3 is provided by nix shell 2 | nodeLinker: pnp 3 | 4 | packageExtensions: 5 | "@trufflesuite/eth-json-rpc-filters@*": 6 | # Used in module code while only declared in dev deps 7 | dependencies: 8 | pify: ^3.0.0 9 | 10 | npmPublishAccess: public 11 | 12 | npmScopes: 13 | rhino.fi: 14 | npmAlwaysAuth: false 15 | npmAuthToken: ${NPM_ACCESS_TOKEN-} 16 | npmPublishRegistry: https://registry.npmjs.org 17 | -------------------------------------------------------------------------------- /src/lib/dvf/post-generic.js: -------------------------------------------------------------------------------- 1 | const { post } = require('request-promise') 2 | const _omitBy = require('lodash/omitBy') 3 | const _isNil = require('lodash/isNil') 4 | 5 | module.exports = async (dvf, endpoint, json = {}, headers = {}) => { 6 | const url = dvf.config.api + endpoint 7 | 8 | const options = { 9 | uri: url, 10 | headers, 11 | // removes null and undefined values 12 | json: _omitBy(json, _isNil) 13 | } 14 | 15 | return post(options) 16 | } 17 | -------------------------------------------------------------------------------- /src/api/getConfig.js: -------------------------------------------------------------------------------- 1 | const { post } = require('request-promise') 2 | 3 | module.exports = async dvf => { 4 | const url = dvf.config.api + '/v1/trading/r/getConf' 5 | try { 6 | const exchangeConf = await post(url, { json: {} }) 7 | dvf.config = Object.assign({}, dvf.config, exchangeConf) 8 | return exchangeConf 9 | } catch (error) { 10 | // TODO: use logger 11 | // console.log('error getting config from dvf-pub-api') 12 | return dvf.config 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/api/eth/call.js: -------------------------------------------------------------------------------- 1 | module.exports = async (dvf, abi, address, action, args, options = {}) => { 2 | const web3 = dvf.eth.getWeb3ForChain(options.chain) 3 | const contract = new web3.eth.Contract(abi, address) 4 | 5 | // using eth.call 6 | // parseInt( response, 16 ) to convert int 7 | /** 8 | return dvf.web3.eth.call({ 9 | to: address, 10 | data: contract.methods[action](...args).encodeABI() 11 | }) 12 | **/ 13 | 14 | return contract.methods[action](...args).call() 15 | } 16 | -------------------------------------------------------------------------------- /examples/helpers/logExampleResult.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const saveAsJson = require('./saveAsJson') 3 | 4 | module.exports = exampleFilename => content => { 5 | const resultsDir = process.env.SAVE_EXAMPLE_RESULTS_TO_DIR 6 | 7 | const { name, base } = path.parse(exampleFilename) 8 | 9 | if (resultsDir) { 10 | fs.mkdirSync(resultsDir, { recursive: true }) 11 | 12 | saveAsJson(path.join(resultsDir, `${name}.json`), content) 13 | } 14 | 15 | console.log(`${base} response ->`, content) 16 | } 17 | -------------------------------------------------------------------------------- /src/lib/dvf/token/getTokenInfoOrThrow.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../DVFError') 2 | 3 | module.exports = (dvf, token) => { 4 | const { tokenRegistry } = dvf.config 5 | 6 | if (!tokenRegistry) { 7 | throw new DVFError('NO_TOKEN_REGISTRY') 8 | } 9 | 10 | const tokenInfo = tokenRegistry[token] 11 | 12 | if (!tokenInfo) { 13 | const validTokens = Object.keys(tokenRegistry) 14 | throw new DVFError('ERR_INVALID_TOKEN', { token, validTokens }) 15 | } 16 | 17 | return { token, ...tokenInfo } 18 | } 19 | -------------------------------------------------------------------------------- /src/api/submitBuyOrder.js: -------------------------------------------------------------------------------- 1 | module.exports = ( 2 | dvf, 3 | symbol, 4 | amount, 5 | price, 6 | gid, 7 | cid, 8 | signedOrder, 9 | validFor, 10 | partnerId, 11 | feeRate, 12 | starkPrivateKey 13 | ) => { 14 | // force amount to be positive ( buy order ) 15 | amount = Math.abs(amount) 16 | return dvf.submitOrder( 17 | symbol, 18 | amount, 19 | price, 20 | gid, 21 | cid, 22 | signedOrder, 23 | validFor, 24 | partnerId, 25 | feeRate, 26 | starkPrivateKey 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /src/api/fullWithdrawalRequest.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../lib/dvf/DVFError') 2 | const validateAssertions = require('../lib/validators/validateAssertions') 3 | 4 | module.exports = async (dvf, token) => { 5 | validateAssertions(dvf, { token }) 6 | 7 | const { starkKeyHex } = dvf.config 8 | const { status, transactionHash } = await dvf.contract.fullWithdrawalRequest(token, starkKeyHex) 9 | 10 | if (!status) { 11 | throw new DVFError('ERR_CALLING_FULL_WITHDRAWAL_REQUEST') 12 | } 13 | return { transactionHash } 14 | } 15 | -------------------------------------------------------------------------------- /src/api/submitSellOrder.js: -------------------------------------------------------------------------------- 1 | module.exports = ( 2 | dvf, 3 | symbol, 4 | amount, 5 | price, 6 | gid, 7 | cid, 8 | signedOrder, 9 | validFor, 10 | partnerId, 11 | feeRate, 12 | starkPrivateKey 13 | ) => { 14 | // force amount to be negative ( sell order ) 15 | amount = Math.abs(amount) * -1 16 | return dvf.submitOrder( 17 | symbol, 18 | amount, 19 | price, 20 | gid, 21 | cid, 22 | signedOrder, 23 | validFor, 24 | partnerId, 25 | feeRate, 26 | starkPrivateKey 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /src/api/account/tokenBalance.js: -------------------------------------------------------------------------------- 1 | const getTokenAddressFromTokenInfoOrThrow = require('../../lib/dvf/token/getTokenAddressFromTokenInfoOrThrow') 2 | 3 | module.exports = (dvf, token, chain = 'ETHEREUN') => { 4 | const tokenInfo = dvf.token.getTokenInfo(token) 5 | const action = 'balanceOf' 6 | const args = [dvf.get('account')] 7 | const tokenAddress = getTokenAddressFromTokenInfoOrThrow(tokenInfo, chain) 8 | 9 | return dvf.eth.call( 10 | dvf.contract.abi.token, 11 | tokenAddress, 12 | action, 13 | args 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /examples/helpers/waitForDepositCreditedOnChain.js: -------------------------------------------------------------------------------- 1 | const P = require('aigle') 2 | 3 | module.exports = async (rhinofi, deposit) => { 4 | console.log('waiting for deposit to be credited on chain...') 5 | 6 | while (true) { 7 | // TODO: add getDeposit to pub-api and client and use it here. 8 | const deposits = await rhinofi.getDeposits(deposit.token) 9 | if (deposits.find(d => d._id === deposit._id && d.status === 'ready')) { 10 | break 11 | } 12 | console.log('still waiting...') 13 | await P.delay(2000) 14 | } 15 | } -------------------------------------------------------------------------------- /nix/overlays.nix: -------------------------------------------------------------------------------- 1 | { sources ? import ./sources.nix }: 2 | [ 3 | (self: super: { 4 | yarn-berry-source = sources.yarn-berry-cjs-rhinofi; 5 | yarn-berry = super.callPackage (import ./packages/yarn-berry.nix) {}; 6 | }) 7 | (self: super: { 8 | gh-md-toc-source = sources.github-markdown-toc; 9 | gh-md-toc = super.callPackage (import ./packages/gh-md-toc.nix) {}; 10 | }) 11 | (self: super: { 12 | append-dev-to-node-package-version = super.callPackage (import ./packages/append-dev-to-node-package-version.nix) {}; 13 | }) 14 | ] -------------------------------------------------------------------------------- /src/api/contract/getPermitNonceWithUnderscoreForAddress.js: -------------------------------------------------------------------------------- 1 | const permitTokenAbi = require('./abi/AaveToken.abi') 2 | /** 3 | * Gets token permission nonce for owner 4 | * This naming "_nonces" prefixed with underscore 5 | * is implemented by Aave (https://docs.aave.com/developers/the-core-protocol/aave-token#_nonces) 6 | * (vs. "nonces" for some other tokens) 7 | */ 8 | module.exports = (dvf, tokenAddress, ownerAddress) => 9 | dvf.eth.call( 10 | permitTokenAbi, 11 | tokenAddress, 12 | '_nonces', 13 | [ownerAddress] 14 | ) 15 | -------------------------------------------------------------------------------- /src/api/withdrawOnchain.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../lib/dvf/DVFError') 2 | const validateAssertions = require('../lib/validators/validateAssertions') 3 | 4 | module.exports = async (dvf, token, address) => { 5 | validateAssertions(dvf, { token }) 6 | 7 | if (!address) { 8 | address = dvf.config.starkKeyHex 9 | } 10 | 11 | const { status, transactionHash } = await dvf.contract.withdraw(token, address) 12 | 13 | if (!status) { 14 | throw new DVFError('ERR_ONCHAIN_WITHDRAW') 15 | } 16 | return { transactionHash } 17 | } 18 | -------------------------------------------------------------------------------- /nix/packages/gh-md-toc.nix: -------------------------------------------------------------------------------- 1 | { 2 | bash, 3 | coreutils, 4 | curl, 5 | gawk, 6 | gh-md-toc-source, 7 | gnugrep, 8 | gnused, 9 | lib, 10 | runCommandNoCC, 11 | 12 | name ? "gh-md-toc" 13 | }: 14 | let 15 | package = runCommandNoCC 16 | name 17 | { buildInputs = [ bash gnused gnugrep coreutils ]; } 18 | '' 19 | binName=${name} 20 | binPath=$out/bin/$binName 21 | mkdir -p $(dirname $binPath) 22 | cp ${gh-md-toc-source}/gh-md-toc $binPath 23 | patchShebangs $binPath 24 | '' 25 | ; 26 | in package -------------------------------------------------------------------------------- /src/api/eth/getGasStationPrice.js: -------------------------------------------------------------------------------- 1 | const { get } = require('request-promise') 2 | 3 | /** 4 | * Provides a safe average gas price 5 | */ 6 | 7 | module.exports = async (dvf) => { 8 | try { 9 | const res = await get(`${dvf.config.gasApi}/json/ethgasAPI.json?api-key=${dvf.config.gasStationApiKey || ''}`) 10 | dvf.config.defaultGasPrice = parseInt((JSON.parse(res).average * 1.25 *100000000)) 11 | } catch(e) { 12 | console.log('Error getting safe gas priec, using default ', e) 13 | } 14 | return dvf.config.defaultGasPrice 15 | } 16 | -------------------------------------------------------------------------------- /src/api/ledger/transferUsingVaultIdAndStarkKey.js: -------------------------------------------------------------------------------- 1 | const { post } = require('request-promise') 2 | const makeCreateSignedTransferTxLedger = require('../../lib/ledger/makeCreateSignedTransferTxLedger') 3 | 4 | module.exports = async (dvf, transferData, path, feeRecipient) => { 5 | const url = dvf.config.api + '/v1/trading/w/transfer' 6 | const createSignedTransferTx = makeCreateSignedTransferTxLedger(dvf)(path) 7 | const json = await dvf.createTransferPayload(transferData, feeRecipient, createSignedTransferTx) 8 | return post(url, { json }) 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/dvf/addNonceAndExpirationTimestamp.js: -------------------------------------------------------------------------------- 1 | const getExpirationTimestampInHours = require('./getExpirationTimestampInHours') 2 | const generateRandomNonce = require('./generateRandomNonce') 3 | 4 | module.exports = ({ defaultStarkExpiry }) => transaction => { 5 | const expirationTimestamp = transaction.expirationTimestamp || 6 | getExpirationTimestampInHours(defaultStarkExpiry) 7 | 8 | const nonce = transaction.nonce || generateRandomNonce() 9 | 10 | return { 11 | ...transaction, 12 | nonce, 13 | expirationTimestamp 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/api/getVaultId.js: -------------------------------------------------------------------------------- 1 | const validateAssertions = require('../lib/validators/validateAssertions') 2 | 3 | module.exports = async (dvf, token, nonce, signature) => { 4 | validateAssertions(dvf, {token}) 5 | 6 | const existingVaultId = dvf.config.tokenRegistry[token].starkVaultId 7 | if (existingVaultId != null) { 8 | return existingVaultId 9 | } 10 | else { 11 | const vaultId = await dvf.getVaultIdFromServer(token, nonce, signature) 12 | 13 | dvf.config.tokenRegistry[token].starkVaultId = vaultId 14 | 15 | return vaultId 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/lib/dvf/token/getTokenInfoByTokenId.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../DVFError') 2 | const _find = require('lodash/find') 3 | 4 | module.exports = (dvf, starkTokenId) => { 5 | const {tokenRegistry} = dvf.config 6 | 7 | if (!tokenRegistry) { 8 | throw new DVFError('NO_TOKEN_REGISTRY') 9 | } 10 | const tokenInfo = _find(tokenRegistry, { 11 | starkTokenId 12 | }) 13 | 14 | if (!tokenInfo) { 15 | const validTokens = Object.keys(tokenRegistry) 16 | throw new DVFError('ERR_INVALID_TOKEN', {validTokens}) 17 | } 18 | 19 | return tokenInfo 20 | } 21 | -------------------------------------------------------------------------------- /src/api/cancelOpenOrders.js: -------------------------------------------------------------------------------- 1 | const post = require('../lib/dvf/post-authenticated') 2 | 3 | /** 4 | * Cancel all open orders for the current user 5 | * If `canceled` is true for the given order, it has been successfully canceled 6 | * otherwise it will be queued for cancellation unless it is no longer `active` 7 | * @returns {object[]} { orderId: string, canceled: boolean, active: boolean} 8 | */ 9 | module.exports = async (dvf, nonce, signature) => { 10 | const endpoint = '/v1/trading/w/cancelOpenOrders' 11 | 12 | return post(dvf, endpoint, nonce, signature, {}) 13 | } 14 | -------------------------------------------------------------------------------- /src/api/getBalance.js: -------------------------------------------------------------------------------- 1 | const validateAssertions = require('../lib/validators/validateAssertions') 2 | 3 | const post = require('../lib/dvf/post-authenticated') 4 | 5 | /* 6 | params: { 7 | token: 'ETH', (optional) 8 | fields: ['balance', 'updatedAt'] (optional) 9 | } 10 | */ 11 | module.exports = async (dvf, params, nonce, signature) => { 12 | if (params) { 13 | validateAssertions(dvf, params) 14 | } 15 | 16 | const endpoint = '/v1/trading/r/getBalance' 17 | 18 | const data = params 19 | 20 | return post(dvf, endpoint, nonce, signature, data) 21 | } 22 | -------------------------------------------------------------------------------- /src/api/amm/poolAPY.js: -------------------------------------------------------------------------------- 1 | const { Joi } = require('@rhino.fi/dvf-utils') 2 | const get = require('../../lib/dvf/get-generic') 3 | const validateWithJoi = require('../../lib/validators/validateWithJoi') 4 | 5 | const schema = Joi.object({ 6 | poolName: Joi.string() 7 | }) 8 | 9 | const validateInputs = validateWithJoi(schema)('INVALID_METHOD_ARGUMENT')({ 10 | context: 'poolAPY' 11 | }) 12 | 13 | module.exports = async (dvf, data) => { 14 | const { poolName } = validateInputs(data) 15 | const endpoint = `/v1/trading/amm/poolAPY/${poolName}` 16 | return get(dvf, endpoint) 17 | } 18 | -------------------------------------------------------------------------------- /src/api/amm/poolTVL.js: -------------------------------------------------------------------------------- 1 | const { Joi } = require('@rhino.fi/dvf-utils') 2 | const get = require('../../lib/dvf/get-generic') 3 | const validateWithJoi = require('../../lib/validators/validateWithJoi') 4 | 5 | const schema = Joi.object({ 6 | poolName: Joi.string() 7 | }) 8 | 9 | const validateInputs = validateWithJoi(schema)('INVALID_METHOD_ARGUMENT')({ 10 | context: 'poolTVL' 11 | }) 12 | 13 | module.exports = async (dvf, data) => { 14 | const { poolName } = validateInputs(data) 15 | const endpoint = `/v1/trading/amm/poolTVL/${poolName}` 16 | return get(dvf, endpoint) 17 | } 18 | -------------------------------------------------------------------------------- /src/lib/validators/validateWithJoi.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../dvf/DVFError') 2 | 3 | const defaultOptions = Object.freeze({ 4 | allowUnknown: true, 5 | stripUnknown: true, 6 | presence: 'required' 7 | }) 8 | 9 | module.exports = (schema, options = defaultOptions) => errorType => errorProps => value => { 10 | const { value: validated, error } = schema.validate(value, options) 11 | if (error) { 12 | throw new DVFError( 13 | errorType, 14 | { ...errorProps, reason: error.message, details: error.details } 15 | ) 16 | } 17 | return validated 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/stark/ledger/createDepositData.js: -------------------------------------------------------------------------------- 1 | module.exports = async (dvf, path, token, amount, tempVaultId, nonce, signature) => { 2 | const starkVaultId = await dvf.getVaultId(token, nonce, signature) 3 | const tokenInfo = dvf.token.getTokenInfoOrThrow(token) 4 | const quantizedAmount = dvf.token.toQuantizedAmount(token, amount) 5 | const starkDeposit = await dvf.stark.ledger.createSignedTransfer( 6 | path, 7 | tokenInfo, 8 | quantizedAmount, 9 | tempVaultId, 10 | starkVaultId 11 | ) 12 | starkDeposit.starkVaultId = starkVaultId 13 | 14 | return starkDeposit 15 | } 16 | -------------------------------------------------------------------------------- /src/api/amm/poolSwapFees.js: -------------------------------------------------------------------------------- 1 | const { Joi } = require('@rhino.fi/dvf-utils') 2 | const get = require('../../lib/dvf/get-generic') 3 | const validateWithJoi = require('../../lib/validators/validateWithJoi') 4 | 5 | const schema = Joi.object({ 6 | poolName: Joi.string() 7 | }) 8 | 9 | const validateInputs = validateWithJoi(schema)('INVALID_METHOD_ARGUMENT')({ 10 | context: 'poolSwapFees' 11 | }) 12 | 13 | module.exports = async (dvf, data) => { 14 | const { poolName } = validateInputs(data) 15 | const endpoint = `/v1/trading/amm/poolSwapFees/${poolName}` 16 | return get(dvf, endpoint) 17 | } 18 | -------------------------------------------------------------------------------- /src/api/amm/poolTokensRate.js: -------------------------------------------------------------------------------- 1 | const { Joi } = require('@rhino.fi/dvf-utils') 2 | const get = require('../../lib/dvf/get-generic') 3 | const validateWithJoi = require('../../lib/validators/validateWithJoi') 4 | 5 | const schema = Joi.object({ 6 | poolName: Joi.string() 7 | }) 8 | 9 | const validateInputs = validateWithJoi(schema)('INVALID_METHOD_ARGUMENT')({ 10 | context: 'poolTokensRate' 11 | }) 12 | 13 | module.exports = async (dvf, data) => { 14 | const { poolName } = validateInputs(data) 15 | const endpoint = `/v1/trading/amm/poolTokensRate/${poolName}` 16 | return get(dvf, endpoint) 17 | } 18 | -------------------------------------------------------------------------------- /src/api/amm/poolStoredTokens.js: -------------------------------------------------------------------------------- 1 | const { Joi } = require('@rhino.fi/dvf-utils') 2 | const get = require('../../lib/dvf/get-generic') 3 | const validateWithJoi = require('../../lib/validators/validateWithJoi') 4 | 5 | const schema = Joi.object({ 6 | poolName: Joi.string() 7 | }) 8 | 9 | const validateInputs = validateWithJoi(schema)('INVALID_METHOD_ARGUMENT')({ 10 | context: 'poolStoredTokens' 11 | }) 12 | 13 | module.exports = async (dvf, data) => { 14 | const { poolName } = validateInputs(data) 15 | const endpoint = `/v1/trading/amm/poolStoredTokens/${poolName}` 16 | return get(dvf, endpoint) 17 | } 18 | -------------------------------------------------------------------------------- /src/lib/stark/ledger/createWithdrawalData.js: -------------------------------------------------------------------------------- 1 | module.exports = async (dvf, path, token, amount) => { 2 | const tokenInfo = dvf.token.getTokenInfoOrThrow(token) 3 | const quantizedAmount = dvf.token.toQuantizedAmount(token, amount) 4 | const tempVaultId = dvf.config.DVF.tempStarkVaultId 5 | let starkVaultId = tokenInfo.starkVaultId 6 | 7 | const starkWithdrawal = await dvf.stark.ledger.createSignedTransfer( 8 | path, 9 | tokenInfo, 10 | quantizedAmount, 11 | starkVaultId, 12 | tempVaultId 13 | ) 14 | starkWithdrawal.starkVaultId = starkVaultId 15 | return starkWithdrawal 16 | } 17 | -------------------------------------------------------------------------------- /examples/checkEthAccountBalance.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S yarn node 2 | const Web3 = require('web3') 3 | 4 | const getBalanceInEth = async (web3, account) => web3.utils.fromWei( 5 | await web3.eth.getBalance(account.address), 6 | 'ether' 7 | ) 8 | 9 | const envVars = require('./helpers/loadFromEnvOrConfig')( 10 | process.env.CONFIG_FILE_NAME 11 | ) 12 | 13 | const web3 = new Web3(new Web3.providers.HttpProvider(envVars.RPC_URL)) 14 | 15 | const ethPrivKey = envVars.ETH_PRIVATE_KEY 16 | const account = web3.eth.accounts.privateKeyToAccount(ethPrivKey) 17 | 18 | getBalanceInEth(web3, account).then(console.log) 19 | -------------------------------------------------------------------------------- /examples/src/publicPermissions.js: -------------------------------------------------------------------------------- 1 | const publicPermissionsDescriptor = await rhinofi.publicUserPermissions() 2 | 3 | logExampleResult(publicPermissionsDescriptor) 4 | 5 | // Get currently set permissions for a user, authenticated endpoint 6 | const currentUerPermissions = await rhinofi.account.getPermissions() 7 | logExampleResult(currentUerPermissions) 8 | 9 | // Enable all of the permissions 10 | Object.keys(currentUerPermissions).map(async (permissionKey) => { 11 | const updatedPermissions = await rhinofi.account.setPermissions({ key: permissionKey, value: true }) 12 | logExampleResult(updatedPermissions) 13 | }) -------------------------------------------------------------------------------- /src/api/amm/poolVolume24Hours.js: -------------------------------------------------------------------------------- 1 | const { Joi } = require('@rhino.fi/dvf-utils') 2 | const get = require('../../lib/dvf/get-generic') 3 | const validateWithJoi = require('../../lib/validators/validateWithJoi') 4 | 5 | const schema = Joi.object({ 6 | poolName: Joi.string() 7 | }) 8 | 9 | const validateInputs = validateWithJoi(schema)('INVALID_METHOD_ARGUMENT')({ 10 | context: 'poolVolume24Hours' 11 | }) 12 | 13 | module.exports = async (dvf, data) => { 14 | const { poolName } = validateInputs(data) 15 | const endpoint = `/v1/trading/amm/poolVolume24Hours/${poolName}` 16 | return get(dvf, endpoint) 17 | } 18 | -------------------------------------------------------------------------------- /src/lib/stark/signAmmFundingOrder.js: -------------------------------------------------------------------------------- 1 | const P = require('aigle') 2 | const createSignedTransaction = require('../dvf/createSignedTransaction') 3 | 4 | module.exports = dvf => async data => ({ 5 | ...data, 6 | starkPublicKey: await dvf.dvfStarkProvider.getPublicKey(), 7 | // Need to do this in series since createSignedTransaction might call 8 | // into ledger and these calls cannot be executed in parallel. 9 | orders: await P.mapSeries( 10 | data.orders, 11 | async order => ({ 12 | ...order, 13 | starkOrder: await createSignedTransaction(dvf)(order.starkOrder) 14 | }) 15 | ) 16 | }) 17 | -------------------------------------------------------------------------------- /src/api/contract/depositCancel.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../../lib/dvf/DVFError') 2 | 3 | module.exports = async (dvf, vaultId, token, tradingKey) => { 4 | tradingKey = tradingKey || dvf.config.starkKeyHex 5 | const args = [tradingKey, dvf.token.getTokenInfo(token).starkTokenId, vaultId] 6 | 7 | const action = 'depositCancel' 8 | 9 | try { 10 | return dvf.eth.send( 11 | dvf.contract.abi.getStarkEx(), 12 | dvf.config.DVF.starkExContractAddress, 13 | action, 14 | args 15 | ) 16 | } catch (e) { 17 | console.log(e) 18 | throw new DVFError('ERR_DEPOSIT_CANCEL') 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/api/contract/depositReclaim.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../../lib/dvf/DVFError') 2 | 3 | module.exports = async (dvf, vaultId, token, tradingKey) => { 4 | tradingKey = tradingKey || dvf.config.starkKeyHex 5 | const args = [tradingKey, dvf.token.getTokenInfo(token).starkTokenId, vaultId] 6 | 7 | const action = 'depositReclaim' 8 | 9 | try { 10 | return dvf.eth.send( 11 | dvf.contract.abi.getStarkEx(), 12 | dvf.config.DVF.starkExContractAddress, 13 | action, 14 | args 15 | ) 16 | } catch (e) { 17 | console.log(e) 18 | throw new DVFError('ERR_DEPOSIT_RECLAIM') 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/src/ledgerSubmitOrder.js: -------------------------------------------------------------------------------- 1 | // Submit an order to sell 0.3 Eth for USDT at 250 USDT per 1 Eth 2 | const symbol = 'ETH:USDT' 3 | const amount = -0.3 4 | const price = 200 5 | const validFor = '0' 6 | const feeRate = '' 7 | const ledgerPath= `44'/60'/0'/0'/0` 8 | 9 | const submitOrderResponse = await rhinofi.submitOrder({ 10 | symbol, 11 | amount, 12 | price, 13 | ledgerPath, 14 | validFor, // Optional 15 | feeRate, // Optional 16 | gid: '1', // Optional 17 | cid: '1', // Optional 18 | partnerId: 'P1' // Optional 19 | }) 20 | 21 | logExampleResult(submitOrderResponse) 22 | -------------------------------------------------------------------------------- /src/api/amm/poolsData.js: -------------------------------------------------------------------------------- 1 | const { Joi } = require('@rhino.fi/dvf-utils') 2 | const get = require('../../lib/dvf/get-generic') 3 | const validateWithJoi = require('../../lib/validators/validateWithJoi') 4 | 5 | const schema = Joi.object({ 6 | pools: Joi.alternatives().try(Joi.string(), Joi.array().items(Joi.string())) 7 | }) 8 | 9 | const validateInputs = validateWithJoi(schema)('INVALID_METHOD_ARGUMENT')({ 10 | context: 'poolsData' 11 | }) 12 | 13 | module.exports = async (dvf, data) => { 14 | const { pools } = validateInputs(data) 15 | const endpoint = '/v1/trading/amm/poolsData' 16 | return get(dvf, endpoint, { pools }) 17 | } 18 | -------------------------------------------------------------------------------- /src/api/contract/withdraw.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../../lib/dvf/DVFError') 2 | 3 | module.exports = async (dvf, token, tradingKey) => { 4 | const { starkTokenId } = dvf.token.getTokenInfo(token) 5 | tradingKey = tradingKey || dvf.config.starkKeyHex 6 | 7 | const args = [tradingKey, starkTokenId] 8 | 9 | const action = 'withdraw' 10 | 11 | try { 12 | return dvf.eth.send( 13 | dvf.contract.abi.getStarkEx(), 14 | dvf.config.DVF.starkExContractAddress, 15 | action, 16 | args 17 | ) 18 | } catch (e) { 19 | console.log(e) 20 | throw new DVFError('ERR_ONCHAIN_WITHDRAW') 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/lib/dvf/getBridgeContractAddressOrThrow.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('./DVFError') 2 | 3 | module.exports = (dvf, chain) => { 4 | const bridgeContractAddress = dvf.config.DVF.bridgedDepositContractsPerChain 5 | // Legacy location of contract address 6 | ? dvf.config.DVF.bridgedDepositContractsPerChain[chain] 7 | // Future/new location of contract address 8 | : dvf.config.DVF.bridgeConfigPerChain[chain] && dvf.config.DVF.bridgeConfigPerChain[chain].contractAddress 9 | if (!bridgeContractAddress) { 10 | throw new DVFError('NO_BRIDGE_CONTRACT_FOR_CHAIN', { chain }) 11 | } 12 | return bridgeContractAddress 13 | } 14 | -------------------------------------------------------------------------------- /src/lib/dvf/makeAuthHeaders.js: -------------------------------------------------------------------------------- 1 | const makeEcRecoverHeader = data => { 2 | const bufferStarkAuthData = Buffer.from(JSON.stringify(data)) 3 | return 'EcRecover ' + bufferStarkAuthData.toString('base64') 4 | } 5 | 6 | module.exports = (dvf, nonce, signature) => { 7 | if (!nonce) throw new Error('nonce is required') 8 | if (!signature) throw new Error('signature is required') 9 | 10 | const authData = { 11 | signature, 12 | nonce, 13 | ...(dvf.config.useSignature 14 | ? { ethAddress: dvf.get('account') } 15 | : {} 16 | ) 17 | } 18 | 19 | return { Authorization: makeEcRecoverHeader(authData) } 20 | } 21 | -------------------------------------------------------------------------------- /src/api/getRegistrationStatuses.js: -------------------------------------------------------------------------------- 1 | const { Joi } = require('@rhino.fi/dvf-utils') 2 | const get = require('../lib/dvf/get-generic') 3 | const validateWithJoi = require('../lib/validators/validateWithJoi') 4 | 5 | const schema = Joi.object({ 6 | targetEthAddress: Joi.ethAddress() 7 | }) 8 | 9 | const validateInputs = validateWithJoi(schema)('INVALID_METHOD_ARGUMENT')({ 10 | context: `getRegistrationStatuses` 11 | }) 12 | 13 | module.exports = async (dvf, data) => { 14 | const { targetEthAddress } = validateInputs(data) 15 | 16 | const endpoint = `/v1/trading/registrations/${targetEthAddress}` 17 | return get(dvf, endpoint) 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/stark/ledger/getPath.test.js: -------------------------------------------------------------------------------- 1 | const instance = require('../../../api/test/helpers/instance') 2 | 3 | let dvf 4 | 5 | describe('dvf.stark.ledger.getPath', () => { 6 | beforeAll(async () => { 7 | 8 | dvf = await instance() 9 | }) 10 | 11 | it('gets stark path for ledger', async () => { 12 | const address = '0x7d92F2d76cd93DA39066f9B695adc33e4dc08a54' 13 | const derivedStarkPath = `2645'/579218131'/1393043894'/1304463956'/727418492'/0` 14 | 15 | const starkPath = await dvf.stark.ledger.getPath(address) 16 | 17 | // console.log({starkPath}) 18 | expect(starkPath).toMatch(derivedStarkPath) 19 | 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /src/api/airdropEligibility.js: -------------------------------------------------------------------------------- 1 | const { Joi } = require('@rhino.fi/dvf-utils') 2 | const get = require('../lib/dvf/get-generic') 3 | const validateWithJoi = require('../lib/validators/validateWithJoi') 4 | 5 | const schema = Joi.object({ 6 | ethAddress: Joi.ethAddress(), 7 | token: Joi.string() 8 | }) 9 | 10 | const validateInputs = validateWithJoi(schema)('INVALID_METHOD_ARGUMENT')({ 11 | context: 'airdropEligibility' 12 | }) 13 | 14 | module.exports = async (dvf, data) => { 15 | const { ethAddress, token } = validateInputs(data) 16 | const endpoint = '/v1/trading/r/airdropEligibility' 17 | return get(dvf, endpoint, {ethAddress, token}) 18 | } 19 | -------------------------------------------------------------------------------- /src/api/getOrder.js: -------------------------------------------------------------------------------- 1 | const post = require('../lib/dvf/post-authenticated') 2 | const extractOrderIdsInput = require('../lib/util/extractOrderIdsInput') 3 | 4 | const validateAssertions = require('../lib/validators/validateAssertions') 5 | 6 | /** 7 | * 8 | * @param {Object} orderIdOrCid 'xxx' or { orderId: 'xxx' } or { cid: 'yyy' } 9 | */ 10 | module.exports = async (dvf, orderIdOrCid, nonce, signature) => { 11 | const input = extractOrderIdsInput(orderIdOrCid) 12 | validateAssertions(dvf, input) 13 | 14 | const endpoint = '/v1/trading/r/getOrder' 15 | 16 | const data = input 17 | 18 | return post(dvf, endpoint, nonce, signature, data) 19 | } 20 | -------------------------------------------------------------------------------- /src/lib/dvf/createSignedTransaction.js: -------------------------------------------------------------------------------- 1 | // Logic common to all stark transactions/orders. 2 | // Takes transaction with all props apart from nonce and expirationTimestamp 3 | // and returns one with those props as well a signature added. 4 | const addNonceAndExpirationTimestamp = require('./addNonceAndExpirationTimestamp') 5 | 6 | module.exports = dvf => async transaction => { 7 | // dvfStarkProvider abstracts specifics of how a transaction is signed. 8 | 9 | transaction = addNonceAndExpirationTimestamp(dvf.config)(transaction) 10 | const signature = await dvf.dvfStarkProvider.sign(transaction) 11 | 12 | return { ...transaction, signature } 13 | } 14 | -------------------------------------------------------------------------------- /src/lib/stark/createTransferMessage.js: -------------------------------------------------------------------------------- 1 | const sw = require('@rhino.fi/starkware-crypto') 2 | 3 | module.exports = (dvf, ...args) => { 4 | const message = (dvf.sw || sw).getTransferMsgHash( 5 | args[0], // amount (uint63 decimal str) 6 | args[1], // nonce (uint31) 7 | args[2], // temp vault id or sender_vault_id (uint31) 8 | args[3], // token (hex str with 0x prefix < prime) 9 | args[4], // user vault or receiver_vault_id (uint31) 10 | args[5], // receiver_public_key (hex str with 0x prefix < prime) 11 | args[6] // expiration_timestamp (uint22) 12 | ) 13 | return { starkMessage: dvf.sw ? message.toString(16) : message } 14 | } 15 | -------------------------------------------------------------------------------- /src/api/cancelOrder.js: -------------------------------------------------------------------------------- 1 | const post = require('../lib/dvf/post-authenticated') 2 | const extractOrderIdsInput = require('../lib/util/extractOrderIdsInput') 3 | 4 | const validateAssertions = require('../lib/validators/validateAssertions') 5 | 6 | /** 7 | * 8 | * @param {Object} orderIdOrCid 'xxx' or { orderId: 'xxx' } or { cid: 'yyy' } 9 | */ 10 | module.exports = async (dvf, orderIdOrCid, nonce, signature) => { 11 | const input = extractOrderIdsInput(orderIdOrCid) 12 | validateAssertions(dvf, input) 13 | 14 | const endpoint = '/v1/trading/w/cancelOrder' 15 | 16 | const data = input 17 | 18 | return post(dvf, endpoint, nonce, signature, data) 19 | } 20 | -------------------------------------------------------------------------------- /nix/packages/yarn-berry.nix: -------------------------------------------------------------------------------- 1 | # TODO: move to rhinofi/yarn-berry-cjs 2 | { 3 | lib, 4 | nodejs, 5 | runCommandNoCC, 6 | yarn-berry-source, 7 | 8 | name ? "yarn" 9 | }: 10 | let 11 | package = runCommandNoCC 12 | name 13 | { buildInputs = [ nodejs ]; } 14 | '' 15 | binName=${name} 16 | binPath=$out/bin/$binName 17 | mkdir -p $(dirname $binPath) 18 | cp ${yarn-berry-source}/bin/$binName $binPath 19 | patchShebangs $binPath 20 | '' 21 | ; 22 | in package.overrideAttrs (old: { 23 | passthru = (old.passthru or {}) // { 24 | inherit nodejs; 25 | yarn-source-path = "${yarn-berry-source}/bin/yarn"; 26 | }; 27 | }) -------------------------------------------------------------------------------- /src/api/amm/getRewardsLockedState.js: -------------------------------------------------------------------------------- 1 | const { Joi } = require('@rhino.fi/dvf-utils') 2 | 3 | const get = require('../../lib/dvf/get-authenticated') 4 | const validateWithJoi = require('../../lib/validators/validateWithJoi') 5 | 6 | const schema = Joi.object({ 7 | poolName: Joi.string() 8 | }) 9 | 10 | const validateData = validateWithJoi(schema)('INVALID_METHOD_ARGUMENT')({ 11 | context: 'getRewardsLockedState' 12 | }) 13 | 14 | module.exports = (dvf, data, nonce, signature) => { 15 | const { poolName } = validateData(data) 16 | const endpoint = `/v1/trading/amm/rewardsLockedState/${poolName}` 17 | 18 | return get(dvf, endpoint, nonce, signature) 19 | } 20 | -------------------------------------------------------------------------------- /src/api/eth/getNetwork.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Returns current network id and name 3 | * see: 4 | * https://github.com/MetaMask/faq/blob/master/DEVELOPERS.md#construction_worker-network-check 5 | * 6 | **/ 7 | module.exports = async (dvf, chain) => { 8 | const web3 = dvf.eth.getWeb3ForChain(chain) 9 | const id = await web3.eth.net.getId() 10 | 11 | const labels = { 12 | '1': 'mainnet', 13 | '2': 'morden', 14 | '3': 'ropsten', 15 | '4': 'Rinkeby', 16 | '5': 'Goerli', 17 | '42': 'Kovan', 18 | '137': 'Matic Network', 19 | '80001': 'Mumbai' 20 | } 21 | 22 | return { 23 | id: id, 24 | name: labels[id] || 'unknown' 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/api/amm/poolUserRewards.js: -------------------------------------------------------------------------------- 1 | const { Joi } = require('@rhino.fi/dvf-utils') 2 | const getAuthenticated = require('../../lib/dvf/get-authenticated') 3 | const validateWithJoi = require('../../lib/validators/validateWithJoi') 4 | 5 | const schema = Joi.object({ 6 | poolName: Joi.string() 7 | }) 8 | 9 | const validateInputs = validateWithJoi(schema)('INVALID_METHOD_ARGUMENT')({ 10 | context: 'poolUserRewards' 11 | }) 12 | 13 | module.exports = async (dvf, data, nonce, signature) => { 14 | const { poolName } = validateInputs(data) 15 | const endpoint = `/v1/trading/amm/poolUserRewards/${poolName}` 16 | return getAuthenticated(dvf, endpoint, nonce, signature) 17 | } 18 | -------------------------------------------------------------------------------- /src/api/amm/poolUserLpBalance.js: -------------------------------------------------------------------------------- 1 | const { Joi } = require('@rhino.fi/dvf-utils') 2 | const getAuthenticated = require('../../lib/dvf/get-authenticated') 3 | const validateWithJoi = require('../../lib/validators/validateWithJoi') 4 | 5 | const schema = Joi.object({ 6 | poolName: Joi.string() 7 | }) 8 | 9 | const validateInputs = validateWithJoi(schema)('INVALID_METHOD_ARGUMENT')({ 10 | context: 'poolUserLpBalance' 11 | }) 12 | 13 | module.exports = async (dvf, data, nonce, signature) => { 14 | const { poolName } = validateInputs(data) 15 | const endpoint = `/v1/trading/amm/poolLpBalance/${poolName}` 16 | return getAuthenticated(dvf, endpoint, nonce, signature) 17 | } 18 | -------------------------------------------------------------------------------- /src/api/contract/abi/WithdrawalBalanceReader.abi.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | inputs: [ 4 | { internalType: "address", name: "_starkContract", type: "address" }, 5 | ], 6 | stateMutability: "nonpayable", 7 | type: "constructor", 8 | }, 9 | { 10 | inputs: [ 11 | { internalType: "uint256[]", name: "_tokenIds", type: "uint256[]" }, 12 | { internalType: "uint256", name: "_whoKey", type: "uint256" }, 13 | ], 14 | name: "allWithdrawalBalances", 15 | outputs: [ 16 | { internalType: "uint256[]", name: "balances", type: "uint256[]" }, 17 | ], 18 | stateMutability: "view", 19 | type: "function", 20 | }, 21 | ]; 22 | -------------------------------------------------------------------------------- /src/api/contract/fullWithdrawalRequest.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../../lib/dvf/DVFError') 2 | 3 | module.exports = async (dvf, token, tradingKey) => { 4 | const { starkVaultId } = dvf.token.getTokenInfo(token) 5 | 6 | tradingKey = tradingKey || dvf.config.starkKeyHex 7 | 8 | const args = [tradingKey, starkVaultId] 9 | 10 | const action = 'fullWithdrawalRequest' 11 | 12 | try { 13 | return dvf.eth.send( 14 | dvf.contract.abi.getStarkEx(), 15 | dvf.config.DVF.starkExContractAddress, 16 | action, 17 | args 18 | ) 19 | } catch (e) { 20 | console.log(e) 21 | throw new DVFError('ERR_FULL_WITHDRAWAL_REQUEST') 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /nix/netrc-create.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -ueo pipefail 3 | 4 | github_token=''${GITHUB_TOKEN-} 5 | 6 | if [[ -z $github_token ]]; then 7 | read -s github_token 8 | fi 9 | 10 | github_user=${1:-dvf-ci} 11 | netrc_path=${2:-$HOME/.netrc} 12 | nix_conf_path=${3:-$HOME/.config/nix/nix.conf} 13 | 14 | # Adding leaidng newline incase existing file doesn't end with one. 15 | cat <> "$netrc_path" 16 | 17 | machine github.com 18 | login $github_user 19 | password $github_token 20 | EOF 21 | 22 | mkdir -p $(dirname "$nix_conf_path") 23 | 24 | # Adding leaidng newline incase existing file doesn't end with one. 25 | echo -e "\nnetrc-file = $netrc_path" >> "$nix_conf_path" 26 | -------------------------------------------------------------------------------- /src/api/walletFailedEvent.js: -------------------------------------------------------------------------------- 1 | const { Joi } = require('@rhino.fi/dvf-utils') 2 | 3 | const postGeneric = require('../lib/dvf/post-generic') 4 | const validateWithJoi = require('../lib/validators/validateWithJoi') 5 | 6 | const schema = Joi.object({ 7 | walletType: Joi.string(), 8 | errorText: Joi.string() 9 | }) 10 | 11 | const validateData = validateWithJoi(schema)('INVALID_METHOD_ARGUMENT')({ 12 | context: 'walletFailedEvent' 13 | }) 14 | 15 | module.exports = async (dvf, data) => { 16 | const endpoint = '/v1/trading/w/walletFailedEvent' 17 | const { walletType, errorText } = validateData(data) 18 | return postGeneric(dvf, endpoint, { walletType, errorText }) 19 | } 20 | -------------------------------------------------------------------------------- /src/api/amm/poolUserAccruedFees.js: -------------------------------------------------------------------------------- 1 | const { Joi } = require('@rhino.fi/dvf-utils') 2 | const getAuthenticated = require('../../lib/dvf/get-authenticated') 3 | const validateWithJoi = require('../../lib/validators/validateWithJoi') 4 | 5 | const schema = Joi.object({ 6 | poolName: Joi.string() 7 | }) 8 | 9 | const validateInputs = validateWithJoi(schema)('INVALID_METHOD_ARGUMENT')({ 10 | context: 'poolUserAccruedFees' 11 | }) 12 | 13 | module.exports = async (dvf, data, nonce, signature) => { 14 | const { poolName } = validateInputs(data) 15 | const endpoint = `/v1/trading/amm/poolUserAccruedFees/${poolName}` 16 | return getAuthenticated(dvf, endpoint, nonce, signature) 17 | } 18 | -------------------------------------------------------------------------------- /src/api/walletSuccessEvent.js: -------------------------------------------------------------------------------- 1 | const { Joi } = require('@rhino.fi/dvf-utils') 2 | 3 | const postGeneric = require('../lib/dvf/post-generic') 4 | const validateWithJoi = require('../lib/validators/validateWithJoi') 5 | 6 | const schema = Joi.object({ 7 | walletType: Joi.string(), 8 | successText: Joi.string() 9 | }) 10 | 11 | const validateData = validateWithJoi(schema)('INVALID_METHOD_ARGUMENT')({ 12 | context: 'walletSuccessEvent' 13 | }) 14 | 15 | module.exports = async (dvf, data) => { 16 | const endpoint = '/v1/trading/w/walletSuccessEvent' 17 | const { walletType, successText } = validateData(data) 18 | return postGeneric(dvf, endpoint, { walletType, successText }) 19 | } 20 | -------------------------------------------------------------------------------- /src/api/amm/poolTvlHistory.js: -------------------------------------------------------------------------------- 1 | const { Joi } = require('@rhino.fi/dvf-utils') 2 | const get = require('../../lib/dvf/get-generic') 3 | const validateWithJoi = require('../../lib/validators/validateWithJoi') 4 | 5 | const schema = Joi.object({ 6 | poolName: Joi.string(), 7 | duration: Joi.string().valid('1d', '1w', '1m', '1y') 8 | }) 9 | 10 | const validateInputs = validateWithJoi(schema)('INVALID_METHOD_ARGUMENT')({ 11 | context: 'poolTvlHistory' 12 | }) 13 | 14 | module.exports = async (dvf, data) => { 15 | const { poolName, duration } = validateInputs(data) 16 | const endpoint = `/v1/trading/amm/poolTvlHistory/${poolName}?duration=${duration}` 17 | return get(dvf, endpoint) 18 | } 19 | -------------------------------------------------------------------------------- /src/api/withdrawOnchain.test.js: -------------------------------------------------------------------------------- 1 | const nock = require('nock') 2 | const instance = require('./test/helpers/instance') 3 | const mockGetConf = require('./test/fixtures/getConf') 4 | 5 | let dvf 6 | 7 | describe.skip('dvf.withdrawOnchain', () => { 8 | beforeAll(async () => { 9 | nock.cleanAll() 10 | mockGetConf() 11 | dvf = await instance() 12 | }) 13 | 14 | beforeEach(() => { 15 | nock.cleanAll() 16 | }) 17 | 18 | it(`Withdraw ETH or ERC20 from onchain call to stark ex`, async () => { 19 | mockGetConf() 20 | const token = 'ETH' 21 | 22 | const response = await dvf.withdrawOnchain(token) 23 | expect(response.transactionHash).toMatch(/[\da-f]/i) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /src/api/contract/depositFromSidechainBridge.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = (dvf, { bridgeContractAddress, tokenAddress, baseUnitAmount }, options) => { 3 | const [action, args, value] = tokenAddress === undefined // Native token 4 | // For native token (ie: MATIC on Polygon), use specifoc method and pass amount as value 5 | ? ['depositNative', [], baseUnitAmount] 6 | // For other tokens, use amount as argument (in baseUnits, unlike starkEx deposits) 7 | : ['deposit', [tokenAddress, baseUnitAmount]] 8 | 9 | return dvf.eth.send( 10 | dvf.contract.abi.getSidechainBridgeInterface(), 11 | bridgeContractAddress, 12 | action, 13 | args, 14 | value, 15 | options 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /src/api/contract/register.js: -------------------------------------------------------------------------------- 1 | module.exports = async (dvf, tradingKey, deFiSignature, ethAddress) => { 2 | throw new Error('Direct Registration to StarkExv4 is not supported via this function') 3 | 4 | // Signature generation for StarkExv4 to be implemented and 5 | // Tested. The previous logic used exchange's signature 6 | // to register users 7 | 8 | // ethAddress = ethAddress || dvf.get('account') 9 | 10 | // const args = [ethAddress, `0x${tradingKey}`, deFiSignature] 11 | 12 | // await dvf.eth.send( 13 | // dvf.contract.abi.getStarkEx(), 14 | // dvf.config.DVF.starkExContractAddress, 15 | // 'registerEthAddress', 16 | // args 17 | // ) 18 | 19 | // return true 20 | } 21 | -------------------------------------------------------------------------------- /src/api/amm/poolsUserData.js: -------------------------------------------------------------------------------- 1 | const { Joi } = require('@rhino.fi/dvf-utils') 2 | const getAuthenticated = require('../../lib/dvf/get-authenticated') 3 | const validateWithJoi = require('../../lib/validators/validateWithJoi') 4 | 5 | const schema = Joi.object({ 6 | pools: Joi.alternatives().try(Joi.string(), Joi.array().items(Joi.string())) 7 | }) 8 | 9 | const validateInputs = validateWithJoi(schema)('INVALID_METHOD_ARGUMENT')({ 10 | context: 'poolsUserData' 11 | }) 12 | 13 | module.exports = async (dvf, data, nonce, signature) => { 14 | const { pools } = validateInputs(data) 15 | const endpoint = '/v1/trading/amm/poolsUserData' 16 | return getAuthenticated(dvf, endpoint, nonce, signature, { pools }) 17 | } 18 | -------------------------------------------------------------------------------- /src/api/amm/postRewardsLockedState.js: -------------------------------------------------------------------------------- 1 | const { Joi } = require('@rhino.fi/dvf-utils') 2 | 3 | const post = require('../../lib/dvf/post-authenticated') 4 | const validateWithJoi = require('../../lib/validators/validateWithJoi') 5 | 6 | const schema = Joi.object({ 7 | poolName: Joi.string(), 8 | wrapped: Joi.boolean() 9 | }) 10 | 11 | const validateData = validateWithJoi(schema)('INVALID_METHOD_ARGUMENT')({ 12 | context: 'postRewardsLockedState' 13 | }) 14 | 15 | module.exports = (dvf, data, nonce, signature) => { 16 | const { poolName, wrapped } = validateData(data) 17 | const endpoint = `/v1/trading/amm/rewardsLockedState/${poolName}` 18 | return post(dvf, endpoint, nonce, signature, { wrapped }) 19 | } 20 | -------------------------------------------------------------------------------- /src/api/test/fixtures/feeRate.js: -------------------------------------------------------------------------------- 1 | const nock = require('nock') 2 | 3 | module.exports = () => { 4 | const httpResponse = { 5 | address: '0x65CEEE596B2aba52Acc09f7B6C81955C1DB86404', 6 | timestamp: 1568959208939, 7 | fees: { 8 | small: { threshold: 0, feeBps: 25 }, 9 | medium: { threshold: 500, feeBps: 21 }, 10 | large: { threshold: 2000, feeBps: 20 } 11 | }, 12 | signature: '0x52f18b47494e465aa4ed0f0f123fae4d40d3ac0862b61862e6cc8e5a119dbfe1061a4ee381092a10350185071f4829dbfd6c5f2e26df76dee0593cbe3cbd87321b' 13 | } 14 | 15 | nock('https://api.stg.rhino.fi') 16 | .get('/api/v1/feeRate/' + '0x65CEEE596B2aba52Acc09f7B6C81955C1DB86404') 17 | .reply(200, httpResponse) 18 | } 19 | -------------------------------------------------------------------------------- /src/api/walletConnect/getPublicKey.js: -------------------------------------------------------------------------------- 1 | const Eth = require('@ledgerhq/hw-app-eth').default 2 | const selectTransport = require('../../ledger/selectTransport') 3 | 4 | module.exports = async (dvf, path) => { 5 | const Transport = selectTransport(dvf.isBrowser) 6 | const transport = await Transport.create() 7 | const eth = new Eth(transport) 8 | const { address } = await eth.getAddress(path) 9 | const starkPath = dvf.stark.ledger.getPath(address) 10 | const tempKey = (await eth.starkGetPublicKey(starkPath)).toString('hex') 11 | const starkPublicKey = { 12 | x: tempKey.substr(2, 64), 13 | y: tempKey.substr(66) 14 | } 15 | await transport.close() 16 | return dvf.stark.formatStarkPublicKey(starkPublicKey) 17 | } 18 | -------------------------------------------------------------------------------- /src/lib/dvf/token/getTokenAddressFromTokenInfoOrThrow.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../DVFError') 2 | 3 | module.exports = (tokenInfo, chain) => { 4 | const tokenAddress = tokenInfo.tokenAddressPerChain[chain] 5 | // Token unsupported for that chain if not defined 6 | if (!tokenAddress) { 7 | throw new DVFError('UNSUPPORTED_TOKEN_FOR_CHAIN', {tokenInfo, chain}) 8 | // ETH for ETHEREUM, MATIC for Polygon/Matic... 9 | // 'native' case kept for retro-compatibility / potential revert cases but means the same thing 10 | } else if (tokenAddress === 'native' || tokenAddress === '0x0000000000000000000000000000000000000000') { 11 | return undefined 12 | } 13 | // Standard ERC20 address 14 | return tokenAddress 15 | } 16 | -------------------------------------------------------------------------------- /src/api/getVaultIdAndStarkKey.js: -------------------------------------------------------------------------------- 1 | const { Joi } = require('@rhino.fi/dvf-utils') 2 | 3 | const get = require('../lib/dvf/get-authenticated') 4 | const validateWithJoi = require('../lib/validators/validateWithJoi') 5 | 6 | const schema = Joi.object({ 7 | token: Joi.string(), 8 | targetEthAddress: Joi.ethAddress() 9 | }) 10 | 11 | const validateInputs = validateWithJoi(schema)('INVALID_METHOD_ARGUMENT')({ 12 | context: `getVaultIdAndStarkKey` 13 | }) 14 | 15 | module.exports = async (dvf, data, nonce, signature) => { 16 | const { token, targetEthAddress } = validateInputs(data) 17 | 18 | const endpoint = '/v1/trading/r/vaultIdAndStarkKey' 19 | 20 | return get(dvf, endpoint, nonce, signature, { token, targetEthAddress }) 21 | } 22 | -------------------------------------------------------------------------------- /src/api/topPerformersTokens.js: -------------------------------------------------------------------------------- 1 | const { Joi } = require('@rhino.fi/dvf-utils') 2 | const get = require('../lib/dvf/get-generic') 3 | const validateWithJoi = require('../lib/validators/validateWithJoi') 4 | 5 | const schema = Joi.object({ 6 | count: Joi.number(), 7 | minMarketCap: Joi.number().optional(), 8 | chains: Joi.array().items(Joi.string()).optional() 9 | }) 10 | 11 | const validateInputs = validateWithJoi(schema)('INVALID_METHOD_ARGUMENT')({ 12 | context: 'topPerformersTokensQuery' 13 | }) 14 | module.exports = async (dvf, data) => { 15 | const { count, minMarketCap, chains } = validateInputs(data) 16 | const endpoint = '/v1/trading/r/topPerformersTokens' 17 | return get(dvf, endpoint, { count, minMarketCap, chains }) 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/stark/createOrderMessage.js: -------------------------------------------------------------------------------- 1 | const sw = require('@rhino.fi/starkware-crypto') 2 | const DVFError = require('../dvf/DVFError') 3 | 4 | module.exports = (dvf, starkOrder) => { 5 | try { 6 | const message = (dvf.sw || sw).getLimitOrderMsgHash( 7 | starkOrder.vaultIdSell, 8 | starkOrder.vaultIdBuy, 9 | starkOrder.amountSell, 10 | starkOrder.amountBuy, 11 | starkOrder.tokenSell, 12 | starkOrder.tokenBuy, 13 | starkOrder.nonce, 14 | starkOrder.expirationTimestamp 15 | ) 16 | return dvf.sw ? message.toString(16) : message 17 | } catch (err) { 18 | console.error('ERR_CREATING_STARK_ORDER_MESSAGE: error', err) 19 | throw new DVFError('ERR_CREATING_STARK_ORDER_MESSAGE') 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/api/getGasPrice.js: -------------------------------------------------------------------------------- 1 | const { get } = require('request-promise') 2 | 3 | module.exports = async (dvf) => { 4 | const defaultGasPrice = dvf.config.defaultGasPrice 5 | const oldGasPrice = { fast: defaultGasPrice * 1.2, average: defaultGasPrice * 0.9, cheap: defaultGasPrice * 0.8 } 6 | 7 | const endpoint = '/v1/trading/r/getGasPrice' 8 | const url = dvf.config.api + endpoint 9 | 10 | try { 11 | const newGasPrice = await get(url, { json: true }) 12 | dvf.config.defaultGasPrice = newGasPrice.fast || dvf.config.defaultGasPrice 13 | return newGasPrice || oldGasPrice 14 | } 15 | catch (e) { 16 | // TODO: user dvf-logger 17 | // console.log('failed to get gas price from dvf pub api') 18 | return oldGasPrice 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/api/getFeeRate.js: -------------------------------------------------------------------------------- 1 | const { Joi } = require('@rhino.fi/dvf-utils') 2 | const get = require('../lib/dvf/get-authenticated') 3 | const validateWithJoi = require('../lib/validators/validateWithJoi') 4 | 5 | const schema = Joi.object({ 6 | feature: Joi.string(), 7 | symbol: Joi.string() 8 | }) 9 | 10 | const validateArg0 = validateWithJoi(schema, { presence: 'optional' })('INVALID_METHOD_ARGUMENT')({ 11 | context: `getFeeRate` 12 | }) 13 | 14 | const endpoint = '/v1/trading/r/feeRate' 15 | /** 16 | * 17 | * Retrieve feeRate based on rhino.fi feeRate rules 18 | */ 19 | module.exports = async (dvf, data, nonce, signature) => { 20 | const { feature, symbol } = validateArg0(data || {}) 21 | return get(dvf, endpoint, nonce, signature, { feature, symbol }) 22 | } 23 | -------------------------------------------------------------------------------- /src/api/getTokenHolders.test.js: -------------------------------------------------------------------------------- 1 | const nock = require('nock') 2 | const instance = require('./test/helpers/instance') 3 | 4 | const mockGetConf = require('./test/fixtures/getConf') 5 | 6 | let dvf 7 | 8 | describe('dvf.getTokenHolders', () => { 9 | beforeAll(async () => { 10 | mockGetConf() 11 | dvf = await instance() 12 | }) 13 | 14 | it('Returns the token holders count recieved from the API....', async () => { 15 | const apiResponse = '15' 16 | const token = 'DVF' 17 | 18 | nock(dvf.config.api) 19 | .get('/v1/trading/r/getTokenHolders') 20 | .query({ token }) 21 | .reply(200, apiResponse) 22 | 23 | const tokenHolders = await dvf.getTokenHolders(token) 24 | expect(tokenHolders).toEqual(apiResponse) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /src/lib/dvf/request.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Alt lib for authenticed requests, uses dvf-utils request 3 | * and allows cancellable signal 4 | */ 5 | const { request } = require('@rhino.fi/dvf-utils') 6 | const addAuthHeadersOrData = require('./addAuthHeadersOrData') 7 | 8 | module.exports = async (dvf, method, endpoint, nonce, signature, { 9 | data = {}, 10 | headers = {}, 11 | signal = null 12 | } = {}) => { 13 | const { headers: requestHeaders, data: requestData } = await addAuthHeadersOrData( 14 | dvf, nonce, signature, { headers, data } 15 | ) 16 | 17 | const dataKey = method === 'get' ? 'qs' : 'data' 18 | 19 | return request[method](dvf.config.api + endpoint, { 20 | headers: requestHeaders, 21 | [dataKey]: requestData, 22 | signal 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /src/lib/stark/starkSign.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../../lib/dvf/DVFError') 2 | const sw = require('@rhino.fi/starkware-crypto') 3 | 4 | const sigKeysToString = sig => ({ 5 | r: sig.r.toString(16), 6 | s: sig.s.toString(16) 7 | }) 8 | 9 | module.exports = (dvf, starkKeyPair, starkMessage) => { 10 | let starkSignature 11 | 12 | if (!starkKeyPair) { 13 | throw new Error('starkKeyPair is required') 14 | } 15 | if (!starkMessage) { 16 | throw new Error('starkMessage required') 17 | } 18 | 19 | try { 20 | starkSignature = sigKeysToString( 21 | (dvf.sw || sw).sign(starkKeyPair, starkMessage) 22 | ) 23 | } catch (error) { 24 | throw new DVFError('ERR_CREATING_STARK_SIGNATURE', { error }) 25 | } 26 | 27 | return starkSignature 28 | } 29 | -------------------------------------------------------------------------------- /src/api/contract/getAllWithdrawalBalancesEthAddress.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../../lib/dvf/DVFError') 2 | 3 | module.exports = async (dvf, starkTokenIds, address) => { 4 | if (!address) { 5 | throw new Error('getWithdrawalBalanceEthAddress: address is required') 6 | } 7 | 8 | const args = [starkTokenIds, address] 9 | 10 | try { 11 | return (withdrawalBalance = await dvf.eth.call( 12 | dvf.contract.abi.WithdrawalBalanceReader, 13 | dvf.config.DVF.registrationAndDepositInterfaceAddress, 14 | 'allWithdrawalBalances', 15 | args, 16 | { chain: 'ETHEREUM' } 17 | )) 18 | } catch (e) { 19 | console.warn('contract/getAllWithdrawalBalancesEthAddress error is: ', e) 20 | throw new DVFError('ERR_GETTING_AVAILABLE_WITHDRAWAL') 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/api/getTokenLiquidityLeft.test.js: -------------------------------------------------------------------------------- 1 | const nock = require('nock') 2 | const instance = require('./test/helpers/instance') 3 | 4 | const mockGetConf = require('./test/fixtures/getConf') 5 | 6 | let dvf 7 | 8 | describe('dvf.getTokenLiquidityLeft', () => { 9 | beforeAll(async () => { 10 | mockGetConf() 11 | dvf = await instance() 12 | }) 13 | 14 | it('Returns the token liquidity left recieved from the API....', async () => { 15 | const apiResponse = '0.3245' 16 | const token = 'DVF' 17 | 18 | nock(dvf.config.api) 19 | .get('/v1/trading/r/getTokenLiquidityLeft') 20 | .query({ token }) 21 | .reply(200, apiResponse) 22 | 23 | const liquidity = await dvf.getTokenLiquidityLeft(token) 24 | expect(liquidity).toEqual(apiResponse) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /src/lib/dvf/token/getSafeQuantizedAmountOrThrow.js: -------------------------------------------------------------------------------- 1 | 2 | const { Long, toQuantizedAmountBN, fromQuantizedToBaseUnitsBN } = require('@rhino.fi/dvf-utils') 3 | 4 | module.exports = (baseUnitAmount, tokenInfo) => { 5 | const quantisedAmount = toQuantizedAmountBN(tokenInfo, baseUnitAmount) 6 | 7 | if (quantisedAmount.isLessThan(1)) { 8 | throw new Error( 9 | `Amount too small, got: ${baseUnitAmount}, allowed minumum: ` + 10 | `${fromQuantizedToBaseUnitsBN(tokenInfo, 1)}` 11 | ) 12 | } 13 | 14 | if (quantisedAmount.isGreaterThan(Long.MAX_VALUE)) { 15 | throw new Error( 16 | `Amount too large, got: ${baseUnitAmount} allowed minumum: ` + 17 | `${fromQuantizedToBaseUnitsBN(tokenInfo, Long.MAX_VALUE)}` 18 | ) 19 | } 20 | 21 | return quantisedAmount 22 | } 23 | -------------------------------------------------------------------------------- /examples/src/getWithdrawal.js: -------------------------------------------------------------------------------- 1 | let withdrawalId 2 | const token = undefined 3 | const userAddress = rhinofi.get('account') 4 | const withdrawals = await rhinofi.getWithdrawals(token, userAddress) 5 | 6 | if (withdrawals.length === 0) { 7 | console.log('creating a new withdrawal') 8 | 9 | const token = 'ETH' 10 | const amount = 0.1 11 | 12 | const withdrawalResponse = await rhinofi.transferAndWithdraw({ 13 | recipientEthAddress: rhinofi.get('account'), 14 | token, 15 | amount 16 | }) 17 | 18 | console.log('withdrawalResponse', withdrawalResponse) 19 | withdrawalId = withdrawalResponse._id 20 | } 21 | else { 22 | withdrawalId = withdrawals[0]._id 23 | } 24 | 25 | const getWithdrawalResponse = await rhinofi.getWithdrawal(withdrawalId) 26 | 27 | logExampleResult(getWithdrawalResponse) -------------------------------------------------------------------------------- /src/api/contract/isApproved.js: -------------------------------------------------------------------------------- 1 | const getTokenAddressFromTokenInfoOrThrow = require('../../lib/dvf/token/getTokenAddressFromTokenInfoOrThrow') 2 | 3 | /** 4 | * Check if a token is approved for locking 5 | */ 6 | module.exports = (dvf, token, chain, spender = dvf.config.DVF.starkExContractAddress) => { 7 | // REVIEW: shall we throw if token is ETH or USDT ? 8 | const tokenInfo = dvf.token.getTokenInfo(token) 9 | const tokenAddress = getTokenAddressFromTokenInfoOrThrow(tokenInfo, chain) 10 | 11 | const args = [ 12 | dvf.get('account'), // address _owner 13 | spender // address _spender 14 | ] 15 | 16 | const action = 'allowance' 17 | 18 | return dvf.eth.call( 19 | dvf.contract.abi.token, 20 | tokenAddress, 21 | action, 22 | args, 23 | { chain } 24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /src/lib/stark/starkSignAuth.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../../lib/dvf/DVFError') 2 | const sw = require('@rhino.fi/starkware-crypto') 3 | 4 | const sigKeysToString = sig => ({ 5 | r: sig.r.toString(16), 6 | s: sig.s.toString(16), 7 | recoveryParam: sig.recoveryParam 8 | }) 9 | 10 | module.exports = (dvf, tradingKey, nonce) => { 11 | if (!tradingKey) { 12 | throw new Error('tradingKey is required') 13 | } 14 | if (!nonce) { 15 | throw new Error('nonce is required') 16 | } 17 | 18 | try { 19 | const {starkKeyPair} = dvf.stark.createKeyPair(tradingKey) 20 | return sigKeysToString( 21 | (dvf.sw || sw).ec.sign(nonce, starkKeyPair, {canonical: true}) 22 | ) 23 | } catch (error) { 24 | throw new DVFError('ERR_CREATING_STARK_SIGNATURE', { error }) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/api/test/helpers/instance.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creats a client instance for testing 3 | **/ 4 | const getWeb3 = require('../../../../examples/helpers/getWeb3') 5 | 6 | const RhinofiClientFactory = require('../../../index') 7 | 8 | module.exports = async () => { 9 | const rpcUrl = process.env.RPC_URL 10 | const privateKey = process.env.PRIVATE_ETH_KEY 11 | 12 | const { web3 } = getWeb3(privateKey, rpcUrl) 13 | 14 | const gasStationApiKey = process.env.ETH_GAS_STATION_KEY || '' 15 | 16 | const config = { gasStationApiKey } 17 | 18 | // It's possible to overwrite the API address with the testnet address 19 | // for example like this: 20 | // config.api = 'https://api.stg.rhino.fi' 21 | // config.api = 'http://localhost:7777/v1/trading' 22 | return RhinofiClientFactory(web3, config) 23 | } 24 | -------------------------------------------------------------------------------- /src/api/account/permissions.js: -------------------------------------------------------------------------------- 1 | const post = require('../../lib/dvf/post-authenticated') 2 | 3 | /** 4 | * Get all permissions for the authenticated user 5 | */ 6 | const getPermissions = (dvf, nonce, signature) => { 7 | const endpoint = '/v1/trading/r/getPublicUserPermissions' 8 | 9 | return post(dvf, endpoint, nonce, signature, {}) 10 | } 11 | 12 | /** 13 | * Set a certain permission for the authenticated user 14 | * @param {string} key permission key 15 | * @param {boolean} value permission value 16 | */ 17 | const setPermissions = (dvf, { key, value }, nonce, signature) => { 18 | const endpoint = '/v1/trading/r/setPublicUserPermissions' 19 | 20 | return post(dvf, endpoint, nonce, signature, { key, value }) 21 | } 22 | 23 | module.exports = { 24 | getPermissions, 25 | setPermissions 26 | } 27 | -------------------------------------------------------------------------------- /src/api/getGasPrice.test.js: -------------------------------------------------------------------------------- 1 | const nock = require('nock') 2 | const instance = require('./test/helpers/instance') 3 | 4 | const mockGetConf = require('./test/fixtures/getConf') 5 | const mockGasPrice = require('./test/fixtures/mockGasPrice') 6 | 7 | let dvf 8 | 9 | describe('dvf.getGasPrice', () => { 10 | beforeAll(async () => { 11 | nock.cleanAll() 12 | mockGetConf() 13 | dvf = await instance() 14 | }) 15 | 16 | beforeEach(() => { 17 | nock.cleanAll() 18 | }) 19 | 20 | it('Returns gas price range', async () => { 21 | const apiResponse = { 22 | cheap: 700000000, 23 | average: 600000000, 24 | fast: 500000000 25 | } 26 | mockGasPrice() 27 | const response = await dvf.getGasPrice() 28 | 29 | expect(response).toMatchObject(apiResponse) 30 | }) 31 | }) 32 | -------------------------------------------------------------------------------- /src/lib/validators/validateProps.js: -------------------------------------------------------------------------------- 1 | const FP = require('lodash/fp') 2 | const validateAssertions = require('./validateAssertions') 3 | 4 | const validators = { 5 | orderId: require('./orderId'), 6 | symbol: require('./symbol'), 7 | token: require('./token'), 8 | nonce: require('./nonce'), 9 | signature: require('./deFiSignature'), 10 | amount: require('./amount'), 11 | price: require('./price'), 12 | starkPublicKey: require('./starkPublicKey'), 13 | starkKeyPair: require('./starkKeyPair'), 14 | ethAddress: require('./ethAddress'), 15 | deFiSignature: require('./deFiSignature'), 16 | starkPrivateKey: require('./starkPrivateKey'), 17 | withdrawalId: require('./withdrawalId') 18 | } 19 | 20 | module.exports = (dvf, props, obj) => validateAssertions( 21 | dvf, 22 | FP.pick(props, obj) 23 | ) 24 | -------------------------------------------------------------------------------- /src/api/amm/postAmmFundingOrders.js: -------------------------------------------------------------------------------- 1 | const R = require('ramda') 2 | 3 | const post = require('../../lib/dvf/post-authenticated') 4 | const validateWithJoi = require('../../lib/validators/validateWithJoi') 5 | const { fundOrderDataSchema } = require('./schemas') 6 | 7 | const validateData = validateWithJoi(fundOrderDataSchema)('INVALID_METHOD_ARGUMENT')({ 8 | context: 'postAmmFundingOrder' 9 | }) 10 | 11 | const endpoint = '/v1/trading/amm/fundingOrders' 12 | 13 | module.exports = async (dvf, data, nonce, signature) => post( 14 | dvf, endpoint, nonce, signature, await R.compose( 15 | // Only sign if there is no starkPublicKey (schema ensures that signatures 16 | // are present if starkPublicKey is provides). 17 | a => a.starkPublicKey ? a : dvf.stark.signAmmFundingOrder(a), 18 | validateData 19 | )(data) 20 | ) 21 | -------------------------------------------------------------------------------- /src/api/sign/nonceSignature.js: -------------------------------------------------------------------------------- 1 | const { 2 | generateAuthMessageForAuthVersion, 3 | formatNonceForAuthVersion 4 | } = require('@rhino.fi/dvf-utils') 5 | /** 6 | * if either message and signature are not provided a 7 | * new nonce/message and signature are created. if nonce 8 | * and signature were provided the same are returned back 9 | */ 10 | module.exports = async (dvf, nonce, signature) => { 11 | if (!(nonce && signature)) { 12 | nonce = Date.now() / 1000 13 | const authVersion = dvf.config.DVF.authVersion || 1 14 | const message = generateAuthMessageForAuthVersion(nonce, authVersion) 15 | // ex: '1615893628.661' (v1) or 'v2-1615893628.661' (v2) 16 | nonce = formatNonceForAuthVersion(nonce, authVersion) 17 | signature = await dvf.sign(message) 18 | } 19 | 20 | return { nonce, signature } 21 | } 22 | -------------------------------------------------------------------------------- /src/api/contract/getAllWithdrawalBalances.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../../lib/dvf/DVFError') 2 | 3 | module.exports = async (dvf, starkTokenIds, tradingKey) => { 4 | tradingKey = tradingKey || dvf.config.starkKeyHex 5 | 6 | if (!tradingKey) { 7 | throw new Error('getWithdrawalBalance: tradingKey is required') 8 | } 9 | 10 | const args = [starkTokenIds, tradingKey] 11 | 12 | try { 13 | return (withdrawalBalance = await dvf.eth.call( 14 | dvf.contract.abi.WithdrawalBalanceReader, 15 | dvf.config.DVF.registrationAndDepositInterfaceAddress, 16 | 'allWithdrawalBalances', 17 | args, 18 | { chain: 'ETHEREUM' } 19 | )) 20 | } catch (e) { 21 | console.warn('contract/getAllWithdrawalBalances error is: ', e) 22 | throw new DVFError('ERR_GETTING_AVAILABLE_WITHDRAWAL') 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/lib/wallet/attachStarkProvider.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../dvf/DVFError') 2 | const makeKeystoreProvider = require('../keystore/index') 3 | const makeLedgerProvider = require('../ledger/index') 4 | 5 | module.exports = (dvf, wallet) => { 6 | if (!wallet) throw new DVFError('WALLET_IS_REQUIRED') 7 | 8 | dvf.config.wallet = wallet 9 | 10 | if (wallet.type === 'tradingKey') { 11 | if (!wallet.meta.starkPrivateKey) throw new DVFError('STARK_PRIVATE_KEY_IS_REQUIRED') 12 | 13 | const provider = makeKeystoreProvider(dvf.sw)(wallet.meta.starkPrivateKey) 14 | dvf.dvfStarkProvider = provider 15 | } else if (wallet.type === 'ledger') { 16 | if (!wallet.meta.path) throw new DVFError('LEDGER_PATH_IS_REQUIRED') 17 | 18 | const provider = makeLedgerProvider(dvf) 19 | dvf.dvfStarkProvider = provider 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/api/register.js: -------------------------------------------------------------------------------- 1 | const post = require('../lib/dvf/post-authenticated') 2 | const DVFError = require('../lib/dvf/DVFError') 3 | 4 | const validateAssertions = require('../lib/validators/validateAssertions') 5 | 6 | module.exports = async (dvf, starkPublicKey, nonce, signature, contractWalletAddress, encryptedTradingKey, meta) => { 7 | validateAssertions(dvf, {starkPublicKey}) 8 | 9 | const tradingKey = starkPublicKey.x 10 | 11 | const endpoint = '/v1/trading/w/register' 12 | 13 | const data = { 14 | starkKey: tradingKey, 15 | nonce, 16 | signature, 17 | ...(encryptedTradingKey && {encryptedTradingKey}), 18 | ...(contractWalletAddress && {contractWalletAddress}), 19 | ...(meta && {meta}) 20 | } 21 | 22 | const userRegistered = await post(dvf, endpoint, nonce, signature, data) 23 | 24 | return userRegistered 25 | } 26 | -------------------------------------------------------------------------------- /src/api/contract/getWithdrawalBalance.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../../lib/dvf/DVFError') 2 | const BN = require('bignumber.js') 3 | 4 | module.exports = async (dvf, token, tradingKey) => { 5 | tradingKey = tradingKey || dvf.config.starkKeyHex 6 | 7 | if (!tradingKey) { 8 | throw new Error('getWithdrawalBalance: tradingKey is required') 9 | } 10 | 11 | const starkTokenId = dvf.config.tokenRegistry[token].starkTokenId 12 | const args = [tradingKey, starkTokenId] 13 | 14 | try { 15 | return (withdrawalBalance = await dvf.eth.call( 16 | dvf.contract.abi.getStarkEx(), 17 | dvf.config.DVF.starkExContractAddress, 18 | 'getWithdrawalBalance', 19 | args 20 | )) 21 | } catch (e) { 22 | console.log('contract/getStarkKey error is: ', e) 23 | throw new DVFError('ERR_GETTING_AVAILABLE_WITHDRAWAL') 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/src/cancelWithdrawal.js: -------------------------------------------------------------------------------- 1 | let withdrawalId 2 | const userAddress = rhinofi.get('account') 3 | const withdrawals = await rhinofi.getWithdrawals(undefined, userAddress) 4 | const nonFastWithdrawals = withdrawals.filter(w => !w.fastWithdrawalData) 5 | 6 | if (nonFastWithdrawals.length === 0) { 7 | console.log('creating a new withdrawal') 8 | 9 | const token = 'ETH' 10 | const amount = 0.1 11 | 12 | const withdrawalResponse = await rhinofi.transferAndWithdraw({ 13 | recipientEthAddress: userAddress, 14 | token, 15 | amount, 16 | }) 17 | 18 | 19 | console.log('withdrawalResponse', withdrawalResponse) 20 | withdrawalId = withdrawalResponse._id 21 | } else { 22 | withdrawalId = nonFastWithdrawals[0]._id 23 | } 24 | 25 | const canceledWithdrawal = await rhinofi.cancelWithdrawal(withdrawalId) 26 | 27 | logExampleResult(canceledWithdrawal) 28 | -------------------------------------------------------------------------------- /src/api/getTokenSaleStartEnd.test.js: -------------------------------------------------------------------------------- 1 | const nock = require('nock') 2 | const instance = require('./test/helpers/instance') 3 | 4 | const mockGetConf = require('./test/fixtures/getConf') 5 | 6 | let dvf 7 | 8 | describe('dvf.getTokenSaleStartEnd', () => { 9 | beforeAll(async () => { 10 | mockGetConf() 11 | dvf = await instance() 12 | }) 13 | 14 | it('Returns the token sale start and end times recieved from the API....', async () => { 15 | const apiResponse = { 16 | start: 126545121, 17 | end: 12852221 18 | } 19 | const token = 'DVF' 20 | 21 | nock(dvf.config.api) 22 | .get('/v1/trading/r/getTokenSaleStartEnd') 23 | .query({ token }) 24 | .reply(200, apiResponse) 25 | 26 | const liquidity = await dvf.getTokenSaleStartEnd(token) 27 | expect(JSON.parse(liquidity)).toEqual(apiResponse) 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /src/api/sign/sign.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Signs toSign asyncronously 3 | * 4 | * For more information, check: 5 | * https://web3js.readthedocs.io/en/1.0/web3-eth.html#sign 6 | */ 7 | module.exports = async (dvf, toSign, signWithStarkProvider) => { 8 | // metamask will take care of the 3rd parameter, "password" 9 | if (dvf.web3.currentProvider.isMetaMask || dvf.web3.currentProvider.isWalletConnect || dvf.web3.currentProvider.isCoinbaseWallet) { 10 | return dvf.web3.eth.personal.sign(toSign, dvf.get('account')) 11 | } else if (dvf.web3.currentProvider.connector) { 12 | return dvf.web3.currentProvider.connector.signPersonalMessage([toSign, dvf.get('account')]) 13 | } else if (signWithStarkProvider) { // Smart-wallet (Authereum) case 14 | return dvf.web3.eth.personal.sign(toSign, dvf.get('account')) 15 | } else { 16 | return dvf.web3.eth.sign(toSign, dvf.get('account')) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/api/bitfinex/transfers.js: -------------------------------------------------------------------------------- 1 | const { Joi } = require('@rhino.fi/dvf-utils') 2 | const get = require('../../lib/dvf/get-generic') 3 | const validateWithJoi = require('../../lib/validators/validateWithJoi') 4 | 5 | const schema = Joi.object({ 6 | user: Joi.ethAddress().optional(), 7 | token: Joi.string().optional(), 8 | memo: Joi.string().optional(), 9 | skip: Joi.number().optional(), 10 | limit: Joi.number().optional() 11 | }) 12 | 13 | const validateInputs = validateWithJoi(schema)('INVALID_METHOD_ARGUMENT')({ 14 | context: 'bitfinex/transfers' 15 | }) 16 | 17 | module.exports = async (dvf, id, data) => { 18 | const endpoint = '/v1/trading/bitfinex/transfers' 19 | 20 | if (id) { 21 | return get(dvf, `${endpoint}/${id}`) 22 | } 23 | 24 | const { user, token, memo, skip, limit } = validateInputs(data) 25 | return get(dvf, endpoint, { user, token, memo, skip, limit }) 26 | } 27 | -------------------------------------------------------------------------------- /src/lib/stark/ledger/getPublicKey.js: -------------------------------------------------------------------------------- 1 | const Eth = require('@ledgerhq/hw-app-eth').default 2 | const selectTransport = require('../../ledger/selectTransport') 3 | 4 | module.exports = async (dvf, path) => { 5 | const Transport = selectTransport(dvf.isBrowser) 6 | let transport 7 | try { 8 | transport = await Transport.create() 9 | const eth = new Eth(transport) 10 | const { address } = await eth.getAddress(path) 11 | const starkPath = dvf.stark.ledger.getPath(address) 12 | const tempKey = (await eth.starkGetPublicKey(starkPath)).toString('hex') 13 | const starkPublicKey = { 14 | x: tempKey.substr(2, 64), 15 | y: tempKey.substr(66) 16 | } 17 | await transport.close() 18 | return dvf.stark.formatStarkPublicKey(starkPublicKey) 19 | } catch (error) { 20 | if (transport) { 21 | await transport.close() 22 | } 23 | throw error 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/lib/dvf/createSignedTransfer.js: -------------------------------------------------------------------------------- 1 | const createSignedTransaction = require('./createSignedTransaction') 2 | const DVFError = require('./DVFError') 3 | 4 | module.exports = async (dvf, transferTransaction) => { 5 | // dvfStarkProvider abstracts specifics of how a public key is obtained. 6 | const { dvfStarkProvider } = dvf 7 | const starkPublicKey = await dvfStarkProvider.getPublicKey() 8 | 9 | const senderPublicKey = `0x${starkPublicKey.x}` 10 | 11 | const existingSenderPublicKey = transferTransaction.senderPublicKey 12 | if ( 13 | existingSenderPublicKey && 14 | senderPublicKey !== existingSenderPublicKey 15 | ) { 16 | throw new DVFError( 17 | 'Unexpected senderPublicKey', 18 | { expected: senderPublicKey, actual: existingSenderPublicKey } 19 | ) 20 | } 21 | 22 | return createSignedTransaction(dvf)({ 23 | ...transferTransaction, 24 | senderPublicKey 25 | }) 26 | } 27 | -------------------------------------------------------------------------------- /src/lib/stark/ledger/getPath.js: -------------------------------------------------------------------------------- 1 | const getBits = require('./getBits') 2 | 3 | /* 4 | Ledger specific helper method to derive Stark Path based on a specific Eth Address 5 | The final derivation logic and the values have not been finalised at moment. 6 | This will require updates once values are finalised for purpose, plugin and application 7 | 8 | accountIndex is currently not used but is available for future use 9 | 10 | reference to derivation logic: https://github.com/ethereum/EIPs/pull/2645 11 | */ 12 | 13 | module.exports = (dvf, address, accountIndex = 0) => { 14 | // derived values 15 | const addressBits = getBits(address) 16 | 17 | const starkPath = ` 18 | ${dvf.config.purpose}' 19 | /${dvf.config.plugin}' 20 | /${dvf.config.application}' 21 | /${addressBits[0]}' 22 | /${addressBits[1]}' 23 | /${accountIndex || dvf.config.accountIndex} 24 | ` 25 | return starkPath.replace(/\s+/g, '') 26 | } 27 | -------------------------------------------------------------------------------- /src/lib/dvf/addAuthHeadersOrData.js: -------------------------------------------------------------------------------- 1 | const makeAuthHeaders = require('./makeAuthHeaders') 2 | 3 | module.exports = async (dvf, nonce, signature, { headers = {}, data = {} }) => { 4 | if ( 5 | // if any of these is set, don't generated nonce/signature as they are 6 | // expected to be passed in by the caller. 7 | // TODO: change this, so that nonce/signature can always be generated 8 | // automatically and is cached on the clients state. 9 | !dvf.config.useTradingKey && !dvf.config.useSignature && 10 | (nonce == null || !signature) 11 | ) { 12 | ({ nonce, signature } = await dvf.sign.nonceSignature(nonce, signature)) 13 | } 14 | 15 | if (dvf.config.useAuthHeader || dvf.config.useTradingKey || dvf.config.useSignature) { 16 | headers = { ...headers, ...makeAuthHeaders(dvf, nonce, signature) } 17 | } else { 18 | data = { ...data, nonce, signature } 19 | } 20 | 21 | return { headers, data } 22 | } 23 | -------------------------------------------------------------------------------- /src/api/amm/getAmmFundingOrderData.js: -------------------------------------------------------------------------------- 1 | const { Joi } = require('@rhino.fi/dvf-utils') 2 | 3 | const get = require('../../lib/dvf/get-authenticated') 4 | const validateWithJoi = require('../../lib/validators/validateWithJoi') 5 | 6 | const schema = Joi.object({ 7 | pool: Joi.string(), 8 | token: Joi.string(), 9 | amount: Joi.bigNumber().greaterThan(0) 10 | }) 11 | 12 | const validateData = validateWithJoi(schema)('INVALID_METHOD_ARGUMENT')({ 13 | context: 'ammGetFundingOrders.js' 14 | }) 15 | 16 | const endpoint = '/v1/trading/amm/fundingOrderData' 17 | 18 | module.exports = (dvf, data, nonce, signature) => { 19 | // convert `amount` to string, since it's passed as a query string parameter, and bigNumber vars aren't converted 20 | const validatedData = validateData(data) 21 | const requestData = { ...validatedData, amount: validatedData.amount.toString() } 22 | 23 | return get(dvf, endpoint, nonce, signature, requestData) 24 | } 25 | -------------------------------------------------------------------------------- /src/api/account/select.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Finds and returns an account based on given id. 3 | * 4 | * id - Can be an index of the array such as 0 or an address 5 | **/ 6 | module.exports = async (dvf, id) => { 7 | // check for ethereum accounts and select a default one 8 | const accounts = await dvf.web3.eth.getAccounts() 9 | 10 | if (typeof id === 'number') { 11 | if (!accounts[id]) { 12 | // console.error('Error: You have no account at index:', +id) 13 | } 14 | // emit and store current account 15 | else { 16 | return dvf.set('account', accounts[id].toLowerCase()) 17 | } 18 | } 19 | 20 | for (let index in accounts) { 21 | if (accounts[index] === id) { 22 | // emit and store current account 23 | return dvf.set( 24 | 'account', 25 | dvf.web3.utils.toChecksumAddress(accounts[index].toLowerCase()) 26 | ) 27 | } 28 | } 29 | 30 | return dvf.set('account', null) 31 | } 32 | -------------------------------------------------------------------------------- /examples/src/submitOrder.js: -------------------------------------------------------------------------------- 1 | const getPriceFromOrderBook = require('./helpers/getPriceFromOrderBook') 2 | 3 | // Submit an order to sell 0.1 Eth for USDT 4 | const symbol = 'ETH:USDT' 5 | const amount = -0.1 6 | const validFor = '0' 7 | const feeRate = '' 8 | 9 | // Gets the price from the order book api and cuts 5% to make sure the order will be settled 10 | const tickersData = await rhinofi.getTickers('ETH:USDT'); 11 | const orderBookPrice = getPriceFromOrderBook(tickersData); 12 | const price = orderBookPrice - orderBookPrice * 0.05; 13 | 14 | const submitOrderResponse = await rhinofi.submitOrder({ 15 | symbol, 16 | amount, 17 | price, 18 | starkPrivateKey: starkPrivKey, 19 | validFor, // Optional 20 | feeRate, // Optional 21 | gid: '1', // Optional 22 | cid: 'mycid-' + Math.random().toString(36).substring(7), // Optional 23 | partnerId: 'P1' // Optional 24 | }) 25 | 26 | logExampleResult(submitOrderResponse) 27 | -------------------------------------------------------------------------------- /src/lib/validators/validateAssertions.js: -------------------------------------------------------------------------------- 1 | const validators = { 2 | id: require('./id'), 3 | orderId: require('./orderId'), 4 | cid: require('./cid'), 5 | symbol: require('./symbol'), 6 | token: require('./token'), 7 | nonce: require('./nonce'), 8 | signature: require('./deFiSignature'), 9 | amount: require('./amount'), 10 | price: require('./price'), 11 | starkPublicKey: require('./starkPublicKey'), 12 | starkKeyPair: require('./starkKeyPair'), 13 | ethAddress: require('./ethAddress'), 14 | deFiSignature: require('./deFiSignature'), 15 | starkPrivateKey: require('./starkPrivateKey'), 16 | withdrawalId: require('./withdrawalId') 17 | } 18 | 19 | module.exports = (dvf, parameters) => { 20 | for (const [key, value] of Object.entries(parameters)) { 21 | 22 | if (!validators[key]) { 23 | continue 24 | } 25 | 26 | const result = validators[key](dvf, value) 27 | 28 | if (result) { 29 | return result 30 | } 31 | } 32 | 33 | return false 34 | } 35 | -------------------------------------------------------------------------------- /src/api/contract/getWithdrawalBalanceEthAddress.js: -------------------------------------------------------------------------------- 1 | const DVFError = require('../../lib/dvf/DVFError') 2 | const BN = require('bignumber.js') 3 | 4 | module.exports = async (dvf, token, address) => { 5 | address = address || dvf.get('account') 6 | 7 | if (!address) { 8 | throw new Error('getWithdrawalBalanceEthAddress: address is required') 9 | } 10 | 11 | const starkTokenId = dvf.config.tokenRegistry[token].starkTokenId 12 | 13 | if (!starkTokenId) { 14 | throw new Error(`getWithdrawalBalanceEthAddress: no starkTokenId for token: ${token}`) 15 | } 16 | 17 | const args = [address, starkTokenId] 18 | 19 | try { 20 | return (withdrawalBalance = await dvf.eth.call( 21 | dvf.contract.abi.getStarkEx(), 22 | dvf.config.DVF.starkExContractAddress, 23 | 'getWithdrawalBalance', 24 | args 25 | )) 26 | } catch (e) { 27 | console.log('contract/getWithdrawalBalance error is: ', e) 28 | throw new DVFError('ERR_GETTING_AVAILABLE_WITHDRAWAL') 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/lib/dvf/DVFError.js: -------------------------------------------------------------------------------- 1 | /** 2 | * For more info on Custom Errors, check: 3 | * https://rclayton.silvrback.com/custom-errors-in-node-js 4 | */ 5 | const REASONS = require('./errorReasons') 6 | 7 | module.exports = class DVFError extends Error { 8 | constructor(errorCode, data) { 9 | super(errorCode) 10 | 11 | // ensure the name of this error is the same as the class name 12 | this.name = this.constructor.name 13 | 14 | // TODO: check if data is not used by Error class 15 | this.data = data 16 | 17 | if (REASONS[errorCode]) { 18 | this.reason = REASONS[errorCode].trim() 19 | } else { 20 | this.reason = errorCode 21 | } 22 | 23 | // This clips the constructor invocation from the stack trace. 24 | // It's not absolutely essential, but it does make the stack trace a little nicer. 25 | // @see Node.js reference (bottom) 26 | if (typeof Error.captureStackTrace === 'function') { 27 | Error.captureStackTrace(this, this.constructor) 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /examples/helpers/getOrCreateActiveOrder.js: -------------------------------------------------------------------------------- 1 | const P = require('aigle') 2 | 3 | const defaultOrderProps = Object.freeze({ 4 | cid: 'mycid-' + Math.random().toString(36).substring(7), 5 | // Order to sell 0.1 ETH at a the price of 100000 USDT per ETH. 6 | symbol: 'ETH:USDT', 7 | amount: -0.1, 8 | // We are asking a hight price to ensure that the order stays on the book 9 | price: 100000 10 | }) 11 | 12 | module.exports = async (rhinofi, starkPrivateKey, orderProps = defaultOrderProps) => { 13 | const orders = await rhinofi.getOrders(orderProps.symbol) 14 | 15 | if (orders.length > 0) { 16 | return orders[0] 17 | } 18 | 19 | console.log('submitting new order') 20 | 21 | const order = await rhinofi.submitOrder({ ...orderProps, starkPrivateKey }) 22 | 23 | console.log('waiting until order appears on the book...') 24 | while (true) { 25 | // TODD: 26 | if ((await rhinofi.getOrder(order._id)).active === true) break 27 | await P.delay(1000) 28 | console.log('still waiting...') 29 | } 30 | 31 | return order 32 | } 33 | -------------------------------------------------------------------------------- /examples/helpers/examplesList.js: -------------------------------------------------------------------------------- 1 | // This list is used to build and test examples. 2 | // It determines the orders of the example files. 3 | module.exports = Object.freeze([ 4 | 'register', 5 | 'deposit', 6 | 'getDeposits', 7 | 'getBalance', 8 | 'submitOrder', 9 | 'getOrders', 10 | 'getOrders_forSymbolPair', 11 | 'cancelOrder', 12 | 'getConfig', 13 | 'getOrder', 14 | 'getOrdersHist', 15 | 'getUserConfig', 16 | 'withdraw', 17 | 'fastWithdrawal', 18 | 'getWithdrawals', 19 | 'getWithdrawal', 20 | 'withdrawOnChain', 21 | 'fullWithdrawalRequest', 22 | 'getVaultId', 23 | 'ledgerDeposit', 24 | 'ledgerSubmitOrder', 25 | 'ledgerWithdraw', 26 | 'cancelWithdrawal', 27 | 'authWithTradingKey', 28 | 'fastWithdrawalFee', 29 | 'fastWithdrawalMaxAmount', 30 | 'ammDeposit', 31 | 'ammWithdrawal', 32 | 'transferAndWithdraw', 33 | 'publicPermissions', 34 | 'transfer', 35 | 'getRegistrationStatuses', 36 | // TODO 37 | // 'submitBuyOrder', 38 | // 'submitSellOrder', 39 | // Not implemented 40 | // 'getFeeRates', 41 | // 'getFeeRate', 42 | ]) 43 | -------------------------------------------------------------------------------- /src/lib/util/createPromiseAndCallbackFn.js: -------------------------------------------------------------------------------- 1 | // Used in various on-chain + HTTP POST functions 2 | // ss we need the txHash ASAP (before the tx is mined), 3 | // we need to use the 'transactionHash' of the underlyind 'send' method 4 | // PromEvents make it difficult to listen to events when the PromEvent 5 | // is nested in other Promises. This workaround allows to listen to the 6 | // event without changing the return signatures of the underlying 'send' 7 | // More : https://github.com/ChainSafe/web3.js/issues/1547 8 | module.exports = extraCallback => { 9 | let transactionHashCb 10 | const transactionHashPromise = new Promise((resolve, reject) => { 11 | transactionHashCb = (err, result) => { 12 | if (err) { 13 | reject(err) 14 | } else { 15 | let clearCallback 16 | if (extraCallback) { 17 | clearCallback = extraCallback(result) 18 | } 19 | resolve({ 20 | transactionHash: result, 21 | clearCallback, 22 | }) 23 | } 24 | } 25 | }) 26 | return [transactionHashPromise, transactionHashCb] 27 | } 28 | -------------------------------------------------------------------------------- /src/api/airdropEligibility.test.js: -------------------------------------------------------------------------------- 1 | const nock = require('nock') 2 | const instance = require('./test/helpers/instance') 3 | 4 | const mockGetConf = require('./test/fixtures/getConf') 5 | 6 | let dvf 7 | 8 | describe('dvf.airdropEligibility', () => { 9 | beforeAll(async () => { 10 | mockGetConf() 11 | dvf = await instance() 12 | }) 13 | 14 | it('Returns the airdrop amount eligible for this address recieved from the API....', async () => { 15 | const ethAddress = '0x15A9812E214B18cF5346a2FEC9EA91A68FD9ce00' 16 | const token = 'DVF' 17 | 18 | const apiResponse = { 19 | claims: [ 20 | { 21 | user: ethAddress, 22 | token, 23 | amount: 21.52, 24 | requested: false 25 | } 26 | ], 27 | isRegistered: true 28 | } 29 | 30 | nock(dvf.config.api) 31 | .get('/v1/trading/r/airdropEligibility') 32 | .query({ ethAddress, token }) 33 | .reply(200, apiResponse) 34 | 35 | const res = await dvf.airdropEligibility({ethAddress, token}) 36 | expect(res).toEqual(apiResponse) 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /src/api/ledger/withdraw.js: -------------------------------------------------------------------------------- 1 | const { post } = require('request-promise') 2 | const validateAssertions = require('../../lib/validators/validateAssertions') 3 | 4 | module.exports = async (dvf, token, amount, starkWithdrawal) => { 5 | validateAssertions(dvf, { 6 | token, 7 | amount 8 | }) 9 | 10 | // console.log({ currency }) 11 | const nonce = starkWithdrawal.nonce 12 | const starkVaultId = starkWithdrawal.starkVaultId 13 | const expireTime = starkWithdrawal.expireTime 14 | const starkPublicKey = starkWithdrawal.starkPublicKey 15 | const starkSignature = starkWithdrawal.starkSignature 16 | 17 | // TODO: This could be updated to send starkWithdrawal 18 | // However this will require updates to public api 19 | // and public api reference documents 20 | 21 | const data = { 22 | token, 23 | amount, 24 | nonce, 25 | starkPublicKey, 26 | starkSignature, 27 | starkVaultId, 28 | expireTime 29 | } 30 | 31 | //console.log({ data }) 32 | const url = dvf.config.api + '/v1/trading/w/withdraw' 33 | 34 | return post(url, { 35 | json: data 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /src/api/getBalanceUsd.test.js: -------------------------------------------------------------------------------- 1 | const nock = require('nock') 2 | const instance = require('./test/helpers/instance') 3 | 4 | const mockGetConf = require('./test/fixtures/getConf') 5 | const makeQueryValidator = require('./test/helpers/makeQueryValidator') 6 | 7 | let dvf 8 | 9 | describe('getBalanceUsd', () => { 10 | beforeAll(async () => { 11 | mockGetConf() 12 | dvf = await instance() 13 | }) 14 | 15 | it('Queries for user USD balance', async () => { 16 | // TODO: record actual response with current version of the API 17 | // mock bfx response for currency value using nockBack 18 | 19 | const apiResponse = { 20 | ethAddress: '0x14d06788090769f669427b6aea1c0240d2321f34', 21 | balanceUsd: '2' 22 | } 23 | 24 | const queryValidator = makeQueryValidator(apiResponse) 25 | 26 | nock(dvf.config.api) 27 | .get('/v1/trading/r/getBalanceUsd') 28 | .query(true) 29 | .reply(queryValidator) 30 | 31 | const response = await dvf.getBalanceUsd() 32 | expect(queryValidator).toBeCalled() 33 | expect(response).toMatchObject(apiResponse) 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /src/api/ledger/deposit.js: -------------------------------------------------------------------------------- 1 | const {post} = require('request-promise') 2 | const validateAssertions = require('../../lib/validators/validateAssertions') 3 | 4 | module.exports = async (dvf, token, amount, path, nonce, signature) => { 5 | validateAssertions(dvf, {amount, token}) 6 | const tempVaultId = dvf.config.DVF.tempStarkVaultId || '1' 7 | const depositAmount = dvf.util.prepareDepositAmount(amount, token) 8 | const starkDeposit = await dvf.stark.ledger.createDepositData(path, token, depositAmount, tempVaultId, nonce, signature) 9 | 10 | const data = { 11 | token, 12 | amount: depositAmount, 13 | nonce: starkDeposit.nonce, 14 | expireTime: starkDeposit.expireTime, 15 | starkVaultId: starkDeposit.starkVaultId, 16 | starkSignature: starkDeposit.starkSignature, 17 | starkPublicKey: starkDeposit.starkPublicKey 18 | } 19 | 20 | const url = dvf.config.api + '/v1/trading/w/deposit' 21 | 22 | const deposit = await post(url, {json: data}) 23 | const ctDeposit = await dvf.contract.deposit(tempVaultId, token, amount, `0x${starkDeposit.starkPublicKey.x}`) 24 | 25 | return {...deposit, ...ctDeposit} 26 | } 27 | -------------------------------------------------------------------------------- /src/api/contract/depositFromStarkTx.js: -------------------------------------------------------------------------------- 1 | // TODO: this file contains very similar logic to ./deposit, so one could be 2 | // implemented in terms of the other once the TODO in the latter is resolved. 3 | const sendToStarkExContract = require('./sendToStarkExContract') 4 | 5 | const { fromQuantizedToBaseUnitsBN } = require('@rhino.fi/dvf-utils') 6 | 7 | module.exports = (dvf, { starkKey, tokenId, vaultId, amount }, options) => { 8 | const ethTokenInfo = dvf.token.getTokenInfoOrThrow('ETH') 9 | 10 | // This should never happen. 11 | if (!ethTokenInfo.starkTokenId) { 12 | throw new Error('ethTokenInfo.starkTokenId not defined') 13 | } 14 | 15 | const methodArgs = [starkKey, tokenId, vaultId] 16 | const [args, value] = tokenId === ethTokenInfo.starkTokenId 17 | // For ETH, we convert amount to WEI and add as extra send arg 18 | ? [methodArgs, fromQuantizedToBaseUnitsBN(ethTokenInfo, amount).toString()] 19 | // For other tokens, use amount as is (should be quantised), and add to 20 | // method args. 21 | : [methodArgs.concat(amount.toString())] 22 | 23 | return sendToStarkExContract(dvf)('deposit')(args, value, options) 24 | } 25 | -------------------------------------------------------------------------------- /src/api/account/permissions.test.js: -------------------------------------------------------------------------------- 1 | const nock = require('nock') 2 | const instance = require('../test/helpers/instance') 3 | 4 | const mockGetConf = require('../test/fixtures/getConf') 5 | 6 | let dvf 7 | 8 | describe('dvf.account.permissions', () => { 9 | beforeAll(async () => { 10 | nock.cleanAll() 11 | mockGetConf() 12 | dvf = await instance() 13 | }) 14 | 15 | const apiResponse = { balances: true, tradingHistory: false } 16 | 17 | it('Posts getPublicUserPermissions to get current api permissions', async () => { 18 | nock(dvf.config.api) 19 | .post('/v1/trading/r/getPublicUserPermissions') 20 | .reply(200, apiResponse) 21 | 22 | const response = await dvf.account.getPermissions() 23 | 24 | expect(response).toEqual(apiResponse) 25 | }) 26 | 27 | it('Posts setPublicUserPermissions to get current api permissions', async () => { 28 | nock(dvf.config.api) 29 | .post('/v1/trading/r/setPublicUserPermissions') 30 | .reply(200, apiResponse) 31 | 32 | const response = await dvf.account.setPermissions({ key: 'balances', value: true }) 33 | 34 | expect(response).toEqual(apiResponse) 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /src/api/eth/send.js: -------------------------------------------------------------------------------- 1 | module.exports = async (dvf, abi, address, action, args, value, options = {}) => { 2 | if (dvf.config.send) { 3 | return dvf.config.send(dvf, abi, address, action, args, value, options) 4 | } 5 | const web3 = dvf.eth.getWeb3ForChain(options.chain) 6 | const contract = new web3.eth.Contract(abi, address) 7 | const method = contract.methods[action](...args) 8 | 9 | const gasLimit = 10 | action === 'fullWithdrawalRequest' 11 | ? 10 * dvf.config.defaultGasLimit 12 | : dvf.config.defaultGasLimit 13 | 14 | const gasPrice = await dvf.eth.getGasPrice() 15 | const { id: chainId } = await dvf.eth.getNetwork() 16 | 17 | let sendOptions = { 18 | chainId, 19 | from: dvf.get('account'), 20 | gasLimit: gasLimit, 21 | gasPrice: gasPrice, 22 | ...(value && { value }) 23 | } 24 | 25 | const txPromEvent = method.send(sendOptions) 26 | if (options.transactionHashCb) { 27 | txPromEvent.on('transactionHash', (txHash) => options.transactionHashCb(null, txHash)) 28 | txPromEvent.catch(error => { 29 | options.transactionHashCb(error) 30 | throw error 31 | }) 32 | } 33 | return txPromEvent 34 | } 35 | -------------------------------------------------------------------------------- /src/api/contract/abi/tokenFaucet.abi.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Token abi, used for faucet 3 | * 4 | * see: 5 | * https://ropsten.etherscan.io/address/0x513c6802d75A48d3B9A1557720f1B43689Fac1C9#code 6 | */ 7 | module.exports = [{"inputs":[{"internalType":"address[3]","name":"_tokens","type":"address[3]"},{"internalType":"uint256","name":"_maxBalance","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"constant":false,"inputs":[],"name":"drip","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"maxBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"token1","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"token2","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"token3","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}] -------------------------------------------------------------------------------- /src/api/ledger/signEIP712Data.js: -------------------------------------------------------------------------------- 1 | const selectTransport = require('../../lib/ledger/selectTransport') 2 | const Eth = require('@ledgerhq/hw-app-eth').default 3 | const { getStructHash } = require('eip-712') 4 | 5 | // https://eips.ethereum.org/EIPS/eip-712 6 | module.exports = async (dvf, jsonData, ledgerPath) => { 7 | const Transport = selectTransport(dvf.isBrowser) 8 | const createdTransport = await Transport.create() 9 | const ledgerEth = new Eth(createdTransport) 10 | 11 | const domainStructHash = getStructHash(jsonData, 'EIP712Domain', jsonData.domain) 12 | const domainStructString = Buffer.from(domainStructHash).toString('hex') 13 | 14 | const messageStructHash = getStructHash(jsonData, jsonData.primaryType, jsonData.message) 15 | const messageStructString = Buffer.from(messageStructHash).toString('hex') 16 | 17 | return ledgerEth.signEIP712HashedMessage( 18 | ledgerPath, 19 | domainStructString, 20 | messageStructString 21 | ).then(signature => { 22 | return { 23 | r: Buffer.from(signature.r, 'hex'), 24 | s: Buffer.from(signature.s, 'hex'), 25 | v: signature.v 26 | } 27 | }).finally(() => { 28 | createdTransport.close() 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /src/api/ledger/transfer.js: -------------------------------------------------------------------------------- 1 | const FP = require('lodash/fp') 2 | const { Joi } = require('@rhino.fi/dvf-utils') 3 | 4 | const validateWithJoi = require('../../lib/validators/validateWithJoi') 5 | 6 | const schema = Joi.object({ 7 | token: Joi.string(), 8 | amount: Joi.bigNumber().greaterThan(0), 9 | memo: Joi.string().optional(), 10 | recipientEthAddress: Joi.ethAddress() 11 | }) 12 | 13 | const validateInputs = validateWithJoi(schema)('INVALID_METHOD_ARGUMENT')({ 14 | context: `transfer` 15 | }) 16 | 17 | module.exports = async (dvf, data, path, nonce, signature) => { 18 | dvf = FP.set('config.useAuthHeader', true, dvf) 19 | const { token, amount, recipientEthAddress, memo } = validateInputs(data) 20 | const { vaultId, starkKey } = await dvf.getVaultIdAndStarkKey({ 21 | token, 22 | targetEthAddress: recipientEthAddress 23 | }, nonce, signature) 24 | const feeRecipient = await dvf.getVaultIdAndStarkKey({ 25 | token, 26 | targetEthAddress: dvf.config.DVF.deversifiAddress 27 | }, nonce, signature) 28 | return dvf.ledger.transferUsingVaultIdAndStarkKey({ 29 | token, 30 | amount, 31 | memo, 32 | recipientVaultId: vaultId, 33 | recipientPublicKey: starkKey 34 | }, path, feeRecipient) 35 | } 36 | -------------------------------------------------------------------------------- /src/lib/dvf/makeCreateSignedTransferTx.js: -------------------------------------------------------------------------------- 1 | module.exports = dvf => async data => { 2 | const { 3 | recipientPublicKey, 4 | recipientVaultId, 5 | tokenInfo, 6 | quantisedAmount, 7 | quantisedFeeAmount 8 | } = data 9 | // dvfStarkProvider abstracts specifics of how a public key is obtained. 10 | const { dvfStarkProvider } = dvf 11 | const starkPublicKey = await dvfStarkProvider.getPublicKey() 12 | 13 | const txParams = { 14 | amount: quantisedAmount.toString(), 15 | senderPublicKey: `0x${starkPublicKey.x}`, 16 | receiverPublicKey: recipientPublicKey, 17 | receiverVaultId: recipientVaultId, 18 | senderVaultId: tokenInfo.starkVaultId, 19 | token: tokenInfo.starkTokenId, 20 | ...(quantisedFeeAmount 21 | ? { 22 | feeInfoUser: { 23 | feeLimit: quantisedFeeAmount.toString(), 24 | // Same as sender vaultId 25 | sourceVaultId: tokenInfo.starkVaultId, 26 | // Same as token 27 | tokenId: tokenInfo.starkTokenId 28 | } 29 | } 30 | : {} 31 | ), 32 | type: 'TransferRequest' 33 | } 34 | 35 | return { 36 | tx: await dvf.createSignedTransfer(txParams), 37 | starkPublicKey 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/api/transfer.js: -------------------------------------------------------------------------------- 1 | const FP = require('lodash/fp') 2 | const { Joi } = require('@rhino.fi/dvf-utils') 3 | 4 | const validateWithJoi = require('../lib/validators/validateWithJoi') 5 | 6 | const schema = Joi.object({ 7 | token: Joi.string(), 8 | amount: Joi.bigNumber().greaterThan(0), 9 | memo: Joi.string().optional(), 10 | partnerId: Joi.string().optional(), 11 | recipientEthAddress: Joi.ethAddress() 12 | }) 13 | 14 | const validateInputs = validateWithJoi(schema)('INVALID_METHOD_ARGUMENT')({ 15 | context: `transfer` 16 | }) 17 | 18 | module.exports = async (dvf, data, nonce, signature) => { 19 | dvf = FP.set('config.useAuthHeader', true, dvf) 20 | const { token, amount, memo, partnerId, recipientEthAddress } = validateInputs(data) 21 | const { vaultId, starkKey } = await dvf.getVaultIdAndStarkKey({ 22 | token, 23 | targetEthAddress: recipientEthAddress 24 | }, nonce, signature) 25 | const feeRecipient = await dvf.getVaultIdAndStarkKey({ 26 | token, 27 | targetEthAddress: dvf.config.DVF.deversifiAddress 28 | }, nonce, signature) 29 | return dvf.transferUsingVaultIdAndStarkKey({ 30 | token, 31 | amount, 32 | memo, 33 | partnerId, 34 | recipientVaultId: vaultId, 35 | recipientPublicKey: starkKey 36 | }, feeRecipient) 37 | } 38 | -------------------------------------------------------------------------------- /src/lib/dvf/createOrderMetaData.js: -------------------------------------------------------------------------------- 1 | const validateProps = require('../validators/validateProps') 2 | const validateAssertions = require('../validators/validateAssertions') 3 | 4 | const starkSignedOrder = async (dvf, starkPrivateKey, starkMessage) => { 5 | validateAssertions(dvf, { starkPrivateKey }) 6 | 7 | const { starkKeyPair, starkPublicKey } = await dvf.stark.createKeyPair( 8 | starkPrivateKey 9 | ) 10 | 11 | const starkSignature = dvf.stark.sign(starkKeyPair, starkMessage) 12 | 13 | return { 14 | starkPublicKey, 15 | starkSignature 16 | } 17 | } 18 | 19 | module.exports = async (dvf, orderData) => { 20 | validateProps(dvf, ['amount', 'symbol', 'price'], orderData) 21 | 22 | const { starkOrder, starkMessage } = await dvf.stark.createOrder(orderData) 23 | let starkPublicKey, starkSignature 24 | if (orderData.starkPrivateKey) { 25 | ({starkPublicKey, starkSignature} = await starkSignedOrder(dvf, orderData.starkPrivateKey, starkMessage)) 26 | } else if (orderData.ledgerPath) { 27 | ({starkPublicKey, starkSignature} = await dvf.stark.ledger.createSignedOrder(orderData.ledgerPath, starkOrder, { starkMessage })) 28 | } 29 | 30 | return { 31 | starkPublicKey, 32 | starkOrder, 33 | starkMessage, 34 | starkSignature 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/api/get30DaysVolume.test.js: -------------------------------------------------------------------------------- 1 | const nock = require('nock') 2 | 3 | const instance = require('./test/helpers/instance') 4 | const mockGetConf = require('./test/fixtures/getConf') 5 | const makeQueryValidator = require('./test/helpers/makeQueryValidator') 6 | 7 | let dvf 8 | 9 | describe('getFeeRate', () => { 10 | beforeAll(async () => { 11 | mockGetConf() 12 | dvf = await instance() 13 | }) 14 | 15 | it(`Query for user's 30 days trading volume`, async () => { 16 | // TODO: record actual response with current version of the API 17 | // mock bfx response for currency value using nockBack 18 | 19 | const apiResponse = { 20 | totalUSDVolume: 199.7428219247271, 21 | tokens: { 22 | ETH: { tokenAmount: 0.51, USDVolume: 82.0539 }, 23 | ZRX: { tokenAmount: 178.53426171, USDVolume: 117.6889219247271 } 24 | }, 25 | startDate: '2020-04-04T16:54:25.886Z' 26 | } 27 | 28 | const queryValidator = makeQueryValidator(apiResponse) 29 | 30 | nock(dvf.config.api) 31 | .get('/v1/trading/r/30DaysVolume') 32 | .query(true) 33 | .reply(queryValidator) 34 | 35 | const response = await dvf.get30DaysVolume() 36 | expect(queryValidator).toBeCalled() 37 | expect(response).toMatchObject(apiResponse) 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // for production use 'https://api.rhino.fi', 3 | api: 'https://api.stg.rhino.fi', 4 | gasApi: 'https://ethgasstation.info', 5 | 6 | // default transaction arguments 7 | defaultGasLimit: 300000, 8 | defaultGasPrice: 50000000000, 9 | 10 | // default stark related constants 11 | // default expiration time for transfers and orders in hours 12 | defaultStarkExpiry: 4320, 13 | // default nonce age in seconds 14 | defaultNonceAge: 43200, 15 | // in case no provider is provided we will try connecting to the this default 16 | 17 | // address 18 | defaultProvider: 'http://localhost:8545', 19 | 20 | // default account to select in case no account is provided by the userConfig 21 | // parameter 22 | account: 0, 23 | // selects account from web3 provider based on config.account upon 24 | // initialization 25 | autoSelectAccount: true, 26 | 27 | // Ledger Stark Path Constants 28 | purpose: 2645, 29 | plugin: 579218131, 30 | application: 1393043894, 31 | accountIndex: 0, 32 | 33 | 34 | // enables integrators to select if they want to fetch user config upon initialization 35 | autoLoadUserConf: true, 36 | autoLoadExchangeConf: true, 37 | 38 | // enables integrations exposing starkProvider 39 | starkProvider: null 40 | } 41 | -------------------------------------------------------------------------------- /src/api/test/helpers/compile.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const fs = require('fs') 3 | 4 | // compiling contracts 5 | const solc = require('solc') 6 | 7 | const CONTRACTS_PATH = path.join(__dirname, '../contracts/') 8 | 9 | /** 10 | * Compile all .sol files in this folder and save them as .js 11 | */ 12 | 13 | const compile = async (filename) => { 14 | filename = filename.replace('.sol', '') 15 | 16 | const filePath = path.join(CONTRACTS_PATH, filename + '.sol') 17 | const source = fs.readFileSync(filePath, 'utf-8') 18 | 19 | // grabs the contract reference 20 | const compiled = solc.compile(source, 1) 21 | 22 | const contract = compiled.contracts[':' + filename] 23 | 24 | if (contract) { 25 | console.log(' OK!') 26 | } 27 | 28 | if (compiled.errors) { 29 | if (contract) { 30 | console.log('warnings:') 31 | } else { 32 | console.log('errors:') 33 | } 34 | 35 | console.log(compiled.errors) 36 | } 37 | 38 | if (!contract) return 39 | 40 | fs.writeFileSync(filePath + '.json', JSON.stringify(contract)) 41 | } 42 | 43 | const allFiles = fs.readdirSync(CONTRACTS_PATH) 44 | // grab all filename ending with .sol 45 | const files = allFiles.filter((file) => /\.sol$/.test(file)) 46 | 47 | for (var file of files) { 48 | compile(file) 49 | } 50 | -------------------------------------------------------------------------------- /src/lib/dvf/createMarketOrderMetaData.js: -------------------------------------------------------------------------------- 1 | const validateProps = require('../validators/validateProps') 2 | const validateAssertions = require('../validators/validateAssertions') 3 | 4 | const starkSignedOrder = async (dvf, starkPrivateKey, starkMessage) => { 5 | validateAssertions(dvf, { starkPrivateKey }) 6 | 7 | const { starkKeyPair, starkPublicKey } = await dvf.stark.createKeyPair( 8 | starkPrivateKey 9 | ) 10 | 11 | const starkSignature = dvf.stark.sign(starkKeyPair, starkMessage) 12 | 13 | return { 14 | starkPublicKey, 15 | starkSignature 16 | } 17 | } 18 | 19 | module.exports = async (dvf, orderData) => { 20 | validateProps(dvf, ['amountToSell', 'symbol', 'tokenToSell', 'worstCasePrice'], orderData) 21 | const { starkOrder, starkMessage } = await dvf.stark.createOrder(orderData) 22 | let starkPublicKey, starkSignature 23 | 24 | if (orderData.starkPrivateKey) { 25 | ({ starkPublicKey, starkSignature } = await starkSignedOrder(dvf, orderData.starkPrivateKey, starkMessage)) 26 | } else if (orderData.ledgerPath) { 27 | ({ starkPublicKey, starkSignature } = await dvf.stark.ledger.createSignedOrder(orderData.ledgerPath, starkOrder, { starkMessage })) 28 | } 29 | 30 | return { 31 | starkPublicKey, 32 | starkOrder, 33 | starkMessage, 34 | starkSignature 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /examples/src/cancelOrder.js: -------------------------------------------------------------------------------- 1 | const P = require('aigle') 2 | let order 3 | const orders = await rhinofi.getOrders() 4 | 5 | console.log('orders', orders) 6 | 7 | if (orders.length == 0) { 8 | console.log('submitting new order') 9 | 10 | // Submit an order to sell 0.1 ETH at a the price of 5000 USDT per ETH 11 | const symbol = 'ETH:USDT' 12 | const amount = -0.1 13 | const price = 5000 14 | const validFor = '0' 15 | const feeRate = '' 16 | 17 | order = await rhinofi.submitOrder({ 18 | symbol, 19 | amount, 20 | price, 21 | starkPrivateKey: starkPrivKey, 22 | validFor, // Optional 23 | feeRate, // Optional 24 | gid: '1', // Optional 25 | cid: 'mycid-cancel-example-' + Math.random().toString(36).substring(7), // Optional 26 | partnerId: 'P1' // Optional 27 | }) 28 | 29 | console.log('submitOrder response ->', order) 30 | 31 | while (true) { 32 | console.log('checking if order appears on the book...') 33 | if ((await rhinofi.getOrders()).find(o => o._id === order._id)) break 34 | await P.delay(1000) 35 | } 36 | } 37 | else { 38 | order = orders[0] 39 | } 40 | 41 | console.log('cancelling orderId', order._id) 42 | 43 | const response = await rhinofi.cancelOrder(order._id) 44 | // Alternative with cid : 45 | // const response = await rhinofi.cancelOrder({ cid: order.cid }) 46 | 47 | logExampleResult(response) 48 | -------------------------------------------------------------------------------- /src/api/amm/applyFundingOrderDataSlippage.js: -------------------------------------------------------------------------------- 1 | const R = require('ramda') 2 | const validateWithJoi = require('../../lib/validators/validateWithJoi') 3 | const { toBN } = require('@rhino.fi/dvf-utils') 4 | 5 | const { fundOrderDataSchema } = require('./schemas') 6 | 7 | const validateData = validateWithJoi(fundOrderDataSchema)('INVALID_METHOD_ARGUMENT')({ 8 | context: 'applyFundingOrderDataSlippage' 9 | }) 10 | 11 | const amountBuyPath = ['starkOrder', 'amountBuy'] 12 | 13 | module.exports = (dvf, data, slippage) => { 14 | const validatedData = validateData(data) 15 | 16 | if (slippage > 1 || slippage < 0) { 17 | throw new Error(`Slippage can only be 0 - 1 to represent 0 - 100%, provided: ${slippage}`) 18 | } 19 | 20 | // If withdrawal: 21 | // Reduce token0 and token1 22 | // If deposit 23 | // Reduce LP tokens 24 | // In both cases, amountBuy for each order will be reduced 25 | const { orders } = validatedData 26 | const ordersPostSlippage = R.map( 27 | R.over( 28 | R.lensPath(amountBuyPath), 29 | reduceBySlippage(slippage) 30 | ) 31 | )(orders) 32 | 33 | const appliedData = R.assoc('orders', ordersPostSlippage)(validatedData) 34 | 35 | return appliedData 36 | } 37 | 38 | /** 39 | * @type { (slippage: Number) => (amount: string) => string } 40 | */ 41 | const reduceBySlippage = slippage => amount => 42 | toBN(amount).times(1 - slippage).integerValue().toFixed(0) 43 | -------------------------------------------------------------------------------- /src/lib/ledger/makeCreateSignedTransferTxLedger.js: -------------------------------------------------------------------------------- 1 | const createSignedTransaction = require('../dvf/createSignedTransaction') 2 | const addNonceAndExpirationTimestamp = require('../dvf/addNonceAndExpirationTimestamp') 3 | 4 | module.exports = dvf => path => async data => { 5 | const { 6 | recipientPublicKey, 7 | recipientVaultId, 8 | tokenInfo, 9 | quantisedAmount, 10 | quantisedFeeAmount 11 | } = data 12 | const starkPublicKey = await dvf.stark.ledger.getPublicKey(path) 13 | 14 | let tx = { 15 | amount: quantisedAmount.toString(), 16 | senderPublicKey: `0x${starkPublicKey.x}`, 17 | receiverPublicKey: recipientPublicKey, 18 | receiverVaultId: recipientVaultId, 19 | senderVaultId: tokenInfo.starkVaultId, 20 | token: tokenInfo.starkTokenId, 21 | ...(quantisedFeeAmount 22 | ? { 23 | feeInfoUser: { 24 | feeLimit: quantisedFeeAmount.toString(), 25 | // Same as sender vaultId 26 | sourceVaultId: tokenInfo.starkVaultId, 27 | // Same as token 28 | tokenId: tokenInfo.starkTokenId 29 | } 30 | } 31 | : {} 32 | ), 33 | type: 'TransferRequest' 34 | } 35 | 36 | tx = addNonceAndExpirationTimestamp(dvf.config)(tx) 37 | const { signature } = await createSignedTransaction(dvf)(tx) 38 | tx.signature = signature 39 | 40 | return { 41 | starkPublicKey, 42 | tx 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /examples/helpers/example.js.tmpl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S yarn node 2 | /* eslint-disable no-unused-vars */ 3 | 4 | /* 5 | DO NOT EDIT THIS FILE BY HAND! 6 | Examples are generated using helpers/buildExamples.js script. 7 | Check README.md for more details. 8 | */ 9 | 10 | const sw = require('@rhino.fi/starkware-crypto') 11 | const getWeb3 = require('./helpers/getWeb3') 12 | 13 | const RhinofiClientFactory = require('../src') 14 | const envVars = require('./helpers/loadFromEnvOrConfig')( 15 | process.env.CONFIG_FILE_NAME 16 | ) 17 | const logExampleResult = require('./helpers/logExampleResult')(__filename) 18 | 19 | const ethPrivKey = envVars.ETH_PRIVATE_KEY 20 | // NOTE: you can also generate a new key using:` 21 | // const starkPrivKey = rhinofi.stark.createPrivateKey() 22 | const starkPrivKey = envVars.STARK_PRIVATE_KEY 23 | const rpcUrl = envVars.RPC_URL 24 | 25 | const { web3, provider } = getWeb3(ethPrivKey, rpcUrl) 26 | 27 | const rhinofiConfig = { 28 | api: envVars.API_URL, 29 | dataApi: envVars.DATA_API_URL, 30 | useAuthHeader: true, 31 | wallet: { 32 | type: 'tradingKey', 33 | meta: { 34 | starkPrivateKey: starkPrivKey 35 | } 36 | } 37 | // Add more variables to override default values 38 | } 39 | 40 | ;(async () => { 41 | const rhinofi = await RhinofiClientFactory(web3, rhinofiConfig) 42 | 43 | {{{EXAMPLE_SRC}}} 44 | })() 45 | .catch(error => { 46 | console.error(error) 47 | process.exit(1) 48 | }) 49 | -------------------------------------------------------------------------------- /examples/helpers/loadFromEnvOrConfig.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | 4 | const getConfigVar_ = (config, configFileName) => (varName, defaultValue) => { 5 | const value = process.env[ varName ] || config[ varName ] || defaultValue 6 | 7 | if (value) { 8 | return value 9 | } 10 | else { 11 | throw new Error( 12 | `${varName} is required. Set it in ${configFileName} or via an env var.` 13 | ) 14 | } 15 | } 16 | 17 | module.exports = (configFileName = 'config.json') => { 18 | const configFilePath = path.normalize( 19 | path.join(__dirname, '..', configFileName) 20 | ) 21 | 22 | let config = {} 23 | 24 | try { 25 | config = JSON.parse( 26 | fs.readFileSync(configFilePath).toString() 27 | ) 28 | } 29 | catch (error) { 30 | if (error.code == 'ENOENT') { 31 | console.log(`warning: ${configFilePath} not found`) 32 | } 33 | else { 34 | throw error 35 | } 36 | } 37 | 38 | const getConfigVar = getConfigVar_(config, configFilePath) 39 | 40 | const apiUrl = getConfigVar('API_URL', 'https://api.stg.rhino.fi') 41 | const ETH_PRIVATE_KEY = getConfigVar('ETH_PRIVATE_KEY') 42 | 43 | return { 44 | RPC_URL: getConfigVar('RPC_URL'), 45 | ETH_PRIVATE_KEY, 46 | STARK_PRIVATE_KEY: getConfigVar('STARK_PRIVATE_KEY', ETH_PRIVATE_KEY), 47 | API_URL: apiUrl, 48 | DATA_API_URL: getConfigVar('DATA_API_URL', apiUrl) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/api/amm/getAmmFundingOrders.js: -------------------------------------------------------------------------------- 1 | const { Joi } = require('@rhino.fi/dvf-utils') 2 | const getAuthenticated = require('../../lib/dvf/get-authenticated') 3 | 4 | const validateWithJoi = require('../../lib/validators/validateWithJoi') 5 | 6 | const schema = Joi.alternatives().try( 7 | Joi.object({ 8 | ammFundingOrderId: Joi.string() 9 | }), 10 | Joi.object({ 11 | sortDirection: Joi.string() 12 | .valid('DESC', 'ASC') 13 | .optional(), 14 | startDate: Joi.date().optional(), 15 | endDate: Joi.date() 16 | .greater(Joi.ref('startDate')) 17 | .optional(), 18 | skip: Joi.number() 19 | .integer() 20 | .min(0) 21 | .optional(), 22 | limit: Joi.number() 23 | .integer() 24 | .min(1) 25 | .max(100) 26 | .optional(), 27 | pool: Joi.string().optional() 28 | }) 29 | ) 30 | 31 | const validateData = validateWithJoi(schema)('INVALID_METHOD_ARGUMENT')({ 32 | context: 'getAmmFundingOrders' 33 | }) 34 | 35 | module.exports = (dvf, nonce, signature, data, headers) => { 36 | const validatedData = validateData(data) 37 | const endpoint = `/v1/trading/amm/fundingOrders` 38 | if (validatedData.ammFundingOrderId) { 39 | return getAuthenticated( 40 | dvf, 41 | `${endpoint}/${validatedData.ammFundingOrderId}`, 42 | nonce, 43 | signature 44 | ) 45 | } 46 | return getAuthenticated(dvf, endpoint, nonce, signature, validatedData, headers) 47 | } 48 | -------------------------------------------------------------------------------- /src/api/withdrawV2.js: -------------------------------------------------------------------------------- 1 | const FP = require('lodash/fp') 2 | const { Joi } = require('@rhino.fi/dvf-utils') 3 | 4 | const post = require('../lib/dvf/post-authenticated') 5 | 6 | const generateRandomNonceV2 = require('../lib/dvf/generateRandomNonceV2') 7 | const validateWithJoi = require('../lib/validators/validateWithJoi') 8 | const getSafeQuantizedAmountOrThrow = require('../lib/dvf/token/getSafeQuantizedAmountOrThrow') 9 | 10 | const schema = Joi.object({ 11 | token: Joi.string(), 12 | amount: Joi.bigNumber().greaterThan(0), // number or number string 13 | nonce: Joi.number().integer() 14 | .min(0) 15 | // Will be auto-generated if not provided. 16 | .optional() 17 | }) 18 | 19 | const validateArg0 = validateWithJoi(schema)('INVALID_METHOD_ARGUMENT')({ 20 | context: `withdrawV2` 21 | }) 22 | 23 | const endpoint = '/v1/trading/withdrawals' 24 | 25 | module.exports = async (dvf, data, authNonce, signature) => { 26 | const { token, amount, nonce } = validateArg0(data) 27 | 28 | const tokenInfo = dvf.token.getTokenInfoOrThrow(token) 29 | const quantisedAmount = getSafeQuantizedAmountOrThrow(amount, tokenInfo) 30 | 31 | const payload = { 32 | token, 33 | amount: quantisedAmount, 34 | nonce: nonce || generateRandomNonceV2() 35 | } 36 | 37 | // Force the use of header (instead of payload) for authentication. 38 | dvf = FP.set('config.useAuthHeader', true, dvf) 39 | return post(dvf, endpoint, authNonce, signature, payload) 40 | } 41 | -------------------------------------------------------------------------------- /src/lib/stark/calculateFact.test.js: -------------------------------------------------------------------------------- 1 | const calculateFact = require('./calculateFact') 2 | 3 | const recipient = '0xCc6E2d20cC5AaFDCa329bA2d63e5ba5edD684B2F' 4 | const baseUnitsAmount = '10' 5 | const tokenAddress = '0xe343Ef947f88873F740c5a1CB96b3Fc201993aD4' 6 | const salt = 101 7 | const expectedResult = 'd04c9a75be0510852034a048b086a111b6481b7e544c4510f89b660e534a7a0d' 8 | 9 | describe('calculateFact', () => { 10 | it('works is given valid data', () => { 11 | expect(calculateFact(recipient, baseUnitsAmount, tokenAddress, salt)) 12 | .toEqual(expectedResult) 13 | }) 14 | 15 | it('gives the same result if addresses are lower case', () => { 16 | expect(calculateFact(recipient.toLowerCase(), baseUnitsAmount, tokenAddress.toLowerCase(), salt)) 17 | .toEqual(expectedResult) 18 | }) 19 | 20 | it('throws an error if checksum is invalid', () => { 21 | // changed capitalisation of last char 22 | expect(() => calculateFact('0xCc6E2d20cC5AaFDCa329bA2d63e5ba5edD684B2f', baseUnitsAmount, tokenAddress, salt)) 23 | .toThrow('0xCc6E2d20cC5AaFDCa329bA2d63e5ba5edD684B2f is not a valid address, or the checksum is invalid') 24 | }) 25 | 26 | it('throws an error if address is invalid', () => { 27 | // changed last char to X 28 | expect(() => calculateFact('0xCc6E2d20cC5AaFDCa329bA2d63e5ba5edD684B2X', baseUnitsAmount, tokenAddress, salt)) 29 | .toThrow('0xCc6E2d20cC5AaFDCa329bA2d63e5ba5edD684B2X is not a valid address, or the checksum is invalid') 30 | }) 31 | }) 32 | -------------------------------------------------------------------------------- /examples/09.getConfig.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S yarn node 2 | /* eslint-disable no-unused-vars */ 3 | 4 | /* 5 | DO NOT EDIT THIS FILE BY HAND! 6 | Examples are generated using helpers/buildExamples.js script. 7 | Check README.md for more details. 8 | */ 9 | 10 | const sw = require('@rhino.fi/starkware-crypto') 11 | const getWeb3 = require('./helpers/getWeb3') 12 | 13 | const RhinofiClientFactory = require('../src') 14 | const envVars = require('./helpers/loadFromEnvOrConfig')( 15 | process.env.CONFIG_FILE_NAME 16 | ) 17 | const logExampleResult = require('./helpers/logExampleResult')(__filename) 18 | 19 | const ethPrivKey = envVars.ETH_PRIVATE_KEY 20 | // NOTE: you can also generate a new key using:` 21 | // const starkPrivKey = rhinofi.stark.createPrivateKey() 22 | const starkPrivKey = envVars.STARK_PRIVATE_KEY 23 | const rpcUrl = envVars.RPC_URL 24 | 25 | const { web3, provider } = getWeb3(ethPrivKey, rpcUrl) 26 | 27 | const rhinofiConfig = { 28 | api: envVars.API_URL, 29 | dataApi: envVars.DATA_API_URL, 30 | useAuthHeader: true, 31 | wallet: { 32 | type: 'tradingKey', 33 | meta: { 34 | starkPrivateKey: starkPrivKey 35 | } 36 | } 37 | // Add more variables to override default values 38 | } 39 | 40 | ;(async () => { 41 | const rhinofi = await RhinofiClientFactory(web3, rhinofiConfig) 42 | 43 | const getConfigResponse = await rhinofi.getConfig() 44 | 45 | logExampleResult(getConfigResponse) 46 | 47 | })() 48 | .catch(error => { 49 | console.error(error) 50 | process.exit(1) 51 | }) 52 | -------------------------------------------------------------------------------- /examples/04.getBalance.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S yarn node 2 | /* eslint-disable no-unused-vars */ 3 | 4 | /* 5 | DO NOT EDIT THIS FILE BY HAND! 6 | Examples are generated using helpers/buildExamples.js script. 7 | Check README.md for more details. 8 | */ 9 | 10 | const sw = require('@rhino.fi/starkware-crypto') 11 | const getWeb3 = require('./helpers/getWeb3') 12 | 13 | const RhinofiClientFactory = require('../src') 14 | const envVars = require('./helpers/loadFromEnvOrConfig')( 15 | process.env.CONFIG_FILE_NAME 16 | ) 17 | const logExampleResult = require('./helpers/logExampleResult')(__filename) 18 | 19 | const ethPrivKey = envVars.ETH_PRIVATE_KEY 20 | // NOTE: you can also generate a new key using:` 21 | // const starkPrivKey = rhinofi.stark.createPrivateKey() 22 | const starkPrivKey = envVars.STARK_PRIVATE_KEY 23 | const rpcUrl = envVars.RPC_URL 24 | 25 | const { web3, provider } = getWeb3(ethPrivKey, rpcUrl) 26 | 27 | const rhinofiConfig = { 28 | api: envVars.API_URL, 29 | dataApi: envVars.DATA_API_URL, 30 | useAuthHeader: true, 31 | wallet: { 32 | type: 'tradingKey', 33 | meta: { 34 | starkPrivateKey: starkPrivKey 35 | } 36 | } 37 | // Add more variables to override default values 38 | } 39 | 40 | ;(async () => { 41 | const rhinofi = await RhinofiClientFactory(web3, rhinofiConfig) 42 | 43 | const getBalanceResponse = await rhinofi.getBalance() 44 | 45 | logExampleResult(getBalanceResponse) 46 | 47 | })() 48 | .catch(error => { 49 | console.error(error) 50 | process.exit(1) 51 | }) 52 | -------------------------------------------------------------------------------- /examples/25.fastWithdrawalFee.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S yarn node 2 | /* eslint-disable no-unused-vars */ 3 | 4 | /* 5 | DO NOT EDIT THIS FILE BY HAND! 6 | Examples are generated using helpers/buildExamples.js script. 7 | Check README.md for more details. 8 | */ 9 | 10 | const sw = require('@rhino.fi/starkware-crypto') 11 | const getWeb3 = require('./helpers/getWeb3') 12 | 13 | const RhinofiClientFactory = require('../src') 14 | const envVars = require('./helpers/loadFromEnvOrConfig')( 15 | process.env.CONFIG_FILE_NAME 16 | ) 17 | const logExampleResult = require('./helpers/logExampleResult')(__filename) 18 | 19 | const ethPrivKey = envVars.ETH_PRIVATE_KEY 20 | // NOTE: you can also generate a new key using:` 21 | // const starkPrivKey = rhinofi.stark.createPrivateKey() 22 | const starkPrivKey = envVars.STARK_PRIVATE_KEY 23 | const rpcUrl = envVars.RPC_URL 24 | 25 | const { web3, provider } = getWeb3(ethPrivKey, rpcUrl) 26 | 27 | const rhinofiConfig = { 28 | api: envVars.API_URL, 29 | dataApi: envVars.DATA_API_URL, 30 | useAuthHeader: true, 31 | wallet: { 32 | type: 'tradingKey', 33 | meta: { 34 | starkPrivateKey: starkPrivKey 35 | } 36 | } 37 | // Add more variables to override default values 38 | } 39 | 40 | ;(async () => { 41 | const rhinofi = await RhinofiClientFactory(web3, rhinofiConfig) 42 | 43 | const response = await rhinofi.fastWithdrawalFee('ETH') 44 | 45 | logExampleResult(response) 46 | 47 | })() 48 | .catch(error => { 49 | console.error(error) 50 | process.exit(1) 51 | }) 52 | -------------------------------------------------------------------------------- /examples/03.getDeposits.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S yarn node 2 | /* eslint-disable no-unused-vars */ 3 | 4 | /* 5 | DO NOT EDIT THIS FILE BY HAND! 6 | Examples are generated using helpers/buildExamples.js script. 7 | Check README.md for more details. 8 | */ 9 | 10 | const sw = require('@rhino.fi/starkware-crypto') 11 | const getWeb3 = require('./helpers/getWeb3') 12 | 13 | const RhinofiClientFactory = require('../src') 14 | const envVars = require('./helpers/loadFromEnvOrConfig')( 15 | process.env.CONFIG_FILE_NAME 16 | ) 17 | const logExampleResult = require('./helpers/logExampleResult')(__filename) 18 | 19 | const ethPrivKey = envVars.ETH_PRIVATE_KEY 20 | // NOTE: you can also generate a new key using:` 21 | // const starkPrivKey = rhinofi.stark.createPrivateKey() 22 | const starkPrivKey = envVars.STARK_PRIVATE_KEY 23 | const rpcUrl = envVars.RPC_URL 24 | 25 | const { web3, provider } = getWeb3(ethPrivKey, rpcUrl) 26 | 27 | const rhinofiConfig = { 28 | api: envVars.API_URL, 29 | dataApi: envVars.DATA_API_URL, 30 | useAuthHeader: true, 31 | wallet: { 32 | type: 'tradingKey', 33 | meta: { 34 | starkPrivateKey: starkPrivKey 35 | } 36 | } 37 | // Add more variables to override default values 38 | } 39 | 40 | ;(async () => { 41 | const rhinofi = await RhinofiClientFactory(web3, rhinofiConfig) 42 | 43 | const getDepositsResponse = await rhinofi.getDeposits() 44 | 45 | logExampleResult(getDepositsResponse) 46 | 47 | })() 48 | .catch(error => { 49 | console.error(error) 50 | process.exit(1) 51 | }) 52 | -------------------------------------------------------------------------------- /examples/26.fastWithdrawalMaxAmount.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S yarn node 2 | /* eslint-disable no-unused-vars */ 3 | 4 | /* 5 | DO NOT EDIT THIS FILE BY HAND! 6 | Examples are generated using helpers/buildExamples.js script. 7 | Check README.md for more details. 8 | */ 9 | 10 | const sw = require('@rhino.fi/starkware-crypto') 11 | const getWeb3 = require('./helpers/getWeb3') 12 | 13 | const RhinofiClientFactory = require('../src') 14 | const envVars = require('./helpers/loadFromEnvOrConfig')( 15 | process.env.CONFIG_FILE_NAME 16 | ) 17 | const logExampleResult = require('./helpers/logExampleResult')(__filename) 18 | 19 | const ethPrivKey = envVars.ETH_PRIVATE_KEY 20 | // NOTE: you can also generate a new key using:` 21 | // const starkPrivKey = rhinofi.stark.createPrivateKey() 22 | const starkPrivKey = envVars.STARK_PRIVATE_KEY 23 | const rpcUrl = envVars.RPC_URL 24 | 25 | const { web3, provider } = getWeb3(ethPrivKey, rpcUrl) 26 | 27 | const rhinofiConfig = { 28 | api: envVars.API_URL, 29 | dataApi: envVars.DATA_API_URL, 30 | useAuthHeader: true, 31 | wallet: { 32 | type: 'tradingKey', 33 | meta: { 34 | starkPrivateKey: starkPrivKey 35 | } 36 | } 37 | // Add more variables to override default values 38 | } 39 | 40 | ;(async () => { 41 | const rhinofi = await RhinofiClientFactory(web3, rhinofiConfig) 42 | 43 | const response = await rhinofi.fastWithdrawalMaxAmount('ETH') 44 | 45 | logExampleResult(response) 46 | 47 | })() 48 | .catch(error => { 49 | console.error(error) 50 | process.exit(1) 51 | }) 52 | -------------------------------------------------------------------------------- /examples/11.getOrdersHist.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S yarn node 2 | /* eslint-disable no-unused-vars */ 3 | 4 | /* 5 | DO NOT EDIT THIS FILE BY HAND! 6 | Examples are generated using helpers/buildExamples.js script. 7 | Check README.md for more details. 8 | */ 9 | 10 | const sw = require('@rhino.fi/starkware-crypto') 11 | const getWeb3 = require('./helpers/getWeb3') 12 | 13 | const RhinofiClientFactory = require('../src') 14 | const envVars = require('./helpers/loadFromEnvOrConfig')( 15 | process.env.CONFIG_FILE_NAME 16 | ) 17 | const logExampleResult = require('./helpers/logExampleResult')(__filename) 18 | 19 | const ethPrivKey = envVars.ETH_PRIVATE_KEY 20 | // NOTE: you can also generate a new key using:` 21 | // const starkPrivKey = rhinofi.stark.createPrivateKey() 22 | const starkPrivKey = envVars.STARK_PRIVATE_KEY 23 | const rpcUrl = envVars.RPC_URL 24 | 25 | const { web3, provider } = getWeb3(ethPrivKey, rpcUrl) 26 | 27 | const rhinofiConfig = { 28 | api: envVars.API_URL, 29 | dataApi: envVars.DATA_API_URL, 30 | useAuthHeader: true, 31 | wallet: { 32 | type: 'tradingKey', 33 | meta: { 34 | starkPrivateKey: starkPrivKey 35 | } 36 | } 37 | // Add more variables to override default values 38 | } 39 | 40 | ;(async () => { 41 | const rhinofi = await RhinofiClientFactory(web3, rhinofiConfig) 42 | 43 | const getOrdersHistResponse = await rhinofi.getOrdersHist() 44 | 45 | logExampleResult(getOrdersHistResponse) 46 | 47 | })() 48 | .catch(error => { 49 | console.error(error) 50 | process.exit(1) 51 | }) 52 | -------------------------------------------------------------------------------- /examples/12.getUserConfig.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S yarn node 2 | /* eslint-disable no-unused-vars */ 3 | 4 | /* 5 | DO NOT EDIT THIS FILE BY HAND! 6 | Examples are generated using helpers/buildExamples.js script. 7 | Check README.md for more details. 8 | */ 9 | 10 | const sw = require('@rhino.fi/starkware-crypto') 11 | const getWeb3 = require('./helpers/getWeb3') 12 | 13 | const RhinofiClientFactory = require('../src') 14 | const envVars = require('./helpers/loadFromEnvOrConfig')( 15 | process.env.CONFIG_FILE_NAME 16 | ) 17 | const logExampleResult = require('./helpers/logExampleResult')(__filename) 18 | 19 | const ethPrivKey = envVars.ETH_PRIVATE_KEY 20 | // NOTE: you can also generate a new key using:` 21 | // const starkPrivKey = rhinofi.stark.createPrivateKey() 22 | const starkPrivKey = envVars.STARK_PRIVATE_KEY 23 | const rpcUrl = envVars.RPC_URL 24 | 25 | const { web3, provider } = getWeb3(ethPrivKey, rpcUrl) 26 | 27 | const rhinofiConfig = { 28 | api: envVars.API_URL, 29 | dataApi: envVars.DATA_API_URL, 30 | useAuthHeader: true, 31 | wallet: { 32 | type: 'tradingKey', 33 | meta: { 34 | starkPrivateKey: starkPrivKey 35 | } 36 | } 37 | // Add more variables to override default values 38 | } 39 | 40 | ;(async () => { 41 | const rhinofi = await RhinofiClientFactory(web3, rhinofiConfig) 42 | 43 | const getUserConfigResponse = await rhinofi.getUserConfig() 44 | 45 | logExampleResult(getUserConfigResponse) 46 | 47 | })() 48 | .catch(error => { 49 | console.error(error) 50 | process.exit(1) 51 | }) 52 | -------------------------------------------------------------------------------- /src/api/contract/depositFromProxiedStarkTx.js: -------------------------------------------------------------------------------- 1 | // TODO: this file contains very similar logic to ./deposit, so one could be 2 | // implemented in terms of the other once the TODO in the latter is resolved. 3 | const sendToDVFInterface = require('./sendToDVFInterface') 4 | 5 | const { fromQuantizedToBaseUnitsBN } = require('@rhino.fi/dvf-utils') 6 | const permitParamsToArgs = require('../../lib/util/permitParamsToArgs') 7 | 8 | module.exports = (dvf, { starkKey, tokenId, vaultId, amount, tokenAddress, quantum, permitParams = null }, options) => { 9 | const ethTokenInfo = dvf.token.getTokenInfoOrThrow('ETH') 10 | let action 11 | if (ethTokenInfo.starkTokenId === tokenId) { 12 | action = 'depositEth' 13 | } else if (permitParams) { 14 | action = 'depositWithPermit' 15 | } else { 16 | action = 'deposit' 17 | } 18 | 19 | // This should never happen. 20 | if (!ethTokenInfo.starkTokenId) { 21 | throw new Error('ethTokenInfo.starkTokenId not defined') 22 | } 23 | 24 | const methodArgs = [starkKey, tokenId, vaultId] 25 | const [args, value] = tokenId === ethTokenInfo.starkTokenId 26 | // For ETH, we convert amount to WEI and add as extra send arg 27 | ? [methodArgs, fromQuantizedToBaseUnitsBN(ethTokenInfo, amount).toString()] 28 | // For other tokens, use amount as is (should be quantised), and add to 29 | // method args. 30 | : [methodArgs.concat(amount.toString(), tokenAddress, quantum).concat(permitParamsToArgs(permitParams))] 31 | 32 | return sendToDVFInterface(dvf)(action)(args, value, options) 33 | } 34 | -------------------------------------------------------------------------------- /src/lib/stark/ledger/getPublicKey.test.js: -------------------------------------------------------------------------------- 1 | const instance = require('../../../api/test/helpers/instance') 2 | const mockGetConf = require('../../../api/test/fixtures/getConf') 3 | 4 | jest.mock('../../ledger/selectTransport') 5 | const mockSelector = require('../../ledger/selectTransport') 6 | 7 | const { 8 | createTransportReplayer, 9 | RecordStore 10 | } = require('@ledgerhq/hw-transport-mocker') 11 | 12 | const Transport = createTransportReplayer( 13 | RecordStore.fromString(` 14 | => e002000015058000002c8000003c800000008000000000000000 15 | <= 41043f1c41b1a135df85fe04d2eb22fe942d42191c45154ac03086835ea59efe5c5c425c3f01984de6c3ff2cb6bb0a7b5489c484ec04598b8b2d694b12e984a55a0d28376439324632643736636439334441333930363666394236393561646333336534646330386135349000 16 | => f0020000190680000a55a2862ad3d30829b6cdc08a54ab5b867c00000000 17 | <= 0401841559c5a886771644573dbb6dba210a1a7a0834afcf6bb3cbba1565ae7b3202f0f543d1b6666fa1e093b5d03feb90f0e68ab007baf587b6285d425d8a34dc9000 18 | 19 | `) 20 | ) 21 | 22 | let dvf 23 | 24 | describe('dvf.stark.ledger.getPublicKey', () => { 25 | beforeAll(async () => { 26 | mockGetConf() 27 | dvf = await instance() 28 | }) 29 | 30 | it('gets stark public key from ledger', async () => { 31 | const path = `44'/60'/0'/0'/0` 32 | 33 | mockSelector.mockImplementation(() => { return Transport }) 34 | 35 | const starkPublicKey = await dvf.stark.ledger.getPublicKey(path) 36 | 37 | expect(starkPublicKey.x).toMatch(/[\da-f]/i) 38 | expect(starkPublicKey.y).toMatch(/[\da-f]/i) 39 | }) 40 | }) 41 | -------------------------------------------------------------------------------- /examples/19.getVaultId.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S yarn node 2 | /* eslint-disable no-unused-vars */ 3 | 4 | /* 5 | DO NOT EDIT THIS FILE BY HAND! 6 | Examples are generated using helpers/buildExamples.js script. 7 | Check README.md for more details. 8 | */ 9 | 10 | const sw = require('@rhino.fi/starkware-crypto') 11 | const getWeb3 = require('./helpers/getWeb3') 12 | 13 | const RhinofiClientFactory = require('../src') 14 | const envVars = require('./helpers/loadFromEnvOrConfig')( 15 | process.env.CONFIG_FILE_NAME 16 | ) 17 | const logExampleResult = require('./helpers/logExampleResult')(__filename) 18 | 19 | const ethPrivKey = envVars.ETH_PRIVATE_KEY 20 | // NOTE: you can also generate a new key using:` 21 | // const starkPrivKey = rhinofi.stark.createPrivateKey() 22 | const starkPrivKey = envVars.STARK_PRIVATE_KEY 23 | const rpcUrl = envVars.RPC_URL 24 | 25 | const { web3, provider } = getWeb3(ethPrivKey, rpcUrl) 26 | 27 | const rhinofiConfig = { 28 | api: envVars.API_URL, 29 | dataApi: envVars.DATA_API_URL, 30 | useAuthHeader: true, 31 | wallet: { 32 | type: 'tradingKey', 33 | meta: { 34 | starkPrivateKey: starkPrivKey 35 | } 36 | } 37 | // Add more variables to override default values 38 | } 39 | 40 | ;(async () => { 41 | const rhinofi = await RhinofiClientFactory(web3, rhinofiConfig) 42 | 43 | const token = 'ETH' 44 | 45 | const getVaultIdResponse = await rhinofi.getVaultId(token) 46 | 47 | logExampleResult(getVaultIdResponse) 48 | 49 | })() 50 | .catch(error => { 51 | console.error(error) 52 | process.exit(1) 53 | }) 54 | -------------------------------------------------------------------------------- /src/api/transferAndWithdraw.js: -------------------------------------------------------------------------------- 1 | const FP = require('lodash/fp') 2 | const { Joi } = require('@rhino.fi/dvf-utils') 3 | const post = require('../lib/dvf/post-authenticated') 4 | 5 | const validateWithJoi = require('../lib/validators/validateWithJoi') 6 | const makeCreateSignedTransferTx = require('../lib/dvf/makeCreateSignedTransferTx') 7 | const generateRandomNonceV2 = require('../lib/dvf/generateRandomNonceV2') 8 | 9 | const schema = Joi.object({ 10 | token: Joi.string(), 11 | amount: Joi.bigNumber().greaterThan(0), 12 | feeAmount: Joi.bigNumber().default(0).optional(), 13 | recipientEthAddress: Joi.string(), 14 | nonce: Joi.number().integer().optional() 15 | }) 16 | 17 | const validateInputs = validateWithJoi(schema)('INVALID_METHOD_ARGUMENT')({ 18 | context: 'transferAndWithdraw' 19 | }) 20 | 21 | module.exports = async (dvf, data, authNonce, signature, createSignedTransferTx = makeCreateSignedTransferTx(dvf)) => { 22 | dvf = FP.set('config.useAuthHeader', true, dvf) 23 | const { token, amount, feeAmount, recipientEthAddress, nonce } = validateInputs(data) 24 | 25 | const transferData = { 26 | token, 27 | amount, 28 | feeAmount, 29 | recipientVaultId: 0, // We will always use vaultId 0 30 | recipientPublicKey: recipientEthAddress 31 | } 32 | 33 | const endpoint = '/v1/trading/w/transferAndWithdraw' 34 | 35 | const payload = await dvf.createTransferAndWithdrawPayload(transferData, createSignedTransferTx) 36 | 37 | return post(dvf, endpoint, authNonce, signature, { ...payload, nonce: nonce || generateRandomNonceV2() }) 38 | } 39 | -------------------------------------------------------------------------------- /examples/18.fullWithdrawalRequest.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S yarn node 2 | /* eslint-disable no-unused-vars */ 3 | 4 | /* 5 | DO NOT EDIT THIS FILE BY HAND! 6 | Examples are generated using helpers/buildExamples.js script. 7 | Check README.md for more details. 8 | */ 9 | 10 | const sw = require('@rhino.fi/starkware-crypto') 11 | const getWeb3 = require('./helpers/getWeb3') 12 | 13 | const RhinofiClientFactory = require('../src') 14 | const envVars = require('./helpers/loadFromEnvOrConfig')( 15 | process.env.CONFIG_FILE_NAME 16 | ) 17 | const logExampleResult = require('./helpers/logExampleResult')(__filename) 18 | 19 | const ethPrivKey = envVars.ETH_PRIVATE_KEY 20 | // NOTE: you can also generate a new key using:` 21 | // const starkPrivKey = rhinofi.stark.createPrivateKey() 22 | const starkPrivKey = envVars.STARK_PRIVATE_KEY 23 | const rpcUrl = envVars.RPC_URL 24 | 25 | const { web3, provider } = getWeb3(ethPrivKey, rpcUrl) 26 | 27 | const rhinofiConfig = { 28 | api: envVars.API_URL, 29 | dataApi: envVars.DATA_API_URL, 30 | useAuthHeader: true, 31 | wallet: { 32 | type: 'tradingKey', 33 | meta: { 34 | starkPrivateKey: starkPrivKey 35 | } 36 | } 37 | // Add more variables to override default values 38 | } 39 | 40 | ;(async () => { 41 | const rhinofi = await RhinofiClientFactory(web3, rhinofiConfig) 42 | 43 | const token = 'ETH' 44 | 45 | const withdrawalResponse = await rhinofi.fullWithdrawalRequest(token) 46 | 47 | logExampleResult(withdrawalResponse) 48 | 49 | })() 50 | .catch(error => { 51 | console.error(error) 52 | process.exit(1) 53 | }) 54 | -------------------------------------------------------------------------------- /examples/17.withdrawOnChain.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S yarn node 2 | /* eslint-disable no-unused-vars */ 3 | 4 | /* 5 | DO NOT EDIT THIS FILE BY HAND! 6 | Examples are generated using helpers/buildExamples.js script. 7 | Check README.md for more details. 8 | */ 9 | 10 | const sw = require('@rhino.fi/starkware-crypto') 11 | const getWeb3 = require('./helpers/getWeb3') 12 | 13 | const RhinofiClientFactory = require('../src') 14 | const envVars = require('./helpers/loadFromEnvOrConfig')( 15 | process.env.CONFIG_FILE_NAME 16 | ) 17 | const logExampleResult = require('./helpers/logExampleResult')(__filename) 18 | 19 | const ethPrivKey = envVars.ETH_PRIVATE_KEY 20 | // NOTE: you can also generate a new key using:` 21 | // const starkPrivKey = rhinofi.stark.createPrivateKey() 22 | const starkPrivKey = envVars.STARK_PRIVATE_KEY 23 | const rpcUrl = envVars.RPC_URL 24 | 25 | const { web3, provider } = getWeb3(ethPrivKey, rpcUrl) 26 | 27 | const rhinofiConfig = { 28 | api: envVars.API_URL, 29 | dataApi: envVars.DATA_API_URL, 30 | useAuthHeader: true, 31 | wallet: { 32 | type: 'tradingKey', 33 | meta: { 34 | starkPrivateKey: starkPrivKey 35 | } 36 | } 37 | // Add more variables to override default values 38 | } 39 | 40 | ;(async () => { 41 | const rhinofi = await RhinofiClientFactory(web3, rhinofiConfig) 42 | 43 | const token = 'ETH' 44 | 45 | const withdrawalResponse = await rhinofi.withdrawOnchain(token, rhinofi.config.ethAddress) 46 | 47 | logExampleResult(withdrawalResponse) 48 | })() 49 | .catch(error => { 50 | console.error(error) 51 | process.exit(1) 52 | }) 53 | -------------------------------------------------------------------------------- /src/api/contract/deposit.js: -------------------------------------------------------------------------------- 1 | const errorReasons = require('../../lib/dvf/errorReasons') 2 | 3 | module.exports = async (dvf, vaultId, token, amount, tradingKey) => { 4 | let value 5 | tradingKey = tradingKey || dvf.config.starkKeyHex 6 | 7 | if (token === 'ETH') { 8 | value = dvf.token.toBaseUnitAmount(token, amount) 9 | } else { 10 | value = dvf.token.toQuantizedAmount(token, amount) 11 | } 12 | 13 | const args = [tradingKey, dvf.token.getTokenInfo(token).starkTokenId, vaultId, value] 14 | 15 | const action = 'deposit' 16 | // In order to lock ETH we simply send ETH to the lockerAddress 17 | if (token === 'ETH') { 18 | args.pop() 19 | return dvf.eth.send( 20 | dvf.contract.abi.getStarkEx(), 21 | dvf.config.DVF.starkExContractAddress, 22 | action, 23 | args, 24 | value // send ETH to the contract 25 | ) 26 | } 27 | 28 | try { 29 | return dvf.eth.send( 30 | dvf.contract.abi.getStarkEx(), 31 | dvf.config.DVF.starkExContractAddress, 32 | action, 33 | args 34 | ) 35 | } catch (e) { 36 | // TODO: why are we not simply throwing here? We are not awaiting the send 37 | // so the error could only be caused by a bug in the block above but not 38 | // actual execution of the contract method (since it's async). 39 | if (!dvf.contract.isApproved(token)) { 40 | return { 41 | error: 'ERR_CORE_ETHFX_NEEDS_APPROVAL', 42 | reason: errorReasons.ERR_CORE_ETHFX_NEEDS_APPROVAL.trim(), 43 | originalError: e 44 | } 45 | } else { 46 | throw e 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /examples/01.register.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S yarn node 2 | /* eslint-disable no-unused-vars */ 3 | 4 | /* 5 | DO NOT EDIT THIS FILE BY HAND! 6 | Examples are generated using helpers/buildExamples.js script. 7 | Check README.md for more details. 8 | */ 9 | 10 | const sw = require('@rhino.fi/starkware-crypto') 11 | const getWeb3 = require('./helpers/getWeb3') 12 | 13 | const RhinofiClientFactory = require('../src') 14 | const envVars = require('./helpers/loadFromEnvOrConfig')( 15 | process.env.CONFIG_FILE_NAME 16 | ) 17 | const logExampleResult = require('./helpers/logExampleResult')(__filename) 18 | 19 | const ethPrivKey = envVars.ETH_PRIVATE_KEY 20 | // NOTE: you can also generate a new key using:` 21 | // const starkPrivKey = rhinofi.stark.createPrivateKey() 22 | const starkPrivKey = envVars.STARK_PRIVATE_KEY 23 | const rpcUrl = envVars.RPC_URL 24 | 25 | const { web3, provider } = getWeb3(ethPrivKey, rpcUrl) 26 | 27 | const rhinofiConfig = { 28 | api: envVars.API_URL, 29 | dataApi: envVars.DATA_API_URL, 30 | useAuthHeader: true, 31 | wallet: { 32 | type: 'tradingKey', 33 | meta: { 34 | starkPrivateKey: starkPrivKey 35 | } 36 | } 37 | // Add more variables to override default values 38 | } 39 | 40 | ;(async () => { 41 | const rhinofi = await RhinofiClientFactory(web3, rhinofiConfig) 42 | 43 | const keyPair = await rhinofi.stark.createKeyPair(starkPrivKey) 44 | 45 | const registerResponse = await rhinofi.register(keyPair.starkPublicKey) 46 | 47 | logExampleResult(registerResponse) 48 | 49 | })() 50 | .catch(error => { 51 | console.error(error) 52 | process.exit(1) 53 | }) 54 | -------------------------------------------------------------------------------- /src/api/estimatedNextBatchTime.test.js: -------------------------------------------------------------------------------- 1 | const nock = require('nock') 2 | const instance = require('./test/helpers/instance') 3 | 4 | const mockGetConf = require('./test/fixtures/getConf') 5 | 6 | let dvf 7 | 8 | describe('dvf.estimatedNextBatchTime', () => { 9 | beforeAll(async () => { 10 | nock.cleanAll() 11 | mockGetConf() 12 | dvf = await instance() 13 | }) 14 | 15 | beforeEach(() => { 16 | nock.cleanAll() 17 | }) 18 | 19 | it('Returns the config received from the API', async () => { 20 | const apiResponse = { 21 | estimatedTime: 10000 22 | } 23 | 24 | nock(dvf.config.api) 25 | .get('/v1/trading/r/estimatedNextBatchTime') 26 | .query(true) 27 | .reply(200, apiResponse) 28 | 29 | const response = await dvf.estimatedNextBatchTime() 30 | expect(response).toEqual(apiResponse) 31 | }) 32 | 33 | it('Posts to config API and gets error response', async () => { 34 | const apiErrorResponse = { 35 | statusCode: 422, 36 | error: 'Unprocessable Entity', 37 | message: 38 | 'Please contact support if you believe there should not be an error here', 39 | details: { 40 | name: 'RequestError', 41 | message: 'Error: connect ECONNREFUSED 127.0.0.1:2222' 42 | } 43 | } 44 | 45 | nock(dvf.config.api) 46 | .get('/v1/trading/r/estimatedNextBatchTime') 47 | .query(true) 48 | .reply(422, apiErrorResponse) 49 | 50 | try { 51 | await dvf.estimatedNextBatchTime() 52 | } catch (e) { 53 | expect(JSON.parse(e.error)).toEqual(apiErrorResponse) 54 | } 55 | }) 56 | }) 57 | -------------------------------------------------------------------------------- /examples/31.transfer.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S yarn node 2 | /* eslint-disable no-unused-vars */ 3 | 4 | /* 5 | DO NOT EDIT THIS FILE BY HAND! 6 | Examples are generated using helpers/buildExamples.js script. 7 | Check README.md for more details. 8 | */ 9 | 10 | const sw = require('@rhino.fi/starkware-crypto') 11 | const getWeb3 = require('./helpers/getWeb3') 12 | 13 | const RhinofiClientFactory = require('../src') 14 | const envVars = require('./helpers/loadFromEnvOrConfig')( 15 | process.env.CONFIG_FILE_NAME 16 | ) 17 | const logExampleResult = require('./helpers/logExampleResult')(__filename) 18 | 19 | const ethPrivKey = envVars.ETH_PRIVATE_KEY 20 | // NOTE: you can also generate a new key using:` 21 | // const starkPrivKey = rhinofi.stark.createPrivateKey() 22 | const starkPrivKey = envVars.STARK_PRIVATE_KEY 23 | const rpcUrl = envVars.RPC_URL 24 | 25 | const { web3, provider } = getWeb3(ethPrivKey, rpcUrl) 26 | 27 | const rhinofiConfig = { 28 | api: envVars.API_URL, 29 | dataApi: envVars.DATA_API_URL, 30 | useAuthHeader: true, 31 | wallet: { 32 | type: 'tradingKey', 33 | meta: { 34 | starkPrivateKey: starkPrivKey 35 | } 36 | } 37 | // Add more variables to override default values 38 | } 39 | 40 | ;(async () => { 41 | const rhinofi = await RhinofiClientFactory(web3, rhinofiConfig) 42 | 43 | const transferResponse = await rhinofi.transfer({ 44 | recipientEthAddress: rhinofi.config.DVF.deversifiAddress, 45 | token: 'USDT', 46 | amount: 5 47 | }) 48 | 49 | logExampleResult(transferResponse) 50 | })() 51 | .catch(error => { 52 | console.error(error) 53 | process.exit(1) 54 | }) 55 | -------------------------------------------------------------------------------- /examples/32.getRegistrationStatuses.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S yarn node 2 | /* eslint-disable no-unused-vars */ 3 | 4 | /* 5 | DO NOT EDIT THIS FILE BY HAND! 6 | Examples are generated using helpers/buildExamples.js script. 7 | Check README.md for more details. 8 | */ 9 | 10 | const sw = require('@rhino.fi/starkware-crypto') 11 | const getWeb3 = require('./helpers/getWeb3') 12 | 13 | const RhinofiClientFactory = require('../src') 14 | const envVars = require('./helpers/loadFromEnvOrConfig')( 15 | process.env.CONFIG_FILE_NAME 16 | ) 17 | const logExampleResult = require('./helpers/logExampleResult')(__filename) 18 | 19 | const ethPrivKey = envVars.ETH_PRIVATE_KEY 20 | // NOTE: you can also generate a new key using:` 21 | // const starkPrivKey = rhinofi.stark.createPrivateKey() 22 | const starkPrivKey = envVars.STARK_PRIVATE_KEY 23 | const rpcUrl = envVars.RPC_URL 24 | 25 | const { web3, provider } = getWeb3(ethPrivKey, rpcUrl) 26 | 27 | const rhinofiConfig = { 28 | api: envVars.API_URL, 29 | dataApi: envVars.DATA_API_URL, 30 | useAuthHeader: true, 31 | wallet: { 32 | type: 'tradingKey', 33 | meta: { 34 | starkPrivateKey: starkPrivKey 35 | } 36 | } 37 | // Add more variables to override default values 38 | } 39 | 40 | ;(async () => { 41 | const rhinofi = await RhinofiClientFactory(web3, rhinofiConfig) 42 | 43 | const registrationStatusesResponse = await rhinofi.getRegistrationStatuses({ 44 | targetEthAddress: '0x08152c1265dbc218ccc8ab5c574e6bd52279b3b7' 45 | }) 46 | 47 | logExampleResult(registrationStatusesResponse) 48 | })() 49 | .catch(error => { 50 | console.error(error) 51 | process.exit(1) 52 | }) 53 | -------------------------------------------------------------------------------- /examples/src/ammDeposit.js: -------------------------------------------------------------------------------- 1 | rhinofi.config.useAuthHeader = true 2 | 3 | const waitForDepositCreditedOnChain = require('./helpers/waitForDepositCreditedOnChain') 4 | 5 | const token1 = 'ETH' 6 | const token2 = 'USDT' 7 | if (process.env.DEPOSIT_FIRST === 'true') { 8 | const depositETHResponse = await rhinofi.deposit(token1, 0.1, starkPrivKey) 9 | const depositUSDTResponse = await rhinofi.deposit(token2, 1000, starkPrivKey) 10 | 11 | if (process.env.WAIT_FOR_DEPOSIT_READY === 'true') { 12 | await waitForDepositCreditedOnChain(rhinofi, depositETHResponse) 13 | await waitForDepositCreditedOnChain(rhinofi, depositUSDTResponse) 14 | } 15 | } 16 | 17 | const pool = `${token1}${token2}` 18 | 19 | // Amm deposit consist of 2 orders, one for each of the pool tokens. 20 | // The tokens need to be supplied in a specific ratio. This call fetches 21 | // order data from Rhino.fi API, given one of the tokens and desired deposit 22 | // amount for that token. 23 | const ammFundingOrderData = await rhinofi.getAmmFundingOrderData({ 24 | pool, 25 | token: 'ETH', 26 | amount: 0.1 27 | }) 28 | 29 | // ammFundingOrderData can be inspected/validate if desired, before signing 30 | // the orders it contains and posting them to Rhino.fi API. 31 | 32 | // This call signs the orders contained in the ammFundingOrderData before 33 | // posting them to Rhino.fi API. NOTE: if the orders are pre-signed, the 34 | // method will post them as is. 35 | const ammPostFundingOrderResponse = await rhinofi.postAmmFundingOrders( 36 | await rhinofi.applyFundingOrderDataSlippage(ammFundingOrderData, 0.05) 37 | ) 38 | 39 | logExampleResult(ammPostFundingOrderResponse) 40 | -------------------------------------------------------------------------------- /src/api/amm/schemas.js: -------------------------------------------------------------------------------- 1 | const { Joi } = require('@rhino.fi/dvf-utils') 2 | 3 | const unsignedInt = Joi.number().integer().min(0) 4 | const maxTimestampHours = Math.pow(2, 22) * 1000 * 60 * 60 - 1 5 | 6 | // NOTE: this schema should be shared between backend and client. 7 | const starkOrderSchema = Joi.object({ 8 | vaultIdSell: unsignedInt, 9 | vaultIdBuy: unsignedInt, 10 | amountSell: Joi.string(), 11 | amountBuy: Joi.string(), 12 | tokenSell: Joi.string(), 13 | tokenBuy: Joi.string(), 14 | // If not provided, random nonce will be generated for each order. 15 | nonce: unsignedInt.optional(), 16 | // If not provided, expirationTimestamp will be set based on 17 | // dvf.config.defaultStarkExpiry. 18 | expirationTimestamp: unsignedInt.max(maxTimestampHours).optional(), 19 | signature: Joi.object().optional().keys({ 20 | r: Joi.string(), 21 | s: Joi.string() 22 | }) 23 | // If orders are pre-signed, nonce and expirationTimestamp needs to be present. 24 | }).with('signature', ['nonce', 'expirationTimestamp']) 25 | 26 | const fundOrderDataSchema = Joi.object({ 27 | pool: Joi.string(), 28 | starkPublicKey: Joi 29 | .object({ 30 | x: Joi.string(), 31 | y: Joi.string() 32 | }) 33 | .optional(), 34 | orders: Joi 35 | .array() 36 | .items(Joi.object({ 37 | starkOrder: starkOrderSchema 38 | })) 39 | .length(2) 40 | }).and( 41 | // If any of these are present then all are required. 42 | 'starkPublicKey', 43 | 'orders.0.starkOrder.signature', 44 | 'orders.1.starkOrder.signature' 45 | ) 46 | 47 | module.exports = { 48 | fundOrderDataSchema, 49 | starkOrderSchema 50 | } 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rhino.fi/client-js", 3 | "version": "5.1.1", 4 | "main": "src/index.js", 5 | "files": [ 6 | "src", 7 | "README.md", 8 | "CHANGELOG.md" 9 | ], 10 | "scripts": { 11 | "test": "env-cmd -f ./env/test jest --testTimeout 10000", 12 | "test:watch": "env-cmd -f ./env/test jest --watch --testTimeout 10000 --verbose true", 13 | "test:coverage": "env-cmd -f ./env/test jest --testTimeout 10000 --coverage" 14 | }, 15 | "jest": { 16 | "coverageReporters": [ 17 | "html" 18 | ], 19 | "coverageDirectory": ".coverage" 20 | }, 21 | "keywords": [ 22 | "deversifi", 23 | "ethereum", 24 | "rhino.fi", 25 | "trading" 26 | ], 27 | "homepage": "https://github.com/rhinofi/client-js", 28 | "license": "MIT", 29 | "dependencies": { 30 | "@ledgerhq/hw-app-eth": "6.23.0", 31 | "@ledgerhq/hw-transport-webhid": "6.20.0", 32 | "@rhino.fi/dvf-utils": "^1.4.5", 33 | "@rhino.fi/starkware-crypto": "^0.1.7", 34 | "aigle": "suguru03/aigle#8739846ba9d4cfc116e1546da1181c73564cae0b", 35 | "aware": "^0.3.1", 36 | "bignumber.js": "^9.0.0", 37 | "eip-712": "1.0.0", 38 | "lodash": "^4.17.20", 39 | "ramda": "^0.27.1", 40 | "request": "^2.88.2", 41 | "request-promise": "^4.2.6", 42 | "web3": "^1.7.5", 43 | "web3-utils": "^1.3.0" 44 | }, 45 | "devDependencies": { 46 | "@ledgerhq/hw-transport-mocker": "^5.25.0", 47 | "@truffle/hdwallet-provider": "^2.0.13", 48 | "env-cmd": "^10.1.0", 49 | "jest": "^26.4.2", 50 | "mustache": "^4.0.0", 51 | "nock": "^13.0.4", 52 | "solc": "^0.4.24" 53 | }, 54 | "packageManager": "yarn@3.2.1" 55 | } 56 | -------------------------------------------------------------------------------- /examples/29.transferAndWithdraw.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S yarn node 2 | /* eslint-disable no-unused-vars */ 3 | 4 | /* 5 | DO NOT EDIT THIS FILE BY HAND! 6 | Examples are generated using helpers/buildExamples.js script. 7 | Check README.md for more details. 8 | */ 9 | 10 | const sw = require('@rhino.fi/starkware-crypto') 11 | const getWeb3 = require('./helpers/getWeb3') 12 | 13 | const RhinofiClientFactory = require('../src') 14 | const envVars = require('./helpers/loadFromEnvOrConfig')( 15 | process.env.CONFIG_FILE_NAME 16 | ) 17 | const logExampleResult = require('./helpers/logExampleResult')(__filename) 18 | 19 | const ethPrivKey = envVars.ETH_PRIVATE_KEY 20 | // NOTE: you can also generate a new key using:` 21 | // const starkPrivKey = rhinofi.stark.createPrivateKey() 22 | const starkPrivKey = envVars.STARK_PRIVATE_KEY 23 | const rpcUrl = envVars.RPC_URL 24 | 25 | const { web3, provider } = getWeb3(ethPrivKey, rpcUrl) 26 | 27 | const rhinofiConfig = { 28 | api: envVars.API_URL, 29 | dataApi: envVars.DATA_API_URL, 30 | useAuthHeader: true, 31 | wallet: { 32 | type: 'tradingKey', 33 | meta: { 34 | starkPrivateKey: starkPrivKey 35 | } 36 | } 37 | // Add more variables to override default values 38 | } 39 | 40 | ;(async () => { 41 | const rhinofi = await RhinofiClientFactory(web3, rhinofiConfig) 42 | 43 | const token = 'ETH' 44 | const amount = 0.1 45 | 46 | const withdrawalResponse = await rhinofi.transferAndWithdraw({ 47 | recipientEthAddress: rhinofi.get('account'), 48 | token, 49 | amount, 50 | }) 51 | 52 | logExampleResult(withdrawalResponse) 53 | 54 | })() 55 | .catch(error => { 56 | console.error(error) 57 | process.exit(1) 58 | }) 59 | -------------------------------------------------------------------------------- /examples/15.getWithdrawals.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S yarn node 2 | /* eslint-disable no-unused-vars */ 3 | 4 | /* 5 | DO NOT EDIT THIS FILE BY HAND! 6 | Examples are generated using helpers/buildExamples.js script. 7 | Check README.md for more details. 8 | */ 9 | 10 | const sw = require('@rhino.fi/starkware-crypto') 11 | const getWeb3 = require('./helpers/getWeb3') 12 | 13 | const RhinofiClientFactory = require('../src') 14 | const envVars = require('./helpers/loadFromEnvOrConfig')( 15 | process.env.CONFIG_FILE_NAME 16 | ) 17 | const logExampleResult = require('./helpers/logExampleResult')(__filename) 18 | 19 | const ethPrivKey = envVars.ETH_PRIVATE_KEY 20 | // NOTE: you can also generate a new key using:` 21 | // const starkPrivKey = rhinofi.stark.createPrivateKey() 22 | const starkPrivKey = envVars.STARK_PRIVATE_KEY 23 | const rpcUrl = envVars.RPC_URL 24 | 25 | const { web3, provider } = getWeb3(ethPrivKey, rpcUrl) 26 | 27 | const rhinofiConfig = { 28 | api: envVars.API_URL, 29 | dataApi: envVars.DATA_API_URL, 30 | useAuthHeader: true, 31 | wallet: { 32 | type: 'tradingKey', 33 | meta: { 34 | starkPrivateKey: starkPrivKey 35 | } 36 | } 37 | // Add more variables to override default values 38 | } 39 | 40 | ;(async () => { 41 | const rhinofi = await RhinofiClientFactory(web3, rhinofiConfig) 42 | 43 | // Get withtrawals for all tokens 44 | const token = undefined 45 | // And current user 46 | const userAddress = rhinofi.get('account') 47 | const getWithdrawalsResponse = await rhinofi.getWithdrawals(token, userAddress) 48 | 49 | logExampleResult(getWithdrawalsResponse) 50 | })() 51 | .catch(error => { 52 | console.error(error) 53 | process.exit(1) 54 | }) 55 | -------------------------------------------------------------------------------- /src/api/getMinMaxOrderSize.test.js: -------------------------------------------------------------------------------- 1 | const nock = require("nock"); 2 | const instance = require("./test/helpers/instance"); 3 | const _ = require("lodash"); 4 | 5 | const mockGetConf = require("./test/fixtures/getConf"); 6 | 7 | let dvf; 8 | 9 | describe("dvf.getMinMaxOrderSize()", () => { 10 | beforeAll(async () => { 11 | mockGetConf(); 12 | dvf = await instance(); 13 | }); 14 | 15 | it("Makes GET request to order-size API and gets response", async () => { 16 | const pair = 'ETH:USDT' 17 | 18 | const apiResponse = { 19 | minOrderSize: 0.05, 20 | maxOrderSize: 2000, 21 | }; 22 | 23 | nock(dvf.config.api) 24 | .get(`/market-data/ticker/${pair}/order-size`) 25 | .reply(200, apiResponse); 26 | 27 | const response = await dvf.getMinMaxOrderSize(pair); 28 | 29 | expect(response).toEqual(apiResponse); 30 | }); 31 | 32 | it("Makes GET request to order-size API with non suported pair and gets error response", async () => { 33 | const pair = 'ETH:USD' // incorrect 34 | 35 | const apiErrorResponse = { 36 | statusCode: 400, 37 | error: "Bad Request", 38 | message: 39 | '"symbol" must be one of [ETH:USDT, ZRX:USDT, ZRX:ETH, BTC:USDT, ETH:BTC, KON:RAD, KON:USDT, DAI:ETH, DAI:USDT, CUSDT:USDT, ETH:USDC]', 40 | validation: { 41 | source: "params", 42 | keys: ["symbol"], 43 | }, 44 | }; 45 | 46 | nock(dvf.config.api) 47 | .get(`/market-data/ticker/${pair}/order-size`) 48 | .reply(400, apiErrorResponse); 49 | 50 | try { 51 | await dvf.getMinMaxOrderSize(pair) 52 | } catch (e) { 53 | expect(e.error).toEqual(apiErrorResponse) 54 | } 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /examples/14.fastWithdrawal.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S yarn node 2 | /* eslint-disable no-unused-vars */ 3 | 4 | /* 5 | DO NOT EDIT THIS FILE BY HAND! 6 | Examples are generated using helpers/buildExamples.js script. 7 | Check README.md for more details. 8 | */ 9 | 10 | const sw = require('@rhino.fi/starkware-crypto') 11 | const getWeb3 = require('./helpers/getWeb3') 12 | 13 | const RhinofiClientFactory = require('../src') 14 | const envVars = require('./helpers/loadFromEnvOrConfig')( 15 | process.env.CONFIG_FILE_NAME 16 | ) 17 | const logExampleResult = require('./helpers/logExampleResult')(__filename) 18 | 19 | const ethPrivKey = envVars.ETH_PRIVATE_KEY 20 | // NOTE: you can also generate a new key using:` 21 | // const starkPrivKey = rhinofi.stark.createPrivateKey() 22 | const starkPrivKey = envVars.STARK_PRIVATE_KEY 23 | const rpcUrl = envVars.RPC_URL 24 | 25 | const { web3, provider } = getWeb3(ethPrivKey, rpcUrl) 26 | 27 | const rhinofiConfig = { 28 | api: envVars.API_URL, 29 | dataApi: envVars.DATA_API_URL, 30 | useAuthHeader: true, 31 | wallet: { 32 | type: 'tradingKey', 33 | meta: { 34 | starkPrivateKey: starkPrivKey 35 | } 36 | } 37 | // Add more variables to override default values 38 | } 39 | 40 | ;(async () => { 41 | const rhinofi = await RhinofiClientFactory(web3, rhinofiConfig) 42 | 43 | const fastWithdrawalResponse = await rhinofi.fastWithdrawal( 44 | // recipientEthAddress could be added here to send the withdrawal to address 45 | // other then users registered address. 46 | { token: 'ETH', amount: 0.005 } 47 | ) 48 | 49 | logExampleResult(fastWithdrawalResponse) 50 | 51 | })() 52 | .catch(error => { 53 | console.error(error) 54 | process.exit(1) 55 | }) 56 | -------------------------------------------------------------------------------- /examples/06.getOrders.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S yarn node 2 | /* eslint-disable no-unused-vars */ 3 | 4 | /* 5 | DO NOT EDIT THIS FILE BY HAND! 6 | Examples are generated using helpers/buildExamples.js script. 7 | Check README.md for more details. 8 | */ 9 | 10 | const sw = require('@rhino.fi/starkware-crypto') 11 | const getWeb3 = require('./helpers/getWeb3') 12 | 13 | const RhinofiClientFactory = require('../src') 14 | const envVars = require('./helpers/loadFromEnvOrConfig')( 15 | process.env.CONFIG_FILE_NAME 16 | ) 17 | const logExampleResult = require('./helpers/logExampleResult')(__filename) 18 | 19 | const ethPrivKey = envVars.ETH_PRIVATE_KEY 20 | // NOTE: you can also generate a new key using:` 21 | // const starkPrivKey = rhinofi.stark.createPrivateKey() 22 | const starkPrivKey = envVars.STARK_PRIVATE_KEY 23 | const rpcUrl = envVars.RPC_URL 24 | 25 | const { web3, provider } = getWeb3(ethPrivKey, rpcUrl) 26 | 27 | const rhinofiConfig = { 28 | api: envVars.API_URL, 29 | dataApi: envVars.DATA_API_URL, 30 | useAuthHeader: true, 31 | wallet: { 32 | type: 'tradingKey', 33 | meta: { 34 | starkPrivateKey: starkPrivKey 35 | } 36 | } 37 | // Add more variables to override default values 38 | } 39 | 40 | ;(async () => { 41 | const rhinofi = await RhinofiClientFactory(web3, rhinofiConfig) 42 | 43 | const getOrCreateActiveOrder = require('./helpers/getOrCreateActiveOrder') 44 | 45 | // Ensure that there is at least one order to get. 46 | await getOrCreateActiveOrder(rhinofi, starkPrivKey) 47 | 48 | const getOrdersResponse = await rhinofi.getOrders() 49 | 50 | logExampleResult(getOrdersResponse) 51 | 52 | })() 53 | .catch(error => { 54 | console.error(error) 55 | process.exit(1) 56 | }) 57 | -------------------------------------------------------------------------------- /examples/13.withdraw.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S yarn node 2 | /* eslint-disable no-unused-vars */ 3 | 4 | /* 5 | DO NOT EDIT THIS FILE BY HAND! 6 | Examples are generated using helpers/buildExamples.js script. 7 | Check README.md for more details. 8 | */ 9 | 10 | const sw = require('@rhino.fi/starkware-crypto') 11 | const getWeb3 = require('./helpers/getWeb3') 12 | 13 | const RhinofiClientFactory = require('../src') 14 | const envVars = require('./helpers/loadFromEnvOrConfig')( 15 | process.env.CONFIG_FILE_NAME 16 | ) 17 | const logExampleResult = require('./helpers/logExampleResult')(__filename) 18 | 19 | const ethPrivKey = envVars.ETH_PRIVATE_KEY 20 | // NOTE: you can also generate a new key using:` 21 | // const starkPrivKey = rhinofi.stark.createPrivateKey() 22 | const starkPrivKey = envVars.STARK_PRIVATE_KEY 23 | const rpcUrl = envVars.RPC_URL 24 | 25 | const { web3, provider } = getWeb3(ethPrivKey, rpcUrl) 26 | 27 | const rhinofiConfig = { 28 | api: envVars.API_URL, 29 | dataApi: envVars.DATA_API_URL, 30 | useAuthHeader: true, 31 | wallet: { 32 | type: 'tradingKey', 33 | meta: { 34 | starkPrivateKey: starkPrivKey 35 | } 36 | } 37 | // Add more variables to override default values 38 | } 39 | 40 | ;(async () => { 41 | const rhinofi = await RhinofiClientFactory(web3, rhinofiConfig) 42 | 43 | const token = 'ETH' 44 | const amount = 0.1 45 | 46 | // NOTE: withdraw method as been deprecated 47 | const withdrawalResponse = await rhinofi.transferAndWithdraw({ 48 | recipientEthAddress: rhinofi.get('account'), 49 | token, 50 | amount, 51 | }) 52 | 53 | logExampleResult(withdrawalResponse) 54 | 55 | })() 56 | .catch(error => { 57 | console.error(error) 58 | process.exit(1) 59 | }) 60 | -------------------------------------------------------------------------------- /examples/helpers/buildExamples.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S yarn node 2 | 3 | const fs = require('fs'); 4 | 5 | Mustache = require('mustache') 6 | 7 | const examplesSrcDir = `${__dirname}/../src` 8 | const outputDir = `${__dirname}/..` 9 | const exampleTemplate = fs.readFileSync(`${__dirname}/example.js.tmpl`).toString() 10 | const methodWithNoArgsTemplate = fs.readFileSync(`${__dirname}/methodWithNoArgsTemplate.js.tmpl`).toString() 11 | const files = fs.readdirSync(examplesSrcDir) 12 | const makeExampleFileName = require('./makeExampleFileName') 13 | 14 | const examples = require('./examplesList') 15 | 16 | const renderAndSave = (src, fileName) => { 17 | const srcIndented = src.split('\n') 18 | .map((line) => line != '' ? ' ' + line : line) 19 | .join('\n') 20 | 21 | const rendered = Mustache.render(exampleTemplate, { 22 | EXAMPLE_SRC: srcIndented 23 | }) 24 | 25 | const outFilePath = `${outputDir}/${fileName}` 26 | 27 | fs.writeFileSync(outFilePath, rendered) 28 | 29 | // Mark as executable 30 | const mode = fs.statSync(outFilePath).mode | 31 | fs.constants.S_IXUSR | 32 | fs.constants.S_IXGRP | 33 | fs.constants.S_IXOTH 34 | 35 | fs.chmodSync(outFilePath, mode) 36 | } 37 | 38 | examples.forEach((exampleName, index) => { 39 | const inFilePath = `${examplesSrcDir}/${exampleName}.js` 40 | const outFileName = makeExampleFileName(exampleName, index) 41 | 42 | const src = fs.existsSync(inFilePath) ? 43 | fs.readFileSync(inFilePath).toString() : 44 | Mustache.render(methodWithNoArgsTemplate, { METHOD_NAME: exampleName }) 45 | 46 | renderAndSave(src, outFileName) 47 | }) 48 | 49 | // files.forEach((fileName) => { 50 | // renderAndSave(fs.readFileSync(`${examplesSrcDir}/${fileName}`).toString()) 51 | // }) --------------------------------------------------------------------------------