From ca2145bc20022b4dc99647acbdf3fb8af578ba69 Mon Sep 17 00:00:00 2001 From: rouzwelt Date: Tue, 16 May 2023 22:23:50 +0000 Subject: [PATCH] use provider to get vault balances re check vault balances and filter takeOrders based on it before submitting the tx to decrease race condition - add orderbook abi path to CLI args --- Dockerfile | 2 +- README.md | 4 +- arb-bot.js | 2 + docs/html/global.html | 49 +- docs/html/index.html | 2 +- docs/html/index.js.html | 495 ++++++----- docs/html/query.js.html | 8 +- docs/html/utils.js.html | 2 +- src/abis/OrderBook.json | 1793 +++++++++++++++++++++++++++++++++++++++ src/index.js | 489 ++++++----- src/query.js | 6 - 11 files changed, 2389 insertions(+), 463 deletions(-) create mode 100644 src/abis/OrderBook.json diff --git a/Dockerfile b/Dockerfile index c63f9373..352ad6b0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ FROM node:16 ADD . . RUN npm install -CMD while true; do node arb-bot -k "${BOT_WALLET_PRIVATEKEY}" -r "${RPC_URL}" --orderbook-address "${ORDERBOOK_ADDRESS}" --arb-address "${ARB_ADDRESS}" | tee -a logs.txt && sleep 30; done; +CMD while true; do node arb-bot -k "${BOT_WALLET_PRIVATEKEY}" -r "${RPC_URL}" --orderbook-address "${ORDERBOOK_ADDRESS}" --arb-address "${ARB_ADDRESS}" | tee -a logs.txt && sleep 10; done; diff --git a/README.md b/README.md index 6a4b0cfe..1df7acb0 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ Other optional arguments are: - `--subgraph-url` A custom subgraph endpoint URL, used to read order details from, the default is Rain Orderbook Subgraph. The custom subgraph should follow the Rain Orderbook Subgraph schema. - `--interpreter-abi` The path to IInterpreter ABI json file used for instantiating ethers contract instances, should be absolute path, default is the `./src/abis/IInerpreterV1.json`. - `--arb-abi` The path to Arb (ZeroExOrderBookFlashBorrower) ABI json file used for instantiating ethers contract instances, should be absolute path, default is the `./src/abis/ZeroExOrderBookFlashBorrower.json`. +- `--orderbook-abi` The path to Orderbook ABI json file used for instantiating ethers contract instances, should be absolute path, default is the `./src/abis/OrderBook.json`. - `--no-monthly-ratelimit` Used to respect monthly 200k 0x API calls, mainly used when not running this app on a bash loop, e.g. Github Actions - `-h` or `--help` To show the CLI command's help - `-v` or `--version` To show the app's version @@ -61,6 +62,7 @@ which will show: --arb-address
Address of the deployed arb contract. Will override 'arbAddress' field in './config.json' file --interpreter-abi Path to the IInterpreter contract ABI, default is the ABI in the './stc/abis' folder --arb-abi Path to the Arb (ZeroExOrderBookFlashBorrower) contract ABI, default is the ABI in the './stc/abis' folder + --orderbook-abi Path to the Orderbook contract ABI, should be absolute path, default is the ABI in the './src/abis' folder --no-monthly-ratelimit Pass to make the app respect 200k 0x API calls per month rate limit, mainly used when not running this app on a bash loop -V, --version output the version number -h, --help output usage information @@ -110,7 +112,7 @@ const wallet = new ethers.Wallet(walletPrivateKey, provider) const queryResult = await arb.query(subgraphUrl); // to get the configuration object -const config = await arb.getConfig(wallet, orderbookAddress, arbAddress, ...[ arbAbiPath, interpreterAbiPath ]); +const config = await arb.getConfig(wallet, orderbookAddress, arbAddress, ...[ arbAbiPath, interpreterAbiPath, orderbookAbiPath ]); // to run the clearing process and get the report object which holds the report of cleared orders const reports = await arb.clear(wallet, config, queryResult, ...[ slippage, prioritization ]) diff --git a/arb-bot.js b/arb-bot.js index 311a278b..311b8323 100755 --- a/arb-bot.js +++ b/arb-bot.js @@ -29,6 +29,7 @@ const getOptions = async argv => { .option("--arb-address
", "Address of the deployed arb contract. Will override 'arbAddress' field in './config.json' file") .option("--interpreter-abi ", "Path to the IInterpreter contract ABI, should be absolute path, default is the ABI in the './src/abis' folder") .option("--arb-abi ", "Path to the Arb (ZeroExOrderBookFlashBorrower) contract ABI, should be absolute path, default is the ABI in the './src/abis' folder") + .option("--orderbook-abi ", "Path to the Orderbook contract ABI, should be absolute path, default is the ABI in the './src/abis' folder") .option("--no-monthly-ratelimit", "Pass to make the app respect 200k 0x API calls per month rate limit, mainly used when not running this app on a bash loop") .version(version) .parse(argv) @@ -77,6 +78,7 @@ const main = async argv => { if (options.interpreterAbi) config.interpreterAbi = options.interpreterAbi; if (options.arbAbi) config.arbAbi = options.arbAbi; + if (options.orderbookAbi) config.orderbookAbi = options.orderbookAbi; const reports = await clear( signer, diff --git a/docs/html/global.html b/docs/html/global.html index 0507d432..206c9e7c 100644 --- a/docs/html/global.html +++ b/docs/html/global.html @@ -498,7 +498,7 @@
Parameters:
-number +string @@ -572,7 +572,7 @@
Parameters:
Source:
@@ -808,7 +808,7 @@
Parameters:
Source:
@@ -1240,7 +1240,7 @@
Parameters:
Source:
@@ -1286,7 +1286,7 @@
Returns:
-

getConfig(wallet, orderbookAddress, arbAddress, interpreterAbiPath, arbAbiPath)

+

getConfig(wallet, orderbookAddress, arbAddress, arbAbiPath, interpreterAbiPath, orderbookAbiPath)

@@ -1397,6 +1397,29 @@
Parameters:
+ + + arbAbiPath + + + + + +string + + + + + + + + + + (optional) The path to Arb contract ABI, default is ABI in './src/abis' folder + + + + interpreterAbiPath @@ -1422,7 +1445,7 @@
Parameters:
- arbAbiPath + orderbookAbiPath @@ -1438,7 +1461,7 @@
Parameters:
- (optional) The path to Arb contract ABI, default is ABI in './src/abis' folder + (optional) The path to Orderbook contract ABI, default is ABI in './src/abis' folder @@ -1479,7 +1502,7 @@
Parameters:
Source:
@@ -1649,7 +1672,7 @@
Parameters:
Source:
@@ -2042,7 +2065,7 @@
Parameters:
Source:
@@ -2504,7 +2527,7 @@
Parameters:
Source:
@@ -2641,7 +2664,7 @@
Parameters:
Source:
@@ -3005,7 +3028,7 @@

Home

Global

  • diff --git a/docs/html/index.html b/docs/html/index.html index cb2511a7..ea24accc 100644 --- a/docs/html/index.html +++ b/docs/html/index.html @@ -56,7 +56,7 @@

    Home

    Global

    • diff --git a/docs/html/index.js.html b/docs/html/index.js.html index e3edb9a1..b5954ce3 100644 --- a/docs/html/index.js.html +++ b/docs/html/index.js.html @@ -32,6 +32,7 @@

      Source: index.js

      const ethers = require("ethers"); const CONFIG = require("../config.json"); const { DefaultQuery } = require("./query"); +let { abi: orderbookAbi } = require("./abis/OrderBook.json"); const { abi: erc20Abi } = require("./abis/ERC20Upgradeable.json"); let { abi: interpreterAbi } = require("./abis/IInterpreterV1.json"); let { abi: arbAbi } = require("./abis/ZeroExOrderBookFlashBorrower.json"); @@ -267,8 +268,9 @@

      Source: index.js

      * @param {ethers.Wallet} wallet - The ethers wallet with private key instance * @param {string} orderbookAddress - The Rain Orderbook contract address deployed on the network * @param {string} arbAddress - The Rain Arb contract address deployed on the network - * @param {string} interpreterAbiPath - (optional) The path to IInterpreter contract ABI, default is ABI in './src/abis' folder * @param {string} arbAbiPath - (optional) The path to Arb contract ABI, default is ABI in './src/abis' folder + * @param {string} interpreterAbiPath - (optional) The path to IInterpreter contract ABI, default is ABI in './src/abis' folder + * @param {string} orderbookAbiPath - (optional) The path to Orderbook contract ABI, default is ABI in './src/abis' folder * @returns The configuration object */ exports.getConfig = async( @@ -276,7 +278,8 @@

      Source: index.js

      orderbookAddress, arbAddress, arbAbiPath = "", - interpreterAbiPath = "" + interpreterAbiPath = "", + orderbookAbiPath = "", ) => { const AddressPattern = /^0x[a-fA-F0-9]{40}$/; const chainId = (await wallet.getChainId()); @@ -287,6 +290,7 @@

      Source: index.js

      config.arbAddress = arbAddress; if (interpreterAbiPath) config.interpreterAbi = interpreterAbiPath; if (arbAbiPath) config.arbAbi = arbAbiPath; + if (orderbookAbiPath) config.orderbookAbi = orderbookAbiPath; return config; }; @@ -295,11 +299,11 @@

      Source: index.js

      * * @param {ethers.Signer} signer - The ethersjs signer constructed from provided private keys and rpc url provider * @param {object} config - The configuration object - * @param {number} slippage - (optional) The slippage for clearing orders, default is 0.01 i.e. 1 percent + * @param {string} slippage - (optional) The slippage for clearing orders, default is 0.01 i.e. 1 percent * @param {boolean} prioritization - (optional) Prioritize better deals to get cleared first, default is true * @returns The report of details of cleared orders */ -exports.clear = async(signer, config, queryResults, slippage = 0.01, prioritization = true) => { +exports.clear = async(signer, config, queryResults, slippage = "0.01", prioritization = true) => { let hits = 0; const api = config.apiUrl; const chainId = config.chainId; @@ -308,6 +312,7 @@

      Source: index.js

      const nativeToken = config.nativeToken.address; const intAbiPath = config.interpreterAbi; const arbAbiPath = config.arbAbi; + const orderbookAbiPath = config.orderbookAbi; // set the api key in headers if (config.apiKey) HEADERS.headers["0x-api-key"] = config.apiKey; @@ -319,10 +324,16 @@

      Source: index.js

      if (arbAbiPath) arbAbi = JSON.parse( fs.readFileSync(path.resolve(__dirname, arbAbiPath)).toString() )?.abi; + if (orderbookAbiPath) orderbookAbi = JSON.parse( + fs.readFileSync(path.resolve(__dirname, orderbookAbiPath)).toString() + )?.abi; // instantiating arb contract const arb = new ethers.Contract(arbAddress, arbAbi, signer); + // instantiating orderbook contract + const orderbook = new ethers.Contract(orderbookAddress, orderbookAbi, signer); + // orderbook as signer used for eval const obAsSigner = new ethers.VoidSigner( orderbookAddress, @@ -357,7 +368,11 @@

      Source: index.js

      const _output = order.validOutputs[j]; const _outputBalance = ethers.utils.parseUnits( ethers.utils.formatUnits( - _output.tokenVault.balance, + await orderbook.vaultBalance( + order.owner.id, + _output.token.id, + _output.vault.id.split("-")[0] + ), _output.token.decimals ) ); @@ -484,7 +499,7 @@

      Source: index.js

      if (bundledOrders[i].takeOrders.length) { try { console.log( - `------------------------- Trying To Clear For ${ + `------------------------- Trying To Clear ${ bundledOrders[i].buyTokenSymbol }/${ bundledOrders[i].sellTokenSymbol @@ -493,257 +508,301 @@

      Source: index.js

      ); console.log(`Buy Token Address: ${bundledOrders[i].buyToken}`); console.log(`Sell Token Address: ${bundledOrders[i].sellToken}`, "\n"); - console.log(">>> Getting current price for this token pair...", "\n"); - - let cumulativeAmount = ethers.constants.Zero; - bundledOrders[i].takeOrders.forEach(v => { - cumulativeAmount = cumulativeAmount.add(v.quoteAmount); - }); - const price = (await axios.get( - `${ - api - }swap/v1/price?buyToken=${ - bundledOrders[i].buyToken - }&sellToken=${ - bundledOrders[i].sellToken - }&sellAmount=${ - cumulativeAmount.div( - "1" + "0".repeat(18 - bundledOrders[i].sellTokenDecimals) - ).div(2).toString() - }&skipValidation=false`, - HEADERS - ))?.data?.price; - hits++; - const currentPrice = ethers.utils.parseUnits(price); - - console.log(`Quote amount: ${ethers.utils.formatUnits( - cumulativeAmount.div( - "1" + "0".repeat(18 - bundledOrders[i].sellTokenDecimals) - ).div(2), - bundledOrders[i].sellTokenDecimals - )} ${bundledOrders[i].sellTokenSymbol}`); - console.log(`Current market price of this token pair: ${price}`); - console.log("Current ratio of the orders in this token pair:"); - bundledOrders[i].takeOrders.forEach(v => { - console.log(ethers.utils.formatEther(v.ratio)); - }); - console.log( - "\n>>> Filtering the bundled orders of this token pair with lower ratio than current market price...", - "\n" + console.log(">>> Updating vault balances...", "\n"); + const newBalances = await Promise.allSettled( + bundledOrders[i].takeOrders.map(async(v) => { + return ethers.utils.parseUnits( + ethers.utils.formatUnits( + await orderbook.vaultBalance( + v.takeOrder.order.owner, + bundledOrders[i].sellToken, + v.takeOrder.order.validOutputs[ + v.takeOrder.outputIOIndex + ].vaultId + ), + bundledOrders[i].sellTokenDecimals + ) + ); + }) ); - + newBalances.forEach((v, j) => { + if (v.status === "fulfilled") { + if (v.value.isZero()) { + bundledOrders[i].takeOrders[j].quoteAmount = ethers.BigNumber.from("0"); + } + else { + if (v.value.lt(bundledOrders[i].takeOrders[j].quoteAmount)) { + bundledOrders[i].takeOrders[j].quoteAmount = v.value; + } + } + } + else { + console.log(`Could not get vault balance for order ${ + bundledOrders[i].takeOrders[j].id + } due to:`); + console.log(v.reason); + bundledOrders[i].takeOrders[j].quoteAmount = ethers.BigNumber.from("0"); + } + }); bundledOrders[i].takeOrders = bundledOrders[i].takeOrders.filter( - v => currentPrice.gte(v.ratio) + v => !v.quoteAmount.isZero() ); if (bundledOrders[i].takeOrders.length) { + console.log(">>> Getting current price for this token pair...", "\n"); - cumulativeAmount = ethers.constants.Zero; + let cumulativeAmount = ethers.constants.Zero; bundledOrders[i].takeOrders.forEach(v => { cumulativeAmount = cumulativeAmount.add(v.quoteAmount); }); - - const bundledQuoteAmount = cumulativeAmount.div( - "1" + "0".repeat(18 - bundledOrders[i].sellTokenDecimals) - ); - - console.log(">>> Getting quote for this token pair...", "\n"); - const response = await axios.get( + const price = (await axios.get( `${ api - }swap/v1/quote?buyToken=${ + }swap/v1/price?buyToken=${ bundledOrders[i].buyToken }&sellToken=${ bundledOrders[i].sellToken }&sellAmount=${ - bundledQuoteAmount.toString() - }&slippagePercentage=${ - slippage - }`, + cumulativeAmount.div( + "1" + "0".repeat(18 - bundledOrders[i].sellTokenDecimals) + ).div(2).toString() + }&skipValidation=false`, HEADERS - ); + ))?.data?.price; hits++; + const currentPrice = ethers.utils.parseUnits(price); - const txQuote = response?.data; - if (txQuote) { - console.log("the full quote that will be submitted is:" + "\n" + JSON.stringify(txQuote, null, 2), "\n"); - const takeOrdersConfigStruct = { - output: bundledOrders[i].buyToken, - input: bundledOrders[i].sellToken, - // max and min input should be exactly the same as quoted sell amount - // this makes sure the cleared order amount will exactly match the 0x quote - minimumInput: bundledQuoteAmount, - maximumInput: bundledQuoteAmount, - maximumIORatio: ethers.constants.MaxUint256, - orders: bundledOrders[i].takeOrders.map(v => v.takeOrder), - }; - - // submit the transaction - try { - console.log(">>> Estimating the profit for this token pair...", "\n"); - const gasLimit = await arb.estimateGas.arb( - takeOrdersConfigStruct, - // set to zero because only profitable transactions are submitted - 0, - txQuote.allowanceTarget, - txQuote.data, - { gasPrice: txQuote.gasPrice } - ); - const maxEstimatedProfit = estimateProfit( - txQuote, - bundledOrders[i], - gasLimit.mul(txQuote.gasPrice), - "0.1" - ).div( - "1" + "0".repeat(18 - bundledOrders[i].buyTokenDecimals) - ); - console.log(`Max Estimated Profit: ${ - ethers.utils.formatUnits( - maxEstimatedProfit, - bundledOrders[i].buyTokenDecimals - ) - } ${bundledOrders[i].buyTokenSymbol}`, "\n"); - - if (!maxEstimatedProfit.isNegative()) { - console.log(">>> Trying to submit the transaction for this token pair...", "\n"); - const tx = await arb.arb( + console.log(`Quote amount: ${ethers.utils.formatUnits( + cumulativeAmount.div( + "1" + "0".repeat(18 - bundledOrders[i].sellTokenDecimals) + ).div(2), + bundledOrders[i].sellTokenDecimals + )} ${bundledOrders[i].sellTokenSymbol}`); + console.log(`Current market price of this token pair: ${price}`); + console.log("Current ratio of the orders in this token pair:"); + bundledOrders[i].takeOrders.forEach(v => { + console.log(ethers.utils.formatEther(v.ratio)); + }); + + console.log( + "\n>>> Filtering the bundled orders of this token pair with lower ratio than current market price...", + "\n" + ); + + bundledOrders[i].takeOrders = bundledOrders[i].takeOrders.filter( + v => currentPrice.gte(v.ratio) + ); + + if (bundledOrders[i].takeOrders.length) { + + cumulativeAmount = ethers.constants.Zero; + bundledOrders[i].takeOrders.forEach(v => { + cumulativeAmount = cumulativeAmount.add(v.quoteAmount); + }); + + const bundledQuoteAmount = cumulativeAmount.div( + "1" + "0".repeat(18 - bundledOrders[i].sellTokenDecimals) + ); + + console.log(">>> Getting quote for this token pair...", "\n"); + const response = await axios.get( + `${ + api + }swap/v1/quote?buyToken=${ + bundledOrders[i].buyToken + }&sellToken=${ + bundledOrders[i].sellToken + }&sellAmount=${ + bundledQuoteAmount.toString() + }&slippagePercentage=${ + slippage + }`, + HEADERS + ); + hits++; + + const txQuote = response?.data; + if (txQuote) { + console.log("the full quote that will be submitted is:" + "\n" + JSON.stringify(txQuote, null, 2), "\n"); + const takeOrdersConfigStruct = { + output: bundledOrders[i].buyToken, + input: bundledOrders[i].sellToken, + // max and min input should be exactly the same as quoted sell amount + // this makes sure the cleared order amount will exactly match the 0x quote + minimumInput: bundledQuoteAmount, + maximumInput: bundledQuoteAmount, + maximumIORatio: ethers.constants.MaxUint256, + orders: bundledOrders[i].takeOrders.map(v => v.takeOrder), + }; + + // submit the transaction + try { + console.log(">>> Estimating the profit for this token pair...", "\n"); + const gasLimit = await arb.estimateGas.arb( takeOrdersConfigStruct, // set to zero because only profitable transactions are submitted 0, txQuote.allowanceTarget, txQuote.data, - { gasPrice: txQuote.gasPrice, gasLimit } + { gasPrice: txQuote.gasPrice } ); - console.log(ETHERSCAN_TX_PAGE[chainId] + tx.hash, "\n"); - console.log( - ">>> Transaction submitted successfully to the network, waiting for transaction to mine...", - "\n" + const maxEstimatedProfit = estimateProfit( + txQuote, + bundledOrders[i], + gasLimit.mul(txQuote.gasPrice), + "0.1" + ).div( + "1" + "0".repeat(18 - bundledOrders[i].buyTokenDecimals) ); - - try { - const receipt = await tx.wait(); - const income = getIncome(signer, receipt); - const gasCost = ethers.BigNumber.from( - txQuote.gasPrice - ).mul(receipt.gasUsed); - const clearActualPrice = getActualPrice( - receipt, - orderbookAddress, - arbAddress, - bundledQuoteAmount.mul( - "1" + "0".repeat( - 18 - bundledOrders[i].sellTokenDecimals - ) - ), - bundledOrders[i].sellTokenDecimals, + console.log(`Max Estimated Profit: ${ + ethers.utils.formatUnits( + maxEstimatedProfit, bundledOrders[i].buyTokenDecimals + ) + } ${bundledOrders[i].buyTokenSymbol}`, "\n"); + + if (!maxEstimatedProfit.isNegative()) { + console.log(">>> Trying to submit the transaction for this token pair...", "\n"); + const tx = await arb.arb( + takeOrdersConfigStruct, + // set to zero because only profitable transactions are submitted + 0, + txQuote.allowanceTarget, + txQuote.data, + { gasPrice: txQuote.gasPrice, gasLimit } + ); + console.log(ETHERSCAN_TX_PAGE[chainId] + tx.hash, "\n"); + console.log( + ">>> Transaction submitted successfully to the network, waiting for transaction to mine...", + "\n" ); - const netProfit = income - ? income.sub( - ethers.utils.parseUnits( - txQuote.buyTokenToEthRate - ).mul( - gasCost - ).div( + + try { + const receipt = await tx.wait(); + const income = getIncome(signer, receipt); + const gasCost = ethers.BigNumber.from( + txQuote.gasPrice + ).mul(receipt.gasUsed); + const clearActualPrice = getActualPrice( + receipt, + orderbookAddress, + arbAddress, + bundledQuoteAmount.mul( "1" + "0".repeat( - 36 - bundledOrders[i].buyTokenDecimals + 18 - bundledOrders[i].sellTokenDecimals + ) + ), + bundledOrders[i].sellTokenDecimals, + bundledOrders[i].buyTokenDecimals + ); + const netProfit = income + ? income.sub( + ethers.utils.parseUnits( + txQuote.buyTokenToEthRate + ).mul( + gasCost + ).div( + "1" + "0".repeat( + 36 - bundledOrders[i].buyTokenDecimals + ) ) ) - ) - : undefined; - console.log(`${bundledOrders[i].takeOrders.length} orders cleared successfully!`); - console.log(`Clear Quote Price: ${txQuote.price}`); - console.log(`Clear Actual Price: ${clearActualPrice}`); - console.log(`Clear Amount: ${ - ethers.utils.formatUnits( - bundledQuoteAmount, - bundledOrders[i].sellTokenDecimals - ) - } ${bundledOrders[i].sellTokenSymbol}`); - console.log(`Consumed Gas: ${ethers.utils.formatEther(gasCost)} ETH`, "\n"); - if (income) { - console.log(`Raw Income: ${ethers.utils.formatUnits( + : undefined; + console.log(`${bundledOrders[i].takeOrders.length} orders cleared successfully!`); + console.log(`Clear Quote Price: ${txQuote.price}`); + console.log(`Clear Actual Price: ${clearActualPrice}`); + console.log(`Clear Amount: ${ + ethers.utils.formatUnits( + bundledQuoteAmount, + bundledOrders[i].sellTokenDecimals + ) + } ${bundledOrders[i].sellTokenSymbol}`); + console.log(`Consumed Gas: ${ethers.utils.formatEther(gasCost)} ETH`, "\n"); + if (income) { + console.log(`Raw Income: ${ethers.utils.formatUnits( + income, + bundledOrders[i].buyTokenDecimals + )} ${bundledOrders[i].buyTokenSymbol}`); + console.log(`Net Profit: ${ethers.utils.formatUnits( + netProfit, + bundledOrders[i].buyTokenDecimals + )} ${bundledOrders[i].buyTokenSymbol}`, "\n"); + } + + report.push({ + transactionHash: receipt.transactionHash, + tokenPair: + bundledOrders[i].buyTokenSymbol + + "/" + + bundledOrders[i].sellTokenSymbol, + buyToken: bundledOrders[i].buyToken, + buyTokenDecimals: bundledOrders[i].buyTokenDecimals, + sellToken: bundledOrders[i].sellToken, + sellTokenDecimals: bundledOrders[i].sellTokenDecimals, + clearedAmount: bundledQuoteAmount.toString(), + clearPrice: txQuote.price, + clearGuaranteedPrice: txQuote.guaranteedPrice, + clearActualPrice, + maxEstimatedProfit, + gasUsed: receipt.gasUsed, + gasCost, income, - bundledOrders[i].buyTokenDecimals - )} ${bundledOrders[i].buyTokenSymbol}`); - console.log(`Net Profit: ${ethers.utils.formatUnits( netProfit, - bundledOrders[i].buyTokenDecimals - )} ${bundledOrders[i].buyTokenSymbol}`, "\n"); + clearedOrders: bundledOrders[i].takeOrders, + }); + + // // filter out upcoming take orders matching current cleared order + // if (i + 1 < bundledOrders.length) console.log( + // ">>> Updating upcoming bundled orders...", + // "\n" + // ); + // for (let j = i + 1; j < bundledOrders.length; j++) { + // bundledOrders[j].takeOrders = bundledOrders[j].takeOrders + // .filter(v => { + // for (const item of bundledOrders[i].takeOrders) { + // if ( + // item.id === v.id || + // ( + // bundledOrders[j].sellToken === + // bundledOrders[i].sellToken && + + // v.takeOrder.order.owner === + // item.takeOrder.order.owner && + + // v.takeOrder.order.validOutputs[ + // v.takeOrder.outputIOIndex + // ].vaultId === + // item.takeOrder.order.validOutputs[ + // v.takeOrder.outputIOIndex + // ].vaultId + // ) + // ) return false; + // return true; + // } + // }); + // } } - - report.push({ - transactionHash: receipt.transactionHash, - tokenPair: - bundledOrders[i].buyTokenSymbol + - "/" + - bundledOrders[i].sellTokenSymbol, - buyToken: bundledOrders[i].buyToken, - buyTokenDecimals: bundledOrders[i].buyTokenDecimals, - sellToken: bundledOrders[i].sellToken, - sellTokenDecimals: bundledOrders[i].sellTokenDecimals, - clearedAmount: bundledQuoteAmount.toString(), - clearPrice: txQuote.price, - clearGuaranteedPrice: txQuote.guaranteedPrice, - clearActualPrice, - maxEstimatedProfit, - gasUsed: receipt.gasUsed, - gasCost, - income, - netProfit, - clearedOrders: bundledOrders[i].takeOrders, - }); - - // filter out upcoming take orders matching current cleared order - if (i + 1 < bundledOrders.length) console.log( - ">>> Updating upcoming bundled orders...", - "\n" - ); - for (let j = i + 1; j < bundledOrders.length; j++) { - bundledOrders[j].takeOrders = bundledOrders[j].takeOrders - .filter(v => { - for (const item of bundledOrders[i].takeOrders) { - if ( - item.id === v.id || - ( - bundledOrders[j].sellToken === - bundledOrders[i].sellToken && - - v.takeOrder.order.owner === - item.takeOrder.order.owner && - - v.takeOrder.order.validOutputs[ - v.takeOrder.outputIOIndex - ].vaultId === - item.takeOrder.order.validOutputs[ - v.takeOrder.outputIOIndex - ].vaultId - ) - ) return false; - return true; - } - }); + catch (error) { + console.log(">>> Transaction execution failed due to:"); + console.log(error, "\n"); } } - catch (error) { - console.log(">>> Transaction execution failed due to:"); - console.log(error.reason, "\n"); - } + else console.log(">>> Skipping because estimated negative profit for this token pair", "\n"); + } + catch (error) { + console.log(">>> Transaction failed due to:"); + console.log(error, "\n"); } - else console.log(">>> Skipping because estimated negative profit for this token pair", "\n"); - } - catch (error) { - console.log(">>> Transaction failed due to:"); - console.log(error.reason, "\n"); } + else console.log("Failed to get quote from 0x", "\n"); } - else console.log("Failed to get quote from 0x", "\n"); + else console.log( + "All orders of this token pair have higher ratio than current market price, checking next token pair...", + "\n" + ); } - else console.log( - "All orders of this token pair have higher ratio than current market price, checking next token pair...", - "\n" - ); + else console.log("All orders of this token pair have empty vault balance, skipping...", "\n"); } catch (error) { console.log(">>> Failed to get quote from 0x due to:", "\n"); @@ -771,7 +830,7 @@

      Home

      Global

      • diff --git a/docs/html/query.js.html b/docs/html/query.js.html index 1484633c..db1a3d85 100644 --- a/docs/html/query.js.html +++ b/docs/html/query.js.html @@ -48,9 +48,6 @@

        Source: query.js

        decimals symbol } - tokenVault { - balance - } vault { id } @@ -62,9 +59,6 @@

        Source: query.js

        decimals symbol } - tokenVault { - balance - } vault { id } @@ -86,7 +80,7 @@

        Home

        Global

        • diff --git a/docs/html/utils.js.html b/docs/html/utils.js.html index 14c1ea1c..2c8d5006 100644 --- a/docs/html/utils.js.html +++ b/docs/html/utils.js.html @@ -237,7 +237,7 @@

          Home

          Global

          • diff --git a/src/abis/OrderBook.json b/src/abis/OrderBook.json new file mode 100644 index 00000000..0a74198f --- /dev/null +++ b/src/abis/OrderBook.json @@ -0,0 +1,1793 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "OrderBook", + "sourceName": "contracts/orderbook/OrderBook.sol", + "abi": [ + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "deployer", + "type": "address" + }, + { + "internalType": "bytes", + "name": "meta", + "type": "bytes" + } + ], + "internalType": "struct DeployerDiscoverableMetaV1ConstructionConfig", + "name": "config_", + "type": "tuple" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ActiveDebt", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "result", + "type": "bytes32" + } + ], + "name": "FlashLenderCallbackFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "i", + "type": "uint256" + } + ], + "name": "InvalidSignature", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "minimumInput", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "input", + "type": "uint256" + } + ], + "name": "MinimumInput", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "NotOrderOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "unmeta", + "type": "bytes" + } + ], + "name": "NotRainMetaV1", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "SameOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "aliceToken", + "type": "address" + }, + { + "internalType": "address", + "name": "bobToken", + "type": "address" + } + ], + "name": "TokenMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "expectedHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "actualHash", + "type": "bytes32" + } + ], + "name": "UnexpectedMetaHash", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroReceiver", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroToken", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "contract IExpressionDeployerV1", + "name": "expressionDeployer", + "type": "address" + }, + { + "components": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "bool", + "name": "handleIO", + "type": "bool" + }, + { + "components": [ + { + "internalType": "contract IInterpreterV1", + "name": "interpreter", + "type": "address" + }, + { + "internalType": "contract IInterpreterStoreV1", + "name": "store", + "type": "address" + }, + { + "internalType": "address", + "name": "expression", + "type": "address" + } + ], + "internalType": "struct Evaluable", + "name": "evaluable", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "vaultId", + "type": "uint256" + } + ], + "internalType": "struct IO[]", + "name": "validInputs", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "vaultId", + "type": "uint256" + } + ], + "internalType": "struct IO[]", + "name": "validOutputs", + "type": "tuple[]" + } + ], + "indexed": false, + "internalType": "struct Order", + "name": "order", + "type": "tuple" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "orderHash", + "type": "uint256" + } + ], + "name": "AddOrder", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "aliceOutput", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bobOutput", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "aliceInput", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bobInput", + "type": "uint256" + } + ], + "indexed": false, + "internalType": "struct ClearStateChange", + "name": "clearStateChange", + "type": "tuple" + } + ], + "name": "AfterClear", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "components": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "bool", + "name": "handleIO", + "type": "bool" + }, + { + "components": [ + { + "internalType": "contract IInterpreterV1", + "name": "interpreter", + "type": "address" + }, + { + "internalType": "contract IInterpreterStoreV1", + "name": "store", + "type": "address" + }, + { + "internalType": "address", + "name": "expression", + "type": "address" + } + ], + "internalType": "struct Evaluable", + "name": "evaluable", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "vaultId", + "type": "uint256" + } + ], + "internalType": "struct IO[]", + "name": "validInputs", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "vaultId", + "type": "uint256" + } + ], + "internalType": "struct IO[]", + "name": "validOutputs", + "type": "tuple[]" + } + ], + "indexed": false, + "internalType": "struct Order", + "name": "alice", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "bool", + "name": "handleIO", + "type": "bool" + }, + { + "components": [ + { + "internalType": "contract IInterpreterV1", + "name": "interpreter", + "type": "address" + }, + { + "internalType": "contract IInterpreterStoreV1", + "name": "store", + "type": "address" + }, + { + "internalType": "address", + "name": "expression", + "type": "address" + } + ], + "internalType": "struct Evaluable", + "name": "evaluable", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "vaultId", + "type": "uint256" + } + ], + "internalType": "struct IO[]", + "name": "validInputs", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "vaultId", + "type": "uint256" + } + ], + "internalType": "struct IO[]", + "name": "validOutputs", + "type": "tuple[]" + } + ], + "indexed": false, + "internalType": "struct Order", + "name": "bob", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "aliceInputIOIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "aliceOutputIOIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bobInputIOIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bobOutputIOIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "aliceBountyVaultId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bobBountyVaultId", + "type": "uint256" + } + ], + "indexed": false, + "internalType": "struct ClearConfig", + "name": "clearConfig", + "type": "tuple" + } + ], + "name": "Clear", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[][]", + "name": "context", + "type": "uint256[][]" + } + ], + "name": "Context", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "vaultId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "indexed": false, + "internalType": "struct DepositConfig", + "name": "config", + "type": "tuple" + } + ], + "name": "Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "subject", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "meta", + "type": "bytes" + } + ], + "name": "MetaV1", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "orderHash", + "type": "uint256" + } + ], + "name": "OrderExceedsMaxRatio", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "orderHash", + "type": "uint256" + } + ], + "name": "OrderNotFound", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "orderHash", + "type": "uint256" + } + ], + "name": "OrderZeroAmount", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "components": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "bool", + "name": "handleIO", + "type": "bool" + }, + { + "components": [ + { + "internalType": "contract IInterpreterV1", + "name": "interpreter", + "type": "address" + }, + { + "internalType": "contract IInterpreterStoreV1", + "name": "store", + "type": "address" + }, + { + "internalType": "address", + "name": "expression", + "type": "address" + } + ], + "internalType": "struct Evaluable", + "name": "evaluable", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "vaultId", + "type": "uint256" + } + ], + "internalType": "struct IO[]", + "name": "validInputs", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "vaultId", + "type": "uint256" + } + ], + "internalType": "struct IO[]", + "name": "validOutputs", + "type": "tuple[]" + } + ], + "indexed": false, + "internalType": "struct Order", + "name": "order", + "type": "tuple" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "orderHash", + "type": "uint256" + } + ], + "name": "RemoveOrder", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "bool", + "name": "handleIO", + "type": "bool" + }, + { + "components": [ + { + "internalType": "contract IInterpreterV1", + "name": "interpreter", + "type": "address" + }, + { + "internalType": "contract IInterpreterStoreV1", + "name": "store", + "type": "address" + }, + { + "internalType": "address", + "name": "expression", + "type": "address" + } + ], + "internalType": "struct Evaluable", + "name": "evaluable", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "vaultId", + "type": "uint256" + } + ], + "internalType": "struct IO[]", + "name": "validInputs", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "vaultId", + "type": "uint256" + } + ], + "internalType": "struct IO[]", + "name": "validOutputs", + "type": "tuple[]" + } + ], + "internalType": "struct Order", + "name": "order", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "inputIOIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "outputIOIndex", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "context", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "internalType": "struct SignedContextV1[]", + "name": "signedContext", + "type": "tuple[]" + } + ], + "indexed": false, + "internalType": "struct TakeOrderConfig", + "name": "config", + "type": "tuple" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "input", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "output", + "type": "uint256" + } + ], + "name": "TakeOrder", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "vaultId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "indexed": false, + "internalType": "struct WithdrawConfig", + "name": "config", + "type": "tuple" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Withdraw", + "type": "event" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "vaultId", + "type": "uint256" + } + ], + "internalType": "struct IO[]", + "name": "validInputs", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "vaultId", + "type": "uint256" + } + ], + "internalType": "struct IO[]", + "name": "validOutputs", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "contract IExpressionDeployerV1", + "name": "deployer", + "type": "address" + }, + { + "internalType": "bytes[]", + "name": "sources", + "type": "bytes[]" + }, + { + "internalType": "uint256[]", + "name": "constants", + "type": "uint256[]" + } + ], + "internalType": "struct EvaluableConfig", + "name": "evaluableConfig", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "meta", + "type": "bytes" + } + ], + "internalType": "struct OrderConfig", + "name": "config_", + "type": "tuple" + } + ], + "name": "addOrder", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "bool", + "name": "handleIO", + "type": "bool" + }, + { + "components": [ + { + "internalType": "contract IInterpreterV1", + "name": "interpreter", + "type": "address" + }, + { + "internalType": "contract IInterpreterStoreV1", + "name": "store", + "type": "address" + }, + { + "internalType": "address", + "name": "expression", + "type": "address" + } + ], + "internalType": "struct Evaluable", + "name": "evaluable", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "vaultId", + "type": "uint256" + } + ], + "internalType": "struct IO[]", + "name": "validInputs", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "vaultId", + "type": "uint256" + } + ], + "internalType": "struct IO[]", + "name": "validOutputs", + "type": "tuple[]" + } + ], + "internalType": "struct Order", + "name": "alice_", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "bool", + "name": "handleIO", + "type": "bool" + }, + { + "components": [ + { + "internalType": "contract IInterpreterV1", + "name": "interpreter", + "type": "address" + }, + { + "internalType": "contract IInterpreterStoreV1", + "name": "store", + "type": "address" + }, + { + "internalType": "address", + "name": "expression", + "type": "address" + } + ], + "internalType": "struct Evaluable", + "name": "evaluable", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "vaultId", + "type": "uint256" + } + ], + "internalType": "struct IO[]", + "name": "validInputs", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "vaultId", + "type": "uint256" + } + ], + "internalType": "struct IO[]", + "name": "validOutputs", + "type": "tuple[]" + } + ], + "internalType": "struct Order", + "name": "bob_", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "aliceInputIOIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "aliceOutputIOIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bobInputIOIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bobOutputIOIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "aliceBountyVaultId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bobBountyVaultId", + "type": "uint256" + } + ], + "internalType": "struct ClearConfig", + "name": "clearConfig_", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "context", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "internalType": "struct SignedContextV1[]", + "name": "aliceSignedContext_", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "context", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "internalType": "struct SignedContextV1[]", + "name": "bobSignedContext_", + "type": "tuple[]" + } + ], + "name": "clear", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "vaultId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct DepositConfig", + "name": "config_", + "type": "tuple" + } + ], + "name": "deposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "flashFee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC3156FlashBorrower", + "name": "receiver_", + "type": "address" + }, + { + "internalType": "address", + "name": "token_", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount_", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data_", + "type": "bytes" + } + ], + "name": "flashLoan", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token_", + "type": "address" + } + ], + "name": "maxFlashLoan", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes[]", + "name": "data", + "type": "bytes[]" + } + ], + "name": "multicall", + "outputs": [ + { + "internalType": "bytes[]", + "name": "results", + "type": "bytes[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "bool", + "name": "handleIO", + "type": "bool" + }, + { + "components": [ + { + "internalType": "contract IInterpreterV1", + "name": "interpreter", + "type": "address" + }, + { + "internalType": "contract IInterpreterStoreV1", + "name": "store", + "type": "address" + }, + { + "internalType": "address", + "name": "expression", + "type": "address" + } + ], + "internalType": "struct Evaluable", + "name": "evaluable", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "vaultId", + "type": "uint256" + } + ], + "internalType": "struct IO[]", + "name": "validInputs", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "vaultId", + "type": "uint256" + } + ], + "internalType": "struct IO[]", + "name": "validOutputs", + "type": "tuple[]" + } + ], + "internalType": "struct Order", + "name": "order_", + "type": "tuple" + } + ], + "name": "removeOrder", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "output", + "type": "address" + }, + { + "internalType": "address", + "name": "input", + "type": "address" + }, + { + "internalType": "uint256", + "name": "minimumInput", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maximumInput", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maximumIORatio", + "type": "uint256" + }, + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "bool", + "name": "handleIO", + "type": "bool" + }, + { + "components": [ + { + "internalType": "contract IInterpreterV1", + "name": "interpreter", + "type": "address" + }, + { + "internalType": "contract IInterpreterStoreV1", + "name": "store", + "type": "address" + }, + { + "internalType": "address", + "name": "expression", + "type": "address" + } + ], + "internalType": "struct Evaluable", + "name": "evaluable", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "vaultId", + "type": "uint256" + } + ], + "internalType": "struct IO[]", + "name": "validInputs", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "vaultId", + "type": "uint256" + } + ], + "internalType": "struct IO[]", + "name": "validOutputs", + "type": "tuple[]" + } + ], + "internalType": "struct Order", + "name": "order", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "inputIOIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "outputIOIndex", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "context", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "internalType": "struct SignedContextV1[]", + "name": "signedContext", + "type": "tuple[]" + } + ], + "internalType": "struct TakeOrderConfig[]", + "name": "orders", + "type": "tuple[]" + } + ], + "internalType": "struct TakeOrdersConfig", + "name": "takeOrders_", + "type": "tuple" + } + ], + "name": "takeOrders", + "outputs": [ + { + "internalType": "uint256", + "name": "totalInput_", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalOutput_", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "vaultBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "vaultId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct WithdrawConfig", + "name": "config_", + "type": "tuple" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x6080604052606580546001600160a01b031990811690915560668054909116905560006067553480156200003257600080fd5b506040516200578e3803806200578e83398101604081905262000055916200054a565b7f10f97a047a9d287eb96c885188fbdcd3bf1a525a1b31270fc4f9f6a0bc9554a660001b81620000908282602001516200020d60201b60201c565b60208101516040517fbea766d03fa1efd3f81cc8634d08320bc62bb0ed9234ac59bbaafa5893fb6b1391620000c9913391309162000658565b60405180910390a18051620000de9062000250565b5050600054610100900460ff1615808015620001015750600054600160ff909116105b806200011d5750303b1580156200011d575060005460ff166001145b620001865760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805460ff191660011790558015620001aa576000805461ff0019166101001790555b620001b462000314565b620001be6200037c565b801562000205576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050620007ca565b80516020820120828114620002405760405163074fe10f60e41b815260048101849052602481018290526044016200017d565b6200024b82620003d8565b505050565b604080516000808252602082019092526001600160a01b03831691635511cb6791906200028e565b6060815260200190600190039081620002785790505b5060408051600080825260208201908152818301928390526001600160e01b031960e086901b16909252620002c8929160448201620006c7565b6060604051808303816000875af1158015620002e8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200030e91906200075a565b50505050565b600054610100900460ff16620003705760405162461bcd60e51b815260206004820152602b60248201526000805160206200576e83398151915260448201526a6e697469616c697a696e6760a81b60648201526084016200017d565b6200037a62000408565b565b600054610100900460ff166200037a5760405162461bcd60e51b815260206004820152602b60248201526000805160206200576e83398151915260448201526a6e697469616c697a696e6760a81b60648201526084016200017d565b620003e3816200046a565b620004055780604051630c89984b60e31b81526004016200017d9190620007ae565b50565b600054610100900460ff16620004645760405162461bcd60e51b815260206004820152602b60248201526000805160206200576e83398151915260448201526a6e697469616c697a696e6760a81b60648201526084016200017d565b60018055565b60006008825110156200047f57506000919050565b50600801516001600160401b031667ff0a89c674ee78741490565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b0381118282101715620004d557620004d56200049a565b60405290565b604051601f8201601f191681016001600160401b03811182821017156200050657620005066200049a565b604052919050565b6001600160a01b03811681146200040557600080fd5b60005b838110156200054157818101518382015260200162000527565b50506000910152565b600060208083850312156200055e57600080fd5b82516001600160401b03808211156200057657600080fd5b90840190604082870312156200058b57600080fd5b62000595620004b0565b8251620005a2816200050e565b81528284015182811115620005b657600080fd5b80840193505086601f840112620005cc57600080fd5b825182811115620005e157620005e16200049a565b620005f5601f8201601f19168601620004db565b925080835287858286010111156200060c57600080fd5b6200061d8186850187870162000524565b5092830152509392505050565b600081518084526200064481602086016020860162000524565b601f01601f19169290920160200192915050565b60018060a01b03841681528260208201526060604082015260006200068160608301846200062a565b95945050505050565b600081518084526020808501945080840160005b83811015620006bc578151875295820195908201906001016200069e565b509495945050505050565b6000606082016060835280865180835260808501915060808160051b8601019250602080890160005b838110156200072257607f198887030185526200070f8683516200062a565b95509382019390820190600101620006f0565b5050858403818701525050506200073a81866200068a565b905082810360408401526200075081856200068a565b9695505050505050565b6000806000606084860312156200077057600080fd5b83516200077d816200050e565b602085015190935062000790816200050e565b6040850151909250620007a3816200050e565b809150509250925092565b602081526000620007c360208301846200062a565b9392505050565b614f9480620007da6000396000f3fe608060405234801561001057600080fd5b50600436106100c95760003560e01c80639e18968b11610081578063d9d98ce41161005b578063d9d98ce4146101cb578063e23746a3146101de578063e6b62636146101f157600080fd5b80639e18968b14610167578063ac9650d81461017a578063d97b2e481461019a57600080fd5b8063613255ab116100b2578063613255ab1461010b578063702766781461012c5780637a8048df1461013f57600080fd5b80634f266187146100ce5780635cffe9de146100e3575b600080fd5b6100e16100dc366004613759565b610204565b005b6100f66100f1366004613797565b610351565b60405190151581526020015b60405180910390f35b61011e610119366004613836565b610600565b604051908152602001610102565b6100e161013a366004613853565b6106aa565b61015261014d3660046138a0565b610a4c565b60408051928352602083019190915201610102565b6100e1610175366004613da4565b610fb0565b61018d610188366004613e66565b611570565b6040516101029190613f49565b61011e6101a8366004613fc9565b606960209081526000938452604080852082529284528284209052825290205481565b61011e6101d936600461400a565b6106a1565b6100e16101ec366004614036565b611665565b6100e16101ff366004613759565b611779565b61020c611860565b336000908152606960209081526040822090829061022c90850185613836565b73ffffffffffffffffffffffffffffffffffffffff1681526020808201929092526040908101600090812085840135825290925280822054925061027390840135836118d3565b905061027f81836140a0565b3360009081526069602090815260408220919061029e90870187613836565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600085602001358152602001908152602001600020819055507f2538ccc7ad2a119a36f2e65c1e2fc908beef800cd59b5d6680db24de18e7847a338483604051610324939291906140eb565b60405180910390a161034361033c6020850185613836565b33836118eb565b505061034e60018055565b50565b600061035b61199e565b73ffffffffffffffffffffffffffffffffffffffff85166103a8576040517fad1991f500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff86166103f5576040517f6ba9ecd800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6066805473ffffffffffffffffffffffffffffffffffffffff8088167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316179092556065805492891692909116919091179055606784905583156104765761047673ffffffffffffffffffffffffffffffffffffffff86168786611a0f565b6040517f23e30c8b00000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff8816906323e30c8b906104d59033908a908a9087908b908b9060040161416c565b6020604051808303816000875af11580156104f4573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061051891906141b2565b90507f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd9811461057b576040517f5b62c548000000000000000000000000000000000000000000000000000000008152600481018290526024015b60405180910390fd5b606754945084156105b7576065546066546105b19173ffffffffffffffffffffffffffffffffffffffff91821691163088611ae3565b60006067555b606580547fffffffffffffffffffffffff00000000000000000000000000000000000000009081169091556066805490911690556105f361199e565b5060019695505050505050565b600061060a611b47565b6106a1576040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa158015610678573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061069c91906141b2565b6106a4565b60005b92915050565b6106b2611860565b600080806106c360408501856141cb565b6106d1906020810190613836565b73ffffffffffffffffffffffffffffffffffffffff16635511cb676106f960408701876141cb565b610707906020810190614209565b61071460408901896141cb565b610722906040810190614209565b6040805160028082526020820152600081830152606081019091526040518663ffffffff1660e01b815260040161075d9594939291906142f7565b6060604051808303816000875af115801561077c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107a091906143ed565b92509250925060006040518060a001604052803373ffffffffffffffffffffffffffffffffffffffff16815260200160008780604001906107e191906141cb565b6107ef906020810190614209565b60018181106108005761080061443a565b90506020028101906108129190614469565b919091118252506040805160608101825273ffffffffffffffffffffffffffffffffffffffff8089168252878116602083810191909152908716828401528301520161085e87806144ce565b808060200260200160405190810160405280939291908181526020016000905b828210156108aa5761089b60608302860136819003810190614535565b8152602001906001019061087e565b505050505081526020018680602001906108c491906144ce565b808060200260200160405190810160405280939291908181526020016000905b828210156109105761090160608302860136819003810190614535565b815260200190600101906108e4565b50505050508152509050600061092582611b98565b60008181526068602052604090819020600190559091507f73e46afa6205785bdaa1daaf8b6ccc71715ec06b3b4264f5a00fde98671c2fc690339061096c908901896141cb565b61097a906020810190613836565b848460405161098c9493929190614633565b60405180910390a160006109a36060880188614469565b90501115610a3e576109f56109bb6060880188614469565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611be792505050565b7fbea766d03fa1efd3f81cc8634d08320bc62bb0ed9234ac59bbaafa5893fb6b133382610a2560608a018a614469565b604051610a35949392919061467d565b60405180910390a15b505050505061034e60018055565b600080610a57611860565b6000610abe604080516101208101825260006080820181815260a08301829052835160608082018652838252602080830185905282870185905260c086019290925260e0850181905261010085018190529184528301829052928201528181019190915290565b6040805160a08101825260008082526020808301829052835160608082018652838252918101839052808501929092529282015281810182905260808101829052908601355b610b1160a0880188614209565b905084108015610b215750600081115b15610ef857610b3360a0880188614209565b85818110610b4357610b4361443a565b9050602002810190610b5591906146b3565b610b5e906146e7565b805190935091506000610b7083611b98565b600081815260686020526040902054909150610be35782516040805133815273ffffffffffffffffffffffffffffffffffffffff909216602083015281018290527fe721f6888210c87666d3888f93b4139a86ab7757999ce54e268cba700d642a3b9060600160405180910390a1610eec565b610bf06020890189613836565b73ffffffffffffffffffffffffffffffffffffffff168360600151856020015181518110610c2057610c2061443a565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff1614610ccd578260600151846020015181518110610c6157610c6161443a565b6020908102919091018101515190610c7b908a018a613836565b6040517ff902523f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff928316600482015291166024820152604401610572565b610cdd6040890160208a01613836565b73ffffffffffffffffffffffffffffffffffffffff168360800151856040015181518110610d0d57610d0d61443a565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff1614610d6d578260800151846040015181518110610d4e57610d4e61443a565b602002602001015160000151886020016020810190610c7b9190613836565b6000610d888486602001518760400151338960600151611c28565b9050886080013581602001511115610df85783516040805133815273ffffffffffffffffffffffffffffffffffffffff909216602083015281018390527f460c258f27efac20e56c4607a28003d235168e76997ffb7542637d26d45ea6d8906060015b60405180910390a1610eea565b8051600003610e565783516040805133815273ffffffffffffffffffffffffffffffffffffffff909216602083015281018390527f3ba461a0ffd1b6782d4817ae7be605cfb1bbb4fa503c0dd613b8e50f1dcafacd90606001610deb565b8051600090610e669085906118d3565b90506000610e84836020015160018461232a9092919063ffffffff16565b9050610e9082866140a0565b9450610e9c818a614781565b9850610eaa86828486612348565b7f219a030b7ae56e7bea2baab709a4a45dc174a1f85e57730e5cb395bc3296254233888484604051610edf9493929190614794565b60405180910390a150505b505b50600190930192610b04565b610f068160608901356140a0565b95508660400135861015610f5357604080517f45094d8800000000000000000000000000000000000000000000000000000000815290880135600482015260248101879052604401610572565b610f84333087610f6660208c018c613836565b73ffffffffffffffffffffffffffffffffffffffff16929190611ae3565b610f9e610f976040890160208a01613836565b33886118eb565b50505050610fab60018055565b915091565b610fb8611860565b8351855173ffffffffffffffffffffffffffffffffffffffff9182169116036110285784516040517f227e4ce900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401610572565b83606001518360400135815181106110425761104261443a565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff16856080015184602001358151811061107e5761107e61443a565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff16146111435784608001518360200135815181106110bf576110bf61443a565b60200260200101516000015184606001518460400135815181106110e5576110e561443a565b6020908102919091010151516040517ff902523f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff928316600482015291166024820152604401610572565b60608501518051843590811061115b5761115b61443a565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff1684608001518460600135815181106111975761119761443a565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff16146111fc576060850151805184359081106111d6576111d661443a565b60200260200101516000015184608001518460600135815181106110e5576110e561443a565b60006068600061120b88611b98565b8152602001908152602001600020540361128a577fe721f6888210c87666d3888f93b4139a86ab7757999ce54e268cba700d642a3b33866000015161124f88611b98565b6040805173ffffffffffffffffffffffffffffffffffffffff94851681529390921660208401529082015260600160405180910390a1611560565b60006068600061129987611b98565b815260200190815260200160002054036112dd577fe721f6888210c87666d3888f93b4139a86ab7757999ce54e268cba700d642a3b33856000015161124f87611b98565b7fd153812deb929a6e4378f6f8cf61d010470840bf2e736f43fb2275803958bfa23386868660405161131294939291906148c4565b60405180910390a160006113358685600001358660200135886000015186611c28565b9050600061135286866040013587606001358a6000015188611c28565b9050600061136083836127f0565b9050611376888260400151836000015186612348565b61138a878260600151836020015185612348565b6060810151815160009161139d916140a0565b90506000826040015183602001516113b591906140a0565b9050811561145b57336000908152606960209081526040822060808d01518051869492938d01359081106113eb576113eb61443a565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008a60800135815260200190815260200160002060008282546114559190614781565b90915550505b80156114ff5733600090815260696020526040812060808b015180518493919060608d013590811061148f5761148f61443a565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008a60a00135815260200190815260200160002060008282546114f99190614781565b90915550505b5050604080513381528251602080830191909152830151818301529082015160608083019190915282015160808201527f3f20e55919cca701abb2a40ab72542b25ea7eed63a50f979dd2cd3231e5f488d9060a00160405180910390a15050505b61156960018055565b5050505050565b60608167ffffffffffffffff81111561158b5761158b6138d5565b6040519080825280602002602001820160405280156115be57816020015b60608152602001906001900390816115a95790505b50905060005b8281101561165e5761162e308585848181106115e2576115e261443a565b90506020028101906115f49190614469565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506128c592505050565b8282815181106116405761164061443a565b602002602001018190525080806116569061494e565b9150506115c4565b5092915050565b61166d611860565b61167a6020820182613836565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461170c57336116ba6020830183613836565b6040517f4702b91400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff928316600482015291166024820152604401610572565b600061171f61171a83614986565b611b98565b60008181526068602052604080822091909155519091507fe2dd87ac53228b6f23feabcc7301fe64145f23cc3c3ed75e6cd07341ae7f22829061176790339085908590614a62565b60405180910390a15061034e60018055565b611781611860565b7fadc7bd964a04a8a02261d33d2d09c6a7d9f539bc5eab77008e85fc6661ef123133826040516117b2929190614b67565b60405180910390a16117d133306040840135610f666020860186613836565b336000908152606960209081526040808320908401359290916117f690850185613836565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008360200135815260200190815260200160002060008282546118549190614781565b90915550506001805550565b6002600154036118cc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610572565b6002600155565b60008183106118e257816118e4565b825b9392505050565b60665473ffffffffffffffffffffffffffffffffffffffff848116911614801561192f575060655473ffffffffffffffffffffffffffffffffffffffff8381169116145b1561197257600061194b606754836118d390919063ffffffff16565b905061195781836140a0565b9150806067600082825461196b91906140a0565b9091555050505b80156119995761199973ffffffffffffffffffffffffffffffffffffffff84168383611a0f565b505050565b6119a6611b47565b15611a0d576065546066546067546040517f60817cfa00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff93841660048201529290911660248301526044820152606401610572565b565b60405173ffffffffffffffffffffffffffffffffffffffff83166024820152604481018290526119999084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152612a04565b60405173ffffffffffffffffffffffffffffffffffffffff80851660248301528316604482015260648101829052611b419085907f23b872dd0000000000000000000000000000000000000000000000000000000090608401611a61565b50505050565b60655460009073ffffffffffffffffffffffffffffffffffffffff16151580611b87575060665473ffffffffffffffffffffffffffffffffffffffff1615155b80611b93575060675415155b905090565b600081604051602001611bab9190614b91565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052805160209091012092915050565b611bf081612b10565b61034e57806040517f644cc2580000000000000000000000000000000000000000000000000000000081526004016105729190614ba4565b611c5a6040518060a0016040528060008152602001600081526020016060815260200160008152602001606081525090565b6000611c6587611b98565b60408051600480825260a08201909252919250606091600091816020015b6060815260200190600190039081611c8357905050895160408051600381526020810187905273ffffffffffffffffffffffffffffffffffffffff928316818301529189166060830152608082019052909150816001800381518110611ceb57611ceb61443a565b6020026020010181905250611e8089606001518981518110611d0f57611d0f61443a565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff168a606001518a81518110611d4757611d4761443a565b60200260200101516020015160ff168b606001518b81518110611d6c57611d6c61443a565b602002602001015160400151606960008e6000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008e606001518e81518110611dd357611dd361443a565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008e606001518e81518110611e3157611e3161443a565b602002602001015160400151815260200190815260200160002054600060408051600581526020810196909652858101949094526060850192909252608084015260a083015260c08201905290565b81600160030381518110611e9657611e9661443a565b6020026020010181905250611fdc89608001518881518110611eba57611eba61443a565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff168a608001518981518110611ef257611ef261443a565b60200260200101516020015160ff168b608001518a81518110611f1757611f1761443a565b602002602001015160400151606960008e6000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008e608001518d81518110611f7e57611f7e61443a565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008e608001518d81518110611e3157611e3161443a565b81600160040381518110611ff257611ff261443a565b60200260200101819052506120078186612b40565b9150506000886000015173ffffffffffffffffffffffffffffffffffffffff1690506000808a604001516000015173ffffffffffffffffffffffffffffffffffffffff16636715f8258c60400151602001518561206b8f6040015160400151612e70565b886040518563ffffffff1660e01b815260040161208b9493929190614c0c565b600060405180830381865afa1580156120a8573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526120ee9190810190614ca2565b9150915060008260028451038151811061210a5761210a61443a565b6020026020010151905060008360018551038151811061212c5761212c61443a565b602002602001015190506121708d608001518c8151811061214f5761214f61443a565b60200260200101516020015160ff16600284612e999092919063ffffffff16565b91506121cc8d608001518c8151811061218b5761218b61443a565b6020026020010151602001518e606001518e815181106121ad576121ad61443a565b602002602001015160200151600184612f1e909392919063ffffffff16565b90506122b4606960008f6000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008f608001518e8151811061222c5761222c61443a565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008f608001518e8151811061228a5761228a61443a565b602002602001015160400151815260200190815260200160002054836118d390919063ffffffff16565b60408051600281526020810183905280820184905260608101909152909250866002815181106122e6576122e661443a565b60200260200101819052506040518060a001604052808381526020018281526020018781526020018681526020018481525097505050505050505095945050505050565b60006123408484670de0b6b3a764000085612f99565b949350505050565b8281604001516003815181106123605761236061443a565b602002602001015160048151811061237a5761237a61443a565b60200260200101818152505081816040015160048151811061239e5761239e61443a565b60200260200101516004815181106123b8576123b861443a565b602090810291909101015282156124c557835173ffffffffffffffffffffffffffffffffffffffff1660009081526069602052604080822090830151805186939190600390811061240b5761240b61443a565b60200260200101516000815181106124255761242561443a565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083604001516003815181106124805761248061443a565b602002602001015160028151811061249a5761249a61443a565b6020026020010151815260200190815260200160002060008282546124bf9190614781565b90915550505b81156125c757835173ffffffffffffffffffffffffffffffffffffffff1660009081526069602052604080822090830151805185939190600490811061250d5761250d61443a565b60200260200101516000815181106125275761252761443a565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083604001516004815181106125825761258261443a565b602002602001015160028151811061259c5761259c61443a565b6020026020010151815260200190815260200160002060008282546125c191906140a0565b90915550505b7f17a5c0f3785132a57703932032f6863e7920434150aa1dc940e567b440fdce1f3382604001516040516125fc929190614d06565b60405180910390a16080810151511561268d5783604001516020015173ffffffffffffffffffffffffffffffffffffffff1663946aadc6826060015183608001516040518363ffffffff1660e01b815260040161265a929190614d35565b600060405180830381600087803b15801561267457600080fd5b505af1158015612688573d6000803e3d6000fd5b505050505b836020015115611b4157600084604001516000015173ffffffffffffffffffffffffffffffffffffffff16636715f82586604001516020015184606001516126dc896040015160400151612ff4565b86604001516040518563ffffffff1660e01b81526004016127009493929190614c0c565b600060405180830381865afa15801561271d573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526127639190810190614ca2565b9150506000815111156115695784604001516020015173ffffffffffffffffffffffffffffffffffffffff1663946aadc68360600151836040518363ffffffff1660e01b81526004016127b7929190614d35565b600060405180830381600087803b1580156127d157600080fd5b505af11580156127e5573d6000803e3d6000fd5b505050505050505050565b61281b6040518060800160405280600081526020016000815260200160008152602001600081525090565b6128466040518060800160405280600081526020016000815260200160008152602001600081525090565b602083015183516128659161285d9190600161232a565b8551906118d3565b8152602084015184516128869161287e9190600161232a565b8451906118d3565b602080830191909152840151815161289f91600161232a565b6040820152602080840151908201516128b991600161232a565b60608201529392505050565b606073ffffffffffffffffffffffffffffffffffffffff83163b61296b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e747261637400000000000000000000000000000000000000000000000000006064820152608401610572565b6000808473ffffffffffffffffffffffffffffffffffffffff16846040516129939190614d4e565b600060405180830381855af49150503d80600081146129ce576040519150601f19603f3d011682016040523d82523d6000602084013e6129d3565b606091505b50915091506129fb8282604051806060016040528060278152602001614f386027913961301f565b95945050505050565b6000612a66826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166130389092919063ffffffff16565b8051909150156119995780806020019051810190612a849190614d60565b611999576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610572565b6000600882511015612b2457506000919050565b506008015167ffffffffffffffff1667ff0a89c674ee78741490565b60606000825167ffffffffffffffff811115612b5e57612b5e6138d5565b604051908082528060200260200182016040528015612b87578160200160208202803683370190505b509050600080845111612b9b576000612ba1565b83516001015b855160010101905060008167ffffffffffffffff811115612bc457612bc46138d5565b604051908082528060200260200182016040528015612bf757816020015b6060815260200190600190039081612be25790505b5090506000612c1c604080516002815233602082015230818301526060810190915290565b828281518110612c2e57612c2e61443a565b602002602001018190525060005b8751811015612c8c578180600101925050878181518110612c5f57612c5f61443a565b6020026020010151838381518110612c7957612c7961443a565b6020908102919091010152600101612c3c565b50855115612e6657808060010191505083828281518110612caf57612caf61443a565b602002602001018190525060005b8651811015612e6457612d8e878281518110612cdb57612cdb61443a565b602002602001015160000151612d6b612d188a8581518110612cff57612cff61443a565b6020026020010151602001518051602090810291012090565b6040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101829052600090605c01604051602081830303815290604052805190602001209050919050565b898481518110612d7d57612d7d61443a565b602002602001015160400151613047565b612dc7576040517f52bf984800000000000000000000000000000000000000000000000000000000815260048101829052602401610572565b868181518110612dd957612dd961443a565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff16858281518110612e0d57612e0d61443a565b6020026020010181815250508180600101925050868181518110612e3357612e3361443a565b602002602001015160200151838381518110612e5157612e5161443a565b6020908102919091010152600101612cbd565b505b5095945050505050565b6000602082901b77ffffffffffffffffffffffffffffffffffffffff00000000166002176106a4565b60008260121115612ece5760128390036001831615612ec457612ebc8582613214565b9150506118e4565b612ebc8582613262565b6012831115612f17577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee83016002831615612f0d57612ebc858261328e565b612ebc8582613314565b50826118e4565b60008360ff168360ff161115612f5c578383036002831615612f4f57612f47868260ff1661328e565b915050612340565b612f47868260ff16613314565b8260ff168460ff161115612f90578284036001831615612f8357612f47868260ff16613214565b612f47868260ff16613262565b50929392505050565b600080612fa7868686613343565b90506001836002811115612fbd57612fbd614d7d565b148015612fda575060008480612fd557612fd5614dac565b868809115b156129fb57612fea600182614781565b9695505050505050565b600062010000602083901b77ffffffffffffffffffffffffffffffffffffffff0000000016176106a4565b6060831561302e5750816118e4565b6118e48383613410565b60606123408484600085613454565b6000806000613056858561356d565b9092509050600081600481111561306f5761306f614d7d565b1480156130a757508573ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16145b156130b7576001925050506118e4565b6000808773ffffffffffffffffffffffffffffffffffffffff16631626ba7e60e01b88886040516024016130ec929190614ddb565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516131759190614d4e565b600060405180830381855afa9150503d80600081146131b0576040519150601f19603f3d011682016040523d82523d6000602084013e6131b5565b606091505b50915091508180156131c8575080516020145b8015613208575080517f1626ba7e000000000000000000000000000000000000000000000000000000009061320690830160209081019084016141b2565b145b98975050505050505050565b6000604e821061323857821561322b57600161322e565b60005b60ff1690506106a4565b600a82900a80848161324c5761324c614dac565b049150808202841461165e575060010192915050565b6000604e8210156132855781600a0a838161327f5761327f614dac565b046118e4565b50600092915050565b6000604e82106132ce5782156132c4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6132c7565b60005b90506106a4565b50600a81900a82810290838183816132e8576132e8614dac565b041461165e577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff612340565b600a81900a6133238184614df4565b9050604e82106106a45782156132855761333e82600a614f2b565b6118e4565b600080807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8587098587029250828110838203039150508060000361339b5783828161339157613391614dac565b04925050506118e4565b8084116133a757600080fd5b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b8151156134205781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105729190614ba4565b6060824710156134e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610572565b6000808673ffffffffffffffffffffffffffffffffffffffff16858760405161350f9190614d4e565b60006040518083038185875af1925050503d806000811461354c576040519150601f19603f3d011682016040523d82523d6000602084013e613551565b606091505b5091509150613562878383876135b2565b979650505050505050565b60008082516041036135a35760208301516040840151606085015160001a61359787828585613652565b945094505050506135ab565b506000905060025b9250929050565b606083156136485782516000036136415773ffffffffffffffffffffffffffffffffffffffff85163b613641576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610572565b5081612340565b6123408383613410565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156136895750600090506003613738565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156136dd573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811661373157600060019250925050613738565b9150600090505b94509492505050565b60006060828403121561375357600080fd5b50919050565b60006060828403121561376b57600080fd5b6118e48383613741565b73ffffffffffffffffffffffffffffffffffffffff8116811461034e57600080fd5b6000806000806000608086880312156137af57600080fd5b85356137ba81613775565b945060208601356137ca81613775565b935060408601359250606086013567ffffffffffffffff808211156137ee57600080fd5b818801915088601f83011261380257600080fd5b81358181111561381157600080fd5b89602082850101111561382357600080fd5b9699959850939650602001949392505050565b60006020828403121561384857600080fd5b81356118e481613775565b60006020828403121561386557600080fd5b813567ffffffffffffffff81111561387c57600080fd5b8201608081850312156118e457600080fd5b600060c0828403121561375357600080fd5b6000602082840312156138b257600080fd5b813567ffffffffffffffff8111156138c957600080fd5b6123408482850161388e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516060810167ffffffffffffffff81118282101715613927576139276138d5565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613974576139746138d5565b604052919050565b801515811461034e57600080fd5b80356139958161397c565b919050565b6000606082840312156139ac57600080fd5b6139b4613904565b905081356139c181613775565b815260208201356139d181613775565b602082015260408201356139e481613775565b604082015292915050565b600067ffffffffffffffff821115613a0957613a096138d5565b5060051b60200190565b803560ff8116811461399557600080fd5b600060608284031215613a3657600080fd5b613a3e613904565b90508135613a4b81613775565b8152613a5960208301613a13565b60208201526040820135604082015292915050565b600082601f830112613a7f57600080fd5b81356020613a94613a8f836139ef565b61392d565b82815260609283028501820192828201919087851115613ab357600080fd5b8387015b85811015613ad657613ac98982613a24565b8452928401928101613ab7565b5090979650505050505050565b600060e08284031215613af557600080fd5b60405160a0810167ffffffffffffffff8282108183111715613b1957613b196138d5565b8160405282935084359150613b2d82613775565b818352613b3c6020860161398a565b6020840152613b4e866040870161399a565b604084015260a0850135915080821115613b6757600080fd5b613b7386838701613a6e565b606084015260c0850135915080821115613b8c57600080fd5b50613b9985828601613a6e565b6080830152505092915050565b600082601f830112613bb757600080fd5b813567ffffffffffffffff811115613bd157613bd16138d5565b613c0260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161392d565b818152846020838601011115613c1757600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f830112613c4557600080fd5b81356020613c55613a8f836139ef565b82815260059290921b84018101918181019086841115613c7457600080fd5b8286015b84811015613d9957803567ffffffffffffffff80821115613c9857600080fd5b908801906060828b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0011215613ccf5760008081fd5b613cd7613904565b86830135613ce481613775565b815260408381013583811115613cfa5760008081fd5b8401603f81018d13613d0c5760008081fd5b88810135613d1c613a8f826139ef565b81815260059190911b82018301908a8101908f831115613d3c5760008081fd5b928401925b82841015613d5a5783358252928b0192908b0190613d41565b858c0152505050606084013583811115613d745760008081fd5b613d828d8a83880101613ba6565b918301919091525085525050918301918301613c78565b509695505050505050565b60008060008060006101408688031215613dbd57600080fd5b853567ffffffffffffffff80821115613dd557600080fd5b613de189838a01613ae3565b96506020880135915080821115613df757600080fd5b613e0389838a01613ae3565b9550613e128960408a0161388e565b9450610100880135915080821115613e2957600080fd5b613e3589838a01613c34565b9350610120880135915080821115613e4c57600080fd5b50613e5988828901613c34565b9150509295509295909350565b60008060208385031215613e7957600080fd5b823567ffffffffffffffff80821115613e9157600080fd5b818501915085601f830112613ea557600080fd5b813581811115613eb457600080fd5b8660208260051b8501011115613ec957600080fd5b60209290920196919550909350505050565b60005b83811015613ef6578181015183820152602001613ede565b50506000910152565b60008151808452613f17816020860160208601613edb565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015613fbc577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0888603018452613faa858351613eff565b94509285019290850190600101613f70565b5092979650505050505050565b600080600060608486031215613fde57600080fd5b8335613fe981613775565b92506020840135613ff981613775565b929592945050506040919091013590565b6000806040838503121561401d57600080fd5b823561402881613775565b946020939093013593505050565b60006020828403121561404857600080fd5b813567ffffffffffffffff81111561405f57600080fd5b820160e081850312156118e457600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156106a4576106a4614071565b80356140be81613775565b73ffffffffffffffffffffffffffffffffffffffff16825260208181013590830152604090810135910152565b73ffffffffffffffffffffffffffffffffffffffff8416815260a0810161411560208301856140b3565b826080830152949350505050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b600073ffffffffffffffffffffffffffffffffffffffff808916835280881660208401525085604083015284606083015260a0608083015261320860a083018486614123565b6000602082840312156141c457600080fd5b5051919050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa18336030181126141ff57600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261423e57600080fd5b83018035915067ffffffffffffffff82111561425957600080fd5b6020019150600581901b36038213156135ab57600080fd5b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8311156142a357600080fd5b8260051b80836020870137939093016020019392505050565b600081518084526020808501945080840160005b838110156142ec578151875295820195908201906001016142d0565b509495945050505050565b6060808252810185905260006080600587901b8301810190830188835b898110156143c3577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8086850301835281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18c360301811261437557600080fd5b8b01602081810191359067ffffffffffffffff82111561439457600080fd5b8136038313156143a357600080fd5b6143ae878385614123565b96509485019493909301925050600101614314565b50505082810360208401526143d9818688614271565b9050828103604084015261320881856142bc565b60008060006060848603121561440257600080fd5b835161440d81613775565b602085015190935061441e81613775565b604085015190925061442f81613775565b809150509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261449e57600080fd5b83018035915067ffffffffffffffff8211156144b957600080fd5b6020019150368190038213156135ab57600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261450357600080fd5b83018035915067ffffffffffffffff82111561451e57600080fd5b60200191506060810236038213156135ab57600080fd5b60006060828403121561454757600080fd5b6118e48383613a24565b600081518084526020808501945080840160005b838110156142ec578151805173ffffffffffffffffffffffffffffffffffffffff1688528381015160ff16848901526040908101519088015260609096019590820190600101614565565b600073ffffffffffffffffffffffffffffffffffffffff80835116845260208301511515602085015260408301518181511660408601528160208201511660608601528160408201511660808601525050606082015160e060a085015261461a60e0850182614551565b9050608083015184820360c08601526129fb8282614551565b600073ffffffffffffffffffffffffffffffffffffffff80871683528086166020840152506080604083015261466c60808301856145b0565b905082606083015295945050505050565b73ffffffffffffffffffffffffffffffffffffffff85168152836020820152606060408201526000612fea606083018486614123565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff818336030181126141ff57600080fd5b6000608082360312156146f957600080fd5b6040516080810167ffffffffffffffff828210818311171561471d5761471d6138d5565b81604052843591508082111561473257600080fd5b61473e36838701613ae3565b83526020850135602084015260408501356040840152606085013591508082111561476857600080fd5b5061477536828601613c34565b60608301525092915050565b808201808211156106a4576106a4614071565b600073ffffffffffffffffffffffffffffffffffffffff8087168352602060808185015286516080808601526147ce6101008601826145b0565b90508188015160a086015260408089015160c08701526060808a01517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808885030160e08901528381518086528686019150868160051b870101878401935060005b828110156148a7577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe088830301845284518a815116835289810151878b85015261487b888501826142bc565b91890151848303858b01529190506148938183613eff565b968b0196958b01959350505060010161482f565b50948a019b909b5250509095019590955250919695505050505050565b600061012073ffffffffffffffffffffffffffffffffffffffff871683528060208401526148f4818401876145b0565b9050828103604084015261490881866145b0565b9150508235606083015260208301356080830152604083013560a0830152606083013560c0830152608083013560e083015260a083013561010083015295945050505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361497f5761497f614071565b5060010190565b60006106a43683613ae3565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126149c757600080fd5b830160208101925035905067ffffffffffffffff8111156149e757600080fd5b6060810236038213156135ab57600080fd5b8183526000602080850194508260005b858110156142ec578135614a1c81613775565b73ffffffffffffffffffffffffffffffffffffffff16875260ff614a41838501613a13565b16878401526040828101359088015260609687019690910190600101614a09565b600073ffffffffffffffffffffffffffffffffffffffff8086168352606060208401528435614a9081613775565b811660608401526020850135614aa58161397c565b151560808401526040850135614aba81613775565b811660a08401526060850135614acf81613775565b811660c08401526080850135614ae481613775565b1660e0830152614af760a0850185614992565b60e0610100850152614b0e610140850182846149f9565b915050614b1e60c0860186614992565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa085840301610120860152614b548382846149f9565b9350505050826040830152949350505050565b73ffffffffffffffffffffffffffffffffffffffff83168152608081016118e460208301846140b3565b6020815260006118e460208301846145b0565b6020815260006118e46020830184613eff565b600081518084526020808501808196508360051b8101915082860160005b85811015614bff578284038952614bed8483516142bc565b98850198935090840190600101614bd5565b5091979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff85168152836020820152826040820152608060608201526000612fea6080830184614bb7565b600082601f830112614c5857600080fd5b81516020614c68613a8f836139ef565b82815260059290921b84018101918181019086841115614c8757600080fd5b8286015b84811015613d995780518352918301918301614c8b565b60008060408385031215614cb557600080fd5b825167ffffffffffffffff80821115614ccd57600080fd5b614cd986838701614c47565b93506020850151915080821115614cef57600080fd5b50614cfc85828601614c47565b9150509250929050565b73ffffffffffffffffffffffffffffffffffffffff831681526040602082015260006123406040830184614bb7565b82815260406020820152600061234060408301846142bc565b600082516141ff818460208701613edb565b600060208284031215614d7257600080fd5b81516118e48161397c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b8281526040602082015260006123406040830184613eff565b80820281158282048414176106a4576106a4614071565b600181815b80851115614e6457817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115614e4a57614e4a614071565b80851615614e5757918102915b93841c9390800290614e10565b509250929050565b600082614e7b575060016106a4565b81614e88575060006106a4565b8160018114614e9e5760028114614ea857614ec4565b60019150506106a4565b60ff841115614eb957614eb9614071565b50506001821b6106a4565b5060208310610133831016604e8410600b8410161715614ee7575081810a6106a4565b614ef18383614e0b565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115614f2357614f23614071565b029392505050565b60006118e48383614e6c56fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220bcb739c0a05ec535da7d3f611d7d3b691e276cdff9a11d99ed132638740edd3d64736f6c63430008130033496e697469616c697a61626c653a20636f6e7472616374206973206e6f742069", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100c95760003560e01c80639e18968b11610081578063d9d98ce41161005b578063d9d98ce4146101cb578063e23746a3146101de578063e6b62636146101f157600080fd5b80639e18968b14610167578063ac9650d81461017a578063d97b2e481461019a57600080fd5b8063613255ab116100b2578063613255ab1461010b578063702766781461012c5780637a8048df1461013f57600080fd5b80634f266187146100ce5780635cffe9de146100e3575b600080fd5b6100e16100dc366004613759565b610204565b005b6100f66100f1366004613797565b610351565b60405190151581526020015b60405180910390f35b61011e610119366004613836565b610600565b604051908152602001610102565b6100e161013a366004613853565b6106aa565b61015261014d3660046138a0565b610a4c565b60408051928352602083019190915201610102565b6100e1610175366004613da4565b610fb0565b61018d610188366004613e66565b611570565b6040516101029190613f49565b61011e6101a8366004613fc9565b606960209081526000938452604080852082529284528284209052825290205481565b61011e6101d936600461400a565b6106a1565b6100e16101ec366004614036565b611665565b6100e16101ff366004613759565b611779565b61020c611860565b336000908152606960209081526040822090829061022c90850185613836565b73ffffffffffffffffffffffffffffffffffffffff1681526020808201929092526040908101600090812085840135825290925280822054925061027390840135836118d3565b905061027f81836140a0565b3360009081526069602090815260408220919061029e90870187613836565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600085602001358152602001908152602001600020819055507f2538ccc7ad2a119a36f2e65c1e2fc908beef800cd59b5d6680db24de18e7847a338483604051610324939291906140eb565b60405180910390a161034361033c6020850185613836565b33836118eb565b505061034e60018055565b50565b600061035b61199e565b73ffffffffffffffffffffffffffffffffffffffff85166103a8576040517fad1991f500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff86166103f5576040517f6ba9ecd800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6066805473ffffffffffffffffffffffffffffffffffffffff8088167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316179092556065805492891692909116919091179055606784905583156104765761047673ffffffffffffffffffffffffffffffffffffffff86168786611a0f565b6040517f23e30c8b00000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff8816906323e30c8b906104d59033908a908a9087908b908b9060040161416c565b6020604051808303816000875af11580156104f4573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061051891906141b2565b90507f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd9811461057b576040517f5b62c548000000000000000000000000000000000000000000000000000000008152600481018290526024015b60405180910390fd5b606754945084156105b7576065546066546105b19173ffffffffffffffffffffffffffffffffffffffff91821691163088611ae3565b60006067555b606580547fffffffffffffffffffffffff00000000000000000000000000000000000000009081169091556066805490911690556105f361199e565b5060019695505050505050565b600061060a611b47565b6106a1576040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa158015610678573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061069c91906141b2565b6106a4565b60005b92915050565b6106b2611860565b600080806106c360408501856141cb565b6106d1906020810190613836565b73ffffffffffffffffffffffffffffffffffffffff16635511cb676106f960408701876141cb565b610707906020810190614209565b61071460408901896141cb565b610722906040810190614209565b6040805160028082526020820152600081830152606081019091526040518663ffffffff1660e01b815260040161075d9594939291906142f7565b6060604051808303816000875af115801561077c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107a091906143ed565b92509250925060006040518060a001604052803373ffffffffffffffffffffffffffffffffffffffff16815260200160008780604001906107e191906141cb565b6107ef906020810190614209565b60018181106108005761080061443a565b90506020028101906108129190614469565b919091118252506040805160608101825273ffffffffffffffffffffffffffffffffffffffff8089168252878116602083810191909152908716828401528301520161085e87806144ce565b808060200260200160405190810160405280939291908181526020016000905b828210156108aa5761089b60608302860136819003810190614535565b8152602001906001019061087e565b505050505081526020018680602001906108c491906144ce565b808060200260200160405190810160405280939291908181526020016000905b828210156109105761090160608302860136819003810190614535565b815260200190600101906108e4565b50505050508152509050600061092582611b98565b60008181526068602052604090819020600190559091507f73e46afa6205785bdaa1daaf8b6ccc71715ec06b3b4264f5a00fde98671c2fc690339061096c908901896141cb565b61097a906020810190613836565b848460405161098c9493929190614633565b60405180910390a160006109a36060880188614469565b90501115610a3e576109f56109bb6060880188614469565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611be792505050565b7fbea766d03fa1efd3f81cc8634d08320bc62bb0ed9234ac59bbaafa5893fb6b133382610a2560608a018a614469565b604051610a35949392919061467d565b60405180910390a15b505050505061034e60018055565b600080610a57611860565b6000610abe604080516101208101825260006080820181815260a08301829052835160608082018652838252602080830185905282870185905260c086019290925260e0850181905261010085018190529184528301829052928201528181019190915290565b6040805160a08101825260008082526020808301829052835160608082018652838252918101839052808501929092529282015281810182905260808101829052908601355b610b1160a0880188614209565b905084108015610b215750600081115b15610ef857610b3360a0880188614209565b85818110610b4357610b4361443a565b9050602002810190610b5591906146b3565b610b5e906146e7565b805190935091506000610b7083611b98565b600081815260686020526040902054909150610be35782516040805133815273ffffffffffffffffffffffffffffffffffffffff909216602083015281018290527fe721f6888210c87666d3888f93b4139a86ab7757999ce54e268cba700d642a3b9060600160405180910390a1610eec565b610bf06020890189613836565b73ffffffffffffffffffffffffffffffffffffffff168360600151856020015181518110610c2057610c2061443a565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff1614610ccd578260600151846020015181518110610c6157610c6161443a565b6020908102919091018101515190610c7b908a018a613836565b6040517ff902523f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff928316600482015291166024820152604401610572565b610cdd6040890160208a01613836565b73ffffffffffffffffffffffffffffffffffffffff168360800151856040015181518110610d0d57610d0d61443a565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff1614610d6d578260800151846040015181518110610d4e57610d4e61443a565b602002602001015160000151886020016020810190610c7b9190613836565b6000610d888486602001518760400151338960600151611c28565b9050886080013581602001511115610df85783516040805133815273ffffffffffffffffffffffffffffffffffffffff909216602083015281018390527f460c258f27efac20e56c4607a28003d235168e76997ffb7542637d26d45ea6d8906060015b60405180910390a1610eea565b8051600003610e565783516040805133815273ffffffffffffffffffffffffffffffffffffffff909216602083015281018390527f3ba461a0ffd1b6782d4817ae7be605cfb1bbb4fa503c0dd613b8e50f1dcafacd90606001610deb565b8051600090610e669085906118d3565b90506000610e84836020015160018461232a9092919063ffffffff16565b9050610e9082866140a0565b9450610e9c818a614781565b9850610eaa86828486612348565b7f219a030b7ae56e7bea2baab709a4a45dc174a1f85e57730e5cb395bc3296254233888484604051610edf9493929190614794565b60405180910390a150505b505b50600190930192610b04565b610f068160608901356140a0565b95508660400135861015610f5357604080517f45094d8800000000000000000000000000000000000000000000000000000000815290880135600482015260248101879052604401610572565b610f84333087610f6660208c018c613836565b73ffffffffffffffffffffffffffffffffffffffff16929190611ae3565b610f9e610f976040890160208a01613836565b33886118eb565b50505050610fab60018055565b915091565b610fb8611860565b8351855173ffffffffffffffffffffffffffffffffffffffff9182169116036110285784516040517f227e4ce900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401610572565b83606001518360400135815181106110425761104261443a565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff16856080015184602001358151811061107e5761107e61443a565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff16146111435784608001518360200135815181106110bf576110bf61443a565b60200260200101516000015184606001518460400135815181106110e5576110e561443a565b6020908102919091010151516040517ff902523f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff928316600482015291166024820152604401610572565b60608501518051843590811061115b5761115b61443a565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff1684608001518460600135815181106111975761119761443a565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff16146111fc576060850151805184359081106111d6576111d661443a565b60200260200101516000015184608001518460600135815181106110e5576110e561443a565b60006068600061120b88611b98565b8152602001908152602001600020540361128a577fe721f6888210c87666d3888f93b4139a86ab7757999ce54e268cba700d642a3b33866000015161124f88611b98565b6040805173ffffffffffffffffffffffffffffffffffffffff94851681529390921660208401529082015260600160405180910390a1611560565b60006068600061129987611b98565b815260200190815260200160002054036112dd577fe721f6888210c87666d3888f93b4139a86ab7757999ce54e268cba700d642a3b33856000015161124f87611b98565b7fd153812deb929a6e4378f6f8cf61d010470840bf2e736f43fb2275803958bfa23386868660405161131294939291906148c4565b60405180910390a160006113358685600001358660200135886000015186611c28565b9050600061135286866040013587606001358a6000015188611c28565b9050600061136083836127f0565b9050611376888260400151836000015186612348565b61138a878260600151836020015185612348565b6060810151815160009161139d916140a0565b90506000826040015183602001516113b591906140a0565b9050811561145b57336000908152606960209081526040822060808d01518051869492938d01359081106113eb576113eb61443a565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008a60800135815260200190815260200160002060008282546114559190614781565b90915550505b80156114ff5733600090815260696020526040812060808b015180518493919060608d013590811061148f5761148f61443a565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008a60a00135815260200190815260200160002060008282546114f99190614781565b90915550505b5050604080513381528251602080830191909152830151818301529082015160608083019190915282015160808201527f3f20e55919cca701abb2a40ab72542b25ea7eed63a50f979dd2cd3231e5f488d9060a00160405180910390a15050505b61156960018055565b5050505050565b60608167ffffffffffffffff81111561158b5761158b6138d5565b6040519080825280602002602001820160405280156115be57816020015b60608152602001906001900390816115a95790505b50905060005b8281101561165e5761162e308585848181106115e2576115e261443a565b90506020028101906115f49190614469565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506128c592505050565b8282815181106116405761164061443a565b602002602001018190525080806116569061494e565b9150506115c4565b5092915050565b61166d611860565b61167a6020820182613836565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461170c57336116ba6020830183613836565b6040517f4702b91400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff928316600482015291166024820152604401610572565b600061171f61171a83614986565b611b98565b60008181526068602052604080822091909155519091507fe2dd87ac53228b6f23feabcc7301fe64145f23cc3c3ed75e6cd07341ae7f22829061176790339085908590614a62565b60405180910390a15061034e60018055565b611781611860565b7fadc7bd964a04a8a02261d33d2d09c6a7d9f539bc5eab77008e85fc6661ef123133826040516117b2929190614b67565b60405180910390a16117d133306040840135610f666020860186613836565b336000908152606960209081526040808320908401359290916117f690850185613836565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008360200135815260200190815260200160002060008282546118549190614781565b90915550506001805550565b6002600154036118cc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610572565b6002600155565b60008183106118e257816118e4565b825b9392505050565b60665473ffffffffffffffffffffffffffffffffffffffff848116911614801561192f575060655473ffffffffffffffffffffffffffffffffffffffff8381169116145b1561197257600061194b606754836118d390919063ffffffff16565b905061195781836140a0565b9150806067600082825461196b91906140a0565b9091555050505b80156119995761199973ffffffffffffffffffffffffffffffffffffffff84168383611a0f565b505050565b6119a6611b47565b15611a0d576065546066546067546040517f60817cfa00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff93841660048201529290911660248301526044820152606401610572565b565b60405173ffffffffffffffffffffffffffffffffffffffff83166024820152604481018290526119999084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152612a04565b60405173ffffffffffffffffffffffffffffffffffffffff80851660248301528316604482015260648101829052611b419085907f23b872dd0000000000000000000000000000000000000000000000000000000090608401611a61565b50505050565b60655460009073ffffffffffffffffffffffffffffffffffffffff16151580611b87575060665473ffffffffffffffffffffffffffffffffffffffff1615155b80611b93575060675415155b905090565b600081604051602001611bab9190614b91565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052805160209091012092915050565b611bf081612b10565b61034e57806040517f644cc2580000000000000000000000000000000000000000000000000000000081526004016105729190614ba4565b611c5a6040518060a0016040528060008152602001600081526020016060815260200160008152602001606081525090565b6000611c6587611b98565b60408051600480825260a08201909252919250606091600091816020015b6060815260200190600190039081611c8357905050895160408051600381526020810187905273ffffffffffffffffffffffffffffffffffffffff928316818301529189166060830152608082019052909150816001800381518110611ceb57611ceb61443a565b6020026020010181905250611e8089606001518981518110611d0f57611d0f61443a565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff168a606001518a81518110611d4757611d4761443a565b60200260200101516020015160ff168b606001518b81518110611d6c57611d6c61443a565b602002602001015160400151606960008e6000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008e606001518e81518110611dd357611dd361443a565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008e606001518e81518110611e3157611e3161443a565b602002602001015160400151815260200190815260200160002054600060408051600581526020810196909652858101949094526060850192909252608084015260a083015260c08201905290565b81600160030381518110611e9657611e9661443a565b6020026020010181905250611fdc89608001518881518110611eba57611eba61443a565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff168a608001518981518110611ef257611ef261443a565b60200260200101516020015160ff168b608001518a81518110611f1757611f1761443a565b602002602001015160400151606960008e6000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008e608001518d81518110611f7e57611f7e61443a565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008e608001518d81518110611e3157611e3161443a565b81600160040381518110611ff257611ff261443a565b60200260200101819052506120078186612b40565b9150506000886000015173ffffffffffffffffffffffffffffffffffffffff1690506000808a604001516000015173ffffffffffffffffffffffffffffffffffffffff16636715f8258c60400151602001518561206b8f6040015160400151612e70565b886040518563ffffffff1660e01b815260040161208b9493929190614c0c565b600060405180830381865afa1580156120a8573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526120ee9190810190614ca2565b9150915060008260028451038151811061210a5761210a61443a565b6020026020010151905060008360018551038151811061212c5761212c61443a565b602002602001015190506121708d608001518c8151811061214f5761214f61443a565b60200260200101516020015160ff16600284612e999092919063ffffffff16565b91506121cc8d608001518c8151811061218b5761218b61443a565b6020026020010151602001518e606001518e815181106121ad576121ad61443a565b602002602001015160200151600184612f1e909392919063ffffffff16565b90506122b4606960008f6000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008f608001518e8151811061222c5761222c61443a565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008f608001518e8151811061228a5761228a61443a565b602002602001015160400151815260200190815260200160002054836118d390919063ffffffff16565b60408051600281526020810183905280820184905260608101909152909250866002815181106122e6576122e661443a565b60200260200101819052506040518060a001604052808381526020018281526020018781526020018681526020018481525097505050505050505095945050505050565b60006123408484670de0b6b3a764000085612f99565b949350505050565b8281604001516003815181106123605761236061443a565b602002602001015160048151811061237a5761237a61443a565b60200260200101818152505081816040015160048151811061239e5761239e61443a565b60200260200101516004815181106123b8576123b861443a565b602090810291909101015282156124c557835173ffffffffffffffffffffffffffffffffffffffff1660009081526069602052604080822090830151805186939190600390811061240b5761240b61443a565b60200260200101516000815181106124255761242561443a565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083604001516003815181106124805761248061443a565b602002602001015160028151811061249a5761249a61443a565b6020026020010151815260200190815260200160002060008282546124bf9190614781565b90915550505b81156125c757835173ffffffffffffffffffffffffffffffffffffffff1660009081526069602052604080822090830151805185939190600490811061250d5761250d61443a565b60200260200101516000815181106125275761252761443a565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083604001516004815181106125825761258261443a565b602002602001015160028151811061259c5761259c61443a565b6020026020010151815260200190815260200160002060008282546125c191906140a0565b90915550505b7f17a5c0f3785132a57703932032f6863e7920434150aa1dc940e567b440fdce1f3382604001516040516125fc929190614d06565b60405180910390a16080810151511561268d5783604001516020015173ffffffffffffffffffffffffffffffffffffffff1663946aadc6826060015183608001516040518363ffffffff1660e01b815260040161265a929190614d35565b600060405180830381600087803b15801561267457600080fd5b505af1158015612688573d6000803e3d6000fd5b505050505b836020015115611b4157600084604001516000015173ffffffffffffffffffffffffffffffffffffffff16636715f82586604001516020015184606001516126dc896040015160400151612ff4565b86604001516040518563ffffffff1660e01b81526004016127009493929190614c0c565b600060405180830381865afa15801561271d573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526127639190810190614ca2565b9150506000815111156115695784604001516020015173ffffffffffffffffffffffffffffffffffffffff1663946aadc68360600151836040518363ffffffff1660e01b81526004016127b7929190614d35565b600060405180830381600087803b1580156127d157600080fd5b505af11580156127e5573d6000803e3d6000fd5b505050505050505050565b61281b6040518060800160405280600081526020016000815260200160008152602001600081525090565b6128466040518060800160405280600081526020016000815260200160008152602001600081525090565b602083015183516128659161285d9190600161232a565b8551906118d3565b8152602084015184516128869161287e9190600161232a565b8451906118d3565b602080830191909152840151815161289f91600161232a565b6040820152602080840151908201516128b991600161232a565b60608201529392505050565b606073ffffffffffffffffffffffffffffffffffffffff83163b61296b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e747261637400000000000000000000000000000000000000000000000000006064820152608401610572565b6000808473ffffffffffffffffffffffffffffffffffffffff16846040516129939190614d4e565b600060405180830381855af49150503d80600081146129ce576040519150601f19603f3d011682016040523d82523d6000602084013e6129d3565b606091505b50915091506129fb8282604051806060016040528060278152602001614f386027913961301f565b95945050505050565b6000612a66826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166130389092919063ffffffff16565b8051909150156119995780806020019051810190612a849190614d60565b611999576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610572565b6000600882511015612b2457506000919050565b506008015167ffffffffffffffff1667ff0a89c674ee78741490565b60606000825167ffffffffffffffff811115612b5e57612b5e6138d5565b604051908082528060200260200182016040528015612b87578160200160208202803683370190505b509050600080845111612b9b576000612ba1565b83516001015b855160010101905060008167ffffffffffffffff811115612bc457612bc46138d5565b604051908082528060200260200182016040528015612bf757816020015b6060815260200190600190039081612be25790505b5090506000612c1c604080516002815233602082015230818301526060810190915290565b828281518110612c2e57612c2e61443a565b602002602001018190525060005b8751811015612c8c578180600101925050878181518110612c5f57612c5f61443a565b6020026020010151838381518110612c7957612c7961443a565b6020908102919091010152600101612c3c565b50855115612e6657808060010191505083828281518110612caf57612caf61443a565b602002602001018190525060005b8651811015612e6457612d8e878281518110612cdb57612cdb61443a565b602002602001015160000151612d6b612d188a8581518110612cff57612cff61443a565b6020026020010151602001518051602090810291012090565b6040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101829052600090605c01604051602081830303815290604052805190602001209050919050565b898481518110612d7d57612d7d61443a565b602002602001015160400151613047565b612dc7576040517f52bf984800000000000000000000000000000000000000000000000000000000815260048101829052602401610572565b868181518110612dd957612dd961443a565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff16858281518110612e0d57612e0d61443a565b6020026020010181815250508180600101925050868181518110612e3357612e3361443a565b602002602001015160200151838381518110612e5157612e5161443a565b6020908102919091010152600101612cbd565b505b5095945050505050565b6000602082901b77ffffffffffffffffffffffffffffffffffffffff00000000166002176106a4565b60008260121115612ece5760128390036001831615612ec457612ebc8582613214565b9150506118e4565b612ebc8582613262565b6012831115612f17577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee83016002831615612f0d57612ebc858261328e565b612ebc8582613314565b50826118e4565b60008360ff168360ff161115612f5c578383036002831615612f4f57612f47868260ff1661328e565b915050612340565b612f47868260ff16613314565b8260ff168460ff161115612f90578284036001831615612f8357612f47868260ff16613214565b612f47868260ff16613262565b50929392505050565b600080612fa7868686613343565b90506001836002811115612fbd57612fbd614d7d565b148015612fda575060008480612fd557612fd5614dac565b868809115b156129fb57612fea600182614781565b9695505050505050565b600062010000602083901b77ffffffffffffffffffffffffffffffffffffffff0000000016176106a4565b6060831561302e5750816118e4565b6118e48383613410565b60606123408484600085613454565b6000806000613056858561356d565b9092509050600081600481111561306f5761306f614d7d565b1480156130a757508573ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16145b156130b7576001925050506118e4565b6000808773ffffffffffffffffffffffffffffffffffffffff16631626ba7e60e01b88886040516024016130ec929190614ddb565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516131759190614d4e565b600060405180830381855afa9150503d80600081146131b0576040519150601f19603f3d011682016040523d82523d6000602084013e6131b5565b606091505b50915091508180156131c8575080516020145b8015613208575080517f1626ba7e000000000000000000000000000000000000000000000000000000009061320690830160209081019084016141b2565b145b98975050505050505050565b6000604e821061323857821561322b57600161322e565b60005b60ff1690506106a4565b600a82900a80848161324c5761324c614dac565b049150808202841461165e575060010192915050565b6000604e8210156132855781600a0a838161327f5761327f614dac565b046118e4565b50600092915050565b6000604e82106132ce5782156132c4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6132c7565b60005b90506106a4565b50600a81900a82810290838183816132e8576132e8614dac565b041461165e577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff612340565b600a81900a6133238184614df4565b9050604e82106106a45782156132855761333e82600a614f2b565b6118e4565b600080807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8587098587029250828110838203039150508060000361339b5783828161339157613391614dac565b04925050506118e4565b8084116133a757600080fd5b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b8151156134205781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105729190614ba4565b6060824710156134e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610572565b6000808673ffffffffffffffffffffffffffffffffffffffff16858760405161350f9190614d4e565b60006040518083038185875af1925050503d806000811461354c576040519150601f19603f3d011682016040523d82523d6000602084013e613551565b606091505b5091509150613562878383876135b2565b979650505050505050565b60008082516041036135a35760208301516040840151606085015160001a61359787828585613652565b945094505050506135ab565b506000905060025b9250929050565b606083156136485782516000036136415773ffffffffffffffffffffffffffffffffffffffff85163b613641576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610572565b5081612340565b6123408383613410565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156136895750600090506003613738565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156136dd573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811661373157600060019250925050613738565b9150600090505b94509492505050565b60006060828403121561375357600080fd5b50919050565b60006060828403121561376b57600080fd5b6118e48383613741565b73ffffffffffffffffffffffffffffffffffffffff8116811461034e57600080fd5b6000806000806000608086880312156137af57600080fd5b85356137ba81613775565b945060208601356137ca81613775565b935060408601359250606086013567ffffffffffffffff808211156137ee57600080fd5b818801915088601f83011261380257600080fd5b81358181111561381157600080fd5b89602082850101111561382357600080fd5b9699959850939650602001949392505050565b60006020828403121561384857600080fd5b81356118e481613775565b60006020828403121561386557600080fd5b813567ffffffffffffffff81111561387c57600080fd5b8201608081850312156118e457600080fd5b600060c0828403121561375357600080fd5b6000602082840312156138b257600080fd5b813567ffffffffffffffff8111156138c957600080fd5b6123408482850161388e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516060810167ffffffffffffffff81118282101715613927576139276138d5565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613974576139746138d5565b604052919050565b801515811461034e57600080fd5b80356139958161397c565b919050565b6000606082840312156139ac57600080fd5b6139b4613904565b905081356139c181613775565b815260208201356139d181613775565b602082015260408201356139e481613775565b604082015292915050565b600067ffffffffffffffff821115613a0957613a096138d5565b5060051b60200190565b803560ff8116811461399557600080fd5b600060608284031215613a3657600080fd5b613a3e613904565b90508135613a4b81613775565b8152613a5960208301613a13565b60208201526040820135604082015292915050565b600082601f830112613a7f57600080fd5b81356020613a94613a8f836139ef565b61392d565b82815260609283028501820192828201919087851115613ab357600080fd5b8387015b85811015613ad657613ac98982613a24565b8452928401928101613ab7565b5090979650505050505050565b600060e08284031215613af557600080fd5b60405160a0810167ffffffffffffffff8282108183111715613b1957613b196138d5565b8160405282935084359150613b2d82613775565b818352613b3c6020860161398a565b6020840152613b4e866040870161399a565b604084015260a0850135915080821115613b6757600080fd5b613b7386838701613a6e565b606084015260c0850135915080821115613b8c57600080fd5b50613b9985828601613a6e565b6080830152505092915050565b600082601f830112613bb757600080fd5b813567ffffffffffffffff811115613bd157613bd16138d5565b613c0260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161392d565b818152846020838601011115613c1757600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f830112613c4557600080fd5b81356020613c55613a8f836139ef565b82815260059290921b84018101918181019086841115613c7457600080fd5b8286015b84811015613d9957803567ffffffffffffffff80821115613c9857600080fd5b908801906060828b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0011215613ccf5760008081fd5b613cd7613904565b86830135613ce481613775565b815260408381013583811115613cfa5760008081fd5b8401603f81018d13613d0c5760008081fd5b88810135613d1c613a8f826139ef565b81815260059190911b82018301908a8101908f831115613d3c5760008081fd5b928401925b82841015613d5a5783358252928b0192908b0190613d41565b858c0152505050606084013583811115613d745760008081fd5b613d828d8a83880101613ba6565b918301919091525085525050918301918301613c78565b509695505050505050565b60008060008060006101408688031215613dbd57600080fd5b853567ffffffffffffffff80821115613dd557600080fd5b613de189838a01613ae3565b96506020880135915080821115613df757600080fd5b613e0389838a01613ae3565b9550613e128960408a0161388e565b9450610100880135915080821115613e2957600080fd5b613e3589838a01613c34565b9350610120880135915080821115613e4c57600080fd5b50613e5988828901613c34565b9150509295509295909350565b60008060208385031215613e7957600080fd5b823567ffffffffffffffff80821115613e9157600080fd5b818501915085601f830112613ea557600080fd5b813581811115613eb457600080fd5b8660208260051b8501011115613ec957600080fd5b60209290920196919550909350505050565b60005b83811015613ef6578181015183820152602001613ede565b50506000910152565b60008151808452613f17816020860160208601613edb565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015613fbc577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0888603018452613faa858351613eff565b94509285019290850190600101613f70565b5092979650505050505050565b600080600060608486031215613fde57600080fd5b8335613fe981613775565b92506020840135613ff981613775565b929592945050506040919091013590565b6000806040838503121561401d57600080fd5b823561402881613775565b946020939093013593505050565b60006020828403121561404857600080fd5b813567ffffffffffffffff81111561405f57600080fd5b820160e081850312156118e457600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156106a4576106a4614071565b80356140be81613775565b73ffffffffffffffffffffffffffffffffffffffff16825260208181013590830152604090810135910152565b73ffffffffffffffffffffffffffffffffffffffff8416815260a0810161411560208301856140b3565b826080830152949350505050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b600073ffffffffffffffffffffffffffffffffffffffff808916835280881660208401525085604083015284606083015260a0608083015261320860a083018486614123565b6000602082840312156141c457600080fd5b5051919050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa18336030181126141ff57600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261423e57600080fd5b83018035915067ffffffffffffffff82111561425957600080fd5b6020019150600581901b36038213156135ab57600080fd5b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8311156142a357600080fd5b8260051b80836020870137939093016020019392505050565b600081518084526020808501945080840160005b838110156142ec578151875295820195908201906001016142d0565b509495945050505050565b6060808252810185905260006080600587901b8301810190830188835b898110156143c3577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8086850301835281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18c360301811261437557600080fd5b8b01602081810191359067ffffffffffffffff82111561439457600080fd5b8136038313156143a357600080fd5b6143ae878385614123565b96509485019493909301925050600101614314565b50505082810360208401526143d9818688614271565b9050828103604084015261320881856142bc565b60008060006060848603121561440257600080fd5b835161440d81613775565b602085015190935061441e81613775565b604085015190925061442f81613775565b809150509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261449e57600080fd5b83018035915067ffffffffffffffff8211156144b957600080fd5b6020019150368190038213156135ab57600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261450357600080fd5b83018035915067ffffffffffffffff82111561451e57600080fd5b60200191506060810236038213156135ab57600080fd5b60006060828403121561454757600080fd5b6118e48383613a24565b600081518084526020808501945080840160005b838110156142ec578151805173ffffffffffffffffffffffffffffffffffffffff1688528381015160ff16848901526040908101519088015260609096019590820190600101614565565b600073ffffffffffffffffffffffffffffffffffffffff80835116845260208301511515602085015260408301518181511660408601528160208201511660608601528160408201511660808601525050606082015160e060a085015261461a60e0850182614551565b9050608083015184820360c08601526129fb8282614551565b600073ffffffffffffffffffffffffffffffffffffffff80871683528086166020840152506080604083015261466c60808301856145b0565b905082606083015295945050505050565b73ffffffffffffffffffffffffffffffffffffffff85168152836020820152606060408201526000612fea606083018486614123565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff818336030181126141ff57600080fd5b6000608082360312156146f957600080fd5b6040516080810167ffffffffffffffff828210818311171561471d5761471d6138d5565b81604052843591508082111561473257600080fd5b61473e36838701613ae3565b83526020850135602084015260408501356040840152606085013591508082111561476857600080fd5b5061477536828601613c34565b60608301525092915050565b808201808211156106a4576106a4614071565b600073ffffffffffffffffffffffffffffffffffffffff8087168352602060808185015286516080808601526147ce6101008601826145b0565b90508188015160a086015260408089015160c08701526060808a01517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808885030160e08901528381518086528686019150868160051b870101878401935060005b828110156148a7577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe088830301845284518a815116835289810151878b85015261487b888501826142bc565b91890151848303858b01529190506148938183613eff565b968b0196958b01959350505060010161482f565b50948a019b909b5250509095019590955250919695505050505050565b600061012073ffffffffffffffffffffffffffffffffffffffff871683528060208401526148f4818401876145b0565b9050828103604084015261490881866145b0565b9150508235606083015260208301356080830152604083013560a0830152606083013560c0830152608083013560e083015260a083013561010083015295945050505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361497f5761497f614071565b5060010190565b60006106a43683613ae3565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126149c757600080fd5b830160208101925035905067ffffffffffffffff8111156149e757600080fd5b6060810236038213156135ab57600080fd5b8183526000602080850194508260005b858110156142ec578135614a1c81613775565b73ffffffffffffffffffffffffffffffffffffffff16875260ff614a41838501613a13565b16878401526040828101359088015260609687019690910190600101614a09565b600073ffffffffffffffffffffffffffffffffffffffff8086168352606060208401528435614a9081613775565b811660608401526020850135614aa58161397c565b151560808401526040850135614aba81613775565b811660a08401526060850135614acf81613775565b811660c08401526080850135614ae481613775565b1660e0830152614af760a0850185614992565b60e0610100850152614b0e610140850182846149f9565b915050614b1e60c0860186614992565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa085840301610120860152614b548382846149f9565b9350505050826040830152949350505050565b73ffffffffffffffffffffffffffffffffffffffff83168152608081016118e460208301846140b3565b6020815260006118e460208301846145b0565b6020815260006118e46020830184613eff565b600081518084526020808501808196508360051b8101915082860160005b85811015614bff578284038952614bed8483516142bc565b98850198935090840190600101614bd5565b5091979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff85168152836020820152826040820152608060608201526000612fea6080830184614bb7565b600082601f830112614c5857600080fd5b81516020614c68613a8f836139ef565b82815260059290921b84018101918181019086841115614c8757600080fd5b8286015b84811015613d995780518352918301918301614c8b565b60008060408385031215614cb557600080fd5b825167ffffffffffffffff80821115614ccd57600080fd5b614cd986838701614c47565b93506020850151915080821115614cef57600080fd5b50614cfc85828601614c47565b9150509250929050565b73ffffffffffffffffffffffffffffffffffffffff831681526040602082015260006123406040830184614bb7565b82815260406020820152600061234060408301846142bc565b600082516141ff818460208701613edb565b600060208284031215614d7257600080fd5b81516118e48161397c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b8281526040602082015260006123406040830184613eff565b80820281158282048414176106a4576106a4614071565b600181815b80851115614e6457817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115614e4a57614e4a614071565b80851615614e5757918102915b93841c9390800290614e10565b509250929050565b600082614e7b575060016106a4565b81614e88575060006106a4565b8160018114614e9e5760028114614ea857614ec4565b60019150506106a4565b60ff841115614eb957614eb9614071565b50506001821b6106a4565b5060208310610133831016604e8410600b8410161715614ee7575081810a6106a4565b614ef18383614e0b565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115614f2357614f23614071565b029392505050565b60006118e48383614e6c56fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220bcb739c0a05ec535da7d3f611d7d3b691e276cdff9a11d99ed132638740edd3d64736f6c63430008130033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/src/index.js b/src/index.js index 4ffb5b5f..65a56c76 100644 --- a/src/index.js +++ b/src/index.js @@ -4,6 +4,7 @@ const axios = require("axios"); const ethers = require("ethers"); const CONFIG = require("../config.json"); const { DefaultQuery } = require("./query"); +let { abi: orderbookAbi } = require("./abis/OrderBook.json"); const { abi: erc20Abi } = require("./abis/ERC20Upgradeable.json"); let { abi: interpreterAbi } = require("./abis/IInterpreterV1.json"); let { abi: arbAbi } = require("./abis/ZeroExOrderBookFlashBorrower.json"); @@ -239,8 +240,9 @@ exports.query = async(subgraphUrl) => { * @param {ethers.Wallet} wallet - The ethers wallet with private key instance * @param {string} orderbookAddress - The Rain Orderbook contract address deployed on the network * @param {string} arbAddress - The Rain Arb contract address deployed on the network - * @param {string} interpreterAbiPath - (optional) The path to IInterpreter contract ABI, default is ABI in './src/abis' folder * @param {string} arbAbiPath - (optional) The path to Arb contract ABI, default is ABI in './src/abis' folder + * @param {string} interpreterAbiPath - (optional) The path to IInterpreter contract ABI, default is ABI in './src/abis' folder + * @param {string} orderbookAbiPath - (optional) The path to Orderbook contract ABI, default is ABI in './src/abis' folder * @returns The configuration object */ exports.getConfig = async( @@ -248,7 +250,8 @@ exports.getConfig = async( orderbookAddress, arbAddress, arbAbiPath = "", - interpreterAbiPath = "" + interpreterAbiPath = "", + orderbookAbiPath = "", ) => { const AddressPattern = /^0x[a-fA-F0-9]{40}$/; const chainId = (await wallet.getChainId()); @@ -259,6 +262,7 @@ exports.getConfig = async( config.arbAddress = arbAddress; if (interpreterAbiPath) config.interpreterAbi = interpreterAbiPath; if (arbAbiPath) config.arbAbi = arbAbiPath; + if (orderbookAbiPath) config.orderbookAbi = orderbookAbiPath; return config; }; @@ -280,6 +284,7 @@ exports.clear = async(signer, config, queryResults, slippage = "0.01", prioritiz const nativeToken = config.nativeToken.address; const intAbiPath = config.interpreterAbi; const arbAbiPath = config.arbAbi; + const orderbookAbiPath = config.orderbookAbi; // set the api key in headers if (config.apiKey) HEADERS.headers["0x-api-key"] = config.apiKey; @@ -291,10 +296,16 @@ exports.clear = async(signer, config, queryResults, slippage = "0.01", prioritiz if (arbAbiPath) arbAbi = JSON.parse( fs.readFileSync(path.resolve(__dirname, arbAbiPath)).toString() )?.abi; + if (orderbookAbiPath) orderbookAbi = JSON.parse( + fs.readFileSync(path.resolve(__dirname, orderbookAbiPath)).toString() + )?.abi; // instantiating arb contract const arb = new ethers.Contract(arbAddress, arbAbi, signer); + // instantiating orderbook contract + const orderbook = new ethers.Contract(orderbookAddress, orderbookAbi, signer); + // orderbook as signer used for eval const obAsSigner = new ethers.VoidSigner( orderbookAddress, @@ -329,7 +340,11 @@ exports.clear = async(signer, config, queryResults, slippage = "0.01", prioritiz const _output = order.validOutputs[j]; const _outputBalance = ethers.utils.parseUnits( ethers.utils.formatUnits( - _output.tokenVault.balance, + await orderbook.vaultBalance( + order.owner.id, + _output.token.id, + _output.vault.id.split("-")[0] + ), _output.token.decimals ) ); @@ -456,7 +471,7 @@ exports.clear = async(signer, config, queryResults, slippage = "0.01", prioritiz if (bundledOrders[i].takeOrders.length) { try { console.log( - `------------------------- Trying To Clear For ${ + `------------------------- Trying To Clear ${ bundledOrders[i].buyTokenSymbol }/${ bundledOrders[i].sellTokenSymbol @@ -465,257 +480,301 @@ exports.clear = async(signer, config, queryResults, slippage = "0.01", prioritiz ); console.log(`Buy Token Address: ${bundledOrders[i].buyToken}`); console.log(`Sell Token Address: ${bundledOrders[i].sellToken}`, "\n"); - console.log(">>> Getting current price for this token pair...", "\n"); - - let cumulativeAmount = ethers.constants.Zero; - bundledOrders[i].takeOrders.forEach(v => { - cumulativeAmount = cumulativeAmount.add(v.quoteAmount); - }); - const price = (await axios.get( - `${ - api - }swap/v1/price?buyToken=${ - bundledOrders[i].buyToken - }&sellToken=${ - bundledOrders[i].sellToken - }&sellAmount=${ - cumulativeAmount.div( - "1" + "0".repeat(18 - bundledOrders[i].sellTokenDecimals) - ).div(2).toString() - }&skipValidation=false`, - HEADERS - ))?.data?.price; - hits++; - const currentPrice = ethers.utils.parseUnits(price); - - console.log(`Quote amount: ${ethers.utils.formatUnits( - cumulativeAmount.div( - "1" + "0".repeat(18 - bundledOrders[i].sellTokenDecimals) - ).div(2), - bundledOrders[i].sellTokenDecimals - )} ${bundledOrders[i].sellTokenSymbol}`); - console.log(`Current market price of this token pair: ${price}`); - console.log("Current ratio of the orders in this token pair:"); - bundledOrders[i].takeOrders.forEach(v => { - console.log(ethers.utils.formatEther(v.ratio)); - }); - console.log( - "\n>>> Filtering the bundled orders of this token pair with lower ratio than current market price...", - "\n" + console.log(">>> Updating vault balances...", "\n"); + const newBalances = await Promise.allSettled( + bundledOrders[i].takeOrders.map(async(v) => { + return ethers.utils.parseUnits( + ethers.utils.formatUnits( + await orderbook.vaultBalance( + v.takeOrder.order.owner, + bundledOrders[i].sellToken, + v.takeOrder.order.validOutputs[ + v.takeOrder.outputIOIndex + ].vaultId + ), + bundledOrders[i].sellTokenDecimals + ) + ); + }) ); - + newBalances.forEach((v, j) => { + if (v.status === "fulfilled") { + if (v.value.isZero()) { + bundledOrders[i].takeOrders[j].quoteAmount = ethers.BigNumber.from("0"); + } + else { + if (v.value.lt(bundledOrders[i].takeOrders[j].quoteAmount)) { + bundledOrders[i].takeOrders[j].quoteAmount = v.value; + } + } + } + else { + console.log(`Could not get vault balance for order ${ + bundledOrders[i].takeOrders[j].id + } due to:`); + console.log(v.reason); + bundledOrders[i].takeOrders[j].quoteAmount = ethers.BigNumber.from("0"); + } + }); bundledOrders[i].takeOrders = bundledOrders[i].takeOrders.filter( - v => currentPrice.gte(v.ratio) + v => !v.quoteAmount.isZero() ); if (bundledOrders[i].takeOrders.length) { + console.log(">>> Getting current price for this token pair...", "\n"); - cumulativeAmount = ethers.constants.Zero; + let cumulativeAmount = ethers.constants.Zero; bundledOrders[i].takeOrders.forEach(v => { cumulativeAmount = cumulativeAmount.add(v.quoteAmount); }); - - const bundledQuoteAmount = cumulativeAmount.div( - "1" + "0".repeat(18 - bundledOrders[i].sellTokenDecimals) - ); - - console.log(">>> Getting quote for this token pair...", "\n"); - const response = await axios.get( + const price = (await axios.get( `${ api - }swap/v1/quote?buyToken=${ + }swap/v1/price?buyToken=${ bundledOrders[i].buyToken }&sellToken=${ bundledOrders[i].sellToken }&sellAmount=${ - bundledQuoteAmount.toString() - }&slippagePercentage=${ - slippage - }`, + cumulativeAmount.div( + "1" + "0".repeat(18 - bundledOrders[i].sellTokenDecimals) + ).div(2).toString() + }&skipValidation=false`, HEADERS - ); + ))?.data?.price; hits++; + const currentPrice = ethers.utils.parseUnits(price); - const txQuote = response?.data; - if (txQuote) { - console.log("the full quote that will be submitted is:" + "\n" + JSON.stringify(txQuote, null, 2), "\n"); - const takeOrdersConfigStruct = { - output: bundledOrders[i].buyToken, - input: bundledOrders[i].sellToken, - // max and min input should be exactly the same as quoted sell amount - // this makes sure the cleared order amount will exactly match the 0x quote - minimumInput: bundledQuoteAmount, - maximumInput: bundledQuoteAmount, - maximumIORatio: ethers.constants.MaxUint256, - orders: bundledOrders[i].takeOrders.map(v => v.takeOrder), - }; - - // submit the transaction - try { - console.log(">>> Estimating the profit for this token pair...", "\n"); - const gasLimit = await arb.estimateGas.arb( - takeOrdersConfigStruct, - // set to zero because only profitable transactions are submitted - 0, - txQuote.allowanceTarget, - txQuote.data, - { gasPrice: txQuote.gasPrice } - ); - const maxEstimatedProfit = estimateProfit( - txQuote, - bundledOrders[i], - gasLimit.mul(txQuote.gasPrice), - "0.1" - ).div( - "1" + "0".repeat(18 - bundledOrders[i].buyTokenDecimals) - ); - console.log(`Max Estimated Profit: ${ - ethers.utils.formatUnits( - maxEstimatedProfit, - bundledOrders[i].buyTokenDecimals - ) - } ${bundledOrders[i].buyTokenSymbol}`, "\n"); - - if (!maxEstimatedProfit.isNegative()) { - console.log(">>> Trying to submit the transaction for this token pair...", "\n"); - const tx = await arb.arb( + console.log(`Quote amount: ${ethers.utils.formatUnits( + cumulativeAmount.div( + "1" + "0".repeat(18 - bundledOrders[i].sellTokenDecimals) + ).div(2), + bundledOrders[i].sellTokenDecimals + )} ${bundledOrders[i].sellTokenSymbol}`); + console.log(`Current market price of this token pair: ${price}`); + console.log("Current ratio of the orders in this token pair:"); + bundledOrders[i].takeOrders.forEach(v => { + console.log(ethers.utils.formatEther(v.ratio)); + }); + + console.log( + "\n>>> Filtering the bundled orders of this token pair with lower ratio than current market price...", + "\n" + ); + + bundledOrders[i].takeOrders = bundledOrders[i].takeOrders.filter( + v => currentPrice.gte(v.ratio) + ); + + if (bundledOrders[i].takeOrders.length) { + + cumulativeAmount = ethers.constants.Zero; + bundledOrders[i].takeOrders.forEach(v => { + cumulativeAmount = cumulativeAmount.add(v.quoteAmount); + }); + + const bundledQuoteAmount = cumulativeAmount.div( + "1" + "0".repeat(18 - bundledOrders[i].sellTokenDecimals) + ); + + console.log(">>> Getting quote for this token pair...", "\n"); + const response = await axios.get( + `${ + api + }swap/v1/quote?buyToken=${ + bundledOrders[i].buyToken + }&sellToken=${ + bundledOrders[i].sellToken + }&sellAmount=${ + bundledQuoteAmount.toString() + }&slippagePercentage=${ + slippage + }`, + HEADERS + ); + hits++; + + const txQuote = response?.data; + if (txQuote) { + console.log("the full quote that will be submitted is:" + "\n" + JSON.stringify(txQuote, null, 2), "\n"); + const takeOrdersConfigStruct = { + output: bundledOrders[i].buyToken, + input: bundledOrders[i].sellToken, + // max and min input should be exactly the same as quoted sell amount + // this makes sure the cleared order amount will exactly match the 0x quote + minimumInput: bundledQuoteAmount, + maximumInput: bundledQuoteAmount, + maximumIORatio: ethers.constants.MaxUint256, + orders: bundledOrders[i].takeOrders.map(v => v.takeOrder), + }; + + // submit the transaction + try { + console.log(">>> Estimating the profit for this token pair...", "\n"); + const gasLimit = await arb.estimateGas.arb( takeOrdersConfigStruct, // set to zero because only profitable transactions are submitted 0, txQuote.allowanceTarget, txQuote.data, - { gasPrice: txQuote.gasPrice, gasLimit } + { gasPrice: txQuote.gasPrice } ); - console.log(ETHERSCAN_TX_PAGE[chainId] + tx.hash, "\n"); - console.log( - ">>> Transaction submitted successfully to the network, waiting for transaction to mine...", - "\n" + const maxEstimatedProfit = estimateProfit( + txQuote, + bundledOrders[i], + gasLimit.mul(txQuote.gasPrice), + "0.1" + ).div( + "1" + "0".repeat(18 - bundledOrders[i].buyTokenDecimals) ); - - try { - const receipt = await tx.wait(); - const income = getIncome(signer, receipt); - const gasCost = ethers.BigNumber.from( - txQuote.gasPrice - ).mul(receipt.gasUsed); - const clearActualPrice = getActualPrice( - receipt, - orderbookAddress, - arbAddress, - bundledQuoteAmount.mul( - "1" + "0".repeat( - 18 - bundledOrders[i].sellTokenDecimals - ) - ), - bundledOrders[i].sellTokenDecimals, + console.log(`Max Estimated Profit: ${ + ethers.utils.formatUnits( + maxEstimatedProfit, bundledOrders[i].buyTokenDecimals + ) + } ${bundledOrders[i].buyTokenSymbol}`, "\n"); + + if (!maxEstimatedProfit.isNegative()) { + console.log(">>> Trying to submit the transaction for this token pair...", "\n"); + const tx = await arb.arb( + takeOrdersConfigStruct, + // set to zero because only profitable transactions are submitted + 0, + txQuote.allowanceTarget, + txQuote.data, + { gasPrice: txQuote.gasPrice, gasLimit } + ); + console.log(ETHERSCAN_TX_PAGE[chainId] + tx.hash, "\n"); + console.log( + ">>> Transaction submitted successfully to the network, waiting for transaction to mine...", + "\n" ); - const netProfit = income - ? income.sub( - ethers.utils.parseUnits( - txQuote.buyTokenToEthRate - ).mul( - gasCost - ).div( + + try { + const receipt = await tx.wait(); + const income = getIncome(signer, receipt); + const gasCost = ethers.BigNumber.from( + txQuote.gasPrice + ).mul(receipt.gasUsed); + const clearActualPrice = getActualPrice( + receipt, + orderbookAddress, + arbAddress, + bundledQuoteAmount.mul( "1" + "0".repeat( - 36 - bundledOrders[i].buyTokenDecimals + 18 - bundledOrders[i].sellTokenDecimals + ) + ), + bundledOrders[i].sellTokenDecimals, + bundledOrders[i].buyTokenDecimals + ); + const netProfit = income + ? income.sub( + ethers.utils.parseUnits( + txQuote.buyTokenToEthRate + ).mul( + gasCost + ).div( + "1" + "0".repeat( + 36 - bundledOrders[i].buyTokenDecimals + ) ) ) - ) - : undefined; - console.log(`${bundledOrders[i].takeOrders.length} orders cleared successfully!`); - console.log(`Clear Quote Price: ${txQuote.price}`); - console.log(`Clear Actual Price: ${clearActualPrice}`); - console.log(`Clear Amount: ${ - ethers.utils.formatUnits( - bundledQuoteAmount, - bundledOrders[i].sellTokenDecimals - ) - } ${bundledOrders[i].sellTokenSymbol}`); - console.log(`Consumed Gas: ${ethers.utils.formatEther(gasCost)} ETH`, "\n"); - if (income) { - console.log(`Raw Income: ${ethers.utils.formatUnits( + : undefined; + console.log(`${bundledOrders[i].takeOrders.length} orders cleared successfully!`); + console.log(`Clear Quote Price: ${txQuote.price}`); + console.log(`Clear Actual Price: ${clearActualPrice}`); + console.log(`Clear Amount: ${ + ethers.utils.formatUnits( + bundledQuoteAmount, + bundledOrders[i].sellTokenDecimals + ) + } ${bundledOrders[i].sellTokenSymbol}`); + console.log(`Consumed Gas: ${ethers.utils.formatEther(gasCost)} ETH`, "\n"); + if (income) { + console.log(`Raw Income: ${ethers.utils.formatUnits( + income, + bundledOrders[i].buyTokenDecimals + )} ${bundledOrders[i].buyTokenSymbol}`); + console.log(`Net Profit: ${ethers.utils.formatUnits( + netProfit, + bundledOrders[i].buyTokenDecimals + )} ${bundledOrders[i].buyTokenSymbol}`, "\n"); + } + + report.push({ + transactionHash: receipt.transactionHash, + tokenPair: + bundledOrders[i].buyTokenSymbol + + "/" + + bundledOrders[i].sellTokenSymbol, + buyToken: bundledOrders[i].buyToken, + buyTokenDecimals: bundledOrders[i].buyTokenDecimals, + sellToken: bundledOrders[i].sellToken, + sellTokenDecimals: bundledOrders[i].sellTokenDecimals, + clearedAmount: bundledQuoteAmount.toString(), + clearPrice: txQuote.price, + clearGuaranteedPrice: txQuote.guaranteedPrice, + clearActualPrice, + maxEstimatedProfit, + gasUsed: receipt.gasUsed, + gasCost, income, - bundledOrders[i].buyTokenDecimals - )} ${bundledOrders[i].buyTokenSymbol}`); - console.log(`Net Profit: ${ethers.utils.formatUnits( netProfit, - bundledOrders[i].buyTokenDecimals - )} ${bundledOrders[i].buyTokenSymbol}`, "\n"); + clearedOrders: bundledOrders[i].takeOrders, + }); + + // // filter out upcoming take orders matching current cleared order + // if (i + 1 < bundledOrders.length) console.log( + // ">>> Updating upcoming bundled orders...", + // "\n" + // ); + // for (let j = i + 1; j < bundledOrders.length; j++) { + // bundledOrders[j].takeOrders = bundledOrders[j].takeOrders + // .filter(v => { + // for (const item of bundledOrders[i].takeOrders) { + // if ( + // item.id === v.id || + // ( + // bundledOrders[j].sellToken === + // bundledOrders[i].sellToken && + + // v.takeOrder.order.owner === + // item.takeOrder.order.owner && + + // v.takeOrder.order.validOutputs[ + // v.takeOrder.outputIOIndex + // ].vaultId === + // item.takeOrder.order.validOutputs[ + // v.takeOrder.outputIOIndex + // ].vaultId + // ) + // ) return false; + // return true; + // } + // }); + // } } - - report.push({ - transactionHash: receipt.transactionHash, - tokenPair: - bundledOrders[i].buyTokenSymbol + - "/" + - bundledOrders[i].sellTokenSymbol, - buyToken: bundledOrders[i].buyToken, - buyTokenDecimals: bundledOrders[i].buyTokenDecimals, - sellToken: bundledOrders[i].sellToken, - sellTokenDecimals: bundledOrders[i].sellTokenDecimals, - clearedAmount: bundledQuoteAmount.toString(), - clearPrice: txQuote.price, - clearGuaranteedPrice: txQuote.guaranteedPrice, - clearActualPrice, - maxEstimatedProfit, - gasUsed: receipt.gasUsed, - gasCost, - income, - netProfit, - clearedOrders: bundledOrders[i].takeOrders, - }); - - // filter out upcoming take orders matching current cleared order - if (i + 1 < bundledOrders.length) console.log( - ">>> Updating upcoming bundled orders...", - "\n" - ); - for (let j = i + 1; j < bundledOrders.length; j++) { - bundledOrders[j].takeOrders = bundledOrders[j].takeOrders - .filter(v => { - for (const item of bundledOrders[i].takeOrders) { - if ( - item.id === v.id || - ( - bundledOrders[j].sellToken === - bundledOrders[i].sellToken && - - v.takeOrder.order.owner === - item.takeOrder.order.owner && - - v.takeOrder.order.validOutputs[ - v.takeOrder.outputIOIndex - ].vaultId === - item.takeOrder.order.validOutputs[ - v.takeOrder.outputIOIndex - ].vaultId - ) - ) return false; - return true; - } - }); + catch (error) { + console.log(">>> Transaction execution failed due to:"); + console.log(error, "\n"); } } - catch (error) { - console.log(">>> Transaction execution failed due to:"); - console.log(error, "\n"); - } + else console.log(">>> Skipping because estimated negative profit for this token pair", "\n"); + } + catch (error) { + console.log(">>> Transaction failed due to:"); + console.log(error, "\n"); } - else console.log(">>> Skipping because estimated negative profit for this token pair", "\n"); - } - catch (error) { - console.log(">>> Transaction failed due to:"); - console.log(error, "\n"); } + else console.log("Failed to get quote from 0x", "\n"); } - else console.log("Failed to get quote from 0x", "\n"); + else console.log( + "All orders of this token pair have higher ratio than current market price, checking next token pair...", + "\n" + ); } - else console.log( - "All orders of this token pair have higher ratio than current market price, checking next token pair...", - "\n" - ); + else console.log("All orders of this token pair have empty vault balance, skipping...", "\n"); } catch (error) { console.log(">>> Failed to get quote from 0x due to:", "\n"); diff --git a/src/query.js b/src/query.js index cf95087d..ce01f532 100644 --- a/src/query.js +++ b/src/query.js @@ -20,9 +20,6 @@ exports.DefaultQuery = `{ decimals symbol } - tokenVault { - balance - } vault { id } @@ -34,9 +31,6 @@ exports.DefaultQuery = `{ decimals symbol } - tokenVault { - balance - } vault { id }