Added array schema failover / PF onOpen / bridge test / deps bump

This commit is contained in:
Aleksandr Kraiz
2023-03-06 20:47:34 +04:00
parent 31e6a863cc
commit 56cd1356f0
8 changed files with 318 additions and 1027 deletions

1205
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "@orionprotocol/sdk",
"version": "0.17.26",
"version": "0.17.27",
"description": "Orion Protocol SDK",
"main": "./lib/esm/index.js",
"module": "./lib/esm/index.js",
@@ -42,14 +42,14 @@
"@tsconfig/strictest": "^1.0.2",
"@types/express": "^4.17.17",
"@types/jest": "^29.4.0",
"@types/node": "^18.14.0",
"@types/node": "^18.14.6",
"@types/node-fetch": "^2.6.2",
"@types/socket.io-client": "1.4.33",
"@types/uuid": "^9.0.1",
"@types/ws": "^8.5.4",
"@typescript-eslint/eslint-plugin": "^5.53.0",
"@typescript-eslint/parser": "^5.53.0",
"babel-core": "^6.26.3",
"@babel/core": "^7.21.0",
"babel-loader": "^9.1.2",
"concurrently": "^7.6.0",
"eslint": "^8.34.0",
@@ -70,11 +70,11 @@
"webpack-cli": "^5.0.1"
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@babel/runtime": "^7.21.0",
"@ethersproject/abstract-signer": "^5.7.0",
"@ethersproject/providers": "^5.7.2",
"@lukeed/csprng": "^1.0.1",
"@orionprotocol/contracts": "0.2.2",
"@orionprotocol/contracts": "0.3.0",
"bignumber.js": "^9.1.1",
"bson-objectid": "^2.0.4",
"buffer": "^6.0.3",
@@ -92,7 +92,7 @@
"ts-node": "^10.9.1",
"uuid": "^9.0.0",
"ws": "^8.12.1",
"zod": "^3.20.6"
"zod": "^3.21.2"
},
"homepage": "https://github.com/orionprotocol/sdk#readme",
"files": [

View File

@@ -102,7 +102,7 @@ export default async function swap({
const sourceChainNativeCryptocurrency = getNativeCryptocurrency(sourceAssetToAddress);
const sourceExchangeContract = Exchange__factory.connect(sourceExchangeContractAddress, sourceProvider);
const sourceChainGasPriceWei = await simpleFetch(sourceOrionBlockchain.getGasPriceWei)();
// const sourceChainGasPriceWei = await simpleFetch(sourceOrionBlockchain.getGasPriceWei)();
const sourceChainAssetAddress = sourceAssetToAddress[assetName];
if (sourceChainAssetAddress === undefined) throw new Error(`Asset '${assetName}' not found in source chain`);
@@ -197,8 +197,23 @@ export default async function swap({
targetChainId: ethers.BigNumber.from(targetChain),
});
let sourceChainGasPrice: ethers.BigNumber;
const sourceChainFeeData = await sourceChainOrionUnit.provider.getFeeData();
if (ethers.BigNumber.isBigNumber(sourceChainFeeData.gasPrice)) { //
unsignedLockAtomicTx.gasPrice = sourceChainFeeData.gasPrice;
sourceChainGasPrice = sourceChainFeeData.gasPrice;
} else if (
ethers.BigNumber.isBigNumber(sourceChainFeeData.maxFeePerGas) &&
ethers.BigNumber.isBigNumber(sourceChainFeeData.maxPriorityFeePerGas)
) { // EIP-1559
unsignedLockAtomicTx.maxFeePerGas = sourceChainFeeData.maxFeePerGas;
unsignedLockAtomicTx.maxPriorityFeePerGas = sourceChainFeeData.maxPriorityFeePerGas;
sourceChainGasPrice = sourceChainFeeData.maxFeePerGas;
} else {
throw new Error('Can\'t get gas price');
}
unsignedLockAtomicTx.chainId = parseInt(chainId, 10);
unsignedLockAtomicTx.gasPrice = ethers.BigNumber.from(sourceChainGasPriceWei);
unsignedLockAtomicTx.from = walletAddress;
let value = new BigNumber(0);
@@ -214,7 +229,7 @@ export default async function swap({
);
unsignedLockAtomicTx.gasLimit = ethers.BigNumber.from(LOCKATOMIC_GAS_LIMIT);
const transactionCost = ethers.BigNumber.from(LOCKATOMIC_GAS_LIMIT).mul(sourceChainGasPriceWei);
const transactionCost = ethers.BigNumber.from(LOCKATOMIC_GAS_LIMIT).mul(sourceChainGasPrice);
const denormalizedTransactionCost = denormalizeNumber(transactionCost, NATIVE_CURRENCY_PRECISION);
sourceChainBalanceGuard.registerRequirement({
@@ -261,7 +276,7 @@ export default async function swap({
options?.logger?.('Atomic swap placed.');
const targetChainGasPriceWei = await simpleFetch(targetOrionBlockchain.getGasPriceWei)();
// const targetChainGasPriceWei = await simpleFetch(targetOrionBlockchain.getGasPriceWei)();
const unsignedRedeemAtomicTx = await targetExchangeContract.populateTransaction.redeemAtomic(
{
amount: amountBlockchainParam,
@@ -276,13 +291,28 @@ export default async function swap({
secret
)
let targetChainGasPrice: ethers.BigNumber;
const targetChainFeeData = await targetChainOrionUnit.provider.getFeeData();
if (ethers.BigNumber.isBigNumber(targetChainFeeData.gasPrice)) { //
unsignedRedeemAtomicTx.gasPrice = targetChainFeeData.gasPrice;
targetChainGasPrice = targetChainFeeData.gasPrice;
} else if (
ethers.BigNumber.isBigNumber(targetChainFeeData.maxFeePerGas) &&
ethers.BigNumber.isBigNumber(targetChainFeeData.maxPriorityFeePerGas)
) { // EIP-1559
unsignedRedeemAtomicTx.maxFeePerGas = targetChainFeeData.maxFeePerGas;
unsignedRedeemAtomicTx.maxPriorityFeePerGas = targetChainFeeData.maxPriorityFeePerGas;
targetChainGasPrice = targetChainFeeData.maxFeePerGas;
} else {
throw new Error('Can\'t get gas price');
}
unsignedRedeemAtomicTx.chainId = parseInt(targetChain, 10);
unsignedRedeemAtomicTx.gasPrice = ethers.BigNumber.from(targetChainGasPriceWei);
unsignedRedeemAtomicTx.from = walletAddress;
unsignedRedeemAtomicTx.gasLimit = ethers.BigNumber.from(REDEEMATOMIC_GAS_LIMIT);
const targetTransactionCost = ethers.BigNumber.from(REDEEMATOMIC_GAS_LIMIT).mul(targetChainGasPriceWei);
const targetDenormalizedTransactionCost = denormalizeNumber(targetTransactionCost, NATIVE_CURRENCY_PRECISION);
const redeemAtomicTransactionCost = ethers.BigNumber.from(REDEEMATOMIC_GAS_LIMIT).mul(targetChainGasPrice);
const targetDenormalizedTransactionCost = denormalizeNumber(redeemAtomicTransactionCost, NATIVE_CURRENCY_PRECISION);
targetChainBalanceGuard.registerRequirement({
reason: 'Network fee',
@@ -314,8 +344,21 @@ export default async function swap({
targetChainAssetAddress,
amountBlockchainParam,
);
if (ethers.BigNumber.isBigNumber(targetChainFeeData.gasPrice)) { //
unsignedWithdrawTx.gasPrice = targetChainFeeData.gasPrice;
targetChainGasPrice = targetChainFeeData.gasPrice;
} else if (
ethers.BigNumber.isBigNumber(targetChainFeeData.maxFeePerGas) &&
ethers.BigNumber.isBigNumber(targetChainFeeData.maxPriorityFeePerGas)
) { // EIP-1559
unsignedWithdrawTx.maxFeePerGas = targetChainFeeData.maxFeePerGas;
unsignedWithdrawTx.maxPriorityFeePerGas = targetChainFeeData.maxPriorityFeePerGas;
targetChainGasPrice = targetChainFeeData.maxFeePerGas;
} else {
throw new Error('Can\'t get gas price');
}
unsignedWithdrawTx.chainId = parseInt(targetChain, 10);
unsignedWithdrawTx.gasLimit = ethers.BigNumber.from(WITHDRAW_GAS_LIMIT);
unsignedWithdrawTx.gasPrice = ethers.BigNumber.from(targetChainGasPriceWei);
unsignedWithdrawTx.from = walletAddress;
unsignedWithdrawTx.nonce = await targetProvider.getTransactionCount(walletAddress, 'pending');
const signedTx = await signer.signTransaction(unsignedWithdrawTx);

View File

@@ -0,0 +1,28 @@
import { Wallet } from 'ethers';
import Orion from '../Orion';
import { SupportedChainId } from '../types';
const privateKey = process.env['PRIVATE_KEY']
if (privateKey === undefined) throw new Error('Private key is required');
jest.setTimeout(30000);
describe('Bridge', () => {
test('Execution', async () => {
const orion = new Orion('testing');
const wallet = new Wallet(privateKey);
await orion.bridge.swap(
'ORN',
0.12345678,
SupportedChainId.FANTOM_TESTNET,
SupportedChainId.BSC_TESTNET,
wallet,
{
autoApprove: true,
logger: console.log,
withdrawToWallet: true,
},
);
});
});

View File

@@ -1,4 +1,6 @@
import { ethers } from 'ethers';
import { z } from 'zod';
import getValidArrayItemsSchema from '../../../utils/getValidArrayItems';
const baseAtomicHistorySchema = z.object({
success: z.boolean(),
@@ -14,9 +16,9 @@ const baseAtomicHistoryItem = z.object({
_id: z.string(),
__v: z.number(),
asset: z.string(),
sender: z.string(),
secretHash: z.string(),
receiver: z.string().optional(),
sender: z.string().refine(ethers.utils.isAddress),
secretHash: z.string().refine(ethers.utils.isHexString),
receiver: z.string().refine(ethers.utils.isAddress).optional(),
secret: z.string().optional(),
});
@@ -64,7 +66,7 @@ export const targetAtomicHistorySchema = baseAtomicHistorySchema.extend({
});
const atomicHistorySchema = baseAtomicHistorySchema.extend({
data: z.array(
data: getValidArrayItemsSchema(
z.discriminatedUnion('type', [sourceAtomicHistorySchemaItem, targetAtomicHistorySchemaItem]),
),
});

View File

@@ -85,10 +85,13 @@ export default class PriceFeedSubscription<T extends SubscriptionType = Subscrip
// https://stackoverflow.com/questions/19304157/getting-the-reason-why-websockets-closed-with-close-code-1006
private isClosedIntentionally = false;
private readonly onOpen: ((event: WebSocket.Event) => void) | undefined;
constructor(
type: T,
url: string,
params: Subscription<T>,
onOpen?: (event: WebSocket.Event) => void,
) {
this.id = uuidv4();
this.url = url;
@@ -97,7 +100,7 @@ export default class PriceFeedSubscription<T extends SubscriptionType = Subscrip
this.payload = params.payload;
}
this.callback = params.callback;
this.onOpen = onOpen;
this.init();
}
@@ -118,6 +121,9 @@ export default class PriceFeedSubscription<T extends SubscriptionType = Subscrip
const { payload, url, type } = this;
this.ws = new WebSocket(`${url}/${type}${payload !== undefined ? `/${payload.toString()}` : ''}`);
if (this.onOpen !== undefined) {
this.ws.onopen = this.onOpen;
}
this.ws.onmessage = (e) => {
const { data } = e;

View File

@@ -1,3 +1,4 @@
import type WebSocket from 'ws';
import PriceFeedSubscription, { type SubscriptionType, type Subscription } from './PriceFeedSubscription';
export * as schemas from './schemas';
@@ -20,11 +21,13 @@ export class PriceFeedWS {
subscribe<S extends SubscriptionType>(
type: S,
params: Subscription<S>,
onOpen?: (event: WebSocket.Event) => void,
) {
const sub = new PriceFeedSubscription(
type,
this.url,
params,
onOpen
);
this.subscriptions = {
...this.subscriptions,

View File

@@ -0,0 +1,20 @@
import type { Schema, ZodTypeDef } from 'zod';
import { z } from 'zod'
export default function getValidArrayItemsSchema<DataOut, Def extends ZodTypeDef, DataIn> (
elemSchema: Schema<DataOut, Def, DataIn>,
) {
return z.array(z.unknown()).transform((items) => {
const validItems: Array<z.infer<typeof elemSchema>> = [];
for (let i = 0; i < items.length; i++) {
const item = items[i];
const parsedItem = elemSchema.safeParse(item);
if (parsedItem.success) {
validItems.push(parsedItem.data);
} else {
console.log(`Array item with index ${i} is invalid. Error: ${parsedItem.error.message}. Data: ${JSON.stringify(item)}.`)
}
}
return validItems;
})
}