diff --git a/src/utils/api/swaps/__utils__/testUtils.ts b/src/utils/api/swaps/__utils__/testUtils.ts index 5bd2ab6..94e631e 100644 --- a/src/utils/api/swaps/__utils__/testUtils.ts +++ b/src/utils/api/swaps/__utils__/testUtils.ts @@ -1 +1,18 @@ +import { TransactionToSign, TransactionToSignType } from '@/models/Transaction'; +import { encodeAddress } from 'algosdk'; + export const dummyContract = `BiAH6AcBBJmH0wcDAGQmASB6zQMKOJBtxFB4ILNYiiRQngRy5XJoFC96k36qJavhHDIEgQISMwAQIxIQMwEQJBIQQAENMgQhBBIzABAkEhAzARAkEhAzAhAjEhBAAG8yBCEEEjMAECQSEDMBECMSEDMCECMSEEAAAQAzAAEiDjMAIDIDEhAzABMyAxIQMwEBIg4zASAyAxIQEDMAESUSEDMAFCgSEDMAFSgSEDMBBygSEDMBCSgSEDMCACgSEDMCBygSEDMCCCEFEhBCAN4zAAEiDjMAIDIDEhAzABMyAxIQMwAVMgMSEDMBEzIDEhAzABElEhAzABIhBhIQMwERJRIQMwESIQYSEDMAFDMBABIQMwEUKBIQMwIHgCCKaxNJ5pPcb9t1+hmc6CDAgBtvAussZRHPAQiO5V1OjRIQMwIAMwEAEhAzAgiBoMIeEhBCAFozAAEiDjMAIDIDEhAzAAkyAxIQMwEBIg4zASAyAxIQMwETMgMSEDMBFTIDEhAQMwAAKBIQMwAHMwEAEhAzAAiB0OgMDxAzARElEhAzAQAzARQSEDMBEiEFEhBD`; +export function expectUserTxn( + txn: TransactionToSign, + from: string, + to: string, + assetIndex?: number, +) { + expect(txn.signer).toBe(undefined); + expect(txn.type).toBe(TransactionToSignType.UserTransaction); + expect(encodeAddress(txn.transaction.from.publicKey)).toBe(from); + expect(encodeAddress(txn.transaction.to.publicKey)).toBe(to); + if (typeof assetIndex !== `undefined`) { + expect(txn.transaction.assetIndex).toBe(assetIndex); + } +} diff --git a/src/utils/api/swaps/createPerformSwapTxns.test.ts b/src/utils/api/swaps/createPerformSwapTxns.test.ts index d30e129..746dadf 100644 --- a/src/utils/api/swaps/createPerformSwapTxns.test.ts +++ b/src/utils/api/swaps/createPerformSwapTxns.test.ts @@ -7,10 +7,39 @@ import { generateAccount } from 'algosdk'; import { LogicSigAccount, encodeAddress } from 'algosdk'; import getLogicSign from '../accounts/getLogicSignature'; import { TransactionToSign, TransactionToSignType } from '@/models/Transaction'; -import { dummyContract } from './__utils__/testUtils'; +import { dummyContract, expectUserTxn } from './__utils__/testUtils'; import { SwapConfiguration, SwapType } from '@/models/Swap'; import { GET_INCENTIVE_FEE, INCENTIVE_WALLET } from '@/common/constants'; +export function expectIncentivePayment( + txn: TransactionToSign, + from: string, + version: string, +) { + expect(txn.signer).toBe(undefined); + expect(txn.type).toBe(TransactionToSignType.UserFeeTransaction); + expect(txn.transaction.type).toBe(`pay`); + expect(encodeAddress(txn.transaction.to.publicKey)).toBe(INCENTIVE_WALLET); + expect(encodeAddress(txn.transaction.from.publicKey)).toBe(from); + expect(txn.transaction.amount).toBe(GET_INCENTIVE_FEE(version)); +} +function expectOfferedAsaXferTxn( + txn: TransactionToSign, + logicSig: LogicSigAccount, + to: string, + amount: number, + assetIndex: number, +) { + expect(txn.signer).toBe(logicSig); + expect(txn.type).toBe(TransactionToSignType.LsigTransaction); + expect(encodeAddress(txn.transaction.from.publicKey)).toBe( + logicSig.address(), + ); + expect(encodeAddress(txn.transaction.to.publicKey)).toBe(to); + expect(txn.transaction.assetIndex).toBe(assetIndex); + expect(txn.transaction.amount).toBe(amount); +} + describe(`createPerformSwapTxns`, () => { it(`generates asa to asa txns correctly`, async () => { const dummyAccount = generateAccount(); @@ -46,51 +75,102 @@ describe(`createPerformSwapTxns`, () => { dummyLogicSig, dummySwapConfiguration, ); + // Ensure length is correct + expect(txns.length).toBe(3); - // First txn - expect((txns[0] as TransactionToSign).signer).toBe(dummyLogicSig); - expect((txns[0] as TransactionToSign).type).toBe( - TransactionToSignType.LsigTransaction, + // First txn - Offered ASA + expectOfferedAsaXferTxn( + txns[0], + dummyLogicSig, + dummyAccount.addr, + 0, + expectedAssetIndex, ); - expect( - encodeAddress((txns[0] as TransactionToSign).transaction.from.publicKey), - ).toBe(dummyLogicSig.address()); - expect( - encodeAddress((txns[0] as TransactionToSign).transaction.to.publicKey), - ).toBe(dummyAccount.addr); - expect((txns[0] as TransactionToSign).transaction.assetIndex).toBe( + + // Second txn - Requested ASA + expectUserTxn( + txns[1], + dummyAccount.addr, + dummyAccount.addr, expectedAssetIndex, ); - expect((txns[0] as TransactionToSign).transaction.amount).toBe(0); - // Second txn - expect((txns[1] as TransactionToSign).signer).toBe(undefined); - expect((txns[1] as TransactionToSign).type).toBe( - TransactionToSignType.UserTransaction, + // Third txn - Incentive payment + expectIncentivePayment(txns[2], dummyAccount.addr, dummySwapVersion); + }); + it(`generates Multi ASA to ALGO txns correctly`, async () => { + const dummyAccount = generateAccount(); + const dummyLogicSig = getLogicSign(dummyContract) as LogicSigAccount; + const expectedAssetIndex = 123; + const expectedSecondAssetIndex = 1234; + const dummyOfferingAsset = { + index: expectedAssetIndex, + creator: `test`, + name: `test`, + imageUrl: `test`, + decimals: 6, + unitName: `test`, + amount: 1, + frozen: false, + offeringAmount: 0, + requestingAmount: 0, + } as Asset; + const secondDummyOfferingAsset = { + index: expectedSecondAssetIndex, + creator: `test`, + name: `test`, + imageUrl: `test`, + decimals: 6, + unitName: `test`, + amount: 10, + frozen: false, + offeringAmount: 0, + requestingAmount: 0, + } as Asset; + const dummySwapVersion = `0.0.2`; + const dummySwapConfiguration = { + version: dummySwapVersion, + type: SwapType.MULTI_ASA_TO_ALGO, + offering: [dummyOfferingAsset, secondDummyOfferingAsset], + requesting: [dummyOfferingAsset], + creator: dummyAccount.addr, + proxy: dummyLogicSig.address(), + escrow: dummyLogicSig.address(), + contract: dummyContract, + } as SwapConfiguration; + + const txns = await createPerformSwapTxns( + ChainType.TestNet, + dummyAccount.addr, + dummyLogicSig, + dummySwapConfiguration, ); - expect( - encodeAddress((txns[1] as TransactionToSign).transaction.from.publicKey), - ).toBe(dummyAccount.addr); - expect( - encodeAddress((txns[1] as TransactionToSign).transaction.to.publicKey), - ).toBe(dummyAccount.addr); - expect((txns[1] as TransactionToSign).transaction.assetIndex).toBe( + + // Ensure length is correct + expect(txns.length).toBe(4); + + // First txn - Incentive Fee + expectIncentivePayment(txns[0], dummyAccount.addr, dummySwapVersion); + + // Second txn - Request Algo Payment + expectUserTxn(txns[1], dummyAccount.addr, dummyAccount.addr); + + // Third txn - Assets Transfer + expectOfferedAsaXferTxn( + txns[2], + dummyLogicSig, + dummyAccount.addr, + 0, expectedAssetIndex, ); - // Third txn - expect((txns[2] as TransactionToSign).signer).toBe(undefined); - expect((txns[2] as TransactionToSign).type).toBe( - TransactionToSignType.UserFeeTransaction, - ); - expect( - encodeAddress((txns[2] as TransactionToSign).transaction.from.publicKey), - ).toBe(dummyAccount.addr); - expect( - encodeAddress((txns[2] as TransactionToSign).transaction.to.publicKey), - ).toBe(INCENTIVE_WALLET); - expect((txns[2] as TransactionToSign).transaction.amount).toBe( - GET_INCENTIVE_FEE(dummySwapVersion), + // Fourth txn - Assets Transfer + expectOfferedAsaXferTxn( + txns[3], + dummyLogicSig, + dummyAccount.addr, + 0, + expectedSecondAssetIndex, ); }); }); diff --git a/src/utils/api/swaps/createSwapDepositTxns.test.ts b/src/utils/api/swaps/createSwapDepositTxns.test.ts new file mode 100644 index 0000000..1862c19 --- /dev/null +++ b/src/utils/api/swaps/createSwapDepositTxns.test.ts @@ -0,0 +1,73 @@ +import { Asset } from '@/models/Asset'; +import { ChainType } from '@/models/Chain'; +import createSwapDepositTxns from './createSwapDepositTxns'; + +import { encodeAddress, generateAccount, LogicSigAccount } from 'algosdk'; +import getLogicSign from '../accounts/getLogicSignature'; +import { TransactionToSign, TransactionToSignType } from '@/models/Transaction'; +import { dummyContract, expectUserTxn } from './__utils__/testUtils'; +import { ASA_TO_ASA_FUNDING_FEE } from '@/common/constants'; +import getAccountInfo from '@/utils/api/accounts/getAccountInfo'; + +async function expectUserFeeTransaction( + txn: TransactionToSign, + from: string, + to: string, + fundingFee: number, +) { + const escrowAccountInfo = await getAccountInfo(ChainType.TestNet, to); + + const escrowBalance = + escrowAccountInfo && `account` in escrowAccountInfo + ? escrowAccountInfo.account.amount + : 0; + expect(txn.signer).toBe(undefined); + expect(txn.type).toBe(TransactionToSignType.UserFeeTransaction); + expect(encodeAddress(txn.transaction.from.publicKey)).toBe(from); + expect(encodeAddress(txn.transaction.to.publicKey)).toBe(to); + expect(txn.transaction.amount).toBe(Math.abs(fundingFee - escrowBalance)); +} + +describe(`createSwapDepositTxns`, () => { + it(`generates swap deposit txns`, async () => { + const dummyAccount = generateAccount(); + const dummyLogicSig = getLogicSign(dummyContract) as LogicSigAccount; + const expectedAssetIndex = 123; + const dummyAssets: Asset[] = [ + { + index: expectedAssetIndex, + creator: `test`, + name: `test`, + imageUrl: `test`, + decimals: 6, + unitName: `test`, + amount: 1, + frozen: false, + offeringAmount: 0, + requestingAmount: 0, + }, + ]; + const txns = await createSwapDepositTxns( + ChainType.TestNet, + dummyAccount.addr, + dummyLogicSig, + dummyAssets, + ASA_TO_ASA_FUNDING_FEE + 20, + ); + // Ensure length is correct + expect(txns.length).toBe(2); + await expectUserFeeTransaction( + txns[0], + dummyAccount.addr, + dummyLogicSig.address(), + ASA_TO_ASA_FUNDING_FEE + 20, + ); + + expectUserTxn( + txns[1], + dummyAccount.addr, + dummyLogicSig.address(), + dummyAssets[0].index, + ); + }); +});