Skip to content

Commit

Permalink
Merge pull request #290 from rainlanguage/2024-12-29-dotrain-tasks
Browse files Browse the repository at this point in the history
dotrain tasks
  • Loading branch information
rouzwelt authored Jan 3, 2025
2 parents 4828f68 + 47b85c0 commit 90e9da2
Show file tree
Hide file tree
Showing 28 changed files with 828 additions and 317 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ The app requires these arguments (all arguments can be set in env variables alte
- `--arb-address`, Address of the deployed arb contract, Will override the 'ARB_ADDRESS' in env variables
- `--bot-min-balance` The minimum gas token balance the bot wallet must have. Will override the 'BOT_MIN_BALANCE' in env variables
- `-s` or `--subgraph`, Subgraph URL(s) to read orders details from, can be used in combination with --orders, Will override the 'SUBGRAPH' in env variables
- `--dispair`, Address of dispair (ExpressionDeployer contract) to use for tasks, Will override the 'DISPAIR' in env variables

Other optional arguments are:
- `--generic-arb-address`, Address of the deployed generic arb contract to perform inter-orderbook clears, Will override the 'GENERIC_ARB_ADDRESS' in env variables
Expand Down Expand Up @@ -275,6 +276,9 @@ QUOTE_GAS=

# Only clear orders through RP4, excludes intra and inter orderbook clears
RP_ONLY="true"

# Address of dispair (ExpressionDeployer contract) to use for tasks
DISPAIR="address"
```
If both env variables and CLI argument are set, the CLI arguments will be prioritized and override the env variables.

Expand Down
3 changes: 3 additions & 0 deletions example.env
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ QUOTE_GAS=
# Only clear orders through RP4, excludes intra and inter orderbook clears
RP_ONLY="true"

# Address of dispair (ExpressionDeployer contract) to use for tasks
DISPAIR="address"


# test rpcs vars
TEST_POLYGON_RPC=
Expand Down
36 changes: 36 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"@opentelemetry/resources": "^1.22.0",
"@opentelemetry/sdk-trace-base": "^1.22.0",
"@opentelemetry/semantic-conventions": "^1.22.0",
"@rainlanguage/dotrain": "^6.0.1-alpha.21",
"@rainlanguage/orderbook": "^0.0.1-alpha.6",
"axios": "^1.3.4",
"commander": "^11.0.0",
Expand Down
10 changes: 10 additions & 0 deletions src/abis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ export const routeProcessor3Abi = [
`function processRoute(address tokenIn, uint256 amountIn, address tokenOut, uint256 amountOutMin ,address to, bytes memory route) external payable returns (uint256 amountOut)`,
] as const;

/**
* ExpressionDeployerNPE2 minimal ABI
*/
export const deployerAbi = [
"function parse2(bytes memory data) external view returns (bytes memory bytecode)",
"function iStore() external view returns (address)",
"function iInterpreter() external view returns (address)",
"function iParser() external view returns (address)",
] as const;

/**
* Minimal ABI for Arb contract
*/
Expand Down
14 changes: 14 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { config } from "dotenv";
import { isAddress } from "viem";
import { getGasPrice } from "./gas";
import { Command } from "commander";
import { getMetaInfo } from "./config";
Expand Down Expand Up @@ -75,6 +76,7 @@ const ENV_OPTIONS = {
txGas: process?.env?.TX_GAS,
quoteGas: process?.env?.QUOTE_GAS,
route: process?.env?.ROUTE,
dispair: process?.env?.DISPAIR,
rpOnly: process?.env?.RP_ONLY?.toLowerCase() === "true" ? true : false,
ownerProfile: process?.env?.OWNER_PROFILE
? Array.from(process?.env?.OWNER_PROFILE.matchAll(/[^,\s]+/g)).map((v) => v[0])
Expand Down Expand Up @@ -120,6 +122,10 @@ const getOptions = async (argv: any, version?: string) => {
"--generic-arb-address <address>",
"Address of the deployed generic arb contract to perform inter-orderbook clears, Will override the 'GENERIC_ARB_ADDRESS' in env variables",
)
.option(
"--dispair <address>",
"Address of dispair (ExpressionDeployer contract) to use for tasks, Will override the 'DISPAIR' in env variables",
)
.option(
"-l, --lps <string>",
"List of liquidity providers (dex) to use by the router as one quoted string seperated by a comma for each, example: 'SushiSwapV2,UniswapV3', Will override the 'LIQUIDITY_PROVIDERS' in env variables, if unset will use all available liquidty providers",
Expand Down Expand Up @@ -260,6 +266,7 @@ const getOptions = async (argv: any, version?: string) => {
cmdOptions.route = cmdOptions.route || getEnv(ENV_OPTIONS.route);
cmdOptions.publicRpc = cmdOptions.publicRpc || getEnv(ENV_OPTIONS.publicRpc);
cmdOptions.rpOnly = cmdOptions.rpOnly || getEnv(ENV_OPTIONS.rpOnly);
cmdOptions.dispair = cmdOptions.dispair || getEnv(ENV_OPTIONS.dispair);
if (cmdOptions.ownerProfile) {
const profiles: Record<string, number> = {};
cmdOptions.ownerProfile.forEach((v: string) => {
Expand Down Expand Up @@ -469,6 +476,13 @@ export async function startup(argv: any, version?: string, tracer?: Tracer, ctx?
throw "invalid txGas value, must be an integer greater than zero optionally with appended percentage sign to apply as percentage to original gas";
}
}
if (options.dispair) {
if (typeof options.dispair !== "string" || !isAddress(options.dispair, { strict: false })) {
throw "expected dispair (ExpressionDeployer contract) address";
}
} else {
throw "undefined dispair address";
}
if (options.quoteGas) {
try {
options.quoteGas = BigInt(options.quoteGas);
Expand Down
80 changes: 1 addition & 79 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BigNumber } from "ethers";
import { getSgOrderbooks } from "./sg";
import { sendTransaction } from "./tx";
import { WNATIVE } from "sushi/currency";
import { ChainId, ChainKey } from "sushi/chain";
import { DataFetcher, LiquidityProviders } from "sushi/router";
Expand Down Expand Up @@ -30,7 +30,6 @@ import {
ROUTE_PROCESSOR_3_1_ADDRESS,
ROUTE_PROCESSOR_3_2_ADDRESS,
} from "sushi/config";
import { sendTransaction } from "./tx";

/**
* Get the chain config for a given chain id
Expand Down Expand Up @@ -221,83 +220,6 @@ export async function getDataFetcher(
}
}

/**
* Get the bounty check ensure task bytecode
* @param inputToEthPrice - Input token to Eth price
* @param outputToEthPrice - Output token to Eth price
* @param minimumExcepted - Minimum expected amount
*/
export function getBountyEnsureBytecode(
inputToEthPrice: BigNumber,
outputToEthPrice: BigNumber,
minimumExcepted: BigNumber,
sender: string,
): string {
const inputPrice = inputToEthPrice.toHexString().substring(2).padStart(64, "0");
const outputPrice = outputToEthPrice.toHexString().substring(2).padStart(64, "0");
const minimum = minimumExcepted.toHexString().substring(2).padStart(64, "0");
const msgSender = sender.substring(2).padStart(64, "0").toLowerCase();
// rainlang bytecode:
// :ensure(equal-to(sender context<0 0>()) \"unknown sender\"),
// :ensure(
// greater-than-or-equal-to(
// add(
// mul(inputToEthPrice context<1 0>())
// mul(outputToEthPrice context<1 1>())
// )
// minimumExcepted
// )
// \"minimum sender output\"
// );
return `0x0000000000000000000000000000000000000000000000000000000000000006${msgSender}8e756e6b6e6f776e2073656e6465720000000000000000000000000000000000${inputPrice}${outputPrice}${minimum}956d696e696d756d2073656e646572206f7574707574000000000000000000000000000000000000000000000000000000000000000000000000000000000047010000100500000110000103100000011000001e1200001d020000011000050110000403100101011000033d12000003100001011000023d1200002b120000211200001d020000`;
}

/**
* Get the bounty check ensure task bytecode for clear2 withdraw
* @param botAddress - Bot wallet address
* @param inputToken - Input token address
* @param outputToken - Output token address
* @param orgInputBalance - Input token original balance
* @param orgOutputBalance - Output token original balance
* @param inputToEthPrice - Input token to Eth price
* @param outputToEthPrice - Output token to Eth price
* @param minimumExcepted - Minimum expected amount
*/
export function getWithdrawEnsureBytecode(
botAddress: string,
inputToken: string,
outputToken: string,
orgInputBalance: BigNumber,
orgOutputBalance: BigNumber,
inputToEthPrice: BigNumber,
outputToEthPrice: BigNumber,
minimumExcepted: BigNumber,
sender: string,
): string {
const bot = botAddress.substring(2).padStart(64, "0");
const input = inputToken.substring(2).padStart(64, "0");
const output = outputToken.substring(2).padStart(64, "0");
const inputBalance = orgInputBalance.toHexString().substring(2).padStart(64, "0");
const outputBalance = orgOutputBalance.toHexString().substring(2).padStart(64, "0");
const inputPrice = inputToEthPrice.toHexString().substring(2).padStart(64, "0");
const outputPrice = outputToEthPrice.toHexString().substring(2).padStart(64, "0");
const minimum = minimumExcepted.toHexString().substring(2).padStart(64, "0");
const msgSender = sender.substring(2).padStart(64, "0").toLowerCase();
// rainlang bytecode:
// :ensure(equal-to(sender context<0 0>()) \"unknown sender\"),
// :ensure(
// greater-than-or-equal-to(
// add(
// mul(sub(erc20-balance-of(inputToken botAddress) originalInputBalance) inputToEthPrice)
// mul(sub(erc20-balance-of(outputToken botAddress) originalOutputBalance) outputToEthPrice)
// )
// minimumSenderOutput
// )
// \"minimumSenderOutput\"
// );
return `0x000000000000000000000000000000000000000000000000000000000000000b${msgSender}8e756e6b6e6f776e2073656e6465720000000000000000000000000000000000${input}${bot}${inputBalance}${inputPrice}${output}${outputBalance}${outputPrice}${minimum}936d696e696d756d53656e6465724f75747075740000000000000000000000000000000000000000000000000000000000000000000000000000000000000067010000180700000110000103100000011000001e1200001d0200000110000a011000090110000801100007011000030110000611120000471200003d1200000110000501100004011000030110000211120000471200003d1200002b120000211200001d020000`;
}

/**
* List of L2 chains that require SEPARATE L1 gas actions.
* other L2 chains that dont require separate L1 gas actions
Expand Down
31 changes: 30 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import axios from "axios";
import { ethers } from "ethers";
import { ChainId } from "sushi";
import { versions } from "process";
import { PublicClient } from "viem";
import { parseAbi, PublicClient } from "viem";
import { processLps } from "./utils";
import { initAccounts } from "./account";
import { processOrders } from "./processOrders";
Expand All @@ -26,6 +26,7 @@ import {
onFetchResponse,
createViemClient,
} from "./config";
import { deployerAbi } from "./abis";

/**
* Get the order details from a source, i.e array of subgraphs and/or a local json file
Expand Down Expand Up @@ -196,6 +197,29 @@ export async function getConfig(
options.publicRpc,
);

const interpreter = await (async () => {
try {
return await viemClient.readContract({
address: options.dispair as `0x${string}`,
abi: parseAbi(deployerAbi),
functionName: "iInterpreter",
});
} catch {
throw "failed to get dispair interpreter address";
}
})();
const store = await (async () => {
try {
return await viemClient.readContract({
address: options.dispair as `0x${string}`,
abi: parseAbi(deployerAbi),
functionName: "iStore",
});
} catch {
throw "failed to get dispair store address";
}
})();

config.rpc = rpcUrls;
config.arbAddress = arbAddress;
config.genericArbAddress = options.genericArbAddress;
Expand All @@ -219,6 +243,11 @@ export async function getConfig(
config.txGas = options.txGas;
config.quoteGas = options.quoteGas;
config.rpOnly = options.rpOnly;
config.dispair = {
interpreter,
store,
deployer: options.dispair,
};

// init accounts
const { mainAccount, accounts } = await initAccounts(walletKey, config, options, tracer, ctx);
Expand Down
48 changes: 30 additions & 18 deletions src/modes/interOrderbook.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { orderbookAbi } from "../abis";
import { estimateGasCost } from "../gas";
import { BaseError, PublicClient } from "viem";
import { getBountyEnsureBytecode } from "../config";
import { BigNumber, Contract, ethers } from "ethers";
import { containsNodeError, errorSnapshot } from "../error";
import { getBountyEnsureRainlang, parseRainlang } from "../task";
import { BotConfig, BundledOrders, ViemClient, DryrunResult, SpanAttrs } from "../types";
import {
ONE18,
Expand Down Expand Up @@ -88,16 +88,20 @@ export async function dryrun({

const task = {
evaluable: {
interpreter: orderPairObject.takeOrders[0].takeOrder.order.evaluable.interpreter,
store: orderPairObject.takeOrders[0].takeOrder.order.evaluable.store,
interpreter: config.dispair.interpreter,
store: config.dispair.store,
bytecode:
config.gasCoveragePercentage === "0"
? "0x"
: getBountyEnsureBytecode(
ethers.utils.parseUnits(inputToEthPrice),
ethers.utils.parseUnits(outputToEthPrice),
ethers.constants.Zero,
signer.account.address,
: await parseRainlang(
await getBountyEnsureRainlang(
ethers.utils.parseUnits(inputToEthPrice),
ethers.utils.parseUnits(outputToEthPrice),
ethers.constants.Zero,
signer.account.address,
),
config.viemClient,
config.dispair,
),
},
signedContext: [],
Expand Down Expand Up @@ -149,11 +153,15 @@ export async function dryrun({
// sender output which is already called above
if (config.gasCoveragePercentage !== "0") {
const headroom = (Number(config.gasCoveragePercentage) * 1.03).toFixed();
task.evaluable.bytecode = getBountyEnsureBytecode(
ethers.utils.parseUnits(inputToEthPrice),
ethers.utils.parseUnits(outputToEthPrice),
gasCost.mul(headroom).div("100"),
signer.account.address,
task.evaluable.bytecode = await parseRainlang(
await getBountyEnsureRainlang(
ethers.utils.parseUnits(inputToEthPrice),
ethers.utils.parseUnits(outputToEthPrice),
gasCost.mul(headroom).div("100"),
signer.account.address,
),
config.viemClient,
config.dispair,
);
rawtx.data = arb.interface.encodeFunctionData("arb3", [
orderPairObject.orderbook,
Expand All @@ -169,11 +177,15 @@ export async function dryrun({
.div(100);
rawtx.gas = gasLimit.toBigInt();
gasCost = gasLimit.mul(gasPrice).add(estimation.l1Cost);
task.evaluable.bytecode = getBountyEnsureBytecode(
ethers.utils.parseUnits(inputToEthPrice),
ethers.utils.parseUnits(outputToEthPrice),
gasCost.mul(config.gasCoveragePercentage).div("100"),
signer.account.address,
task.evaluable.bytecode = await parseRainlang(
await getBountyEnsureRainlang(
ethers.utils.parseUnits(inputToEthPrice),
ethers.utils.parseUnits(outputToEthPrice),
gasCost.mul(config.gasCoveragePercentage).div("100"),
signer.account.address,
),
config.viemClient,
config.dispair,
);
rawtx.data = arb.interface.encodeFunctionData("arb3", [
orderPairObject.orderbook,
Expand Down
Loading

0 comments on commit 90e9da2

Please sign in to comment.