diff --git a/.gitignore b/.gitignore index de0aa30..9047e92 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,5 @@ cabal.project.local .DS_Store config-files/* *.skey +*.vkey +*.addr diff --git a/Dockerfile b/Dockerfile index 829eb91..6e32a46 100644 --- a/Dockerfile +++ b/Dockerfile @@ -70,6 +70,6 @@ ENV BOTC_MAX_ORDERS_MATCHES='5' ENV BOTC_MAX_TXS_PER_ITERATION='4' ENV BOTC_RANDOMIZE_MATCHES_FOUND='True' ENV BOTC_ASSET_FILTER='[{"commodityAsset":"c6e65ba7878b2f8ea0ad39287d3e2fd256dc5c4160fc19bdf4c4d87e.7447454e53","currencyAsset":"lovelace"},{"commodityAsset":"0254a6ffa78edb03ea8933dbd4ca078758dbfc0fc6bb0d28b7a9c89f.4c454e4649","currencyAsset":"lovelace"},{"commodityAsset":"8cafc9b387c9f6519cacdce48a8448c062670c810d8da4b232e56313.6d4e5458","currencyAsset":"lovelace"},{"commodityAsset":"171163f05e4f30b6be3c22668c37978e7d508b84f83558e523133cdf.74454d50","currencyAsset":"lovelace"}]' -ENV BOTC_POREFS='{"refAddr":"addr_test1wpgexmeunzsykesf42d4eqet5yvzeap6trjnflxqtkcf66g0kpnxt","refNftAC":"fae686ea8f21d567841d703dea4d4221c2af071a6f2b433ff07c0af2.e6a295bb83d06f53fcf91151f54acec0a63fbd6f0d924206d5d012e6da3b72af","refNftUtxoRef":"39f987a6beb9cc4c45bba149a21c28068f640f3593f15f8157f0b6022b431977#0","scriptRef":"39f987a6beb9cc4c45bba149a21c28068f640f3593f15f8157f0b6022b431977#1","nftPolicyRef":"39f987a6beb9cc4c45bba149a21c28068f640f3593f15f8157f0b6022b431977#0"}' +ENV BOTC_POREFS='{"refAddr":"addr_test1wrgvy8fermjrruaf7fnndtmpuw4xx4cnvfqjp5zqu8kscfcvh32qk","refNftAC":"fae686ea8f21d567841d703dea4d4221c2af071a6f2b433ff07c0af2.8309f9861928a55d37e84f6594b878941edce5e351f7904c2c63b559bde45c5c","scriptRef":"be6f8dc16d4e8d5aad566ff6b5ffefdda574817a60d503e2a0ea95f773175050#2","nftPolicyRef":"be6f8dc16d4e8d5aad566ff6b5ffefdda574817a60d503e2a0ea95f773175050#1"}' ENTRYPOINT ["/bin/bash", "./start.sh"] diff --git a/README.md b/README.md index b8038ac..0c74231 100644 --- a/README.md +++ b/README.md @@ -61,25 +61,19 @@ benefit from customizing existing order matching strategies or even implementing For a more detailed description, please see the [Genius Yield Whitepaper](https://www.geniusyield.co/whitepaper.pdf?lng=en). -Given a pair of tokens, an order will contain the number of tokens it offers, the price -of one unit of those offered, and the minimal amount that must be bought from the order. -Besides that, the order will have optionally a lifetime and, of course, a notion of ownership +Given a pair of tokens, an order will contain the number of tokens it offers and the price of one unit of offered token in terms of asked tokens. +Besides that, the order will have some life timeline and, of course, a notion of ownership related to the one that created it. For example, we could create an order offering of `10 tokenA`, -with a unit price of `2 tokenB`, that is, we expect to receive `2 tokenB` per `1 tokenA`. Also, we -want the minimal amount to be bought be `5 tokenA`. Clearly, the owners of this order will be -the creator, who placed the order, and it's important to keep in mind that all this information -is **mandatory**, excluding the lifetime of the order, which is optional, meaning, when it is missing, the order won't -expire and it is going to be be available for an unlimited time. Once we create an order, the offered -tokens will be locked in the order, until it is filled or cancelled. +with a unit price of `2 tokenB`, that is, we expect to receive `2 tokenB` per `1 tokenA`. Clearly, the owners of this order will be +us and it's important to mention that all this information is **mandatory**, but we can avoid setting +the life timeline, meaning the order will always be available. Once we create an order, the offered +tokens will be locked on the order. Given an specific order, two interesting "actions" might be performed on it. The owner can cancel it and get back the previously locked tokens. Or anyone can _fill_ it, filling an order is just paying the correct amount of tokens the owner of the order expects to receive related to the amount of tokens we want to buy from that order. Following the previous example, anyone -could fill that order by buying from it `6 tokenA` and paying the owner `12 tokenB`. But, it -isn't possible to buy, for instance, `3 tokenA` from the order because the minimal amount -was setup to `5`, except the amount of offered tokens is less than that. This makes sure that no tokens get -stuck in the contract, even if the number is lower then the minimal fill amount. +could fill that order by buying from it `6 tokenA` and paying `12 tokenB`. One important thing to mention, that is actually completely transparent for the end user, is that there are two kinds of fills: _complete_ and _partial_. A complete fill will buy all @@ -93,9 +87,9 @@ There shouldn't be any surprise if we mention that each action is performed by a Now, let's suppose, besides the previous order, we have another one offering of `20 tokenB`, with a unit price of `0.4 tokenA`. We could earn some tokens by “combining” the two orders -and take advantage of the price difference. Following the example, given we bought `6 tokenA` -using `12 tokenB`, we now can use these `6 tokenA` to buy back `15 tokenB` from this other -order, earning `3 tokenB`. These two fills can be combined into a single transaction, in +and take advantage of the price difference. Following the example, given we bought `8 tokenA` +using `16 tokenB`, we now can use these `8 tokenA` to buy back `20 tokenB` from this other +order, earning `4 tokenB`. These two fills can be combined into a single transaction, in fact, we could combine more than two orders. The SOR has the ability to build these transactions matching orders programmatically, @@ -130,22 +124,18 @@ Using the previous example we could have two cases: -If we want our earnings to be in `tokenB`, then the commodity must be `tokenA`. We can buy from -the sell order, `6 tokenA` using `12 tokenB`, then using these `6 tokenA` we buy back `15 tokenB` from -the buy order, earning `3 tokenB`. However, if we want our earnings to be in `tokenA`, then the -commodity must be `tokenB`. So we can buy from the sell order, `18 tokenB` using `7 tokenA`, then -using these `18 tokenB` we buy back `9 tokenA` from the buy order, earning `2 tokenA`. +If we want our earnings to be in `tokenA` then the +commodity must be `tokenB`. So we can buy from the sell order, `20 tokenB` using `8 tokenA`, then +using these `20 tokenB` we can get `10 tokenA` from the buy order, earning `2 tokenA`. ## Building and running the Smart Order Router > [!NOTE] -> The Genius Yield DEX is in the public testnet phase at the moment. -> > In order to run Smart Order Router instances for the public testnet, please use the preprod testnet as in the examples below. ### Running the SOR using Docker -A ready-to-run, containerized version of the Smart Order Router is availabe via the [GitHub Container Registry](ghcr.io/geniusyield/smart-order-router:latest). +A ready-to-run, containerized version of the Smart Order Router is available via the [GitHub Container Registry](ghcr.io/geniusyield/smart-order-router:latest). A Smart Order Router container instance using the Maestro backend can be started by using the following snippet: @@ -156,7 +146,7 @@ A Smart Order Router container instance using the Maestro backend can be started PAYMENT_SIGNING_KEY_CBOR_HEX=5820d682e237a04d43ad011fdecd141acd485f6d3d634466692d58f6d75250f39134 COLLATERAL_UTXO_REF=7cc7b044d26981d3fc73ae72994f289d99ba113ceefb5b83f4d7643bfb12682a#1 MAESTRO_API_KEY=some_api_key -CARDANO_NETWORK=testnet-preprod +CARDANO_NETWORK=preprod docker run -it \ -e BOTC_SKEY="{\"cborHex\": \"$PAYMENT_SIGNING_KEY_CBOR_HEX\", \"type\": \"PaymentSigningKeyShelley_ed25519\", \"description\": \"Payment Signing Key\"}" \ @@ -171,6 +161,9 @@ Alternatively the Blockfrost or the Kupo backend could be used. This can be accomplished for Blockfrost by using the following commands: +> [!NOTE] +> Few of the optimisations that we make use of such as querying UTxOs and their datums in a single request, aren't available for Blockfrost, thus, this provider is expected to run slow compared to other providers. + ``` bash # SMART ORDER ROUTER INSTANCE USING BLOCKFROST # ============================================ @@ -178,7 +171,7 @@ This can be accomplished for Blockfrost by using the following commands: PAYMENT_SIGNING_KEY_CBOR_HEX=5820d682e237a04d43ad011fdecd141acd485f6d3d634466692d58f6d75250f39134 COLLATERAL_UTXO_REF=7cc7b044d26981d3fc73ae72994f289d99ba113ceefb5b83f4d7643bfb12682a#1 BLOCKFROST_API_KEY=some_api_key -CARDANO_NETWORK=testnet-preprod +CARDANO_NETWORK=preprod docker run -it \ -e BOTC_SKEY="{\"cborHex\": \"$PAYMENT_SIGNING_KEY_CBOR_HEX\", \"type\": \"PaymentSigningKeyShelley_ed25519\", \"description\": \"Payment Signing Key\"}" \ @@ -189,6 +182,12 @@ docker run -it \ And the following commands can be used to start a Kupo backed instance, if you want to use an existing Kupo instance: + > **ⓘ How to run Kupo efficiently?** + > + > Firstly, Kupo requires a node running, note that node itself maintains efficient access to information such as current protocol parameters, current set of pool ids, etc. but it doesn't efficiently provide us with UTxOs when say queried by a particular address. Kupo helps in covering this gap and gives us efficient lookup tables to query for UTxOs. For our use case, we are only interested in our own bot's UTxOs, order UTxOs and the required reference scripts / reference inputs. So we'll run Kupo to keep track of only those UTxOs, note that if we instead run Kupo by matching against star (`*`) pattern, then as Kupo does many disk writes, we would quickly burn out our SSDs TBW limit. + > + > Please see the scripts, [`kupo-preprod.sh`](./scripts/kupo-preprod.sh) for pre-production network and [`kupo-mainnet.sh`](./scripts/kupo-mainnet.sh) for mainnet network to see how this can be achieved. Note that these two scripts take as an argument the match pattern for bot's UTxOs, you may very well give the bech32 address of bot as value of this argument. To understand what all the script does, please see Kupo's [documentation](https://cardanosolutions.github.io/kupo/#section/Getting-started). + ``` bash # SMART ORDER ROUTER INSTANCE USING KUPO (existing Kupo instance) # =============================================================== @@ -197,7 +196,7 @@ PAYMENT_SIGNING_KEY_CBOR_HEX=5820d682e237a04d43ad011fdecd141acd485f6d3d634466692 COLLATERAL_UTXO_REF=7cc7b044d26981d3fc73ae72994f289d99ba113ceefb5b83f4d7643bfb12682a#1 KUPO_URL=http://some.url.to.your.kupo.instance:1442 CARDANO_NODE_SOCKET_PATH=/cardano/node/socket -CARDANO_NETWORK=testnet-preprod +CARDANO_NETWORK=preprod docker run -it \ -e BOTC_SKEY="{\"cborHex\": \"$PAYMENT_SIGNING_KEY_CBOR_HEX\", \"type\": \"PaymentSigningKeyShelley_ed25519\", \"description\": \"Payment Signing Key\"}" \ @@ -277,25 +276,24 @@ file. The complete bot configuration looks like this: ```json { - "signingKeyFP":"bot.skey", - "nftMintingPolicyFP":"compiled-scripts/minting-policy", - "orderValidatorFP":"compiled-scripts/partial-order", - "validatorRefs":{ - "refAddr":"addr_test1wpgexmeunzsykesf42d4eqet5yvzeap6trjnflxqtkcf66g0kpnxt", - "refNftAC":"fae686ea8f21d567841d703dea4d4221c2af071a6f2b433ff07c0af2.e6a295bb83d06f53fcf91151f54acec0a63fbd6f0d924206d5d012e6da3b72af", - "refNftUtxoRef":"39f987a6beb9cc4c45bba149a21c28068f640f3593f15f8157f0b6022b431977#0", - "scriptRef":"39f987a6beb9cc4c45bba149a21c28068f640f3593f15f8157f0b6022b431977#1", - "nftPolicyRef":"39f987a6beb9cc4c45bba149a21c28068f640f3593f15f8157f0b6022b431977#0" + "signingKeyFP": "bot.skey", + "nftMintingPolicyFP": "compiled-scripts/minting-policy", + "orderValidatorFP": "compiled-scripts/partial-order", + "validatorRefs": { + "refAddr": "addr_test1wrgvy8fermjrruaf7fnndtmpuw4xx4cnvfqjp5zqu8kscfcvh32qk", + "refNftAC": "fae686ea8f21d567841d703dea4d4221c2af071a6f2b433ff07c0af2.8309f9861928a55d37e84f6594b878941edce5e351f7904c2c63b559bde45c5c", + "scriptRef": "be6f8dc16d4e8d5aad566ff6b5ffefdda574817a60d503e2a0ea95f773175050#2", + "nftPolicyRef": "be6f8dc16d4e8d5aad566ff6b5ffefdda574817a60d503e2a0ea95f773175050#1" }, - "strategy":"OneSellToManyBuy", - "scanDelay":40000000, - "maxOrderMatches":5, - "maxTxsPerIteration":5, - "randomizeMatchesFound":true, - "scanTokens":[ + "strategy": "OneSellToManyBuy", + "scanDelay": 40000000, + "maxOrderMatches": 5, + "maxTxsPerIteration": 4, + "randomizeMatchesFound": true, + "scanTokens": [ { - "commodityAsset":"c6e65ba7878b2f8ea0ad39287d3e2fd256dc5c4160fc19bdf4c4d87e.7447454e53", - "currencyAsset":"lovelace" + "commodityAsset": "c6e65ba7878b2f8ea0ad39287d3e2fd256dc5c4160fc19bdf4c4d87e.7447454e53", + "currencyAsset": "lovelace" } ] } @@ -353,7 +351,7 @@ the private signing key, the verification key, and the wallet address on the preprod testnet. You can claim some **preprod** lovelaces using the [faucet](https://docs.cardano.org/cardano-testnet/tools/faucet/). -It's **recomended** to create and setup a `collateral`. A UTxO with 5 ADAs will +It's **recommended** to create and setup a `collateral`. A UTxO with 5 ADAs will do the work. But as we mentioned the `collateral` config field is optional. #### Deployed Contract @@ -365,29 +363,24 @@ that is completely placed on the blockchain. That is the validator and the minti ##### Preprod ```json { - "validatorRefs":{ - "refAddr":"addr_test1wpgexmeunzsykesf42d4eqet5yvzeap6trjnflxqtkcf66g0kpnxt", - "refNftAC":"fae686ea8f21d567841d703dea4d4221c2af071a6f2b433ff07c0af2.e6a295bb83d06f53fcf91151f54acec0a63fbd6f0d924206d5d012e6da3b72af", - "refNftUtxoRef":"39f987a6beb9cc4c45bba149a21c28068f640f3593f15f8157f0b6022b431977#0", - "scriptRef":"39f987a6beb9cc4c45bba149a21c28068f640f3593f15f8157f0b6022b431977#1", - "nftPolicyRef":"39f987a6beb9cc4c45bba149a21c28068f640f3593f15f8157f0b6022b431977#0" + "validatorRefs": { + "refAddr": "addr_test1wrgvy8fermjrruaf7fnndtmpuw4xx4cnvfqjp5zqu8kscfcvh32qk", + "refNftAC": "fae686ea8f21d567841d703dea4d4221c2af071a6f2b433ff07c0af2.8309f9861928a55d37e84f6594b878941edce5e351f7904c2c63b559bde45c5c", + "scriptRef": "be6f8dc16d4e8d5aad566ff6b5ffefdda574817a60d503e2a0ea95f773175050#2", + "nftPolicyRef": "be6f8dc16d4e8d5aad566ff6b5ffefdda574817a60d503e2a0ea95f773175050#1" } } ``` ##### Mainnet -> [!NOTE] -> The Smart Order Router configuration for the Cardano Mainnet is going to be available as we approach the Mainnet launch date. - ```json { "validatorRefs":{ - "refAddr":"", - "refNftAC":"", - "refNftUtxoRef":"", - "scriptRef":"", - "nftPolicyRef":"" + "refAddr": "addr1w9zr09hgj7z6vz3d7wnxw0u4x30arsp5k8avlcm84utptls8uqd0z", + "refNftAC": "fae686ea8f21d567841d703dea4d4221c2af071a6f2b433ff07c0af2.4aff78908ef2dce98bfe435fb3fd2529747b1c4564dff5adebedf4e46d0fc63d", + "scriptRef": "062f97b0e64130bc18b4a227299a62d6d59a4ea852a4c90db3de2204a2cd19ea#2", + "nftPolicyRef": "062f97b0e64130bc18b4a227299a62d6d59a4ea852a4c90db3de2204a2cd19ea#1" } } ``` @@ -521,7 +514,7 @@ different SOR instances? does not return proper order matches and there aren't enough tokens in the transaction bucket to pay for an order. -- `GYTxMonadException "partiallyFillPartialOrder: amount x must be smaller than offered amount x`, +- `GYTxMonadException "... amount x must be smaller than offered amount x ...`, you are trying to partially fill an order, but the partial fill amount is the max volume of the order. Use [`CompleteFill`](./geniusyield-orderbot-framework/src/GeniusYield/OrderBot/MatchingStrategy.hs#L98C17-L98C29) instead. See [GeniusYield.OrderBot.MatchingStrategy](./geniusyield-orderbot-framework/src/GeniusYield/OrderBot/MatchingStrategy.hs#L98) for more information. diff --git a/cabal.project b/cabal.project index b155cec..f34675f 100644 --- a/cabal.project +++ b/cabal.project @@ -33,8 +33,8 @@ package strict-containers source-repository-package type: git location: https://github.com/maestro-org/haskell-sdk - tag: 3c4efccb00df8e9fe0313dfb5228e294caa1807d - --sha256: sha256-H7SsK0ri7cmE9d9cUhFZu24s1FWrvMuVlKzplFfBe+4= + tag: fb8e32869f7a8f5fa2585b542e4eb4207eaf735b + --sha256: sha256-LOiC9zPXjPFshKfb/SKkFBkAFnOuQDwCqeDB7ZyB3b0= -- Unfortunately, cardano-node 8.1.2 is constrained with plutus-ledger-api 1.5.0.0 and we would like at least 1.6.0.0. -- This is done in accordance with changes in https://github.com/input-output-hk/cardano-ledger/pull/3430/files. @@ -64,8 +64,8 @@ source-repository-package source-repository-package type: git location: https://github.com/geniusyield/atlas - tag: v0.3.0 - --sha256: sha256-55nIF4S6TyrI1onWH6bu7WDynqHophMZ+QCyw8IFsfQ= + tag: 44b235ac4f4337dd2b2488b4f79f33a987f2be72 + --sha256: sha256-EL7o0yYJIC8KvH0kZ9J0n+h/+w4GI4hCz39QyDA7Udg= source-repository-package type: git @@ -76,12 +76,12 @@ source-repository-package cardano-simple psm --- Temporary until the changes are merged upstream. Delete this afterwards. +-- This should be present for in hackage index state >= 18th of september, 23. Remove it when we update to it. source-repository-package type: git - location: https://github.com/sourabhxyz/blockfrost-haskell - tag: d1ace8d71512a70f37131e4d4c4d5ba67a6324c9 - --sha256: sha256-GXKuRyBnpITjxRDHauJwpjQu8qX4UPLud/opc+Mi9Fg= + location: https://github.com/blockfrost/blockfrost-haskell + tag: 206e1a0f62e2a7cc08d05db8d62cef8dc2fbd98e + --sha256: sha256-R6BP3hwrOBmlRabA3prUuOGkYzETmQIM+K+Oh+fczEY= subdir: blockfrost-api blockfrost-client-core @@ -128,8 +128,8 @@ source-repository-package source-repository-package type: git location: https://github.com/paolino/openapi3 - tag: c30d0de6875d75edd64d1aac2272886528bc492d - --sha256: 0b0fzj5vrnfrc8qikabxhsnp4p8lrjpssblbh2rb7aji5hzzfli9 + tag: f22c31611c295637a3e72b341cd1c56d1d87b993 + --sha256: 10l7wlaz9rcr3fysi1vwg7qqa826bb7nidkpx9jy1q7ja7ddw47i source-repository-package type: git diff --git a/compiled-scripts/minting-policy b/compiled-scripts/minting-policy index 95a254e..8b8521b 100644 --- a/compiled-scripts/minting-policy +++ b/compiled-scripts/minting-policy @@ -1,11 +1,12 @@ { - "cborHex": "590c6e590c6b01000032323232323232323232323232323232323232323232323232323232323232323232323232222232323232323232325333023302a30300021323232323232325333028001161533302a5330223302623375e6e9cc0b0020dd39816181718160008020a998111aba3302c00115330223032375a6070605a002266e3cdd7181b981680080289919999814111299981600089128008a9998171801181880089911180100198188008998018011818000919181a99998111bae3030302e003480008cccc08c009200075a6eac004dd59818181798170011bae302f302e302d00200423232533302e533302e3035303b00214a0266e3c05cdd7181880089919191919191919191919191919299981e18230028a99981e19b870040051533303c3371e6eb8c0fc01c05c54ccc0f0c11c00c54ccc0f0c10c00854ccc0f0c1180044c8c8c8c8c8c8c8c8c94ccc114cc0d88cc0dc8c148dd6982a8009bab30540013330327006eacc120c11cc118068cc0c48dd31981911ba8303a375a0026eac004ccc0c9c019817982400a1ba800e3302f3303b488100488100375066e08cdc0006016a99982299b88304b00333704900000108008a99982299b87304b0033370466e1000c00800840044cdc024004002207c2c66e10008004cdc100119b830070033370400266e0c0140094ccc104c12001452000153330413371200a90000a40022900119981e91129998219825000880109980180099b8600200153330403371200400220022004a66608066e2400800440084004c0c400cc0c0004cdc100324004606060840022c2c2c2c2c2c6eb4c0f801cdd6981e981e0021bad303c003375a607600c6eb4c0e8010c0e0c0e0c0e0004c0dc004c0d8c0d8c0d8004c0d4004c0d0004c0ccc0ccc0c94ccc0c4c0e40084ccccc0b08c888c00800cc100004dd49bae303400100923232320045333034303b304100215333034303d371a6eb8c0dc0044c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c94ccc118c134c14c0044c8c8c8c94ccc128cdd79ba7304c00250561323232533304d337126e34dd71828000a408026464646464646464646464646464640aaa6660b260ba00420a82c6eb4c174004c16c008dd6982d800982c802a99982b182e8010a99982b18309bad305900113205053330543058001104f161615333056305e0021320505333054001104f161630630023061001375460ac00260a800aa6660a260b00042a6660a260b86eb4c1500044c812d4ccc13cc14c0044128585854ccc144c1640084c812d4ccc13c00441285858c178008c170004dd5182880098278008b18270011bad304e001304c006163750607a6eb4c130004c128008dd69825000982a0008b1baa304800130460045333043304a30500021325333044533043304b0011304d0011325333045337126e34dd71824000a408026407ea666086608e002207c2c2c608c0042c6e34dd718230008b18280009baa30440013042002375a608400260800046eb4c100004c0f80114ccc0ecc108c1200084c94ccc0f14cc0ecc10c0044c1140044c94ccc0f4cdc49b8d375c6080002902009901ba99981d981f800881b0b0b181f0010b1b8d375c607c0022c60900026ea8c0f0004c0e80114ccc0dcc0f8c1100084c8c8c8c8c8c8c8c8c8c80e94ccc0f8c10801440e4594ccc0fcc1180084c8c8c8c8c80f54ccc104c11401440f0594ccc108c1240084c8c8c8c8c81014ccc110c12001440fc594ccc114c13000854ccc114c138dc69bae304800113203f53330433047001103e161615333045304d00215333045304e371a6eb8c1200044c80fd4ccc10cc11c00440f8585858c148008c140004dd518228008a99982118250010991919191919020a999822982480108200b1bad30490013047002375a608e002608a0046eb4c11400458c13c008c134004dd518210008a99981f982380109901ca99981e800881c0b0b182600118250009baa303f001303d005533303a30410021533303a3043371a6eb8c0f40044c80d14ccc0e0c0f000440cc585854ccc0e8c10800854ccc0e8c10cdc69bae303d0011320345333038303c001103316161630470023045001375460740022c60880026ea8c0e0004c0d80045858c104004dd50008b0a99981899b87480100084c8c8c8c80114ccc0d0c0ecc10400854ccc0d0c0f4dc69bae303700113232323232323232323232323232323232325333046304d3053001132323232533304a3375e6e9cc13000941584c8c8c94ccc134cdc49b8d375c60a00029020099191919191919191919191919191902aa99982c982e801082a0b1bad305d001305b002375a60b600260b200aa6660ac60ba0042a6660ac60c26eb4c1640044c81414ccc150c160004413c585854ccc158c1780084c81414ccc150004413c5858c18c008c184004dd5182b000982a002a999828982c0010a999828982e1bad305400113204b533304f3053001104a161615333051305900213204b533304f001104a1616305e002305c001375460a2002609e0022c609c0046eb4c138004c13001858dd4181e9bad304c001304a002375a609400260a80022c6ea8c120004c1180114ccc10cc128c1400084c94ccc1114cc10cc12c0044c1340044c94ccc114cdc49b8d375c6090002902009901fa9998219823800881f0b0b18230010b1b8d375c608c0022c60a00026ea8c110004c108008dd6982100098200011bad3040001303e004533303b30423048002132533303c53303b304300113045001132533303d337126e34dd71820000a408026406ea666076607e002206c2c2c607c0042c6e34dd7181f0008b18240009baa303c001303a0045333037303e304400213232323232323232323203a533303e3042005103916533303f30460021323232323203d53330413045005103c16533304230490021323232323204053330443048005103f165333045304c00215333045304e371a6eb8c1200044c80fd4ccc10cc11c00440f8585854ccc114c13400854ccc114c138dc69bae304800113203f53330433047001103e161616305200230500013754608a0022a66608460940042646464646464082a66608a609200420802c6eb4c124004c11c008dd6982380098228011bad304500116304f002304d001375460840022a66607e608e004264072a66607a00220702c2c609800460940026ea8c0fc004c0f40154ccc0e8c10400854ccc0e8c10cdc69bae303d0011320345333038303c001103316161533303a30420021533303a3043371a6eb8c0f40044c80d14ccc0e0c0f000440cc585858c11c008c114004dd5181d0008b18220009baa3038001303600116163041001375400260680022c607c00460780026ea8c0c4c0c0c0c0c0bc00c58c0ec004dd518179816981798168008b198100068028b19998118061bab302c009200116375660566054605460546054605460540106eb0c0a8020dd618148041b92337166eb4c0a0c09c004dd7181418131814000981280098130008a9998119980f918179bad3032001333301d0063756604c00640022c2c203860600026ea8020c088c088004c084c084004c07cc084c07c0114ccc074c090c0a80084dd718100008b18150009baa301e301d301c001223232533301a3375e00698101000001e133005002374c6600a00200660386036603400660366032004446601c66ec000800407088cc04c894ccc0580040704cc03ccdd81812980d80098021813180d8009801180d000800911199802111ba63330062237506600e6eb4008dd68009bab00237560020040024446666024446600244a66602e00420022666600801060380046036004002444a6660300022660220060042646464a66603a66ebc0080044cc050cdd80011980598158031815801999804006802980f8020a99980e99b90375c0046eb80044cc050018cccc02003000cc07c0100144cc05000ccccc020030018014c07c010c0a4008c0a0010c07400401400800488004880088c034894ccc0400045288a998051801980a80089801180a0009299980799b89001480044c0080044004dc0a40004a66601a66e2400520001610012322223300d2253330100011005153330123375e603e602a00200c260086040602a0022600460280020026ea400488cdd2a4000660066ea4008cc00cdd4800a5eb815d0111998050010008a504988cc0088c888c00800cdd5980b0009ba90012223300522533300800112250011533300a3375e602e601a0020082600a601a0022600460180020024600444a66600a002294054cc018c00cc0280044c008c0240048c8c0088cc0080080048c0088cc00800800555cf9198010008a5157344601c6ea80055d12ba110014bd6f7b6301b8248008dc3a40006e1d2002370e901c1b8848000dc4a400026e9d2f5c0aae7555cf1", + "cborHex": "591157591154010000323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323222222323232323232323232325333038303f304600213232323232323232323232323232533304700116153330465330393303e23303900f304630410010081533039303530440011533039304e375a60aa608a002266e3cdd7182a18228008048991999821918281981c1bab30473046304200100200823232533304a533304a3051305800214a0266e3c084dd7182480089919191919191919191919191919191919191919191919191919191919191929998349838983b80109919192999836183b8098a99983619b870120131533306c3371e6eb8c1ac0680bc54ccc1b0c1cc04454ccc1b0c1dc03c54ccc1b0c15ccc17801c01854ccc1b1288a99983619baf00e00a1533306c3370e01a0522a6660d86609e6eacc1acc1a8c198094c120ccc149c0198220039ba801330483330527006608804c0e060906660a4e00cc110cc16522010048810037500506666608a00e00c0140120102a6660d860e66eb402054ccc1b14cc17ccdc48159bad00a133712609c66098609605460940266eb402454ccc1b0c1cc040418458585858585858585858585858c1e8008c1e0004dd5183400a8991929998359839183c8010a999835983b0090a99983599b870110121533306b3371e6eb8c1a80640b854ccc1acc1c804054ccc1acc1d803854ccc1acc158cc17401801454ccc1accdc49bad306a003375a60d40022a6660d666ebc03402454ccc1accdc38060140a999835998271bab306a3069306502430473330517006608600c6ea0048c11cccc145c0198218128379823999828b803304333058488100488100375004e6666608800c00a01201000e2a6660d660e46eb401c54ccc1ad4cc178cdc48151bad009133712609a66096609405260920246eb402054ccc1acc1c803c41805858585858585858585858585854ccc1acc1d804854ccc1accdc38088090a99983599b8f375c60d403205c2a6660d660e40202a6660d660ec01c2a6660d660ac660ba00c00a2a6660d694454ccc1accdd78068048a99983599b8700c0281533306b3304e375660d460d260ca048608e6660a2e00cc10c018dd40091823999828b803304302506f304733305170066086660b091100488100375004e6666608800c00a01201000e2a6660d660e46eb401c54ccc1ad4cc178cdc48151bad009133712609a66096609405260920246eb402054ccc1acc1c803c418058585858585858585858585858c1e4004dd5183400a983b8009baa3066014306501630640183063306200330620023061002305f001305a305f008375a60bc01060ba0106eb4c170c15cc170030dd6982d982d0021bad305a006375a60b20186eb4c160028c158004c154004c150004c14c004c148004c144004c140004c13c004c138004c134004c130004c12cc12cc11d4ccc134c1540084ccccc1108c888c00800cc174004dd49bae304c00100d232323200453330503057305e00215333050305a371a6eb8c13c0044c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c94ccc188c1a4c1c00044c8c8c8c94ccc198cdd79ba7306400250731325333067337126e34dd71833000a4080264646464646464646464646464646464646464646464640e6a6660fc60f600420e42c6eb4c1ec004c1e40114ccc1e8c20404c220040084c8c8c8c8c8c81d54ccc20004c1f400841d058dd6983e800983d8011bad307b0013079002375a60f20022c6110020026ea8c1dc004c1d4008dd6983a80098398011bad30730013071002375a60e200260de00aa6660e060ee0042a6660e060f86eb4c1bc0044c81994ccc1c4c1b80044194585854ccc1c0c1e00084c81994ccc1c400441945858c1f8008c1f0004dd518360009835002a99983598390010a999835983b9bad306a001132061533306c3069001106016161533306b3073002132061533306c0011060161630790023077001375460ce00260ca0022c60c800c2c6ea0c144dd6983200098310011bad3062001307100116375460c000260bc008a6660be60cc60da004264a6660c0a660b260ce002260d4002264a6660c266e24dc69bae3060001481004c815d4ccc188c17c00441585858c17800858dc69bae305e00116306d001375460b800260b40046eb4c168004c160008dd6982c000982b002299982b982f1832801099299982c299828982f80089831000899299982c99b89371a6eb8c160005204013204f533305a3057001104e1616305600216371a6eb8c15800458c194004dd5182a00098290022999829982d1830801099191919191919191919029299982e982d00288288b299982d98310010991919191902aa999830182e802882a0b299982f18328010991919191902c29998319830002882b8b299983098340010a99983098359b8d375c60c00022640aea6660c460be00220ac2c2c2a6660c260d20042a6660c260d66e34dd7183000089902ba999831182f800882b0b0b0b183780118368009baa305d0011533305e3066002132323232323205953330643061002105816375a60c200260be0046eb4c17c004c174008dd6982e8008b183600118350009baa305a0011533305b3063002132051533305c0011050161630690023067001375460ae00260aa00aa6660ac60ba0042a6660ac60c06e34dd7182a800899026299982b982a00088258b0b0a99982b182f0010a99982b18301b8d375c60aa002264098a6660ae60a800220962c2c2c60c800460c40026ea8c14800458c184004dd5182800098270008b0b182f0009baa001161533304d305600213232323200453330503057305e00215333050305a371a6eb8c13c0044c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c94ccc188c1a4c1c00044c8c8c8c94ccc198cdd79ba7306400250731325333067337126e34dd71833000a4080264646464646464646464646464646464646464646464640e6a6660fc60f600420e42c6eb4c1ec004c1e40114ccc1e8c20404c220040084c8c8c8c8c8c81d54ccc20004c1f400841d058dd6983e800983d8011bad307b0013079002375a60f20022c6110020026ea8c1dc004c1d4008dd6983a80098398011bad30730013071002375a60e200260de00aa6660e060ee0042a6660e060f86eb4c1bc0044c81994ccc1c4c1b80044194585854ccc1c0c1e00084c81994ccc1c400441945858c1f8008c1f0004dd518360009835002a99983598390010a999835983b9bad306a001132061533306c3069001106016161533306b3073002132061533306c0011060161630790023077001375460ce00260ca0022c60c800c2c6ea0c144dd6983200098310011bad3062001307100116375460c000260bc008a6660be60cc60da004264a6660c0a660b260ce002260d4002264a6660c266e24dc69bae3060001481004c815d4ccc188c17c00441585858c17800858dc69bae305e00116306d001375460b800260b40046eb4c168004c160008dd6982c000982b002299982b982f1832801099299982c299828982f80089831000899299982c99b89371a6eb8c160005204013204f533305a3057001104e1616305600216371a6eb8c15800458c194004dd5182a00098290022999829982d1830801099191919191919191919029299982e982d00288288b299982d98310010991919191902aa999830182e802882a0b299982f18328010991919191902c29998319830002882b8b299983098340010a99983098359b8d375c60c00022640aea6660c460be00220ac2c2c2a6660c260d20042a6660c260d66e34dd7183000089902ba999831182f800882b0b0b0b183780118368009baa305d0011533305e3066002132323232323205953330643061002105816375a60c200260be0046eb4c17c004c174008dd6982e8008b183600118350009baa305a0011533305b3063002132051533305c0011050161630690023067001375460ae00260aa00aa6660ac60ba0042a6660ac60c06e34dd7182a800899026299982b982a00088258b0b0a99982b182f0010a99982b18301b8d375c60aa002264098a6660ae60a800220962c2c2c60c800460c40026ea8c14800458c184004dd5182800098270008b0b182f0009baa001304c00116305b0023059001375460926090609060880062c60b00026ea8c11cc108c11cc10800458cc0cc05802458cccc0ec054040800458dd6982198210049bad30420083041008375a60800106eacc0fcc0f8c0d0c0f8030dd6181f0061bac303d00d372466e2cdd6981e181d8009bae303c3037303c001303600430390013038001302d3033333303523253302e3302d303a001011130433302b37566074607200202060686072607060680026eb0c0e001c8c8c94ccc0f0c114c1280084c0ec00458c128004dd5181c981c181c181a181c981c181a0008b181b8008a99981c19818118229bad3048001333302e00800320011616102d304600137540146eacc0d0004c0c8c0c8004c0c4004c0c0004c0acc0c0c0ac0114ccc0c0c0dcc0f80084dd718178008b181f0009baa302d302c3028001223232533302c3375e00605e05c26600e0046e98cc01c00400cc0a8c0a4c09400cc0a4c09000888888ccc049c0198031980ca45004881000033330127006600c00a0046600c008002446464a66605466e24dd6801a400005826600a0046e98cc01400400cc0a0c09cc08c00cc09cc08800888cc054cdd800100081611980111919a999813980b000891280089118010019191118010019ba6001122500133003253330273375e002054244a00224460040066eac00400488cc088894ccc0a00040a44c8ccc014c0dcc09c0088cc05ccdd8181b981400180080108009801181280080091198008012400446464644660020060046eb4c08cc088008dd69811000980e00091180111180191191919191919191911980080180119b820023370600c00666e08004cdc1803001299981598190020a40002a66605666e2401120001480045200233025533302a3371200400220022004a66605466e2400800440084004c028008c024008cdc100200119b8200400225333020337120029000898050008800919299981018011119b88302800233704900000088008a99981018011119b873028002337046008e1000440044cdc0240040026002e1088cc0108cc0148c0b4dd698180009bab302f0013330057000046600646e98cc0108dd418059bad0013756002002446603644a66604200204426601c66ec0c0b8c07c004c010c0bcc07c004c008c0780040048c064894ccc07c0045288a998089801980e80089801180e000911199802111ba63330062237506600e6eb4008dd68009bab00237560020040024446666032446600244a666042004200226666008010603e004603c004002444a66604400226601e0060042646464a66604866ebc0080044cc048cdd8001198059819803181980199980400680298110020a99981219b90375c0046eb80044cc048018cccc02003000cc0880100144cc04800ccccc020030018014c088010c0c4008c0c0010c0800040140080048800488008dc0a4000466602c002941289299980a99b890014800058400488cdd2a4000660066ea4008cc00cdd4800a5eb815d02ba322323333004375c6024601a00490001199980280124000eb4dd58008019bae30113010300c001232222330112253330170011005153330163375e6048602a00200c26008604a602a0022600460280020026ea400488cdd79ba7300a002374e60140024466601e00400229408c02cc02cc02cc02c00526233002232223002003375660360026ea4004888cc024894ccc03c004489400454ccc038cdd7980e1806800802098029806800898011806000800918031129998060008a5015330043003300a00113002300900123300800114a24602c6ea8004c0088894ccc020c03c00440084cc00c004cdc30010009800911299980400089128008a9998039801180300089911180100198030008998018011802800919180111980100100091801119801001000aba25742ae6955cf8801a610100004c010101004bd6f7b6301b8248008dc3a40006e1d2002370e90021b87480e0dc4240006e2520001374e97ae05573aaae79", "description": "DEX.PartialOrderNFT", "params": [ "PlutusLedgerApi.V1.Scripts:ScriptHash", - "GHC.Num.Integer:Integer" + "PlutusLedgerApi.V1.Address:Address", + "PlutusLedgerApi.V1.Value:AssetClass" ], - "rawHex": "590c6b01000032323232323232323232323232323232323232323232323232323232323232323232323232222232323232323232325333023302a30300021323232323232325333028001161533302a5330223302623375e6e9cc0b0020dd39816181718160008020a998111aba3302c00115330223032375a6070605a002266e3cdd7181b981680080289919999814111299981600089128008a9998171801181880089911180100198188008998018011818000919181a99998111bae3030302e003480008cccc08c009200075a6eac004dd59818181798170011bae302f302e302d00200423232533302e533302e3035303b00214a0266e3c05cdd7181880089919191919191919191919191919299981e18230028a99981e19b870040051533303c3371e6eb8c0fc01c05c54ccc0f0c11c00c54ccc0f0c10c00854ccc0f0c1180044c8c8c8c8c8c8c8c8c94ccc114cc0d88cc0dc8c148dd6982a8009bab30540013330327006eacc120c11cc118068cc0c48dd31981911ba8303a375a0026eac004ccc0c9c019817982400a1ba800e3302f3303b488100488100375066e08cdc0006016a99982299b88304b00333704900000108008a99982299b87304b0033370466e1000c00800840044cdc024004002207c2c66e10008004cdc100119b830070033370400266e0c0140094ccc104c12001452000153330413371200a90000a40022900119981e91129998219825000880109980180099b8600200153330403371200400220022004a66608066e2400800440084004c0c400cc0c0004cdc100324004606060840022c2c2c2c2c2c6eb4c0f801cdd6981e981e0021bad303c003375a607600c6eb4c0e8010c0e0c0e0c0e0004c0dc004c0d8c0d8c0d8004c0d4004c0d0004c0ccc0ccc0c94ccc0c4c0e40084ccccc0b08c888c00800cc100004dd49bae303400100923232320045333034303b304100215333034303d371a6eb8c0dc0044c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c94ccc118c134c14c0044c8c8c8c94ccc128cdd79ba7304c00250561323232533304d337126e34dd71828000a408026464646464646464646464646464640aaa6660b260ba00420a82c6eb4c174004c16c008dd6982d800982c802a99982b182e8010a99982b18309bad305900113205053330543058001104f161615333056305e0021320505333054001104f161630630023061001375460ac00260a800aa6660a260b00042a6660a260b86eb4c1500044c812d4ccc13cc14c0044128585854ccc144c1640084c812d4ccc13c00441285858c178008c170004dd5182880098278008b18270011bad304e001304c006163750607a6eb4c130004c128008dd69825000982a0008b1baa304800130460045333043304a30500021325333044533043304b0011304d0011325333045337126e34dd71824000a408026407ea666086608e002207c2c2c608c0042c6e34dd718230008b18280009baa30440013042002375a608400260800046eb4c100004c0f80114ccc0ecc108c1200084c94ccc0f14cc0ecc10c0044c1140044c94ccc0f4cdc49b8d375c6080002902009901ba99981d981f800881b0b0b181f0010b1b8d375c607c0022c60900026ea8c0f0004c0e80114ccc0dcc0f8c1100084c8c8c8c8c8c8c8c8c8c80e94ccc0f8c10801440e4594ccc0fcc1180084c8c8c8c8c80f54ccc104c11401440f0594ccc108c1240084c8c8c8c8c81014ccc110c12001440fc594ccc114c13000854ccc114c138dc69bae304800113203f53330433047001103e161615333045304d00215333045304e371a6eb8c1200044c80fd4ccc10cc11c00440f8585858c148008c140004dd518228008a99982118250010991919191919020a999822982480108200b1bad30490013047002375a608e002608a0046eb4c11400458c13c008c134004dd518210008a99981f982380109901ca99981e800881c0b0b182600118250009baa303f001303d005533303a30410021533303a3043371a6eb8c0f40044c80d14ccc0e0c0f000440cc585854ccc0e8c10800854ccc0e8c10cdc69bae303d0011320345333038303c001103316161630470023045001375460740022c60880026ea8c0e0004c0d80045858c104004dd50008b0a99981899b87480100084c8c8c8c80114ccc0d0c0ecc10400854ccc0d0c0f4dc69bae303700113232323232323232323232323232323232325333046304d3053001132323232533304a3375e6e9cc13000941584c8c8c94ccc134cdc49b8d375c60a00029020099191919191919191919191919191902aa99982c982e801082a0b1bad305d001305b002375a60b600260b200aa6660ac60ba0042a6660ac60c26eb4c1640044c81414ccc150c160004413c585854ccc158c1780084c81414ccc150004413c5858c18c008c184004dd5182b000982a002a999828982c0010a999828982e1bad305400113204b533304f3053001104a161615333051305900213204b533304f001104a1616305e002305c001375460a2002609e0022c609c0046eb4c138004c13001858dd4181e9bad304c001304a002375a609400260a80022c6ea8c120004c1180114ccc10cc128c1400084c94ccc1114cc10cc12c0044c1340044c94ccc114cdc49b8d375c6090002902009901fa9998219823800881f0b0b18230010b1b8d375c608c0022c60a00026ea8c110004c108008dd6982100098200011bad3040001303e004533303b30423048002132533303c53303b304300113045001132533303d337126e34dd71820000a408026406ea666076607e002206c2c2c607c0042c6e34dd7181f0008b18240009baa303c001303a0045333037303e304400213232323232323232323203a533303e3042005103916533303f30460021323232323203d53330413045005103c16533304230490021323232323204053330443048005103f165333045304c00215333045304e371a6eb8c1200044c80fd4ccc10cc11c00440f8585854ccc114c13400854ccc114c138dc69bae304800113203f53330433047001103e161616305200230500013754608a0022a66608460940042646464646464082a66608a609200420802c6eb4c124004c11c008dd6982380098228011bad304500116304f002304d001375460840022a66607e608e004264072a66607a00220702c2c609800460940026ea8c0fc004c0f40154ccc0e8c10400854ccc0e8c10cdc69bae303d0011320345333038303c001103316161533303a30420021533303a3043371a6eb8c0f40044c80d14ccc0e0c0f000440cc585858c11c008c114004dd5181d0008b18220009baa3038001303600116163041001375400260680022c607c00460780026ea8c0c4c0c0c0c0c0bc00c58c0ec004dd518179816981798168008b198100068028b19998118061bab302c009200116375660566054605460546054605460540106eb0c0a8020dd618148041b92337166eb4c0a0c09c004dd7181418131814000981280098130008a9998119980f918179bad3032001333301d0063756604c00640022c2c203860600026ea8020c088c088004c084c084004c07cc084c07c0114ccc074c090c0a80084dd718100008b18150009baa301e301d301c001223232533301a3375e00698101000001e133005002374c6600a00200660386036603400660366032004446601c66ec000800407088cc04c894ccc0580040704cc03ccdd81812980d80098021813180d8009801180d000800911199802111ba63330062237506600e6eb4008dd68009bab00237560020040024446666024446600244a66602e00420022666600801060380046036004002444a6660300022660220060042646464a66603a66ebc0080044cc050cdd80011980598158031815801999804006802980f8020a99980e99b90375c0046eb80044cc050018cccc02003000cc07c0100144cc05000ccccc020030018014c07c010c0a4008c0a0010c07400401400800488004880088c034894ccc0400045288a998051801980a80089801180a0009299980799b89001480044c0080044004dc0a40004a66601a66e2400520001610012322223300d2253330100011005153330123375e603e602a00200c260086040602a0022600460280020026ea400488cdd2a4000660066ea4008cc00cdd4800a5eb815d0111998050010008a504988cc0088c888c00800cdd5980b0009ba90012223300522533300800112250011533300a3375e602e601a0020082600a601a0022600460180020024600444a66600a002294054cc018c00cc0280044c008c0240048c8c0088cc0080080048c0088cc00800800555cf9198010008a5157344601c6ea80055d12ba110014bd6f7b6301b8248008dc3a40006e1d2002370e901c1b8848000dc4a400026e9d2f5c0aae7555cf1", + "rawHex": "591154010000323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323222222323232323232323232325333038303f304600213232323232323232323232323232533304700116153330465330393303e23303900f304630410010081533039303530440011533039304e375a60aa608a002266e3cdd7182a18228008048991999821918281981c1bab30473046304200100200823232533304a533304a3051305800214a0266e3c084dd7182480089919191919191919191919191919191919191919191919191919191919191929998349838983b80109919192999836183b8098a99983619b870120131533306c3371e6eb8c1ac0680bc54ccc1b0c1cc04454ccc1b0c1dc03c54ccc1b0c15ccc17801c01854ccc1b1288a99983619baf00e00a1533306c3370e01a0522a6660d86609e6eacc1acc1a8c198094c120ccc149c0198220039ba801330483330527006608804c0e060906660a4e00cc110cc16522010048810037500506666608a00e00c0140120102a6660d860e66eb402054ccc1b14cc17ccdc48159bad00a133712609c66098609605460940266eb402454ccc1b0c1cc040418458585858585858585858585858c1e8008c1e0004dd5183400a8991929998359839183c8010a999835983b0090a99983599b870110121533306b3371e6eb8c1a80640b854ccc1acc1c804054ccc1acc1d803854ccc1acc158cc17401801454ccc1accdc49bad306a003375a60d40022a6660d666ebc03402454ccc1accdc38060140a999835998271bab306a3069306502430473330517006608600c6ea0048c11cccc145c0198218128379823999828b803304333058488100488100375004e6666608800c00a01201000e2a6660d660e46eb401c54ccc1ad4cc178cdc48151bad009133712609a66096609405260920246eb402054ccc1acc1c803c41805858585858585858585858585854ccc1acc1d804854ccc1accdc38088090a99983599b8f375c60d403205c2a6660d660e40202a6660d660ec01c2a6660d660ac660ba00c00a2a6660d694454ccc1accdd78068048a99983599b8700c0281533306b3304e375660d460d260ca048608e6660a2e00cc10c018dd40091823999828b803304302506f304733305170066086660b091100488100375004e6666608800c00a01201000e2a6660d660e46eb401c54ccc1ad4cc178cdc48151bad009133712609a66096609405260920246eb402054ccc1acc1c803c418058585858585858585858585858c1e4004dd5183400a983b8009baa3066014306501630640183063306200330620023061002305f001305a305f008375a60bc01060ba0106eb4c170c15cc170030dd6982d982d0021bad305a006375a60b20186eb4c160028c158004c154004c150004c14c004c148004c144004c140004c13c004c138004c134004c130004c12cc12cc11d4ccc134c1540084ccccc1108c888c00800cc174004dd49bae304c00100d232323200453330503057305e00215333050305a371a6eb8c13c0044c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c94ccc188c1a4c1c00044c8c8c8c94ccc198cdd79ba7306400250731325333067337126e34dd71833000a4080264646464646464646464646464646464646464646464640e6a6660fc60f600420e42c6eb4c1ec004c1e40114ccc1e8c20404c220040084c8c8c8c8c8c81d54ccc20004c1f400841d058dd6983e800983d8011bad307b0013079002375a60f20022c6110020026ea8c1dc004c1d4008dd6983a80098398011bad30730013071002375a60e200260de00aa6660e060ee0042a6660e060f86eb4c1bc0044c81994ccc1c4c1b80044194585854ccc1c0c1e00084c81994ccc1c400441945858c1f8008c1f0004dd518360009835002a99983598390010a999835983b9bad306a001132061533306c3069001106016161533306b3073002132061533306c0011060161630790023077001375460ce00260ca0022c60c800c2c6ea0c144dd6983200098310011bad3062001307100116375460c000260bc008a6660be60cc60da004264a6660c0a660b260ce002260d4002264a6660c266e24dc69bae3060001481004c815d4ccc188c17c00441585858c17800858dc69bae305e00116306d001375460b800260b40046eb4c168004c160008dd6982c000982b002299982b982f1832801099299982c299828982f80089831000899299982c99b89371a6eb8c160005204013204f533305a3057001104e1616305600216371a6eb8c15800458c194004dd5182a00098290022999829982d1830801099191919191919191919029299982e982d00288288b299982d98310010991919191902aa999830182e802882a0b299982f18328010991919191902c29998319830002882b8b299983098340010a99983098359b8d375c60c00022640aea6660c460be00220ac2c2c2a6660c260d20042a6660c260d66e34dd7183000089902ba999831182f800882b0b0b0b183780118368009baa305d0011533305e3066002132323232323205953330643061002105816375a60c200260be0046eb4c17c004c174008dd6982e8008b183600118350009baa305a0011533305b3063002132051533305c0011050161630690023067001375460ae00260aa00aa6660ac60ba0042a6660ac60c06e34dd7182a800899026299982b982a00088258b0b0a99982b182f0010a99982b18301b8d375c60aa002264098a6660ae60a800220962c2c2c60c800460c40026ea8c14800458c184004dd5182800098270008b0b182f0009baa001161533304d305600213232323200453330503057305e00215333050305a371a6eb8c13c0044c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c94ccc188c1a4c1c00044c8c8c8c94ccc198cdd79ba7306400250731325333067337126e34dd71833000a4080264646464646464646464646464646464646464646464640e6a6660fc60f600420e42c6eb4c1ec004c1e40114ccc1e8c20404c220040084c8c8c8c8c8c81d54ccc20004c1f400841d058dd6983e800983d8011bad307b0013079002375a60f20022c6110020026ea8c1dc004c1d4008dd6983a80098398011bad30730013071002375a60e200260de00aa6660e060ee0042a6660e060f86eb4c1bc0044c81994ccc1c4c1b80044194585854ccc1c0c1e00084c81994ccc1c400441945858c1f8008c1f0004dd518360009835002a99983598390010a999835983b9bad306a001132061533306c3069001106016161533306b3073002132061533306c0011060161630790023077001375460ce00260ca0022c60c800c2c6ea0c144dd6983200098310011bad3062001307100116375460c000260bc008a6660be60cc60da004264a6660c0a660b260ce002260d4002264a6660c266e24dc69bae3060001481004c815d4ccc188c17c00441585858c17800858dc69bae305e00116306d001375460b800260b40046eb4c168004c160008dd6982c000982b002299982b982f1832801099299982c299828982f80089831000899299982c99b89371a6eb8c160005204013204f533305a3057001104e1616305600216371a6eb8c15800458c194004dd5182a00098290022999829982d1830801099191919191919191919029299982e982d00288288b299982d98310010991919191902aa999830182e802882a0b299982f18328010991919191902c29998319830002882b8b299983098340010a99983098359b8d375c60c00022640aea6660c460be00220ac2c2c2a6660c260d20042a6660c260d66e34dd7183000089902ba999831182f800882b0b0b0b183780118368009baa305d0011533305e3066002132323232323205953330643061002105816375a60c200260be0046eb4c17c004c174008dd6982e8008b183600118350009baa305a0011533305b3063002132051533305c0011050161630690023067001375460ae00260aa00aa6660ac60ba0042a6660ac60c06e34dd7182a800899026299982b982a00088258b0b0a99982b182f0010a99982b18301b8d375c60aa002264098a6660ae60a800220962c2c2c60c800460c40026ea8c14800458c184004dd5182800098270008b0b182f0009baa001304c00116305b0023059001375460926090609060880062c60b00026ea8c11cc108c11cc10800458cc0cc05802458cccc0ec054040800458dd6982198210049bad30420083041008375a60800106eacc0fcc0f8c0d0c0f8030dd6181f0061bac303d00d372466e2cdd6981e181d8009bae303c3037303c001303600430390013038001302d3033333303523253302e3302d303a001011130433302b37566074607200202060686072607060680026eb0c0e001c8c8c94ccc0f0c114c1280084c0ec00458c128004dd5181c981c181c181a181c981c181a0008b181b8008a99981c19818118229bad3048001333302e00800320011616102d304600137540146eacc0d0004c0c8c0c8004c0c4004c0c0004c0acc0c0c0ac0114ccc0c0c0dcc0f80084dd718178008b181f0009baa302d302c3028001223232533302c3375e00605e05c26600e0046e98cc01c00400cc0a8c0a4c09400cc0a4c09000888888ccc049c0198031980ca45004881000033330127006600c00a0046600c008002446464a66605466e24dd6801a400005826600a0046e98cc01400400cc0a0c09cc08c00cc09cc08800888cc054cdd800100081611980111919a999813980b000891280089118010019191118010019ba6001122500133003253330273375e002054244a00224460040066eac00400488cc088894ccc0a00040a44c8ccc014c0dcc09c0088cc05ccdd8181b981400180080108009801181280080091198008012400446464644660020060046eb4c08cc088008dd69811000980e00091180111180191191919191919191911980080180119b820023370600c00666e08004cdc1803001299981598190020a40002a66605666e2401120001480045200233025533302a3371200400220022004a66605466e2400800440084004c028008c024008cdc100200119b8200400225333020337120029000898050008800919299981018011119b88302800233704900000088008a99981018011119b873028002337046008e1000440044cdc0240040026002e1088cc0108cc0148c0b4dd698180009bab302f0013330057000046600646e98cc0108dd418059bad0013756002002446603644a66604200204426601c66ec0c0b8c07c004c010c0bcc07c004c008c0780040048c064894ccc07c0045288a998089801980e80089801180e000911199802111ba63330062237506600e6eb4008dd68009bab00237560020040024446666032446600244a666042004200226666008010603e004603c004002444a66604400226601e0060042646464a66604866ebc0080044cc048cdd8001198059819803181980199980400680298110020a99981219b90375c0046eb80044cc048018cccc02003000cc0880100144cc04800ccccc020030018014c088010c0c4008c0c0010c0800040140080048800488008dc0a4000466602c002941289299980a99b890014800058400488cdd2a4000660066ea4008cc00cdd4800a5eb815d02ba322323333004375c6024601a00490001199980280124000eb4dd58008019bae30113010300c001232222330112253330170011005153330163375e6048602a00200c26008604a602a0022600460280020026ea400488cdd79ba7300a002374e60140024466601e00400229408c02cc02cc02cc02c00526233002232223002003375660360026ea4004888cc024894ccc03c004489400454ccc038cdd7980e1806800802098029806800898011806000800918031129998060008a5015330043003300a00113002300900123300800114a24602c6ea8004c0088894ccc020c03c00440084cc00c004cdc30010009800911299980400089128008a9998039801180300089911180100198030008998018011802800919180111980100100091801119801001000aba25742ae6955cf8801a610100004c010101004bd6f7b6301b8248008dc3a40006e1d2002370e90021b87480e0dc4240006e2520001374e97ae05573aaae79", "role": "MintingPolicyRole", "type": "PlutusScriptV2", "version": "ScriptV2" diff --git a/compiled-scripts/partial-order b/compiled-scripts/partial-order index f9518ac..557cada 100644 --- a/compiled-scripts/partial-order +++ b/compiled-scripts/partial-order @@ -1,12 +1,11 @@ { - "cborHex": "590ba2590b9f0100003232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232222222323232323232323232323232323232533303e304530490031323232323232323333041233040008304530410013758608800c4646464666608a464a660826608a6094608c002044260a4660846eacc128c124c118004084c124c120c114004dd61824004919192999826182a182b80109919192999827982b1998238038008048991919191919191919299982c182f001899299982c9982b11299982e0008a501533305b3375e60b400200629444c008c164004dd6182c00a8a99982c99b87008304d4800841385858dd49bae305702913253330593060004132533305a3371090000008a99982d19b880010081533305a337126eb4c16409400454ccc168cc008c100ccc121c0180e800998231981fa4500488100375005ea0062a6660b4666084e24ccc121c00091981b91ba6330382375060a06eb4004dd58009998243803304600737500026608c6607e910100488100375066e000840bcc8c8c8cc168894ccc1800041844c94ccc1814cc158cc14cc17cc178c178004c1a80104cc168c17c0040144dd5982f982f182d982f80109801982f001182d182f000806998271982c91299982f8008b099299982f99baf306b305e002005100113003305d0023069305d00100d4bd70182d182b00a183199826182c982a81719826182c982c182a81719826182c982c182c182a81719826182c981b182a817198261ba8337020100026609860b2606a60aa05c6609860b260b0606a60aa05c6609860b2606860aa05c6609860b260b0606860aa05c6609860b2606660aa05c6609860b260b0606660aa05c6609860b2606460aa05c660986ea0cdc01bad305930583032305502e480092f5c02646464a6660ba60c860d000426464a6660be60ca60d40042a6660be6607c607a6eb4c1780040144150584150c1a8004dd5182e0128a99982e9981e181f9bad305c00100313232533305f3065306a0021533305f3303e303d375a60bc00200a20a82c20a860d40026ea8c17009458c1a0004dd5182d012182c80b8b0b0b0b0b1bad3058003153330593370e010609a90010a99982c99800981f999823b80301c007330453303e48900488100375066e04cdc0998280089981f2441004881003305033045006375000e6607c9110048810002050021323232533305c306230670021533305c3303b303e375a60b600200626464a6660bc60c860d20042a6660bc6607a60786eb4c174004014414c58414cc1a4004dd5182d8120b09919299982f183218348010a99982f1981e981e1bad305d00100510531610533069001375460b604860ce0026ea8c16408cc1600585858c101c48991919982b111299982e80088010991919802a99982f29982a19828982e982e182e000983400309982c182e8008038999826380004375660ba60b860b2004200860b800660b000260b60020ba01266094660aa44a6660b60022c264a6660b666ebcc19cc16800807440044c00cc164008c194c1640040252f5c060ac04e60c400460c00026ea809cc148088dd698288101998249bab305000f00300b3758609e01e6eacc138c134c13402c4110dd729998272999827182a0008a5113370e901c00088010b1b8d375c00260960022c60ae0026ea8c124c120c120c114c124c120c11400458dd59823982318218009823182298210011bae304501016304200130413041304100130403040001303f001303e001303a303e005303d002162323232323232323232323303430470153750a66609066e20c13400ccdc12400000420022a66609066e1cc13400ccdc119b840030020021001133700900100099b840020013370400466e0c01800ccdc100099b830060025333044304a00414800054ccc110cdc480224000290008a4004666080444a66608c6098002200426600600266e180080054ccc10ccdc480100088008801299982199b8900200110021001302d002302c0023045375a607e607c00466e08008dd6981f000981c981e80518240009baa303a3039001303500c375a6070606e002606c002606a002606800260660026064002606200260606060002605e002605c00260540064604e604e60040024604c604c60040024604a604a600400246048604860040024604660466046002446604444a66605000205226603266ec0c0c8c098004c010c0ccc098004c008c0940040048c0b0cc054c0b0cc054c0b12f5c06602a600e9452f5c06602a600c00297ae022323253301b33005302400230240011330053024302300130243023002301f002301e002223232323232323232533302a533302a33710004002294454ccc0a8cdc38010008991929998161819802099b88375a60560046eb4c0ac004528181b802181b0020a5014a22a6660546603a00c00a266605460166052605001060166052605000e944528181a00118198011baa002375400460480046046004603c004603a0044604e60566ea80048c0a0cc044c008004cc044c0a0cc044cdd2a400897ae03301130034a297ae04bd7011813998081814198081ba80014bd701980818012514bd70119ba533301d0014800920004bd70111813198079ba90023300f375200297ae023300223233533301d35746002244a00224460040064644460040066e980044894004cc00c94ccc074cdd7800810091280089118010019bab001001223301822533301e00101f132333005302a301d002233011337606054603c00600200420026004603600200246444666032444a66604000426601246600e0486eacc0b00040044c94ccc08400854cc058cc01c0040904cc0288cc020dd59816800812980f0018991919299981199b8f00200115330193300a0040031330073021006302100515333023337200040022a660326601400804e26600e604200c00a2a660326601404e00626600e00c604200a6eb8c0b4c084010dd7181618100021bab302c301f00237566056603c00400400244666030444a66603e00426601046600e0106eb4c0ac0040044c94ccc08000854cc054cc01c0040204cc0248cc020dd69816000804980e8018991919299981119b8f00200115330183300a0040031330073020006302000515333022337200040022a660306601400801626600e604000c00a2a660306601401600626600e00c604000a6eb8c0b0c080010dd71815980f8021bad302b301e002375a6054603a00400400290001180a11299980d0008a51153300f3003301800113002301700125333016337120029000898050008800911919299980c19baf00301b01a133005002374c6600a002006602c602a6024006602a6022004446600e66ec0008004064888ccc01088dd3199803111ba833007375a0046eb4004dd58011bab0010020012223333011223300122533301900210011333300400830170023016002001222533301a00113300b0030021323232533301c3375e00400226601c66ec0008cc02cc0a0018c0a000cccc020034014c06801054ccc070cdc81bae002375c00226601c00c6666010018006603400800a26601c006666601001800c00a6034008604c004604a008603000200a0040024400244004ae8088c8c8c94ccc048c0640084c8c94ccc050c06cc07c0084cdd79ba7003374e0022940c07c004dd50020a999809180d00109919299980a180e180f801099baf374e0066e9c004528180f8009baa004132325333014301a301f00213375e6e9c00cdd38008a50301f0013754008603a00460360026ea8008dc0a400093111998060010008a5022333003002375c6014600c0026eb8c028c024c018004888cccc01000920002333300500248001d69bab0010032322223300922533300f00110051533300e3375e6032601a00200c260086034601a0022600460180020026ea400488cdd79ba73003002374e6006002460246ea8004c0048894ccc020004489400454ccc01cc008c0180044c888c00800cc0180044cc00c008c0140048c8c0088cc0080080048c0088cc0080080055d12ba15734aae7c40093010100004bd6f7b6301b8248008dc3a40006e1d2002370e90021ba548000dd2a4004aae7555cf1", + "cborHex": "5912f95912f6010000323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232222223232323232323232323232323232323232533304c3054305a002132323232323232333304f23304d0083053304e001375860a400c4646464646464a6660b460c46660a000800400c2646464646464646464646464646464a6660d260e0008264a6660d4660ce44a6660da002294054ccc1b0cdd798358008018a5113002306a001375860d20302a6660d466e1c034c161200213232323232533306f33051333305502300d01501430083333304601000f0030020011533306f53304f3076375a60dc0542660d001860f8660c40e8660c40e8660c40e897ae010631533306f33333304b0140150160744a04666660b00960480024646464646609a6666609802c02a0126ea0cdc09bad00853330753304b002077153330753304a002304730027082002266e040052002100100700530017086608c608a02a4660020060046eb4c1c40d0dd68020a5010631616306d306c003306c002306b00230690013064007161637526eb8c1a00b854ccc1a4c1c40104c94ccc1a8c1d800454ccc1a8cdc400080609929998359981b80098350140a9998359981b0009835013899191919191919191919191999983991299983c80089112800891919a99983d19839802983c80089999983102a9ba9022375660f260f00024a6660f8002244a002264a6660f86108026eb4c22c04c1ec00854ccc1f0c20c04ccc1c8080090004488c00800c48940044894004dd7184480983d000891280089128009299983d99b8f001027132223002233001007004307a00413300530790044a026600860f000600460e660f00040369448c00488c8d4ccc1e94cc1c800c4ccc1e80192825113232233001003002375066e00dd68071bad00933306970000200e246600200401c44646464646464646464a66610a02660ce0080162a66610a0266ebc014c24804cc1e0c21004128cc1e008ccc1e0098cc1e0c2100411ccc1e0dd419b8102701c3307802533078024330783752062660f0610802084660f0610802082660f06ea0cdc01bad30840104048008cc1e0c210040fccc1e004ccc1e0c24804cc1e0dd419b80375a014002660f002e660f06ea0cdc000b00125eb80cc1e0dd400a25eb8054ccc21404cdc49998331119b8000233308401222533308b010011002133003337000049001184400800a40006eacc2580400520000044805041e45858594ccc21004cc1f4090cc1bd221004881001480004cc1e4008cc1bd220100488100330780010233306e001008375661000200aa66610002610e020042c2a66610002611202004260fe002266660ce6eb8c1fc004098800458c23804008c23004004dd5183e183d800983d183b002199834380330620193750012660ca046660c40346ea004058c1d0080cccccc14006406806c0092812513305c3305e488100488100001307102b337006eb4c1c0c1bc0a4004c02001cdd69837183680198368011836001183500098328040b0b183480c8b0b1bad3068003153330693370e01860ae900109929998351981b00098348138a9998351981a80098348130991919299983699827999982981080580980919982e38030060013305600d3750600a01e26464a6660de66666609602802a02c004a660ce002266ebc0081d08ccccc16012c0900048cc1240140044008418c58cc19c02cc1eccc1841cccc1841cccc1841cd2f5c060d804c2c6666608601a01860d600460d600260d660d400260d200260c800e2c2c60d00302c46464646464a6660dc660880040e020022a6660dc6608600460806004e1040044cdc0240040026002e10cc0fcc0f80108cc00400c008dd6983518348011bad30690013063008233054012333057700660a2660a6020028981010100333057700660a20126ea0028004c1d4008c1cc004dd5015983100d98308131830010182f810182f0111bad305d01f333053001005009375660b60186eb0c168030dd5982c982c182c0040827182c182b8011bae30570013055305530513333053232533051330523058001023130613304e375660b060ae00204460a460ae60ac60a40026eb0c1580248c8c94ccc168c18cc1a00084c16400458c1a0004dd5182b982b182b1829182b982b18290008b1bab30553054001304f30543053304f002375c60a60242c60a0002609e609e609e002609c609c002609a0026098002608e609800860960022c60b40026ea8c124c120004c10c03cc118004c114004c110004c10c004c108004c104004c100004c0fc004c0f8004c0f4004c0f0004c0ec004c0e8004c0d400c88c8c94ccc0e4c104c11c008528899804182319816182319816182325eb80cc0b0c01d28a5eb80cc0b0c018dd6981c000a5eb80010c11c004dd5000911919299981c182018230010a5113300730453302b3005375a606e00266056608a6605666e9520044bd701981598032514bd7025eb80010c118004dd5000918209981399ba548008cc09cdd4000a5eb80cc09cc00928a5eb808cdd2999819800a4004900025eb8088c8c94cc0b4cc014c0d0008c0d00044cc014c0d0c0cc004c0d0c0cc008c0b8008c0b400888c8c8c8c8c8c8c8c94ccc0e94ccc0e8cdc40010008a511533303a3370e00400226464a6660786088008266e20dd6981d8011bad303b00114a06094008609200829405288991919299981ea99981e982280109919299981f98239826801099baf374e0066e9c00452818268009baa0081533303d304600213232533303f3048304d00213375e6e9c00cdd38008a50304d001375401026464a66607e608c609a004266ebcdd38019ba700114a0609a0026ea80204ccc0f4c038c0f0c0ec02cc038c0f0c0ec029288a50304b0023049001375400c608e004608c0046ea8008dd5001181a0011819801181680118160011181c181f1baa00122330010024800888c00888c00c88c8c8c8c8c8c8c8c88cc00400c008cdc100119b830060033370400266e0c0180094ccc0e0c0fc01052000153330383371200890000a40022900119819299981b99b8900200110011002533303733712004002200420026014004601200466e08010008cdc10020011299981699b89001480044c06c004400488c00888c00c88cdc399b82001004337040040064460044460064466e20cdc100080219b820020032222233301d7006602e660329110048810000333301d7006602e00a0046602e0080024466604e444a66605c00426602446600e0686eacc0f40040044c94ccc0bc00854cc098cc01c0040d04cc04c8cc020dd5981f00081a98160018991919299981899b8f00200115330293300a004003133007302f006302f00515333031337200040022a660526601400806e26600e605e00c00a2a660526601406e00626600e00c605e00a6eb8c0f8c0bc010dd7181e98170021bab303d302d0023756607860580040040024466604c444a66605a004266022466e2401cdd6981e00080089929998170010a9981299b890010071330122337126eb4c0f4004020c0ac00c4c8c8c94ccc0c0cdc78010008a9981419b89004003133007302e006302e00515333030337200040022a6605066e240100284cc01cc0b801801454cc0a0cdc480500189980380318170029bae303d302e004375c6078605a0086eb4c0f0c0b0008dd6981d9815801001000a40004644460040066eacc0d4004888888cccc0988cc090c0a8c0940040140188c8c8c8c8c8c94ccc0c4cc04ccc074dd598180031bab3030302f00233302070066602444666044e00008dd5982100081b8009980d1980e24410048810000a13008001163756605e0026052a66605e606c0042c2a66605e6070004264646464008a666064607260800042646464646464646464646464064a66607c607600a20622ca6660786086004264646464068a666080607a00820662ca66607c608a6098004264a66607e608c609a002264a66608060866e9cc0f800454ccc100c12cdc69bae303f001132323203753330433040002103616375a6080002607c0062c2c609c0022c6ea8c0f400458c130004dd5181d8008a99981e1822001099018a99981e80088180b0b182500118240009baa303800130360053016375600660286eac008cc0488c8c8c8cdd8299981ca9980c982000189821801898238020b1824002180b1bab30470033301423233760a66607266e24dc69bae3047002481004c11c00858c120008dd698238009bab3046002371a6eb8c110004dd58009819800981880119807919191919191919bb03047007304800730173756608e00ca66606e607c608a008264a666070607e608c002264a66607260786e9cc0dc00454ccc0e4c110dc69bae30380011323232030533303c3039002102f16375a6072002606e0082c2c608e0022c6ea8c0d800858c050dd59822802182200119808919191919bb05333038533018303f003130420031304600416304700430153756608c0066602646466ec14ccc0e0cdc49b8d375c608c0049020098230010b18238011bad30460013756608a0046e34dd718218009bab3043002375460820026eacc0c400458c100004dd500098170008999980b1bae302e00100a23232320045333032303930400021323232323232323232323232032533303e303b005103116533303c30430021323232320345333040303d004103316533303e3045304c002132533303f3046304d00113253330403043374e607c0022a66608060966e34dd7181f800899191901ba9998219820001081b0b1bad3040001303e0031616304e001163754607a0022c60980026ea8c0ec00454ccc0f0c1100084c80c54ccc0f400440c05858c128008c120004dd5181c000981b002980b1bab0033014375600466024464646466ec14ccc0e54cc064c10000c4c10c00c4c11c01058c120010c058dd598238019980a11919bb05333039337126e34dd71823801240802608e0042c60900046eb4c11c004dd598230011b8d375c60880026eac004c0cc004c0c4008cc03c8c8c8c8c8c8c8cdd818238039824003980b9bab30470065333037303e30450041325333038303f30460011325333039303c374e606e0022a66607260886e34dd7181c0008991919018299981e181c80108178b1bad3039001303700416163047001163754606c0042c60286eacc114010c110008cc0448c8c8c8cdd8299981c29980c181f80189821001898230020b1823802180a9bab30460033301323233760a66607066e24dc69bae3046002481004c11800858c11c008dd698230009bab3045002371a6eb8c10c004dd598218011baa3041001375660620022c60800026ea800458c0f4008c0ec004dd5181598150009814981280088011181091299981380081509980c98019812800980118120009199810112999813000890020991299981398008010b0998021812801919b90001003375c60666048002002494094ccc088cc0188cc0180bc0040044004588cc0840045289180e91129998120008801099801998020011811000981080091198021198021b8948000004cc03000800488cc0108c00cdd698178008009119801918019bab302e0010012301922533301f00114a22a6602c6006603a00226004603800244644603644a66604200204826464646464a66604aa66604a6058004294054ccc094c0b40084cccc030dd718120008041198050008028a5013300930240010041375660486046603e604800c2600e604600c606600460620026ea8c084c080c080008c080004c068c07c004894cc058cdd780100209980b80080191980111911180100198150009ba90012223301822533301e00112250011533301d3375e605660380020082600a6038002260046036002002446464a66603666e24dd6801a400003e26600a0046e98cc01400400cc064c060c05000cc060c04c00888cc02ccdd800100080f111812198051ba90023300a375200297ae0223330057000046600646e98cc0108dd418031bad0013756002002446602444a66603000203626601466ec0c094c058004c010c098c058004c008c054004004dc0a4000444666008446e98ccc01888dd4198039bad002375a0026eac008dd58008010009111999808111980091299980c001080089999802004180b001180a801000911299980c8008998058018010991919299980d99baf00200113300e3376000466016605400c605400666601001a00a60320082a66603666e40dd70011bae00113300e006333300800c003301900400513300e003333300800c00600530190043028002302700430170010050020012200122002574093111998018011bae300c3007001375c60186016600e002444666600800490001199980280124000eb4dd58008019191111980591299980880088028a99980819baf301e300f00100613004301f300f00113002300e001001375200244666014004002294088cdd79ba73003002374e60060024602c6ea8004c0088894ccc020c03c00440084cc00c004cdc30010009800911299980400089128008a9998039801180300089911180100198030008998018011802800919180111980100100091801119801001000aba25742ae6955cf919800a400090011baf374e97ae010024c010100004bd6f7b6301b8748000dc3a40046e1d2004370e901c1b8748100dc4240006e9520005573aaae79", "description": "DEX.PartialOrder", "params": [ "PlutusLedgerApi.V1.Address:Address", - "PlutusLedgerApi.V1.Value:AssetClass", - "GHC.Num.Integer:Integer" + "PlutusLedgerApi.V1.Value:AssetClass" ], - "rawHex": "590b9f0100003232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232222222323232323232323232323232323232533303e304530490031323232323232323333041233040008304530410013758608800c4646464666608a464a660826608a6094608c002044260a4660846eacc128c124c118004084c124c120c114004dd61824004919192999826182a182b80109919192999827982b1998238038008048991919191919191919299982c182f001899299982c9982b11299982e0008a501533305b3375e60b400200629444c008c164004dd6182c00a8a99982c99b87008304d4800841385858dd49bae305702913253330593060004132533305a3371090000008a99982d19b880010081533305a337126eb4c16409400454ccc168cc008c100ccc121c0180e800998231981fa4500488100375005ea0062a6660b4666084e24ccc121c00091981b91ba6330382375060a06eb4004dd58009998243803304600737500026608c6607e910100488100375066e000840bcc8c8c8cc168894ccc1800041844c94ccc1814cc158cc14cc17cc178c178004c1a80104cc168c17c0040144dd5982f982f182d982f80109801982f001182d182f000806998271982c91299982f8008b099299982f99baf306b305e002005100113003305d0023069305d00100d4bd70182d182b00a183199826182c982a81719826182c982c182a81719826182c982c182c182a81719826182c981b182a817198261ba8337020100026609860b2606a60aa05c6609860b260b0606a60aa05c6609860b2606860aa05c6609860b260b0606860aa05c6609860b2606660aa05c6609860b260b0606660aa05c6609860b2606460aa05c660986ea0cdc01bad305930583032305502e480092f5c02646464a6660ba60c860d000426464a6660be60ca60d40042a6660be6607c607a6eb4c1780040144150584150c1a8004dd5182e0128a99982e9981e181f9bad305c00100313232533305f3065306a0021533305f3303e303d375a60bc00200a20a82c20a860d40026ea8c17009458c1a0004dd5182d012182c80b8b0b0b0b0b1bad3058003153330593370e010609a90010a99982c99800981f999823b80301c007330453303e48900488100375066e04cdc0998280089981f2441004881003305033045006375000e6607c9110048810002050021323232533305c306230670021533305c3303b303e375a60b600200626464a6660bc60c860d20042a6660bc6607a60786eb4c174004014414c58414cc1a4004dd5182d8120b09919299982f183218348010a99982f1981e981e1bad305d00100510531610533069001375460b604860ce0026ea8c16408cc1600585858c101c48991919982b111299982e80088010991919802a99982f29982a19828982e982e182e000983400309982c182e8008038999826380004375660ba60b860b2004200860b800660b000260b60020ba01266094660aa44a6660b60022c264a6660b666ebcc19cc16800807440044c00cc164008c194c1640040252f5c060ac04e60c400460c00026ea809cc148088dd698288101998249bab305000f00300b3758609e01e6eacc138c134c13402c4110dd729998272999827182a0008a5113370e901c00088010b1b8d375c00260960022c60ae0026ea8c124c120c120c114c124c120c11400458dd59823982318218009823182298210011bae304501016304200130413041304100130403040001303f001303e001303a303e005303d002162323232323232323232323303430470153750a66609066e20c13400ccdc12400000420022a66609066e1cc13400ccdc119b840030020021001133700900100099b840020013370400466e0c01800ccdc100099b830060025333044304a00414800054ccc110cdc480224000290008a4004666080444a66608c6098002200426600600266e180080054ccc10ccdc480100088008801299982199b8900200110021001302d002302c0023045375a607e607c00466e08008dd6981f000981c981e80518240009baa303a3039001303500c375a6070606e002606c002606a002606800260660026064002606200260606060002605e002605c00260540064604e604e60040024604c604c60040024604a604a600400246048604860040024604660466046002446604444a66605000205226603266ec0c0c8c098004c010c0ccc098004c008c0940040048c0b0cc054c0b0cc054c0b12f5c06602a600e9452f5c06602a600c00297ae022323253301b33005302400230240011330053024302300130243023002301f002301e002223232323232323232533302a533302a33710004002294454ccc0a8cdc38010008991929998161819802099b88375a60560046eb4c0ac004528181b802181b0020a5014a22a6660546603a00c00a266605460166052605001060166052605000e944528181a00118198011baa002375400460480046046004603c004603a0044604e60566ea80048c0a0cc044c008004cc044c0a0cc044cdd2a400897ae03301130034a297ae04bd7011813998081814198081ba80014bd701980818012514bd70119ba533301d0014800920004bd70111813198079ba90023300f375200297ae023300223233533301d35746002244a00224460040064644460040066e980044894004cc00c94ccc074cdd7800810091280089118010019bab001001223301822533301e00101f132333005302a301d002233011337606054603c00600200420026004603600200246444666032444a66604000426601246600e0486eacc0b00040044c94ccc08400854cc058cc01c0040904cc0288cc020dd59816800812980f0018991919299981199b8f00200115330193300a0040031330073021006302100515333023337200040022a660326601400804e26600e604200c00a2a660326601404e00626600e00c604200a6eb8c0b4c084010dd7181618100021bab302c301f00237566056603c00400400244666030444a66603e00426601046600e0106eb4c0ac0040044c94ccc08000854cc054cc01c0040204cc0248cc020dd69816000804980e8018991919299981119b8f00200115330183300a0040031330073020006302000515333022337200040022a660306601400801626600e604000c00a2a660306601401600626600e00c604000a6eb8c0b0c080010dd71815980f8021bad302b301e002375a6054603a00400400290001180a11299980d0008a51153300f3003301800113002301700125333016337120029000898050008800911919299980c19baf00301b01a133005002374c6600a002006602c602a6024006602a6022004446600e66ec0008004064888ccc01088dd3199803111ba833007375a0046eb4004dd58011bab0010020012223333011223300122533301900210011333300400830170023016002001222533301a00113300b0030021323232533301c3375e00400226601c66ec0008cc02cc0a0018c0a000cccc020034014c06801054ccc070cdc81bae002375c00226601c00c6666010018006603400800a26601c006666601001800c00a6034008604c004604a008603000200a0040024400244004ae8088c8c8c94ccc048c0640084c8c94ccc050c06cc07c0084cdd79ba7003374e0022940c07c004dd50020a999809180d00109919299980a180e180f801099baf374e0066e9c004528180f8009baa004132325333014301a301f00213375e6e9c00cdd38008a50301f0013754008603a00460360026ea8008dc0a400093111998060010008a5022333003002375c6014600c0026eb8c028c024c018004888cccc01000920002333300500248001d69bab0010032322223300922533300f00110051533300e3375e6032601a00200c260086034601a0022600460180020026ea400488cdd79ba73003002374e6006002460246ea8004c0048894ccc020004489400454ccc01cc008c0180044c888c00800cc0180044cc00c008c0140048c8c0088cc0080080048c0088cc0080080055d12ba15734aae7c40093010100004bd6f7b6301b8248008dc3a40006e1d2002370e90021ba548000dd2a4004aae7555cf1", + "rawHex": "5912f6010000323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232222223232323232323232323232323232323232533304c3054305a002132323232323232333304f23304d0083053304e001375860a400c4646464646464a6660b460c46660a000800400c2646464646464646464646464646464a6660d260e0008264a6660d4660ce44a6660da002294054ccc1b0cdd798358008018a5113002306a001375860d20302a6660d466e1c034c161200213232323232533306f33051333305502300d01501430083333304601000f0030020011533306f53304f3076375a60dc0542660d001860f8660c40e8660c40e8660c40e897ae010631533306f33333304b0140150160744a04666660b00960480024646464646609a6666609802c02a0126ea0cdc09bad00853330753304b002077153330753304a002304730027082002266e040052002100100700530017086608c608a02a4660020060046eb4c1c40d0dd68020a5010631616306d306c003306c002306b00230690013064007161637526eb8c1a00b854ccc1a4c1c40104c94ccc1a8c1d800454ccc1a8cdc400080609929998359981b80098350140a9998359981b0009835013899191919191919191919191999983991299983c80089112800891919a99983d19839802983c80089999983102a9ba9022375660f260f00024a6660f8002244a002264a6660f86108026eb4c22c04c1ec00854ccc1f0c20c04ccc1c8080090004488c00800c48940044894004dd7184480983d000891280089128009299983d99b8f001027132223002233001007004307a00413300530790044a026600860f000600460e660f00040369448c00488c8d4ccc1e94cc1c800c4ccc1e80192825113232233001003002375066e00dd68071bad00933306970000200e246600200401c44646464646464646464a66610a02660ce0080162a66610a0266ebc014c24804cc1e0c21004128cc1e008ccc1e0098cc1e0c2100411ccc1e0dd419b8102701c3307802533078024330783752062660f0610802084660f0610802082660f06ea0cdc01bad30840104048008cc1e0c210040fccc1e004ccc1e0c24804cc1e0dd419b80375a014002660f002e660f06ea0cdc000b00125eb80cc1e0dd400a25eb8054ccc21404cdc49998331119b8000233308401222533308b010011002133003337000049001184400800a40006eacc2580400520000044805041e45858594ccc21004cc1f4090cc1bd221004881001480004cc1e4008cc1bd220100488100330780010233306e001008375661000200aa66610002610e020042c2a66610002611202004260fe002266660ce6eb8c1fc004098800458c23804008c23004004dd5183e183d800983d183b002199834380330620193750012660ca046660c40346ea004058c1d0080cccccc14006406806c0092812513305c3305e488100488100001307102b337006eb4c1c0c1bc0a4004c02001cdd69837183680198368011836001183500098328040b0b183480c8b0b1bad3068003153330693370e01860ae900109929998351981b00098348138a9998351981a80098348130991919299983699827999982981080580980919982e38030060013305600d3750600a01e26464a6660de66666609602802a02c004a660ce002266ebc0081d08ccccc16012c0900048cc1240140044008418c58cc19c02cc1eccc1841cccc1841cccc1841cd2f5c060d804c2c6666608601a01860d600460d600260d660d400260d200260c800e2c2c60d00302c46464646464a6660dc660880040e020022a6660dc6608600460806004e1040044cdc0240040026002e10cc0fcc0f80108cc00400c008dd6983518348011bad30690013063008233054012333057700660a2660a6020028981010100333057700660a20126ea0028004c1d4008c1cc004dd5015983100d98308131830010182f810182f0111bad305d01f333053001005009375660b60186eb0c168030dd5982c982c182c0040827182c182b8011bae30570013055305530513333053232533051330523058001023130613304e375660b060ae00204460a460ae60ac60a40026eb0c1580248c8c94ccc168c18cc1a00084c16400458c1a0004dd5182b982b182b1829182b982b18290008b1bab30553054001304f30543053304f002375c60a60242c60a0002609e609e609e002609c609c002609a0026098002608e609800860960022c60b40026ea8c124c120004c10c03cc118004c114004c110004c10c004c108004c104004c100004c0fc004c0f8004c0f4004c0f0004c0ec004c0e8004c0d400c88c8c94ccc0e4c104c11c008528899804182319816182319816182325eb80cc0b0c01d28a5eb80cc0b0c018dd6981c000a5eb80010c11c004dd5000911919299981c182018230010a5113300730453302b3005375a606e00266056608a6605666e9520044bd701981598032514bd7025eb80010c118004dd5000918209981399ba548008cc09cdd4000a5eb80cc09cc00928a5eb808cdd2999819800a4004900025eb8088c8c94cc0b4cc014c0d0008c0d00044cc014c0d0c0cc004c0d0c0cc008c0b8008c0b400888c8c8c8c8c8c8c8c94ccc0e94ccc0e8cdc40010008a511533303a3370e00400226464a6660786088008266e20dd6981d8011bad303b00114a06094008609200829405288991919299981ea99981e982280109919299981f98239826801099baf374e0066e9c00452818268009baa0081533303d304600213232533303f3048304d00213375e6e9c00cdd38008a50304d001375401026464a66607e608c609a004266ebcdd38019ba700114a0609a0026ea80204ccc0f4c038c0f0c0ec02cc038c0f0c0ec029288a50304b0023049001375400c608e004608c0046ea8008dd5001181a0011819801181680118160011181c181f1baa00122330010024800888c00888c00c88c8c8c8c8c8c8c8c88cc00400c008cdc100119b830060033370400266e0c0180094ccc0e0c0fc01052000153330383371200890000a40022900119819299981b99b8900200110011002533303733712004002200420026014004601200466e08010008cdc10020011299981699b89001480044c06c004400488c00888c00c88cdc399b82001004337040040064460044460064466e20cdc100080219b820020032222233301d7006602e660329110048810000333301d7006602e00a0046602e0080024466604e444a66605c00426602446600e0686eacc0f40040044c94ccc0bc00854cc098cc01c0040d04cc04c8cc020dd5981f00081a98160018991919299981899b8f00200115330293300a004003133007302f006302f00515333031337200040022a660526601400806e26600e605e00c00a2a660526601406e00626600e00c605e00a6eb8c0f8c0bc010dd7181e98170021bab303d302d0023756607860580040040024466604c444a66605a004266022466e2401cdd6981e00080089929998170010a9981299b890010071330122337126eb4c0f4004020c0ac00c4c8c8c94ccc0c0cdc78010008a9981419b89004003133007302e006302e00515333030337200040022a6605066e240100284cc01cc0b801801454cc0a0cdc480500189980380318170029bae303d302e004375c6078605a0086eb4c0f0c0b0008dd6981d9815801001000a40004644460040066eacc0d4004888888cccc0988cc090c0a8c0940040140188c8c8c8c8c8c94ccc0c4cc04ccc074dd598180031bab3030302f00233302070066602444666044e00008dd5982100081b8009980d1980e24410048810000a13008001163756605e0026052a66605e606c0042c2a66605e6070004264646464008a666064607260800042646464646464646464646464064a66607c607600a20622ca6660786086004264646464068a666080607a00820662ca66607c608a6098004264a66607e608c609a002264a66608060866e9cc0f800454ccc100c12cdc69bae303f001132323203753330433040002103616375a6080002607c0062c2c609c0022c6ea8c0f400458c130004dd5181d8008a99981e1822001099018a99981e80088180b0b182500118240009baa303800130360053016375600660286eac008cc0488c8c8c8cdd8299981ca9980c982000189821801898238020b1824002180b1bab30470033301423233760a66607266e24dc69bae3047002481004c11c00858c120008dd698238009bab3046002371a6eb8c110004dd58009819800981880119807919191919191919bb03047007304800730173756608e00ca66606e607c608a008264a666070607e608c002264a66607260786e9cc0dc00454ccc0e4c110dc69bae30380011323232030533303c3039002102f16375a6072002606e0082c2c608e0022c6ea8c0d800858c050dd59822802182200119808919191919bb05333038533018303f003130420031304600416304700430153756608c0066602646466ec14ccc0e0cdc49b8d375c608c0049020098230010b18238011bad30460013756608a0046e34dd718218009bab3043002375460820026eacc0c400458c100004dd500098170008999980b1bae302e00100a23232320045333032303930400021323232323232323232323232032533303e303b005103116533303c30430021323232320345333040303d004103316533303e3045304c002132533303f3046304d00113253330403043374e607c0022a66608060966e34dd7181f800899191901ba9998219820001081b0b1bad3040001303e0031616304e001163754607a0022c60980026ea8c0ec00454ccc0f0c1100084c80c54ccc0f400440c05858c128008c120004dd5181c000981b002980b1bab0033014375600466024464646466ec14ccc0e54cc064c10000c4c10c00c4c11c01058c120010c058dd598238019980a11919bb05333039337126e34dd71823801240802608e0042c60900046eb4c11c004dd598230011b8d375c60880026eac004c0cc004c0c4008cc03c8c8c8c8c8c8c8cdd818238039824003980b9bab30470065333037303e30450041325333038303f30460011325333039303c374e606e0022a66607260886e34dd7181c0008991919018299981e181c80108178b1bad3039001303700416163047001163754606c0042c60286eacc114010c110008cc0448c8c8c8cdd8299981c29980c181f80189821001898230020b1823802180a9bab30460033301323233760a66607066e24dc69bae3046002481004c11800858c11c008dd698230009bab3045002371a6eb8c10c004dd598218011baa3041001375660620022c60800026ea800458c0f4008c0ec004dd5181598150009814981280088011181091299981380081509980c98019812800980118120009199810112999813000890020991299981398008010b0998021812801919b90001003375c60666048002002494094ccc088cc0188cc0180bc0040044004588cc0840045289180e91129998120008801099801998020011811000981080091198021198021b8948000004cc03000800488cc0108c00cdd698178008009119801918019bab302e0010012301922533301f00114a22a6602c6006603a00226004603800244644603644a66604200204826464646464a66604aa66604a6058004294054ccc094c0b40084cccc030dd718120008041198050008028a5013300930240010041375660486046603e604800c2600e604600c606600460620026ea8c084c080c080008c080004c068c07c004894cc058cdd780100209980b80080191980111911180100198150009ba90012223301822533301e00112250011533301d3375e605660380020082600a6038002260046036002002446464a66603666e24dd6801a400003e26600a0046e98cc01400400cc064c060c05000cc060c04c00888cc02ccdd800100080f111812198051ba90023300a375200297ae0223330057000046600646e98cc0108dd418031bad0013756002002446602444a66603000203626601466ec0c094c058004c010c098c058004c008c054004004dc0a4000444666008446e98ccc01888dd4198039bad002375a0026eac008dd58008010009111999808111980091299980c001080089999802004180b001180a801000911299980c8008998058018010991919299980d99baf00200113300e3376000466016605400c605400666601001a00a60320082a66603666e40dd70011bae00113300e006333300800c003301900400513300e003333300800c00600530190043028002302700430170010050020012200122002574093111998018011bae300c3007001375c60186016600e002444666600800490001199980280124000eb4dd58008019191111980591299980880088028a99980819baf301e300f00100613004301f300f00113002300e001001375200244666014004002294088cdd79ba73003002374e60060024602c6ea8004c0088894ccc020c03c00440084cc00c004cdc30010009800911299980400089128008a9998039801180300089911180100198030008998018011802800919180111980100100091801119801001000aba25742ae6955cf919800a400090011baf374e97ae010024c010100004bd6f7b6301b8748000dc3a40046e1d2004370e901c1b8748100dc4240006e9520005573aaae79", "role": "ValidatorRole", "type": "PlutusScriptV2", "version": "ScriptV2" diff --git a/config-files/atlas-config-blockfrost.json b/config-files/atlas-config-blockfrost.json index eb7aa95..d7f6b22 100644 --- a/config-files/atlas-config-blockfrost.json +++ b/config-files/atlas-config-blockfrost.json @@ -2,7 +2,7 @@ "coreProvider": { "blockfrostKey": "<>" }, - "networkId": "testnet-preprod", + "networkId": "preprod", "logging": [ { "type": { diff --git a/config-files/atlas-config-kupo.json b/config-files/atlas-config-kupo.json index acd3367..358b3cd 100644 --- a/config-files/atlas-config-kupo.json +++ b/config-files/atlas-config-kupo.json @@ -3,7 +3,7 @@ "socketPath": "<>", "kupoUrl": "<>" }, - "networkId": "testnet-preprod", + "networkId": "preprod", "logging": [ { "type": { diff --git a/config-files/atlas-config-maestro.json b/config-files/atlas-config-maestro.json index bd87428..bf37d4b 100644 --- a/config-files/atlas-config-maestro.json +++ b/config-files/atlas-config-maestro.json @@ -2,7 +2,7 @@ "coreProvider": { "maestroToken": "<>" }, - "networkId": "testnet-preprod", + "networkId": "preprod", "logging": [ { "type": { diff --git a/config-files/bot-config.json b/config-files/bot-config.json index 4d23a80..32ea631 100644 --- a/config-files/bot-config.json +++ b/config-files/bot-config.json @@ -1,23 +1,22 @@ { - "signingKeyFP":"bot.skey", - "nftMintingPolicyFP":"compiled-scripts/minting-policy", - "orderValidatorFP":"compiled-scripts/partial-order", - "validatorRefs":{ - "refAddr":"addr_test1wpgexmeunzsykesf42d4eqet5yvzeap6trjnflxqtkcf66g0kpnxt", - "refNftAC":"fae686ea8f21d567841d703dea4d4221c2af071a6f2b433ff07c0af2.e6a295bb83d06f53fcf91151f54acec0a63fbd6f0d924206d5d012e6da3b72af", - "refNftUtxoRef":"39f987a6beb9cc4c45bba149a21c28068f640f3593f15f8157f0b6022b431977#0", - "scriptRef":"39f987a6beb9cc4c45bba149a21c28068f640f3593f15f8157f0b6022b431977#1", - "nftPolicyRef":"39f987a6beb9cc4c45bba149a21c28068f640f3593f15f8157f0b6022b431977#0" - }, - "strategy":"OneSellToManyBuy", - "scanDelay":40000000, - "maxOrderMatches":5, - "maxTxsPerIteration":5, - "randomizeMatchesFound":true, - "scanTokens":[ - { - "commodityAsset":"c6e65ba7878b2f8ea0ad39287d3e2fd256dc5c4160fc19bdf4c4d87e.7447454e53", - "currencyAsset":"lovelace" - } - ] + "signingKeyFP": "bot.skey", + "nftMintingPolicyFP": "compiled-scripts/minting-policy", + "orderValidatorFP": "compiled-scripts/partial-order", + "validatorRefs": { + "refAddr": "addr_test1wrgvy8fermjrruaf7fnndtmpuw4xx4cnvfqjp5zqu8kscfcvh32qk", + "refNftAC": "fae686ea8f21d567841d703dea4d4221c2af071a6f2b433ff07c0af2.8309f9861928a55d37e84f6594b878941edce5e351f7904c2c63b559bde45c5c", + "scriptRef": "be6f8dc16d4e8d5aad566ff6b5ffefdda574817a60d503e2a0ea95f773175050#2", + "nftPolicyRef": "be6f8dc16d4e8d5aad566ff6b5ffefdda574817a60d503e2a0ea95f773175050#1" + }, + "strategy": "OneSellToManyBuy", + "scanDelay": 40000000, + "maxOrderMatches": 5, + "maxTxsPerIteration": 4, + "randomizeMatchesFound": true, + "scanTokens": [ + { + "commodityAsset": "c6e65ba7878b2f8ea0ad39287d3e2fd256dc5c4160fc19bdf4c4d87e.7447454e53", + "currencyAsset": "lovelace" + } + ] } diff --git a/docker-compose.yml b/docker-compose.yml index daf237b..355768f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,7 +8,7 @@ services: {"cborHex": "$PAYMENT_SIGNING_KEY_CBOR_HEX", "type": "PaymentSigningKeyShelley_ed25519", "description": "Payment Signing Key"} BOTC_COLLATERAL: "$COLLATERAL_UTXO_REF" BOTC_CONFIG: | - {"coreProvider": { "socketPath": "/ipc/node.socket", "kupoUrl": "kupo:1442" }, "networkId": "testnet-preprod", "logging": [{ "type": { "tag": "stderr" }, "severity": "Info", "verbosity": "V2" }]} + {"coreProvider": { "socketPath": "/ipc/node.socket", "kupoUrl": "kupo:1442" }, "networkId": "preprod", "logging": [{ "type": { "tag": "stderr" }, "severity": "Info", "verbosity": "V2" }]} restart: always volumes: [node-ipc:/ipc] depends_on: diff --git a/geniusyield-dex-api/geniusyield-dex-api.cabal b/geniusyield-dex-api/geniusyield-dex-api.cabal index ca2d67e..cee1279 100644 --- a/geniusyield-dex-api/geniusyield-dex-api.cabal +++ b/geniusyield-dex-api/geniusyield-dex-api.cabal @@ -101,7 +101,7 @@ library import: common-ghc-opts hs-source-dirs: src exposed-modules: - GeniusYield.DEX.Api.Constants GeniusYield.DEX.Api.PartialOrder + GeniusYield.DEX.Api.PartialOrderConfig GeniusYield.DEX.Api.Utils GeniusYield.DEX.Api.Types diff --git a/geniusyield-dex-api/src/GeniusYield/DEX/Api/Constants.hs b/geniusyield-dex-api/src/GeniusYield/DEX/Api/Constants.hs deleted file mode 100644 index f66a775..0000000 --- a/geniusyield-dex-api/src/GeniusYield/DEX/Api/Constants.hs +++ /dev/null @@ -1,16 +0,0 @@ -{-| -Module : GeniusYield.DEX.Api.Constants -Copyright : (c) 2023 GYELD GMBH -License : Apache 2.0 -Maintainer : support@geniusyield.co -Stability : develop - --} -module GeniusYield.DEX.Api.Constants ( minDeposit ) where - -import GeniusYield.Imports ( Natural ) - --- | Altering this constant will result in a modification of the --- validator address. -minDeposit :: Natural -minDeposit = 2_000_000 diff --git a/geniusyield-dex-api/src/GeniusYield/DEX/Api/PartialOrder.hs b/geniusyield-dex-api/src/GeniusYield/DEX/Api/PartialOrder.hs index 6d8d553..ff7d6a1 100644 --- a/geniusyield-dex-api/src/GeniusYield/DEX/Api/PartialOrder.hs +++ b/geniusyield-dex-api/src/GeniusYield/DEX/Api/PartialOrder.hs @@ -11,65 +11,112 @@ module GeniusYield.DEX.Api.PartialOrder ( PartialOrderDatum (..) , PartialOrderAction (..) , PartialOrderInfo (..) - , minDeposit , partialOrders - , completelyFillPartialOrder - , partiallyFillPartialOrder + , fillMultiplePartialOrders , getPartialOrderInfo + , getPartialOrdersInfos ) where -import Control.Monad.Except (ExceptT (..), runExceptT) -import qualified PlutusLedgerApi.V1 as Plutus -import qualified PlutusLedgerApi.V1.Value as Plutus +import Control.Monad.Except (ExceptT (..), + runExceptT) +import qualified Data.Map.Merge.Strict as Map +import qualified Data.Map.Strict as Map +import qualified Data.Set as Set +import qualified PlutusLedgerApi.V1 as Plutus +import qualified PlutusLedgerApi.V1.Value as Plutus import qualified PlutusTx -import qualified PlutusTx.Prelude as PlutusTx -import qualified PlutusTx.Ratio as PlutusTx +import qualified PlutusTx.AssocMap as PlutusTx +import qualified PlutusTx.Prelude as PlutusTx +import qualified PlutusTx.Ratio as PlutusTx -import GeniusYield.DEX.Api.Constants +import Control.Monad.Error.Class (liftEither) +import Control.Monad.Reader +import Data.Foldable (foldlM) +import Data.Maybe (fromJust) +import GeniusYield.DEX.Api.PartialOrderConfig (PartialOrderConfigInfoF (..), + fetchPartialOrderConfig) import GeniusYield.DEX.Api.Types +import GeniusYield.HTTP.Errors (IsGYApiError (..)) import GeniusYield.Imports import GeniusYield.TxBuilder.Class +import GeniusYield.TxBuilder.Errors (GYTxMonadException, + throwAppError) import GeniusYield.Types -import GeniusYield.TxBuilder.Errors (throwAppError, GYTxMonadException) -import GeniusYield.HTTP.Errors (IsGYApiError (..)) -import Control.Monad.Error.Class (liftEither) -import Control.Monad.Reader ------------------------------------------------------------------------------- --- Partial Order datum +-- Partial Order Datum ------------------------------------------------------------------------------- +-- | Representation of total fees contained in the order. +data PartialOrderContainedFee = PartialOrderContainedFee + { pocfLovelaces :: Integer + -- ^ Fees explicitly charged in lovelaces, like flat lovelace fee collected + -- from maker and taker(s). + , pocfOfferedTokens :: Integer + -- ^ Fees explicitly collected as percentage of offered tokens from maker. + , pocfAskedTokens :: Integer + -- ^ Fees explicitly collected as percentage of asked tokens from taker. + } deriving stock (Generic, Show) + +PlutusTx.unstableMakeIsData ''PartialOrderContainedFee + + +instance Semigroup PartialOrderContainedFee where + (<>) a b = + PartialOrderContainedFee + { pocfLovelaces = pocfLovelaces a + pocfLovelaces b + , pocfOfferedTokens = pocfOfferedTokens a + pocfOfferedTokens b + , pocfAskedTokens = pocfAskedTokens a + pocfAskedTokens b + } + +instance Monoid PartialOrderContainedFee where mempty = PartialOrderContainedFee 0 0 0 + +-- | Datum of the fee output. +data PartialOrderFeeOutput = PartialOrderFeeOutput + { pofdMentionedFees :: PlutusTx.Map Plutus.TxOutRef Plutus.Value + -- ^ Map, mapping order being consumed to the collected fees. + , pofdReservedValue :: Plutus.Value + -- ^ Value reserved in this UTxO which is not to be considered as fees. + , pofdSpentUTxORef :: Maybe Plutus.TxOutRef + -- ^ If not @Nothing@, it mentions the UTxO being consumed, whose value is used to provide for UTxOs minimum ada requirement. + } deriving stock (Generic, Show) + +PlutusTx.unstableMakeIsData ''PartialOrderFeeOutput + data PartialOrderDatum = PartialOrderDatum - { podOwnerKey :: !Plutus.PubKeyHash + { podOwnerKey :: Plutus.PubKeyHash -- ^ Public key hash of the owner. Order cancellations must be signed by this. - , podOwnerAddr :: !Plutus.Address + , podOwnerAddr :: Plutus.Address -- ^ Address of the owner. Payments must be made to this address. - , podOfferedAsset :: !Plutus.AssetClass + , podOfferedAsset :: Plutus.AssetClass -- ^ The asset being offered. - , podOfferedOriginalAmount :: !Integer + , podOfferedOriginalAmount :: Integer -- ^ Original number of units being offered. Initially, this would be same as `podOfferedAmount`. - , podOfferedAmount :: !Integer + , podOfferedAmount :: Integer -- ^ The number of units being offered. - , podAskedAsset :: !Plutus.AssetClass + , podAskedAsset :: Plutus.AssetClass -- ^ The asset being asked for as payment. - , podPrice :: !PlutusTx.Rational + , podPrice :: PlutusTx.Rational -- ^ The price for one unit of the offered asset. - , podMinFilling :: !Integer - -- ^ Minimal number of units of the offered asset that must be paid for in a partial filling. - , podNFT :: !Plutus.TokenName + , podNFT :: Plutus.TokenName -- ^ Token name of the NFT identifying this order. - , podStart :: !(Maybe Plutus.POSIXTime) + , podStart :: Maybe Plutus.POSIXTime -- ^ The time when the order can earliest be filled (optional). - , podEnd :: !(Maybe Plutus.POSIXTime) + , podEnd :: Maybe Plutus.POSIXTime -- ^ The time when the order can latest be filled (optional). - , podFee :: Integer - -- ^ Fee the filler is entitled to take. , podPartialFills :: Integer -- ^ Number of partial fills order has undergone, initially would be 0. - } - deriving stock (Show) - -PlutusTx.makeIsDataIndexed ''PartialOrderDatum [ ('PartialOrderDatum, 0) ] + , podMakerLovelaceFlatFee :: Integer + -- ^ Flat fee (in lovelace) paid by the maker. + , podTakerLovelaceFlatFee :: Integer + -- ^ Flat fee (in lovelace) paid by the taker. + , podContainedFee :: PartialOrderContainedFee + -- ^ Total fees contained in the order. + , podContainedPayment :: Integer + -- ^ Payment (in asked asset) contained in the order. + } deriving stock (Generic, Show) + +PlutusTx.makeIsDataIndexed ''PartialOrderDatum [('PartialOrderDatum, 0)] -- | Exceptions raised while (partially) filling (partial) orders. data FillOrderException @@ -83,16 +130,19 @@ data FillOrderException data PodException = PodNftNotAvailable | PodNonPositiveAmount !Integer | PodNonPositivePrice !GYRational - | PodNonPositiveMinFilling !Integer | PodRequestedAmountGreaterOrEqualToOfferedAmount - { poeReqAmt:: !Natural + { poeReqAmt :: !Natural , poeOfferedAmount :: !Natural } - | PodRequestedAmountLessThanMinFilling - { poeReqAmt:: !Natural - , poeMinFilling :: !Natural - } - | PodFeeNotEnough !Integer + | PodRequestedAmountGreaterThanOfferedAmount {poeReqAmt:: !Natural, poeOfferedAmount :: !Natural} + | PodNonDifferentAssets !GYAssetClass + -- ^ Offered asset is same as asked asset. + | PodEndEarlierThanStart + !GYTime -- ^ Start time. + !GYTime -- ^ End time. + | PodNegativeFrontendFee !GYValue + | PodNotAllOrderRefsPresent -- ^ We couldn't fetch information for some of the given `GYTxOutRef`s. Note that this does not relate to UTxO being spent as depending upon provider, we would fetch information for even those `GYTxOutRef` which have been spent. + !(Set.Set GYTxOutRef) -- ^ Missing output refs. deriving stock Show deriving anyclass (Exception, IsGYApiError) @@ -115,39 +165,60 @@ PlutusTx.makeIsDataIndexed ''PartialOrderAction [ ('PartialCancel, 0) -- Partial Order info ------------------------------------------------------------------------------- +data POIContainedFee = POIContainedFee + { poifLovelaces :: !Natural + , poifOfferedTokens :: !Natural + , poifAskedTokens :: !Natural + } deriving stock (Show, Eq, Generic) + +instance Semigroup POIContainedFee where + (<>) a b = + POIContainedFee + { poifLovelaces = poifLovelaces a + poifLovelaces b + , poifOfferedTokens = poifOfferedTokens a + poifOfferedTokens b + , poifAskedTokens = poifAskedTokens a + poifAskedTokens b + } + +instance Monoid POIContainedFee where mempty = POIContainedFee 0 0 0 + data PartialOrderInfo = PartialOrderInfo - { poiRef :: !GYTxOutRef - -- ^ Reference to the partial order. - , poiOwnerKey :: !GYPubKeyHash - -- ^ Public key hash of the owner. - , poiOwnerAddr :: !GYAddress - -- ^ Address of the owner. - , poiOfferedAsset :: !GYAssetClass - -- ^ The asset being offered. - , poiOfferedOriginalAmount :: !Natural - -- ^ The number of units originally offered. - , poiOfferedAmount :: !Natural - -- ^ The number of units being offered. - , poiAskedAsset :: !GYAssetClass - -- ^ The asset being asked for as payment. - , poiPrice :: !GYRational - -- ^ The price for one unit of the offered asset. - , poiMinFilling :: !Natural - -- ^ Minimal number of units of the asked-for asset that must be paid in a partial filling. - , poiNFT :: !GYTokenName - -- ^ Token name of the NFT identifying this partial order. - , poiFeesDeposits :: !Natural - -- ^ Number of lovelace included for fees and deposits. - , poiStart :: !(Maybe GYTime) - -- ^ The time when the order can earliest be filled (optional). - , poiEnd :: !(Maybe GYTime) - -- ^ The time when the order can latest be filled (optional). - , poiFee :: !Natural - -- ^ The fee for each filling. - , poiPartialFills :: !Natural - -- ^ The number of past partial fills. + { poiRef :: !GYTxOutRef -- ^ Reference to the partial order. + , poiOwnerKey :: !GYPubKeyHash -- ^ Public key hash of the owner. + , poiOwnerAddr :: !GYAddress -- ^ Address of the owner. + , poiOfferedAsset :: !GYAssetClass -- ^ The asset being offered. + , poiOfferedOriginalAmount :: !Natural -- ^ The number of units originally offered. + , poiOfferedAmount :: !Natural -- ^ The number of units being offered. + , poiAskedAsset :: !GYAssetClass -- ^ The asset being asked for as payment. + , poiPrice :: !GYRational -- ^ The price for one unit of the offered asset. + , poiNFT :: !GYTokenName -- ^ Token name of the NFT identifying this partial order. + , poiStart :: !(Maybe GYTime) -- ^ The time when the order can earliest be filled (optional). + , poiEnd :: !(Maybe GYTime) -- ^ The time when the order can latest be filled (optional). + , poiPartialFills :: !Natural -- ^ The number of past partial fills. + , poiMakerLovelaceFlatFee :: !Natural -- ^ Flat fee (in lovelace) paid by the maker. + , poiTakerLovelaceFlatFee :: !Natural -- ^ Flat fee (in lovelace) paid by the taker. + , poiContainedFee :: !POIContainedFee -- ^ Fee contained in the order. + , poiContainedPayment :: !Natural -- ^ Payment (in asked asset) contained in the order. + , poiUTxOValue :: !GYValue -- ^ Total value in the UTxO. + , poiUTxOAddr :: !GYAddress -- ^ Address of the order UTxO. + , poiNFTCS :: !GYMintingPolicyId -- ^ Caching the CS to avoid recalculating for it. } deriving stock (Show, Eq, Generic) +poiContainedFeeToPlutus :: POIContainedFee -> PartialOrderContainedFee +poiContainedFeeToPlutus POIContainedFee {..} = + PartialOrderContainedFee + { pocfLovelaces = fromIntegral poifLovelaces + , pocfOfferedTokens = fromIntegral poifOfferedTokens + , pocfAskedTokens = fromIntegral poifAskedTokens + } + +poiContainedFeeFromPlutus :: PartialOrderContainedFee -> POIContainedFee +poiContainedFeeFromPlutus PartialOrderContainedFee {..} = + POIContainedFee + { poifLovelaces = fromIntegral pocfLovelaces + , poifOfferedTokens = fromIntegral pocfOfferedTokens + , poifAskedTokens = fromIntegral pocfAskedTokens + } + ------------------------------------------------------------------------------- -- Queries ------------------------------------------------------------------------------- @@ -155,7 +226,7 @@ data PartialOrderInfo = PartialOrderInfo -- | List and transform all the partial orders for the given function. partialOrders :: forall a m - . GYApiQueryMonad m + . GYApiQueryMonad m => (PartialOrderInfo -> Maybe a) -- ^ Filter + Transformer function. Nothing means ignore that partial order. -> m [a] @@ -163,7 +234,8 @@ partialOrders pOrderPredicate = do DEXInfo {dexPartialOrderValidator} <- ask addr <- scriptAddress dexPartialOrderValidator - foldM mkPOrderInfo [] =<< utxosAtAddressesWithDatums [addr] + let paymentCred = addressToPaymentCredential addr & fromJust + foldM mkPOrderInfo [] =<< utxosAtPaymentCredentialWithDatums paymentCred where mkPOrderInfo @@ -179,210 +251,268 @@ partialOrders pOrderPredicate = do runExceptT (makePartialOrderInfo utxoRef (utxoAddress, utxoValue, d)) - addPartialOrderInfo :: [a] -> PartialOrderInfo -> [a] + addPartialOrderInfo + :: [a] + -> PartialOrderInfo + -> [a] addPartialOrderInfo pois = maybe pois (:pois) . pOrderPredicate ------------------------------------------------------------------------------- -- Tx building ------------------------------------------------------------------------------- --- | Completely fill a partially-fillable order. -completelyFillPartialOrder - :: (HasCallStack, GYApiMonad m) - => Either GYTxOutRef PartialOrderInfo - -- ^ The order reference. - -> m (GYTxSkeleton PlutusV2) -completelyFillPartialOrder poiSource = do - di@DEXInfo{dexPORefs} <- ask - - oi@PartialOrderInfo {..} <- case poiSource of - Left orderRef -> getPartialOrderInfo orderRef - Right poi -> return poi - - let price = partialOrderPrice oi poiOfferedAmount - feesAndDeposits = valueFromLovelace - $ max 0 - $ toInteger poiFeesDeposits - toInteger poiFee - refScript = maybe mempty mustHaveRefInput (porValidatorRef dexPORefs) - refMinting = maybe mempty mustHaveRefInput (porNftPolicyRef dexPORefs) - - cs <- validFillRangeConstraints poiStart poiEnd - return $ mconcat - [ mustHaveInput (partialOrderInfoToIn oi CompleteFill di) - , mustHaveOutput (partialOrderInfoToPayment oi $ price <> feesAndDeposits) - , mustMint (mintingScript di) nothingRedeemer poiNFT (-1) - , cs - , refScript - , refMinting - , mustHaveRefInput (porRefNftRef dexPORefs) - ] - --- | Partially fill a partially-fillable order. -partiallyFillPartialOrder +{- | Fills multiple orders. If the provided amount of offered tokens to buy in an order is equal to the offered amount, then we completely fill the order. Otherwise, it gets partially filled. +-} +fillMultiplePartialOrders :: (HasCallStack, GYApiMonad m) - => Either GYTxOutRef PartialOrderInfo - -- ^ The order reference. - -> Natural - -- ^ The amount of offered tokens to buy. + => [(Either GYTxOutRef PartialOrderInfo, Natural)] -> m (GYTxSkeleton PlutusV2) -partiallyFillPartialOrder poiSource amt = do - di@DEXInfo{dexNftPolicy, dexPartialOrderValidator, dexPORefs} <- ask - - oi@PartialOrderInfo {..} <- case poiSource of - Left orderRef -> getPartialOrderInfo orderRef - Right poi -> return poi - - outAddr <- scriptAddress dexPartialOrderValidator - - when (amt == 0) . throwAppError - $ PodNonPositiveAmount $ toInteger amt - when (amt >= poiOfferedAmount) . throwAppError - $ PodRequestedAmountGreaterOrEqualToOfferedAmount amt poiOfferedAmount - when (amt < poiMinFilling) . throwAppError - $ PodRequestedAmountLessThanMinFilling amt poiMinFilling - - let od = partialOrderInfoToPartialOrderDatum oi - { poiOfferedAmount = poiOfferedAmount - amt - , poiPartialFills = poiPartialFills + 1 - } - price = partialOrderPrice oi amt - feesAndDeposits = valueFromLovelace - $ max 0 - $ toInteger poiFeesDeposits - - toInteger (poiFee + minDeposit) - v = mconcat - [ valueSingleton poiOfferedAsset (toInteger $ poiOfferedAmount - amt) - , feesAndDeposits - , valueSingleton (GYToken (mintingPolicyId dexNftPolicy) poiNFT) 1 - ] - payment = price <> valueFromLovelace (toInteger minDeposit) - - o = mkGYTxOut outAddr v (datumFromPlutusData od) - refScript = maybe mempty mustHaveRefInput (porValidatorRef dexPORefs) - - cs <- validFillRangeConstraints poiStart poiEnd - return $ mconcat - [ mustHaveInput (partialOrderInfoToIn oi (PartialFill $ toInteger amt) di) - , mustHaveOutput o - , mustHaveOutput (partialOrderInfoToPayment oi payment) - , cs - , refScript - , mustHaveRefInput (porRefNftRef dexPORefs) - ] +fillMultiplePartialOrders eOrders = do + di <- ask + + -- This machinery is needed to accomodate the fact that MatchExecutionInfos may not have the PartialOrderInfo. In that case we must query it from the ref. + let separateOrders :: ([(GYTxOutRef, Natural)], [(PartialOrderInfo, Natural)]) + -> (Either GYTxOutRef PartialOrderInfo, Natural) + -> ([(GYTxOutRef, Natural)], [(PartialOrderInfo, Natural)]) + separateOrders (!refOrders, !poiOrders) (Left ref, n) = ((ref, n) : refOrders, poiOrders) + separateOrders (!refOrders, !poiOrders) (Right poi, n) = (refOrders, (poi, n) : poiOrders) + + (!ordersWithRefAndAmount, !ordersWithPoiAndAmount) = foldl' separateOrders ([],[]) eOrders + ordersWithRefAndAmount' = Map.fromList ordersWithRefAndAmount + queriedOrders <- getPartialOrdersInfos $ Map.keys ordersWithRefAndAmount' + -- Even though we use `dropMissing`, `getPartialOrdersInfos` verify that all entries are present. + let otherOrdersWithPoiAndAmount = Map.elems $ Map.merge Map.dropMissing Map.dropMissing (Map.zipWithMatched (\_ poi amt -> (poi, amt))) queriedOrders ordersWithRefAndAmount' + orders = otherOrdersWithPoiAndAmount ++ ordersWithPoiAndAmount + por = dexPORefs di + + (cfgRef, poci) <- fetchPartialOrderConfig (porRefAddr por) (porRefNft por) + + let buildWithFeeOutput = do + let (!feeOutputMap, !totalContainedFee, !maxTakerFee) = + foldl' + (\(!mapAcc, !feeAcc, !prevMaxTakerFee) (PartialOrderInfo {..}, amtToFill) -> + let curMaxTakerFee = max prevMaxTakerFee poiTakerLovelaceFlatFee in + if amtToFill == poiOfferedAmount then + let orderContainedFee = poiContainedFeeToValue poiContainedFee poiOfferedAsset poiAskedAsset + in (PlutusTx.unionWith (<>) mapAcc (PlutusTx.singleton (txOutRefToPlutus poiRef) (valueToPlutus orderContainedFee)), feeAcc <> orderContainedFee, curMaxTakerFee) + else (mapAcc, feeAcc, curMaxTakerFee) + ) + (PlutusTx.empty, mempty, 0) + orders + fee = totalContainedFee <> valueFromLovelace (fromIntegral maxTakerFee) + feeOutput + | fee == mempty = mempty + | otherwise = + mustHaveOutput $ mkGYTxOut (pociFeeAddr poci) fee $ datumFromPlutusData $ PartialOrderFeeOutput feeOutputMap mempty Nothing + foldlM + (\(!prevSkel) (poi@PartialOrderInfo {..}, amt) -> do + commonCheck amt poiOfferedAmount + cs <- validFillRangeConstraints poiStart poiEnd + let skel = + if amt == poiOfferedAmount then + + let expectedValueOut = expectedPaymentWithDeposit poi True + + in + mustHaveInput (partialOrderInfoToIn poi CompleteFill di) + <> mustHaveOutput (partialOrderInfoToPayment poi expectedValueOut) + <> mustMint (mintingScript di) nothingRedeemer poiNFT (-1) + <> cs + + else + + let price' = partialOrderPrice poi amt + od = partialOrderInfoToPartialOrderDatum poi + { poiOfferedAmount = poiOfferedAmount - amt + , poiPartialFills = poiPartialFills + 1 + , poiContainedPayment = poiContainedPayment + fromIntegral (valueAssetClass price' poiAskedAsset) + } + + expectedValueOut = poiUTxOValue <> price' `valueMinus` valueSingleton poiOfferedAsset (toInteger amt) + o = mkGYTxOut poiUTxOAddr expectedValueOut (datumFromPlutusData od) + + in + mustHaveInput (partialOrderInfoToIn poi (PartialFill $ toInteger amt) di) + <> mustHaveOutput o + <> cs + + pure $! prevSkel <> skel + ) + (mustHaveRefInput cfgRef <> feeOutput) + orders + + + let buildWithoutFeeOutput = do + let maxTakerFee = foldl' (\prevMaxTakerFee (PartialOrderInfo {..}, _) -> max prevMaxTakerFee poiTakerLovelaceFlatFee) 0 orders + foldlM + (\(!prevSkel) (idx, (poi@PartialOrderInfo {..}, amt)) -> do + commonCheck amt poiOfferedAmount + let price' = partialOrderPrice poi amt + tf = if idx == 1 then mempty { poifLovelaces = fromIntegral maxTakerFee } else mempty + od = partialOrderInfoToPartialOrderDatum poi + { poiOfferedAmount = poiOfferedAmount - amt + , poiPartialFills = poiPartialFills + 1 + , poiContainedFee = poiContainedFee <> tf + , poiContainedPayment = poiContainedPayment + fromIntegral (valueAssetClass price' poiAskedAsset) + } + + expectedValueOut = poiUTxOValue <> price' <> poiContainedFeeToValue tf poiOfferedAsset poiAskedAsset `valueMinus` valueSingleton poiOfferedAsset (toInteger amt) + o = mkGYTxOut poiUTxOAddr expectedValueOut (datumFromPlutusData od) + + cs <- validFillRangeConstraints poiStart poiEnd + + pure $! + prevSkel + <> mustHaveInput (partialOrderInfoToIn poi (PartialFill $ toInteger amt) di) + <> mustHaveOutput o + <> cs + ) + (mustHaveRefInput cfgRef) + (zip [(1 :: Natural).. ] orders) + + if isJust $ find (\(PartialOrderInfo {..}, amt) -> amt == poiOfferedAmount) orders then buildWithFeeOutput + else buildWithoutFeeOutput + where + commonCheck amt poiOfferedAmount = do + when (amt == 0) . throwAppError $ PodNonPositiveAmount $ toInteger amt + when (amt > poiOfferedAmount) . throwAppError $ PodRequestedAmountGreaterThanOfferedAmount amt poiOfferedAmount ------------------------------------------------------------------------------- -- Utilities ------------------------------------------------------------------------------- +partialOrderInfoToPartialOrderDatum :: PartialOrderInfo -> PartialOrderDatum +partialOrderInfoToPartialOrderDatum PartialOrderInfo {..} = PartialOrderDatum + { podOwnerKey = pubKeyHashToPlutus poiOwnerKey + , podOwnerAddr = addressToPlutus poiOwnerAddr + , podOfferedAsset = assetClassToPlutus poiOfferedAsset + , podOfferedOriginalAmount = fromIntegral poiOfferedOriginalAmount + , podOfferedAmount = fromIntegral poiOfferedAmount + , podAskedAsset = assetClassToPlutus poiAskedAsset + , podPrice = PlutusTx.fromGHC $ toRational poiPrice + , podNFT = tokenNameToPlutus poiNFT + , podStart = timeToPlutus <$> poiStart + , podEnd = timeToPlutus <$> poiEnd + , podPartialFills = fromIntegral poiPartialFills + , podMakerLovelaceFlatFee = fromIntegral poiMakerLovelaceFlatFee + , podTakerLovelaceFlatFee = toInteger poiTakerLovelaceFlatFee + , podContainedFee = poiContainedFeeToPlutus poiContainedFee + , podContainedPayment = toInteger poiContainedPayment + } + +poiGetContainedFeeValue :: PartialOrderInfo -> GYValue +poiGetContainedFeeValue PartialOrderInfo {..} = poiContainedFeeToValue poiContainedFee poiOfferedAsset poiAskedAsset + +poiContainedFeeToValue :: POIContainedFee -> GYAssetClass -> GYAssetClass -> GYValue +poiContainedFeeToValue POIContainedFee {..} offAC askAC = + valueSingleton GYLovelace (fromIntegral poifLovelaces) + <> valueSingleton offAC (fromIntegral poifOfferedTokens) + <> valueSingleton askAC (fromIntegral poifAskedTokens) + -- | Builds an input consuming the given order for a particular action. -partialOrderInfoToIn - :: PartialOrderInfo - -> PartialOrderAction - -> DEXInfo - -> GYTxIn PlutusV2 -partialOrderInfoToIn oi@PartialOrderInfo {..} oa DEXInfo {..} = - GYTxIn +partialOrderInfoToIn :: PartialOrderInfo + -> PartialOrderAction + -> DEXInfo + -> GYTxIn PlutusV2 +partialOrderInfoToIn oi@PartialOrderInfo {..} oa DEXInfo {..} = GYTxIn { gyTxInTxOutRef = poiRef - , gyTxInWitness = GYTxInWitnessScript - script - (datumFromPlutusData $ partialOrderInfoToPartialOrderDatum oi) - $ redeemerFromPlutusData oa + , gyTxInWitness = GYTxInWitnessScript + script + (datumFromPlutusData $ partialOrderInfoToPartialOrderDatum oi) + $ redeemerFromPlutusData oa } where script = case porValidatorRef dexPORefs of Nothing -> GYInScript dexPartialOrderValidator Just ref -> GYInReference ref (validatorToScript dexPartialOrderValidator) + +mintingScript :: DEXInfo -> GYMintScript PlutusV2 +mintingScript DEXInfo{dexNftPolicy, dexPORefs} = case porNftPolicyRef dexPORefs of + Nothing -> GYMintScript dexNftPolicy + Just ref -> GYMintReference ref (mintingPolicyToScript dexNftPolicy) + -- | Builds an output paying some value to the order owner, also adds the UTxO -- reference of the order to the datum. -partialOrderInfoToPayment :: PartialOrderInfo -> GYValue -> GYTxOut PlutusV2 -partialOrderInfoToPayment oi v = - mkGYTxOut (poiOwnerAddr oi) v - (datumFromPlutusData $ txOutRefToPlutus $ poiRef oi) - --- | Given a UTxO reference of an order, returns the complete information. -getPartialOrderInfo :: GYApiQueryMonad m => GYTxOutRef -> m PartialOrderInfo -getPartialOrderInfo oref = do - utxo <- utxoAtTxOutRef' oref - vod <- utxoDatum' utxo - runExceptT (makePartialOrderInfo oref vod) >>= liftEither +partialOrderInfoToPayment :: PartialOrderInfo -> GYValue -> GYTxOut 'PlutusV2 +partialOrderInfoToPayment oi v = mkGYTxOut (poiOwnerAddr oi) v (datumFromPlutusData $ txOutRefToPlutus $ poiRef oi) -- | Given an order and the bought amount from that order, returns the total -- price. partialOrderPrice :: PartialOrderInfo -> Natural -> GYValue -partialOrderPrice PartialOrderInfo {..} amt = - valueSingleton poiAskedAsset $ - ceiling $ rationalToGHC poiPrice * toRational amt - -mintingScript :: DEXInfo -> GYMintScript PlutusV2 -mintingScript DEXInfo{dexNftPolicy, dexPORefs} = - case porNftPolicyRef dexPORefs of - Nothing -> GYMintScript dexNftPolicy - Just ref -> GYMintReference ref (mintingPolicyToScript dexNftPolicy) +partialOrderPrice PartialOrderInfo {..} amt = valueSingleton poiAskedAsset $ ceiling $ rationalToGHC poiPrice * toRational amt + +{- | Note that at any moment, an order UTxO contains:- + * An NFT. + * Remaining offered tokens. + * Payment for tokens consumed. + * Initial deposit. + * Collected fees. +-} +expectedPaymentWithDeposit :: PartialOrderInfo -> Bool -> GYValue +expectedPaymentWithDeposit poi@PartialOrderInfo {..} isCompleteFill = + let toSubtract = valueSingleton (GYToken poiNFTCS poiNFT) 1 <> valueSingleton poiOfferedAsset (toInteger poiOfferedAmount) <> poiGetContainedFeeValue poi + toAdd = if isCompleteFill then partialOrderPrice poi poiOfferedAmount else mempty + in poiUTxOValue <> toAdd `valueMinus` toSubtract -partialOrderInfoToPartialOrderDatum :: PartialOrderInfo -> PartialOrderDatum -partialOrderInfoToPartialOrderDatum PartialOrderInfo {..} = - PartialOrderDatum - { podOwnerKey = pubKeyHashToPlutus poiOwnerKey - , podOwnerAddr = addressToPlutus poiOwnerAddr - , podOfferedAsset = assetClassToPlutus poiOfferedAsset - , podOfferedOriginalAmount = fromIntegral poiOfferedOriginalAmount - , podOfferedAmount = fromIntegral poiOfferedAmount - , podAskedAsset = assetClassToPlutus poiAskedAsset - , podPrice = PlutusTx.fromGHC $ toRational poiPrice - , podMinFilling = fromIntegral poiMinFilling - , podNFT = tokenNameToPlutus poiNFT - , podStart = timeToPlutus <$> poiStart - , podEnd = timeToPlutus <$> poiEnd - , podFee = fromIntegral poiFee - , podPartialFills = fromIntegral poiPartialFills - } +-- | Given a UTxO reference of an order, returns the complete information. +getPartialOrderInfo :: GYApiQueryMonad m + => GYTxOutRef + -> m PartialOrderInfo +getPartialOrderInfo orderRef = do + utxoWithDatum <- utxoAtTxOutRefWithDatum' orderRef + vod <- utxoDatumPure' utxoWithDatum + + runExceptT (makePartialOrderInfo orderRef vod) >>= liftEither + +getPartialOrdersInfos :: GYApiQueryMonad m + => [GYTxOutRef] + -> m (Map.Map GYTxOutRef PartialOrderInfo) +getPartialOrdersInfos orderRefs = do + utxosWithDatums <- utxosAtTxOutRefsWithDatums orderRefs + let vod = utxosDatumsPure utxosWithDatums + when (Map.size vod /= length orderRefs) $ throwAppError $ PodNotAllOrderRefsPresent $ Set.fromList orderRefs `Set.difference` Map.keysSet vod + runExceptT (Map.traverseWithKey makePartialOrderInfo vod) >>= liftEither -- | Given the UTxO reference of an order, returns the complete information. -- Checking the validity of the order. -makePartialOrderInfo - :: GYApiQueryMonad m - => GYTxOutRef - -> (GYAddress, GYValue, PartialOrderDatum) - -> ExceptT GYTxMonadException m PartialOrderInfo -makePartialOrderInfo orderRef (_, v, PartialOrderDatum {..}) = do +makePartialOrderInfo :: GYApiQueryMonad m + => GYTxOutRef + -> (GYAddress, GYValue, PartialOrderDatum) + -> ExceptT GYTxMonadException m PartialOrderInfo +makePartialOrderInfo orderRef (utxoAddr, v, PartialOrderDatum {..}) = do DEXInfo{dexNftPolicy} <- ask - addr <- addressFromPlutus' podOwnerAddr - key <- pubKeyHashFromPlutus' podOwnerKey + addr <- addressFromPlutus' podOwnerAddr + + key <- pubKeyHashFromPlutus' podOwnerKey offeredAsset <- assetClassFromPlutus' podOfferedAsset nft <- tokenNameFromPlutus' podNFT askedAsset <- assetClassFromPlutus' podAskedAsset - let price = rationalFromPlutus podPrice - feesDeposits = flip valueAssetClass GYLovelace $ v `valueMinus` - valueSingleton offeredAsset podOfferedAmount - - when (price <= 0) $ - throwAppError (PodNonPositivePrice price) - when (valueAssetClass v (GYToken (mintingPolicyId dexNftPolicy) nft) /= 1) $ throwAppError PodNftNotAvailable - when (podFee < 200_000) $ - throwAppError (PodFeeNotEnough podFee) - return PartialOrderInfo - { poiRef = orderRef - , poiOwnerKey = key - , poiOwnerAddr = addr - , poiOfferedAsset = offeredAsset - , poiOfferedOriginalAmount = fromInteger podOfferedOriginalAmount - , poiOfferedAmount = fromInteger podOfferedAmount - , poiAskedAsset = askedAsset - , poiPrice = price - , poiMinFilling = fromInteger podMinFilling - , poiNFT = nft - , poiFeesDeposits = fromInteger feesDeposits - , poiStart = timeFromPlutus <$> podStart - , poiEnd = timeFromPlutus <$> podEnd - , poiFee = fromInteger podFee - , poiPartialFills = fromInteger podPartialFills + { poiRef = orderRef + , poiOwnerKey = key + , poiOwnerAddr = addr + , poiOfferedAsset = offeredAsset + , poiOfferedOriginalAmount = fromInteger podOfferedOriginalAmount + , poiOfferedAmount = fromInteger podOfferedAmount + , poiAskedAsset = askedAsset + , poiPrice = rationalFromPlutus podPrice + , poiNFT = nft + , poiStart = timeFromPlutus <$> podStart + , poiEnd = timeFromPlutus <$> podEnd + , poiPartialFills = fromInteger podPartialFills + , poiMakerLovelaceFlatFee = fromIntegral podMakerLovelaceFlatFee + , poiTakerLovelaceFlatFee = fromInteger podTakerLovelaceFlatFee + , poiContainedFee = poiContainedFeeFromPlutus podContainedFee + , poiContainedPayment = fromInteger podContainedPayment + , poiUTxOValue = v + , poiUTxOAddr = utxoAddr + , poiNFTCS = mintingPolicyId dexNftPolicy } validFillRangeConstraints diff --git a/geniusyield-dex-api/src/GeniusYield/DEX/Api/PartialOrderConfig.hs b/geniusyield-dex-api/src/GeniusYield/DEX/Api/PartialOrderConfig.hs new file mode 100644 index 0000000..9be50f4 --- /dev/null +++ b/geniusyield-dex-api/src/GeniusYield/DEX/Api/PartialOrderConfig.hs @@ -0,0 +1,126 @@ +{-# LANGUAGE TemplateHaskell #-} +{-| +Module : GeniusYield.DEX.Api.PartialOrderConfig +Copyright : (c) 2023 GYELD GMBH +License : Apache 2.0 +Maintainer : support@geniusyield.co +Stability : develop + +-} + +module GeniusYield.DEX.Api.PartialOrderConfig + ( PocdException (..) + , PartialOrderConfigInfoF (..) + , PartialOrderConfigInfo + , fetchPartialOrderConfig + ) where + +import qualified Cardano.Api as Api +import Data.Text (pack) +import Network.HTTP.Types (status400) + +import GeniusYield.HTTP.Errors (GYApiError (..), IsGYApiError (..)) +import GeniusYield.Imports +import GeniusYield.TxBuilder (GYTxQueryMonad (utxosAtAddressWithDatums), + addressFromPlutus', throwAppError, + utxoDatumPure') +import GeniusYield.Types +import qualified PlutusLedgerApi.V1 as Plutus +import PlutusTx (BuiltinData, + FromData (fromBuiltinData)) +import qualified PlutusTx +import PlutusTx.Builtins.Internal (BuiltinByteString (..)) +import PlutusTx.Ratio as PlutusTx (Rational) + +data PartialOrderConfigDatum = PartialOrderConfigDatum + { pocdSignatories :: [Plutus.PubKeyHash] + -- ^ Public key hashes of the potential signatories. + , pocdReqSignatories :: Integer + -- ^ Number of required signatures. + , pocdNftSymbol :: Plutus.CurrencySymbol + -- ^ Currency symbol of the partial order Nft. + , pocdFeeAddr :: Plutus.Address + -- ^ Address to which fees are paid. + , pocdMakerFeeFlat :: Integer + -- ^ Flat fee (in lovelace) paid by the maker. + , pocdMakerFeeRatio :: PlutusTx.Rational + -- ^ Proportional fee (in the offered token) paid by the maker. + , pocdTakerFee :: Integer + -- ^ Flat fee (in lovelace) paid by the taker. + , pocdMinDeposit :: Integer + -- ^ Minimum required deposit (in lovelace). + } deriving stock (Generic, Show) + +PlutusTx.unstableMakeIsData ''PartialOrderConfigDatum + +data PartialOrderConfigInfoF addr = PartialOrderConfigInfo + { pociSignatories :: ![GYPubKeyHash] + -- ^ Public key hashes of the potential signatories. + , pociReqSignatories :: !Integer + -- ^ Number of required signatures. + , pociNftSymbol :: !GYMintingPolicyId + -- ^ Minting Policy Id of the partial order Nft. + , pociFeeAddr :: !addr + -- ^ Address to which fees are paid. + , pociMakerFeeFlat :: !Integer + -- ^ Flat fee (in lovelace) paid by the maker. + , pociMakerFeeRatio :: !GYRational + -- ^ Proportional fee (in the offered token) paid by the maker. + , pociTakerFee :: !Integer + -- ^ Flat fee (in lovelace) paid by the taker. + , pociMinDeposit :: !Integer + -- ^ Minimum required deposit (in lovelace). + } deriving stock (Show, Generic, Functor) + +type PartialOrderConfigInfo = PartialOrderConfigInfoF GYAddress + +instance FromData (PartialOrderConfigInfoF Plutus.Address) where + fromBuiltinData :: BuiltinData -> Maybe (PartialOrderConfigInfoF Plutus.Address) + fromBuiltinData d = do + PartialOrderConfigDatum{..} <- fromBuiltinData d + signatories <- fromEither $ mapM pubKeyHashFromPlutus pocdSignatories + nftSymbol <- fromEither $ mintingPolicyIdFromCurrencySymbol pocdNftSymbol + pure PartialOrderConfigInfo + { pociSignatories = signatories + , pociReqSignatories = pocdReqSignatories + , pociNftSymbol = nftSymbol + , pociFeeAddr = pocdFeeAddr + , pociMakerFeeFlat = pocdMakerFeeFlat + , pociMakerFeeRatio = rationalFromPlutus pocdMakerFeeRatio + , pociTakerFee = pocdTakerFee + , pociMinDeposit = pocdMinDeposit + } + where + fromEither :: Either e a -> Maybe a + fromEither = either (const Nothing) Just + +newtype PocdException = PocdException GYAssetClass + deriving stock Show + deriving anyclass Exception + +instance IsGYApiError PocdException where + toApiError (PocdException nftToken) = GYApiError + { gaeErrorCode = "PARTIAL_ORDER_CONFIG_NOT_FOUND" + , gaeHttpStatus = status400 + , gaeMsg = pack $ printf "Partial order config not found for NFT: %s" nftToken + } + +fetchPartialOrderConfig :: GYTxQueryMonad m => GYAddress -> GYAssetClass -> m (GYTxOutRef, PartialOrderConfigInfo) +fetchPartialOrderConfig addr nftToken = do + utxos <- utxosAtAddressWithDatums addr $ Just nftToken + case utxos of + [p@(utxo, Just _)] -> do + (_, _, d') <- utxoDatumPure' p + feeAddr <- addressFromPlutus' $ pociFeeAddr d' + pure (utxoRef utxo, feeAddr <$ d') + _ -> throwAppError $ PocdException nftToken + +mintingPolicyIdFromCurrencySymbol :: Plutus.CurrencySymbol -> Either PlutusToCardanoError GYMintingPolicyId +mintingPolicyIdFromCurrencySymbol cs = + let + BuiltinByteString bs = Plutus.unCurrencySymbol cs + in + case Api.deserialiseFromRawBytes Api.AsPolicyId bs of + Left e -> Left $ DeserialiseRawBytesError $ pack $ + "mintingPolicyIdFromCurrencySymbol: " <> show cs <> ", error: " <> show e + Right pid -> Right $ mintingPolicyIdFromApi pid diff --git a/geniusyield-dex-api/src/GeniusYield/DEX/Api/Types.hs b/geniusyield-dex-api/src/GeniusYield/DEX/Api/Types.hs index 0e2902b..904d6b3 100644 --- a/geniusyield-dex-api/src/GeniusYield/DEX/Api/Types.hs +++ b/geniusyield-dex-api/src/GeniusYield/DEX/Api/Types.hs @@ -16,23 +16,24 @@ module GeniusYield.DEX.Api.Types , mkPORefs ) where -import Control.Monad.Reader ( MonadReader ) +import Control.Monad.Reader (MonadReader) -import Ply ( TypedScript, ScriptRole (..) ) -import Ply.Core.Apply ( (#) ) -import PlutusLedgerApi.V1 ( Address ) -import PlutusLedgerApi.V1.Scripts ( ScriptHash ) -import PlutusLedgerApi.V1.Value ( AssetClass ) +import PlutusLedgerApi.V1 (Address) +import PlutusLedgerApi.V1.Scripts (ScriptHash) +import PlutusLedgerApi.V1.Value (AssetClass) +import Ply (ScriptRole (..), TypedScript) +import Ply.Core.Apply ((#)) -import GeniusYield.DEX.Api.Constants ( minDeposit ) -import GeniusYield.DEX.Api.Utils ( validatorFromPly, mintingPolicyFromPly ) -import GeniusYield.Types ( GYValidator, GYMintingPolicy - , GYAddress, GYAssetClass - , PlutusVersion(PlutusV2) - , GYTxOutRef, scriptPlutusHash - , validatorToScript, assetClassToPlutus - ) -import GeniusYield.TxBuilder.Class ( GYTxMonad, GYTxQueryMonad ) +import GeniusYield.DEX.Api.Utils (mintingPolicyFromPly, + validatorFromPly) +import GeniusYield.TxBuilder.Class (GYTxMonad, GYTxQueryMonad) +import GeniusYield.Types (GYAddress, GYAssetClass, + GYMintingPolicy, GYTxOutRef, + GYValidator, + PlutusVersion (PlutusV2), + assetClassToPlutus, + scriptPlutusHash, + validatorToScript) type GYApiQueryMonad m = (MonadReader DEXInfo m, GYTxQueryMonad m) @@ -51,8 +52,6 @@ data PORefs = PORefs -- ^ The address where the reference NFT will be placed. , porRefNft :: !GYAssetClass -- ^ The reference NFT. - , porRefNftRef :: !GYTxOutRef - -- ^ The location of the reference NFT. , porValidatorRef :: !(Maybe GYTxOutRef) -- ^ The reference for the partial order validator. , porNftPolicyRef :: !(Maybe GYTxOutRef) @@ -62,34 +61,36 @@ data PORefs = PORefs -- Smart Constructors mkDEXMintingPolicy - :: TypedScript 'MintingPolicyRole '[ScriptHash, Integer] + :: TypedScript 'MintingPolicyRole '[ScriptHash, Address, AssetClass] -> GYValidator PlutusV2 + -> Address + -> GYAssetClass -> GYMintingPolicy PlutusV2 -mkDEXMintingPolicy mintingPolicyRaw v = mintingPolicyFromPly $ mintingPolicyRaw - # scriptPlutusHash (validatorToScript v) - # toInteger minDeposit +mkDEXMintingPolicy mintingPolicyRaw v addr ac = + mintingPolicyFromPly $ mintingPolicyRaw + # scriptPlutusHash (validatorToScript v) + # addr + # assetClassToPlutus ac mkDEXValidator - :: TypedScript 'ValidatorRole '[Address, AssetClass, Integer] + :: TypedScript 'ValidatorRole '[Address, AssetClass] -> Address -> GYAssetClass -> GYValidator PlutusV2 mkDEXValidator validatorRaw addr ac = validatorFromPly $ validatorRaw # addr # assetClassToPlutus ac - # fromIntegral minDeposit + mkPORefs :: GYAddress -> GYAssetClass - -> GYTxOutRef -> Maybe GYTxOutRef -> Maybe GYTxOutRef -> PORefs -mkPORefs porAddr porAC porRef mVRef mNPRef = +mkPORefs porAddr porAC mVRef mNPRef = PORefs { porRefAddr = porAddr , porRefNft = porAC - , porRefNftRef = porRef , porValidatorRef = mVRef , porNftPolicyRef = mNPRef } diff --git a/geniusyield-orderbot-framework/lib-common/GeniusYield/OrderBot/Types.hs b/geniusyield-orderbot-framework/lib-common/GeniusYield/OrderBot/Types.hs index 158d1c4..0deb22f 100644 --- a/geniusyield-orderbot-framework/lib-common/GeniusYield/OrderBot/Types.hs +++ b/geniusyield-orderbot-framework/lib-common/GeniusYield/OrderBot/Types.hs @@ -15,22 +15,24 @@ module GeniusYield.OrderBot.Types , Volume (..) , Price (..) , mkOrderInfo + , isSellOrder + , isBuyOrder , mkOrderAssetPair , equivalentAssetPair , mkEquivalentAssetPair ) where -import Data.Aeson (ToJSON, (.=)) -import qualified Data.Aeson as Aeson -import Data.Kind (Type) -import Data.Ratio (denominator, numerator, (%)) -import Numeric.Natural (Natural) +import Data.Aeson (ToJSON, (.=)) +import qualified Data.Aeson as Aeson +import Data.Kind (Type) +import Data.Ratio (denominator, numerator, (%)) +import Numeric.Natural (Natural) -import GeniusYield.Types.TxOutRef (GYTxOutRef) -import GeniusYield.Types.Value (GYAssetClass (..)) +import GeniusYield.Types.TxOutRef (GYTxOutRef) +import GeniusYield.Types.Value (GYAssetClass (..)) import GeniusYield.DEX.Api.PartialOrder (PartialOrderInfo (..)) -import GeniusYield.Types (rationalToGHC) +import GeniusYield.Types (rationalToGHC) ------------------------------------------------------------------------------- -- Information on DEX orders relevant to a matching strategy @@ -51,14 +53,14 @@ See: 'mkOrderInfo'. -} type OrderInfo :: OrderType -> Type data OrderInfo t = OrderInfo - { orderRef :: !GYTxOutRef + { orderRef :: !GYTxOutRef , orderType :: !(SOrderType t) , assetInfo :: !OrderAssetPair - , volume :: !Volume + , volume :: !Volume -- ^ Volume of the 'commodityAsset', either being bought or sold. - , price :: !Price + , price :: !Price -- ^ Price of each 'commodityAsset', in 'currencyAsset'. - , mPoi :: !(Maybe PartialOrderInfo) + , mPoi :: !(Maybe PartialOrderInfo) -- ^ The complete PartialOrderInfo. To avoid quering it again when filling the order } deriving stock (Eq, Show) @@ -81,8 +83,8 @@ asking for another. For sell orders, where the offered asset in the DEX order is deemed to be a 'commodityAsset', there is no conversion necessary. 'volume' is simply in terms -of the offered asset amount and minFilling. Similarly, 'price' is the same as the -DEX order's price. +of the offered asset amount and the minFill is simply 1. +Similarly, 'price' is the same as the DEX order's price. But what about buy orders? These are the orders that are offering an asset which is deemed to be a 'currencyAsset'. And they are asking for an asset which is deemed @@ -93,6 +95,8 @@ In that case, the price is simply the DEX order's price but flipped (e.g x % y - The volume conversion is slightly more involved, the max volume is the DEX order's price multiplied by the DEX order's offered amount. If the result is not a whole number, it is ceiled - because more payment is always accepted, but less is not. +The min volume is just the ceiling of the price, because that's the amount of +commodity assets you would need to pay to access 1 of the offered currencyAssets. -} mkOrderInfo @@ -104,12 +108,12 @@ mkOrderInfo mkOrderInfo oap poi@PartialOrderInfo{..} = case orderType of BuyOrder -> let maxVolume = ceiling $ (toInteger poiOfferedAmount % 1) * askedPrice - minVolume = ceiling $ (toInteger poiMinFilling % 1) * askedPrice + minVolume = ceiling askedPrice in builder SBuyOrder (Volume minVolume maxVolume) $ Price (denominator askedPrice % numerator askedPrice) SellOrder -> builder SSellOrder - (Volume poiMinFilling poiOfferedAmount) $ + (Volume 1 poiOfferedAmount) $ Price askedPrice where orderType = mkOrderType poiAskedAsset oap @@ -117,6 +121,14 @@ mkOrderInfo oap poi@PartialOrderInfo{..} = case orderType of builder :: SOrderType t -> Volume -> Price -> SomeOrderInfo builder t vol price = SomeOrderInfo $ OrderInfo poiRef t oap vol price (Just poi) +isSellOrder :: OrderInfo t -> Bool +isSellOrder OrderInfo { orderType = SSellOrder} = True +isSellOrder _ = False + +isBuyOrder :: OrderInfo t -> Bool +isBuyOrder OrderInfo { orderType = SBuyOrder} = True +isBuyOrder _ = False + ------------------------------------------------------------------------------- -- Order classification components. ------------------------------------------------------------------------------- @@ -134,24 +146,14 @@ deriving stock instance Show (SOrderType t) -- Order components ------------------------------------------------------------------------------- -{- | The amount of the commodity asset (being brought or sold), represented as a -closed interval. - -This is particularly relevant for orders that support partial filling. Indeed, -for a partially fillable order, the volume is _dynamic_. It has to be _at least_ -`minFilling`, and _at most_ `offeredAmount`. - -Say a partial order selling 30 A tokens for some B tokens. The order placer has -set the `minFilling` to 10 - suggesting that anyone wishing to buy some of these -A tokens (but not all) must buy _at least_ 10 A tokens. Therefore, its A token -'Volume' is (10, 30). +{- | The amount of the commodity asset (being brought or sold), represented as +a closed interval. -For regular orders, where partial fills are not permitted, and one must buy the -whole offered amount - 'volumeMin', and 'volumeMax' are the same, equal to the -`offeredAmount`. For example, a non-partially fillable order selling 30 A tokens -for some B tokens, will have its 'Volume' set to (30, 30). +Although the contract permits fills as low a 1 indivisible token, +the @volumeMin@ field is still needed, because Buy orders are normalized and you +can't always fill it for 1. The amount depends on the price of the order. -volumeMin should always be <= volumeMax. Users are responsible for maintaining +@volumeMin@ should always be @<= volumeMax@. Users are responsible for maintaining this invariant. -} data Volume = Volume diff --git a/geniusyield-orderbot-framework/lib-datasource/GeniusYield/OrderBot/DataSource.hsig b/geniusyield-orderbot-framework/lib-datasource/GeniusYield/OrderBot/DataSource.hsig index 73f0977..4b49ca2 100644 --- a/geniusyield-orderbot-framework/lib-datasource/GeniusYield/OrderBot/DataSource.hsig +++ b/geniusyield-orderbot-framework/lib-datasource/GeniusYield/OrderBot/DataSource.hsig @@ -57,10 +57,7 @@ mkDEX -- ^ Script Ref for the Partial Orders Validator -> Maybe GYTxOutRef -- ^ Script Ref for the NFT minting policy - -> (GYAddress, GYAssetClass, GYTxOutRef) - {- ^ Triplet representing the UTxO, address and value, where the NFT minting - policy of the orders is placed. - -} + -> (GYAddress, GYAssetClass) -> DEX {- | Fetch all unique DEX order asset pairings, and for each such asset pair, fetch all buy and sell orders. diff --git a/geniusyield-orderbot-framework/src/GeniusYield/OrderBot.hs b/geniusyield-orderbot-framework/src/GeniusYield/OrderBot.hs index b9df130..af8be63 100644 --- a/geniusyield-orderbot-framework/src/GeniusYield/OrderBot.hs +++ b/geniusyield-orderbot-framework/src/GeniusYield/OrderBot.hs @@ -11,96 +11,92 @@ module GeniusYield.OrderBot ( OrderBot (..) , runOrderBot ) where -import Control.Arrow ((&&&), second) -import Control.Concurrent (threadDelay) -import Control.Exception ( SomeException - , AsyncException( UserInterrupt ) - , fromException, bracket, handle - ) -import Control.Monad (forever, unless, filterM) -import Control.Monad.Reader (runReaderT) -import Data.Foldable (fold, toList) -import Data.Functor ((<&>)) -import Data.Aeson (ToJSON, encode) -import Data.Functor.Identity (runIdentity) -import Data.Maybe (mapMaybe) -import Data.List (find) - -import System.Exit ( exitSuccess ) - -import qualified Data.Map as M -import qualified Data.List.NonEmpty as NE (toList) -import qualified Data.ByteString.Char8 as B -import qualified Data.ByteString.Lazy as BL -import qualified Data.Text as Txt - -import GeniusYield.Providers.Common (SubmitTxException) -import GeniusYield.GYConfig ( GYCoreConfig (cfgNetworkId) - , withCfgProviders - , coreConfigIO - ) -import GeniusYield.OrderBot.DataSource ( closeDB, connectDB, mkDEX ) -import GeniusYield.OrderBot.MatchingStrategy ( IndependentStrategy - , MatchResult - , MatchExecutionInfo (..) - , executionSkeleton - , matchExecutionInfoUtxoRef - ) -import GeniusYield.OrderBot.OrderBook ( OrderBook - , buyOrders, sellOrders - , foldrOrders - , populateOrderBook - , withEachAsset - , maOrderBookToList - ) -import GeniusYield.OrderBot.Types ( OrderAssetPair(..), assetInfo ) -import GeniusYield.TxBuilder ( GYTxBuildResult(..) - , GYTxSkeleton - , GYTxMonadNode - , utxosAtTxOutRefs - , runGYTxQueryMonadNode - ) -import GeniusYield.TxBuilder.Node ( runGYTxMonadNodeParallelWithStrategy ) -import GeniusYield.Types - -import GeniusYield.DEX.Api.Types ( DEXInfo (..) - , PORefs (..) - , dexNftPolicy - , dexPartialOrderValidator - ) -import GeniusYield.Transaction ( BuildTxException - , GYCoinSelectionStrategy(GYRandomImproveMultiAsset) - ) +import Control.Arrow (second, (&&&)) +import Control.Concurrent (threadDelay) +import Control.Exception (AsyncException (UserInterrupt), + SomeException, bracket, + fromException, handle) +import Control.Monad (filterM, forever, + unless) +import Control.Monad.Reader (runReaderT) +import Data.Aeson (ToJSON, encode) +import Data.Foldable (foldl', toList) +import Data.Functor ((<&>)) +import Data.Functor.Identity (runIdentity) +import Data.List (find) +import Data.Maybe (mapMaybe) + +import System.Exit (exitSuccess) + +import qualified Data.ByteString.Char8 as B +import qualified Data.ByteString.Lazy as BL +import qualified Data.List.NonEmpty as NE (toList) +import qualified Data.Map as M +import qualified Data.Text as Txt + +import GeniusYield.GYConfig (GYCoreConfig (cfgNetworkId), + coreConfigIO, + withCfgProviders) +import GeniusYield.OrderBot.DataSource (closeDB, connectDB, + mkDEX) +import GeniusYield.OrderBot.MatchingStrategy (IndependentStrategy, + MatchExecutionInfo (..), + MatchResult, + executionSkeleton, + matchExecutionInfoUtxoRef) +import GeniusYield.OrderBot.OrderBook (OrderBook, buyOrders, + foldrOrders, + maOrderBookToList, + populateOrderBook, + sellOrders, + withEachAsset) +import GeniusYield.OrderBot.Types (OrderAssetPair (..), + assetInfo) +import GeniusYield.Providers.Common (SubmitTxException) +import GeniusYield.TxBuilder (GYTxBuildResult (..), + GYTxMonadNode, + GYTxSkeleton, + runGYTxQueryMonadNode, + utxosAtTxOutRefs) +import GeniusYield.TxBuilder.Node (runGYTxMonadNodeParallelWithStrategy) +import GeniusYield.Types + +import GeniusYield.DEX.Api.Types (DEXInfo (..), + PORefs (..), + dexNftPolicy, + dexPartialOrderValidator) +import GeniusYield.Transaction (BuildTxException, + GYCoinSelectionStrategy (GYLegacy)) -- | The order bot is product type between bot info and "execution strategies". data OrderBot = OrderBot - { botSkey :: !GYPaymentSigningKey + { botSkey :: !GYPaymentSigningKey -- ^ Signing key of the bot. - , botCollateral :: !(Maybe (GYTxOutRef, Bool)) + , botCollateral :: !(Maybe (GYTxOutRef, Bool)) {- ^ UTxO ref of the collateral UTxO in the bot's wallet. NOTE: If collateral is Nothing, then Atlas will choose some UTxO to - function as collateral. If a TxOutRef is given, the bool indicates wheter + function as collateral. If a TxOutRef is given, the bool indicates whether the collateral can be spent in the tx. -} - , botExecutionStrat :: !ExecutionStrategy + , botExecutionStrat :: !ExecutionStrategy -- ^ The execution strategy, which includes and governs the matching strategy. , botAssetPairFilter :: [OrderAssetPair] {- ^ List that can be used to filter out uninteresting orders/pools. The multiasset order book is created only with the existing pairs on the list. -} - , botRescanDelay :: Int + , botRescanDelay :: Int {- ^ How many microseconds to wait after a tx submission before rescanning the chain for orders. -} - , botTakeMatches :: [MatchResult] -> IO [MatchResult] + , botTakeMatches :: [MatchResult] -> IO [MatchResult] {- ^ How and how many matching results do the bot takes to build, sign and submit every iteration. -} } -{- | Currently, we only have the parallel execution strategy: MultiAssetTraverse, +{- | Currently, we only have the parallel execution strategy: @MultiAssetTraverse@, where each order book for each unique asset pair (see: "GeniusYield.OrderBot.Types.equivalentAssetPair") is processed independently. -} @@ -139,7 +135,7 @@ runOrderBot (dexPartialOrderValidator di) (porNftPolicyRef por) (porValidatorRef por) - (porRefAddr por, porRefNft por, porRefNftRef por) + (porRefAddr por, porRefNft por) logInfo $ unlines [ "" @@ -160,7 +156,7 @@ runOrderBot logInfo "Rescanning for orders..." -- First we populate the multi asset orderbook, using the provided - -- 'populateOrderBook'. + -- @populateOrderBook@. book <- populateOrderBook conn dex botAssetPairFilter let bookList = maOrderBookToList book @@ -259,7 +255,7 @@ buildTransactions matchesToExecute di netId providers botAddr botCollateral = handle handlerBuildTx $ do res <- runGYTxMonadNodeParallelWithStrategy - GYRandomImproveMultiAsset + GYLegacy netId providers [botAddr] botAddr botCollateral $ traverse resultToSkeleton matchesToExecute @@ -283,7 +279,7 @@ buildTransactions matchesToExecute di netId getBodies = NE.toList . runIdentity . sequence resultToSkeleton :: MatchResult -> GYTxMonadNode (GYTxSkeleton 'PlutusV2) - resultToSkeleton mResult = fold <$> traverse (flip runReaderT di . executionSkeleton) mResult + resultToSkeleton mResult = runReaderT (executionSkeleton mResult) di handlerBuildTx :: BuildTxException -> IO [(GYTxBody, MatchResult)] handlerBuildTx ex = logWarn (unwords ["BuildTxException:", show ex]) @@ -372,8 +368,8 @@ totalBuyOrders :: OrderBook -> Int totalBuyOrders = foldrOrders (const (+1)) 0 . buyOrders matchingsPerOrderAssetPair :: [OrderAssetPair] -> [MatchResult] -> M.Map OrderAssetPair Int -matchingsPerOrderAssetPair oaps = foldl succOAP (M.fromList $ map (,0) oaps) +matchingsPerOrderAssetPair oaps = foldl' succOAP (M.fromList $ map (, 0) oaps) where succOAP :: M.Map OrderAssetPair Int -> MatchResult -> M.Map OrderAssetPair Int - succOAP m (OrderExecutionInfo _ oi:_) = M.insertWith (+) (assetInfo oi) 1 m + succOAP m (OrderExecutionInfo _ oi : _) = M.insertWith (+) (assetInfo oi) 1 m succOAP m _ = m diff --git a/geniusyield-orderbot-framework/src/GeniusYield/OrderBot/MatchingStrategy.hs b/geniusyield-orderbot-framework/src/GeniusYield/OrderBot/MatchingStrategy.hs index 0303668..ffbbe15 100644 --- a/geniusyield-orderbot-framework/src/GeniusYield/OrderBot/MatchingStrategy.hs +++ b/geniusyield-orderbot-framework/src/GeniusYield/OrderBot/MatchingStrategy.hs @@ -17,22 +17,20 @@ module GeniusYield.OrderBot.MatchingStrategy , matchExecutionInfoUtxoRef ) where -import Data.Aeson (ToJSON (toJSON), (.=)) -import qualified Data.Aeson as Aeson -import Data.Ratio ((%)) -import Data.Text (Text) -import Numeric.Natural (Natural) - -import GeniusYield.DEX.Api.PartialOrder ( completelyFillPartialOrder - , partiallyFillPartialOrder - , PartialOrderInfo - ) -import GeniusYield.DEX.Api.Types (GYApiMonad) -import GeniusYield.OrderBot.OrderBook (OrderBook) -import GeniusYield.OrderBot.Types -import GeniusYield.TxBuilder (GYTxSkeleton) -import GeniusYield.Types.TxOutRef (showTxOutRef, GYTxOutRef) -import GeniusYield.Types.PlutusVersion (PlutusVersion(PlutusV2)) +import Data.Aeson (ToJSON (toJSON), (.=)) +import qualified Data.Aeson as Aeson +import Data.Maybe (fromJust) +import Data.Text (Text) +import Numeric.Natural (Natural) + +import GeniusYield.DEX.Api.PartialOrder (PartialOrderInfo (poiOfferedAmount), + fillMultiplePartialOrders) +import GeniusYield.DEX.Api.Types (GYApiMonad) +import GeniusYield.OrderBot.OrderBook (OrderBook) +import GeniusYield.OrderBot.Types +import GeniusYield.TxBuilder (GYTxSkeleton) +import GeniusYield.Types.PlutusVersion (PlutusVersion (PlutusV2)) +import GeniusYield.Types.TxOutRef (GYTxOutRef, showTxOutRef) {- | A matching strategy has access to the 'OrderBook' for a single asset pair, alongside all its relevant query functions. It must produce a 'MatchResult' which @@ -67,7 +65,7 @@ instance ToJSON MatchExecutionInfo where ] where prettySOrderType :: SOrderType t -> Text - prettySOrderType SBuyOrder = "Buy" + prettySOrderType SBuyOrder = "Buy" prettySOrderType SSellOrder = "Sell" {- | The result of order matching - should contain information to perform execute order and LP transactions. @@ -98,14 +96,22 @@ must be paid by the order. data FillType = CompleteFill | PartialFill Natural deriving stock (Eq, Show) executionSkeleton - :: GYApiMonad m - => MatchExecutionInfo - -> m (GYTxSkeleton PlutusV2) -executionSkeleton (OrderExecutionInfo CompleteFill oi) = completelyFillPartialOrder $ poiSource oi -executionSkeleton (OrderExecutionInfo (PartialFill n) oi@OrderInfo {orderType = SSellOrder}) = - partiallyFillPartialOrder (poiSource oi) n -executionSkeleton (OrderExecutionInfo (PartialFill n) oi@OrderInfo {orderType = SBuyOrder, price}) = - partiallyFillPartialOrder (poiSource oi) . floor $ (toInteger n % 1) * getPrice price + :: GYApiMonad m + => MatchResult + -> m (GYTxSkeleton 'PlutusV2) +executionSkeleton mr = fillMultiplePartialOrders $ map f mr + where + f (OrderExecutionInfo ft o) = + (poiSource o + , case ft of + CompleteFill -> poiOfferedAmount $ fromJust $ mPoi o + PartialFill n -> + if isBuyOrder o then + floor $ fromIntegral n * getPrice (price o) + else + n + ) + matchExecutionInfoUtxoRef :: MatchExecutionInfo -> GYTxOutRef matchExecutionInfoUtxoRef (OrderExecutionInfo CompleteFill OrderInfo {orderRef}) = orderRef @@ -114,4 +120,4 @@ matchExecutionInfoUtxoRef (OrderExecutionInfo (PartialFill _) OrderInfo {orderRe -- | If the order contains the PartialOrderInfo, return it. If not, return the ref poiSource :: forall t. OrderInfo t -> Either GYTxOutRef PartialOrderInfo poiSource OrderInfo {orderRef, mPoi = Nothing} = Left orderRef -poiSource OrderInfo {mPoi = Just poi} = Right poi +poiSource OrderInfo {mPoi = Just poi} = Right poi diff --git a/geniusyield-orderbot/src/OrderBotConfig.hs b/geniusyield-orderbot/src/OrderBotConfig.hs index 7d2cbdb..b1d5d05 100644 --- a/geniusyield-orderbot/src/OrderBotConfig.hs +++ b/geniusyield-orderbot/src/OrderBotConfig.hs @@ -26,7 +26,6 @@ import Data.Random ( shuffle, sample ) import qualified Data.Vector as V import Data.List ( nub ) import GHC.Generics ( Generic ) -import GHC.Natural ( naturalToInteger ) import System.Envy ( FromEnv (fromEnv), Var, Parser, envMaybe, env , decodeEnv ) @@ -162,7 +161,6 @@ data PORConfig = PORConfig { botCRefAddr :: GYAddress , botCRefNft :: GYAssetClass - , botCRefNftRef :: GYTxOutRef , botCScriptRef :: Maybe GYTxOutRef , botCNftPolicyRef :: Maybe GYTxOutRef } @@ -173,7 +171,6 @@ instance FromJSON PORConfig where PORConfig <$> (addressFromBech32 <$> obj .: "refAddr") <*> obj .: "refNftAC" - <*> obj .: "refNftUtxoRef" <*> obj .:? "scriptRef" <*> obj .:? "nftPolicyRef" parseJSON _ = fail "Expecting object value" @@ -225,7 +222,7 @@ intToNatural _ i | i > 0 = return $ fromInteger $ toInteger i intToNatural msg _ = throwIO $ userError $ msg ++ " is negative or zero" takeMatches :: Bool -> Natural -> [MatchResult] -> IO [MatchResult] -takeMatches r (fromIntegral . naturalToInteger -> maxTxPerIter) matches = +takeMatches r (fromIntegral -> maxTxPerIter) matches = take maxTxPerIter <$> if r then shuffleList matches else return matches shuffleList :: [a] -> IO [a] @@ -245,10 +242,9 @@ getDexInfo OrderBotConfig{ botCFPNftPolicy let partialOrderValidator = mkDEXValidator dexValidatorRaw (addressToPlutus $ botCRefAddr botCPORConfig) (botCRefNft botCPORConfig) - nftPolicy = mkDEXMintingPolicy dexPolicyRaw partialOrderValidator + nftPolicy = mkDEXMintingPolicy dexPolicyRaw partialOrderValidator (addressToPlutus $ botCRefAddr botCPORConfig) (botCRefNft botCPORConfig) porefs = PORefs { porRefAddr = botCRefAddr botCPORConfig , porRefNft = botCRefNft botCPORConfig - , porRefNftRef = botCRefNftRef botCPORConfig , porValidatorRef = botCScriptRef botCPORConfig , porNftPolicyRef = botCNftPolicyRef botCPORConfig } @@ -260,9 +256,9 @@ getDexInfo OrderBotConfig{ botCFPNftPolicy where readNftPolicy - :: IO (TypedScript 'MintingPolicyRole '[ScriptHash, Integer]) + :: IO (TypedScript 'MintingPolicyRole '[ScriptHash, Address, AssetClass]) readNftPolicy = readTypedScript botCFPNftPolicy readOrderValidator - :: IO (TypedScript 'ValidatorRole '[Address, AssetClass, Integer]) + :: IO (TypedScript 'ValidatorRole '[Address, AssetClass]) readOrderValidator = readTypedScript botCFPOrderValidator diff --git a/geniusyield-orderbot/test/Main.hs b/geniusyield-orderbot/test/Main.hs index 68602a1..574388c 100644 --- a/geniusyield-orderbot/test/Main.hs +++ b/geniusyield-orderbot/test/Main.hs @@ -31,8 +31,6 @@ qcTestsForStrategy strat = testGroup (show strat) mkStrategyTest iStrat propCanExecuteFill , testProperty "Can find only Match - Price" $ propCanFindOnlyMatching iStrat genOrderInfosWrongPrices - , testProperty "Can find only Match - Volume" $ - propCanFindOnlyMatching iStrat genOrderInfosWrongVolumes ] where iStrat = mkIndependentStrategy strat 10 diff --git a/geniusyield-orderbot/test/Tests/Prop/Strategies.hs b/geniusyield-orderbot/test/Tests/Prop/Strategies.hs index b60c001..67f8e78 100644 --- a/geniusyield-orderbot/test/Tests/Prop/Strategies.hs +++ b/geniusyield-orderbot/test/Tests/Prop/Strategies.hs @@ -98,70 +98,27 @@ genOrderInfosWrongPrices = do && volumeMin (volume bOrder) <= volumeMax (volume sOrder) genBuyOrder' :: OrderAssetPair -> Gen (OrderInfo 'BuyOrder) - genBuyOrder' oap = OrderInfo <$> genGYTxOutRef - <*> pure SBuyOrder - <*> pure oap - <*> genVolume - <*> genPrice `suchThat` ((< (50%1)) . getPrice) - <*> pure Nothing + genBuyOrder' oap = do + price <- genPrice `suchThat` ((< (50%1)) . getPrice) + volume <- genVolume (ceiling $ getPrice price) + utxoRef <- genGYTxOutRef + return $ OrderInfo utxoRef SBuyOrder oap volume price Nothing genSellOrder' :: OrderAssetPair -> Gen (OrderInfo 'SellOrder) genSellOrder' oap = OrderInfo <$> genGYTxOutRef <*> pure SSellOrder <*> pure oap - <*> genVolume + <*> genVolume 1 <*> genPrice `suchThat` ((> (50%1)) . getPrice) <*> pure Nothing -{- | Generates a fixes OrderAssetPair, a list of buy and sell orders that - don't generate any matches because the volume of the buy orders - can't fill any sell order. - - And an extra buy and sell orders that can be matched togheter. --} -genOrderInfosWrongVolumes :: Gen (OrderAssetPair, [OrderInfo 'BuyOrder], [OrderInfo 'SellOrder], OrderInfo 'BuyOrder, OrderInfo 'SellOrder) -genOrderInfosWrongVolumes = do - sellOrders <- listOf1 $ genSellOrder oap - let minMinVolume = minimum $ map (volumeMin . volume) sellOrders - buyOrders <- genBuys [] minMinVolume - - newBuyOrder <- genBuyOrder oap - newSellOrder <- genSellOrder oap `suchThat` sellOrderIsProfitable newBuyOrder - return (oap, buyOrders, sellOrders, newBuyOrder, newSellOrder) - where - goldPolicyId = "ff80aaaf03a273b8f5c558168dc0e2377eea810badbae6eceefc14ef" - oap = mkOrderAssetPair GYLovelace (GYToken goldPolicyId "GOLD") - - sellOrderIsProfitable :: OrderInfo 'BuyOrder -> OrderInfo 'SellOrder -> Bool - sellOrderIsProfitable bOrder sOrder = price sOrder <= price bOrder - && volumeMin (volume sOrder) <= volumeMax (volume bOrder) - && volumeMin (volume bOrder) <= volumeMax (volume sOrder) - - genBuyOrder' :: Natural -> Gen (OrderInfo 'BuyOrder) - genBuyOrder' max = OrderInfo <$> genGYTxOutRef - <*> pure SBuyOrder - <*> pure oap - <*> genVolume' 1 (fromIntegral max) - <*> genPrice - <*> pure Nothing - - genBuys :: [OrderInfo 'BuyOrder] -> Natural -> Gen [OrderInfo 'BuyOrder] - genBuys acc 0 = return acc - genBuys acc max = do - size <- getSize - order <- genBuyOrder' max - let nMax = max - volumeMax (volume order) - if size < length acc - then return acc - else oneof [genBuys (order : acc) nMax, genBuys acc nMax] - {- | Property that checks if the sum of the offered tokens in the buy orders is less than or equal to the sum of offered tokens in the sell orders. -} propOffered :: [MatchExecutionInfo] -> Bool propOffered [] = True -propOffered xs = let buys = filter isBuyOrder xs - sells = filter isSellOrder xs +propOffered xs = let buys = filter isBuyOrderMEI xs + sells = filter isSellOrderMEI xs in sumOfOffered buys <= sumOfOffered sells {- | Property that checks if the sum of the price tokens in the buy orders is @@ -172,8 +129,8 @@ propOffered xs = let buys = filter isBuyOrder xs -} propPrice :: [MatchExecutionInfo] -> Bool propPrice [] = True -propPrice xs = let buys = filter isBuyOrder xs - sells = filter isSellOrder xs +propPrice xs = let buys = filter isBuyOrderMEI xs + sells = filter isSellOrderMEI xs in sumOfPrice buys >= sumOfPrice sells {- | Property that checks if the matches generated by the strategy can be done @@ -195,14 +152,14 @@ propCanExecuteFill = all canFill -------------------------------------------------- -- | Checks if a MatchExecutionInfo is a sell order -isSellOrder :: MatchExecutionInfo -> Bool -isSellOrder (OrderExecutionInfo _ OrderInfo {orderType = SSellOrder}) = True -isSellOrder _ = False +isSellOrderMEI :: MatchExecutionInfo -> Bool +isSellOrderMEI (OrderExecutionInfo _ OrderInfo {orderType = SSellOrder}) = True +isSellOrderMEI _ = False -- | Checks if a MatchExecutionInfo is a buy order -isBuyOrder :: MatchExecutionInfo -> Bool -isBuyOrder (OrderExecutionInfo _ OrderInfo {orderType = SBuyOrder}) = True -isBuyOrder _ = False +isBuyOrderMEI :: MatchExecutionInfo -> Bool +isBuyOrderMEI (OrderExecutionInfo _ OrderInfo {orderType = SBuyOrder}) = True +isBuyOrderMEI _ = False -- | Given a list of MatchExecutionInfo, sums the offered tokens filled sumOfOffered :: [MatchExecutionInfo] -> Natural diff --git a/geniusyield-orderbot/test/Tests/Prop/Utils.hs b/geniusyield-orderbot/test/Tests/Prop/Utils.hs index f1c673f..346f81c 100644 --- a/geniusyield-orderbot/test/Tests/Prop/Utils.hs +++ b/geniusyield-orderbot/test/Tests/Prop/Utils.hs @@ -30,11 +30,11 @@ genGYTxOutRef = do genHexString :: Gen Char genHexString = elements $ ['a'..'f'] ++ ['0'..'9'] --- | Generator for the Volume. With a fixed minVolume of 34% -genVolume :: Gen Volume -genVolume = do - vh <- chooseInteger (100,100000000) - pure $ Volume (ceiling $ (vh % 1) * (34 % 100)) (fromIntegral vh) +-- | Given a min, generate the max Volume. +genVolume :: Integer -> Gen Volume +genVolume min = do + vh <- chooseInteger (min ,100000000) + pure $ Volume (fromIntegral min) (fromIntegral vh) {- | Generator for the Volume. With a fixed minVolume of 34%. with an specified minimum and maximum. @@ -53,19 +53,18 @@ genPrice = do -- | Generator for a buy order, using all previous generators genBuyOrder :: OrderAssetPair -> Gen (OrderInfo 'BuyOrder) -genBuyOrder oap = OrderInfo <$> genGYTxOutRef - <*> pure SBuyOrder - <*> pure oap - <*> genVolume - <*> genPrice - <*> pure Nothing +genBuyOrder oap = do + price <- genPrice + volume <- genVolume (ceiling $ getPrice price) + utxoRef <- genGYTxOutRef + return $ OrderInfo utxoRef SBuyOrder oap volume price Nothing -- | Generator for a sell order, using all previous generators genSellOrder :: OrderAssetPair -> Gen (OrderInfo 'SellOrder) genSellOrder oap = OrderInfo <$> genGYTxOutRef <*> pure SSellOrder <*> pure oap - <*> genVolume + <*> genVolume 1 <*> genPrice <*> pure Nothing @@ -87,8 +86,9 @@ shrinkOrderInfo :: forall t. OrderInfo t -> [OrderInfo t] shrinkOrderInfo order = [ order { volume = vol'} | vol' <- shrinkVolume (volume order) ] --- | Shrinks a Volume by making sure the min is positive and the max is over the min +{- | Shrinks a Volume by making sure the max is over the min. + The min is fixed, so no need to shrink it. +-} shrinkVolume :: Volume -> [Volume] shrinkVolume v@Volume{volumeMin, volumeMax} = - [ v { volumeMin = vl' } | vl' <- shrinkIntegral volumeMin, vl' > 0 ] ++ - [ v { volumeMax = vh' } | vh' <- shrinkIntegral volumeMax, vh' > volumeMin ] + [ v { volumeMax = vh' } | vh' <- shrinkIntegral volumeMax, vh' >= volumeMin ] diff --git a/impl/datasource-providers/GeniusYield/OrderBot/DataSource/Providers.hs b/impl/datasource-providers/GeniusYield/OrderBot/DataSource/Providers.hs index d08998c..6a1e795 100644 --- a/impl/datasource-providers/GeniusYield/OrderBot/DataSource/Providers.hs +++ b/impl/datasource-providers/GeniusYield/OrderBot/DataSource/Providers.hs @@ -15,16 +15,16 @@ module GeniusYield.OrderBot.DataSource.Providers , withEachAssetOrders ) where -import Data.List (foldl') -import Data.Map.Strict (Map) -import qualified Data.Map.Strict as Map +import Data.List (foldl') +import Data.Map.Strict (Map) +import qualified Data.Map.Strict as Map -import GeniusYield.DEX.Api.PartialOrder -import GeniusYield.OrderBot.Types -import GeniusYield.TxBuilder -import GeniusYield.Types -import GeniusYield.DEX.Api.Types (DEXInfo(..), mkPORefs) -import Control.Monad.Reader (ReaderT(runReaderT)) +import Control.Monad.Reader (ReaderT (runReaderT)) +import GeniusYield.DEX.Api.PartialOrder +import GeniusYield.DEX.Api.Types (DEXInfo (..), mkPORefs) +import GeniusYield.OrderBot.Types +import GeniusYield.TxBuilder +import GeniusYield.Types data Connection = Connection !GYNetworkId {-# UNPACK #-} !GYProviders @@ -42,12 +42,12 @@ mkDEX :: GYMintingPolicy PlutusV2 -> GYValidator PlutusV2 -> Maybe GYTxOutRef -> Maybe GYTxOutRef - -> (GYAddress, GYAssetClass, GYTxOutRef) + -> (GYAddress, GYAssetClass) -> DEX -mkDEX nft partialOrder mVRef mNPRef (porAddr, porAC, porRef) = +mkDEX nft partialOrder mVRef mNPRef (porAddr, porAC) = DEXInfo { dexNftPolicy = nft , dexPartialOrderValidator = partialOrder - , dexPORefs = mkPORefs porAddr porAC porRef mVRef mNPRef + , dexPORefs = mkPORefs porAddr porAC mVRef mNPRef } withEachAssetOrders @@ -65,7 +65,7 @@ withEachAssetOrders c dex assetFilter f acc = do let (buys, sells) = foldl' ( \(!buys, !sells) (SomeOrderInfo oInf@OrderInfo {orderType}) -> case orderType of - SBuyOrder -> (oInf : buys, sells) + SBuyOrder -> (oInf : buys, sells) SSellOrder -> (buys, oInf : sells) ) ([], []) @@ -117,7 +117,7 @@ partialOrderInfoToOrderInfo :: (OrderAssetPair, PartialOrderInfo) -> SomeOrderIn partialOrderInfoToOrderInfo = uncurry mkOrderInfo isAfterStart :: GYTime -> Maybe GYTime -> Bool -isAfterStart current = maybe True (current >) +isAfterStart current = maybe True (current >=) isBeforeEnd :: GYTime -> Maybe GYTime -> Bool -isBeforeEnd current = maybe True (current <) +isBeforeEnd current = maybe True (current <=) diff --git a/impl/orderbook-list/GeniusYield/OrderBot/OrderBook/List.hs b/impl/orderbook-list/GeniusYield/OrderBot/OrderBook/List.hs index 84d482e..3ee625d 100644 --- a/impl/orderbook-list/GeniusYield/OrderBot/OrderBook/List.hs +++ b/impl/orderbook-list/GeniusYield/OrderBot/OrderBook/List.hs @@ -88,6 +88,8 @@ buildOrderBookList :: [(OrderAssetPair, OrderBook)] -> (# OrderAssetPair, [OrderInfo 'BuyOrder], [OrderInfo 'SellOrder] #) -> [(OrderAssetPair, OrderBook)] +buildOrderBookList acc (# _, _, [] #) = acc +buildOrderBookList acc (# _, [], _ #) = acc buildOrderBookList acc (# oap, buyOrders, sellOrders #) = (oap, OrderBook (Orders $ sortOn price sellOrders) (Orders $ sortOn (Down . price) buyOrders)) : acc diff --git a/scripts/kupo-mainnet.sh b/scripts/kupo-mainnet.sh new file mode 100755 index 0000000..a653c06 --- /dev/null +++ b/scripts/kupo-mainnet.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +FROM_BABBAGE=72316796.c58a24ba8203e7629422a24d9dc68ce2ed495420bf40d9dab124373655161a20 +MATCH_ORDERS=22f6999d4effc0ade05f6e1a70b702c65d6b3cdf0e301e4a8267f585.* +MATCH_BOT=$1 +MATCH_MINTING_POLICY_REF=1@062f97b0e64130bc18b4a227299a62d6d59a4ea852a4c90db3de2204a2cd19ea +MATCH_VALIDATOR_REF=2@062f97b0e64130bc18b4a227299a62d6d59a4ea852a4c90db3de2204a2cd19ea +MATCH_CONFIG_ADDR=addr1w9zr09hgj7z6vz3d7wnxw0u4x30arsp5k8avlcm84utptls8uqd0z + +kupo \ + --node-socket $NODE_MAINNET/db/node.socket \ + --node-config $NODE_MAINNET/config.json \ + --since $FROM_BABBAGE \ + --match $MATCH_ORDERS \ + --match $MATCH_BOT \ + --match $MATCH_MINTING_POLICY_REF \ + --match $MATCH_VALIDATOR_REF \ + --match $MATCH_CONFIG_ADDR \ + --prune-utxo \ + --workdir $NODE_MAINNET/kupo/sor/db diff --git a/scripts/kupo-preprod.sh b/scripts/kupo-preprod.sh new file mode 100755 index 0000000..601861e --- /dev/null +++ b/scripts/kupo-preprod.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +FROM_BABBAGE=3542390.f93e682d5b91a94d8660e748aef229c19cb285bfb9830db48941d6a78183d81f +MATCH_ORDERS=158f42b49e0841301b45358b87744167f43359cc3785eab8d30893e1.* +MATCH_BOT=$1 +MATCH_MINTING_POLICY_REF=1@be6f8dc16d4e8d5aad566ff6b5ffefdda574817a60d503e2a0ea95f773175050 +MATCH_VALIDATOR_REF=2@be6f8dc16d4e8d5aad566ff6b5ffefdda574817a60d503e2a0ea95f773175050 +MATCH_CONFIG_ADDR=addr_test1wrgvy8fermjrruaf7fnndtmpuw4xx4cnvfqjp5zqu8kscfcvh32qk + +kupo \ + --node-socket $NODE_PREPROD/db/node.socket \ + --node-config $NODE_PREPROD/config.json \ + --since $FROM_BABBAGE \ + --match $MATCH_ORDERS \ + --match $MATCH_BOT \ + --match $MATCH_MINTING_POLICY_REF \ + --match $MATCH_VALIDATOR_REF \ + --match $MATCH_CONFIG_ADDR \ + --prune-utxo \ + --workdir $NODE_PREPROD/kupo/sor/db