diff --git a/PMM-sample.md b/PMM-sample.md new file mode 100644 index 0000000..ad1eb45 --- /dev/null +++ b/PMM-sample.md @@ -0,0 +1,288 @@ + + +
+ Orion Protocol SDK logo +

PMM example

+
+ +## Overview + +This is special example which shows how to use PMM liquidity without using SDK, only using few libraries + +```ts +import {ethers, Wallet} from "ethers"; +import {z} from "zod"; +import hmacSHA256 from "crypto-js/hmac-sha256"; +import Hex from "crypto-js/enc-hex"; + +/////////////////////////////// +/////////////////////////////// +const pmmOrderQuotationSchema = z.object({ + info: z.string().default(''), + makerAsset: z.string().default(''), + takerAsset: z.string().default(''), + maker: z.string().default(''), + allowedSender: z.string().default(''), + makingAmount: z.string().default(''), + takingAmount: z.string().default(''), +}); + +const pmmOrderSchema = z.object({ + order: pmmOrderQuotationSchema.default({}), + signature: z.string().default(''), + success: z.boolean().default(false), + error: z.string().default(''), +}); + +const orionRFQContractABI = + [ + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "info", + "type": "uint256" + }, + { + "internalType": "address", + "name": "makerAsset", + "type": "address" + }, + { + "internalType": "address", + "name": "takerAsset", + "type": "address" + }, + { + "internalType": "address", + "name": "maker", + "type": "address" + }, + { + "internalType": "address", + "name": "allowedSender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "makingAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "takingAmount", + "type": "uint256" + } + ], + "internalType": "struct OrderRFQLib.OrderRFQ", + "name": "order", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "flagsAndAmount", + "type": "uint256" + } + ], + "name": "fillOrderRFQ", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ]; + +function encode_utf8(s : string) { + return unescape(encodeURIComponent(s)); +} +function sign(message : string, key: string) { + return hmacSHA256( + encode_utf8(message), + encode_utf8(key) + ).toString(Hex); +} + +function generateHeaders(body : any, method : string, path : string, timestamp : number, apiKey : string, secretKey : string) { + const sortedBody = Object.keys(body) + .sort() + .map((key) => ( + `${key}=${body[key]}` + )).join('&'); + + const payload = timestamp + method.toUpperCase() + path + sortedBody; + + const signature = sign(payload, secretKey); + + const httpOptions = { + headers: { + 'API-KEY': apiKey, + 'ACCESS-TIMESTAMP': timestamp.toString(), + 'ACCESS-SIGN': signature + } + }; + return httpOptions; +} + +// +async function RFQOrder( + tokenFrom: string, + tokenTo: string, + fromTokenAmount: string, + apiKey: string, // + secretKey: string, + wallet: string +) : Promise> { + + // NB: replace with correct URL for networks + const apiUrl = 'https://testing.orion.xyz/bsc-testnet/backend' + // const apiUrl = 'https://trade.orion.xyz/bsc-mainnet/backend' + , path = '/rfq' + , url = `${apiUrl}/api/v1/integration/pmm`+path + , headers = { + 'Content-Type': 'application/json', + } + , data = { + "baseToken":tokenFrom, // USDT + "quoteToken":tokenTo, // ORN + "amount": fromTokenAmount, // 100 + "taker": wallet, + "feeBps": 0 + } + , method = 'POST' + , timestamp = Date.now() + , signatureHeaders = generateHeaders(data, method, path, timestamp, apiKey, secretKey) + , compiledHeaders = {...headers, ...signatureHeaders.headers, } + , body = JSON.stringify(data) + ; + + let res = pmmOrderSchema.parse({}); + + try { + const result = await fetch(url,{ + headers: compiledHeaders, + method, + body + }); + + // const data = await result.text(); + // console.log(data); + + const json = await result.json(); + console.log(json); + const parseResult = pmmOrderSchema.safeParse(json); + + if(!parseResult.success) { + // Try to parse error answer + const errorSchema = z.object({error: z.object({code: z.number(), reason: z.string()})}); + + const errorParseResult = errorSchema.safeParse(json); + + if(!errorParseResult.success) + throw Error(`Unrecognized answer from aggregator: ${json}`); + + throw Error(errorParseResult.data.error.reason); + } + + res.order = parseResult.data.order; + res.signature = parseResult.data.signature; + res.error = ''; + res.success = true; + } + catch(err) { + res.error = `${err}`; + } + return res; +} + + +(async() => { + const apiKey = '958153f1-b8b9-3ec4-84eb-2147429105d9'; + const secretKey = 'secretKey'; + + // BNB testnet tokens and router + const assetORN = '0xf223eca06261145b3287a0fefd8cfad371c7eb34'; + const assetUSDT = '0xcb2951e90d8dcf16e1fa84ac0c83f48906d6a744'; + const router = '0x89357522C0ed6E557D39dC75290859246077bdfC'; + + // BNB mainnet tokens and router + // const assetORN = '0xe4ca1f75eca6214393fce1c1b316c237664eaa8e'; + // const assetUSDT = '0x55d398326f99059ff775485246999027b3197955'; + // const router = '0xcb2D40EabC4f4c92Ee993Eb3D67f7717bE476E76'; + + const rfqOrder = await RFQOrder( + assetORN, // Spending asset + assetUSDT, // Receiving asset + '1000000000', // Amount in "satoshi" of spending asset + apiKey, + secretKey, + '0x61Eed69c0d112C690fD6f44bB621357B89fBE67F' // Can be any address, ignored for now + ); + + if(!rfqOrder.success) { + console.log(rfqOrder.error); + return; + } + + // ... here you can check order prices, etc. + console.log(rfqOrder); + + // Calldata according to provided rfqOrder + const contractInterface = new ethers.Interface(orionRFQContractABI); + const calldata = contractInterface.encodeFunctionData("fillOrderRFQ", [rfqOrder.order, rfqOrder.signature, 0]); + console.log('Calldata = ', calldata); + + // Call router with this data + // Replace with your private key + const yourWalletPrivateKey = '0xf1.......'; + + const tx = { + to: router, + data: calldata + } + + const provider = new ethers.JsonRpcProvider("https://data-seed-prebsc-1-s1.binance.org:8545/"); + const signer = new ethers.Wallet(yourWalletPrivateKey, provider); + console.log('Address = ', signer.address); + const transactionResponse = await signer.sendTransaction(tx); + console.log("Transaction hash:", transactionResponse.hash); + await transactionResponse.wait(); +})(); +``` + +RFQ order response example description (`rfqOrder` from example above): + +``` + { + quotation: { + info: '31545611720730315633520017429', + makerAsset: '0xcb2951e90d8dcf16e1fa84ac0c83f48906d6a744', + takerAsset: '0xf223eca06261145b3287a0fefd8cfad371c7eb34', + maker: '0x1ff516e5ce789085cff86d37fc27747df852a80a', + allowedSender: '0x0000000000000000000000000000000000000000', + makingAmount: '193596929', + takingAmount: '10000000000' + }, + signature: '0x8a2f9140a3c3a5734eda763a19c54c5ac909d8a03db37d9804af9115641fd1d35896b66ca6e136c1c89e0478fb7382a4b875d0f74529c1e83601f9383d310dde1b', + success: true, + error: '' + } +``` + + +* info - can be ignored +* makerAsset - your RECEIVING asset (what you expect to receive from contract, in this case USDT) +* takerAsset - your SPENDING asset (what you're giving to contract, in this case ORN) +* maker - can be ignored for now; +* allowedSender - can be ignored for now; +* makingAmount - how much you will RECEIVE (in receiving asset's precision) +* takingAmount - how much you should SPEND (in spending asset's precision) \ No newline at end of file diff --git a/package.json b/package.json index cec14eb..a307337 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@orionprotocol/sdk", - "version": "0.20.68-rc1", + "version": "0.20.79-rc1", "description": "Orion Protocol SDK", "main": "./lib/index.cjs", "module": "./lib/index.js", diff --git a/src/Unit/Pmm/index.ts b/src/Unit/Pmm/index.ts index cb41136..d4f1c57 100644 --- a/src/Unit/Pmm/index.ts +++ b/src/Unit/Pmm/index.ts @@ -79,6 +79,6 @@ export default class Pmm { const contract = new ethers.Contract(this.contractAddress, orionRFQContractABI, signer); // @ts-ignore - return contract.fillOrderRFQ(order.quotation, order.signature, BigInt(0)); + return contract.fillOrderRFQ(order.order, order.signature, BigInt(0)); } } \ No newline at end of file diff --git a/src/Unit/Pmm/schemas/order.ts b/src/Unit/Pmm/schemas/order.ts index 2651660..a9727e4 100644 --- a/src/Unit/Pmm/schemas/order.ts +++ b/src/Unit/Pmm/schemas/order.ts @@ -11,7 +11,7 @@ export const pmmOrderQuotationSchema = z.object({ }); export const pmmOrderSchema = z.object({ - quotation: pmmOrderQuotationSchema.default({}), + order: pmmOrderQuotationSchema.default({}), signature: z.string().default(''), success: z.boolean().default(false), error: z.string().default(''), diff --git a/src/services/Aggregator/index.ts b/src/services/Aggregator/index.ts index f04e4c5..1077a53 100644 --- a/src/services/Aggregator/index.ts +++ b/src/services/Aggregator/index.ts @@ -461,11 +461,10 @@ class Aggregator { throw Error(errorParseResult.data.error.reason); } - res.quotation = parseResult.data.quotation; + res.order = parseResult.data.order; res.signature = parseResult.data.signature; res.error = ''; res.success = true; - // return result; } catch(err) { res.error = `${err}`;