@hyperbridge/sdk 2.8.8 → 2.8.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/index.d.ts +406 -18
- package/dist/browser/index.js +724 -58
- package/dist/browser/index.js.map +1 -1
- package/dist/node/index.cjs +729 -54
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.d.cts +8 -5
- package/dist/node/index.d.ts +8 -5
- package/dist/node/index.js +724 -58
- package/dist/node/index.js.map +1 -1
- package/dist/node/{intents-helpers-BCsIHRts.d.cts → intents-helpers-BKj42imQ.d.cts} +721 -225
- package/dist/node/{intents-helpers-BCsIHRts.d.ts → intents-helpers-BKj42imQ.d.ts} +721 -225
- package/dist/node/intents-helpers.cjs +104 -27
- package/dist/node/intents-helpers.cjs.map +1 -1
- package/dist/node/intents-helpers.d.cts +2 -1
- package/dist/node/intents-helpers.d.ts +2 -1
- package/dist/node/intents-helpers.js +100 -29
- package/dist/node/intents-helpers.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { bR as AggregationLogger, bS as BidNonceKeyFn, bT as BidSignature, bU as ENTRY_POINT_V08_ADDRESS, bV as FILL_ORDER_ABI, ai as FILL_ORDER_V1_ABI, bW as FetchLike, bX as FillData, bY as HexString, bZ as IntentGatewayV2, b_ as LpBalance, b$ as OrderCommitmentFn, aN as PHANTOM_QUOTE_HAIRCUT_BPS, c0 as PhantomAggregation, c1 as PhantomLegAggregation, c2 as PhantomLegBidder, c3 as PoolAndPositionInfo, c4 as RecoverBidSigner, c5 as RpcBidInfo, c6 as SolverBalanceReader, c7 as SolverV4Position, bf as UNISWAP_QUOTE_HAIRCUT_BPS, c8 as UniswapV4Contracts, c9 as V4PositionState, ca as YieldVaultMap, cb as aggregatePhantomBids, bh as applyPhantomQuoteHaircut, bi as applyUniswapQuoteHaircut, br as decodeAcceptedSourceChains, bs as decodeERC7821ExecuteBatch, bu as decodePhantomBidDeclaration, bv as decodeUserOpScale, bx as encodeAcceptedSourceChains, by as encodeERC7821ExecuteBatch, bB as encodePhantomBidDeclaration, bC as encodeUserOpScale, cc as extractFillData, cd as fetchBidsForOrder, ce as getTotalSolverBalance, cf as memoizedSolverBalance, cg as orderCommitmentFromDecoded, bM as poolSlug, ch as positionAmountOfToken, ci as readV4Position, cj as recoverBidSignerViem, ck as setAggregationFetch, bO as sortPoolSymbols, cl as splitBidSignature, cm as weightedMedian, cn as zipFillLegs } from './intents-helpers-BKj42imQ.cjs';
|
|
2
2
|
import 'consola';
|
|
3
3
|
import 'decimal.js';
|
|
4
4
|
import 'graphql-request';
|
|
@@ -6,3 +6,4 @@ import 'viem';
|
|
|
6
6
|
import 'viem/chains';
|
|
7
7
|
import '@polkadot/api';
|
|
8
8
|
import '@polkadot/keyring/types';
|
|
9
|
+
import '@polkadot/types/interfaces';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { bR as AggregationLogger, bS as BidNonceKeyFn, bT as BidSignature, bU as ENTRY_POINT_V08_ADDRESS, bV as FILL_ORDER_ABI, ai as FILL_ORDER_V1_ABI, bW as FetchLike, bX as FillData, bY as HexString, bZ as IntentGatewayV2, b_ as LpBalance, b$ as OrderCommitmentFn, aN as PHANTOM_QUOTE_HAIRCUT_BPS, c0 as PhantomAggregation, c1 as PhantomLegAggregation, c2 as PhantomLegBidder, c3 as PoolAndPositionInfo, c4 as RecoverBidSigner, c5 as RpcBidInfo, c6 as SolverBalanceReader, c7 as SolverV4Position, bf as UNISWAP_QUOTE_HAIRCUT_BPS, c8 as UniswapV4Contracts, c9 as V4PositionState, ca as YieldVaultMap, cb as aggregatePhantomBids, bh as applyPhantomQuoteHaircut, bi as applyUniswapQuoteHaircut, br as decodeAcceptedSourceChains, bs as decodeERC7821ExecuteBatch, bu as decodePhantomBidDeclaration, bv as decodeUserOpScale, bx as encodeAcceptedSourceChains, by as encodeERC7821ExecuteBatch, bB as encodePhantomBidDeclaration, bC as encodeUserOpScale, cc as extractFillData, cd as fetchBidsForOrder, ce as getTotalSolverBalance, cf as memoizedSolverBalance, cg as orderCommitmentFromDecoded, bM as poolSlug, ch as positionAmountOfToken, ci as readV4Position, cj as recoverBidSignerViem, ck as setAggregationFetch, bO as sortPoolSymbols, cl as splitBidSignature, cm as weightedMedian, cn as zipFillLegs } from './intents-helpers-BKj42imQ.js';
|
|
2
2
|
import 'consola';
|
|
3
3
|
import 'decimal.js';
|
|
4
4
|
import 'graphql-request';
|
|
@@ -6,3 +6,4 @@ import 'viem';
|
|
|
6
6
|
import 'viem/chains';
|
|
7
7
|
import '@polkadot/api';
|
|
8
8
|
import '@polkadot/keyring/types';
|
|
9
|
+
import '@polkadot/types/interfaces';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { keccak256, toHex, encodeAbiParameters, encodeFunctionData, decodeFunctionData, decodeAbiParameters, numberToBytes, bytesToBigInt, recoverAddress, parseAbiParameters, concat, encodePacked, pad } from 'viem';
|
|
2
2
|
import '@polkadot/api';
|
|
3
|
-
import {
|
|
4
|
-
import '@polkadot/util-crypto';
|
|
3
|
+
import { u8aToHex, u8aConcat, hexToU8a, stringToU8a, isHex, u8aToString } from '@polkadot/util';
|
|
4
|
+
import { xxhashAsU8a } from '@polkadot/util-crypto';
|
|
5
5
|
import { Struct, Vector, u8, Bytes } from 'scale-ts';
|
|
6
6
|
import 'p-queue';
|
|
7
7
|
import { privateKeyToAccount } from 'viem/accounts';
|
|
@@ -576,6 +576,11 @@ var ABI2 = [
|
|
|
576
576
|
type: "uint256",
|
|
577
577
|
internalType: "uint256"
|
|
578
578
|
},
|
|
579
|
+
{
|
|
580
|
+
name: "validUntil",
|
|
581
|
+
type: "uint256",
|
|
582
|
+
internalType: "uint256"
|
|
583
|
+
},
|
|
579
584
|
{
|
|
580
585
|
name: "outputs",
|
|
581
586
|
type: "tuple[]",
|
|
@@ -1886,6 +1891,7 @@ var ABI2 = [
|
|
|
1886
1891
|
var IntentGatewayV2_default = { ABI: ABI2 };
|
|
1887
1892
|
|
|
1888
1893
|
// src/chains/intentsCoprocessor.ts
|
|
1894
|
+
u8aToHex(u8aConcat(xxhashAsU8a("System", 128), xxhashAsU8a("Events", 128)));
|
|
1889
1895
|
new TextEncoder().encode("intents::bid::");
|
|
1890
1896
|
new TextEncoder().encode("intents::phantom::order::");
|
|
1891
1897
|
Struct({ filler: Bytes(32), user_op: Vector(u8) });
|
|
@@ -1945,6 +1951,53 @@ function sortPoolSymbols(symbolA, symbolB) {
|
|
|
1945
1951
|
function poolSlug(symbolA, symbolB) {
|
|
1946
1952
|
return sortPoolSymbols(symbolA, symbolB).join("-");
|
|
1947
1953
|
}
|
|
1954
|
+
var FILL_ORDER_V1_ABI = [
|
|
1955
|
+
{
|
|
1956
|
+
type: "function",
|
|
1957
|
+
name: "fillOrder",
|
|
1958
|
+
stateMutability: "payable",
|
|
1959
|
+
outputs: [],
|
|
1960
|
+
inputs: [
|
|
1961
|
+
ABI2.find((e) => e.type === "function" && e.name === "fillOrder").inputs[0],
|
|
1962
|
+
{
|
|
1963
|
+
name: "options",
|
|
1964
|
+
type: "tuple",
|
|
1965
|
+
internalType: "struct FillOptions",
|
|
1966
|
+
components: [
|
|
1967
|
+
{ name: "relayerFee", type: "uint256", internalType: "uint256" },
|
|
1968
|
+
{ name: "nativeDispatchFee", type: "uint256", internalType: "uint256" },
|
|
1969
|
+
{
|
|
1970
|
+
name: "outputs",
|
|
1971
|
+
type: "tuple[]",
|
|
1972
|
+
internalType: "struct TokenInfo[]",
|
|
1973
|
+
components: [
|
|
1974
|
+
{ name: "token", type: "bytes32", internalType: "bytes32" },
|
|
1975
|
+
{ name: "amount", type: "uint256", internalType: "uint256" }
|
|
1976
|
+
]
|
|
1977
|
+
}
|
|
1978
|
+
]
|
|
1979
|
+
}
|
|
1980
|
+
]
|
|
1981
|
+
}
|
|
1982
|
+
];
|
|
1983
|
+
function decodeFillOrder(data) {
|
|
1984
|
+
try {
|
|
1985
|
+
const decoded = decodeFunctionData({ abi: ABI2, data });
|
|
1986
|
+
if (decoded.functionName === "fillOrder" && decoded.args && decoded.args.length >= 2) {
|
|
1987
|
+
return { order: decoded.args[0], options: decoded.args[1] };
|
|
1988
|
+
}
|
|
1989
|
+
} catch {
|
|
1990
|
+
}
|
|
1991
|
+
try {
|
|
1992
|
+
const decoded = decodeFunctionData({ abi: FILL_ORDER_V1_ABI, data });
|
|
1993
|
+
if (decoded.functionName === "fillOrder" && decoded.args && decoded.args.length >= 2) {
|
|
1994
|
+
const legacy = decoded.args[1];
|
|
1995
|
+
return { order: decoded.args[0], options: { ...legacy, validUntil: 0n } };
|
|
1996
|
+
}
|
|
1997
|
+
} catch {
|
|
1998
|
+
}
|
|
1999
|
+
return null;
|
|
2000
|
+
}
|
|
1948
2001
|
var SELECT_SOLVER_TYPEHASH = keccak256(toHex("SelectSolver(bytes32 commitment,address solver)"));
|
|
1949
2002
|
var PACKED_USEROP_TYPEHASH = keccak256(
|
|
1950
2003
|
toHex(
|
|
@@ -2591,9 +2644,16 @@ function zipFillLegs(assets, outputs) {
|
|
|
2591
2644
|
};
|
|
2592
2645
|
});
|
|
2593
2646
|
}
|
|
2594
|
-
var UNISWAP_QUOTE_HAIRCUT_BPS =
|
|
2647
|
+
var UNISWAP_QUOTE_HAIRCUT_BPS = 10n;
|
|
2648
|
+
var PHANTOM_QUOTE_HAIRCUT_BPS = 5n;
|
|
2649
|
+
function haircut(amount, bps) {
|
|
2650
|
+
return amount * (10000n - bps) / 10000n;
|
|
2651
|
+
}
|
|
2595
2652
|
function applyUniswapQuoteHaircut(amount) {
|
|
2596
|
-
return amount
|
|
2653
|
+
return haircut(amount, UNISWAP_QUOTE_HAIRCUT_BPS);
|
|
2654
|
+
}
|
|
2655
|
+
function applyPhantomQuoteHaircut(amount) {
|
|
2656
|
+
return haircut(amount, PHANTOM_QUOTE_HAIRCUT_BPS);
|
|
2597
2657
|
}
|
|
2598
2658
|
function weightedMedian(entries) {
|
|
2599
2659
|
const sorted = [...entries].sort((a, b) => a.price < b.price ? -1 : a.price > b.price ? 1 : 0);
|
|
@@ -2616,10 +2676,10 @@ function extractFillData(callData, gatewayAddress) {
|
|
|
2616
2676
|
for (const call of calls) {
|
|
2617
2677
|
if (call.target.toLowerCase() !== normalized) continue;
|
|
2618
2678
|
try {
|
|
2619
|
-
const decoded =
|
|
2620
|
-
if (
|
|
2621
|
-
const order = decoded.
|
|
2622
|
-
const options = decoded.
|
|
2679
|
+
const decoded = decodeFillOrder(call.data);
|
|
2680
|
+
if (!decoded) continue;
|
|
2681
|
+
const order = decoded.order;
|
|
2682
|
+
const options = decoded.options;
|
|
2623
2683
|
const assets = order?.output?.assets;
|
|
2624
2684
|
const outputs = options?.outputs;
|
|
2625
2685
|
if (!assets?.length || !outputs?.length) continue;
|
|
@@ -2746,12 +2806,12 @@ async function fetchBidsForOrder(nodeUrl, commitment) {
|
|
|
2746
2806
|
});
|
|
2747
2807
|
return Array.isArray(data.result) ? data.result : [];
|
|
2748
2808
|
}
|
|
2749
|
-
async function ethCallUint(evmRpcUrl, to, data) {
|
|
2809
|
+
async function ethCallUint(evmRpcUrl, to, data, blockTag = "latest") {
|
|
2750
2810
|
const result = await rpcCall(evmRpcUrl, {
|
|
2751
2811
|
id: 1,
|
|
2752
2812
|
jsonrpc: "2.0",
|
|
2753
2813
|
method: "eth_call",
|
|
2754
|
-
params: [{ to, data },
|
|
2814
|
+
params: [{ to, data }, blockTag]
|
|
2755
2815
|
});
|
|
2756
2816
|
if (result.result === "0x") return 0n;
|
|
2757
2817
|
if (typeof result.result !== "string") {
|
|
@@ -2759,12 +2819,12 @@ async function ethCallUint(evmRpcUrl, to, data) {
|
|
|
2759
2819
|
}
|
|
2760
2820
|
return BigInt(result.result);
|
|
2761
2821
|
}
|
|
2762
|
-
async function getTotalSolverBalance(evmRpcUrl, chain, token, solver, yieldVaults) {
|
|
2822
|
+
async function getTotalSolverBalance(evmRpcUrl, chain, token, solver, yieldVaults, blockTag = "latest") {
|
|
2763
2823
|
const padded = solver.replace("0x", "").padStart(64, "0");
|
|
2764
|
-
const raw = await ethCallUint(evmRpcUrl, token, `0x70a08231${padded}
|
|
2824
|
+
const raw = await ethCallUint(evmRpcUrl, token, `0x70a08231${padded}`, blockTag);
|
|
2765
2825
|
const vaults = yieldVaults[chain]?.[token.toLowerCase()] ?? [];
|
|
2766
2826
|
const vaultBalances = await Promise.all(
|
|
2767
|
-
vaults.map((v) => ethCallUint(evmRpcUrl, v, `0xce96cb77${padded}
|
|
2827
|
+
vaults.map((v) => ethCallUint(evmRpcUrl, v, `0xce96cb77${padded}`, blockTag))
|
|
2768
2828
|
// maxWithdraw(address)
|
|
2769
2829
|
);
|
|
2770
2830
|
return vaultBalances.reduce((acc, b) => acc + b, raw);
|
|
@@ -2774,13 +2834,14 @@ var SELECTOR_POSITION_LIQUIDITY = "0x1efeed33";
|
|
|
2774
2834
|
var SELECTOR_POOL_AND_POSITION_INFO = "0x7ba03aad";
|
|
2775
2835
|
var SELECTOR_GET_SLOT0 = "0xc815641c";
|
|
2776
2836
|
var uint256Arg = (value) => value.toString(16).padStart(64, "0");
|
|
2777
|
-
async function readV4Position(
|
|
2837
|
+
async function readV4Position(params) {
|
|
2838
|
+
const { evmRpcUrl, contracts, tokenId, keccak, blockTag = "latest", logger } = params;
|
|
2778
2839
|
const call = async (to, data) => {
|
|
2779
2840
|
const result = await rpcCall(evmRpcUrl, {
|
|
2780
2841
|
id: 1,
|
|
2781
2842
|
jsonrpc: "2.0",
|
|
2782
2843
|
method: "eth_call",
|
|
2783
|
-
params: [{ to, data },
|
|
2844
|
+
params: [{ to, data }, blockTag]
|
|
2784
2845
|
});
|
|
2785
2846
|
if (result.result === "0x") return null;
|
|
2786
2847
|
if (typeof result.result !== "string") {
|
|
@@ -2824,7 +2885,7 @@ function memoizedV4Position(keccak, logger) {
|
|
|
2824
2885
|
const key = `${chain}|${tokenId}`;
|
|
2825
2886
|
let pending = cache.get(key);
|
|
2826
2887
|
if (!pending) {
|
|
2827
|
-
pending = readV4Position(evmRpcUrl, contracts, tokenId, keccak, logger).catch((err) => {
|
|
2888
|
+
pending = readV4Position({ evmRpcUrl, contracts, tokenId, keccak, logger }).catch((err) => {
|
|
2828
2889
|
cache.delete(key);
|
|
2829
2890
|
throw err;
|
|
2830
2891
|
});
|
|
@@ -2833,13 +2894,13 @@ function memoizedV4Position(keccak, logger) {
|
|
|
2833
2894
|
return pending;
|
|
2834
2895
|
};
|
|
2835
2896
|
}
|
|
2836
|
-
function memoizedSolverBalance(yieldVaults) {
|
|
2897
|
+
function memoizedSolverBalance(yieldVaults, blockTags = {}) {
|
|
2837
2898
|
const cache = /* @__PURE__ */ new Map();
|
|
2838
2899
|
return (evmRpcUrl, chain, token, solver) => {
|
|
2839
2900
|
const key = `${chain}|${token.toLowerCase()}|${solver.toLowerCase()}`;
|
|
2840
2901
|
let pending = cache.get(key);
|
|
2841
2902
|
if (!pending) {
|
|
2842
|
-
pending = getTotalSolverBalance(evmRpcUrl, chain, token, solver, yieldVaults).catch((err) => {
|
|
2903
|
+
pending = getTotalSolverBalance(evmRpcUrl, chain, token, solver, yieldVaults, blockTags[chain]).catch((err) => {
|
|
2843
2904
|
cache.delete(key);
|
|
2844
2905
|
throw err;
|
|
2845
2906
|
});
|
|
@@ -2914,6 +2975,7 @@ async function runAggregation(params, isDelegated) {
|
|
|
2914
2975
|
const getBalance = params.getBalance ?? memoizedSolverBalance(yieldVaults);
|
|
2915
2976
|
const quotesByLeg = /* @__PURE__ */ new Map();
|
|
2916
2977
|
const lpBalances = [];
|
|
2978
|
+
const verifiedPositions = [];
|
|
2917
2979
|
const countedSolvers = /* @__PURE__ */ new Set();
|
|
2918
2980
|
for (const bid of bids) {
|
|
2919
2981
|
if (!bid.user_op) continue;
|
|
@@ -2954,23 +3016,32 @@ async function runAggregation(params, isDelegated) {
|
|
|
2954
3016
|
const declaration = decodePhantomBidDeclaration(decoded.paymasterAndData);
|
|
2955
3017
|
const acceptedSources = declaration.acceptedSources;
|
|
2956
3018
|
const poolPriced = declaration.uniswapV4Positions.length > 0;
|
|
2957
|
-
const
|
|
2958
|
-
|
|
2959
|
-
|
|
3019
|
+
const applyHaircut = poolPriced ? applyUniswapQuoteHaircut : applyPhantomQuoteHaircut;
|
|
3020
|
+
const quotedLegs = [...fillData.legs.entries()].map(([legIndex, leg]) => [
|
|
3021
|
+
legIndex,
|
|
3022
|
+
{ ...leg, solverAmount: applyHaircut(leg.solverAmount) }
|
|
3023
|
+
]).filter(([, leg]) => leg.solverAmount !== 0n);
|
|
2960
3024
|
const declaredPositions = v4Contracts ? declaration.uniswapV4Positions : [];
|
|
2961
|
-
const
|
|
2962
|
-
declaredPositions.map((tokenId) =>
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
3025
|
+
const ownedPositions = (await Promise.all(
|
|
3026
|
+
declaredPositions.map(async (tokenId) => ({
|
|
3027
|
+
tokenId,
|
|
3028
|
+
state: await readPosition(destUrl, chain, v4Contracts, tokenId)
|
|
3029
|
+
}))
|
|
3030
|
+
)).filter((entry) => {
|
|
3031
|
+
if (!entry.state) return false;
|
|
3032
|
+
if (entry.state.owner !== normalizedSolver) {
|
|
2966
3033
|
logger?.warn(
|
|
2967
|
-
{ solver, commitment, tokenId:
|
|
3034
|
+
{ solver, commitment, tokenId: entry.tokenId.toString(), owner: entry.state.owner },
|
|
2968
3035
|
"Ignoring declared Uniswap V4 position: not owned by the bidding solver"
|
|
2969
3036
|
);
|
|
2970
3037
|
return false;
|
|
2971
3038
|
}
|
|
2972
3039
|
return true;
|
|
2973
3040
|
});
|
|
3041
|
+
const positions = ownedPositions.map((entry) => entry.state);
|
|
3042
|
+
for (const entry of ownedPositions) {
|
|
3043
|
+
verifiedPositions.push({ solver: normalizedSolver, chain, tokenId: entry.tokenId });
|
|
3044
|
+
}
|
|
2974
3045
|
const weights = await Promise.all(
|
|
2975
3046
|
// Price influence: the solver's liquidity in THIS leg's output token on the destination
|
|
2976
3047
|
// chain, so a leg is weighted by the inventory that actually backs it.
|
|
@@ -3030,9 +3101,9 @@ async function runAggregation(params, isDelegated) {
|
|
|
3030
3101
|
}
|
|
3031
3102
|
];
|
|
3032
3103
|
});
|
|
3033
|
-
return { legs, lpBalances };
|
|
3104
|
+
return { legs, lpBalances, positions: verifiedPositions, solvers: [...countedSolvers] };
|
|
3034
3105
|
}
|
|
3035
3106
|
|
|
3036
|
-
export { ENTRY_POINT_V08_ADDRESS, FILL_ORDER_ABI, IntentGatewayV2_default as IntentGatewayV2, UNISWAP_QUOTE_HAIRCUT_BPS, aggregatePhantomBids, applyUniswapQuoteHaircut, decodeAcceptedSourceChains, decodeERC7821ExecuteBatch, decodePhantomBidDeclaration, decodeUserOpScale, encodeAcceptedSourceChains, encodeERC7821ExecuteBatch, encodePhantomBidDeclaration, encodeUserOpScale, extractFillData, fetchBidsForOrder, memoizedSolverBalance, orderCommitmentFromDecoded, poolSlug, recoverBidSignerViem, setAggregationFetch, sortPoolSymbols, splitBidSignature, weightedMedian, zipFillLegs };
|
|
3107
|
+
export { ENTRY_POINT_V08_ADDRESS, FILL_ORDER_ABI, FILL_ORDER_V1_ABI, IntentGatewayV2_default as IntentGatewayV2, PHANTOM_QUOTE_HAIRCUT_BPS, UNISWAP_QUOTE_HAIRCUT_BPS, aggregatePhantomBids, applyPhantomQuoteHaircut, applyUniswapQuoteHaircut, decodeAcceptedSourceChains, decodeERC7821ExecuteBatch, decodePhantomBidDeclaration, decodeUserOpScale, encodeAcceptedSourceChains, encodeERC7821ExecuteBatch, encodePhantomBidDeclaration, encodeUserOpScale, extractFillData, fetchBidsForOrder, getTotalSolverBalance, memoizedSolverBalance, orderCommitmentFromDecoded, poolSlug, positionAmountOfToken, readV4Position, recoverBidSignerViem, setAggregationFetch, sortPoolSymbols, splitBidSignature, weightedMedian, zipFillLegs };
|
|
3037
3108
|
//# sourceMappingURL=intents-helpers.js.map
|
|
3038
3109
|
//# sourceMappingURL=intents-helpers.js.map
|