/*
* Copyright (C) 2021-2022 Algodex VASP (BVI) Corp.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
const algosdk = require('algosdk');
const logger = require('../../../logger');
const AlgodError = require('../../../error/AlgodError');
const enc = require('../../../utils/encoder');
const teal = require('../../../teal');
/**
* # 🏭 makeExecuteAlgoTxns(order)
*
* > Transaction Factory for Executing Buy Orders
* ### ☠ Partial Order Execution:
*
* | Index | Direction | Type | Description | Signer |
* | ----- | --------- | ---- | ----------- | ------ |
* | TXN 0 | ESCROW TO ORDERBOOK | {@link algosdk.makeApplicationNoOpTxn} | Transaction must be a call to a stateful contract | {@link Wallet} |
* | TXN 1 | ESCROW TO SELLER | {@link algosdk.makePaymentTxn} | Payment transaction from this escrow to seller | {@link algosdk.LogicSigAccount} |
* | TXN 2 | SELLER TO BUYER | {@link algosdk.makeAssetTransferTxn} | Asset transfer from seller to owner of this escrow (buyer) | {@link Wallet} |
* | TXN 3 | SELLER TO ESCROW | {@link algosdk.makePaymentTxn} | Pay fee refund transaction | {@link Wallet} |
*
*
* ### ☠️ Full Order Execution:
*
* | Index | Direction | Type | Description | Signer |
* | ----- | --------- | ---- | ----------- | ------ |
* | TXN 0 | ESCROW TO ORDERBOOK | {@link algosdk.makeApplicationCloseOutTxn} | Transaction must be a call to a stateful contract | {@link Wallet} |
* | TXN 1 | ESCROW TO SELLER | {@link algosdk.makeAssetTransferTxn} | Payment transaction from this escrow to seller, with closeout to owner (buyer) | {@link Wallet} |
* | TXN 2 | SELLER TO BUYER | {@link algosdk.makeAssetTransferTxn} | Asset transfer from seller to owner of this escrow (buyer) | {@link Wallet} |
*
*
* #
* @param {Order} order The Order
* @param {boolean} [withCloseout] Close Account
* @return {Promise<Transactions>}
* @memberOf module:txns/buy
*/
async function makeExecuteAlgoTxns(
order,
withCloseout = false,
) {
if (!(order.client instanceof algosdk.Algodv2)) {
throw new AlgodError('Order must have a valid SDK client');
}
if (typeof order.appId !== 'number') {
throw new TypeError('Must have valid Application Index');
}
if (typeof order.contract !== 'undefined' && typeof order.contract.entry !== 'string') {
throw new TypeError('Order must have a valid contract state with an entry!');
}
// scrappy way to add batch uniqueness without encoding extra appArg
const uniqueNote = enc.encode(`${Math.random()}`);
let closeRemainderTo;
if (withCloseout) {
closeRemainderTo = order.contract.creator;
}
const _suggestedParams = await teal.getTransactionParams(order.client, order?.contract?.params, true);
_suggestedParams.flatFee = false;
_suggestedParams.fee = 0;
// if (!exists) { // ToDo: I don't think we need to check if taker's exist
logger.debug({entry: order.contract.entry}, 'Creating new order!');
/**
* Application Arguments
* @type {Array}
*/
const _appAccts = [
order.contract.creator,
order.wallet.address,
];
/**
* Application Arguments
* @type {Array<Uint8Array>}
*/
const _appArgs = [
enc.encode(
typeof closeRemainderTo === 'undefined' ?
'execute' :
'execute_with_closeout',
),
enc.encode(order.contract.entry), // Discussion: pointed out earlier but only need to slice if address is in entry
];
/**
* Taker Algo Structures
* @type {Structures}
*/
const _outerTxns = [
{
// TXN 0 - ESCROW TO ORDERBOOK: transaction must be a call to a stateful contract
unsignedTxn: typeof closeRemainderTo === 'undefined' ?
algosdk.makeApplicationNoOpTxn(
order.contract.lsig.address(),
_suggestedParams,
order.appId,
_appArgs,
_appAccts,
undefined,
undefined,
uniqueNote) :
algosdk.makeApplicationCloseOutTxn(
order.contract.lsig.address(),
_suggestedParams,
order.appId,
_appArgs,
_appAccts,
undefined,
undefined,
uniqueNote),
lsig: order.contract.lsig,
},
{
// TXN 1 - ESCROW TO SELLER: Payment transaction from this escrow to seller
// TXN 1 - ESCROW TO SELLER: Payment transaction from this escrow to seller, with closeout to owner (buyer)
unsignedTxn: algosdk.makePaymentTxnWithSuggestedParams(
order.contract.lsig.address(),
order.wallet.address,
// TODO: fix total
order.contract.total,
closeRemainderTo, // closeRemainderTo will only be set during a withCloseout operation
uniqueNote,
_suggestedParams,
undefined,
),
lsig: order.contract.lsig, // When queuedOrder matches taker price lsig is recompiled to escrowlsig
},
{
// TXN 2 - SELLER TO BUYER: Asset transfer from seller to owner of this escrow (buyer)
unsignedTxn: algosdk.makeAssetTransferTxnWithSuggestedParams(
order.wallet.address,
order.contract.creator,
undefined,
undefined,
order.contract.amount,
uniqueNote,
order.asset.id,
_suggestedParams,
undefined,
),
senderAcct: order.wallet.address,
},
];
_outerTxns[2].unsignedTxn.flatFee = true;
const refundFees = 0.002 * 1000000;
if (typeof closeRemainderTo === 'undefined') {
_outerTxns.push({
// TXN 3 - SELLER TO ESCROW: Pay fee refund transaction
unsignedTxn: algosdk.makePaymentTxnWithSuggestedParams(
order.wallet.address,
order.contract.lsig.address(),
refundFees,
undefined,
uniqueNote,
_suggestedParams,
undefined,
),
senderAcct: order.wallet.address,
});
}
return _outerTxns;
}
module.exports = makeExecuteAlgoTxns;