lib/order/txns/buy/makePlaceAlgoTxns.js

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