Skip to content

Instantly share code, notes, and snippets.

@dekz
Created September 27, 2018 10:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dekz/9f728ae9b9bb61e41ac5c776ff8ef37f to your computer and use it in GitHub Desktop.
Save dekz/9f728ae9b9bb61e41ac5c776ff8ef37f to your computer and use it in GitHub Desktop.
import {
assetDataUtils,
BigNumber,
ContractWrappers,
generatePseudoRandomSalt,
Order,
orderHashUtils,
signatureUtils,
SignedOrder,
SignerType,
} from '0x.js';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { NETWORK_CONFIGS, TX_DEFAULTS } from '../configs';
import { DECIMALS, NULL_ADDRESS } from '../constants';
import { PrintUtils } from '../print_utils';
import { providerEngine } from '../provider_engine';
import { getRandomFutureDateInSeconds } from '../utils';
/**
* In this scenario a third party, called the sender, submits the cancel operation on behalf of the maker.
* This allows a sender to pay the gas for the maker. It can be combined with a custom sender
* contract with additional business logic (e.g checking a whitelist). Or the sender
* can choose how and when the transaction should be submitted, if at all.
* The maker creates and signs the order. The signed order and cancelOrder parameters for the
* execute transaction function call are signed by the maker as a proof of cancellation.
*/
export async function scenarioAsync(): Promise<void> {
PrintUtils.printScenario('Execute Transaction cancelOrderOrder');
// Initialize the ContractWrappers, this provides helper functions around calling
// 0x contracts as well as ERC20/ERC721 token contracts on the blockchain
const contractWrappers = new ContractWrappers(providerEngine, { networkId: NETWORK_CONFIGS.networkId });
// Initialize the Web3Wrapper, this provides helper functions around fetching
// account information, balances, general contract logs
const web3Wrapper = new Web3Wrapper(providerEngine);
const [maker, taker, sender] = await web3Wrapper.getAvailableAddressesAsync();
const feeRecipientAddress = sender;
const zrxTokenAddress = contractWrappers.exchange.getZRXTokenAddress();
const etherTokenAddress = contractWrappers.etherToken.getContractAddressIfExists();
if (!etherTokenAddress) {
throw new Error('Ether Token not found on this network');
}
const printUtils = new PrintUtils(
web3Wrapper,
contractWrappers,
{ maker, taker, sender },
{ WETH: etherTokenAddress, ZRX: zrxTokenAddress },
);
printUtils.printAccounts();
// the amount the maker is selling of maker asset
const makerAssetAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(5), DECIMALS);
// the amount the maker wants of taker asset
const takerAssetAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(0.1), DECIMALS);
// the amount of fees the maker pays in ZRX
const makerFee = Web3Wrapper.toBaseUnitAmount(new BigNumber(0.01), DECIMALS);
// the amount of fees the taker pays in ZRX
const takerFee = Web3Wrapper.toBaseUnitAmount(new BigNumber(0.01), DECIMALS);
// 0x v2 uses hex encoded asset data strings to encode all the information needed to identify an asset
const makerAssetData = assetDataUtils.encodeERC20AssetData(zrxTokenAddress);
const takerAssetData = assetDataUtils.encodeERC20AssetData(etherTokenAddress);
let txHash;
// Approve the ERC20 Proxy to move ZRX for maker
const makerZRXApprovalTxHash = await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync(
zrxTokenAddress,
maker,
);
await printUtils.awaitTransactionMinedSpinnerAsync('Maker ZRX Approval', makerZRXApprovalTxHash);
// Approve the ERC20 Proxy to move ZRX for taker
const takerZRXApprovalTxHash = await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync(
zrxTokenAddress,
taker,
);
await printUtils.awaitTransactionMinedSpinnerAsync('Taker ZRX Approval', takerZRXApprovalTxHash);
// Approve the ERC20 Proxy to move WETH for taker
const takerWETHApprovalTxHash = await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync(
etherTokenAddress,
taker,
);
await printUtils.awaitTransactionMinedSpinnerAsync('Taker WETH Approval', takerWETHApprovalTxHash);
// Convert ETH into WETH for taker by depositing ETH into the WETH contract
const takerWETHDepositTxHash = await contractWrappers.etherToken.depositAsync(
etherTokenAddress,
takerAssetAmount,
taker,
);
await printUtils.awaitTransactionMinedSpinnerAsync('Taker WETH Deposit', takerWETHDepositTxHash);
PrintUtils.printData('Setup', [
['Maker ZRX Approval', makerZRXApprovalTxHash],
['Taker ZRX Approval', takerZRXApprovalTxHash],
['Taker WETH Approval', takerWETHApprovalTxHash],
['Taker WETH Deposit', takerWETHDepositTxHash],
]);
// Set up the Order and fill it
const randomExpiration = getRandomFutureDateInSeconds();
// Create the order
const orderWithoutExchangeAddress = {
makerAddress: maker,
takerAddress: NULL_ADDRESS,
senderAddress: NULL_ADDRESS,
feeRecipientAddress,
expirationTimeSeconds: randomExpiration,
salt: generatePseudoRandomSalt(),
makerAssetAmount,
takerAssetAmount,
makerAssetData,
takerAssetData,
makerFee,
takerFee,
};
const exchangeAddress = contractWrappers.exchange.getContractAddress();
const order: Order = {
...orderWithoutExchangeAddress,
exchangeAddress,
};
printUtils.printOrder(order);
// Print out the Balances and Allowances
await printUtils.fetchAndPrintContractAllowancesAsync();
await printUtils.fetchAndPrintContractBalancesAsync();
// Generate the order hash and sign it
const orderHashHex = orderHashUtils.getOrderHashHex(order);
const signature = await signatureUtils.ecSignOrderHashAsync(
providerEngine,
orderHashHex,
maker,
SignerType.Default,
);
const signedOrder: SignedOrder = {
...order,
signature,
};
let orderInfo = await contractWrappers.exchange.getOrderInfoAsync(signedOrder);
printUtils.printOrderInfos({ order: orderInfo });
// The transaction encoder provides helpers in encoding 0x Exchange transactions to allow
// a third party to submit the transaction. This operates in the context of the signer (maker)
// rather then the context of the submitter (sender)
const transactionEncoder = await contractWrappers.exchange.transactionEncoderAsync();
// This is an ABI encoded function call that the taker wishes to perform
// in this scenario it is a fillOrder
const cancelData = transactionEncoder.cancelOrderTx(signedOrder);
// Generate a random salt to mitigate replay attacks
const makerCancelOrderTransactionSalt = generatePseudoRandomSalt();
// The maker signs the operation data (cancelOrder) with the salt
const executeTransactionHex = transactionEncoder.getTransactionHex(
cancelData,
makerCancelOrderTransactionSalt,
maker,
);
const makerCancelOrderSignatureHex = await signatureUtils.ecSignOrderHashAsync(
providerEngine,
executeTransactionHex,
maker,
SignerType.Default,
);
// The sender submits this operation via executeTransaction passing in the signature from the taker
txHash = await contractWrappers.exchange.executeTransactionAsync(
makerCancelOrderTransactionSalt,
maker,
cancelData,
makerCancelOrderSignatureHex,
sender,
{
gasLimit: TX_DEFAULTS.gas,
},
);
const txReceipt = await printUtils.awaitTransactionMinedSpinnerAsync('executeTransaction', txHash);
printUtils.printTransaction('Execute Transaction cancelOrderOrder', txReceipt, [['orderHash', orderHashHex]]);
orderInfo = await contractWrappers.exchange.getOrderInfoAsync(signedOrder);
printUtils.printOrderInfos({ order: orderInfo });
// Stop the Provider Engine
providerEngine.stop();
}
void (async () => {
try {
if (!module.parent) {
await scenarioAsync();
}
} catch (e) {
console.log(e);
providerEngine.stop();
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment