Skip to content

Instantly share code, notes, and snippets.

@coopermaruyama
Created January 6, 2020 20:13
Show Gist options
  • Save coopermaruyama/10605bc36da6886f4fa10bf1dcd2fc63 to your computer and use it in GitHub Desktop.
Save coopermaruyama/10605bc36da6886f4fa10bf1dcd2fc63 to your computer and use it in GitHub Desktop.
Create a signed order using 0x
//
// Generate a signed ZRX order
//
// Requires ts-node and tsconfig-paths to be installed
//
// USAGE EXAMPLE (Sell 5 orchid for 1 ETH):
//
// ERC20_ADDRESS=0x4575f41308EC1483f3d399aa9a2826d74Da13Deb \
// SELL_AMOUNT=5 \
// SELL_FOR_ETH_AMOUNT=1 \
// PRIV=123A.....875348 \
// ts-node -O '{"target": "es2017", "module": "commonjs"}' -r tsconfig-paths/register generate-signed-order.ts
//
import { ContractWrappers, ERC20TokenContract } from '@0x/contract-wrappers';
import { generatePseudoRandomSalt, Order, signatureUtils, orderHashUtils, assetDataUtils, SignedOrder } from '@0x/order-utils';
import Web3 from 'web3';
import { PrivateKeyWalletSubprovider, RPCSubprovider, Web3ProviderEngine, SupportedProvider } from '@0x/subproviders';
import erc20ABI from 'human-standard-token-abi';
import { BigNumber } from '@0x/utils';
import ethUtil from 'ethereumjs-util';
import Bluebird from 'bluebird';
import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
// Ensure env vars are passed
if (!process.env.ERC20_ADDRESS) {
throw 'ERC20_ADDRESS is a required env var';
}
if (!process.env.SELL_AMOUNT) {
throw `SELL_AMOUNT is a required env var`;
}
if (!process.env.SELL_FOR_ETH_AMOUNT) {
throw `SELL_FOR_ETH_AMOUNT is a required env var`;
}
if (!process.env.PRIV) {
throw `PRIV is a required env var`;
}
const EXCHANGE_ADDRESS = '0x61935cbdd02287b511119ddb11aeb42f1593b7ef';
const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
const FEE_RECIPIENT = '0x529bd031bb5ed1a3c1a40b016c5787f279cf8ada';
const WETH_ASSET_DATA = '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';
const NULL_BYTES = '0x';
const FEE = new BigNumber('0.05');
const MS_IN_ONE_SECOND = 1000;
const CHAIN_ID = 1; // mainnet
const MAX_UINT256 = new BigNumber(2).pow(256).minus(1);
async function createOrder(
tokenAddress: string,
sellAmount: BigNumber,
sellForEthAmount: BigNumber,
priv: string
): Promise<SignedOrder> {
const providerEngine = new Web3ProviderEngine();
const privSubprovider = new PrivateKeyWalletSubprovider(priv);
providerEngine.addProvider(privSubprovider);
providerEngine.addProvider(new RPCSubprovider(`https://mainnet.infura.io`));
providerEngine.start();
const now = new BigNumber(Date.now() / MS_IN_ONE_SECOND).integerValue();
const privBuff = Buffer.from(priv, 'hex');
const pubBuff = ethUtil.privateToAddress(privBuff);
const pub = `0x${pubBuff.toString('hex')}`;
const makerAssetData = assetDataUtils.encodeERC20AssetData(
tokenAddress
);
const provider = new Web3.providers.HttpProvider(`https://mainnet.infura.io`);
const web3 = new Web3(provider);
const Token = web3.eth.contract(erc20ABI);
const makerToken = Token.at(tokenAddress);
const makerDecimals = await Bluebird.promisify<string>(makerToken.decimals)();
const expMaker = new BigNumber(10).pow(+makerDecimals);
const expEth = new BigNumber(10).pow(18);
// approve the exchange to spend tokens on our behalf
await approve(pub, tokenAddress, provider);
// Create the order
const order: Order = {
exchangeAddress: EXCHANGE_ADDRESS,
makerAddress: pub,
takerAddress: NULL_ADDRESS,
senderAddress: NULL_ADDRESS,
feeRecipientAddress: pub,
expirationTimeSeconds: now.plus(900), // 15 Minutes
salt: generatePseudoRandomSalt(),
makerAssetAmount: sellAmount.times(expMaker),
takerAssetAmount: sellForEthAmount.times(expEth),
makerAssetData,
takerAssetData: WETH_ASSET_DATA,
makerFee: new BigNumber(0),
// takerFee: sellForEthAmount.times(FEE),
takerFee: new BigNumber(0),
makerFeeAssetData: '0x',
takerFeeAssetData: '0x',
chainId: 1
};
const signedOrder = await signatureUtils.ecSignOrderAsync(providerEngine, order, pub);
providerEngine.stop();
return signedOrder;
}
async function approve(walletAddr: string, tokenAddr: string, provider: SupportedProvider) {
const contractAddrs = getContractAddressesForChainOrThrow(CHAIN_ID);
const erc20 = new ERC20TokenContract(tokenAddr, provider);
const allowance = await erc20.allowance(
walletAddr,
contractAddrs.erc20Proxy
).callAsync();
// Create allowance tx.
if (allowance.eq(0)) {
await erc20.approve(
contractAddrs.erc20Proxy,
MAX_UINT256
).sendTransactionAsync({ from: walletAddr });
}
}
createOrder(
process.env.ERC20_ADDRESS,
new BigNumber(process.env.SELL_AMOUNT),
new BigNumber(process.env.SELL_FOR_ETH_AMOUNT),
process.env.PRIV
).then(console.log);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment