@parifi/synthetix-sdk-ts
Version:
A Typescript SDK for interactions with the Synthetix protocol
1,302 lines (1,286 loc) • 216 kB
JavaScript
// src/utils/common.ts
import { formatEther, maxUint128, parseEther } from "viem";
import { mainnet, base, optimism, arbitrum, baseSepolia, arbitrumSepolia } from "viem/chains";
import { randomBytes } from "crypto";
// src/constants/partner.ts
var DEFAULT_TRACKING_CODE = "0x5052460000000000000000000000000000000000000000000000000000000000";
var DEFAULT_REFERRER = "0x32A74B2cE1a0783A182f607D29207e571a1cDD4D";
// src/constants/protocol.ts
var DEFAULT_DECIMALS = 18;
var PUBLIC_PYTH_ENDPOINT = "https://hermes.pyth.network";
var DEFAULT_PYTH_TIMEOUT = 15e3;
// src/constants/common.ts
import { zeroAddress } from "viem";
var ZERO_ADDRESS = zeroAddress;
var SIG_ORACLE_DATA_REQUIRED = "0xcf2cabdf";
var SIG_FEE_REQUIRED = "0x0e7186fb";
var SIG_ERRORS = "0x0b42fd17";
var MAX_ERC7412_RETRIES = 80;
var DISABLED_MARKETS = {
84532: [3, 6300],
8453: [6300]
};
var publicRpcEndpoints = {
8453: "https://base.llamarpc.com",
84532: "https://sepolia.base.org",
42161: "https://arbitrum.llamarpc.com",
421614: "https://sepolia-rollup.arbitrum.io/rpc"
};
var DEFAULT_LOGGER_LEVEL = 4;
var CUSTOM_DECIMALS = {
[421614 /* ARBITUM_SEPOLIA */]: {
2: 6,
USDC: 6
},
[42161 /* ARBITRUM */]: {
2: 6,
USDC: 6
},
[84532 /* BASE_SEPOLIA */]: {
1: 6,
USDC: 6
},
[8453 /* BASE */]: {
1: 6,
USDC: 6
}
};
// src/constants/pythPriceIds.ts
var SYMBOL_TO_PYTH_FEED = /* @__PURE__ */ new Map([
// Markets
["BTC", "0xc9d8b075a5c69303365ae23633d4e085199bf5c520a3b90fed1322a0342ffc33"],
["SOL", "0xef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d"],
["WIF", "0x4ca4beeca86f0d164160323817a4e42b10010a724c2217c6ee41b54cd4cc61fc"],
["ETH", "0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace"],
["AAVE", "0x2b9ab1e972a281585084148ba1389800799bd4be63b957507db1349314e47445"],
["ADA", "0x2a01deaec9e51a579277b34b122399984d0bbf57e2458a7e42fecd2829867a0d"],
["ARB", "0x3fa4252848f9f0a1480be62745a4629d9eb1322aebab8a791e344b3b9c1adcf5"],
["AVAX", "0x93da3352f9f1d105fdfe4971cfa80e9dd777bfc5d0f683ebb6e1294b92137bb7"],
["BCH", "0x3dd2b63686a450ec7290df3a1e0b583c0481f651351edfa7636f39aed55cf8a3"],
["BNB", "0x2f95862b045670cd22bee3114c39763a4a08beeb663b145d283c31d7d1101c4f"],
["CRV", "0xa19d04ac696c7a6616d291c7e5d1377cc8be437c327b75adb5dc1bad745fcae8"],
["DOGE", "0xdcef50dd0a4cd2dcc17e45df1676dcb336a11a61c69df7a0299b0150c672d25c"],
["DYDX", "0x6489800bb8974169adfe35937bf6736507097d13c190d760c557108c7e93a81b"],
["GMX", "0xb962539d0fcb272a494d65ea56f94851c2bcf8823935da05bd628916e2e9edbf"],
["LINK", "0x8ac0c70fff57e9aefdf5edf44b51d62c2d433653cbb2cf5cc06bb115af04d221"],
["LTC", "0x6e3f3fa8253588df9326580180233eb791e03b443a3ba7a1d892e73874e19a54"],
["MKR", "0x9375299e31c0deb9c6bc378e6329aab44cb48ec655552a70d4b9050346a30378"],
["NEAR", "0xc415de8d2eba7db216527dff4b60e8f3a5311c740dadb233e13e12547e226750"],
["OP", "0x385f64d993f7b77d8182ed5003d97c60aa3361f3cecfe711544d2d59165e9bdf"],
["ORDI", "0x193c739db502aadcef37c2589738b1e37bdb257d58cf1ab3c7ebc8e6df4e3ec0"],
["PEPE", "0xd69731a2e74ac1ce884fc3890f7ee324b6deb66147055249568869ed700882e4"],
["POL", "0xffd11c5a1cfd42f80afb2df4d9f264c15f956d68153335374ec10722edd70472"],
["PYTH", "0x0bbf28e9a841a1cc788f6a361b17ca072d0ea3098a1e5df1c3922d06719579ff"],
["RUNE", "0x5fcf71143bb70d41af4fa9aa1287e2efd3c5911cee59f909f915c9f61baacb1e"],
["SHIB", "0xf0d57deca57b3da2fe63a493f4c25925fdfd8edf834b20f93e1f84dbd1504d4a"],
["STX", "0xec7a775f46379b5e943c3526b1c8d54cd49749176b0b98e02dde68d1bd335c17"],
["TIA", "0x09f7c1d7dfbb7df2b8fe3d3d87ee94a2259d212da4f30c1f0540d066dfa44723"],
["UNI", "0x78d185a741d07edb3412b09008b7c5cfb9bbbd7d568bf00ba737b456ba171501"],
["XLM", "0xb7a8eba68a997cd0210c2e1e4ee811ad2d174b3611c22d9ebf16f4cb7e9ba850"],
["XRP", "0xec5d399846a9209f3fe5881d70aae9268c94339ff9817e8d18ff19fa05eea1c8"],
["ATOM", "0xb00b60f88b03a6a625a8d1c048c3f66653edf217439983d037e7222c4e612819"],
["BLUR", "0x856aac602516addee497edf6f50d39e8c95ae5fb0da1ed434a8c2ab9c3e877e9"],
["BOME", "0x30e4780570973e438fdb3f1b7ad22618b2fc7333b65c7853a7ca144c39052f7a"],
["BONK", "0x72b021217ca3fe68922a19aaf990109cb9d84e9ad004b4d2025ad6f529314419"],
["COMP", "0x4a8e42861cabc5ecb50996f92e7cfa2bce3fd0a2423b0c44c9b423fb2bd25478"],
["DOT", "0xca3eed9b267293f6595901c734c7525ce8ef49adafe8284606ceb307afa2ca5b"],
["EIGEN", "0xc65db025687356496e8653d0d6608eec64ce2d96e2e28c530e574f0e4f712380"],
["ENA", "0xb7910ba7322db020416fcac28b48c01212fd9cc8fbcbaf7d30477ed8605f6bd4"],
["EOS", "0x06ade621dbc31ed0fc9255caaab984a468abe84164fb2ccc76f02a4636d97e31"],
["ETC", "0x7f5cc8d963fc5b3d2ae41fe5685ada89fd4f14b435f8050f28c7fd409f40c2d8"],
["ETHBTC", "0xc96458d393fe9deb7a7d63a0ac41e2898a67a7750dbd166673279e06c868df0a"],
["ETHFI", "0xb27578a9654246cb0a2950842b92330e9ace141c52b63829cc72d5c45a5a595a"],
["FIL", "0x150ac9b959aee0051e4091f0ef5216d941f590e1c5e7f91cf7635b5c11628c0e"],
["FLOW", "0x2fb245b9a84554a0f15aa123cbb5f64cd263b59e9a87d80148cbffab50c69f30"],
["FTM", "0x5c6c0d2386e3352356c3ab84434fafb5ea067ac2678a38a338c4a69ddc4bdb0c"],
["FXS", "0x735f591e4fed988cd38df74d8fcedecf2fe8d9111664e0fd500db9aa78b316b1"],
["GALA", "0x0781209c28fda797616212b7f94d77af3a01f3e94a5d421760aef020cf2bcb51"],
["GRT", "0x4d1f8dae0d96236fb98e8f47471a366ec3b1732b47041781934ca3a9bb2f35e7"],
["ICP", "0xc9907d786c5821547777780a1e4f89484f3417cb14dd244f2b0a34ea7a554d67"],
["IMX", "0x941320a8989414874de5aa2fc340a75d5ed91fdff1613dd55f83844d52ea63a2"],
["INJ", "0x7a5bc1d2b56ad029048cd63964b3ad2776eadf812edc1a43a31406cb54bff592"],
["IO", "0x82595d1509b770fa52681e260af4dda9752b87316d7c048535d8ead3fa856eb1"],
["JTO", "0xb43660a5f790c69354b0729a5ef9d50d68f1df92107540210b9cccba1f947cc2"],
["JUP", "0x0a0408d619e9380abad35060f9192039ed5042fa6f82301d0e48bb52be830996"],
["LDO", "0xc63e2a7f37a04e5e614c07238bedb25dcc38927fba8fe890597a593c0b2fa4ad"],
["MEME", "0xcd2cee36951a571e035db0dfad138e6ecdb06b517cc3373cd7db5d3609b7927c"],
["MEW", "0x514aed52ca5294177f20187ae883cec4a018619772ddce41efcc36a6448f5d5d"],
["NOT", "0x75ec6f04d4bded6afdc1440689be4402dd1e23d2ff2c21e081871eb2739ceb36"],
["PENDLE", "0x9a4df90b25497f66b1afb012467e316e801ca3d839456db028892fe8c70c8016"],
["PEOPLE", "0xb7fe919d83815ca6074c82a3286b1cd6ffb7d3136e323cd2b1ef706cfc7e5945"],
["POPCAT", "0xb9312a7ee50e189ef045aa3c7842e099b061bd9bdc99ac645956c3b660dc8cce"],
["RENDER", "0x3d4a2bd9535be6ce8059d75eadeba507b043257321aa544717c56fa19b49e35d"],
["SATS", "0x40440d18fb5ad809e2825ce7dfc035cfa57135c13062a04addafe0c7f54425e0"],
["SEI", "0x53614f1cb0c031d4af66c04cb9c756234adad0e1cee85303795091499a4084eb"],
["SNX", "0x39d020f60982ed892abbcd4a06a276a9f9b7bfbce003204c110b6e488f502da3"],
["STRK", "0x6a182399ff70ccf3e06024898942028204125a819e519a335ffa4579e66cd870"],
["SUI", "0x23d7315113f5b1d3ba7a83604c44b94d79f4fd69af77f804fc7f920a6dc65744"],
["SUSHI", "0x26e4f737fde0263a9eea10ae63ac36dcedab2aaf629261a994e1eeb6ee0afe53"],
["TAO", "0x410f41de235f2db824e562ea7ab2d3d3d4ff048316c61d629c0b93f58584e1af"],
["TON", "0x8963217838ab4cf5cadc172203c1f0b763fbaa45f346d8ee50ba994bbcac3026"],
["TRX", "0x67aed5a24fdad045475e7195c98a98aea119c763f272d4523f5bac93a4f33c2b"],
["W", "0xeff7446475e218517566ea99e72a4abec2e1bd8498b43b7d8331e29dcb059389"],
["WLD", "0xd6835ad1f773de4a378115eb6824bd0c0e42d84d1c84d9750e853fb6b6c7794a"],
["YFI", "0x425f4b198ab2504936886c1e93511bb6720fbcf2045a4f3c0723bb213846022f"],
["ZRO", "0x3bd860bea28bf982fa06bcf358118064bb114086cc03993bd76197eaab0b8018"],
["ALGO", "0xfa17ceaf30d19ba51112fdcc750cc83454776f47fb0112e4af07f15f4bb1ebc0"],
["APT", "0x03ae4db29ed4ae33d323568895aa00337e658e348b37509f5372ae51f0af00d5"],
["ARKM", "0x7677dd124dee46cfcd46ff03cf405fb0ed94b1f49efbea3444aadbda939a7ad3"],
["AXL", "0x60144b1d5c9e9851732ad1d9760e3485ef80be39b984f6bf60f82b28a2b7f126"],
["AXS", "0xb7e3904c08ddd9c0c10c6d207d390fd19e87eb6aab96304f571ed94caebdefa0"],
["BAL", "0x07ad7b4a7662d19a6bc675f6b467172d2f3947fa653ca97555a9b20236406628"],
["GOAT", "0xf7731dc812590214d3eb4343bfb13d1b4cfa9b1d4e020644b5d5d8e07d60c66c"],
["MOODENG", "0xffff73128917a90950cd0473fd2551d7cd274fd5a6cc45641881bbcc6ee73417"],
["SAFE", "0x7b3576858506a94fad3a9cc55e32934f0c3931150fe3a3c7b83558dbae5b8e38"],
["PNUT", "0x116da895807f81f6b5c5f01b109376e7f6834dc8b51365ab7cdfa66634340e54"],
["CAT", "0xdf7b724fae4b9ecfd6081effb1ea3bb3b5c250c3641cf903b4d8fb10f4f452df"],
["DEGEN", "0x9c93e4a22c56885af427ac4277437e756e7ec403fbc892f975d497383bb33560"],
["SLERF", "0x1a483c4a63876d286991ac0d6e090298db42e88c3826b6e0cff89daca498eed5"],
["AERO", "0x9db37f4d5654aad3e37e2e14ffd8d53265fb3026d1d8f91146539eebaa2ef45f"],
["CHILLGUY", "0xd98869edbb4a0d2803dc1054405bceb1ddc546bfc9a3d0e31bb0e0448e6181e1"],
["MORPHO", "0x5b2a4c542d4a74dd11784079ef337c0403685e3114ba0d9909b5c7a7e06fdc42"],
["SOLETH", "0xde87506dabfadbef89af2d5d796ebae80ddaea240fc7667aa808fce3629cd8fb"],
// Collaterals
["USDC", "0xeaa020c61cc479712813461ce153894a96a6c00b21ed0cfc2798d1f9a9e9c94a"],
["USDE", "0x6ec879b1e9963de5ee97e9c8710b742d6228252a5e2ca12d4ae81d7fe5ee8c5d"],
["DAI", "0xb0948a5e5313200c632b51bb5ca32f6de0d36e9950a942d19751e833f70dabfd"]
]);
// src/contracts/addreses/zap.ts
import { zeroAddress as zeroAddress2 } from "viem";
var ZAP_BY_CHAIN = {
[8453 /* BASE */]: "0x459Bbb4231f0a606DC514BacD5712A5922CBe6c8",
[84532 /* BASE_SEPOLIA */]: zeroAddress2,
[42161 /* ARBITRUM */]: "0x9ec181B2E69fB36C50031F0c87Bc0749b766A9f4",
[421614 /* ARBITUM_SEPOLIA */]: "0xb569ed692206a1d73996088ae646333b1d59d9c5"
};
var SYNTHETIX_ZAP = {
[84532 /* BASE_SEPOLIA */]: "0xA6Fab50eB36F3eB2118eE2f4C12C968F8608bbc9",
[8453 /* BASE */]: "0xA6Fab50eB36F3eB2118eE2f4C12C968F8608bbc9"
};
// src/utils/common.ts
function getPublicRpcEndpoint(chainId) {
return publicRpcEndpoints[chainId];
}
function getChain(chainId) {
const chains = [mainnet, base, optimism, arbitrum, baseSepolia, arbitrumSepolia];
for (const chain of Object.values(chains)) {
if (chain.id === chainId) {
return chain;
}
}
throw new Error(`Chain with id ${chainId} not found`);
}
function convertWeiToEther(amountInWei) {
if (amountInWei == void 0) {
throw new Error("Invalid amount received during conversion: undefined");
}
if (typeof amountInWei == "bigint") {
return Number(formatEther(amountInWei));
} else if (typeof amountInWei == "string") {
return Number(formatEther(BigInt(amountInWei)));
} else {
throw new Error("Expected string or bigint for conversion");
}
}
function convertEtherToWei(amount) {
if (amount == void 0) {
throw new Error("Invalid amount received during conversion: undefined");
}
if (typeof amount == "number") {
return parseEther(amount.toString());
} else if (typeof amount == "string") {
return parseEther(amount);
} else {
throw new Error("Expected string or bigint for conversion");
}
}
function sleep(seconds) {
return new Promise((resolve) => setTimeout(resolve, seconds * 1e3));
}
function generateRandomAccountId() {
const maxUint128Half = maxUint128 / BigInt(2);
const buffer = randomBytes(8);
const randomAccountId = BigInt("0x" + buffer.toString("hex"));
if (randomAccountId > maxUint128Half) {
throw new Error("Account ID greater than Maxuint128");
}
return randomAccountId;
}
var batchArray = (arr, batchSize) => {
return arr.reduce((acc, _, i) => i % batchSize ? acc : [...acc, arr.slice(i, i + batchSize)], []);
};
var fetcher = (url, init = {
headers: {
"Content-Type": "application/json"
}
}) => fetch(url, {
...init,
body: JSON.stringify(init.body),
headers: init.headers,
method: init.body && !init.method ? "POST" : init.method
});
var odoFetcher = async (url, options) => {
return fetcher(`https://api.odos.xyz/sor${url}`, options);
};
var assemblePath = async (user, pathId) => {
const response = await odoFetcher(`/assemble`, {
body: {
userAddr: user,
pathId
},
headers: {
"Content-Type": "application/json"
}
});
if (!response.ok) return "";
const data = await response.json();
return data.transaction.data;
};
var getOdosPath = async (quoteParams) => {
if (!quoteParams.fromToken || !quoteParams.toToken || !quoteParams.fromAmount) {
throw new Error("Missing required parameters for Odos path generation");
}
const data = {
chainId: quoteParams.fromChain,
inputTokens: [{ tokenAddress: quoteParams.fromToken, amount: quoteParams.fromAmount }],
outputTokens: [{ tokenAddress: quoteParams.toToken, proportion: 1 }],
userAddr: ZAP_BY_CHAIN[quoteParams.fromChain]
};
const response = await odoFetcher("/quote/v2", {
body: data,
headers: {
"Content-Type": "application/json"
}
});
if (!response.ok) {
console.error(`Failed to get Odos quote: ${response.status} ${response.statusText}`);
return { path: "" };
}
const quote = await response.json();
if (!quote?.pathId) {
console.error("Invalid response from Odos API: missing pathId");
return { path: "" };
}
const quoteId = quote.pathId;
const path = await assemblePath(data.userAddr, quoteId);
if (!path) {
console.error("Failed to assemble path from pathId");
}
return { path };
};
// src/utils/index.ts
import {
decodeAbiParameters,
decodeErrorResult,
decodeFunctionResult,
encodeAbiParameters,
encodeFunctionData,
parseAbiParameters
} from "viem";
// src/contracts/abis/IERC7412.ts
var IERC7412Abi = [
{
type: "function",
name: "fulfillOracleQuery",
inputs: [{ name: "signedOffchainData", type: "bytes", internalType: "bytes" }],
outputs: [],
stateMutability: "payable"
},
{
type: "function",
name: "oracleId",
inputs: [],
outputs: [{ name: "", type: "bytes32", internalType: "bytes32" }],
stateMutability: "view"
},
{
type: "error",
name: "FeeRequired",
inputs: [{ name: "feeAmount", type: "uint256", internalType: "uint256" }]
},
{
type: "error",
name: "OracleDataRequired",
inputs: [
{ name: "oracleContract", type: "address", internalType: "address" },
{ name: "oracleQuery", type: "bytes", internalType: "bytes" }
]
},
{
type: "error",
name: "Errors",
inputs: [
{
type: "bytes[]",
name: "revertReasons"
}
]
}
];
// src/utils/parseError.ts
function parseError(error) {
try {
if (error.cause?.data) {
return error.cause?.data;
}
if (error.cause?.cause?.data) {
return error.cause?.cause?.data;
}
if (error.cause?.cause?.cause?.data) {
return error.cause?.cause?.cause?.data;
}
if (error.cause?.cause?.error?.data) {
return error.cause?.cause?.error?.data;
}
if (error.cause?.cause?.cause?.error?.data) {
return error.cause?.cause?.cause?.error?.data;
}
} catch (err) {
console.log("=== exception in erc7412 error parser:", err);
}
console.log("=== a", JSON.stringify(error, null, 2));
console.error("=== got unknown error in erc7412 parse", error);
return "0x";
}
// src/contracts/addreses/oracleProxy.ts
var ORACLE_PROXY_BY_CHAIN = {
[421614 /* ARBITUM_SEPOLIA */]: "0x59d6ec32e05900949d7fff679a4adc7f94f0208c",
[42161 /* ARBITRUM */]: "0xe87ceB87b63267ef925E2897B629052eb815bB7d",
[8453 /* BASE */]: "0xa03A0Be2f3B85B76765A442683d62E992972d816",
[84532 /* BASE_SEPOLIA */]: "0x4Bc47c016EE6Ead253152CCfe6427D0bC06F544c"
};
// src/utils/index.ts
var Utils = class {
constructor(synthetixSdk) {
this.sdk = synthetixSdk;
}
async isOracleCall(data) {
return Object.values(ORACLE_PROXY_BY_CHAIN).map((a) => a.toLowerCase()).includes(typeof data === "string" ? data.toLowerCase() : data.to.toLowerCase());
}
/**
* Returns the Pyth price ids array
* @param oracleQueryData
* @returns Pyth Price IDs array
*/
getPythPriceIdsFromOracleQuery(oracleQueryData) {
const values = decodeAbiParameters(parseAbiParameters("uint8, uint64, bytes32[]"), oracleQueryData);
return values[2];
}
/**
* Decodes the response from a Smart contract function call
* @param abi Contract ABI
* @param functionName Function name to decode
* @param result Response from function call that needs to be decoded
* @returns Decoded result
*/
decodeResponse(abi, functionName, result) {
const decodedResult = decodeFunctionResult({
abi,
functionName,
data: result
});
return decodedResult;
}
/**
* Determines the type of Error emitted by ERC7412 contract and prepares signed update data
* @param data Error data emitted from ERC7412 contract
* @returns Encoded data for oracle price update transaction
*/
async fetchOracleUpdateData(data) {
const [updateType] = decodeAbiParameters([{ name: "updateType", type: "uint8" }], data);
if (updateType === 1) {
const [updateType2, stalenessOrTime, priceIds] = decodeAbiParameters(
[
{ name: "updateType", type: "uint8" },
{ name: "stalenessTolerance", type: "uint64" },
{ name: "priceIds", type: "bytes32[]" }
],
data
);
const stalenessTolerance = stalenessOrTime;
const updateData = await this.sdk.pyth.pythConnection.getPriceFeedsUpdateData(
priceIds
);
return encodeAbiParameters(
[
{ type: "uint8", name: "updateType" },
{ type: "uint64", name: "stalenessTolerance" },
{ type: "bytes32[]", name: "priceIds" },
{ type: "bytes[]", name: "updateData" }
],
[updateType2, stalenessTolerance, priceIds, updateData]
);
} else if (updateType === 2) {
const [updateType2, requestedTime, priceId] = decodeAbiParameters(
[
{ name: "updateType", type: "uint8" },
{ name: "requestedTime", type: "uint64" },
{ name: "priceIds", type: "bytes32" }
],
data
);
this.sdk.logger.info("Update type: ", updateType2);
this.sdk.logger.info("priceIds: ", priceId);
const [priceFeedUpdateVaa] = await this.sdk.pyth.pythConnection.getVaa(
priceId,
Number(requestedTime.toString())
);
const priceFeedUpdate = "0x" + Buffer.from(priceFeedUpdateVaa, "base64").toString("hex");
return encodeAbiParameters(
[
{ type: "uint8", name: "updateType" },
{ type: "uint64", name: "timestamp" },
{ type: "bytes32[]", name: "priceIds" },
{ type: "bytes[]", name: "updateData" }
],
[updateType2, requestedTime, [priceId], [priceFeedUpdate]]
);
} else {
throw new Error(`Error encoding/decoding data`);
}
}
async handleOracleDataRequiredError(parsedError) {
const err = decodeErrorResult({
abi: IERC7412Abi,
data: parsedError
});
const oracleAddress = err.args[0];
const oracleQuery = err.args[1];
const signedRequiredData = await this.fetchOracleUpdateData(oracleQuery);
const dataVerificationTx = this.generateDataVerificationTx(oracleAddress, signedRequiredData);
return dataVerificationTx;
}
/**
* Handles ERC7412 error by creating a price update tx which is prepended to the existing calls
* @param error Error thrown by the call function
* @param calls Multicall call data
* @returns call array with ERC7412 fulfillOracleQuery transaction
*/
async handleErc7412Error(parsedError) {
let err;
try {
err = decodeErrorResult({
abi: IERC7412Abi,
data: parsedError
});
} catch (decodeErr) {
this.sdk.logger.error("Decode Error: ", decodeErr);
throw new Error("Handle ERC7412 error");
}
if (!["OracleDataRequired", "FeeRequired", "Errors"].includes(err?.errorName))
throw new Error("Handle ERC7412 error");
if (err?.errorName === "Errors") {
const oracleErrors = err.args[0];
let oracleCalls = [];
const batchedOracleErrors = batchArray(oracleErrors, 10);
for (const oracleErrors2 of batchedOracleErrors) {
const promiseOracleCalls = oracleErrors2.map((oracleError) => {
return this.handleErc7412Error(oracleError);
});
const resolvedCalls = await Promise.all(promiseOracleCalls);
oracleCalls = [...oracleCalls, ...resolvedCalls.flat()];
}
return oracleCalls.flat();
}
if (err?.errorName === "OracleDataRequired") {
return [await this.handleOracleDataRequiredError(parsedError)];
}
return [];
}
/**
* Generates Call3Value tx for Price update data of Oracle
* @param oracleContract Oracle Contract address
* @param signedRequiredData Encoded Price Update data
* @returns Transaction Request for Oracle price update
*/
generateDataVerificationTx(_oracleContract, signedRequiredData) {
const priceUpdateCall = {
target: ORACLE_PROXY_BY_CHAIN[this.sdk.rpcConfig.chainId],
callData: encodeFunctionData({
abi: IERC7412Abi,
functionName: "fulfillOracleQuery",
args: [signedRequiredData]
}),
value: 0n,
requireSuccess: false
};
return priceUpdateCall;
}
/**
* Calls the `functionName` on `contractAddress` target using the Multicall contract. If the call requires
* a price update, ERC7412 price update tx is prepended to the tx.
* @param contractAddress Target contract address for the call
* @param abi Contract ABI
* @param functionName Function to be called on the contract
* @param args Arguments list for the function call
* @param calls Array of Call3Value calls for Multicall contract
* @returns Response from the contract function call
*/
async callErc7412({ contractAddress, abi, args, functionName, calls = [] }) {
const multicallInstance = await this.sdk.contracts.getMulticallInstance();
const currentCall = {
target: contractAddress,
callData: encodeFunctionData({
abi,
functionName,
args
}),
value: 0n,
requireSuccess: true
};
calls.push(currentCall);
const publicClient = this.sdk.getPublicClient();
const oracleCalls = await this.getMissingOracleCalls(calls);
const multicallData = encodeFunctionData({
abi: multicallInstance.abi,
functionName: "aggregate3Value",
args: [[...oracleCalls, ...calls]]
});
const totalValue = [...oracleCalls, ...calls].reduce((acc, tx) => {
return acc + (tx.value || 0n);
}, 0n);
const finalTx = {
account: this.sdk.accountAddress,
to: multicallInstance.address,
data: multicallData,
value: totalValue
};
const response = await publicClient.call(finalTx);
this.sdk.logger.info("=== response", response);
if (!response.data) throw new Error("Error decoding call data");
const multicallResult = this.decodeResponse(
multicallInstance.abi,
"aggregate3Value",
response.data
);
const returnData = multicallResult.at(-1);
if (!returnData?.success) throw new Error("Error decoding call data");
const decodedResult = this.decodeResponse(abi, functionName, returnData.returnData);
return decodedResult;
}
/**
* Calls the `functionName` on `contractAddress` target using the Multicall contract. If the call requires
* a price update, ERC7412 price update tx is prepended to the tx.
* @param contractAddress Target contract address for the call
* @param abi Contract ABI
* @param functionName Function to be called on the contract
* @param argsList Array of arguments list for the function call
* @param calls Array of Call3Value calls for Multicall contract
* @returns Array of responses from the contract function call for the multicalls
*/
async multicallErc7412({
contractAddress,
abi,
functionName,
args: argsList,
calls = []
}) {
const multicallInstance = await this.sdk.contracts.getMulticallInstance();
argsList = argsList.map((args) => Array.isArray(args) ? args : [args]);
argsList.forEach((args) => {
const currentCall = {
target: contractAddress,
callData: encodeFunctionData({
abi,
functionName,
args
}),
value: 0n,
requireSuccess: true
};
calls.push(currentCall);
});
calls = calls.map((call) => {
return {
...call,
requireSuccess: call.value === 0n ? true : false
};
});
const oracleCalls = await this.getMissingOracleCalls(calls);
const numCalls = calls.length - oracleCalls.length;
const publicClient = this.sdk.getPublicClient();
const totalValue = [...oracleCalls, ...calls].reduce((acc, tx) => {
return acc + (tx.value || 0n);
}, 0n);
const multicallData = encodeFunctionData({
abi: multicallInstance.abi,
functionName: "aggregate3Value",
args: [[...oracleCalls, ...calls]]
});
const finalTx = {
account: this.sdk.accountAddress,
to: multicallInstance.address,
data: multicallData,
value: totalValue
};
const response = await publicClient.call(finalTx);
const multicallResult = this.decodeResponse(
multicallInstance.abi,
"aggregate3Value",
response.data
);
const callsToDecode = multicallResult.slice(-numCalls);
const decodedResult = callsToDecode.map((result) => this.decodeResponse(abi, functionName, result.returnData));
return decodedResult;
}
isWriteContractParams(data) {
return data.contractAddress !== void 0;
}
/**
* Simulates the `functionName` on `contractAddress` target using the Multicall contract and returns the
* final transaction call with ERC7412 price update data if required
* @param contractAddress Target contract address for the call
* @param abi Contract ABI
* @param functionName Function to be called on the contract
* @param args Arguments list for the function call
* @param calls Array of Call3Value calls for Multicall contract
* @param override Override parameters for the transaction
* @returns Final transaction call with ERC7412 price update data if required
*/
async writeErc7412(data, override = {
shouldRevertOnTxFailure: true
}) {
const calls = data.calls ?? [];
const multicallInstance = await this.sdk.contracts.getMulticallInstance();
if (this.isWriteContractParams(data)) {
const { contractAddress, abi, functionName, args } = data;
const currentCall = {
target: contractAddress,
callData: encodeFunctionData({
abi,
functionName,
args
}),
value: 0n,
requireSuccess: true
};
calls.push(currentCall);
}
const multicallData = encodeFunctionData({
abi: multicallInstance.abi,
functionName: "aggregate3Value",
args: [calls]
});
let totalValue = 0n;
for (const tx of calls) {
totalValue += tx.value || 0n;
}
const finalTx = {
account: override.account || this.sdk.accountAddress,
to: multicallInstance.address,
data: multicallData,
value: totalValue
};
const publicClient = this.sdk.getPublicClient();
if (override.shouldRevertOnTxFailure) await publicClient.call(finalTx);
return this._fromCallDataToTransactionData(finalTx);
}
/**
* Calls the `functionName` on `contractAddress` target using the Multicall contract. If the call requires
* a price update, ERC7412 price update tx is prepended to the tx.
* @param contractAddress Target contract address for the call
* @param abi Contract ABI
* @param functionNames Function to be called on the contract
* @param argsList Array of arguments list for the function call
* @param calls Array of Call3Value calls for Multicall contract
* @returns Array of responses from the contract function call for the multicalls
*/
async multicallMultifunctionErc7412({
contractAddress,
abi,
functionNames,
args: argsList,
calls = []
}, override = {}) {
const multicallInstance = await this.sdk.contracts.getMulticallInstance();
argsList = argsList.map((args) => Array.isArray(args) ? args : [args]);
if (argsList.length != functionNames.length) {
throw new Error("Inconsistent data: args and functionName don't match");
}
argsList.forEach((args, index) => {
const currentCall = {
target: contractAddress,
callData: encodeFunctionData({
abi,
functionName: functionNames[index],
args
}),
value: 0n,
requireSuccess: true
};
calls.push(currentCall);
});
const publicClient = this.sdk.getPublicClient();
const oracleCalls = await this.getMissingOracleCalls(calls);
const multicallData = encodeFunctionData({
abi: multicallInstance.abi,
functionName: "aggregate3Value",
args: [[...oracleCalls, ...calls]]
});
let totalValue = 0n;
for (const tx of [...oracleCalls, ...calls]) {
totalValue += tx.value || 0n;
}
const finalTx = {
account: override.account || this.sdk.accountAddress,
to: multicallInstance.address,
data: multicallData,
value: totalValue
};
const response = await publicClient.call(finalTx);
this.sdk.logger.info("=== response", response);
const multicallResult = this.decodeResponse(
multicallInstance.abi,
"aggregate3Value",
response.data
);
const callsToDecode = multicallResult.slice(-oracleCalls.length);
const decodedResult = callsToDecode.map(
(result, idx) => this.decodeResponse(abi, functionNames[idx], result.returnData)
);
return decodedResult;
}
async getMissingOracleCalls(calls, oracleCalls = [], {
attemps = MAX_ERC7412_RETRIES,
account,
stateOverride
} = {}) {
const publicClient = this.sdk.getPublicClient();
const totalValue = [...oracleCalls, ...calls].reduce((acc, tx) => {
return acc + (tx.value || 0n);
}, 0n);
this.sdk.logger.info("=== init data", { oracleCalls, calls, attemps });
const multicallInstance = await this.sdk.contracts.getMulticallInstance();
const multicallData = encodeFunctionData({
abi: multicallInstance.abi,
functionName: "aggregate3Value",
args: [[...oracleCalls, ...calls]]
});
const parsedTx = {
account: account || this.sdk.accountAddress,
to: multicallInstance.address,
data: multicallData,
value: totalValue,
stateOverride
};
try {
await publicClient.call(parsedTx);
return oracleCalls;
} catch (error) {
const parsedError = parseError(error);
this.sdk.logger.error("=== parsedError in getMissingOracleCalls", parsedError);
const shouldRetry = this.shouldRetryLogic(error, attemps);
console.log("=== shouldRetry", { shouldRetry, error, attemps, parsedError });
if (!shouldRetry) return oracleCalls;
const data = await this.handleErc7412Error(parsedError);
return await this.getMissingOracleCalls(calls, [...oracleCalls, ...data], {
account,
attemps: attemps - 1,
stateOverride
});
}
}
_fromCall3ToTransactionData(call) {
return {
to: call.target,
data: call.callData,
value: call?.value?.toString() ?? "0"
};
}
_fromTransactionDataToCall3(data, requireSuccess = true) {
return {
target: data.to,
callData: data.data,
value: BigInt(data.value || 0),
requireSuccess
};
}
_fromCallDataToTransactionData(calls) {
return {
to: calls.to,
data: calls.data,
value: calls?.value?.toString() ?? "0"
};
}
_fromTransactionDataToCallData(data) {
return {
account: this.sdk.accountAddress,
to: data.to,
data: data.data,
value: BigInt(data.value || 0)
};
}
async processTransactions(data, override) {
const useOracleCall = override.useOracleCalls ?? true;
const oracleCalls = useOracleCall ? await this.getMissingOracleCalls(data, void 0, {
account: override.account,
stateOverride: override?.stateOverride?.reduce(
(acc, state) => {
const includedIndex = acc.findIndex((s) => s.address === state.address);
const included = override?.stateOverride?.[includedIndex];
if (!included && state.stateDiff) acc.push(state);
if (included) {
acc[includedIndex] = {
address: state.address,
stateDiff: [...state.stateDiff, ...included.stateDiff]
};
}
return acc;
},
[]
)
}) ?? void 0 : [];
const txs = [
...!override.useMultiCall ? (override.prepend || []).map((a) => this._fromTransactionDataToCall3(a, true)) : [],
...oracleCalls,
...data
];
if (!override.useMultiCall && !override.submit) return txs.map(this.sdk.utils._fromCall3ToTransactionData);
const tx = await this.sdk.utils.writeErc7412({ calls: txs }, override);
if (!override.submit) return [...override.prepend || [], tx];
return this.sdk.executeTransaction(this.sdk.utils._fromTransactionDataToCallData(tx));
}
shouldRetryLogic(error, attemps = 0) {
const parsedError = parseError(error);
const isErc7412Error = this.isErc7412Error(parsedError);
this.sdk.logger.error("=== parsedError in processTransactions ", {
parsedError,
isErc7412Error
});
if (!attemps && !isErc7412Error) return false;
if (!isErc7412Error) {
return false;
}
return true;
}
isErc7412Error(parsedError) {
return parsedError.startsWith(SIG_ORACLE_DATA_REQUIRED) || parsedError.startsWith(SIG_FEE_REQUIRED) || parsedError.startsWith(SIG_ERRORS);
}
};
// src/core/index.ts
import { encodeFunctionData as encodeFunctionData2, formatEther as formatEther2, parseUnits } from "viem";
var Core = class {
constructor(synthetixSdk) {
this.sdk = synthetixSdk;
this.accountIds = [];
}
async initCore() {
await this.getAccountIds();
}
/**
* Returns the Owner wallet address for an account ID
* @param accountId - Account ID
* @returns string - Address of the account owning the accountId
*/
async getAccountOwner(accountId) {
const coreProxy = await this.sdk.contracts.getCoreProxyInstance();
const response = await this.sdk.utils.callErc7412({
contractAddress: coreProxy.address,
abi: coreProxy.abi,
functionName: "getAccountOwner",
args: [accountId]
});
this.sdk.logger.info(`Core account Owner for id ${accountId} is ${response}`);
return response;
}
/**
* Get the address of the USD stablecoin token
*
* @returns Address of the USD stablecoin token
*/
async getUsdToken() {
const coreProxy = await this.sdk.contracts.getCoreProxyInstance();
const response = await this.sdk.utils.callErc7412({
contractAddress: coreProxy.address,
abi: coreProxy.abi,
functionName: "getUsdToken",
args: []
});
this.sdk.logger.info("USD Token address: ", response);
return response;
}
/**
* Get the core account IDs owned by an address.
* Fetches the account IDs for the given address by checking the balance of
* the AccountProxy contract, which is an NFT owned by the address.
* If no address is provided, uses the connected wallet address.
* @param address The address to get accounts for. Uses connected address if not provided.
* @param defaultAccountId The default account ID to set after fetching.
* @returns A list of account IDs owned by the address
*/
async getAccountIds({
address: accountAddress = this.sdk.accountAddress || ZERO_ADDRESS,
accountId: defaultAccountId = void 0
} = {}) {
if (accountAddress == ZERO_ADDRESS) {
throw new Error("Invalid address");
}
const accountProxy = await this.sdk.contracts.getAccountProxyInstance();
const balance = await accountProxy.read.balanceOf([accountAddress]);
this.sdk.logger.info("balance", balance);
const argsList = [];
for (let index = 0; index < Number(balance); index++) {
argsList.push([accountAddress, index]);
}
const accountIds = await this.sdk.utils.multicallErc7412({
contractAddress: accountProxy.address,
abi: accountProxy.abi,
functionName: "tokenOfOwnerByIndex",
args: argsList
});
this.accountIds = accountIds;
this.sdk.logger.info("accountIds", accountIds);
if (defaultAccountId) {
this.defaultAccountId = defaultAccountId;
} else if (this.accountIds.length > 0) {
this.defaultAccountId = this.accountIds[0];
this.sdk.logger.info("Using default account id as ", this.defaultAccountId);
}
return accountIds;
}
/**
* Get the available collateral for an account for a specified collateral type
* of ``token_address``
* Fetches the amount of undelegated collateral available for withdrawal
* for a given token and account.
* @param tokenAddress The address of the collateral token
* @param accountId The ID of the account to check. Uses default if not provided.
* @returns The available collateral as an ether value.
*/
async getAvailableCollateral({
tokenAddress,
accountId = this.defaultAccountId
}) {
if (!accountId) throw new Error("Invalid account ID");
const coreProxy = await this.sdk.contracts.getCoreProxyInstance();
const availableCollateral = await this.sdk.utils.callErc7412({
contractAddress: coreProxy.address,
abi: coreProxy.abi,
functionName: "getAccountAvailableCollateral",
args: [accountId, tokenAddress]
});
return formatEther2(availableCollateral);
}
/**
* Retrieves the unique system preferred pool
* @returns poolId The id of the pool that is currently set as preferred in the system.
*/
async getPreferredPool() {
const coreProxy = await this.sdk.contracts.getCoreProxyInstance();
const preferredPool = await this.sdk.utils.callErc7412({
contractAddress: coreProxy.address,
abi: coreProxy.abi,
functionName: "getPreferredPool",
args: []
});
this.sdk.logger.info(preferredPool);
return preferredPool;
}
async createAccount(accountId, override = { shouldRevertOnTxFailure: false }) {
const txArgs = [];
if (accountId != void 0) {
txArgs.push(accountId);
}
const coreProxy = await this.sdk.contracts.getCoreProxyInstance();
const createAccountTx = {
target: coreProxy.address,
callData: encodeFunctionData2({
abi: coreProxy.abi,
functionName: "createAccount",
args: txArgs
}),
value: 0n,
requireSuccess: true
};
const txs = [createAccountTx];
return this.sdk.utils.processTransactions(txs, { ...override, useOracleCalls: false });
}
async deposit({
tokenAddress,
amount,
decimals = 18,
accountId = this.defaultAccountId
}, override = { shouldRevertOnTxFailure: false }) {
if (!accountId) throw new Error("Invalid account ID");
const amountInWei = parseUnits(amount.toString(), decimals);
const coreProxy = await this.sdk.contracts.getCoreProxyInstance();
const depositTx = {
target: coreProxy.address,
callData: encodeFunctionData2({
abi: coreProxy.abi,
functionName: "deposit",
args: [accountId, tokenAddress, amountInWei]
}),
value: 0n,
requireSuccess: true
};
const txs = [depositTx];
return this.sdk.utils.processTransactions(txs, override);
}
async withdraw({
tokenAddress,
amount,
decimals = 18,
accountId = this.defaultAccountId
}, override = { shouldRevertOnTxFailure: false }) {
if (!accountId) throw new Error("Account ID is required for withdrawal");
const amountInWei = parseUnits(amount.toString(), decimals);
const coreProxy = await this.sdk.contracts.getCoreProxyInstance();
const withdrawTx = {
target: coreProxy.address,
callData: encodeFunctionData2({
abi: coreProxy.abi,
functionName: "withdraw",
args: [accountId, tokenAddress, amountInWei]
}),
value: 0n,
requireSuccess: true
};
const txs = [withdrawTx];
return this.sdk.utils.processTransactions(txs, override);
}
async delegateCollateral({
tokenAddress,
amount,
poolId,
leverage,
accountId = this.defaultAccountId
}, override = { shouldRevertOnTxFailure: false }) {
if (!accountId) throw new Error("Invalid account ID");
const amountInWei = parseUnits(amount.toString(), 18);
const leverageInWei = parseUnits(leverage.toString(), 18);
const coreProxy = await this.sdk.contracts.getCoreProxyInstance();
const delegateCollateralTx = {
target: coreProxy.address,
callData: encodeFunctionData2({
abi: coreProxy.abi,
functionName: "delegateCollateral",
args: [accountId, poolId, tokenAddress, amountInWei, leverageInWei]
}),
value: 0n,
requireSuccess: true
};
const txs = [delegateCollateralTx];
return this.sdk.utils.processTransactions(txs, override);
}
async mintUsd({
tokenAddress,
amount,
poolId,
accountId = this.defaultAccountId
}, override = { shouldRevertOnTxFailure: false }) {
if (!accountId) throw new Error("Invalid account ID");
const amountInWei = parseUnits(amount.toString(), 18);
const coreProxy = await this.sdk.contracts.getCoreProxyInstance();
const mintUsdTx = {
target: coreProxy.address,
callData: encodeFunctionData2({
abi: coreProxy.abi,
functionName: "mintUsd",
args: [accountId, poolId, tokenAddress, amountInWei]
}),
value: 0n,
requireSuccess: true
};
const txs = [mintUsdTx];
return this.sdk.utils.processTransactions(txs, override);
}
};
// src/index.ts
import {
createPublicClient,
createWalletClient,
encodeFunctionData as encodeFunctionData5,
erc20Abi as erc20Abi5,
getContract as getContract3,
http,
maxUint256 as maxUint2562,
webSocket
} from "viem";
// src/contracts/v3-contracts/42161/index.ts
import * as ARBTokenMain from "@synthetixio/v3-contracts/42161-main/ARBToken.json";
import * as AccountProxyMain from "@synthetixio/v3-contracts/42161-main/AccountProxy.json";
import * as AllErrorsMain from "@synthetixio/v3-contracts/42161-main/AllErrors.json";
import * as CollateralToken_ARBMain from "@synthetixio/v3-contracts/42161-main/CollateralToken_ARB.json";
import * as CollateralToken_USDCMain from "@synthetixio/v3-contracts/42161-main/CollateralToken_USDC.json";
import * as CollateralToken_USDeMain from "@synthetixio/v3-contracts/42161-main/CollateralToken_USDe.json";
import * as CollateralToken_WETHMain from "@synthetixio/v3-contracts/42161-main/CollateralToken_WETH.json";
import * as CollateralToken_sETHMain from "@synthetixio/v3-contracts/42161-main/CollateralToken_sETH.json";
import * as CollateralToken_stBTCMain from "@synthetixio/v3-contracts/42161-main/CollateralToken_stBTC.json";
import * as CollateralToken_tBTCMain from "@synthetixio/v3-contracts/42161-main/CollateralToken_tBTC.json";
import * as CoreProxyMain from "@synthetixio/v3-contracts/42161-main/CoreProxy.json";
import * as OracleManagerProxyMain from "@synthetixio/v3-contracts/42161-main/OracleManagerProxy.json";
import * as PerpsAccountProxyMain from "@synthetixio/v3-contracts/42161-main/PerpsAccountProxy.json";
import * as PerpsMarketProxyMain from "@synthetixio/v3-contracts/42161-main/PerpsMarketProxy.json";
import * as PythERC7412WrapperMain from "@synthetixio/v3-contracts/42161-main/PythERC7412Wrapper.json";
import * as RewardsDistributor_1_ARB_ARBMain from "@synthetixio/v3-contracts/42161-main/RewardsDistributor_1_ARB_ARB.json";
import * as RewardsDistributor_1_USDC_ARBMain from "@synthetixio/v3-contracts/42161-main/RewardsDistributor_1_USDC_ARB.json";
import * as RewardsDistributor_1_USDe_ARBMain from "@synthetixio/v3-contracts/42161-main/RewardsDistributor_1_USDe_ARB.json";
import * as RewardsDistributor_1_WETH_ARBMain from "@synthetixio/v3-contracts/42161-main/RewardsDistributor_1_WETH_ARB.json";
import * as RewardsDistributor_1_sETHMain from "@synthetixio/v3-contracts/42161-main/RewardsDistributor_1_sETH.json";
import * as RewardsDistributor_1_stBTCMain from "@synthetixio/v3-contracts/42161-main/RewardsDistributor_1_stBTC.json";
import * as SpotMarketProxyMain from "@synthetixio/v3-contracts/42161-main/SpotMarketProxy.json";
import * as SynthToken_sETHMain from "@synthetixio/v3-contracts/42161-main/SynthToken_sETH.json";
import * as SynthToken_sUSDCMain from "@synthetixio/v3-contracts/42161-main/SynthToken_sUSDC.json";
import * as SynthToken_stBTCMain from "@synthetixio/v3-contracts/42161-main/SynthToken_stBTC.json";
import * as SynthUSDCTokenMain from "@synthetixio/v3-contracts/42161-main/SynthUSDCToken.json";
import * as TrustedMulticallForwarderMain from "@synthetixio/v3-contracts/42161-main/TrustedMulticallForwarder.json";
import * as USDCTokenMain from "@synthetixio/v3-contracts/42161-main/USDCToken.json";
import * as USDProxyMain from "@synthetixio/v3-contracts/42161-main/USDProxy.json";
import * as WETHTokenMain from "@synthetixio/v3-contracts/42161-main/WETHToken.json";
import * as CannonMain from "@synthetixio/v3-contracts/42161-main/cannon.json";
import * as CollateralTokensMain from "@synthetixio/v3-contracts/42161-main/collateralTokens.json";
import * as ExtrasMain from "@synthetixio/v3-contracts/42161-main/extras.json";
import * as mainMeta from "@synthetixio/v3-contracts/42161-main/meta.json";
import * as MintableTokensMain from "@synthetixio/v3-contracts/42161-main/mintableTokens.json";
import * as RewardsDistributorsMain from "@synthetixio/v3-contracts/42161-main/rewardsDistributors.json";
import * as SynthTokensMain from "@synthetixio/v3-contracts/42161-main/synthTokens.json";
import * as SystemTokenMain from "@synthetixio/v3-contracts/42161-main/systemToken.json";
// src/contracts/abis/zap.ts
var ZAP_ABI = [
{
inputs: [
{ internalType: "address", name: "_usdc", type: "address" },
{ internalType: "address", name: "_usdx", type: "address" },
{ internalType: "address", name: "_spotMarket", type: "address" },
{ internalType: "address", name: "_perpsMarket", type: "address" },
{ internalType: "address", name: "_referrer", type: "address" },
{ internalType: "uint128", name: "_susdcSpotId", type: "uint128" },
{ internalType: "address", name: "_aave", type: "address" },
{ internalType: "address", name: "_router", type: "address" },
{ internalType: "address", name: "_quoter", type: "address" }
],
stateMutability: "nonpayable",
type: "constructor"
},
{
inputs: [{ internalType: "string", name: "reason", type: "string" }],
name: "BuyFailed",
type: "error"
},
{ inputs: [], name: "NotPermitted", type: "error" },
{
inputs: [{ internalType: "address", name: "caller", type: "address" }],
name: "OnlyAave",
type: "error"
},
{ inputs: [], name: "OnlyPlumber", type: "error" },
{
inputs: [{ internalType: "bytes", name: "reason", type: "bytes" }],
name: "PullFailed",
type: "error"
},
{
inputs: [{ internalType: "bytes", name: "reason", type: "bytes" }],
name: "PushFailed",
type: "error"
},
{
inputs: [
{ internalType: "enum Reentrancy.Stage", name: "actual", type: "uint8" },
{
internalType: "enum Reentrancy.Stage",
name: "expected",
type: "uint8"
}
],
name: "ReentrancyDetected",
type: "error"
},
{ inputs: [], name: "ReentrancyGuardReentrantCall", type: "error" },
{
inputs: [{ internalType: "address", name: "token", type: "address" }],
name: "SafeERC20FailedOperation",
type: "error"
},
{
inputs: [{ internalType: "string", name: "reason", type: "string" }],
name: "SellFailed",
type: "error"
},
{
inputs: [{ internalType: "string", name: "reason", type: "string" }],
name: "SwapFailed",
type: "error"
},
{
inputs: [{ internalType: "string", name: "reason", type: "string" }],
name: "UnwrapFailed",
type: "error"
},
{
inputs: [{ internalType: "string", name: "reason", type: "string" }],
name: "WrapFailed",
type: "error"
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "address",
name: "plumber",
type: "address"
}
],
name: "PlumberDesignated",
type: "event"
},
{
inputs: [],
name: "AAVE",
outputs: [{ internalType: "address", name: "", type: "address" }],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "BURN_PERMISSION",
outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "FEE_TIER",
outputs: [{ internalType: "uint24", name: "", type: "uint24" }],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "MODIFY_PERMISSION",
outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "PERPS_MARKET",
outputs: [{ internalType: "address", name: "", type: "address" }],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "PLUMBER",
outputs: [{ internalType: "address", name: "", type: "address" }],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "QUOTER",
outputs: [{ internalType: "address", name: "", type: "address" }],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "REFERRAL_CODE",
outputs: [{ internalType: "uint16", name: "", type: "uint16" }],
stateMutability: "view",
type: "function"
},
{
inputs: [],