diff --git a/examples/interface/src/components/ButtonClick.tsx b/examples/interface/src/components/ButtonClick.tsx
index 5adf71c..3cc805c 100644
--- a/examples/interface/src/components/ButtonClick.tsx
+++ b/examples/interface/src/components/ButtonClick.tsx
@@ -25,6 +25,7 @@ const ButtonClick = () => {
const ekubo_core_address = useMemo(() => "0x031e8a7ab6a6a556548ac85cbb8b5f56e8905696e9f13e9a858142b8ee0cc221", [])
const avnu_address = useMemo(() => "0x07e36202ace0ab52bf438bd8a8b64b3731c48d09f0d8879f5b006384c2f35032", [])
const simple_swapper = useMemo(() => "0x064f7ed2dc5070133ae8ccdf85f01e82507facbe5cdde456e1418e3901dc51a0", [])
+ const quoter = useMemo(() => "0x042aa743335663ed9c7b52b331ab7f81cc8d65280d311506653f9b5cc22be7cb", [])
const provider = new Provider({ sequencer: { network: constants.NetworkName.SN_GOERLI } });
let wrap = new Wrap(
@@ -33,6 +34,7 @@ const ButtonClick = () => {
eth_address,
ekubo_position_address,
ekubo_core_address,
+ quoter,
provider
)
const getERC1155Balance = useCallback(async () => {
@@ -43,9 +45,11 @@ const ButtonClick = () => {
const getCurrentPrice = useCallback(async () => {
if (!address) return;
- // let p = await Wrap.getCurrentPrice();
- // setCurrentPrice(p);
- }, [address, erc1155_address]);
+ if (!account) return;
+ let p = await Wrap.quoteSingle(FeeAmount.LOWEST, eth_address, BigInt(10** 7), account);
+ let realPrice = p / (10 ** 7);
+ setCurrentPrice(realPrice);
+ }, [address, erc1155_address, account]);
useEffect(() => {
getERC1155Balance();
@@ -55,6 +59,14 @@ const ButtonClick = () => {
return () => clearInterval(interval);
}, [getERC1155Balance]);
+ useEffect(() => {
+ getCurrentPrice();
+ const interval = setInterval(() => {
+ getCurrentPrice();
+ }, 3000);
+ return () => clearInterval(interval);
+ }, [getCurrentPrice]);
+
const handleAddLiquidity = useCallback(() => {
if (!account) return;
const realERC1155Amount = erc1155Amount;
@@ -114,7 +126,7 @@ const ButtonClick = () => {
ERC1155 Balance: {balance}
-
Current Price : {currentPrice}
+
Current Price : 1 ETH = {currentPrice} WERC20
Mint ERC1155
diff --git a/sdk/packages/instaswap-core/src/abi/quoter-abi.json b/sdk/packages/instaswap-core/src/abi/quoter-abi.json
new file mode 100644
index 0000000..2ee4036
--- /dev/null
+++ b/sdk/packages/instaswap-core/src/abi/quoter-abi.json
@@ -0,0 +1,274 @@
+[
+ {
+ "type": "impl",
+ "name": "QuoterLockerImpl",
+ "interface_name": "ekubo::interfaces::core::ILocker"
+ },
+ {
+ "type": "interface",
+ "name": "ekubo::interfaces::core::ILocker",
+ "items": [
+ {
+ "type": "function",
+ "name": "locked",
+ "inputs": [
+ {
+ "name": "id",
+ "type": "core::integer::u32"
+ },
+ {
+ "name": "data",
+ "type": "core::array::Array::"
+ }
+ ],
+ "outputs": [
+ {
+ "type": "core::array::Array::"
+ }
+ ],
+ "state_mutability": "external"
+ }
+ ]
+ },
+ {
+ "type": "impl",
+ "name": "QuoterImpl",
+ "interface_name": "ekubo::quoter::IQuoter"
+ },
+ {
+ "type": "enum",
+ "name": "core::bool",
+ "variants": [
+ {
+ "name": "False",
+ "type": "()"
+ },
+ {
+ "name": "True",
+ "type": "()"
+ }
+ ]
+ },
+ {
+ "type": "struct",
+ "name": "ekubo::types::i129::i129",
+ "members": [
+ {
+ "name": "mag",
+ "type": "core::integer::u128"
+ },
+ {
+ "name": "sign",
+ "type": "core::bool"
+ }
+ ]
+ },
+ {
+ "type": "struct",
+ "name": "ekubo::types::keys::PoolKey",
+ "members": [
+ {
+ "name": "token0",
+ "type": "core::starknet::contract_address::ContractAddress"
+ },
+ {
+ "name": "token1",
+ "type": "core::starknet::contract_address::ContractAddress"
+ },
+ {
+ "name": "fee",
+ "type": "core::integer::u128"
+ },
+ {
+ "name": "tick_spacing",
+ "type": "core::integer::u128"
+ },
+ {
+ "name": "extension",
+ "type": "core::starknet::contract_address::ContractAddress"
+ }
+ ]
+ },
+ {
+ "type": "struct",
+ "name": "core::array::Span::",
+ "members": [
+ {
+ "name": "snapshot",
+ "type": "@core::array::Array::"
+ }
+ ]
+ },
+ {
+ "type": "struct",
+ "name": "ekubo::quoter::Route",
+ "members": [
+ {
+ "name": "pool_keys",
+ "type": "core::array::Span::"
+ }
+ ]
+ },
+ {
+ "type": "struct",
+ "name": "ekubo::quoter::QuoteParameters",
+ "members": [
+ {
+ "name": "amount",
+ "type": "ekubo::types::i129::i129"
+ },
+ {
+ "name": "specified_token",
+ "type": "core::starknet::contract_address::ContractAddress"
+ },
+ {
+ "name": "route",
+ "type": "ekubo::quoter::Route"
+ }
+ ]
+ },
+ {
+ "type": "struct",
+ "name": "ekubo::quoter::QuoteResult",
+ "members": [
+ {
+ "name": "amount",
+ "type": "ekubo::types::i129::i129"
+ },
+ {
+ "name": "other_token",
+ "type": "core::starknet::contract_address::ContractAddress"
+ }
+ ]
+ },
+ {
+ "type": "struct",
+ "name": "ekubo::quoter::QuoteSingleParameters",
+ "members": [
+ {
+ "name": "amount",
+ "type": "ekubo::types::i129::i129"
+ },
+ {
+ "name": "specified_token",
+ "type": "core::starknet::contract_address::ContractAddress"
+ },
+ {
+ "name": "pool_key",
+ "type": "ekubo::types::keys::PoolKey"
+ }
+ ]
+ },
+ {
+ "type": "struct",
+ "name": "core::integer::u256",
+ "members": [
+ {
+ "name": "low",
+ "type": "core::integer::u128"
+ },
+ {
+ "name": "high",
+ "type": "core::integer::u128"
+ }
+ ]
+ },
+ {
+ "type": "struct",
+ "name": "ekubo::types::delta::Delta",
+ "members": [
+ {
+ "name": "amount0",
+ "type": "ekubo::types::i129::i129"
+ },
+ {
+ "name": "amount1",
+ "type": "ekubo::types::i129::i129"
+ }
+ ]
+ },
+ {
+ "type": "interface",
+ "name": "ekubo::quoter::IQuoter",
+ "items": [
+ {
+ "type": "function",
+ "name": "quote",
+ "inputs": [
+ {
+ "name": "params",
+ "type": "ekubo::quoter::QuoteParameters"
+ }
+ ],
+ "outputs": [
+ {
+ "type": "ekubo::quoter::QuoteResult"
+ }
+ ],
+ "state_mutability": "view"
+ },
+ {
+ "type": "function",
+ "name": "quote_single",
+ "inputs": [
+ {
+ "name": "params",
+ "type": "ekubo::quoter::QuoteSingleParameters"
+ }
+ ],
+ "outputs": [
+ {
+ "type": "ekubo::quoter::QuoteResult"
+ }
+ ],
+ "state_mutability": "view"
+ },
+ {
+ "type": "function",
+ "name": "delta_to_sqrt_ratio",
+ "inputs": [
+ {
+ "name": "pool_key",
+ "type": "ekubo::types::keys::PoolKey"
+ },
+ {
+ "name": "sqrt_ratio",
+ "type": "core::integer::u256"
+ }
+ ],
+ "outputs": [
+ {
+ "type": "ekubo::types::delta::Delta"
+ }
+ ],
+ "state_mutability": "view"
+ }
+ ]
+ },
+ {
+ "type": "struct",
+ "name": "ekubo::interfaces::core::ICoreDispatcher",
+ "members": [
+ {
+ "name": "contract_address",
+ "type": "core::starknet::contract_address::ContractAddress"
+ }
+ ]
+ },
+ {
+ "type": "constructor",
+ "name": "constructor",
+ "inputs": [
+ {
+ "name": "core",
+ "type": "ekubo::interfaces::core::ICoreDispatcher"
+ }
+ ]
+ },
+ {
+ "type": "event",
+ "name": "ekubo::quoter::Quoter::Event",
+ "kind": "enum",
+ "variants": []
+ }
+ ]
\ No newline at end of file
diff --git a/sdk/packages/instaswap-core/src/tickMath.ts b/sdk/packages/instaswap-core/src/tickMath.ts
index 003b292..198aa40 100644
--- a/sdk/packages/instaswap-core/src/tickMath.ts
+++ b/sdk/packages/instaswap-core/src/tickMath.ts
@@ -1,53 +1,45 @@
import { Decimal } from 'decimal.js-light';
-import JSBI from 'jsbi';
-
-
-export abstract class TickMath {
-
- /**
- * Cannot be constructed.
- */
- private constructor() { }
-
- public static getTickAtSqrtRatio(sqrt_ratio_x128: bigint): number {
- // A fixed point .128 number has at most 128 bits after the decimal,
- // which translates to about 10**38.5 in decimal.
- // That means ~78 decimals of precision should be able to represent
- // any price with full precision.
- // Note there can be loss of precision for intermediate calculations,
- // but this should be sufficient for just computing the price.
- Decimal.set({ precision: 78 });
-
- const sqrt_ratio = new Decimal(sqrt_ratio_x128.toString()).div(new Decimal(2).pow(128));
- const tick = sqrt_ratio
- .div(new Decimal('1.000001').sqrt())
- .log()
- .div(new Decimal('2').log())
- .toFixed(0);
- return Number(tick);
-
- }
-
- public static getSqrtRatioAtTick(tick: number): bigint {
- // A fixed point .128 number has at most 128 bits after the decimal,
- // which translates to about 10**38.5 in decimal.
- // That means ~78 decimals of precision should be able to represent
- // any price with full precision.
- // Note there can be loss of precision for intermediate calculations,
- // but this should be sufficient for just computing the price.
- Decimal.set({ precision: 78 });
-
- const sqrt_ratio_x128 =
- new Decimal('1.000001')
- .sqrt()
- .pow(tick)
- .mul(new Decimal(2).pow(128));
- return BigInt(sqrt_ratio_x128.toFixed(0));
- }
-
- public static tryParseTick(): number | undefined {
- // TODO
- return undefined;
- }
+
+
+
+export function getTickAtSqrtRatio(sqrt_ratio_x128: bigint): number {
+ // A fixed point .128 number has at most 128 bits after the decimal,
+ // which translates to about 10**38.5 in decimal.
+ // That means ~78 decimals of precision should be able to represent
+ // any price with full precision.
+ // Note there can be loss of precision for intermediate calculations,
+ // but this should be sufficient for just computing the price.
+ Decimal.set({ precision: 78 });
+
+ const sqrt_ratio = new Decimal(sqrt_ratio_x128.toString()).div(new Decimal(2).pow(128));
+ const tick = sqrt_ratio
+ .div(new Decimal('1.000001').sqrt())
+ .log()
+ .div(new Decimal('2').log())
+ .toFixed(0);
+ return Number(tick);
+
+}
+
+export function getSqrtRatioAtTick(tick: number): bigint {
+ // A fixed point .128 number has at most 128 bits after the decimal,
+ // which translates to about 10**38.5 in decimal.
+ // That means ~78 decimals of precision should be able to represent
+ // any price with full precision.
+ // Note there can be loss of precision for intermediate calculations,
+ // but this should be sufficient for just computing the price.
+ Decimal.set({ precision: 78 });
+
+ const sqrt_ratio_x128 =
+ new Decimal('1.000001')
+ .sqrt()
+ .pow(tick)
+ .mul(new Decimal(2).pow(128));
+ return BigInt(sqrt_ratio_x128.toFixed(0));
}
+
+export function tryParseTick(): number | undefined {
+ // TODO
+ return undefined;
+}
\ No newline at end of file
diff --git a/sdk/packages/instaswap-core/src/wrap.ts b/sdk/packages/instaswap-core/src/wrap.ts
index bb98562..b740ebd 100644
--- a/sdk/packages/instaswap-core/src/wrap.ts
+++ b/sdk/packages/instaswap-core/src/wrap.ts
@@ -1,12 +1,13 @@
-import { Contract, uint256, CallData, RawArgs, Call, num, cairo, BigNumberish, Provider } from 'starknet'
+import { Contract, uint256, CallData, RawArgs, Call, num, cairo, BigNumberish, Provider, Account, AccountInterface } from 'starknet'
import ERC1155 from "./abi/erc1155-abi.json";
import WERC20 from "./abi/werc20-abi.json";
import ERC20 from "./abi/erc20-abi.json";
import EkuboPosition from "./abi/ekubo-position-abi.json";
+import Quoter from "./abi/quoter-abi.json";
import EkuboCore from "./abi/ekubo-core-abi.json";
import { FeeAmount } from './constants';
-import { TickMath } from './tickMath';
+import { getSqrtRatioAtTick, getTickAtSqrtRatio } from './tickMath';
import { Decimal } from 'decimal.js-light';
const MAX_SQRT_RATIO = 6277100250585753475930931601400621808602321654880405518632n;
@@ -18,15 +19,17 @@ export class Wrap {
public static ERC20Contract: Contract;
public static EkuboPosition: Contract;
public static EkuboCoreContract: Contract;
+ public static QuoterContract: Contract;
- constructor(ERC1155Address: string, WERC20Address: string, ERC20Address: string, EkuboPositionAddress: string, EkuboCoreAddress: string, provider: Provider) {
+ constructor(ERC1155Address: string, WERC20Address: string, ERC20Address: string, EkuboPositionAddress: string, EkuboCoreAddress: string, QuoterAddress: string, provider: Provider) {
Wrap.ERC1155Contract = new Contract(ERC1155, ERC1155Address, provider);
Wrap.WERC20Contract = new Contract(WERC20, WERC20Address, provider);
Wrap.ERC20Contract = new Contract(ERC20, ERC20Address, provider);
Wrap.EkuboPosition = new Contract(EkuboPosition, EkuboPositionAddress, provider);
Wrap.EkuboCoreContract = new Contract(EkuboCore, EkuboCoreAddress, provider);
+ Wrap.QuoterContract = new Contract(Quoter, QuoterAddress, provider);
}
// public deposit = async (amount: bigint) => {
@@ -92,8 +95,8 @@ export class Wrap {
Decimal.set({ precision: 78 });
let lowerSqrtRatioX128 = new Decimal(lowerPrice).sqrt().mul(new Decimal(2).pow(128)).toFixed(0);
let upperSqrtRatioX128 = new Decimal(upperPrice).sqrt().mul(new Decimal(2).pow(128)).toFixed(0);
- const lowerTick = TickMath.getTickAtSqrtRatio(BigInt(lowerSqrtRatioX128));
- const upperTick = TickMath.getTickAtSqrtRatio(BigInt(upperSqrtRatioX128));
+ const lowerTick = getTickAtSqrtRatio(BigInt(lowerSqrtRatioX128));
+ const upperTick = getTickAtSqrtRatio(BigInt(upperSqrtRatioX128));
if (lowerTick > upperTick) {
throw new Error("lowerTick should be less than upperTick");
}
@@ -165,6 +168,41 @@ export class Wrap {
}
+ public static quoteSingle = async (fee: FeeAmount, specified_token: string, amount: bigint, account: AccountInterface): Promise => {
+ this.QuoterContract.connect(account);
+ const sortedTokens: Contract[] = [Wrap.ERC20Contract, Wrap.WERC20Contract].sort((a, b) => a.address.localeCompare(b.address));
+ let tmp = {
+ amount: {
+ mag: amount,
+ sign: false
+ },
+ specified_token: specified_token,
+ pool_key: {
+ token0: sortedTokens[0].address,
+ token1: sortedTokens[1].address,
+ fee: Wrap.getFeeX128(fee),
+ tick_spacing: 200,
+ extension: 0,
+ },
+ };
+ try {
+ const res = await Wrap.QuoterContract.quote_single(tmp);
+ return res;
+ } catch (error: any) {
+ let inputString = error.toString();
+ const substringToFind = "0x3f532df6e73f94d604f4eb8c661635595c91adc1d387931451eacd418cfbd14";
+ const substringStartIndex = inputString.indexOf(substringToFind);
+
+ if (substringStartIndex !== -1) {
+ const startIndex = substringStartIndex + substringToFind.length + 2; // Skip the substring and the following comma and whitespace
+ const endIndex = inputString.indexOf(",", startIndex);
+ const extractedString = inputString.substring(startIndex, endIndex).trim();
+ return extractedString;
+ }
+ return 0;
+ }
+ }
+
public swapFromERC1155ToERC20ByAVNU(erc1155AmountIn: BigNumberish, minERC20AmountOut: BigNumberish, aggregatorAddress: string, userAddress: string, fee: FeeAmount, slippage: number, currentPrice: number) {
// sort tokens
// TODO check length
@@ -385,19 +423,20 @@ export class Wrap {
// TODO check length
const sortedTokens: Contract[] = [Wrap.ERC20Contract, Wrap.WERC20Contract].sort((a, b) => a.address.localeCompare(b.address));
+ let tmp = {
+ pool_key: {
+ token0: sortedTokens[0].address,
+ token1: sortedTokens[1].address,
+ fee: Wrap.getFeeX128(fee),
+ tick_spacing: 200,
+ extension: 0,
+ },
+ initial_tick
+ };
const mayInitializePool: Call = {
contractAddress: Wrap.EkuboCoreContract.address,
entrypoint: "maybe_initialize_pool",
- calldata: CallData.compile({
- pool_key: {
- token0: sortedTokens[0].address,
- token1: sortedTokens[1].address,
- fee: Wrap.getFeeX128(fee),
- tick_spacing: 200,
- extension: 0,
- },
- initial_tick
- })
+ calldata: CallData.compile(tmp)
}
return [mayInitializePool];
}