UNPKG

@algodex/algodex-sdk-patch

Version:

API calls for interacting with the Algorand blockchain

626 lines (504 loc) 24.7 kB
///////////////////////////// // Alexander Trefonas // // 7/12/2021 // // Copyright Algodev Inc // // All Rights Reserved. // ///////////////////////////// const http = require('http'); const algosdk = require('algosdk'); const myAlgoWalletUtil = require('./MyAlgoWalletUtil.js'); const algoDelegateTemplate = require('./algo_delegate_template_teal.js'); const asaDelegateTemplate = require('./ASA_delegate_template_teal.js'); const algoOrderBook = require('./dex_teal.js'); const asaOrderBook = require('./asa_dex_teal.js'); //require('./dex_teal.js'); const dexInternal = require('./algodex_internal_api.js'); const algodex = require('./algodex_api.js'); const constants = require('./constants.js'); const testHelper = require('./test_helper.js'); let ALGO_ESCROW_ORDER_BOOK_ID = -1; let ASA_ESCROW_ORDER_BOOK_ID = -1; const GenerateTransactions = { // helper function to compile program source compileProgram: async function (client, programSource) { let encoder = new TextEncoder(); let programBytes = encoder.encode(programSource); let compileResponse = await client.compile(programBytes).do(); let compiledBytes = new Uint8Array(Buffer.from(compileResponse.result, "base64")); return compiledBytes; }, getAssetSendTxn : async function (client, fromAcct, toAcct, amount, assetId, shouldClose) { if (typeof(fromAcct) !== "string") { fromAcct = fromAcct.addr; } if (typeof(toAcct) !== "string") { toAcct = toAcct.addr; } let params = await client.getTransactionParams().do(); // comment out the next two lines to use suggested fee params.fee = 1000; params.flatFee = true; let closeAddr = undefined; if (shouldClose === true) { closeAddr = toAcct; } let txn = algosdk.makeAssetTransferTxnWithSuggestedParams(fromAcct, toAcct, closeAddr, undefined, amount, undefined, assetId, params); return txn; }, getPayTxn : async function (client, fromAcct, toAcct, amount, shouldClose) { if (typeof(fromAcct) !== "string") { fromAcct = fromAcct.addr; } if (typeof(toAcct) !== "string") { toAcct = toAcct.addr; } let params = await client.getTransactionParams().do(); // comment out the next two lines to use suggested fee params.fee = 1000; params.flatFee = true; const enc = new TextEncoder(); let note = enc.encode("Hello World"); let closeAddr = undefined; if (shouldClose === true) { closeAddr = toAcct; } let txn = algosdk.makePaymentTxnWithSuggestedParams(fromAcct, toAcct, amount, closeAddr, note, params); return txn; }, getCreateAppTxn : async function (client, creatorAccount, isAlgoEscrowApp = true) { // define sender as creator let approvalProgramSource = null; if (isAlgoEscrowApp) { approvalProgramSource = algoOrderBook.getAlgoOrderBookApprovalProgram(); } else { approvalProgramSource = asaOrderBook.getASAOrderBookApprovalProgram(); } const clearProgramSource = algoOrderBook.getClearProgram(); // declare application state storage (immutable) const localInts = 2; const localBytes = 1; const globalInts = 0; const globalBytes = 1; sender = creatorAccount.addr; // declare onComplete as NoOp onComplete = algosdk.OnApplicationComplete.NoOpOC; // get node suggested parameters let params = await client.getTransactionParams().do(); // create unsigned transaction let approvalProgram = await this.compileProgram(client, approvalProgramSource); let clearProgram = await this.compileProgram(client, clearProgramSource); let txn = algosdk.makeApplicationCreateTxn(sender, params, onComplete, approvalProgram, clearProgram, localInts, localBytes, globalInts, globalBytes,); return txn; }, getCloseAlgoEscrowOrderTxns : async function (algodClient, creator, price, assetId, appId) { let numAndDenom = algodex.getNumeratorAndDenominatorFromPrice(price); let n = numAndDenom.n; let d = numAndDenom.d; let creatorAddr = creator.addr; let escrowSource = algodex.buildDelegateTemplateFromArgs(0, assetId, n, d, creatorAddr, false, constants.ESCROW_CONTRACT_VERSION); let lsig = await algodex.getLsigFromProgramSource(algosdk, algodClient, escrowSource, constants.DEBUG_SMART_CONTRACT_SOURCE); let escrowAccountAddr = lsig.address(); let outerTxns = []; // "2500-625-0-15322902" let orderBookEntry = n + "-" + d + "-0-" + assetId; console.log("closing order from order book entry!"); console.log("escrowAccountAddr, creatorAddr, orderBookEntry", escrowAccountAddr, creatorAddr, orderBookEntry); let appArgs = []; let enc = new TextEncoder(); appArgs.push(enc.encode("close")); appArgs.push(enc.encode(orderBookEntry)); //appArgs.push(enc.encode(creatorAddr)); console.log("args length: " + appArgs.length); // get node suggested parameters let params = await algodClient.getTransactionParams().do(); // create unsigned transaction let txn = algosdk.makeApplicationClearStateTxn(lsig.address(), params, appId, appArgs); outerTxns.push({ unsignedTxn: txn, lsig: lsig }); // Submit the transaction // Make payment tx signed with lsig let txn2 = algosdk.makePaymentTxnWithSuggestedParams(lsig.address(), creatorAddr, 0, creatorAddr, undefined, params); outerTxns.push({ unsignedTxn: txn2, lsig: lsig }); let txn3 = algosdk.makePaymentTxnWithSuggestedParams(creatorAddr, creatorAddr, 0, undefined, undefined, params); outerTxns.push({ unsignedTxn: txn3, senderAcct: creator }); return outerTxns; }, getCloseASAEscrowOrderTxns : async function (algodClient, creator, price, assetId, appId) { let numAndDenom = algodex.getNumeratorAndDenominatorFromPrice(price); let n = numAndDenom.n; let d = numAndDenom.d; let creatorAddr = creator.addr; let escrowSource = algodex.buildDelegateTemplateFromArgs(0, assetId, n, d, creatorAddr, true, constants.ESCROW_CONTRACT_VERSION); let lsig = await algodex.getLsigFromProgramSource(algosdk, algodClient, escrowSource, constants.DEBUG_SMART_CONTRACT_SOURCE); let escrowAccountAddr = lsig.address(); let outerTxns = []; // "2500-625-0-15322902" let orderBookEntry = n + "-" + d + "-0-" + assetId; console.log("closing order from order book entry!"); console.log("escrowAccountAddr, creatorAddr, orderBookEntry", escrowAccountAddr, creatorAddr, orderBookEntry); let appArgs = []; let enc = new TextEncoder(); appArgs.push(enc.encode("close")); appArgs.push(enc.encode(orderBookEntry)); //appArgs.push(enc.encode(creatorAddr)); console.log("args length: " + appArgs.length); // get node suggested parameters let params = await algodClient.getTransactionParams().do(); // create unsigned transaction let txn = algosdk.makeApplicationClearStateTxn(lsig.address(), params, appId, appArgs) let txId = txn.txID().toString(); // Submit the transaction // create optin transaction // sender and receiver are both the same let sender = lsig.address(); let recipient = creatorAddr; // We set revocationTarget to undefined as // This is not a clawback operation let revocationTarget = undefined; // CloseReaminerTo is set to undefined as // we are not closing out an asset let closeRemainderTo = creatorAddr; // We are sending 0 assets let amount = 0; // signing and sending "txn" allows sender to begin accepting asset specified by creator and index let txn2 = algosdk.makeAssetTransferTxnWithSuggestedParams(sender, recipient, closeRemainderTo, revocationTarget, amount, undefined, assetId, params); // Make payment tx signed with lsig let txn3 = algosdk.makePaymentTxnWithSuggestedParams(lsig.address(), creatorAddr, 0, creatorAddr, undefined, params); // proof of ownership transaction let txn4 = algosdk.makePaymentTxnWithSuggestedParams(creatorAddr, creatorAddr, 0, undefined, undefined, params); outerTxns.push({ unsignedTxn: txn, lsig: lsig }); outerTxns.push({ unsignedTxn: txn2, lsig: lsig }); outerTxns.push({ unsignedTxn: txn3, lsig: lsig }); outerTxns.push({ unsignedTxn: txn4, senderAcct: creator }); return outerTxns; }, getExecuteAlgoEscrowOrderTxns : async function (algodClient, executorAccount, makerAccount, algoAmountReceiving, asaAmountSending, price, assetId, appId, shouldClose = false) { const orderCreatorAddr = makerAccount.addr; const min = 0; const numAndDenom = algodex.getNumeratorAndDenominatorFromPrice(price); const n = numAndDenom.n; const d = numAndDenom.d; const takerAddr = executorAccount.addr; let appCallType = null; const orderBookEntry = algodex.generateOrder(orderCreatorAddr, n, d, min, assetId, false); const refundFees = 0.002 * 1000000; // fees refunded to escrow in case of partial execution const escrowSource = algodex.buildDelegateTemplateFromArgs(0, assetId, n, d, orderCreatorAddr, false, constants.ESCROW_CONTRACT_VERSION); const lsig = await algodex.getLsigFromProgramSource(algosdk, algodClient, escrowSource, constants.DEBUG_SMART_CONTRACT_SOURCE); const params = await algodClient.getTransactionParams().do(); const appAccts = []; appAccts.push(orderCreatorAddr); appAccts.push(takerAddr); let closeRemainderTo = undefined; if (shouldClose) { closeRemainderTo = makerAccount.addr; } if (closeRemainderTo == undefined) { appCallType = "execute"; } else { appCallType = "execute_with_closeout"; } console.log("arg1: " + appCallType); console.log("arg2: " + orderBookEntry); console.log("arg3: " + orderCreatorAddr); let appArgs = []; let enc = new TextEncoder(); appArgs.push(enc.encode(appCallType)); appArgs.push(enc.encode(orderBookEntry)); console.log(appArgs.length); let transaction1 = null; if (closeRemainderTo == undefined) { transaction1 = algosdk.makeApplicationNoOpTxn(lsig.address(), params, appId, appArgs, appAccts); } else { transaction1 = algosdk.makeApplicationCloseOutTxn(lsig.address(), params, appId, appArgs, appAccts); } // Make payment tx signed with lsig let transaction2 = algosdk.makePaymentTxnWithSuggestedParams(lsig.address(), takerAddr, algoAmountReceiving, closeRemainderTo, undefined, params); // Make asset xfer let transaction3 = await this.getAssetSendTxn(algodClient, takerAddr, orderCreatorAddr, asaAmountSending, assetId, false); let transaction4 = null; if (closeRemainderTo == undefined) { // create refund transaction for fees transaction4 = await this.getPayTxn(algodClient, takerAddr, lsig.address(), refundFees, false); } let retTxns = []; retTxns.push({ 'unsignedTxn': transaction1, 'lsig': lsig }); retTxns.push({ 'unsignedTxn': transaction2, 'lsig': lsig }); retTxns.push({ 'unsignedTxn': transaction3, 'senderAcct': executorAccount }); if (transaction4 != null) { retTxns.push({ 'unsignedTxn': transaction4, 'senderAcct': executorAccount }); } return retTxns; }, getExecuteASAEscrowOrderTxns : async function (algodClient, executorAccount, makerAccount, algoAmountSending, asaAmountReceiving, price, assetId, appId, shouldClose = false) { console.log("here664 ", executorAccount, makerAccount, algoAmountSending, asaAmountReceiving, price, assetId, appId, shouldClose); const orderCreatorAddr = makerAccount.addr; const min = 0; const numAndDenom = algodex.getNumeratorAndDenominatorFromPrice(price); const n = numAndDenom.n; const d = numAndDenom.d; const takerAddr = executorAccount.addr; const refundFees = 0.002 * 1000000; // fees refunded to escrow in case of partial execution let appCallType = null; const orderBookEntry = algodex.generateOrder(orderCreatorAddr, n, d, min, assetId, false); const escrowSource = algodex.buildDelegateTemplateFromArgs(0, assetId, n, d, orderCreatorAddr, true, constants.ESCROW_CONTRACT_VERSION); const lsig = await algodex.getLsigFromProgramSource(algosdk, algodClient, escrowSource, constants.DEBUG_SMART_CONTRACT_SOURCE); const params = await algodClient.getTransactionParams().do(); const appAccts = []; appAccts.push(orderCreatorAddr); appAccts.push(takerAddr); let transaction1 = null; let closeRemainderTo = undefined; if (shouldClose) { closeRemainderTo = makerAccount.addr; appCallType = "execute_with_closeout"; } else { appCallType = "execute"; } let appArgs = []; let enc = new TextEncoder(); appArgs.push(enc.encode(appCallType)); appArgs.push(enc.encode(orderBookEntry)); if (closeRemainderTo == undefined) { transaction1 = algosdk.makeApplicationNoOpTxn(lsig.address(), params, appId, appArgs, appAccts, [0], [assetId]); } else { transaction1 = algosdk.makeApplicationCloseOutTxn(lsig.address(), params, appId, appArgs, appAccts, [0], [assetId]); } console.log("app call type is: " + appCallType); let transaction2 = await this.getPayTxn(algodClient, takerAddr, orderCreatorAddr, algoAmountSending, false); let accountInfo = await algodex.getAccountInfo(takerAddr); let takerAlreadyOptedIntoASA = false; if (accountInfo != null && accountInfo['assets'] != null && accountInfo['assets'].length > 0) { for (let i = 0; i < accountInfo['assets'].length; i++) { if (accountInfo['assets'][i]['asset-id'] === assetId) { takerAlreadyOptedIntoASA = true; break; } } } // asset opt-in transfer let transaction2b = null; if (!takerAlreadyOptedIntoASA) { transaction2b = await this.getAssetSendTxn(algodClient, takerAddr, takerAddr, 0, assetId, false); } // Make asset xfer // Asset transfer from escrow account to order executor let transaction3 = algosdk.makeAssetTransferTxnWithSuggestedParams(lsig.address(), takerAddr, closeRemainderTo, undefined, asaAmountReceiving, undefined, assetId, params); let transaction4 = null; if (closeRemainderTo != undefined) { // Make payment tx signed with lsig back to owner creator console.log("making transaction4 due to closeRemainderTo"); transaction4 = algosdk.makePaymentTxnWithSuggestedParams(lsig.address(), orderCreatorAddr, 0, orderCreatorAddr, undefined, params); } else { // Make fee refund transaction transaction4 = await this.getPayTxn(algodClient, takerAddr, lsig.address(), refundFees, false); } if (transaction2b != null) { myAlgoWalletUtil.setTransactionFee(transaction2b); } let retTxns = []; retTxns.push({ 'unsignedTxn': transaction1, 'lsig': lsig }); retTxns.push({ 'unsignedTxn': transaction2, // FIXME - change to sdk 'senderAcct': executorAccount }); if (transaction2b != null) { retTxns.push({ 'unsignedTxn': transaction2b, // FIXME - change to sdk 'senderAcct': executorAccount }); } retTxns.push({ 'unsignedTxn': transaction3, 'lsig': lsig }); if (closeRemainderTo != undefined) { // close out algo balance to owner retTxns.push({ 'unsignedTxn': transaction4, 'lsig': lsig }); } else { // fee refund retTxns.push({ 'unsignedTxn': transaction4, 'senderAcct': executorAccount }); } return retTxns; }, getPlaceAlgoEscrowOrderTxns : async function (algodClient, makerAccount, algoOrderSize, price, assetId, appId, isExistingEscrow = false, skipASAOptIn = false) { const makerAddr = makerAccount.addr; const min = 0; const numAndDenom = algodex.getNumeratorAndDenominatorFromPrice(price); const n = numAndDenom.n; const d = numAndDenom.d; console.log("getPlaceAlgoEscrowOrderTxns makerWalletAddr, n, d, min, assetId", makerAddr, n, d, min, assetId); let program = algodex.buildDelegateTemplateFromArgs(min, assetId, n, d, makerAddr, false, constants.ESCROW_CONTRACT_VERSION); let lsig = await algodex.getLsigFromProgramSource(algosdk, algodClient, program, constants.DEBUG_SMART_CONTRACT_SOURCE); let generatedOrderEntry = algodex.generateOrder(makerAddr, n, d, min, assetId); console.log("sending trans to: " + lsig.address()); let txn = await this.getPayTxn(algodClient, makerAddr, lsig.address(), algoOrderSize, false); let outerTxns = []; outerTxns.push({ unsignedTxn: txn, senderAcct: makerAccount }); console.log("here3 calling app from logic sig to open order"); let appArgs = []; var enc = new TextEncoder(); appArgs.push(enc.encode("open")); appArgs.push(enc.encode(generatedOrderEntry.slice(59))); appArgs.push(new Uint8Array([constants.ESCROW_CONTRACT_VERSION])); //appArgs.push(algosdk.decodeAddress(makerAddr).publicKey); //console.log("owners bit addr: " + ownersBitAddr); console.log("herezzz_888"); console.log(appArgs.length); let logSigTrans = null; if (!isExistingEscrow) { logSigTrans = await dexInternal.createTransactionFromLogicSig(algodClient, lsig, appId, appArgs, "appOptIn"); outerTxns.push({ unsignedTxn: logSigTrans, lsig: lsig }); } console.log("skipASAOptIn: " + skipASAOptIn); if (!skipASAOptIn) { // asset opt-in transfer let assetOptInTxn = await this.getAssetSendTxn(algodClient, makerAddr, makerAddr, 0, assetId, false); outerTxns.push({ unsignedTxn: assetOptInTxn, senderAcct: makerAccount }); } return outerTxns; }, getPlaceASAEscrowOrderTxns : async function (algodClient, makerAccount, asaOrderSize, price, assetId, appId, isExistingEscrow = false) { console.log("checking assetId type"); assetId = parseInt(assetId+""); const makerAddr = makerAccount.addr; const min = 0; const numAndDenom = algodex.getNumeratorAndDenominatorFromPrice(price); const n = numAndDenom.n; const d = numAndDenom.d; let outerTxns = []; let program = algodex.buildDelegateTemplateFromArgs(min, assetId, n, d, makerAddr, true, constants.ESCROW_CONTRACT_VERSION); let lsig = await algodex.getLsigFromProgramSource(algosdk, algodClient, program); let generatedOrderEntry = algodex.generateOrder(makerAddr, n, d, min, assetId); console.log("address is: " + lsig.address()); // check if the lsig has already opted in let accountInfo = await algodex.getAccountInfo(lsig.address()); let alreadyOptedIn = false; if (accountInfo != null && accountInfo['apps-local-state'] != null && accountInfo['apps-local-state'].length > 0 && accountInfo['apps-local-state'][0].id == appId) { alreadyOptedIn = true; } console.log("alreadyOptedIn: " + alreadyOptedIn); console.log("acct info:" + JSON.stringify(accountInfo)); let params = await algodClient.getTransactionParams().do(); console.log("sending trans to: " + lsig.address()); let assetSendTrans = await this.getAssetSendTxn(algodClient, makerAddr, lsig.address(), asaOrderSize, assetId, false); let payTxn = await this.getPayTxn(algodClient, makerAddr, lsig.address(), constants.MIN_ASA_ESCROW_BALANCE, false); myAlgoWalletUtil.setTransactionFee(payTxn); console.log("typeof: " + typeof payTxn.txId); console.log("the val: " + payTxn.txId); //console.log("confirmed!!"); // create unsigned transaction console.log("here3 calling app from logic sig to open order"); let appArgs = []; var enc = new TextEncoder(); appArgs.push(enc.encode("open")); appArgs.push(enc.encode(generatedOrderEntry.slice(59))); appArgs.push(new Uint8Array([constants.ESCROW_CONTRACT_VERSION])); // add owners address as arg //ownersAddr = "WYWRYK42XADLY3O62N52BOLT27DMPRA3WNBT2OBRT65N6OEZQWD4OSH6PI"; //ownersBitAddr = (algosdk.decodeAddress(ownersAddr)).publicKey; console.log(appArgs.length); let logSigTrans = await dexInternal.createTransactionFromLogicSig(algodClient, lsig, appId, appArgs, "appOptIn"); // create optin transaction // sender and receiver are both the same let sender = lsig.address(); let recipient = sender; // We set revocationTarget to undefined as // This is not a clawback operation let revocationTarget = undefined; // CloseReaminerTo is set to undefined as // we are not closing out an asset let closeRemainderTo = undefined; // We are sending 0 assets let amount = 0; // signing and sending "txn" allows sender to begin accepting asset specified by creator and index let logSigAssetOptInTrans = algosdk.makeAssetTransferTxnWithSuggestedParams(sender, recipient, closeRemainderTo, revocationTarget, amount, undefined, assetId, params); outerTxns.push({ unsignedTxn: payTxn, senderAcct: makerAccount }); outerTxns.push({ unsignedTxn: logSigTrans, lsig: lsig }); outerTxns.push({ unsignedTxn: logSigAssetOptInTrans, lsig: lsig }); outerTxns.push({ unsignedTxn: assetSendTrans, senderAcct: makerAccount }); return outerTxns; }, } module.exports = GenerateTransactions;