From 40a384dcfef8fc0578db95e3f5260ca210459579 Mon Sep 17 00:00:00 2001 From: John Mitsch Date: Thu, 21 Mar 2024 10:08:06 -0300 Subject: [PATCH] Update sendSmartTransaction logic --- packages/libs/sdk/package.json | 2 +- .../recording.har | 26 +++---- .../recording.har | 52 +++++++------- .../libs/sdk/spec/solana/solanaClient.test.ts | 67 ++++++++++++++++++- packages/libs/sdk/src/solana/solana.ts | 33 +++++---- 5 files changed, 126 insertions(+), 54 deletions(-) diff --git a/packages/libs/sdk/package.json b/packages/libs/sdk/package.json index 8febd252..7f35ddf7 100644 --- a/packages/libs/sdk/package.json +++ b/packages/libs/sdk/package.json @@ -6,7 +6,7 @@ "directory": "packages/libs/sdk" }, "license": "MIT", - "version": "2.2.1", + "version": "2.2.2", "main": "./cjs/index.js", "module": "./esm/index.js", "types": "./index.d.ts", diff --git a/packages/libs/sdk/spec/recordings/solana-client-prepareSmartTransaction_3623416322/recording.har b/packages/libs/sdk/spec/recordings/solana-client-prepareSmartTransaction_3623416322/recording.har index 31d6b12e..8db0430a 100644 --- a/packages/libs/sdk/spec/recordings/solana-client-prepareSmartTransaction_3623416322/recording.har +++ b/packages/libs/sdk/spec/recordings/solana-client-prepareSmartTransaction_3623416322/recording.har @@ -256,11 +256,11 @@ } }, { - "_id": "0875a10313acc94fe3ab671e0fd7b894", + "_id": "a9c1d0bd666a232cb0c3b05fe011d7b2", "_order": 0, "cache": {}, "request": { - "bodySize": 416, + "bodySize": 536, "cookies": [], "headers": [ { @@ -281,7 +281,7 @@ { "_fromType": "array", "name": "content-length", - "value": "416" + "value": "536" }, { "_fromType": "array", @@ -304,17 +304,17 @@ "postData": { "mimeType": "application/json", "params": [], - "text": "{\"method\":\"simulateTransaction\",\"jsonrpc\":\"2.0\",\"params\":[\"AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQABAoQRxzJRFnXCoe6MBldnTlUf1q55/eUmCQ4F8R2+2mMoAwZGb+UhFzL/7K26csOb57yM5bvF9xJrLEObOkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAAkDF5cAAAAAAAAA\",{\"replaceRecentBlockhash\":true,\"sigVerify\":false,\"encoding\":\"base64\"}],\"id\":\"3c3df6dd-e35b-41ed-af11-68882bb8fb00\"}" + "text": "{\"method\":\"simulateTransaction\",\"jsonrpc\":\"2.0\",\"params\":[\"AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQACBIQRxzJRFnXCoe6MBldnTlUf1q55/eUmCQ4F8R2+2mMoZN5JCQC+0RqwBLQdgIRoFf0jzu/aVfQUwfhfDiCPBDMDBkZv5SEXMv/srbpyw5vnvIzlu8X3EmssQ5s6QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAgAJAwEAAAAAAAAAAgAFAsBcFQADAgABDAIAAABkAAAAAAAAAAA=\",{\"replaceRecentBlockhash\":true,\"sigVerify\":false,\"encoding\":\"base64\"}],\"id\":\"64f37774-8bf8-4eec-84cf-3d85400ab094\"}" }, "queryString": [], "url": "https://alien-lingering-fire.solana-mainnet.quiknode.pro" }, "response": { - "bodySize": 221, + "bodySize": 568, "content": { "mimeType": "application/json", - "size": 221, - "text": "{\"jsonrpc\":\"2.0\",\"result\":{\"context\":{\"apiVersion\":\"1.17.26\",\"slot\":255238437},\"value\":{\"accounts\":null,\"err\":\"AccountNotFound\",\"logs\":[],\"returnData\":null,\"unitsConsumed\":0}},\"id\":\"3c3df6dd-e35b-41ed-af11-68882bb8fb00\"}\n" + "size": 568, + "text": "{\"jsonrpc\":\"2.0\",\"result\":{\"context\":{\"apiVersion\":\"1.17.26\",\"slot\":255550502},\"value\":{\"accounts\":null,\"err\":null,\"logs\":[\"Program ComputeBudget111111111111111111111111111111 invoke [1]\",\"Program ComputeBudget111111111111111111111111111111 success\",\"Program ComputeBudget111111111111111111111111111111 invoke [1]\",\"Program ComputeBudget111111111111111111111111111111 success\",\"Program 11111111111111111111111111111111 invoke [1]\",\"Program 11111111111111111111111111111111 success\"],\"returnData\":null,\"unitsConsumed\":450}},\"id\":\"64f37774-8bf8-4eec-84cf-3d85400ab094\"}\n" }, "cookies": [], "headers": [ @@ -344,7 +344,7 @@ }, { "name": "x-host-id", - "value": "6f1b845d154ece71" + "value": "b4731fe0f29c1620" }, { "name": "x-node-id", @@ -352,11 +352,11 @@ }, { "name": "date", - "value": "Wed, 20 Mar 2024 00:37:00 GMT" + "value": "Thu, 21 Mar 2024 13:06:20 GMT" }, { "name": "content-length", - "value": "221" + "value": "568" } ], "headersSize": 386, @@ -365,8 +365,8 @@ "status": 200, "statusText": "OK" }, - "startedDateTime": "2024-03-20T00:36:59.488Z", - "time": 811, + "startedDateTime": "2024-03-21T13:06:20.606Z", + "time": 132, "timings": { "blocked": -1, "connect": -1, @@ -374,7 +374,7 @@ "receive": 0, "send": 0, "ssl": -1, - "wait": 811 + "wait": 132 } } ], diff --git a/packages/libs/sdk/spec/recordings/solana-client-sendSmartTransaction_2450286823/recording.har b/packages/libs/sdk/spec/recordings/solana-client-sendSmartTransaction_2450286823/recording.har index 7ebbd0c2..2a2c223d 100644 --- a/packages/libs/sdk/spec/recordings/solana-client-sendSmartTransaction_2450286823/recording.har +++ b/packages/libs/sdk/spec/recordings/solana-client-sendSmartTransaction_2450286823/recording.har @@ -256,11 +256,11 @@ } }, { - "_id": "35244a6aa4ac8734325efac21609ad9c", + "_id": "a9c1d0bd666a232cb0c3b05fe011d7b2", "_order": 0, "cache": {}, "request": { - "bodySize": 385, + "bodySize": 536, "cookies": [], "headers": [ { @@ -281,7 +281,7 @@ { "_fromType": "array", "name": "content-length", - "value": "385" + "value": "536" }, { "_fromType": "array", @@ -304,17 +304,17 @@ "postData": { "mimeType": "application/json", "params": [], - "text": "{\"method\":\"sendTransaction\",\"jsonrpc\":\"2.0\",\"params\":[\"Aci82pcPwf31MTJc0UmBGMTxOqk+P6VfM6Wjv1AEzTGIWmsLgz8ABK+bV8ocMEZV2kloXTXnWbu5qO45o5O8AAEBAAEChBHHMlEWdcKh7owGV2dOVR/Wrnn95SYJDgXxHb7aYygDBkZv5SEXMv/srbpyw5vnvIzlu8X3EmssQ5s6QAAAAAHLaYBE/pnlByh2qLtexxJv4xuqEN/9aSFyVZcWLYn+AQEACQNVkgAAAAAAAA==\",{\"encoding\":\"base64\",\"skipPreflight\":true}],\"id\":\"52b1dbab-07d1-4c3f-a224-c526c54e5aa6\"}" + "text": "{\"method\":\"simulateTransaction\",\"jsonrpc\":\"2.0\",\"params\":[\"AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQACBIQRxzJRFnXCoe6MBldnTlUf1q55/eUmCQ4F8R2+2mMoZN5JCQC+0RqwBLQdgIRoFf0jzu/aVfQUwfhfDiCPBDMDBkZv5SEXMv/srbpyw5vnvIzlu8X3EmssQ5s6QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAgAJAwEAAAAAAAAAAgAFAsBcFQADAgABDAIAAABkAAAAAAAAAAA=\",{\"replaceRecentBlockhash\":true,\"sigVerify\":false,\"encoding\":\"base64\"}],\"id\":\"49f204d0-cc20-45cc-b4e6-8b3f6823e0e1\"}" }, "queryString": [], "url": "https://alien-lingering-fire.solana-mainnet.quiknode.pro" }, "response": { - "bodySize": 162, + "bodySize": 568, "content": { "mimeType": "application/json", - "size": 162, - "text": "{\"jsonrpc\":\"2.0\",\"result\":\"51n3WTQdDFiergup4SMrS3A3mgD16mMH5DJ2Fs1v7F4AGRsnd8XQmTnfCcFcQ9JTMfb36bX2ib1mJGxJkUV75XmJ\",\"id\":\"52b1dbab-07d1-4c3f-a224-c526c54e5aa6\"}\n" + "size": 568, + "text": "{\"jsonrpc\":\"2.0\",\"result\":{\"context\":{\"apiVersion\":\"1.17.26\",\"slot\":255550502},\"value\":{\"accounts\":null,\"err\":null,\"logs\":[\"Program ComputeBudget111111111111111111111111111111 invoke [1]\",\"Program ComputeBudget111111111111111111111111111111 success\",\"Program ComputeBudget111111111111111111111111111111 invoke [1]\",\"Program ComputeBudget111111111111111111111111111111 success\",\"Program 11111111111111111111111111111111 invoke [1]\",\"Program 11111111111111111111111111111111 success\"],\"returnData\":null,\"unitsConsumed\":450}},\"id\":\"49f204d0-cc20-45cc-b4e6-8b3f6823e0e1\"}\n" }, "cookies": [], "headers": [ @@ -344,7 +344,7 @@ }, { "name": "x-host-id", - "value": "7b452f0ca022e7f9" + "value": "f337ac3cc69260b7" }, { "name": "x-node-id", @@ -352,11 +352,11 @@ }, { "name": "date", - "value": "Tue, 19 Mar 2024 14:22:30 GMT" + "value": "Thu, 21 Mar 2024 13:06:20 GMT" }, { "name": "content-length", - "value": "162" + "value": "568" } ], "headersSize": 386, @@ -365,8 +365,8 @@ "status": 200, "statusText": "OK" }, - "startedDateTime": "2024-03-19T14:22:29.981Z", - "time": 131, + "startedDateTime": "2024-03-21T13:06:19.354Z", + "time": 861, "timings": { "blocked": -1, "connect": -1, @@ -374,15 +374,15 @@ "receive": 0, "send": 0, "ssl": -1, - "wait": 131 + "wait": 861 } }, { - "_id": "220ca660d418a27720eee2415340c125", + "_id": "66d0602d2284cff967b7351a80606c49", "_order": 0, "cache": {}, "request": { - "bodySize": 416, + "bodySize": 501, "cookies": [], "headers": [ { @@ -403,7 +403,7 @@ { "_fromType": "array", "name": "content-length", - "value": "416" + "value": "501" }, { "_fromType": "array", @@ -426,17 +426,17 @@ "postData": { "mimeType": "application/json", "params": [], - "text": "{\"method\":\"simulateTransaction\",\"jsonrpc\":\"2.0\",\"params\":[\"AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQABAoQRxzJRFnXCoe6MBldnTlUf1q55/eUmCQ4F8R2+2mMoAwZGb+UhFzL/7K26csOb57yM5bvF9xJrLEObOkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAAkDVZIAAAAAAAAA\",{\"replaceRecentBlockhash\":true,\"sigVerify\":false,\"encoding\":\"base64\"}],\"id\":\"e1e3d1a4-e828-41e7-983e-7855e576ae38\"}" + "text": "{\"method\":\"sendTransaction\",\"jsonrpc\":\"2.0\",\"params\":[\"ARrJaBs8lEzerP4Fr1E5PHngN0ytBeme+74eseLnGVIJ6EuSQ6TJwWuPrgUohpP5ltWFDOXOlJyTB1AmN3cw0ggBAAIEhBHHMlEWdcKh7owGV2dOVR/Wrnn95SYJDgXxHb7aYyhk3kkJAL7RGrAEtB2AhGgV/SPO79pV9BTB+F8OII8EMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwZGb+UhFzL/7K26csOb57yM5bvF9xJrLEObOkAAAAABy2mARP6Z5Qcodqi7XscSb+MbqhDf/WkhclWXFi2J/gMCAgABDAIAAABkAAAAAAAAAAMACQNVkgAAAAAAAAMABQLZAQAA\",{\"encoding\":\"base64\",\"skipPreflight\":true}],\"id\":\"d99246fb-e434-4a41-a64e-fa1b022d8969\"}" }, "queryString": [], "url": "https://alien-lingering-fire.solana-mainnet.quiknode.pro" }, "response": { - "bodySize": 221, + "bodySize": 161, "content": { "mimeType": "application/json", - "size": 221, - "text": "{\"jsonrpc\":\"2.0\",\"result\":{\"context\":{\"apiVersion\":\"1.17.26\",\"slot\":255238517},\"value\":{\"accounts\":null,\"err\":\"AccountNotFound\",\"logs\":[],\"returnData\":null,\"unitsConsumed\":0}},\"id\":\"e1e3d1a4-e828-41e7-983e-7855e576ae38\"}\n" + "size": 161, + "text": "{\"jsonrpc\":\"2.0\",\"result\":\"Y4bnQNsHzWshsquWZcQ88Lq7hKpYFkfd27GvTgizmZpEbtEQdgPs4TNhZ4qvMSUYsTLFBJckjqXg7aLg3GFHcQf\",\"id\":\"d99246fb-e434-4a41-a64e-fa1b022d8969\"}\n" }, "cookies": [], "headers": [ @@ -466,7 +466,7 @@ }, { "name": "x-host-id", - "value": "6cd8c45b4aaa6609" + "value": "6f1b845d154ece71" }, { "name": "x-node-id", @@ -474,11 +474,11 @@ }, { "name": "date", - "value": "Wed, 20 Mar 2024 00:37:33 GMT" + "value": "Thu, 21 Mar 2024 13:06:20 GMT" }, { "name": "content-length", - "value": "221" + "value": "161" } ], "headersSize": 386, @@ -487,8 +487,8 @@ "status": 200, "statusText": "OK" }, - "startedDateTime": "2024-03-20T00:37:32.998Z", - "time": 471, + "startedDateTime": "2024-03-21T13:06:20.245Z", + "time": 222, "timings": { "blocked": -1, "connect": -1, @@ -496,7 +496,7 @@ "receive": 0, "send": 0, "ssl": -1, - "wait": 471 + "wait": 222 } } ], diff --git a/packages/libs/sdk/spec/solana/solanaClient.test.ts b/packages/libs/sdk/spec/solana/solanaClient.test.ts index eb2d8642..00b1a3e8 100644 --- a/packages/libs/sdk/spec/solana/solanaClient.test.ts +++ b/packages/libs/sdk/spec/solana/solanaClient.test.ts @@ -1,5 +1,10 @@ import { solana } from './client'; -import { Transaction, Keypair } from '@solana/web3.js'; +import { + Transaction, + Keypair, + SystemProgram, + PublicKey, +} from '@solana/web3.js'; import withPolly from '../testSetup/pollyTestSetup'; describe('solana client', () => { @@ -37,11 +42,20 @@ describe('solana client', () => { }, async () => { const transaction = new Transaction(); + transaction.add( + SystemProgram.transfer({ + fromPubkey: keyPair.publicKey, + toPubkey: new PublicKey( + '7nkMt6a2VQE86LFp5uPKfjvboNtTFbGe755fkrTUA2Nv' + ), + lamports: 100, + }) + ); const feeLevel = 'medium'; await expect( solana.sendSmartTransaction({ transaction, keyPair, feeLevel }) ).resolves.toMatchInlineSnapshot( - `"51n3WTQdDFiergup4SMrS3A3mgD16mMH5DJ2Fs1v7F4AGRsnd8XQmTnfCcFcQ9JTMfb36bX2ib1mJGxJkUV75XmJ"` + `"Y4bnQNsHzWshsquWZcQ88Lq7hKpYFkfd27GvTgizmZpEbtEQdgPs4TNhZ4qvMSUYsTLFBJckjqXg7aLg3GFHcQf"` ); } ); @@ -53,6 +67,15 @@ describe('solana client', () => { }, async () => { const transaction = new Transaction(); + transaction.add( + SystemProgram.transfer({ + fromPubkey: keyPair.publicKey, + toPubkey: new PublicKey( + '7nkMt6a2VQE86LFp5uPKfjvboNtTFbGe755fkrTUA2Nv' + ), + lamports: 100, + }) + ); const feeLevel = 'medium'; await expect( solana.prepareSmartTransaction({ @@ -64,6 +87,35 @@ describe('solana client', () => { Object { "feePayer": null, "instructions": Array [ + Object { + "data": Array [ + 2, + 0, + 0, + 0, + 100, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "keys": Array [ + Object { + "isSigner": true, + "isWritable": true, + "pubkey": "9tYYGith1uMmFgWeUD7mouq7JaUhzvDSBYd9aZ25JEFZ", + }, + Object { + "isSigner": false, + "isWritable": true, + "pubkey": "7nkMt6a2VQE86LFp5uPKfjvboNtTFbGe755fkrTUA2Nv", + }, + ], + "programId": "11111111111111111111111111111111", + }, Object { "data": Array [ 3, @@ -79,6 +131,17 @@ describe('solana client', () => { "keys": Array [], "programId": "ComputeBudget111111111111111111111111111111", }, + Object { + "data": Array [ + 2, + 217, + 1, + 0, + 0, + ], + "keys": Array [], + "programId": "ComputeBudget111111111111111111111111111111", + }, ], "nonceInfo": null, "recentBlockhash": "2acCVCzy5dPndawWVvTYDoHWqoe4dXo1srhWQ4W5btnb", diff --git a/packages/libs/sdk/src/solana/solana.ts b/packages/libs/sdk/src/solana/solana.ts index 6d50aa99..50f3d4ae 100644 --- a/packages/libs/sdk/src/solana/solana.ts +++ b/packages/libs/sdk/src/solana/solana.ts @@ -59,24 +59,32 @@ export class Solana { */ async prepareSmartTransaction(args: PrepareSmartTransactionArgs) { const { transaction, payerPublicKey, feeLevel = 'medium' } = args; - // Need to fetch this ahead of time and add to transaction so it's in the transaction instructions - // for the simulation - const computeUnitPriceInstruction = - await this.createDynamicPriorityFeeInstruction(feeLevel); - const allInstructions = [ + + // Send simulation with placeholders so the value calculated is accurate + // placeholders kept low to avoid InsufficientFundsForFee error with the high cu budget limit + const simulationInstructions = [ + ComputeBudgetProgram.setComputeUnitPrice({ + microLamports: 1, + }), + ComputeBudgetProgram.setComputeUnitLimit({ units: 1_400_000 }), ...transaction.instructions, - computeUnitPriceInstruction, ]; // eslint-disable-next-line prefer-const - let [units, recentBlockhash] = await Promise.all([ - this.getSimulationUnits(this.connection, allInstructions, payerPublicKey), - this.connection.getLatestBlockhash(), - ]); + let [units, computeUnitPriceInstruction, recentBlockhash] = + await Promise.all([ + this.getSimulationUnits( + this.connection, + simulationInstructions, + payerPublicKey + ), + this.createDynamicPriorityFeeInstruction(feeLevel), + this.connection.getLatestBlockhash(), + ]); transaction.add(computeUnitPriceInstruction); if (units) { - units = Math.ceil(units * 1.1); // margin of error + units = Math.ceil(units * 1.05); // margin of error transaction.add(ComputeBudgetProgram.setComputeUnitLimit({ units })); } transaction.recentBlockhash = recentBlockhash.blockhash; @@ -145,7 +153,8 @@ export class Solana { sigVerify: false, }); if (simulation.value.err) { - return undefined; + console.error('Simulation error:', simulation.value.err); + throw new Error(`Failed to simulate transaction ${simulation.value.err}`); } return simulation.value.unitsConsumed; }