lib/order/txns/buy/makePlaceAlgoTxns.js

  1. /*
  2. * Copyright (C) 2021-2022 Algodex VASP (BVI) Corp.
  3. *
  4. * This Source Code Form is subject to the terms of the Mozilla Public
  5. * License, v. 2.0. If a copy of the MPL was not distributed with this
  6. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
  7. */
  8. const algosdk = require('algosdk');
  9. const logger = require('../../../logger');
  10. const AlgodError = require('../../../error/AlgodError');
  11. const enc = require('../../../utils/encoder');
  12. const teal = require('../../../teal');
  13. const getOptedIn = require('../../../wallet/getOptedIn');
  14. /**
  15. * # 🏭 makePlaceAlgoTxns(order)
  16. *
  17. * > Transaction Factory for Placing Buy Orders
  18. *
  19. *
  20. * Place a buy order into the Algodex {@tutorial Orderbook}. This is referred to as a {@tutorial Maker} order which is
  21. * "Placed into the Orderbook". If the order has been previously placed it will have a contract
  22. * key called "creator". This key determines if the order should call the ALGO delegate contract
  23. * application opt in.
  24. *
  25. * Once the initial transaction has been created and the contract.creator has been sent, the sdk
  26. * reverts to regular payment transactions to the escrow account. These payment transactions
  27. * should include a note that relates to the operation for indexing since they will not have
  28. * the orderbook application call.
  29. *
  30. * The transaction generator also supports bypassing the opt-in check by passing in the optIn flag
  31. *
  32. * ## ALGO Delegate Contract Transactions
  33. *
  34. * ### ➕ Open Order Transactions:
  35. *
  36. * | Index | Direction | Type | Description | Signer |
  37. * | ----- | --------- | ---- | ----------- | ------ |
  38. * | TXN 0 | BUYER TO ESCROW | {@link algosdk.makePaymentTxn} | Pay from order creator to escrow account | {@link Wallet} |
  39. * | TXN 1 | ESCROW TO ORDERBOOK | {@link algosdk.makeApplicationOptInTxn} | Stateful app opt-in to order book | {@link algosdk.LogicSigAccount} |
  40. * | TXN 2 | BUYER TO BUYER | {@link algosdk.makeAssetTransferTxn} | (Optional) ASA opt-in for the order creator's original wallet account | {@link Wallet} |
  41. *
  42. * ### 💰 Add Funds to Order Escrow Transactions:
  43. *
  44. * | Index | Direction | Type | Description | Signer |
  45. * | ----- | --------- | ---- | ----------- | ------ |
  46. * | TXN 0 | BUYER TO ESCROW | {@link algosdk.makePaymentTxn} | Pay from order creator to escrow account | {@link Wallet} |
  47. * | TXN 1 | BUYER TO BUYER | {@link algosdk.makeAssetTransferTxn} | (Optional) ASA opt-in for the order creator's original wallet account | {@link Wallet} |
  48. *
  49. * #
  50. *
  51. *
  52. * @example
  53. * const {makePlaceAlgoTxns, compile} = require('@algodex/algodex-sdk')
  54. * const txns = makePlaceAlgoTxns( await compile({
  55. * 'asset': {
  56. * 'id': 15322902,
  57. * 'decimals': 6,
  58. * },
  59. * 'address': 'WYWRYK42XADLY3O62N52BOLT27DMPRA3WNBT2OBRT65N6OEZQWD4OSH6PI',
  60. * 'price': 2,
  61. * 'amount': 1,
  62. * 'total': 2,
  63. * 'execution': 'maker',
  64. * 'type': 'buy',
  65. * 'appId': 22045503,
  66. * 'version': 6,
  67. * }))
  68. *
  69. * @param {Order} order The Order
  70. * @param {boolean} [optIn] Flag for opting in
  71. * @return {Promise<Transactions>}
  72. * @memberOf module:txns/buy
  73. */
  74. async function makePlaceAlgoTxns(
  75. order,
  76. optIn = false,
  77. ) {
  78. if (!(order.indexer instanceof algosdk.Indexer)) {
  79. throw new AlgodError('Order must have a valid SDK client');
  80. }
  81. if (typeof order.appId !== 'number') {
  82. throw new TypeError('Must have valid Application Index');
  83. }
  84. if (typeof order.contract !== 'undefined' && typeof order.contract.entry !== 'string') {
  85. throw new TypeError('Order must have a valid contract state with an entry!');
  86. }
  87. if (order.execution !== 'maker') {
  88. throw new Error('Must be maker only mode!');
  89. }
  90. if (order.contract?.amount === 0) {
  91. throw new Error('Cannot place a maker order with a 0 Amount');
  92. }
  93. logger.info({order: {price: order.price, amount: order.amount, total: order.total}, optIn}, 'Make Place Algo Txns');
  94. // TODO: Note that contains the creator address and the current operation.
  95. const _note = undefined;
  96. // If the order has a creator key, it already exists
  97. const _exists = typeof order?.contract?.creator !== 'undefined';
  98. if (_exists) {
  99. throw new TypeError('Adding to an existing algo escrow is disabled!');
  100. }
  101. let accountInfo;
  102. if (typeof order?.wallet?.assets === 'undefined' && !optIn) {
  103. logger.warn({address: order.address}, 'Loading account info!');
  104. ({account: accountInfo} = await order.indexer.lookupAccountByID(order.address).do()); // We need to have the same structure between the conditionals
  105. } else {
  106. accountInfo = order.wallet;
  107. }
  108. const _optIn = !optIn ?
  109. await getOptedIn(order.indexer, accountInfo, order.asset.id) :
  110. optIn;
  111. const _suggestedParams = await teal.getTransactionParams(order.client, order.contract.params, true);
  112. /**
  113. * Place Algo Structure
  114. * @type {Structures}
  115. */
  116. const _outerTxns = [{
  117. // Payment Transaction
  118. // TODO: Add Note that tracks this payment transaction when the escrow already exists
  119. unsignedTxn: algosdk.makePaymentTxnWithSuggestedParams(
  120. order.address,
  121. order.contract.lsig.address(),
  122. order.contract.total,
  123. undefined,
  124. _note,
  125. _suggestedParams,
  126. undefined,
  127. ),
  128. senderAcct: order.address,
  129. }];
  130. // Open Order Transaction
  131. if (!_exists) {
  132. logger.debug({entry: order.contract.entry.slice(59)}, 'Creating new order!');
  133. /**
  134. * Application Arguments
  135. * @type {Array<Uint8Array>}
  136. */
  137. const _appArgs = [
  138. enc.encode('open'),
  139. enc.encode(order.contract.entry.slice(59)),
  140. new Uint8Array([order.version]),
  141. ];
  142. // Create Escrow appOptIn Transaction
  143. // This contract application args are parsed by the Algodex Indexer
  144. _outerTxns.push({
  145. unsignedTxn: await teal.txns.makeTransactionFromLogicSig(
  146. order.client,
  147. 'appOptIn',
  148. order.contract.lsig,
  149. order?.contract?.params,
  150. order.appId,
  151. _appArgs,
  152. ),
  153. lsig: order.contract.lsig,
  154. });
  155. }
  156. // Optin Transaction
  157. if (!_optIn) {
  158. logger.debug({address: order.address, asset: order.asset.id}, 'Opting in!');
  159. _outerTxns.push({
  160. unsignedTxn: algosdk.makeAssetTransferTxnWithSuggestedParams(
  161. order.address,
  162. order.address,
  163. undefined,
  164. undefined,
  165. 0,
  166. undefined,
  167. order.asset.id,
  168. _suggestedParams,
  169. undefined,
  170. ),
  171. senderAcct: order.address,
  172. });
  173. }
  174. return _outerTxns;
  175. }
  176. module.exports = makePlaceAlgoTxns;
  177. JAVASCRIPT
    Copied!