Last active
May 16, 2023 18:08
-
-
Save n2nco/127667aed48820780ff62017d0171e6b to your computer and use it in GitHub Desktop.
0x Limit Order - Create a limit order & broadcast it to Arbitrum
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { ethers } from 'ethers'; | |
import fetch from "node-fetch"; | |
import * as utils from "@0x/protocol-utils"; | |
import { providerUtils, BigNumber } from '@0x/utils'; | |
import { ContractWrappers } from '@0x/contract-wrappers'; | |
import * as contractAddresses from "@0x/contract-addresses"; | |
import Web3 from 'web3'; | |
const web3 = new Web3(process.env.RPC_URL); | |
import dotenv from 'dotenv'; | |
dotenv.config(); | |
import { Web3Wrapper } from '@0x/web3-wrapper'; | |
import { GanacheSubprovider, MnemonicWalletSubprovider, RPCSubprovider, Web3ProviderEngine } from '@0x/subproviders'; | |
import { NULL_ADDRESS, ZERO, ONE_SECOND_MS, TEN_MINUTES_MS } from './0x-constants.js'; | |
import { amountToWei } from './0xswap2.js' | |
import { findTokenBySymbolOrName} from './data_all_tokens.js'; | |
import { getUsdTokenPriceByContractAddresses } from './helpers-api.js'; | |
// import { providers } from "ethers" | |
const uni_token_arbitrum = '0xFa7F8980b0f1E64A2062791cc3b0871572f1F7f0'; | |
const usdc_token_arbitrum = '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8'; | |
//Reference: https://github.com/0xProject/0x-starter-project/blob/master/src/scenarios/cancel_pair_limit_orders.ts | |
// const getRandomFutureDateInSeconds = () => { | |
// let rd = new BigNumber(Date.now() + TEN_MINUTES_MS).div(ONE_SECOND_MS).integerValue(BigNumber.ROUND_CEIL); | |
// return rd | |
// }; | |
const getFutureExpiryInSeconds = () => Math.floor(Date.now() / 1000 + 300).toString(); // 5 min expiry | |
//buy-only limit order - "buy 0.11 uni with USDC when the price hits 4.74" | |
//sell-only limit order - "sell 0.11 UNI for USDC when the price hits 5.74" //taker = | |
let limOrder = async (orderType='buy', outputTokenSymbol='uni', limitAmount='0.11', limitPriceUsd='5.12', inputTokenSymbol='usdc', chainId=42161, fromAddress="0x5A68C82e4355968DB53177033ad3eDCfD62aCCcA") => { | |
// Find tokens | |
let inputToken = findTokenBySymbolOrName(inputTokenSymbol, chainId) | |
let outputToken = findTokenBySymbolOrName(outputTokenSymbol, chainId) | |
// Get current prices of tokens | |
let [ inputTokenPrice, outputTokenPrice ] = await getUsdTokenPriceByContractAddresses([inputToken.contractAddress, outputToken.contractAddress]) | |
// Convert limit amount from token units to wei | |
let limitAmountWei = amountToWei(limitAmount, inputToken.decimals).toString() | |
let makerToken; | |
let takerToken; | |
let makerAmount; | |
let takerAmount; | |
if (orderType === 'buy') { | |
makerToken = inputToken; // Paying with inputToken | |
takerToken = outputToken; // Buying outputToken | |
makerAmount = limitAmountWei; // The amount you want to pay | |
// Calculate the amount you want to buy. Calculation: limit price in USD / current output token price | |
let outputAmount = ((limitPriceUsd) / (outputTokenPrice)) .toString() | |
takerAmount = amountToWei(outputAmount, outputToken.decimals).toString(); | |
} else if (orderType === 'sell') { | |
makerToken = outputToken; // Selling outputToken | |
takerToken = inputToken; // Getting paid in inputToken | |
// Calculate the amount you want to receive. Calculation: limit price in USD / current input token price | |
let inputAmount = (limitPriceUsd).dividedBy(inputTokenPrice).toString() | |
takerAmount = amountToWei(inputAmount, inputToken.decimals).toString(); | |
makerAmount = limitAmountWei; // The amount you want to sell | |
} else { | |
throw new Error('Order type must be "buy" or "sell".'); | |
} | |
// provider.addProvider(new RPCSubprovider(rpcUrl)); | |
const pe = new Web3ProviderEngine(); | |
pe.addProvider(new RPCSubprovider(process.env.RPC_URL) | |
providerUtils.startProviderEngine(pe); | |
const contractWrappers = new ContractWrappers(pe, { chainId: 42161 }); | |
async function getLimitOrderInfoAsync(limitOrder) { //returns order info | |
return contractWrappers.exchangeProxy.getLimitOrderInfo(limitOrder).callAsync(); | |
} | |
async function fetchAndPrintLimitOrderInfosAsync(limitOrder) { | |
// Fetch and print the order info | |
const limitOrderInfo = await getLimitOrderInfoAsync(limitOrder); | |
// printUtils.printOrderInfos({ | |
// limitOrder: limitOrder, | |
// }); | |
console.log('limit order info: ', limitOrderInfo) | |
return limitOrderInfo; | |
} | |
// const [maker, taker] = await contractWrappers.getAvailableAddressesAsync(); | |
const web3Wrapper = new Web3Wrapper(pe); | |
// const [maker, taker] = await web3Wrapper.getAvailableAddressesAsync(); | |
// let maker = '0x5A68C82e4355968DB53177033ad3eDCfD62aCCcA'; | |
// let taker = '0x5A68C82e4355968DB53177033ad3eDCfD62aCCcA'; | |
let maker = fromAddress; | |
let taker = 0 // therefore the taker is the 0x exchange contract? | |
// let inputAmountWei = amountToWei(inputAmount, inputToken.decimals).toString() | |
// let outputAmountWei = amountToWei(outputAmount, outputToken.decimals).toString() | |
const limitOrder = new utils.LimitOrder({ | |
chainId: chainId, | |
verifyingContract: contractWrappers.contractAddresses.exchangeProxy, | |
makerToken: makerToken.contractAddress, //input token UNI | |
takerToken: takerToken.contractAddress, //output token USDC | |
makerAmount: makerAmount, //input amount 0.11 | |
takerAmount: takerAmount, //output amount = 0.11 * 5.12 = 0.5632 | |
// takerTokenFeeAmount: ZERO, | |
maker: maker, | |
// taker: taker, | |
sender: NULL_ADDRESS, | |
expiry: getFutureExpiryInSeconds(), | |
salt: Date.now().toString(), | |
}); | |
// .getSignatureWithProviderAsync(web3Wrapper.getProvider(), SignatureType.EthSign, maker); | |
let CHAIN_ID = 42161; | |
const addresses = contractAddresses.getContractAddressesForChainOrThrow(42161) | |
const order = new utils.LimitOrder({ | |
makerToken: addresses.etherToken, | |
takerToken: addresses.zrxToken, | |
makerAmount: "1", // NOTE: This is 1 WEI, 1 ETH would be 1000000000000000000 | |
takerAmount: "1000000000000000", // NOTE this is 0.001 ZRX. 1 ZRX would be 1000000000000000000 | |
maker: maker, | |
sender: NULL_ADDRESS, | |
expiry: getFutureExpiryInSeconds(), | |
salt: Date.now().toString(), | |
chainId: CHAIN_ID, | |
verifyingContract: addresses.exchangeProxy | |
}); | |
let x = await fetchAndPrintLimitOrderInfosAsync(limitOrder) | |
// const privateKey = process.env.ACCOUNT_PKEY; | |
// const privateKeyBuffer = Buffer.from(privateKey.replace('0x', ''), 'hex'); | |
// const signature = order.getSignatureWithKey(privateKeyBuffer); | |
const privateKey = process.env.ACCOUNT_PKEY; | |
const privateKeyBuffer = Buffer.from(privateKey, 'hex'); | |
const signature = await order.getSignatureWithKey(privateKeyBuffer); | |
const signedOrder = { ...order, signature }; | |
let prov = new ethers.providers.JsonRpcProvider('rocess.env.RPC_URL'); | |
const transactionHash = await prov.sendSignedTransaction(signedOrder.rawTransaction) | |
const txHash = await web3Wrapper.sendTransactionAsync({ | |
to: exchangeAddress, | |
data: '0x' + signedLimitOrder.encode(), | |
gas: 400000, | |
gasPrice: new BigNumber('10000000000'), | |
from: makerAddress, | |
value: 0, | |
}); | |
// const transactionHash = await web3.eth.sendSignedTransaction(signedOrder.rawTransaction); | |
const body = JSON.stringify(signedOrder) | |
try { | |
const resp = await fetch("https://arbitrum.api.0x.org/sra/v4/order", { | |
method: "POST", | |
body: body, | |
headers: { | |
"Content-Type": "application/json", | |
'0x-api-key': process.env.API_KEY | |
} | |
}); | |
if (resp.status === 200) { | |
console.log('success') | |
// alert("Successfully posted order to SRA"); | |
} else { | |
console.log('resp status: ', resp.status) | |
const body = await resp.text() | |
console.log('body: ', body) | |
console.log('resp: ', resp) | |
// alert( | |
// `ERROR(status code ${resp.status}): ${JSON.stringify(body, undefined, 2)}` | |
// ); | |
} | |
} catch (e) { | |
console.log('error: ', e.message) | |
// alert(`ERROR: ${e.toString()}`); | |
} | |
console.log('done') | |
} | |
limOrder().then(() => { | |
console.log('done') | |
}) | |
// const web3 = new Web3(`https://arb-mainnet.g.alchemy.com/v2/kcrKq8u6lybfHvmdLhjWweLyFAa_Pa73`); | |
// const web3Wrapper = new Web3Wrapper(web3); | |
// const [maker, taker] = await web3Wrapper.getAvailableAddressesAsync(); | |
async function sign() { | |
// const { MetamaskSubprovider } = require("@0x/subproviders"); | |
const CHAIN_ID = 42161; | |
const NULL_ADDRESS = "0x0000000000000000000000000000000000000000"; | |
const addresses = contractAddresses.getContractAddressesForChainOrThrow( | |
CHAIN_ID | |
); | |
const getFutureExpiryInSeconds = () => | |
Math.floor(Date.now() / 1000 + 300).toString(); // 5 min expiry | |
const myaddress = "0x5A68C82e4355968DB53177033ad3eDCfD62aCCcA"; | |
const maker = myaddress; | |
// Sign order | |
const order = new utils.LimitOrder({ | |
makerToken: addresses.etherToken, | |
takerToken: addresses.zrxToken, | |
makerAmount: "1", // NOTE: This is 1 WEI, 1 ETH would be 1000000000000000000 | |
takerAmount: "1000000000000000", // NOTE this is 0.001 ZRX. 1 ZRX would be 1000000000000000000 | |
maker: maker, | |
sender: NULL_ADDRESS, | |
expiry: getFutureExpiryInSeconds(), | |
salt: Date.now().toString(), | |
chainId: CHAIN_ID, | |
verifyingContract: addresses.exchangeProxy | |
}); | |
//use ethers as provider instead of metamask | |
// const provider = new ethers.providers.Web3Provider(web3.currentProvider); | |
// const signer = provider.getSigner(); | |
const privateKey = process.env.ACCOUNT_PKEY; | |
const privateKeyBuffer = Buffer.from(privateKey.replace('0x', ''), 'hex'); | |
const signature = order.getSignatureWithKey(privateKeyBuffer); | |
// const signature = order.getSignatureWithKey(process.env.ACCOUNT_PKEY) | |
// const signature = await order.getSignatureWithProviderAsync( | |
// signer, | |
// utils.SignatureType.EIP712 // Optional | |
// ); | |
console.log(`Signature: ${JSON.stringify(signature, undefined, 2)}`); | |
// const supportedProvider = new MetamaskSubprovider( | |
// window.web3.currentProvider | |
// ); | |
// const signature = await order.getSignatureWithProviderAsync( | |
// supportedProvider, | |
// utils.SignatureType.EIP712 // Optional | |
// ); | |
// console.log(`Signature: ${JSON.stringify(signature, undefined, 2)}`); | |
const signedOrder = { ...order, signature }; | |
const transactionHash = await web3.eth.sendSignedTransaction(signedLimitOrder.rawTransaction); | |
// await web3.eth.sendTransaction(await response.json()); | |
const resp = await fetch("https://arbitrum.api.0x.org/sra/v4/order", { | |
method: "POST", | |
body: JSON.stringify(signedOrder), | |
headers: { | |
"Content-Type": "application/json", | |
'0x-api-key': process.env.zeroEx-api-key | |
} | |
}); | |
if (resp.ok) { | |
const body = await resp.json(); | |
// Handle the response data here | |
} else { | |
const text = await resp.text(); | |
// Handle the error here | |
console.error(`ERROR(status code ${resp.status}): ${text}`); | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment