UNPKG

@firefly-exchange/library-sui

Version:

Sui library housing helper methods, classes to interact with Bluefin protocol(s) deployed on Sui

272 lines (271 loc) 12.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.addPrefix = exports.verifySECP = exports.parseAndShapeSignedData = exports.createZkSignature = exports.getSalt = exports.getRandomInt = exports.requestGas = exports.storedOrderToSui = exports.printOrder = exports.createOrder = exports.decodeZkSignature = exports.verifyZkPublicKey = exports.verifyZkAddress = exports.getSignerFromSeed = exports.getKeyPairFromPvtKey = exports.keypairFromSecretKey = exports.getKeyPairFromSeed = exports.getProvider = exports.readFile = exports.writeFile = void 0; const enums_1 = require("../src/enums"); const library_1 = require("./library"); const defaults_1 = require("./defaults"); const dotenv_1 = require("dotenv"); const DeploymentConfig_1 = require("./DeploymentConfig"); const verify_1 = require("@mysten/sui/verify"); const bcs_1 = require("@mysten/bcs"); const secp256k1_1 = require("@noble/curves/secp256k1"); const cryptography_1 = require("@mysten/sui/cryptography"); const zklogin_1 = require("@mysten/sui/zklogin"); const fs_1 = __importDefault(require("fs")); const types_1 = require("./types"); const zklogin_2 = require("@mysten/zklogin"); (0, dotenv_1.config)({ path: ".env" }); function writeFile(filePath, jsonData) { fs_1.default.writeFileSync(filePath, JSON.stringify(jsonData)); } exports.writeFile = writeFile; function readFile(filePath) { return fs_1.default.existsSync(filePath) ? JSON.parse(fs_1.default.readFileSync(filePath).toString()) : {}; } exports.readFile = readFile; function getProvider(rpcURL) { return new types_1.SuiClient({ url: rpcURL }); } exports.getProvider = getProvider; function getKeyPairFromSeed(seed, scheme = "Secp256k1") { switch (scheme) { case "ED25519": return types_1.Ed25519Keypair.deriveKeypair(seed); case "Secp256k1": return types_1.Secp256k1Keypair.deriveKeypair(seed); default: throw new Error("Provided scheme is invalid"); } } exports.getKeyPairFromSeed = getKeyPairFromSeed; function keypairFromSecretKey(privateKeyBase64) { const keyPair = (0, cryptography_1.decodeSuiPrivateKey)(privateKeyBase64); return types_1.Ed25519Keypair.fromSecretKey(keyPair.secretKey); } exports.keypairFromSecretKey = keypairFromSecretKey; function getKeyPairFromPvtKey(key, scheme = "Secp256k1") { if (key.startsWith("0x")) { key = key.substring(2); // Remove the first two characters (0x) } switch (scheme) { case "ED25519": return types_1.Ed25519Keypair.fromSecretKey(Buffer.from(key, "hex")); case "Secp256k1": return types_1.Secp256k1Keypair.fromSecretKey(Buffer.from(key, "hex")); case "ZkLogin": return keypairFromSecretKey(key); default: throw new Error("Provided key is invalid"); } } exports.getKeyPairFromPvtKey = getKeyPairFromPvtKey; function getSignerFromSeed(seed, scheme = "Secp256k1") { return getKeyPairFromSeed(seed, scheme); } exports.getSignerFromSeed = getSignerFromSeed; //need to check zkLogin flow const verifyZkAddress = (signature, userAddress) => { const parsedSignature = (0, cryptography_1.parseSerializedSignature)(signature); if (parsedSignature.signatureScheme == "ZkLogin") { const legacyAddress = (0, zklogin_1.computeZkLoginAddressFromSeed)(parsedSignature.zkLogin.addressSeed, parsedSignature.zkLogin.iss, true); const nonLegacyAddress = (0, zklogin_1.computeZkLoginAddressFromSeed)(parsedSignature.zkLogin.addressSeed, parsedSignature.zkLogin.iss, false); return legacyAddress === userAddress || nonLegacyAddress === userAddress; } return false; }; exports.verifyZkAddress = verifyZkAddress; const verifyZkPublicKey = ({ signature, publicKey }) => { const parsedSignature = (0, cryptography_1.parseSerializedSignature)(signature); if (parsedSignature.signatureScheme == "ZkLogin") { const userSignature = (0, bcs_1.toB64)(parsedSignature.zkLogin.userSignature); const parsedUserSignature = (0, cryptography_1.parseSerializedSignature)(userSignature); if (parsedUserSignature.signatureScheme == "MultiSig") { throw "multisig not supported"; } const { publicKey: signaturePublicKey, signatureScheme } = parsedUserSignature; return ((0, verify_1.publicKeyFromRawBytes)(signatureScheme, signaturePublicKey).toBase64() === publicKey); } return false; }; exports.verifyZkPublicKey = verifyZkPublicKey; const decodeZkSignature = (decodedSignature) => { const PUBLIC_KEY_LENGTH = 44; const SCHEME_LENGTH = 1; const SIGNATURE_LENGTH = decodedSignature.length - PUBLIC_KEY_LENGTH - SCHEME_LENGTH; const signature = decodedSignature.slice(0, SIGNATURE_LENGTH); const scheme = decodedSignature.slice(SIGNATURE_LENGTH, SIGNATURE_LENGTH + SCHEME_LENGTH); const publicKey = decodedSignature.slice(-PUBLIC_KEY_LENGTH); return { zkSignature: Buffer.from(signature, "hex").toString("utf-8"), publicKey, scheme }; }; exports.decodeZkSignature = decodeZkSignature; function createOrder(params) { return { market: params?.market || defaults_1.DEFAULT.ORDER.market, maker: params?.maker || defaults_1.DEFAULT.ORDER.maker, isBuy: params?.isBuy == true, reduceOnly: params?.reduceOnly == true, postOnly: params?.postOnly == true, orderbookOnly: params?.orderbookOnly == undefined ? true : params.orderbookOnly, ioc: params?.ioc == true, price: params?.price ? (0, library_1.toBigNumber)(params.price) : defaults_1.DEFAULT.ORDER.price, quantity: params?.quantity ? (0, library_1.toBigNumber)(params.quantity) : defaults_1.DEFAULT.ORDER.quantity, leverage: params?.leverage ? (0, library_1.toBigNumber)(params?.leverage) : defaults_1.DEFAULT.ORDER.leverage, expiration: params?.expiration ? (0, library_1.bigNumber)(params?.expiration) : defaults_1.DEFAULT.ORDER.expiration, salt: params?.salt ? (0, library_1.bigNumber)(params?.salt) : (0, library_1.bigNumber)(Date.now()) }; } exports.createOrder = createOrder; function printOrder(order) { console.log("\norder.maker:", order.maker, "\norder.market:", order.market, "\norder.isBuy:", order.isBuy, "\norder.reduceOnly:", order.reduceOnly, "\norder.postOnly:", order.postOnly, "\norder.orderbookOnly:", order.orderbookOnly, "\norder.ioc:", order.ioc, "\norder.price:", order.price.toFixed(0), "\norder.quantity:", order.quantity.toFixed(0), "\norder.leverage:", order.leverage.toFixed(0), "\norder.expiration:", order.expiration.toFixed(0), "\norder.salt:", order.salt.toFixed(0)); } exports.printOrder = printOrder; /** * Consumes an order of type StoredOrder and returns a SuiOrder that can be sent to on-chain for settlement * @param order StoredOrder to be transformed * @param perpetualID market/perpetual address * @param orderbookOnly (optional) true by default as all orders going through our exchange have orderbook only true */ function storedOrderToSui(order, perpetualID, orderbookOnly = true) { return { market: perpetualID, maker: order.maker, isBuy: order.isBuy, reduceOnly: order.reduceOnly, postOnly: order.postOnly, orderbookOnly, ioc: order.timeInForce == enums_1.TIME_IN_FORCE.IMMEDIATE_OR_CANCEL, quantity: order.amount, price: order.price, leverage: order.leverage, expiration: (0, library_1.bigNumber)(order.expiration), salt: (0, library_1.bigNumber)(order.salt) }; } exports.storedOrderToSui = storedOrderToSui; async function requestGas(address) { const url = DeploymentConfig_1.network.faucet; try { const data = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ FixedAmountRequest: { recipient: address } }) }); return data; } catch (e) { console.log("Error while requesting gas", e.message); } return false; } exports.requestGas = requestGas; function getRandomInt(min, max) { min = Math.ceil(min); max = Math.floor(max); return Math.floor(Math.random() * (min - max) + min); // The maximum is exclusive and the minimum is inclusive } exports.getRandomInt = getRandomInt; function getSalt() { return (Date.now() + getRandomInt(0, 1000000) + getRandomInt(0, 1000000) + getRandomInt(0, 1000000)); } exports.getSalt = getSalt; const createZkSignature = ({ userSignature, zkPayload }) => { const { salt, decodedJWT, proof, maxEpoch } = zkPayload; const addressSeed = (0, zklogin_2.genAddressSeed)(BigInt(salt), "sub", decodedJWT.sub, decodedJWT.aud).toString(); const zkLoginSignature = (0, zklogin_2.getZkLoginSignature)({ inputs: { ...proof, addressSeed }, maxEpoch: maxEpoch, userSignature }); return zkLoginSignature; }; exports.createZkSignature = createZkSignature; const parseAndShapeSignedData = ({ signature, isParsingRequired = true }) => { let data; const parsedSignature = (0, cryptography_1.parseSerializedSignature)(signature); if (isParsingRequired && parsedSignature.signatureScheme === "ZkLogin") { //zk login signature const { userSignature } = parsedSignature.zkLogin; //convert user sig to b64 const convertedUserSignature = (0, bcs_1.toB64)(userSignature); //reparse b64 converted user sig const parsedUserSignature = (0, cryptography_1.parseSerializedSignature)(convertedUserSignature); if (parsedUserSignature.signatureScheme == "MultiSig") { throw "multisig not supported"; } data = { signature: Buffer.from(parsedSignature.serializedSignature).toString("hex") + "3", publicKey: (0, verify_1.publicKeyFromRawBytes)(parsedUserSignature.signatureScheme, parsedUserSignature.publicKey).toBase64() }; } else if (parsedSignature.signatureScheme != "MultiSig") { data = { signature: Buffer.from(parsedSignature.signature).toString("hex") + enums_1.SIGNER_TYPES.UI_ED25519, publicKey: (0, verify_1.publicKeyFromRawBytes)(parsedSignature.signatureScheme, parsedSignature.publicKey).toBase64() }; } else { throw "signature scheme not supported"; } return data; }; exports.parseAndShapeSignedData = parseAndShapeSignedData; /** * Validates if the provided signature is created using the data and public key provided * @param signature hex encoded signature * @param data data bytes * @param publicKey public key bytes * @returns True if signature is valid */ function verifySECP(signature, data, publicKey) { const sig_r_s = secp256k1_1.secp256k1.Signature.fromCompact(signature); const sig_r_s_b1 = sig_r_s.addRecoveryBit(0x1); const recovered_pk_1 = sig_r_s_b1.recoverPublicKey(data).toRawBytes(true).toString(); const sig_r_s_b0 = sig_r_s.addRecoveryBit(0x0); const recovered_pk_0 = sig_r_s_b0.recoverPublicKey(data).toRawBytes(true).toString(); return (publicKey.toString() === recovered_pk_1 || publicKey.toString() === recovered_pk_0); } exports.verifySECP = verifySECP; /** * Ensures that an address string starts with "0x". * If it doesn't, the function prepends "0x" to the address. * * @param address - The token string to check. * @returns The address string with a "0x" prefix. */ function addPrefix(address) { // Convert token to lowercase to check case-insensitively if (address.toLowerCase().startsWith("0x")) { return address; } return "0x" + address; } exports.addPrefix = addPrefix;