Skip to content

Instantly share code, notes, and snippets.

@n2nco
Last active May 16, 2023 18:08
Show Gist options
  • Save n2nco/127667aed48820780ff62017d0171e6b to your computer and use it in GitHub Desktop.
Save n2nco/127667aed48820780ff62017d0171e6b to your computer and use it in GitHub Desktop.
0x Limit Order - Create a limit order & broadcast it to Arbitrum
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