diff --git a/package.json b/package.json index 3584c0b..49b5d0d 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,8 @@ "typescript": "^5.3.3" }, "dependencies": { - "@stellar/stellar-sdk": "^12.1.0", + "@stellar/stellar-sdk": "^13.0.0", "dotenv": "^16.4.5" - } + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/public/testnet.contracts.json b/public/testnet.contracts.json index 3c91780..5ec0f1d 100644 --- a/public/testnet.contracts.json +++ b/public/testnet.contracts.json @@ -1,22 +1,12 @@ { "ids": { - "soroswap_adapter": "CDG7MKZQLLFJLDTZCYM4WJEFAJ5JMREJEVTBQRCWPRAPAOOWUY2VE5P4", - "aggregator": "CBO5CJWTH66QKYDCHBLC3IC44ZNEWDLDPRBJVPWH4CATR5SBERVTHZHF", - "phoenix_factory": "CC2YZDV2T6PDX7ZY5JC4JNBFLMKAKYCKPJ6DJ6AFTDPJH2WS3EYI3HHO", - "phoenix_multihop": "CBMFX6IFCIH4CRBXGIKCOIUJO5NBCPDZNIQJXF3F25M2JNRG2FXJXC4N", - "phoenix_adapter": "CC6GXBKZ4CYGMKH77RP5L7FMO45NHWVQIRVZE5VXGH7N6R4DQ7EPYT6W", - "deployer": "CDC7UZ5N4VL6O37YWS4XL5HLI3EMGMIRKDUX5C2Q6GUS7V4KL265SZED" + "deployer": "CBQZZ5YVQOGQ55WP5ILJX5LVR6FRUYQKLQDOMAF3A7EAE4SZ4MDWXE6A", + "soroswap_adapter": "CDXH5JQNZEHKDQONCA5TLAGPYOKYY4KXAOSMRMW7CPIPFP2IE5ZOB7K4", + "aggregator": "CB6E6HCE75WMRHFKE35SAGFINHXGUJ5OHD55JL74SYE2Y27IZGEWPAGP" }, "hashes": { - "soroswap_adapter": "6e9897a0d084a6b0fb13bc719263ad808718b4417c12b6b314c1536297a46f71", - "aggregator": "de3c2cba30268f60caa470e6e3f3c14f34d16af0a3c19e01b728b3bd55a71991", - "phoenix_factory": "8bbcb24a54e5c1d0f8162cbea128d4b982886f52e58417a51d6ebab3d3744789", - "phoenix_multihop": "dded64a0535b44c856f93e860f3ef42f89d7b9479b7af341c34c3e81f48af550", - "phoenix_token": "52d85a1fc0df526ff84fcf9c45e810b1ed9780aaf2094c2d22dfd487f01ee644", - "phoenix_pool": "79d05d9245407f9447b4404646650bd20b7544a74a2da75a2bb234fa013b4b3f", - "phoenix_stake": "c678ba7a2a19c76f87cc827e45e103eab0febc8f269e4f31d22c8a5f095e7d8e", - "phoenix_adapter": "060d10e53779b685f6b2525fc6b1fb7f491601775a6f17fab319ac15e1e9c509", - "deployer": "c14be64defd72d2c01c78fc46206934a67844692ff17c974c5fd18a5f775cd7e", - "phoenix_stable": "ad0bdd69de996ba01b65c9d3c4190929d81b3a3c9fe2c75c19b21d558cab8bec" + "deployer": "6c83329a13014736589fbfd2af4ba99c080eab87ffc2cdf5d2df20b1c31e7448", + "soroswap_adapter": "b6b8bae178815d1a99acd28ab5898b1b753e7f460f6fad7382c9f371fcf65fbc", + "aggregator": "a4b62ddd5deca2ae411a5b0de42db075fde1c51e734180005d59ba04097cf85f" } -} \ No newline at end of file +} diff --git a/src/deploy.ts b/src/deploy.ts index 4524e7d..7f31e63 100644 --- a/src/deploy.ts +++ b/src/deploy.ts @@ -3,7 +3,12 @@ import { randomBytes } from 'crypto'; import { phoenixSetup } from './protocols/phoenix/phoenix_setup.js'; import { updateAdapters } from './update_protocols.js'; import { AddressBook } from './utils/address_book.js'; -import { airdropAccount, deployContract, installContract, invokeContract } from './utils/contract.js'; +import { + airdropAccount, + deployContract, + installContract, + invokeContract, +} from './utils/contract.js'; import { config } from './utils/env_config.js'; import { TokensBook } from './utils/tokens_book.js'; @@ -20,23 +25,23 @@ export async function deployAndInitAggregator(addressBook: AddressBook) { console.log('-------------------------------------------------------'); console.log('Deploying Adapters using the deployer'); console.log('-------------------------------------------------------'); - console.log("** Soroswap Adapter"); + console.log('** Soroswap Adapter'); await installContract('soroswap_adapter', addressBook, loadedConfig.admin); - + const routerAddress = soroswapAddressBook.getContractId('router'); const initArgs = xdr.ScVal.scvVec([ - xdr.ScVal.scvString("soroswap"), // protocol_id as ScVal string - new Address(routerAddress).toScVal() // protocol_address as ScVal address + xdr.ScVal.scvString('soroswap'), // protocol_id as ScVal string + new Address(routerAddress).toScVal(), // protocol_address as ScVal address ]); const soroswapAdapterDeployParams: xdr.ScVal[] = [ new Address(loadedConfig.admin.publicKey()).toScVal(), - nativeToScVal(Buffer.from(addressBook.getWasmHash("soroswap_adapter"), "hex")), + nativeToScVal(Buffer.from(addressBook.getWasmHash('soroswap_adapter'), 'hex')), nativeToScVal(randomBytes(32)), xdr.ScVal.scvSymbol('initialize'), - initArgs - ] + initArgs, + ]; const response = await invokeContract( 'deployer', @@ -46,54 +51,56 @@ export async function deployAndInitAggregator(addressBook: AddressBook) { loadedConfig.admin ); - const soroswapAdapterAddress = scValToNative(response.returnValue)[0] + const soroswapAdapterAddress = scValToNative(response.returnValue)[0]; console.log('🚀 « soroswapAdapterAddress:', soroswapAdapterAddress); // SAVE ADDRES IN ADDRESS BOOK - addressBook.setContractId("soroswap_adapter", soroswapAdapterAddress) + addressBook.setContractId('soroswap_adapter', soroswapAdapterAddress); console.log('-------------------------------------------------------'); console.log('Deploying Aggregator'); console.log('-------------------------------------------------------'); console.log('Installing Aggregator Contract'); await installContract('aggregator', addressBook, loadedConfig.admin); - + const adaptersVec = [ { - protocol_id: "soroswap", + protocol_id: 'soroswap', address: new Address(addressBook.getContractId('soroswap_adapter')), - paused: false + paused: false, }, ]; - const adaptersVecScVal = xdr.ScVal.scvVec(adaptersVec.map((adapter) => { - return xdr.ScVal.scvMap([ - new xdr.ScMapEntry({ - key: xdr.ScVal.scvSymbol('address'), - val: adapter.address.toScVal(), - }), - new xdr.ScMapEntry({ - key: xdr.ScVal.scvSymbol('paused'), - val: nativeToScVal(adapter.paused), - }), - new xdr.ScMapEntry({ - key: xdr.ScVal.scvSymbol('protocol_id'), - val: xdr.ScVal.scvString(adapter.protocol_id), - }), - ]); - })); + const adaptersVecScVal = xdr.ScVal.scvVec( + adaptersVec.map((adapter) => { + return xdr.ScVal.scvMap([ + new xdr.ScMapEntry({ + key: xdr.ScVal.scvSymbol('address'), + val: adapter.address.toScVal(), + }), + new xdr.ScMapEntry({ + key: xdr.ScVal.scvSymbol('paused'), + val: nativeToScVal(adapter.paused), + }), + new xdr.ScMapEntry({ + key: xdr.ScVal.scvSymbol('protocol_id'), + val: xdr.ScVal.scvString(adapter.protocol_id), + }), + ]); + }) + ); const initAggregatorArgs = xdr.ScVal.scvVec([ new Address(loadedConfig.admin.publicKey()).toScVal(), - adaptersVecScVal + adaptersVecScVal, ]); const soroswapAggregatorDeployParams: xdr.ScVal[] = [ new Address(loadedConfig.admin.publicKey()).toScVal(), - nativeToScVal(Buffer.from(addressBook.getWasmHash("aggregator"), "hex")), + nativeToScVal(Buffer.from(addressBook.getWasmHash('aggregator'), 'hex')), nativeToScVal(randomBytes(32)), xdr.ScVal.scvSymbol('initialize'), - initAggregatorArgs - ] + initAggregatorArgs, + ]; const response_aggregator = await invokeContract( 'deployer', @@ -103,22 +110,22 @@ export async function deployAndInitAggregator(addressBook: AddressBook) { loadedConfig.admin ); - const soroswapAggregatorAddress = scValToNative(response_aggregator.returnValue)[0] + const soroswapAggregatorAddress = scValToNative(response_aggregator.returnValue)[0]; console.log('🚀 « soroswapAggregatorAddress:', soroswapAggregatorAddress); - addressBook.setContractId("aggregator", soroswapAggregatorAddress) + addressBook.setContractId('aggregator', soroswapAggregatorAddress); - console.log("Aggregator initialized") + console.log('Aggregator initialized'); - if (network != 'mainnet') { - console.log("Setting up Phoenix protocol") - // mocks - await phoenixSetup(loadedConfig, addressBook); - console.log("Updating adapters on aggregator.. adding Phoenix") - await updateAdapters(addressBook); - } + // if (network != 'mainnet') { + // console.log("Setting up Phoenix protocol") + // // mocks + // await phoenixSetup(loadedConfig, addressBook); + // console.log("Updating adapters on aggregator.. adding Phoenix") + // await updateAdapters(addressBook); + // } // TODO: IF MAINNET, UPDATE PHOENIX ADAPTERS WITH MAINNET DEPLOYMENT ADDRESS - console.log("Aggregator setup complete") + console.log('Aggregator setup complete'); } const network = process.argv[2]; @@ -129,9 +136,7 @@ const soroswapAddressBook = AddressBook.loadFromFile( network, `../../protocols/soroswap/${soroswapDir}` ); -const soroswapTokensBook = TokensBook.loadFromFile( - `./protocols/soroswap/${soroswapDir}` -); +const soroswapTokensBook = TokensBook.loadFromFile(`./protocols/soroswap/${soroswapDir}`); const loadedConfig = config(network); diff --git a/src/manual_testing/utils.ts b/src/manual_testing/utils.ts index 0bef37e..8a7ca65 100644 --- a/src/manual_testing/utils.ts +++ b/src/manual_testing/utils.ts @@ -1,107 +1,153 @@ -import { invokeContract, invokeCustomContract } from "../utils/contract.js"; -import { - Address, - Asset, - BASE_FEE, - Horizon, - Keypair, - nativeToScVal, - Operation, - scValToNative, +import { invokeContract, invokeCustomContract } from '../utils/contract.js'; +import { + Address, + Asset, + BASE_FEE, + Horizon, + Keypair, + nativeToScVal, + Operation, + scValToNative, TransactionBuilder, - xdr -} from "@stellar/stellar-sdk"; -import type { - Option, -} from "@stellar/stellar-sdk/contract"; -import { deployStellarAsset } from "../utils/contract.js"; -import { getCurrentTimePlusOneHour, signWithKeypair } from "../utils/tx.js"; + xdr, +} from '@stellar/stellar-sdk'; +import type { Option } from '@stellar/stellar-sdk/contract'; +import { deployStellarAsset } from '../utils/contract.js'; +import { getCurrentTimePlusOneHour, signWithKeypair } from '../utils/tx.js'; import * as PhoenixFactoryContract from '../protocols/phoenix/bindgins/factory_bindings.js'; import { AddressBook } from '../utils/address_book.js'; -import { config } from "../utils/env_config.js"; - +import { config } from '../utils/env_config.js'; const network = process.argv[2]; const addressBook = AddressBook.loadFromFile(network); const loadedConfig = config(network); const name_parts = [ - "zi", "ay", "vo", "ak", "rd", "pi", "nv", "ku", "or", "fx", - "ba", "un", "ve", "uy", "pr", "ot", "ml", "kr", "ix", "gx", - "de", "xi", "to", "iv", "ra", "ne", "lz", "ke", "am", "fz", - "cy", "ax", "ui", "ez", "rg", "pe", "nl", "lo", "ib", "jh", - "gu", "ep", "ww", "tv", "su", "rx", "nu", "ox", "kx", + 'zi', + 'ay', + 'vo', + 'ak', + 'rd', + 'pi', + 'nv', + 'ku', + 'or', + 'fx', + 'ba', + 'un', + 've', + 'uy', + 'pr', + 'ot', + 'ml', + 'kr', + 'ix', + 'gx', + 'de', + 'xi', + 'to', + 'iv', + 'ra', + 'ne', + 'lz', + 'ke', + 'am', + 'fz', + 'cy', + 'ax', + 'ui', + 'ez', + 'rg', + 'pe', + 'nl', + 'lo', + 'ib', + 'jh', + 'gu', + 'ep', + 'ww', + 'tv', + 'su', + 'rx', + 'nu', + 'ox', + 'kx', ]; const generateRandomCode = () => { const part1 = name_parts[Math.floor(Math.random() * name_parts.length)]; const part2 = name_parts[Math.floor(Math.random() * name_parts.length)]; - const formatCode = (part1+part2).toUpperCase().substring(0, 4); - return formatCode; -} + const formatCode = (part1 + part2).toUpperCase().substring(0, 4); + return formatCode; +}; const generateRandomAsset = () => { const asset = new Asset(generateRandomCode(), loadedConfig.tokenAdmin.publicKey()); return asset; -} +}; const fetchAssetBalance = async (asset: Asset, account: Keypair) => { let balance; try { balance = await invokeCustomContract( asset.contractId(loadedConfig.passphrase), - "balance", + 'balance', [new Address(account.publicKey()).toScVal()], account, true ); - } catch (error:any) { - if(error.toString().includes('#13')){ - console.log(`🔴 Missing ${asset.code} trustline in ${account.publicKey}`) + } catch (error: any) { + if (error.toString().includes('#13')) { + console.log(`🔴 Missing ${asset.code} trustline in ${account.publicKey}`); return undefined; } else { - throw new Error("🔴 Can't set trustline", error) + throw new Error("🔴 Can't set trustline", error); } - } - if(balance != undefined){ + } + if (balance != undefined) { const parsedBalance: bigint = scValToNative(balance.result.retval); //return (parsedBalance / BigInt(10000000)).toString(); return parsedBalance; } -} +}; const fetchContractBalance = async (contractID: string, account: Keypair) => { let balance; try { balance = await invokeCustomContract( contractID, - "balance", + 'balance', [new Address(account.publicKey()).toScVal()], account, true ); - } catch (error:any) { - if(error.toString().includes('MissingValue')){ - console.log(`🔴 contract not deployed or balance not found`) + } catch (error: any) { + if (error.toString().includes('MissingValue')) { + console.log(`🔴 contract not deployed or balance not found`); return 0; } else { - throw new Error("🔴 Can't set trustline", error) + throw new Error("🔴 Can't set trustline", error); } - } - if(balance != undefined){ + } + if (balance != undefined) { const parsedBalance: bigint = scValToNative(balance.result.retval); //return parsedBalance / BigInt(10000000); return parsedBalance; } -} - -const setTrustline = async (asset: Asset, account: Keypair, rpc: Horizon.Server, limit?: string,) => { - console.log(`🟡 Setting trustline for ${asset.code} with account ${account.publicKey()}`) +}; + +const setTrustline = async ( + asset: Asset, + account: Keypair, + rpc: Horizon.Server, + limit?: string +) => { + console.log(`🟡 Setting trustline for ${asset.code} with account ${account.publicKey()}`); const loadedAccount: Horizon.AccountResponse = await rpc.loadAccount(account.publicKey()); - const operation = Operation.changeTrust({ + const operation = Operation.changeTrust({ asset: asset, - limit: limit || undefined - }) + limit: limit || undefined, + }); const transaction = new TransactionBuilder(loadedAccount, { fee: Number(BASE_FEE).toString(), timebounds: { minTime: 0, maxTime: 0 }, @@ -112,13 +158,20 @@ const setTrustline = async (asset: Asset, account: Keypair, rpc: Horizon.Server, .build(); await transaction.sign(account); const transactionResult = await rpc.submitTransaction(transaction); - if(transactionResult.successful) { - console.log(`🟢 Trustline for ${asset.code} set`) + if (transactionResult.successful) { + console.log(`🟢 Trustline for ${asset.code} set`); } return transactionResult; -} - -const mintToken = async (destination: string, asset: Asset, amount: string, source: Keypair, rpc: Horizon.Server, passphrase: string,) => { +}; + +const mintToken = async ( + destination: string, + asset: Asset, + amount: string, + source: Keypair, + rpc: Horizon.Server, + passphrase: string +) => { console.log('-------------------------------------------------------'); console.log(`Minting ${amount} ${asset.code} to ${destination}`); console.log('-------------------------------------------------------'); @@ -126,105 +179,118 @@ const mintToken = async (destination: string, asset: Asset, amount: string, sour const operation = Operation.payment({ destination: destination, asset: asset, - amount: amount - }) + amount: amount, + }); const transaction = new TransactionBuilder(loadedSource, { fee: BASE_FEE, - networkPassphrase: passphrase + networkPassphrase: passphrase, }) .addOperation(operation) .setTimeout(300) .build(); - await transaction.sign(source); + await transaction.sign(source); const transactionResult = await rpc.submitTransaction(transaction); - if(transactionResult.successful) { - console.log(`🟢 Payment of ${amount} ${asset.code} to ${destination} successful`) + if (transactionResult.successful) { + console.log(`🟢 Payment of ${amount} ${asset.code} to ${destination} successful`); } return transactionResult; -} +}; -const deployAndMint = async (asset: Asset, user: Keypair, amount:string)=>{ +const deployAndMint = async (asset: Asset, user: Keypair, amount: string) => { try { console.log(`🟡 Deploying contract for ${asset.code}`); await deployStellarAsset(asset, loadedConfig.tokenAdmin); - - } catch (error:any) { - if(error.toString().includes('ExistingValue')){ + } catch (error: any) { + if (error.toString().includes('ExistingValue')) { console.log(`🟢 Contract for ${asset.code} already exists`); } else { console.error(error); } - }; + } const userHasTrustline = await fetchAssetBalance(asset, user); - if(!userHasTrustline){ + if (!userHasTrustline) { console.log(`Missing trustline for ${asset.code} in ${user.publicKey()}`); - try{ + try { await setTrustline(asset, user, loadedConfig.horizonRpc); - } catch(e:any){ + } catch (e: any) { console.error(e); - } + } } else { console.log(`🟢 Trustline for ${asset.code} already exists in ${user.publicKey()}`); console.log(`🟢 Balance: ${userHasTrustline}`); } - - await mintToken(user.publicKey(), asset, amount, loadedConfig.tokenAdmin, loadedConfig.horizonRpc, loadedConfig.passphrase); + await mintToken( + user.publicKey(), + asset, + amount, + loadedConfig.tokenAdmin, + loadedConfig.horizonRpc, + loadedConfig.passphrase + ); const newUserBalance = await fetchAssetBalance(asset, user); console.log(`🟢 Test user balance of ${asset.code}: ${newUserBalance}`); -} +}; const formatAmmount = (amount: number) => { //const formattedAmmount = BigInt(amount * 10000000).toString(); //return formattedAmmount - return amount -} + return amount; +}; interface CreatePoolParams { - contractID_A: string, - contractID_B: string, - user: Keypair, - amount_A: number, - amount_B: number, + contractID_A: string; + contractID_B: string; + user: Keypair; + amount_A: number; + amount_B: number; } -const create_soroswap_liquidity_pool = async ( - router:string, - poolParams: CreatePoolParams, - ) => { - console.log('🟡 Creating soroswap liquidity pool') +const create_soroswap_liquidity_pool = async (router: string, poolParams: CreatePoolParams) => { + console.log('🟡 Creating soroswap liquidity pool'); const parsedPoolParams = { ...poolParams, - user: poolParams.user.publicKey() - } - console.log('🔎 poolParams:', parsedPoolParams); + user: poolParams.user.publicKey(), + }; + console.log('🔎 poolParams:', parsedPoolParams); const addSoroswapLiquidityParams: xdr.ScVal[] = [ new Address(poolParams.contractID_A).toScVal(), new Address(poolParams.contractID_B).toScVal(), - nativeToScVal(formatAmmount(poolParams.amount_A), { type: "i128" }), - nativeToScVal(formatAmmount(poolParams.amount_B), { type: "i128" }), - nativeToScVal(0, { type: "i128" }), - nativeToScVal(0, { type: "i128" }), + nativeToScVal(formatAmmount(poolParams.amount_A), { type: 'i128' }), + nativeToScVal(formatAmmount(poolParams.amount_B), { type: 'i128' }), + nativeToScVal(0, { type: 'i128' }), + nativeToScVal(0, { type: 'i128' }), new Address(poolParams.user.publicKey()).toScVal(), - nativeToScVal(getCurrentTimePlusOneHour(), { type: "u64" }), + nativeToScVal(getCurrentTimePlusOneHour(), { type: 'u64' }), ]; - const soroswapInvoke = await invokeCustomContract(router, 'add_liquidity', addSoroswapLiquidityParams, poolParams.user) - if(soroswapInvoke.status === 'SUCCESS'){ - console.log('🟢 Soroswap pool created successfully') + const soroswapInvoke = await invokeCustomContract( + router, + 'add_liquidity', + addSoroswapLiquidityParams, + poolParams.user + ); + if (soroswapInvoke.status === 'SUCCESS') { + console.log('🟢 Soroswap pool created successfully'); } else { console.log('🔎 soroswapInvoke:', soroswapInvoke); } return soroswapInvoke; -} +}; export interface SoroswapPool { - address: string, - asset_0: string, - asset_0_balance: number, - asset_1: string, - asset_1_balance: number, + address: string; + asset_0: string; + asset_0_balance: number; + asset_1: string; + asset_1_balance: number; } -const createSoroswapLP = async (addresses: string[], amount_A: number, amount_B: number, router: string, user: Keypair)=>{ +const createSoroswapLP = async ( + addresses: string[], + amount_A: number, + amount_B: number, + router: string, + user: Keypair +) => { let soroswapPools = []; const poolParams = { contractID_A: addresses[0], @@ -241,36 +307,48 @@ const createSoroswapLP = async (addresses: string[], amount_A: number, amount_B: ]; console.log('🟡 Fetching Soroswap pair address'); try { - const soroswapPool = await invokeCustomContract(router, 'router_pair_for', fetchPoolParams, user, true); + const soroswapPool = await invokeCustomContract( + router, + 'router_pair_for', + fetchPoolParams, + user, + true + ); const soroswapPoolCID = scValToNative(soroswapPool.result.retval); console.log('🟡 Fetching liquidity pool balance'); - const soroswapPoolBalance = await invokeCustomContract(soroswapPoolCID, 'get_reserves', [], user, true); + const soroswapPoolBalance = await invokeCustomContract( + soroswapPoolCID, + 'get_reserves', + [], + user, + true + ); const parsedPoolBalance = scValToNative(soroswapPoolBalance.result.retval); console.log(`🟢 Soroswap pair balance: ${scValToNative(soroswapPoolBalance.result.retval)}`); - console.log('🟢 Soroswap pair address:', soroswapPoolCID) + console.log('🟢 Soroswap pair address:', soroswapPoolCID); const soroswap_pool_result = { address: soroswapPoolCID, asset_0: addresses[0], asset_0_balance: parsedPoolBalance[0], asset_1: addresses[1], asset_1_balance: parsedPoolBalance[1], - } + }; return soroswap_pool_result; } catch (e) { - console.error(e) + console.error(e); } -} +}; const create_phoenix_pool_transaction = async ( - factory_contract: PhoenixFactoryContract.Client, - phoenixAdmin: Keypair, - aggregatorAdmin:Keypair, - assetA:Asset, - assetB:Asset)=>{ - - let firstAsset: Asset = assetA - let secondAsset: Asset = assetB - /* if(assetA.contractId(loadedConfig.passphrase) > assetB.contractId(loadedConfig.passphrase)){ + factory_contract: PhoenixFactoryContract.Client, + phoenixAdmin: Keypair, + aggregatorAdmin: Keypair, + assetA: Asset, + assetB: Asset +) => { + let firstAsset: Asset = assetA; + let secondAsset: Asset = assetB; + /* if(assetA.contractId(loadedConfig.passphrase) > assetB.contractId(loadedConfig.passphrase)){ firstAsset = assetB; secondAsset = assetA; } else { @@ -291,12 +369,12 @@ const create_phoenix_pool_transaction = async ( manager: aggregatorAdmin.publicKey(), max_complexity: 10, min_bond: 6n, - min_reward: 3n + min_reward: 3n, }, token_init_info: { token_a: secondAsset.contractId(loadedConfig.passphrase), token_b: firstAsset.contractId(loadedConfig.passphrase), - } + }, }, share_token_name: `TOKEN-LP-${firstAsset.code}/${secondAsset.code}`, share_token_symbol: `PLP-${firstAsset.code}/${secondAsset.code}`, @@ -304,120 +382,157 @@ const create_phoenix_pool_transaction = async ( amp: 0n, default_slippage_bps: 100n, max_allowed_fee_bps: 2000n, - }); - return await tx.signAndSend().catch((error:any)=>{ - throw new Error(error) - }) -} - -const create_phoenix_liquidity_pool = async (phoenixAdmin: Keypair, aggregatorAdmin:Keypair, assetA:Asset, assetB:Asset)=>{ + return await tx.signAndSend().catch((error: any) => { + throw new Error(error); + }); +}; + +const create_phoenix_liquidity_pool = async ( + phoenixAdmin: Keypair, + aggregatorAdmin: Keypair, + assetA: Asset, + assetB: Asset +) => { const factory_contract = new PhoenixFactoryContract.Client({ publicKey: phoenixAdmin.publicKey()!, - contractId: addressBook.getContractId("phoenix_factory"), + contractId: addressBook.getContractId('phoenix_factory'), networkPassphrase: loadedConfig.passphrase, - rpcUrl: "https://soroban-testnet.stellar.org/", - signTransaction: (tx: string) => signWithKeypair(tx, loadedConfig.passphrase, phoenixAdmin), + rpcUrl: 'https://soroban-testnet.stellar.org/', + signTransaction: async (tx: string) => { + const signedTxXdr = await signWithKeypair(tx, loadedConfig.passphrase, phoenixAdmin); + return { signedTxXdr }; + }, }); let needRetry: boolean = false; try { - console.log('creating phoenix pool transaction') - await create_phoenix_pool_transaction(factory_contract, phoenixAdmin, aggregatorAdmin, assetA, assetB) - } catch (error:any) { - if(error.toString().includes('ExistingValue')){ - console.log('Pool already exists') + console.log('creating phoenix pool transaction'); + await create_phoenix_pool_transaction( + factory_contract, + phoenixAdmin, + aggregatorAdmin, + assetA, + assetB + ); + } catch (error: any) { + if (error.toString().includes('ExistingValue')) { + console.log('Pool already exists'); } - if(error.toString().includes('#5')){ - console.log('Token A bigger than token B, retrying with token B as first asset') + if (error.toString().includes('#5')) { + console.log('Token A bigger than token B, retrying with token B as first asset'); needRetry = true; - } - else { - console.log('🔴 error:', error) + } else { + console.log('🔴 error:', error); } } - if(needRetry){ + if (needRetry) { try { - await create_phoenix_pool_transaction(factory_contract, phoenixAdmin, aggregatorAdmin, assetB, assetA) - } catch (error:any) { - if(error.toString().includes('ExistingValue')){ - console.log('🟡 Pool already exists') + await create_phoenix_pool_transaction( + factory_contract, + phoenixAdmin, + aggregatorAdmin, + assetB, + assetA + ); + } catch (error: any) { + if (error.toString().includes('ExistingValue')) { + console.log('🟡 Pool already exists'); } else { - console.log('🔴 error:', error) + console.log('🔴 error:', error); } } } - - console.log("Getting pair address") + + console.log('Getting pair address'); const getPairParams: xdr.ScVal[] = [ new Address(assetA.contractId(loadedConfig.passphrase)).toScVal(), - new Address(assetB.contractId(loadedConfig.passphrase)).toScVal() - ] - const pairAddress = await invokeContract('phoenix_factory', addressBook, 'query_for_pool_by_token_pair', getPairParams, phoenixAdmin, true) + new Address(assetB.contractId(loadedConfig.passphrase)).toScVal(), + ]; + const pairAddress = await invokeContract( + 'phoenix_factory', + addressBook, + 'query_for_pool_by_token_pair', + getPairParams, + phoenixAdmin, + true + ); console.log('🚀 « pairAddress:', scValToNative(pairAddress.result.retval)); - if(pairAddress.result){ + if (pairAddress.result) { return scValToNative(pairAddress.result.retval); } else { return pairAddress; } -} +}; -const provide_phoenix_liquidity = async (phoenixAdmin: Keypair, pairAddress:string, amount_A: number, amount_B:number)=>{ +const provide_phoenix_liquidity = async ( + phoenixAdmin: Keypair, + pairAddress: string, + amount_A: number, + amount_B: number +) => { const addPhoenixLiquidityParams: xdr.ScVal[] = [ new Address(phoenixAdmin.publicKey()).toScVal(), - nativeToScVal(formatAmmount(amount_A), { type: "i128" }), + nativeToScVal(formatAmmount(amount_A), { type: 'i128' }), nativeToScVal(null), - nativeToScVal(formatAmmount(amount_B), { type: "i128" }), + nativeToScVal(formatAmmount(amount_B), { type: 'i128' }), nativeToScVal(null), nativeToScVal(null), - nativeToScVal(null) - ] + nativeToScVal(null), + ]; - const provide_liquidity = await invokeCustomContract(pairAddress, 'provide_liquidity', addPhoenixLiquidityParams, phoenixAdmin) + const provide_liquidity = await invokeCustomContract( + pairAddress, + 'provide_liquidity', + addPhoenixLiquidityParams, + phoenixAdmin + ); - if(provide_liquidity.status === 'SUCCESS'){ - console.log('🟢 Successfully added liquidity to phoenix pool') + if (provide_liquidity.status === 'SUCCESS') { + console.log('🟢 Successfully added liquidity to phoenix pool'); return provide_liquidity; } else { - console.log('🔴 error providing liquidity:', provide_liquidity) + console.log('🔴 error providing liquidity:', provide_liquidity); return provide_liquidity; } -} +}; -const getPhoenixBalanceForContract = (contractID:string, balancesObject: any)=>{ - for(let asset in balancesObject) { - if(balancesObject[asset].address === contractID){ +const getPhoenixBalanceForContract = (contractID: string, balancesObject: any) => { + for (let asset in balancesObject) { + if (balancesObject[asset].address === contractID) { return balancesObject[asset].amount; } - } -} + } +}; interface PhoenixPool { - phoenix_pool_address: string, - asset_a_address: string, - asset_a_amount: string, - asset_b_address: string, - asset_b_amount: string, - asset_lp_address: string, - asset_lp_amount: string, - stake_address: string, + phoenix_pool_address: string; + asset_a_address: string; + asset_a_amount: string; + asset_b_address: string; + asset_b_amount: string; + asset_lp_address: string; + asset_lp_amount: string; + stake_address: string; } interface DexDistributionRaw { - protocol_id: string, - path: string[], - parts: number, + protocol_id: string; + path: string[]; + parts: number; } const createDexDistribution = async (dexDistributionRaw: DexDistributionRaw[]) => { const dexDistributionScVal = dexDistributionRaw.map((distribution) => { return xdr.ScVal.scvMap([ new xdr.ScMapEntry({ key: xdr.ScVal.scvSymbol('parts'), - val: nativeToScVal(distribution.parts, {type: "u32"}), + val: nativeToScVal(distribution.parts, { type: 'u32' }), }), new xdr.ScMapEntry({ key: xdr.ScVal.scvSymbol('path'), - val: xdr.ScVal.scvVec(distribution.path.map((pathAddress) => new Address(pathAddress).toScVal())), + val: xdr.ScVal.scvVec( + distribution.path.map((pathAddress) => new Address(pathAddress).toScVal()) + ), }), new xdr.ScMapEntry({ key: xdr.ScVal.scvSymbol('protocol_id'), @@ -428,96 +543,103 @@ const createDexDistribution = async (dexDistributionRaw: DexDistributionRaw[]) = const dexDistributionScValVec = xdr.ScVal.scvVec(dexDistributionScVal); return dexDistributionScValVec; -} +}; export enum SwapMethod { EXACT_INPUT = 'swap_exact_tokens_for_tokens', EXACT_OUTPUT = 'swap_tokens_for_exact_tokens', } -const callAggregatorSwap = async (asset_a:string, asset_b:string, max_amount: number, dexDistributionScValVec: xdr.ScVal, user: Keypair, method: SwapMethod ) => { -// fn swap_exact_tokens_for_tokens( -// token_in: Address, -// token_out: Address, -// amount_in: i128, -// amount_out_min: i128, -// distribution: Vec, -// to: Address, -// deadline: u64, -//) - -//fn swap_tokens_for_exact_tokens( -// token_in: Address, -// token_out: Address, -// amount_out: i128, -// amount_in_max: i128, -// distribution: Vec, -// to: Address, -// deadline: u64, -//) - -let aggregatorSwapParams: xdr.ScVal[]; -let parsedAggregatorSwapParams: any; -switch (method) { - case SwapMethod.EXACT_INPUT: - aggregatorSwapParams = [ - new Address(asset_a).toScVal(), - new Address(asset_b).toScVal(), - nativeToScVal(formatAmmount(max_amount), {type: "i128"}), - nativeToScVal(formatAmmount(0), {type: "i128"}), - dexDistributionScValVec, - new Address(user.publicKey()).toScVal(), - nativeToScVal(getCurrentTimePlusOneHour(), {type:'u64'}), - ]; - parsedAggregatorSwapParams = { - token_in: scValToNative(aggregatorSwapParams[0]), - token_out: scValToNative(aggregatorSwapParams[1]), - amount_in: scValToNative(aggregatorSwapParams[2]), - amount_out_min: scValToNative(aggregatorSwapParams[3]), - distribution: scValToNative(aggregatorSwapParams[4]).map((distribution: any)=>{ - return { - protocol_id: distribution.protocol_id, - parts: distribution.parts, - path: distribution.path.toString() - } - }), - to: scValToNative(aggregatorSwapParams[5]), - deadline: scValToNative(aggregatorSwapParams[6]), - } - break; - case SwapMethod.EXACT_OUTPUT: - aggregatorSwapParams = [ - new Address(asset_a).toScVal(), - new Address(asset_b).toScVal(), - nativeToScVal(formatAmmount(max_amount), {type: "i128"}), - nativeToScVal(formatAmmount(max_amount+200000000), {type: "i128"}), - dexDistributionScValVec, - new Address(user.publicKey()).toScVal(), - nativeToScVal(getCurrentTimePlusOneHour(), {type:'u64'}), - ]; - parsedAggregatorSwapParams = { - token_in: scValToNative(aggregatorSwapParams[0]), - token_out: scValToNative(aggregatorSwapParams[1]), - amount_out: scValToNative(aggregatorSwapParams[2]), - amount_in_max: scValToNative(aggregatorSwapParams[3]), - distribution: scValToNative(aggregatorSwapParams[4]).map((distribution: any)=>{ - return { - protocol_id: distribution.protocol_id, - parts: distribution.parts, - path: distribution.path.toString() - } - }), - to: scValToNative(aggregatorSwapParams[5]), - deadline: scValToNative(aggregatorSwapParams[6]), - } - break; - default: - throw new Error('Invalid swap method'); -} +const callAggregatorSwap = async ( + asset_a: string, + asset_b: string, + max_amount: number, + dexDistributionScValVec: xdr.ScVal, + user: Keypair, + method: SwapMethod +) => { + // fn swap_exact_tokens_for_tokens( + // token_in: Address, + // token_out: Address, + // amount_in: i128, + // amount_out_min: i128, + // distribution: Vec, + // to: Address, + // deadline: u64, + //) + + //fn swap_tokens_for_exact_tokens( + // token_in: Address, + // token_out: Address, + // amount_out: i128, + // amount_in_max: i128, + // distribution: Vec, + // to: Address, + // deadline: u64, + //) + + let aggregatorSwapParams: xdr.ScVal[]; + let parsedAggregatorSwapParams: any; + switch (method) { + case SwapMethod.EXACT_INPUT: + aggregatorSwapParams = [ + new Address(asset_a).toScVal(), + new Address(asset_b).toScVal(), + nativeToScVal(formatAmmount(max_amount), { type: 'i128' }), + nativeToScVal(formatAmmount(0), { type: 'i128' }), + dexDistributionScValVec, + new Address(user.publicKey()).toScVal(), + nativeToScVal(getCurrentTimePlusOneHour(), { type: 'u64' }), + ]; + parsedAggregatorSwapParams = { + token_in: scValToNative(aggregatorSwapParams[0]), + token_out: scValToNative(aggregatorSwapParams[1]), + amount_in: scValToNative(aggregatorSwapParams[2]), + amount_out_min: scValToNative(aggregatorSwapParams[3]), + distribution: scValToNative(aggregatorSwapParams[4]).map((distribution: any) => { + return { + protocol_id: distribution.protocol_id, + parts: distribution.parts, + path: distribution.path.toString(), + }; + }), + to: scValToNative(aggregatorSwapParams[5]), + deadline: scValToNative(aggregatorSwapParams[6]), + }; + break; + case SwapMethod.EXACT_OUTPUT: + aggregatorSwapParams = [ + new Address(asset_a).toScVal(), + new Address(asset_b).toScVal(), + nativeToScVal(formatAmmount(max_amount), { type: 'i128' }), + nativeToScVal(formatAmmount(max_amount + 200000000), { type: 'i128' }), + dexDistributionScValVec, + new Address(user.publicKey()).toScVal(), + nativeToScVal(getCurrentTimePlusOneHour(), { type: 'u64' }), + ]; + parsedAggregatorSwapParams = { + token_in: scValToNative(aggregatorSwapParams[0]), + token_out: scValToNative(aggregatorSwapParams[1]), + amount_out: scValToNative(aggregatorSwapParams[2]), + amount_in_max: scValToNative(aggregatorSwapParams[3]), + distribution: scValToNative(aggregatorSwapParams[4]).map((distribution: any) => { + return { + protocol_id: distribution.protocol_id, + parts: distribution.parts, + path: distribution.path.toString(), + }; + }), + to: scValToNative(aggregatorSwapParams[5]), + deadline: scValToNative(aggregatorSwapParams[6]), + }; + break; + default: + throw new Error('Invalid swap method'); + } - console.log(`🟡 Calling aggregator ${method}`) + console.log(`🟡 Calling aggregator ${method}`); console.log('🔎 aggregatorSwapParams:'); - + console.log(parsedAggregatorSwapParams); const aggregatorResponse = await invokeContract( 'aggregator', @@ -526,15 +648,14 @@ switch (method) { aggregatorSwapParams, user ); - if(aggregatorResponse.status === 'SUCCESS'){ - console.log(`🟢 Aggregator ${method} successful`) - const parsedResponse = scValToNative(aggregatorResponse.returnValue) + if (aggregatorResponse.status === 'SUCCESS') { + console.log(`🟢 Aggregator ${method} successful`); + const parsedResponse = scValToNative(aggregatorResponse.returnValue); return parsedResponse; } else { - console.log('🔴 error calling aggregator:', aggregatorResponse) + console.log('🔴 error calling aggregator:', aggregatorResponse); } -} - +}; //Todo: refactor the main script to use preparetestenvironment (DRY!!!!) /* const prepareTestEnvironment = async (nOfTokens: number)=>{ @@ -687,5 +808,5 @@ export { createDexDistribution, callAggregatorSwap, generateRandomAsset, - getPhoenixBalanceForContract -} \ No newline at end of file + getPhoenixBalanceForContract, +}; diff --git a/src/protocols/phoenix/multi_add_liquidity_phoenix.ts b/src/protocols/phoenix/multi_add_liquidity_phoenix.ts index 8d426df..6e72e57 100644 --- a/src/protocols/phoenix/multi_add_liquidity_phoenix.ts +++ b/src/protocols/phoenix/multi_add_liquidity_phoenix.ts @@ -1,4 +1,11 @@ -import { Address, Keypair, nativeToScVal, scValToNative, xdr } from '@stellar/stellar-sdk'; +import { + Address, + Keypair, + nativeToScVal, + Networks, + scValToNative, + xdr, +} from '@stellar/stellar-sdk'; import { mintToken } from '../../mint_token.js'; import { AddressBook } from '../../utils/address_book.js'; import { getTokenBalance, invokeContract, invokeCustomContract } from '../../utils/contract.js'; @@ -8,9 +15,15 @@ import * as PhoenixFactoryContract from './bindgins/factory_bindings.js'; const network = process.argv[2]; -export async function phoenixMultiAddLiquidity(numberOfPaths: number, tokensBook: TokensBook, addressBook: AddressBook, phoenixAdmin: Keypair, tokensAdminAccount: Keypair) { +export async function phoenixMultiAddLiquidity( + numberOfPaths: number, + tokensBook: TokensBook, + addressBook: AddressBook, + phoenixAdmin: Keypair, + tokensAdminAccount: Keypair +) { const tokens = tokensBook.getTokensByNetwork(network); - if(!tokens || tokens.length <= 0) throw new Error('No tokens found in the tokens book'); + if (!tokens || tokens.length <= 0) throw new Error('No tokens found in the tokens book'); console.log('🚀 « tokens:', tokens[0]); try { @@ -29,54 +42,62 @@ export async function phoenixMultiAddLiquidity(numberOfPaths: number, tokensBook for (let i = 0; i < path.length - 1; i++) { let tokenA = path[i]; let tokenB = path[i + 1]; - + if (tokenA > tokenB) { [tokenA, tokenB] = [tokenB, tokenA]; } - + // Mint tokens // export async function mintToken(contractId: string, amount: number, to: string, admin: Keypair) { await mintToken(tokenA, 25000000000000, phoenixAdmin.publicKey(), tokensAdminAccount); await mintToken(tokenB, 25000000000000, phoenixAdmin.publicKey(), tokensAdminAccount); - + console.log('-------------------------------------------------------'); - console.log("Adding liquidity for pair: ", tokenA, "|", tokenB); - - console.log("TOKEN A Balance:", await getTokenBalance(tokenA, phoenixAdmin.publicKey(), phoenixAdmin)); - console.log("TOKEN B Balance:", await getTokenBalance(tokenB, phoenixAdmin.publicKey(), phoenixAdmin)); + console.log('Adding liquidity for pair: ', tokenA, '|', tokenB); + console.log( + 'TOKEN A Balance:', + await getTokenBalance(tokenA, phoenixAdmin.publicKey(), phoenixAdmin) + ); + console.log( + 'TOKEN B Balance:', + await getTokenBalance(tokenB, phoenixAdmin.publicKey(), phoenixAdmin) + ); const factory_contract = new PhoenixFactoryContract.Client({ publicKey: phoenixAdmin.publicKey()!, - contractId: addressBook.getContractId("phoenix_factory"), + contractId: addressBook.getContractId('phoenix_factory'), networkPassphrase: 'Test SDF Network ; September 2015', - rpcUrl: "https://soroban-testnet.stellar.org/", - signTransaction: (tx: string) => signWithKeypair(tx, 'Test SDF Network ; September 2015', phoenixAdmin), + rpcUrl: 'https://soroban-testnet.stellar.org/', + signTransaction: async (tx: string) => { + const signedTxXdr = await signWithKeypair(tx, Networks.TESTNET, phoenixAdmin); + return { signedTxXdr }; + }, }); - // factory.create_liquidity_pool( - // &admin, - // &lp_init_info, - // &String::from_str(&env, "Pool"), - // &String::from_str(&env, "PHO/BTC"), - // &PoolType::Xyk, - // &None::, - // &100i64, - // &1_000, - // ); - // fn create_liquidity_pool( - // env: Env, - // sender: Address, - // lp_init_info: LiquidityPoolInitInfo, - // share_token_name: String, - // share_token_symbol: String, - // pool_type: PoolType, - // amp: Option, - // default_slippage_bps: i64, - // max_allowed_fee_bps: i64, - // ) - const tx = await factory_contract.create_liquidity_pool({ + // factory.create_liquidity_pool( + // &admin, + // &lp_init_info, + // &String::from_str(&env, "Pool"), + // &String::from_str(&env, "PHO/BTC"), + // &PoolType::Xyk, + // &None::, + // &100i64, + // &1_000, + // ); + // fn create_liquidity_pool( + // env: Env, + // sender: Address, + // lp_init_info: LiquidityPoolInitInfo, + // share_token_name: String, + // share_token_symbol: String, + // pool_type: PoolType, + // amp: Option, + // default_slippage_bps: i64, + // max_allowed_fee_bps: i64, + // ) + const tx = await factory_contract.create_liquidity_pool({ sender: phoenixAdmin.publicKey(), lp_init_info: { admin: phoenixAdmin.publicKey(), @@ -90,21 +111,21 @@ export async function phoenixMultiAddLiquidity(numberOfPaths: number, tokensBook manager: phoenixAdmin.publicKey(), max_complexity: 10, min_bond: 6n, - min_reward: 3n + min_reward: 3n, }, token_init_info: { token_a: tokenA, token_b: tokenB, - } + }, }, share_token_name: `TOKEN${i}`, share_token_symbol: `TKN${i}`, pool_type: PhoenixFactoryContract.PoolType.Xyk, amp: 0n, - default_slippage_bps: 100n, - max_allowed_fee_bps: 2000n, + default_slippage_bps: 100n, + max_allowed_fee_bps: 2000n, }); - + try { const result = await tx.signAndSend(); console.log('🚀 « result:', result); @@ -112,55 +133,79 @@ export async function phoenixMultiAddLiquidity(numberOfPaths: number, tokensBook console.log('🚀 « error:', error); } - console.log("Getting pair address") + console.log('Getting pair address'); const getPairParams: xdr.ScVal[] = [ new Address(tokenA).toScVal(), - new Address(tokenB).toScVal() - ] - const pairAddress = await invokeContract('phoenix_factory', addressBook, 'query_for_pool_by_token_pair', getPairParams, phoenixAdmin, true) + new Address(tokenB).toScVal(), + ]; + const pairAddress = await invokeContract( + 'phoenix_factory', + addressBook, + 'query_for_pool_by_token_pair', + getPairParams, + phoenixAdmin, + true + ); console.log('🚀 « pairAddress:', scValToNative(pairAddress.result.retval)); - console.log('Adding liquidity') + console.log('Adding liquidity'); // fn provide_liquidity( - // env: Env, - // depositor: Address, - // desired_a: Option, - // min_a: Option, - // desired_b: Option, - // min_b: Option, - // custom_slippage_bps: Option, - // deadline: Option, - // ); + // env: Env, + // depositor: Address, + // desired_a: Option, + // min_a: Option, + // desired_b: Option, + // min_b: Option, + // custom_slippage_bps: Option, + // deadline: Option, + // ); const addLiquidityParams: xdr.ScVal[] = [ new Address(phoenixAdmin.publicKey()).toScVal(), - nativeToScVal(2000000000000, { type: "i128" }), + nativeToScVal(2000000000000, { type: 'i128' }), + nativeToScVal(null), + nativeToScVal(2000000000000, { type: 'i128' }), nativeToScVal(null), - nativeToScVal(2000000000000, { type: "i128" }), nativeToScVal(null), nativeToScVal(null), - nativeToScVal(null) - ] - - await invokeCustomContract(scValToNative(pairAddress.result.retval), 'provide_liquidity', addLiquidityParams, phoenixAdmin) - - console.log("TOKEN A Balance AFTER:", await getTokenBalance(tokenA, phoenixAdmin.publicKey(), phoenixAdmin)); - console.log("TOKEN B Balance AFTER:", await getTokenBalance(tokenB, phoenixAdmin.publicKey(), phoenixAdmin)); - } + ]; + + await invokeCustomContract( + scValToNative(pairAddress.result.retval), + 'provide_liquidity', + addLiquidityParams, + phoenixAdmin + ); + + console.log( + 'TOKEN A Balance AFTER:', + await getTokenBalance(tokenA, phoenixAdmin.publicKey(), phoenixAdmin) + ); + console.log( + 'TOKEN B Balance AFTER:', + await getTokenBalance(tokenB, phoenixAdmin.publicKey(), phoenixAdmin) + ); + } } } catch (error) { console.log('🚀 « error:', error); - } } -function generatePaths(tokens: Asset[], startAddress: string, endAddress: string, numberOfPaths: number): string[][] { +function generatePaths( + tokens: Asset[], + startAddress: string, + endAddress: string, + numberOfPaths: number +): string[][] { // Filter out the start and end tokens from the list to avoid including them as intermediates - const intermediateTokens = tokens.filter(token => token.contract !== startAddress && token.contract !== endAddress); + const intermediateTokens = tokens.filter( + (token) => token.contract !== startAddress && token.contract !== endAddress + ); console.log('🚀 « intermediateTokens:', intermediateTokens); // Function to generate a path const createPath = (intermediates: Asset[]): string[] => { - return [startAddress, ...intermediates.map(token => token.contract), endAddress]; + return [startAddress, ...intermediates.map((token) => token.contract), endAddress]; }; // Store generated paths diff --git a/src/utils/env_config.ts b/src/utils/env_config.ts index 70a38f6..e6ad4bf 100644 --- a/src/utils/env_config.ts +++ b/src/utils/env_config.ts @@ -1,12 +1,12 @@ -import { Horizon, Keypair, SorobanRpc } from '@stellar/stellar-sdk'; -import dotenv from "dotenv"; -import * as fs from "fs"; -import path from "path"; -import { fileURLToPath } from "url"; +import { Horizon, Keypair, rpc } from '@stellar/stellar-sdk'; +import dotenv from 'dotenv'; +import * as fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); -dotenv.config({ path: path.join(__dirname, "../../.env") }); +dotenv.config({ path: path.join(__dirname, '../../.env') }); interface NetworkConfig { network: string; @@ -23,7 +23,7 @@ interface Config { } class EnvConfig { - rpc: SorobanRpc.Server; + rpc: rpc.Server; horizonRpc: Horizon.Server; passphrase: string; friendbot: string | undefined; @@ -33,14 +33,14 @@ class EnvConfig { testUser: Keypair; constructor( - rpc: SorobanRpc.Server, + rpc: rpc.Server, horizonRpc: Horizon.Server, passphrase: string, friendbot: string | undefined, admin: Keypair, tokenAdmin: Keypair, phoenixAdmin: Keypair, - testUser: Keypair, + testUser: Keypair ) { this.rpc = rpc; this.horizonRpc = horizonRpc; @@ -49,7 +49,7 @@ class EnvConfig { this.admin = admin; this.tokenAdmin = tokenAdmin; this.phoenixAdmin = phoenixAdmin; - this.testUser = testUser ; + this.testUser = testUser; } /** @@ -57,14 +57,11 @@ class EnvConfig { * @returns Environment config */ static loadFromFile(network: string): EnvConfig { - const fileContents = fs.readFileSync( - path.join(__dirname, "../../configs.json"), - "utf8", - ); + const fileContents = fs.readFileSync(path.join(__dirname, '../../configs.json'), 'utf8'); const configs: Config = JSON.parse(fileContents); let rpc_url, horizon_rpc_url, friendbot_url, passphrase; - + const networkConfig = configs.networkConfig.find((config) => config.network === network); if (!networkConfig) { throw new Error(`Network configuration for '${network}' not found`); @@ -90,31 +87,33 @@ class EnvConfig { if ( rpc_url === undefined || horizon_rpc_url === undefined || - (network != "mainnet" && friendbot_url === undefined) || + (network != 'mainnet' && friendbot_url === undefined) || passphrase === undefined - ) { + ) { throw new Error('Error: Configuration is missing required fields, include '); } - if ( + if ( admin === undefined || tokenAdmin === undefined || phoenixAdmin === undefined || testUser === undefined ) { - throw new Error('Error: Configuration is missing required fields, please read .env.example to set up the required fields'); + throw new Error( + 'Error: Configuration is missing required fields, please read .env.example to set up the required fields' + ); } - const allowHttp = network === "standalone"; + const allowHttp = network === 'standalone'; return new EnvConfig( - new SorobanRpc.Server(rpc_url, { allowHttp }), - new Horizon.Server(horizon_rpc_url, {allowHttp}), + new rpc.Server(rpc_url, { allowHttp }), + new Horizon.Server(horizon_rpc_url, { allowHttp }), passphrase, friendbot_url, Keypair.fromSecret(admin), Keypair.fromSecret(tokenAdmin), Keypair.fromSecret(phoenixAdmin), - Keypair.fromSecret(testUser), + Keypair.fromSecret(testUser) ); } @@ -130,8 +129,7 @@ class EnvConfig { } try { return Keypair.fromSecret(userSecretKey); - } - catch (e) { + } catch (e) { throw new Error(`${userKey} secret key might not be found in .env. Failed with error ${e}`); } diff --git a/src/utils/tx.ts b/src/utils/tx.ts index 601b0e5..b0064f2 100644 --- a/src/utils/tx.ts +++ b/src/utils/tx.ts @@ -1,8 +1,8 @@ -import { Account, Keypair, SorobanRpc, Transaction, TransactionBuilder, xdr } from '@stellar/stellar-sdk'; +import { Account, Keypair, rpc, Transaction, TransactionBuilder, xdr } from '@stellar/stellar-sdk'; import { config } from './env_config.js'; -type txResponse = SorobanRpc.Api.SendTransactionResponse | SorobanRpc.Api.GetTransactionResponse; -type txStatus = SorobanRpc.Api.SendTransactionStatus | SorobanRpc.Api.GetTransactionStatus; +type txResponse = rpc.Api.SendTransactionResponse | rpc.Api.GetTransactionResponse; +type txStatus = rpc.Api.SendTransactionStatus | rpc.Api.GetTransactionStatus; const network = process.argv[2]; const loadedConfig = config(network); @@ -25,23 +25,30 @@ export async function invoke( const txBuilder = await createTxBuilder(source); if (typeof operation === 'string') { operation = xdr.Operation.fromXDR(operation, 'base64'); - } txBuilder.addOperation(operation); const tx = txBuilder.build(); - + return invokeTransaction(tx, source, sim); } export async function invokeTransaction(tx: Transaction, source: Keypair, sim: boolean) { // simulate the TX const simulation_resp = await loadedConfig.rpc.simulateTransaction(tx); - if (SorobanRpc.Api.isSimulationError(simulation_resp)) { + if (rpc.Api.isSimulationError(simulation_resp)) { // No resource estimation available from a simulation error. Allow the response formatter // to fetch the error. - if(simulation_resp.error.includes("ExistingValue")) {throw new Error("ExistingValue")} - console.log("There was an error while simulation the transaction: simulation_resp", simulation_resp) - console.log("🚀 ~ invokeTransaction ~ simulation_resp.events[0].event:", simulation_resp.events[0].event.toString()) + if (simulation_resp.error.includes('ExistingValue')) { + throw new Error('ExistingValue'); + } + console.log( + 'There was an error while simulation the transaction: simulation_resp', + simulation_resp + ); + console.log( + '🚀 ~ invokeTransaction ~ simulation_resp.events[0].event:', + simulation_resp.events[0].event.toString() + ); throw Error(`Simulation : ${simulation_resp.error}`); } else if (sim) { // Only simulate the TX. Assemble the TX to borrow the resource estimation algorithm in @@ -58,7 +65,7 @@ export async function invokeTransaction(tx: Transaction, source: Keypair, sim: b txResources.writeBytes() ) .build(); - const assemble_tx = SorobanRpc.assembleTransaction(tx, simulation_resp); + const assemble_tx = rpc.assembleTransaction(tx, simulation_resp); sim_tx_data.resourceFee( xdr.Int64.fromString((Number(sim_tx_data.resourceFee().toString()) + 100000).toString()) ); diff --git a/yarn.lock b/yarn.lock index 32886ff..a2f58c4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -89,10 +89,10 @@ resolved "https://registry.yarnpkg.com/@stellar/js-xdr/-/js-xdr-3.1.2.tgz#db7611135cf21e989602fd72f513c3bed621bc74" integrity sha512-VVolPL5goVEIsvuGqDc5uiKxV03lzfWdvYg1KikvwheDmTBO68CKDji3bAZ/kppZrx5iTA8z3Ld5yuytcvhvOQ== -"@stellar/stellar-base@^12.1.0": - version "12.1.0" - resolved "https://registry.yarnpkg.com/@stellar/stellar-base/-/stellar-base-12.1.0.tgz#45b55a38738ed267e0d55a1fee3c4153c82d0fa6" - integrity sha512-pWwn+XWP5NotmIteZNuJzHeNn9DYSqH3lsYbtFUoSYy1QegzZdi9D8dK6fJ2fpBAnf/rcDjHgHOw3gtHaQFVbg== +"@stellar/stellar-base@^13.0.1": + version "13.0.1" + resolved "https://registry.yarnpkg.com/@stellar/stellar-base/-/stellar-base-13.0.1.tgz#0897f77349ded61e838c0d55519f36f720efe3c9" + integrity sha512-Xbd12mc9Oj/130Tv0URmm3wXG77XMshZtZ2yNCjqX5ZbMD5IYpbBs3DVCteLU/4SLj/Fnmhh1dzhrQXnk4r+pQ== dependencies: "@stellar/js-xdr" "^3.1.2" base32.js "^0.1.0" @@ -101,17 +101,18 @@ sha.js "^2.3.6" tweetnacl "^1.0.3" optionalDependencies: - sodium-native "^4.1.1" + sodium-native "^4.3.0" -"@stellar/stellar-sdk@^12.1.0": - version "12.2.0" - resolved "https://registry.yarnpkg.com/@stellar/stellar-sdk/-/stellar-sdk-12.2.0.tgz#c449bc52be282c7706aabbd257acbc5aec3345b3" - integrity sha512-Wy5sDOqb5JvAC76f4sQIV6Pe3JNyZb0PuyVNjwt3/uWsjtxRkFk6s2yTHTefBLWoR+mKxDjO7QfzhycF1v8FXQ== +"@stellar/stellar-sdk@^13.0.0": + version "13.0.0" + resolved "https://registry.yarnpkg.com/@stellar/stellar-sdk/-/stellar-sdk-13.0.0.tgz#293f0bf6964b384e489b0ad75174d39cd22a3a69" + integrity sha512-+wvmKi+XWwu27nLYTM17EgBdpbKohEkOfCIK4XKfsI4WpMXAqvnqSm98i9h5dAblNB+w8BJqzGs1JY0PtTGm4A== dependencies: - "@stellar/stellar-base" "^12.1.0" - axios "^1.7.2" + "@stellar/stellar-base" "^13.0.1" + axios "^1.7.7" bignumber.js "^9.1.2" eventsource "^2.0.2" + feaxios "^0.0.20" randombytes "^2.1.0" toml "^3.0.0" urijs "^1.19.1" @@ -276,10 +277,10 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== -axios@^1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.2.tgz#b625db8a7051fbea61c35a3cbb3a1daa7b9c7621" - integrity sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw== +axios@^1.7.7: + version "1.7.9" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.9.tgz#d7d071380c132a24accda1b2cfc1535b79ec650a" + integrity sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw== dependencies: follow-redirects "^1.15.6" form-data "^4.0.0" @@ -568,6 +569,13 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" +feaxios@^0.0.20: + version "0.0.20" + resolved "https://registry.yarnpkg.com/feaxios/-/feaxios-0.0.20.tgz#04e976beb7345401fedeba764f0e9e1c4d01afd4" + integrity sha512-g3hm2YDNffNxA3Re3Hd8ahbpmDee9Fv1Pb1C/NoWsjY7mtD8nyNeJytUzn+DK0Hyl9o6HppeWOrtnqgmhOYfWA== + dependencies: + is-retry-allowed "^3.0.0" + file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" @@ -736,6 +744,11 @@ is-path-inside@^3.0.3: resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== +is-retry-allowed@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-3.0.0.tgz#ea79389fd350d156823c491bee9c69f485b1445c" + integrity sha512-9xH0xvoggby+u0uGF7cZXdrutWiBiaFG8ZT4YFPXL8NzkyAwX3AKGLeFQLvzDpM430+nDFBZ1LHkie/8ocL06A== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -1016,10 +1029,10 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -sodium-native@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/sodium-native/-/sodium-native-4.1.1.tgz#109bc924dd55c13db87c6dd30da047487595723c" - integrity sha512-LXkAfRd4FHtkQS4X6g+nRcVaN7mWVNepV06phIsC6+IZFvGh1voW5TNQiQp2twVaMf05gZqQjuS+uWLM6gHhNQ== +sodium-native@^4.3.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/sodium-native/-/sodium-native-4.3.1.tgz#221a11b10876259b85ccd288dd28f07ed4dbf35f" + integrity sha512-YdP64gAdpIKHfL4ttuX4aIfjeunh9f+hNeQJpE9C8UMndB3zkgZ7YmmGT4J2+v6Ibyp6Wem8D1TcSrtdW0bqtg== dependencies: node-gyp-build "^4.8.0"