From 1dd8dfc0f64eae425a0b3f210d27fbcc2adb7412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Brzezin=CC=81ski?= Date: Mon, 21 Oct 2024 15:34:07 +0200 Subject: [PATCH 1/6] reimbrusmnet script --- package.json | 1 + ts/client/scripts/reimbrusment.ts | 181 ++++++++++++++++++++++++++++++ yarn.lock | 9 +- 3 files changed, 189 insertions(+), 2 deletions(-) create mode 100644 ts/client/scripts/reimbrusment.ts diff --git a/package.json b/package.json index 508b751e0..8d342c881 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,7 @@ "binance-api-node": "^0.12.7", "bs58": "^5.0.0", "cross-fetch": "^3.1.5", + "csv-parse": "^5.5.6", "dotenv": "^16.0.3", "fast-copy": "^3.0.1", "lodash": "^4.17.21", diff --git a/ts/client/scripts/reimbrusment.ts b/ts/client/scripts/reimbrusment.ts new file mode 100644 index 000000000..b09b75078 --- /dev/null +++ b/ts/client/scripts/reimbrusment.ts @@ -0,0 +1,181 @@ +import { Connection, Keypair, PublicKey } from '@solana/web3.js'; +import fs from 'fs'; +import * as path from 'path'; +import { parse } from 'csv-parse'; +import { AnchorProvider, Wallet } from 'switchboard-anchor'; +import { MANGO_V4_ID, MangoClient, USDC_MINT } from '../src'; +import { WRAPPED_SOL_MINT } from '@project-serum/serum/lib/token-instructions'; + +const MANGO_MAINNET_GROUP = new PublicKey( + '78b8f4cGCwmZ9ysPFMWLaLTkkaYnUjwMJYStWe5RTSSX', +); + +type Reimbursement = { + mango_account: string; + mangoSOL: number; + MOTHER: number; + SOL: number; + USDC: number; + Notional: string; +}; + +const mints = { + mangoSOL: new PublicKey('MangmsBgFqJhW4cLUR9LxfVgMboY1xAoP8UUBiWwwuY'), + MOTHER: new PublicKey('3S8qX1MsMqRbiwKg2cQyx7nis1oHMgaCuc9c4VfvVdPN'), + SOL: WRAPPED_SOL_MINT, + USDC: USDC_MINT, +}; + +const main = async () => { + const user = await setupWallet(); + const mainConnection = new Connection(''); + const backupConnections = [new Connection(''), new Connection('')]; + const options = AnchorProvider.defaultOptions(); + const userWallet = new Wallet(user); + const userProvider = new AnchorProvider(mainConnection, userWallet, options); + const client = await MangoClient.connect( + userProvider, + 'mainnet-beta', + MANGO_V4_ID['mainnet-beta'], + { + idsSource: 'api', + multipleConnections: backupConnections, + prioritizationFee: 200000, + }, + ); + + const group = await client.getGroup(MANGO_MAINNET_GROUP); + + const csvData = await readCsv(); + + const TO_PROCESS = csvData.slice(1, 5); + const TOKEN = 'SOL'; + + const notReimbursedMangoAccounts: string[] = []; + for (const row of TO_PROCESS) { + const mangoAccountPk = tryGetPubKey(row.mango_account); + if (mangoAccountPk) { + try { + const mint = mints[TOKEN as keyof typeof mints]; + const amount = Number(row[TOKEN as keyof typeof mints]); + if (mint && amount > 0) { + const mangoAccount = await client.getMangoAccount(mangoAccountPk); + console.log('Mango Account exists'); + console.log( + `Start reimbursing ${mint.toBase58()} ${amount} ${ + row.mango_account + }`, + ); + try { + const signature = await client.tokenDeposit( + group, + mangoAccount, + mint, + amount, + ); + console.log( + 'Reimburse end ', + signature.signature, + signature.confirmationStatus, + signature.err, + ); + if (signature.confirmationStatus === 'confirmed') { + console.log('OK'); + } else { + notReimbursedMangoAccounts.push(row.mango_account); + } + } catch (e) { + console.log(e); + notReimbursedMangoAccounts.push(row.mango_account); + } + } + } catch (e) { + console.log('Mango account not exists'); + const wallet = await ownerOfMangoAccount(row.mango_account); + if (!wallet) { + notReimbursedMangoAccounts.push(row.mango_account); + } else { + notReimbursedMangoAccounts.push(row.mango_account); + console.log('Mango Account: ', row.mango_account, 'Owner: ', wallet); + } + } + } else { + console.log('Invalid PublicKey: ', row.mango_account); + throw 'Invalid PublicKey'; + } + } + console.log(notReimbursedMangoAccounts); +}; + +const setupWallet = () => { + const user = Keypair.fromSecretKey( + Buffer.from( + JSON.parse( + fs.readFileSync('keypair.json', { + encoding: 'utf-8', + }), + ), + ), + ); + + return user; +}; + +const ownerOfMangoAccount = async (mangoAccount: string) => { + try { + const respWrapped = await fetch( + `https://api.mngo.cloud/data/v4/user-data/profile-search?search-string=${mangoAccount}&search-method=mango-account`, + ); + const resp = await respWrapped.json(); + const accountOwner = resp?.length > 0 ? resp[0].wallet_pk : null; + if (accountOwner === null) { + throw 'not found'; + } + return accountOwner as string; + } catch (e) { + console.log('cant find mangoAccount:', mangoAccount); + } +}; + +const readCsv = async () => { + const csvFilePath = path.resolve(__dirname, 'reimbursement.csv'); + + const headers = [ + 'mango_account', + 'mangoSOL', + 'MOTHER', + 'SOL', + 'USDC', + 'Notional', + ]; + + return new Promise((resolve, reject) => { + const fileContent = fs.readFileSync(csvFilePath, { encoding: 'utf-8' }); + + parse( + fileContent, + { + delimiter: ',', + columns: headers, + }, + (error, result: Reimbursement[]) => { + if (error) { + reject(error); + } else { + const resp = result.slice(1, result.length); + resolve(resp); + } + }, + ); + }); +}; + +main(); + +export const tryGetPubKey = (pubkey: string | string[]) => { + try { + return new PublicKey(pubkey); + } catch (e) { + return null; + } +}; diff --git a/yarn.lock b/yarn.lock index 963b02d3b..618680681 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1671,6 +1671,11 @@ crypto-hash@^1.3.0: resolved "https://registry.yarnpkg.com/crypto-hash/-/crypto-hash-1.3.0.tgz#b402cb08f4529e9f4f09346c3e275942f845e247" integrity sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg== +csv-parse@^5.5.6: + version "5.5.6" + resolved "https://registry.yarnpkg.com/csv-parse/-/csv-parse-5.5.6.tgz#0d726d58a60416361358eec291a9f93abe0b6b1a" + integrity sha512-uNpm30m/AGSkLxxy7d9yRXpJQFrZzVWLFBkS+6ngPcZkw/5k3L/jjFuj7tVnEpRn+QgmiXr21nDlhCiUK4ij2A== + debug@4, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5: version "4.3.6" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b" @@ -2766,14 +2771,14 @@ node-addon-api@^5.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762" integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA== -node-fetch@3.3.2, "node-fetch@npm:@blockworks-foundation/node-fetch@2.6.11": +node-fetch@3.3.2, node-fetch@^2.6.12, node-fetch@^2.7.0, "node-fetch@npm:@blockworks-foundation/node-fetch@2.6.11": version "2.6.11" resolved "https://registry.yarnpkg.com/@blockworks-foundation/node-fetch/-/node-fetch-2.6.11.tgz#fb536ef0e6a960e7b7993f3c1d3b3bba9bdfbc56" integrity sha512-HeDTxpIypSR4qCoqgUXGr8YL4OG1z7BbV4VhQ9iQs+pt2wV3MtqO+sQk2vXK3WDKu5C6BsbGmWE22BmIrcuOOw== dependencies: whatwg-url "^5.0.0" -node-fetch@^2.6.1, node-fetch@^2.6.12, node-fetch@^2.7.0: +node-fetch@^2.6.1: version "2.7.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== From 0526662e62ab080bf157463c661ccaac064105ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Brzezin=CC=81ski?= Date: Mon, 21 Oct 2024 15:36:45 +0200 Subject: [PATCH 2/6] fix --- ts/client/scripts/{reimbrusment.ts => reimbursement.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ts/client/scripts/{reimbrusment.ts => reimbursement.ts} (100%) diff --git a/ts/client/scripts/reimbrusment.ts b/ts/client/scripts/reimbursement.ts similarity index 100% rename from ts/client/scripts/reimbrusment.ts rename to ts/client/scripts/reimbursement.ts From 24af94c10cfc1ce59c6a1d1f69035ba2940a79ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Brzezin=CC=81ski?= Date: Wed, 23 Oct 2024 19:55:20 +0200 Subject: [PATCH 3/6] fix --- ts/client/scripts/reimbursement.ts | 179 ++++++++++++++++------------- 1 file changed, 97 insertions(+), 82 deletions(-) diff --git a/ts/client/scripts/reimbursement.ts b/ts/client/scripts/reimbursement.ts index b09b75078..de5c05e7d 100644 --- a/ts/client/scripts/reimbursement.ts +++ b/ts/client/scripts/reimbursement.ts @@ -2,8 +2,8 @@ import { Connection, Keypair, PublicKey } from '@solana/web3.js'; import fs from 'fs'; import * as path from 'path'; import { parse } from 'csv-parse'; -import { AnchorProvider, Wallet } from 'switchboard-anchor'; -import { MANGO_V4_ID, MangoClient, USDC_MINT } from '../src'; +import { AnchorProvider, Wallet } from '@coral-xyz/anchor'; +import { MANGO_V4_ID, MangoClient, toNative, USDC_MINT } from '../src'; import { WRAPPED_SOL_MINT } from '@project-serum/serum/lib/token-instructions'; const MANGO_MAINNET_GROUP = new PublicKey( @@ -19,6 +19,79 @@ type Reimbursement = { Notional: string; }; +const setupWallet = () => { + const user = Keypair.fromSecretKey( + Buffer.from( + JSON.parse( + fs.readFileSync('keypair.json', { + encoding: 'utf-8', + }), + ), + ), + ); + + return user; +}; + +const ownerOfMangoAccount = async (mangoAccount: string) => { + try { + const respWrapped = await fetch( + `https://api.mngo.cloud/data/v4/user-data/profile-search?search-string=${mangoAccount}&search-method=mango-account`, + ); + const resp = await respWrapped.json(); + const accountOwner = resp?.length > 0 ? resp[0].wallet_pk : null; + if (accountOwner === null) { + throw 'not found'; + } + return accountOwner as string; + } catch (e) { + console.log(e); + console.log('cant find mangoAccount:', mangoAccount); + } +}; + +const readCsv = async () => { + const csvFilePath = path.resolve(__dirname, 'reimbursement.csv'); + + const headers = [ + 'mango_account', + 'mangoSOL', + 'MOTHER', + 'SOL', + 'USDC', + 'Notional', + ]; + + return new Promise((resolve, reject) => { + const fileContent = fs.readFileSync(csvFilePath, { encoding: 'utf-8' }); + + parse( + fileContent, + { + delimiter: ',', + columns: headers, + }, + (error, result: Reimbursement[]) => { + if (error) { + reject(error); + } else { + const resp = result.slice(1, result.length); + resolve(resp); + } + }, + ); + }); +}; + +const tryGetPubKey = (pubkey: string | string[]) => { + try { + return new PublicKey(pubkey); + } catch (e) { + console.log(e); + return null; + } +}; + const mints = { mangoSOL: new PublicKey('MangmsBgFqJhW4cLUR9LxfVgMboY1xAoP8UUBiWwwuY'), MOTHER: new PublicKey('3S8qX1MsMqRbiwKg2cQyx7nis1oHMgaCuc9c4VfvVdPN'), @@ -28,11 +101,17 @@ const mints = { const main = async () => { const user = await setupWallet(); - const mainConnection = new Connection(''); - const backupConnections = [new Connection(''), new Connection('')]; + const mainConnection = new Connection('http://fcs-da1._peer.internal:18899/'); + const backupConnections = [ + new Connection( + 'https://staked.helius-rpc.com?api-key=c367d0d9-acf2-437d-9ef3-72304f1a4117', + ), + new Connection('https://rpc.mngo.cloud/adr9quiemae0'), + ]; const options = AnchorProvider.defaultOptions(); const userWallet = new Wallet(user); const userProvider = new AnchorProvider(mainConnection, userWallet, options); + const client = await MangoClient.connect( userProvider, 'mainnet-beta', @@ -43,17 +122,18 @@ const main = async () => { prioritizationFee: 200000, }, ); - + console.log(userWallet.publicKey.toBase58(), '@@@@@'); const group = await client.getGroup(MANGO_MAINNET_GROUP); const csvData = await readCsv(); - const TO_PROCESS = csvData.slice(1, 5); - const TOKEN = 'SOL'; + const TO_PROCESS = csvData; + const TOKEN = 'MOTHER'; const notReimbursedMangoAccounts: string[] = []; for (const row of TO_PROCESS) { const mangoAccountPk = tryGetPubKey(row.mango_account); + if (mangoAccountPk) { try { const mint = mints[TOKEN as keyof typeof mints]; @@ -67,19 +147,25 @@ const main = async () => { }`, ); try { - const signature = await client.tokenDeposit( + const decimals = group.getMintDecimals(mint); + const nativeAmount = toNative(amount, decimals); + + const signature = await client.tokenDepositNative( group, mangoAccount, mint, - amount, + nativeAmount, + false, + true, ); + console.log( 'Reimburse end ', signature.signature, signature.confirmationStatus, signature.err, ); - if (signature.confirmationStatus === 'confirmed') { + if (!signature.err) { console.log('OK'); } else { notReimbursedMangoAccounts.push(row.mango_account); @@ -90,7 +176,7 @@ const main = async () => { } } } catch (e) { - console.log('Mango account not exists'); + console.log('Mango account not exists', e); const wallet = await ownerOfMangoAccount(row.mango_account); if (!wallet) { notReimbursedMangoAccounts.push(row.mango_account); @@ -107,75 +193,4 @@ const main = async () => { console.log(notReimbursedMangoAccounts); }; -const setupWallet = () => { - const user = Keypair.fromSecretKey( - Buffer.from( - JSON.parse( - fs.readFileSync('keypair.json', { - encoding: 'utf-8', - }), - ), - ), - ); - - return user; -}; - -const ownerOfMangoAccount = async (mangoAccount: string) => { - try { - const respWrapped = await fetch( - `https://api.mngo.cloud/data/v4/user-data/profile-search?search-string=${mangoAccount}&search-method=mango-account`, - ); - const resp = await respWrapped.json(); - const accountOwner = resp?.length > 0 ? resp[0].wallet_pk : null; - if (accountOwner === null) { - throw 'not found'; - } - return accountOwner as string; - } catch (e) { - console.log('cant find mangoAccount:', mangoAccount); - } -}; - -const readCsv = async () => { - const csvFilePath = path.resolve(__dirname, 'reimbursement.csv'); - - const headers = [ - 'mango_account', - 'mangoSOL', - 'MOTHER', - 'SOL', - 'USDC', - 'Notional', - ]; - - return new Promise((resolve, reject) => { - const fileContent = fs.readFileSync(csvFilePath, { encoding: 'utf-8' }); - - parse( - fileContent, - { - delimiter: ',', - columns: headers, - }, - (error, result: Reimbursement[]) => { - if (error) { - reject(error); - } else { - const resp = result.slice(1, result.length); - resolve(resp); - } - }, - ); - }); -}; - main(); - -export const tryGetPubKey = (pubkey: string | string[]) => { - try { - return new PublicKey(pubkey); - } catch (e) { - return null; - } -}; From 78a43765af6e6d7d217e03521a7d714280f4f5ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Brzezin=CC=81ski?= Date: Wed, 23 Oct 2024 21:18:31 +0200 Subject: [PATCH 4/6] fix --- ts/client/scripts/reimbursement.ts | 61 +++++++++++------------------- 1 file changed, 22 insertions(+), 39 deletions(-) diff --git a/ts/client/scripts/reimbursement.ts b/ts/client/scripts/reimbursement.ts index de5c05e7d..ba26a317c 100644 --- a/ts/client/scripts/reimbursement.ts +++ b/ts/client/scripts/reimbursement.ts @@ -1,4 +1,4 @@ -import { Connection, Keypair, PublicKey } from '@solana/web3.js'; +import { Connection, Keypair, PublicKey, SystemProgram } from '@solana/web3.js'; import fs from 'fs'; import * as path from 'path'; import { parse } from 'csv-parse'; @@ -12,6 +12,7 @@ const MANGO_MAINNET_GROUP = new PublicKey( type Reimbursement = { mango_account: string; + owner: string; mangoSOL: number; MOTHER: number; SOL: number; @@ -33,28 +34,12 @@ const setupWallet = () => { return user; }; -const ownerOfMangoAccount = async (mangoAccount: string) => { - try { - const respWrapped = await fetch( - `https://api.mngo.cloud/data/v4/user-data/profile-search?search-string=${mangoAccount}&search-method=mango-account`, - ); - const resp = await respWrapped.json(); - const accountOwner = resp?.length > 0 ? resp[0].wallet_pk : null; - if (accountOwner === null) { - throw 'not found'; - } - return accountOwner as string; - } catch (e) { - console.log(e); - console.log('cant find mangoAccount:', mangoAccount); - } -}; - const readCsv = async () => { const csvFilePath = path.resolve(__dirname, 'reimbursement.csv'); const headers = [ 'mango_account', + 'owner', 'mangoSOL', 'MOTHER', 'SOL', @@ -101,13 +86,8 @@ const mints = { const main = async () => { const user = await setupWallet(); - const mainConnection = new Connection('http://fcs-da1._peer.internal:18899/'); - const backupConnections = [ - new Connection( - 'https://staked.helius-rpc.com?api-key=c367d0d9-acf2-437d-9ef3-72304f1a4117', - ), - new Connection('https://rpc.mngo.cloud/adr9quiemae0'), - ]; + const mainConnection = new Connection(''); + const backupConnections = [new Connection(''), new Connection('')]; const options = AnchorProvider.defaultOptions(); const userWallet = new Wallet(user); const userProvider = new AnchorProvider(mainConnection, userWallet, options); @@ -128,17 +108,19 @@ const main = async () => { const csvData = await readCsv(); const TO_PROCESS = csvData; - const TOKEN = 'MOTHER'; + const TOKEN = 'SOL'; const notReimbursedMangoAccounts: string[] = []; for (const row of TO_PROCESS) { const mangoAccountPk = tryGetPubKey(row.mango_account); if (mangoAccountPk) { + const mint = mints[TOKEN as keyof typeof mints]; + const amount = Number(row[TOKEN as keyof typeof mints]); try { - const mint = mints[TOKEN as keyof typeof mints]; - const amount = Number(row[TOKEN as keyof typeof mints]); if (mint && amount > 0) { + const decimals = group.getMintDecimals(mint); + const nativeAmount = toNative(amount, decimals); const mangoAccount = await client.getMangoAccount(mangoAccountPk); console.log('Mango Account exists'); console.log( @@ -147,9 +129,6 @@ const main = async () => { }`, ); try { - const decimals = group.getMintDecimals(mint); - const nativeAmount = toNative(amount, decimals); - const signature = await client.tokenDepositNative( group, mangoAccount, @@ -168,7 +147,12 @@ const main = async () => { if (!signature.err) { console.log('OK'); } else { - notReimbursedMangoAccounts.push(row.mango_account); + const ix = SystemProgram.transfer({ + fromPubkey: userWallet.publicKey, + toPubkey: new PublicKey(row.owner), + lamports: toNative(amount, 9).toNumber(), + }); + await client.sendAndConfirmTransactionForGroup(group, [ix]); } } catch (e) { console.log(e); @@ -177,13 +161,12 @@ const main = async () => { } } catch (e) { console.log('Mango account not exists', e); - const wallet = await ownerOfMangoAccount(row.mango_account); - if (!wallet) { - notReimbursedMangoAccounts.push(row.mango_account); - } else { - notReimbursedMangoAccounts.push(row.mango_account); - console.log('Mango Account: ', row.mango_account, 'Owner: ', wallet); - } + const ix = SystemProgram.transfer({ + fromPubkey: userWallet.publicKey, + toPubkey: new PublicKey(row.owner), + lamports: toNative(amount, 9).toNumber(), + }); + await client.sendAndConfirmTransactionForGroup(group, [ix]); } } else { console.log('Invalid PublicKey: ', row.mango_account); From d5ce6b06b6631ddf7c2fd4bb7ffb82b23801427b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Brzezin=CC=81ski?= Date: Wed, 23 Oct 2024 21:31:23 +0200 Subject: [PATCH 5/6] fix --- ts/client/scripts/reimbursement.ts | 62 +++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/ts/client/scripts/reimbursement.ts b/ts/client/scripts/reimbursement.ts index ba26a317c..c892b5140 100644 --- a/ts/client/scripts/reimbursement.ts +++ b/ts/client/scripts/reimbursement.ts @@ -3,8 +3,19 @@ import fs from 'fs'; import * as path from 'path'; import { parse } from 'csv-parse'; import { AnchorProvider, Wallet } from '@coral-xyz/anchor'; -import { MANGO_V4_ID, MangoClient, toNative, USDC_MINT } from '../src'; +import { + createComputeBudgetIx, + MANGO_V4_ID, + MangoClient, + toNative, + USDC_MINT, +} from '../src'; import { WRAPPED_SOL_MINT } from '@project-serum/serum/lib/token-instructions'; +import { sendSignAndConfirmTransactions } from '@blockworks-foundation/mangolana/lib/transactions'; +import { + SequenceType, + TransactionInstructionWithSigners, +} from '@blockworks-foundation/mangolana/lib/globalTypes'; const MANGO_MAINNET_GROUP = new PublicKey( '78b8f4cGCwmZ9ysPFMWLaLTkkaYnUjwMJYStWe5RTSSX', @@ -83,11 +94,12 @@ const mints = { SOL: WRAPPED_SOL_MINT, USDC: USDC_MINT, }; +const backups = [new Connection(''), new Connection('')]; const main = async () => { const user = await setupWallet(); const mainConnection = new Connection(''); - const backupConnections = [new Connection(''), new Connection('')]; + const backupConnections = backups; const options = AnchorProvider.defaultOptions(); const userWallet = new Wallet(user); const userProvider = new AnchorProvider(mainConnection, userWallet, options); @@ -118,7 +130,7 @@ const main = async () => { const mint = mints[TOKEN as keyof typeof mints]; const amount = Number(row[TOKEN as keyof typeof mints]); try { - if (mint && amount > 0) { + if (mint && amount > 0.0001) { const decimals = group.getMintDecimals(mint); const nativeAmount = toNative(amount, decimals); const mangoAccount = await client.getMangoAccount(mangoAccountPk); @@ -152,7 +164,27 @@ const main = async () => { toPubkey: new PublicKey(row.owner), lamports: toNative(amount, 9).toNumber(), }); - await client.sendAndConfirmTransactionForGroup(group, [ix]); + await sendSignAndConfirmTransactions({ + connection: userProvider.connection, + wallet: userWallet, + transactionInstructions: [ + { + instructionsSet: [ + new TransactionInstructionWithSigners( + createComputeBudgetIx(200000), + ), + new TransactionInstructionWithSigners(ix), + ], + sequenceType: SequenceType.Sequential, + }, + ], + backupConnections: [...backups], + config: { + maxTxesInBatch: 2, + autoRetry: true, + logFlowInfo: true, + }, + }); } } catch (e) { console.log(e); @@ -166,7 +198,27 @@ const main = async () => { toPubkey: new PublicKey(row.owner), lamports: toNative(amount, 9).toNumber(), }); - await client.sendAndConfirmTransactionForGroup(group, [ix]); + await sendSignAndConfirmTransactions({ + connection: userProvider.connection, + wallet: userWallet, + transactionInstructions: [ + { + instructionsSet: [ + new TransactionInstructionWithSigners( + createComputeBudgetIx(200000), + ), + new TransactionInstructionWithSigners(ix), + ], + sequenceType: SequenceType.Sequential, + }, + ], + backupConnections: [...backups], + config: { + maxTxesInBatch: 2, + autoRetry: true, + logFlowInfo: true, + }, + }); } } else { console.log('Invalid PublicKey: ', row.mango_account); From 92baed42e3482e13025f08bf30aa4451c6d0cd4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Brzezin=CC=81ski?= Date: Wed, 23 Oct 2024 22:23:02 +0200 Subject: [PATCH 6/6] spl reimbrusment --- ts/client/scripts/reimbursementSplTokens.ts | 256 ++++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 ts/client/scripts/reimbursementSplTokens.ts diff --git a/ts/client/scripts/reimbursementSplTokens.ts b/ts/client/scripts/reimbursementSplTokens.ts new file mode 100644 index 000000000..3a0445533 --- /dev/null +++ b/ts/client/scripts/reimbursementSplTokens.ts @@ -0,0 +1,256 @@ +import { + ComputeBudgetProgram, + Connection, + Keypair, + PublicKey, +} from '@solana/web3.js'; +import fs from 'fs'; +import * as path from 'path'; +import { parse } from 'csv-parse'; +import { AnchorProvider, BN, Wallet } from '@coral-xyz/anchor'; +import { + createComputeBudgetIx, + MANGO_V4_ID, + MangoClient, + toNative, + USDC_MINT, +} from '../src'; +import { WRAPPED_SOL_MINT } from '@project-serum/serum/lib/token-instructions'; +import { sendSignAndConfirmTransactions } from '@blockworks-foundation/mangolana/lib/transactions'; +import { + SequenceType, + TransactionInstructionWithSigners, +} from '@blockworks-foundation/mangolana/lib/globalTypes'; +import { + createAssociatedTokenAccountIdempotentInstruction, + createTransferInstruction, + getAssociatedTokenAddressSync, +} from '@solana/spl-token'; + +const MANGO_MAINNET_GROUP = new PublicKey( + '78b8f4cGCwmZ9ysPFMWLaLTkkaYnUjwMJYStWe5RTSSX', +); + +type Reimbursement = { + mango_account: string; + owner: string; + mangoSOL: number; + MOTHER: number; + SOL: number; + USDC: number; + Notional: string; +}; + +const setupWallet = () => { + const user = Keypair.fromSecretKey( + Buffer.from( + JSON.parse( + fs.readFileSync('keypair.json', { + encoding: 'utf-8', + }), + ), + ), + ); + + return user; +}; + +const readCsv = async () => { + const csvFilePath = path.resolve(__dirname, 'reimbursement.csv'); + + const headers = [ + 'mango_account', + 'owner', + 'mangoSOL', + 'MOTHER', + 'SOL', + 'USDC', + 'Notional', + ]; + + return new Promise((resolve, reject) => { + const fileContent = fs.readFileSync(csvFilePath, { encoding: 'utf-8' }); + + parse( + fileContent, + { + delimiter: ',', + columns: headers, + }, + (error, result: Reimbursement[]) => { + if (error) { + reject(error); + } else { + const resp = result.slice(1, result.length); + resolve(resp); + } + }, + ); + }); +}; + +const tryGetPubKey = (pubkey: string | string[]) => { + try { + return new PublicKey(pubkey); + } catch (e) { + console.log(e); + return null; + } +}; + +const sendTokenDeposit = ( + owner: string, + wallet: Wallet, + nativeAmount: BN, + connection: Connection, +) => { + const userAta = getAssociatedTokenAddressSync( + USDC_MINT, + new PublicKey(owner), + true, + ); + const myAta = getAssociatedTokenAddressSync( + USDC_MINT, + wallet.publicKey, + true, + ); + const createAtaIx = createAssociatedTokenAccountIdempotentInstruction( + wallet.publicKey, + userAta, + new PublicKey(owner), + USDC_MINT, + ); + const sendIx = createTransferInstruction( + myAta, + userAta, + wallet.publicKey, + nativeAmount.toNumber(), + ); + return sendSignAndConfirmTransactions({ + connection: connection, + wallet: wallet, + transactionInstructions: [ + { + instructionsSet: [ + new TransactionInstructionWithSigners( + ComputeBudgetProgram.setComputeUnitLimit({ + units: 40000, + }), + ), + new TransactionInstructionWithSigners(createComputeBudgetIx(2000000)), + new TransactionInstructionWithSigners(createAtaIx), + new TransactionInstructionWithSigners(sendIx), + ], + sequenceType: SequenceType.Sequential, + }, + ], + backupConnections: [...backups], + config: { + maxTxesInBatch: 2, + autoRetry: true, + logFlowInfo: true, + }, + }); +}; + +const mints = { + mangoSOL: new PublicKey('MangmsBgFqJhW4cLUR9LxfVgMboY1xAoP8UUBiWwwuY'), + MOTHER: new PublicKey('3S8qX1MsMqRbiwKg2cQyx7nis1oHMgaCuc9c4VfvVdPN'), + SOL: WRAPPED_SOL_MINT, + USDC: USDC_MINT, +}; +const backups = [new Connection(''), new Connection('')]; + +const main = async () => { + const user = await setupWallet(); + const mainConnection = new Connection(''); + const backupConnections = backups; + const options = AnchorProvider.defaultOptions(); + const userWallet = new Wallet(user); + const userProvider = new AnchorProvider(mainConnection, userWallet, options); + + const client = await MangoClient.connect( + userProvider, + 'mainnet-beta', + MANGO_V4_ID['mainnet-beta'], + { + idsSource: 'api', + multipleConnections: backupConnections, + prioritizationFee: 2000000, + }, + ); + console.log(userWallet.publicKey.toBase58(), '@@@@@'); + const group = await client.getGroup(MANGO_MAINNET_GROUP); + + const csvData = await readCsv(); + + const TO_PROCESS = csvData; + const TOKEN = 'SOL'; + + const notReimbursedMangoAccounts: string[] = []; + for (const row of TO_PROCESS) { + const mangoAccountPk = tryGetPubKey(row.mango_account); + + if (mangoAccountPk) { + const mint = mints[TOKEN as keyof typeof mints]; + const amount = Number(row[TOKEN as keyof typeof mints]); + const decimals = group.getMintDecimals(mint); + const nativeAmount = toNative(amount, decimals); + try { + if (mint && amount > 0.05) { + const mangoAccount = await client.getMangoAccount(mangoAccountPk); + console.log('Mango Account exists'); + console.log( + `Start reimbursing ${mint.toBase58()} ${amount} ${ + row.mango_account + }`, + ); + try { + const signature = await client.tokenDepositNative( + group, + mangoAccount, + mint, + nativeAmount, + false, + true, + ); + + console.log( + 'Reimburse end ', + signature.signature, + signature.confirmationStatus, + signature.err, + ); + if (!signature.err) { + console.log('OK'); + } else { + await sendTokenDeposit( + row.owner, + userWallet, + nativeAmount, + userProvider.connection, + ); + } + } catch (e) { + console.log(e); + notReimbursedMangoAccounts.push(row.mango_account); + } + } + } catch (e) { + console.log('Mango account not exists', e); + await sendTokenDeposit( + row.owner, + userWallet, + nativeAmount, + userProvider.connection, + ); + } + } else { + console.log('Invalid PublicKey: ', row.mango_account); + throw 'Invalid PublicKey'; + } + } + console.log(notReimbursedMangoAccounts); +}; + +main();