Skip to content

Instantly share code, notes, and snippets.

@zengzengzenghuy
Last active February 13, 2025 13:47
Show Gist options
  • Select an option

  • Save zengzengzenghuy/c53aec325e8e701b4f902a029c90290b to your computer and use it in GitHub Desktop.

Select an option

Save zengzengzenghuy/c53aec325e8e701b4f902a029c90290b to your computer and use it in GitHub Desktop.
Rebalance logic for intent solver
import {
createClient,
getClient,
convertViemChainToRelayChain,
MAINNET_RELAY_API,
adaptViemWallet,
} from "@reservoir0x/relay-sdk";
import { createWalletClient, http, parseUnits, publicActions } from "viem";
import { arbitrum, base } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
const SOLVER_ADDRESS = "0xab6395320249174313457713e46d01528dd959ab";
const PRIVATE_KEY = "";
const USDC_ADDRESSES = {
arbitrum: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
base: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
};
const CHECK_INTERVAL = 30000; // 5 seconds
const THRESHOLD = parseUnits("200", 6); // 200 USDC with 6 decimals
const REFILLED_BALANCE = parseUnits("500", 6);
// Viem clients for both chains
const account = privateKeyToAccount(PRIVATE_KEY);
const clients = {
arbitrum: createWalletClient({
chain: arbitrum,
transport: http(),
account,
}).extend(publicActions),
base: createWalletClient({
chain: base,
transport: http(),
account,
}).extend(publicActions),
};
// Initialize Relay SDK client
createClient({
baseApiUrl: "https://api.reservoir.tools", // MAINNET_RELAY_API
chains: [
convertViemChainToRelayChain(arbitrum),
convertViemChainToRelayChain(base),
],
});
process.on("unhandledRejection", (reason, promise) => {
// TODO: fix APIError
console.error("Unhandled Rejection at:", promise, "reason:", reason);
});
async function getUsdcBalance(chain, address) {
const client = clients[chain];
const usdcAddress = USDC_ADDRESSES[chain];
return await client.readContract({
address: usdcAddress,
abi: [
{
type: "function",
name: "balanceOf",
inputs: [{ name: "account", type: "address" }],
outputs: [{ type: "uint256" }],
stateMutability: "view",
},
],
functionName: "balanceOf",
args: [address],
});
}
function convertRelayChainToViemChain(chainName) {
switch (chainName) {
case "Arbitrum One":
return "arbitrum";
case "Base":
return "base";
default:
return "arbitrum";
}
}
async function rebalance(fromChain, toChain, amount) {
try {
// const quote = await getClient()?.actions.getQuote({
// chainId: fromChain.id,
// toChainId: toChain.id,
// currency: USDC_ADDRESSES[convertRelayChainToViemChain(fromChain.name)],
// toCurrency: USDC_ADDRESSES[convertRelayChainToViemChain(toChain.name)],
// amount: amount.toString(),
// });
const options = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: `{"user": "${SOLVER_ADDRESS}","recipient":"${SOLVER_ADDRESS}","originChainId":${
fromChain.id
},"destinationChainId":${toChain.id},"originCurrency":"${
USDC_ADDRESSES[convertRelayChainToViemChain(fromChain.name)]
}","destinationCurrency": "${
USDC_ADDRESSES[convertRelayChainToViemChain(toChain.name)]
}","amount": "${amount.toString()}","tradeType":"EXACT_INPUT", "refundTo":"${SOLVER_ADDRESS}"}`,
};
const response = await fetch("https://api.relay.link/quote", options);
if (!response.ok) {
throw new Error(`Failed to get quote: ${response.statusText}`);
}
const responseData = await response.json();
console.log(responseData);
console.log("Executing swap...");
await getClient().actions.execute({
quote: responseData,
wallet: adaptViemWallet(
clients[convertRelayChainToViemChain(fromChain.name)]
),
onProgress: ({ currentStep, txHashes }) => {
try {
console.log(`Step: ${currentStep?.action}, TxHashes: ${txHashes}`);
} catch (err) {
console.error(`Progress callback error: ${err.message}`);
}
},
});
} catch (error) {
console.error(`Rebalance failed: ${error.message}`);
}
}
async function monitorBalances() {
try {
const [arbitrumBalance, baseBalance] = await Promise.all([
getUsdcBalance("arbitrum", SOLVER_ADDRESS),
getUsdcBalance("base", SOLVER_ADDRESS),
]);
console.log(
`Balances - Arbitrum: ${arbitrumBalance / BigInt(1e6)} USDC, Base: ${
baseBalance / BigInt(1e6)
} USDC`
);
const balances = { arbitrum: arbitrumBalance, base: baseBalance };
for (const chain of ["arbitrum", "base"]) {
if (balances[chain] < THRESHOLD) {
console.log(
`Solver ${SOLVER_ADDRESS} balance on ${chain} is ${
balances[chain] / BigInt(1e6)
} USDC, below threshold ${THRESHOLD / BigInt(1e6)}, rebalancing...`
);
const otherChain = chain === "arbitrum" ? "base" : "arbitrum";
const deficit = THRESHOLD - balances[chain];
console.log("Deficit ", deficit);
if (balances[otherChain] - deficit > THRESHOLD) {
await rebalance(
clients[otherChain].chain,
clients[chain].chain,
deficit
);
} else {
console.warn(
`Not enough funds on ${otherChain} to rebalance ${chain}`
);
}
}
}
} catch (error) {
console.error(`Monitor error: ${error.message}`);
}
}
setInterval(async () => {
try {
await monitorBalances();
} catch (error) {
console.error(`Interval Monitor Error: ${error.message}`);
}
}, CHECK_INTERVAL);
console.log("Monitoring USDC balances...");
@zengzengzenghuy
Copy link
Author

yarn add @reservoir0x/relay-sdk viem

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment