mirror of
https://github.com/orionprotocol/sdk.git
synced 2026-03-29 00:57:56 +03:00
add lock order support
This commit is contained in:
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@orionprotocol/sdk",
|
||||
"version": "0.20.42-rc3",
|
||||
"version": "0.20.42-rc4",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@orionprotocol/sdk",
|
||||
"version": "0.20.42-rc3",
|
||||
"version": "0.20.42-rc4",
|
||||
"hasInstallScript": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@orionprotocol/sdk",
|
||||
"version": "0.20.42-rc3",
|
||||
"version": "0.20.42-rc4",
|
||||
"description": "Orion Protocol SDK",
|
||||
"main": "./lib/index.cjs",
|
||||
"module": "./lib/index.js",
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Exchange__factory } from '@orionprotocol/contracts/lib/ethers-v6/index.
|
||||
import getBalances from '../../utils/getBalances.js';
|
||||
import BalanceGuard from '../../BalanceGuard.js';
|
||||
import getAvailableSources from '../../utils/getAvailableFundsSources.js';
|
||||
import { INTERNAL_PROTOCOL_PRECISION, NATIVE_CURRENCY_PRECISION, SWAP_THROUGH_ORION_POOL_GAS_LIMIT } from '../../constants/index.js';
|
||||
import { INTERNAL_PROTOCOL_PRECISION, NATIVE_CURRENCY_PRECISION, SWAP_THROUGH_ORION_POOL_GAS_LIMIT } from '../../constants';
|
||||
import getNativeCryptocurrencyName from '../../utils/getNativeCryptocurrencyName.js';
|
||||
import { calculateFeeInFeeAsset, denormalizeNumber, normalizeNumber } from '../../utils/index.js';
|
||||
import { signOrder } from '../../crypt/index.js';
|
||||
@@ -37,7 +37,6 @@ type PoolSwap = {
|
||||
|
||||
export type Swap = AggregatorOrder | PoolSwap;
|
||||
|
||||
|
||||
const isValidSingleSwap = (singleSwap: Omit<SingleSwap, 'factory'> & { factory: string }): singleSwap is SingleSwap => {
|
||||
return isValidFactory(singleSwap.factory);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ const hashOrder = (order: Order) => ethers.solidityPackedKeccak256(
|
||||
'uint64',
|
||||
'uint64',
|
||||
'uint64',
|
||||
'uint64',
|
||||
'uint64',
|
||||
'uint8',
|
||||
],
|
||||
[
|
||||
@@ -24,10 +26,12 @@ const hashOrder = (order: Order) => ethers.solidityPackedKeccak256(
|
||||
order.quoteAsset,
|
||||
order.matcherFeeAsset,
|
||||
order.amount,
|
||||
order.targetChainId,
|
||||
order.price,
|
||||
order.matcherFee,
|
||||
order.nonce,
|
||||
order.expiration,
|
||||
order.secretHash,
|
||||
order.buySide === 1 ? '0x01' : '0x00',
|
||||
],
|
||||
);
|
||||
|
||||
71
src/crypt/signLockOrder.ts
Normal file
71
src/crypt/signLockOrder.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import type { TypedDataSigner } from '@ethersproject/abstract-signer';
|
||||
import { ethers } from 'ethers';
|
||||
import ORDER_TYPES from '../constants/orderTypes.js';
|
||||
import type { LockOrder, SignedLockOrder, SupportedChainId } from '../types.js';
|
||||
import getDomainData from './getDomainData.js';
|
||||
import generateSecret from '../utils/generateSecret';
|
||||
|
||||
const DEFAULT_EXPIRATION = 29 * 24 * 60 * 60 * 1000; // 29 days
|
||||
|
||||
type SignerWithTypedDataSign = ethers.Signer & TypedDataSigner;
|
||||
|
||||
export type LockOrderProps = {
|
||||
userAddress: string // адрес юзера который хочет сделать лок
|
||||
senderAddress: string // broker
|
||||
asset: string
|
||||
amount: ethers.BigNumberish
|
||||
targetChainId: ethers.BigNumberish
|
||||
sign: string // подпись юзера
|
||||
signer: ethers.Signer
|
||||
chainId: SupportedChainId
|
||||
}
|
||||
|
||||
export const signLockOrder = async ({
|
||||
userAddress,
|
||||
senderAddress,
|
||||
sign,
|
||||
amount,
|
||||
targetChainId,
|
||||
asset,
|
||||
chainId,
|
||||
signer
|
||||
}: LockOrderProps) => {
|
||||
const nonce = Date.now();
|
||||
const expiration = nonce + DEFAULT_EXPIRATION;
|
||||
const secret = generateSecret();
|
||||
const secretHash = ethers.keccak256(secret);
|
||||
|
||||
const order: LockOrder = {
|
||||
user: userAddress,
|
||||
sender: senderAddress,
|
||||
expiration,
|
||||
asset,
|
||||
amount,
|
||||
targetChainId,
|
||||
secretHash,
|
||||
sign
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
const typedDataSigner = signer as SignerWithTypedDataSign;
|
||||
|
||||
const signature = await typedDataSigner.signTypedData(
|
||||
getDomainData(chainId),
|
||||
ORDER_TYPES,
|
||||
order,
|
||||
);
|
||||
|
||||
// https://github.com/poap-xyz/poap-fun/pull/62#issue-928290265
|
||||
// "Signature's v was always send as 27 or 28, but from Ledger was 0 or 1"
|
||||
const fixedSignature = ethers.Signature.from(signature).serialized;
|
||||
|
||||
// if (!fixedSignature) throw new Error("Can't sign order");
|
||||
|
||||
const signedOrder: SignedLockOrder = {
|
||||
...order,
|
||||
signature: fixedSignature,
|
||||
secret
|
||||
};
|
||||
|
||||
return signedOrder;
|
||||
};
|
||||
@@ -1,12 +1,13 @@
|
||||
import type { TypedDataSigner } from '@ethersproject/abstract-signer';
|
||||
import { BigNumber } from 'bignumber.js';
|
||||
import { ethers } from 'ethers';
|
||||
import { INTERNAL_PROTOCOL_PRECISION } from '../constants/index.js';
|
||||
import { INTERNAL_PROTOCOL_PRECISION } from '../constants';
|
||||
import ORDER_TYPES from '../constants/orderTypes.js';
|
||||
import type { Order, SignedOrder, SupportedChainId } from '../types.js';
|
||||
import normalizeNumber from '../utils/normalizeNumber.js';
|
||||
import getDomainData from './getDomainData.js';
|
||||
import hashOrder from './hashOrder.js';
|
||||
import generateSecret from '../utils/generateSecret';
|
||||
|
||||
const DEFAULT_EXPIRATION = 29 * 24 * 60 * 60 * 1000; // 29 days
|
||||
|
||||
@@ -24,9 +25,12 @@ export const signOrder = async (
|
||||
serviceFeeAssetAddr: string,
|
||||
signer: ethers.Signer,
|
||||
chainId: SupportedChainId,
|
||||
targetChainId?: ethers.BigNumberish,
|
||||
) => {
|
||||
const nonce = Date.now();
|
||||
const expiration = nonce + DEFAULT_EXPIRATION;
|
||||
const secret = generateSecret();
|
||||
const secretHash = ethers.keccak256(secret);
|
||||
|
||||
const order: Order = {
|
||||
senderAddress,
|
||||
@@ -51,6 +55,8 @@ export const signOrder = async (
|
||||
)),
|
||||
nonce,
|
||||
expiration,
|
||||
secretHash,
|
||||
targetChainId,
|
||||
buySide: side === 'BUY' ? 1 : 0,
|
||||
};
|
||||
|
||||
@@ -73,6 +79,7 @@ export const signOrder = async (
|
||||
...order,
|
||||
id: hashOrder(order),
|
||||
signature: fixedSignature,
|
||||
secret
|
||||
};
|
||||
return signedOrder;
|
||||
};
|
||||
|
||||
@@ -8,12 +8,17 @@ import errorSchema from './schemas/errorSchema.js';
|
||||
import placeAtomicSwapSchema from './schemas/placeAtomicSwapSchema.js';
|
||||
import { AggregatorWS } from './ws';
|
||||
import { atomicSwapHistorySchema } from './schemas/atomicSwapHistorySchema.js';
|
||||
import type { BasicAuthCredentials, SignedCancelOrderRequest, SignedOrder } from '../../types.js';
|
||||
import type {
|
||||
BasicAuthCredentials,
|
||||
NetworkShortName,
|
||||
SignedCancelOrderRequest,
|
||||
SignedLockOrder,
|
||||
SignedOrder
|
||||
} from '../../types.js';
|
||||
import {
|
||||
pairConfigSchema, aggregatedOrderbookSchema,
|
||||
exchangeOrderbookSchema, poolReservesSchema,
|
||||
} from './schemas/index.js';
|
||||
import type networkCodes from '../../constants/networkCodes.js';
|
||||
import toUpperCase from '../../utils/toUpperCase.js';
|
||||
import httpToWS from '../../utils/httpToWS.js';
|
||||
import { ethers } from 'ethers';
|
||||
@@ -55,6 +60,7 @@ class Aggregator {
|
||||
this.getTradeProfits = this.getTradeProfits.bind(this);
|
||||
this.placeAtomicSwap = this.placeAtomicSwap.bind(this);
|
||||
this.placeOrder = this.placeOrder.bind(this);
|
||||
this.placeLockOrder = this.placeLockOrder.bind(this);
|
||||
this.cancelOrder = this.cancelOrder.bind(this);
|
||||
this.checkWhitelisted = this.checkWhitelisted.bind(this);
|
||||
this.getLockedBalance = this.getLockedBalance.bind(this);
|
||||
@@ -236,6 +242,39 @@ class Aggregator {
|
||||
);
|
||||
};
|
||||
|
||||
placeLockOrder = (
|
||||
signedLockOrder: SignedLockOrder,
|
||||
rawExchangeRestrictions?: string | undefined,
|
||||
) => {
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
...this.basicAuthHeaders,
|
||||
};
|
||||
|
||||
const url = new URL(`${this.apiUrl}/api/v1/cross-chain`);
|
||||
|
||||
return fetchWithValidation(
|
||||
url.toString(),
|
||||
z.object({
|
||||
orderId: z.string(),
|
||||
placementRequests: z.array(
|
||||
z.object({
|
||||
amount: z.number(),
|
||||
brokerAddress: z.string(),
|
||||
exchange: z.string(),
|
||||
}),
|
||||
).optional(),
|
||||
}),
|
||||
{
|
||||
headers,
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ ...signedLockOrder, rawExchangeRestrictions }),
|
||||
},
|
||||
errorSchema,
|
||||
);
|
||||
};
|
||||
|
||||
cancelOrder = (signedCancelOrderRequest: SignedCancelOrderRequest) => fetchWithValidation(
|
||||
`${this.apiUrl}/api/v1/order`,
|
||||
cancelOrderSchema,
|
||||
@@ -291,7 +330,7 @@ class Aggregator {
|
||||
);
|
||||
};
|
||||
|
||||
getCrossChainAssetsByNetwork = (sourceChain: Uppercase<typeof networkCodes[number]>) => {
|
||||
getCrossChainAssetsByNetwork = (sourceChain: NetworkShortName) => {
|
||||
const url = new URL(`${this.apiUrl}/api/v1/cross-chain/assets`);
|
||||
url.searchParams.append('sourceChain', sourceChain);
|
||||
|
||||
@@ -354,7 +393,7 @@ class Aggregator {
|
||||
*/
|
||||
placeAtomicSwap = (
|
||||
secretHash: string,
|
||||
sourceNetworkCode: Uppercase<typeof networkCodes[number]>,
|
||||
sourceNetworkCode: NetworkShortName,
|
||||
) => fetchWithValidation(
|
||||
`${this.apiUrl}/api/v1/atomic-swap`,
|
||||
placeAtomicSwapSchema,
|
||||
@@ -376,6 +415,7 @@ class Aggregator {
|
||||
/**
|
||||
* Get placed atomic swaps. Each atomic swap received from this list has a target chain corresponding to this Aggregator
|
||||
* @param sender Sender address
|
||||
* @param limit
|
||||
* @returns Fetch promise
|
||||
*/
|
||||
getHistoryAtomicSwaps = (sender: string, limit = 1000) => {
|
||||
|
||||
@@ -82,22 +82,22 @@ type SwapInfoSubscription = {
|
||||
type AddressUpdateUpdate = {
|
||||
kind: 'update'
|
||||
balances: Partial<
|
||||
Record<
|
||||
string,
|
||||
Balance
|
||||
Record<
|
||||
string,
|
||||
Balance
|
||||
>
|
||||
>
|
||||
>
|
||||
order?: z.infer<typeof orderUpdateSchema> | z.infer<typeof fullOrderSchema> | undefined
|
||||
}
|
||||
|
||||
type AddressUpdateInitial = {
|
||||
kind: 'initial'
|
||||
balances: Partial<
|
||||
Record<
|
||||
string,
|
||||
Balance
|
||||
Record<
|
||||
string,
|
||||
Balance
|
||||
>
|
||||
>
|
||||
>
|
||||
orders?: Array<z.infer<typeof fullOrderSchema>> | undefined // The field is not defined if the user has no orders
|
||||
}
|
||||
|
||||
@@ -122,22 +122,22 @@ const exclusiveSubscriptions = [
|
||||
] as const;
|
||||
|
||||
type BufferLike =
|
||||
| string
|
||||
| Buffer
|
||||
| DataView
|
||||
| number
|
||||
| ArrayBufferView
|
||||
| Uint8Array
|
||||
| ArrayBuffer
|
||||
| SharedArrayBuffer
|
||||
| readonly unknown[]
|
||||
| readonly number[]
|
||||
| { valueOf: () => ArrayBuffer }
|
||||
| { valueOf: () => SharedArrayBuffer }
|
||||
| { valueOf: () => Uint8Array }
|
||||
| { valueOf: () => readonly number[] }
|
||||
| { valueOf: () => string }
|
||||
| { [Symbol.toPrimitive]: (hint: string) => string };
|
||||
| string
|
||||
| Buffer
|
||||
| DataView
|
||||
| number
|
||||
| ArrayBufferView
|
||||
| Uint8Array
|
||||
| ArrayBuffer
|
||||
| SharedArrayBuffer
|
||||
| readonly unknown[]
|
||||
| readonly number[]
|
||||
| { valueOf: () => ArrayBuffer }
|
||||
| { valueOf: () => SharedArrayBuffer }
|
||||
| { valueOf: () => Uint8Array }
|
||||
| { valueOf: () => readonly number[] }
|
||||
| { valueOf: () => string }
|
||||
| { [Symbol.toPrimitive]: (hint: string) => string };
|
||||
|
||||
const isSubType = (subType: string): subType is keyof Subscription => Object.values(SubscriptionType).some((t) => t === subType);
|
||||
|
||||
@@ -201,6 +201,7 @@ class AggregatorWS {
|
||||
}
|
||||
|
||||
private messageQueue: BufferLike[] = [];
|
||||
|
||||
private sendWsMessage(message: BufferLike) {
|
||||
if (this.ws?.readyState === WebSocket.OPEN) {
|
||||
this.ws.send(message);
|
||||
@@ -228,6 +229,7 @@ class AggregatorWS {
|
||||
}
|
||||
|
||||
private hearbeatIntervalId: ReturnType<typeof setInterval> | undefined;
|
||||
|
||||
private setupHeartbeat() {
|
||||
const heartbeat = () => {
|
||||
if (this.isAlive) {
|
||||
@@ -328,11 +330,11 @@ class AggregatorWS {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns newest subscription id for given id. Subscription id can be changed during resubscription.
|
||||
* This function ensure that old subscription id will be replaced with newest one.
|
||||
* @param id Id of subscription
|
||||
* @returns Newest subscription id
|
||||
*/
|
||||
* Returns newest subscription id for given id. Subscription id can be changed during resubscription.
|
||||
* This function ensure that old subscription id will be replaced with newest one.
|
||||
* @param id Id of subscription
|
||||
* @returns Newest subscription id
|
||||
*/
|
||||
getNewestSubscriptionId(id: string): string {
|
||||
const newId = this.subIdReplacements[id];
|
||||
if (newId !== undefined && newId !== id) {
|
||||
|
||||
59
src/types.ts
59
src/types.ts
@@ -3,8 +3,10 @@ import type factories from './constants/factories.js';
|
||||
import type { BigNumber } from 'bignumber.js';
|
||||
import type subOrderStatuses from './constants/subOrderStatuses.js';
|
||||
import type positionStatuses from './constants/positionStatuses.js';
|
||||
import type { knownEnvs } from './config/schemas/index.js';
|
||||
import type { knownEnvs } from './config/schemas';
|
||||
import type getHistory from './Orion/bridge/getHistory.js';
|
||||
import type { ethers } from 'ethers';
|
||||
import type { networkCodes } from './constants';
|
||||
|
||||
export type DeepPartial<T> = T extends object ? {
|
||||
[P in keyof T]?: DeepPartial<T[P]>;
|
||||
@@ -47,14 +49,33 @@ export type Order = {
|
||||
matcherFee: number // uint64
|
||||
nonce: number // uint64
|
||||
expiration: number // uint64
|
||||
secretHash: string // uint64
|
||||
buySide: 0 | 1 // uint8, 1=buy, 0=sell
|
||||
targetChainId: ethers.BigNumberish | undefined // uint64
|
||||
}
|
||||
|
||||
export type SignedOrder = {
|
||||
id: string // hash of Order (it's not part of order structure in smart-contract)
|
||||
export type LockOrder = {
|
||||
user: string // address // адрес юзера который хочет сделать лок
|
||||
sender: string // address // broker
|
||||
expiration: ethers.BigNumberish // uint64
|
||||
asset: string // address(?)
|
||||
amount: ethers.BigNumberish // uint64
|
||||
targetChainId: ethers.BigNumberish // uint64
|
||||
secretHash: string // uint64
|
||||
sign: string // uint64 // подпись юзера
|
||||
}
|
||||
|
||||
type SignedOrderAdditionalProps = {
|
||||
signature: string // bytes
|
||||
secret: string
|
||||
needWithdraw?: boolean // bool (not supported yet by smart-contract)
|
||||
} & Order
|
||||
}
|
||||
|
||||
export type SignedOrder = SignedOrderAdditionalProps & Order & {
|
||||
id: string // hash of Order (it's not part of order structure in smart-contract)
|
||||
}
|
||||
|
||||
export type SignedLockOrder = SignedOrderAdditionalProps & LockOrder
|
||||
|
||||
export type CancelOrderRequest = {
|
||||
id: number | string
|
||||
@@ -99,6 +120,8 @@ export enum SupportedChainId {
|
||||
// BROKEN = '0',
|
||||
}
|
||||
|
||||
export type NetworkShortName = Uppercase<typeof networkCodes[number]>;
|
||||
|
||||
const balanceTypes = ['exchange', 'wallet'] as const;
|
||||
|
||||
export type Source = typeof balanceTypes[number];
|
||||
@@ -278,22 +301,22 @@ export type EnvConfig = {
|
||||
analyticsAPI: string
|
||||
referralAPI: string
|
||||
networks: Partial<
|
||||
Record<
|
||||
SupportedChainId,
|
||||
VerboseUnitConfig
|
||||
Record<
|
||||
SupportedChainId,
|
||||
VerboseUnitConfig
|
||||
>
|
||||
>
|
||||
>
|
||||
}
|
||||
export type AggregatedAssets = Partial<
|
||||
Record<
|
||||
string,
|
||||
Partial<
|
||||
Record<SupportedChainId, {
|
||||
address: string
|
||||
}>
|
||||
Record<
|
||||
string,
|
||||
Partial<
|
||||
Record<SupportedChainId, {
|
||||
address: string
|
||||
}>
|
||||
>
|
||||
>
|
||||
>
|
||||
>;
|
||||
>;
|
||||
|
||||
export type RedeemOrder = {
|
||||
sender: string
|
||||
@@ -433,9 +456,9 @@ type BridgeHistory = Awaited<ReturnType<typeof getHistory>>;
|
||||
type BridgeHistoryItem = NonNullable<BridgeHistory[string]>;
|
||||
|
||||
export type AtomicSwap = Partial<
|
||||
Omit<BridgeHistoryItem, 'creationDate' | 'expiration' | 'secret'>
|
||||
Omit<BridgeHistoryItem, 'creationDate' | 'expiration' | 'secret'>
|
||||
> & Partial<
|
||||
Omit<AtomicSwapLocal, 'creationDate' | 'expiration' | 'secret'>
|
||||
Omit<AtomicSwapLocal, 'creationDate' | 'expiration' | 'secret'>
|
||||
> & {
|
||||
sourceChainId: SupportedChainId
|
||||
targetChainId: SupportedChainId
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { networkCodes } from '../constants/index.js';
|
||||
import toUpperCase from './toUpperCase.js';
|
||||
import type { NetworkShortName } from '../types';
|
||||
|
||||
const isUppercasedNetworkCode = (value: string): value is Uppercase<typeof networkCodes[number]> => networkCodes
|
||||
const isUppercasedNetworkCode = (value: string): value is NetworkShortName => networkCodes
|
||||
.map(toUpperCase).some((networkCode) => networkCode === value);
|
||||
|
||||
export default isUppercasedNetworkCode;
|
||||
|
||||
Reference in New Issue
Block a user