Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -224,3 +224,6 @@ broadcast/
node_modules/
target/
token-swap/sutsaKhPL3nMSPtvRY3e9MbpmqQbEJip6vYqT9AQcgN.json

.anchor
test-ledger
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"@coral-xyz/anchor": "^0.27.0",
"@ethersproject/units": "^5.7.0",
"@improbable-eng/grpc-web-node-http-transport": "^0.15.0",
"@jup-ag/core": "^4.0.0-beta.18",
"@jup-ag/api": "^6.0.10",
"@solana/spl-token": "^0.3.8",
"@solana/web3.js": "^1.76.0",
"bs58": "^5.0.0",
Expand All @@ -17,10 +17,12 @@
"@coral-xyz/anchor-cli": "^0.27.0",
"@types/bn.js": "^5.1.0",
"@types/chai": "^4.3.0",
"@types/chai-as-promised": "^7.1.8",
"@types/mocha": "^9.0.0",
"@types/node": "^20.2.3",
"@types/node-fetch": "^2.6.4",
"chai": "^4.3.4",
"chai-as-promised": "^7.1.1",
"dotenv": "^16.0.3",
"mocha": "10.1.0",
"prettier": "^2.6.2",
Expand Down Expand Up @@ -53,4 +55,4 @@
},
"homepage": "https://github.com/sunrise-stake/offset-bridge#readme",
"description": ""
}
}
3 changes: 2 additions & 1 deletion scripts/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ export const USER_KEYPAIR =

export const PROGRAM_ID = new PublicKey("sutsaKhPL3nMSPtvRY3e9MbpmqQbEJip6vYqT9AQcgN");
export const STATE_ADDRESS = new PublicKey("FhVZksvDo2dFUCoqEwqv8idS9i4FtQ97amkcJ1d4MHS5");

export const TEST_STATE_KEYPAIR =
process.env.SOLANA_PRIVATE_KEY ? Keypair.fromSecretKey(bs58.decode(process.env.SOLANA_PRIVATE_KEY)) : Keypair.fromSecretKey(Buffer.from(require('../token-swap/tests/fixtures/test_state_account.json')));
// Wormhole
export const POLYGON_TEST_TOKEN_BRIDGE_ADDRESS = "0x377D55a7928c046E18eEbb61977e714d2a76472a";
export const POLYGON_TEST_BRIDGE_ADDRESS = "0x0CBE91CF822c73C2315FB05100C2F714765d5c20";
Expand Down
68 changes: 68 additions & 0 deletions scripts/jupiterDirectApiV6.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import fetch from 'node-fetch';
import {
Connection,
PublicKey,
} from '@solana/web3.js';
import {WRAPPED_SOL_TOKEN_MINT, BRIDGE_INPUT_MINT_ADDRESS, SOLANA_RPC_ENDPOINT, USER_KEYPAIR} from "./constants";

const API_ENDPOINT = "https://quote-api.jup.ag/v6";

const getQuote = async (
fromMint: PublicKey,
toMint: PublicKey,
amount: number
) => {
return fetch(
`${API_ENDPOINT}/quote?outputMint=${toMint.toBase58()}&inputMint=${fromMint.toBase58()}&amount=${amount}&maxAccounts=30&slippage=0.01&onlyDirectRoutes=false`
).then((response) => response.json());
};

const executeSwap = async (
user: PublicKey,
quote: any
) => {
const data = {
quoteResponse: quote,
userPublicKey: user.toBase58(),
};
return fetch(`${API_ENDPOINT}/swap`, {
method: "POST",
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify(data),
}).then((response) => response.json());
};

(async () => {
console.log("start");
// Setup Solana RPC connection
const connection = new Connection(SOLANA_RPC_ENDPOINT);
console.log(WRAPPED_SOL_TOKEN_MINT);
console.log(BRIDGE_INPUT_MINT_ADDRESS);

const quote = await getQuote(new PublicKey(WRAPPED_SOL_TOKEN_MINT), new PublicKey(BRIDGE_INPUT_MINT_ADDRESS), 1000000);
console.log({ quote });

// Routes are sorted based on outputAmount, so ideally the first route is the best.

quote.routePlan.forEach((r) => {
console.log({r});
})

// Execute swap
const swapResult = await executeSwap(USER_KEYPAIR.publicKey, quote);

if (swapResult.error) {
console.log(swapResult.error);
} else {
console.log(`https://explorer.solana.com/tx/${swapResult.txid}`);
console.log(`inputAddress=${swapResult.inputAddress} outputAddress=${swapResult.outputAddress}`);
console.log(`inputAmount=${swapResult.inputAmount} outputAmount=${swapResult.outputAmount}`);
}
})().catch((error) => {
console.error(error);
process.exit(1);

});
245 changes: 245 additions & 0 deletions scripts/swapJupiterApiV6.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@

import fetch from 'node-fetch';
import fs from "fs";
import JSBI from 'jsbi';
import {
AddressLookupTableAccount,
clusterApiUrl,
Connection,
PublicKey,
Keypair,
TransactionMessage,
TransactionInstruction,
Transaction,
VersionedTransaction,
Blockhash,
AccountInfo
} from '@solana/web3.js';
import { PROGRAM_ID } from './constants';
import {
ASSOCIATED_TOKEN_PROGRAM_ID,
Account,
TOKEN_PROGRAM_ID,
getAssociatedTokenAddress,
getOrCreateAssociatedTokenAccount,
} from "@solana/spl-token";
import {Program, AnchorProvider, Wallet, Instruction} from "@coral-xyz/anchor";
// import { Jupiter, RouteInfo, TOKEN_LIST_URL } from '@jup-ag/core';
import {IDL, TokenSwap} from "./types/token_swap";
import {ENV, WRAPPED_SOL_TOKEN_MINT, BRIDGE_INPUT_MINT_ADDRESS, SOLANA_RPC_ENDPOINT, JupiterToken, USER_KEYPAIR} from "./constants";
import { getQuote, getSwapIx, swapToBridgeInputTx, getJupiterSwapIx, tokenAuthority } from './util';
import { TEST_STATE_ADDRESS, testTokenAuthority } from "./util";

const API_ENDPOINT = "https://quote-api.jup.ag/v6";
const JUPITER_PROGRAM_ID = "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4";
const SOL_USDC_PRICEFEED_ID = new PublicKey("H6ARHf6YXhGYeQfUzQNGk6rDNnLBQKrenN712K4AQJEG");

(async () => {
console.log("start");
// Setup Solana RPC connection
// const connection = new Connection(SOLANA_RPC_ENDPOINT);
const connection = new Connection(clusterApiUrl("mainnet-beta"));
const provider = new AnchorProvider(connection, new Wallet(USER_KEYPAIR), {});
const program = new Program<TokenSwap>(IDL, PROGRAM_ID, provider);
console.log(WRAPPED_SOL_TOKEN_MINT);
console.log(BRIDGE_INPUT_MINT_ADDRESS);

const quote = await getQuote(new PublicKey(WRAPPED_SOL_TOKEN_MINT), new PublicKey(BRIDGE_INPUT_MINT_ADDRESS), 1000000, 18);
console.log({ quote });
fs.writeFile("token-swap/tests/fixtures/jupiter_quote.json", JSON.stringify(quote, null, 4), (err) => {
if (err) {
console.log("Error writing file:", err);
} else {
console.log("Successfully wrote file for jupiter quote.");
}
});
// Routes are sorted based on outputAmount, so ideally the first route is the best.

quote.routePlan.forEach((r) => {
console.log({r});
})


/*
const programATA = await getAssociatedTokenAddress(
new PublicKey(BRIDGE_INPUT_MINT_ADDRESS),
USER_KEYPAIR.publicKey
)
console.log(programATA);
const programATA2 = PublicKey.findProgramAddressSync(
[
USER_KEYPAIR.publicKey.toBuffer(),
TOKEN_PROGRAM_ID.toBuffer(),
(new PublicKey(BRIDGE_INPUT_MINT_ADDRESS)).toBuffer(),
],
ASSOCIATED_TOKEN_PROGRAM_ID
)[0];
console.log(programATA2);
console.log(USER_KEYPAIR.publicKey);
*/

const jupiterIx = await getSwapIx(testTokenAuthority, quote); //USER_KEYPAIR.publicKey, quote); // programATA,

console.log({ jupiterIx });

const {
computeBudgetInstructions, // The necessary instructions to setup the compute budget.
swapInstruction,
addressLookupTableAddresses, // The lookup table addresses that you can use if you are using versioned transaction.
} = jupiterIx;

const jupiterSwapIx = getJupiterSwapIx(swapInstruction);

console.log({ jupiterSwapIx });

fs.writeFile("token-swap/tests/fixtures/jupiter_swap_ix_data.json", JSON.stringify(jupiterSwapIx.data.toJSON(), null, 4), (err) => {
if (err) {
console.log("Error writing file:", err);
} else {
console.log("Successfully wrote file for jupiter swap ix data.");
}
});
const accountMetas = jupiterSwapIx.keys;
fs.writeFile("token-swap/tests/fixtures/account_metas.json", JSON.stringify(accountMetas, null, 4), (err) => {
if (err) {
console.log("Error writing file:", err);
} else {
console.log("Successfully wrote file for account_metas.");
}
});

console.log({ accountMetas });

// Find input and output ATAs of tokenAuthority
const tokenAuthorityAtaOut = await getOrCreateAssociatedTokenAccount(
connection,
USER_KEYPAIR,
new PublicKey(BRIDGE_INPUT_MINT_ADDRESS),
tokenAuthority,
true
);
const tokenAuthorityAtaIn = await getOrCreateAssociatedTokenAccount(
connection,
USER_KEYPAIR,
new PublicKey(WRAPPED_SOL_TOKEN_MINT),
tokenAuthority,
true
);
// create the swap instruction
const swapIx = await program.methods.swap(jupiterSwapIx.data).accounts({
state: TEST_STATE_ADDRESS,
tokenAccountAuthority: tokenAuthority,
inputMint: WRAPPED_SOL_TOKEN_MINT,
tokenAtaAddressIn: tokenAuthorityAtaIn.address,
outputMint: BRIDGE_INPUT_MINT_ADDRESS,
tokenAtaAddressOut: tokenAuthorityAtaOut.address,
jupiterProgram: JUPITER_PROGRAM_ID,
pythPriceFeedAccount: SOL_USDC_PRICEFEED_ID,
}).remainingAccounts([
...accountMetas
]).instruction()

const blockhash = await connection
.getLatestBlockhash()
.then((res) => res.blockhash);

const addressLookupTableAccountInfos = await connection.getMultipleAccountsInfo(
addressLookupTableAddresses.map((key) => new PublicKey(key))
);

const swapTx = swapToBridgeInputTx(
swapIx,
blockhash,
USER_KEYPAIR,
computeBudgetInstructions,
addressLookupTableAddresses,
addressLookupTableAccountInfos
);
console.log( {addressLookupTableAddresses} );
console.log( {addressLookupTableAccountInfos} );

// const price_feed_info = await connection.getAccountInfo(SOL_USDC_PRICEFEED_ID);
// console.log(price_feed_info.data);

fs.writeFile("token-swap/tests/fixtures/accounts_for_test_validator.txt", "", function(err) {
if (err) {
return console.error(err);
}
console.log("File created!");
});
console.log(USER_KEYPAIR.publicKey);
console.log(jupiterSwapIx.data);
for (let i = 0; i < addressLookupTableAddresses.length; i++) {
const alt = addressLookupTableAddresses[i];
const alt_info = addressLookupTableAccountInfos[i];
try {
const rentEpoch = new Number(alt_info.rentEpoch).toPrecision();
fs.writeFile(`token-swap/tests/fixtures/${alt}.json`, JSON.stringify({"pubkey": alt, "account": {
"lamports": alt_info.lamports,
"data": [alt_info.data.toString("base64"), "base64"],
"owner": alt_info.owner.toBase58(),
"executable": alt_info.executable,
"rentEpoch": 361,
"space": alt_info.data.length,
}}, null, 2), (err) => {
if (err) {
console.log("Error writing file:", err);
} else {
console.log("Successfully wrote file for account:", alt);
}

});
fs.appendFile("token-swap/tests/fixtures/accounts_for_test_validator.txt",
`[[test.validator.account]]\naddress = "${alt}" \nfilename = "tests/fixtures/${alt}.json"\n`, function (err) {
if (err) throw err;
console.log('Saved!');
});
} catch (e) {
console.log(alt);
console.log(alt_info);
console.log(e);
}
}
for (const account_meta of accountMetas) {
const account_meta_info = await connection.getAccountInfo(account_meta.pubkey);
try {
const rentEpoch = new Number(account_meta_info.rentEpoch).toPrecision();
fs.writeFile(`token-swap/tests/fixtures/${account_meta.pubkey.toBase58()}.json`, JSON.stringify({"pubkey": account_meta.pubkey.toBase58(), "account": {
"lamports": account_meta_info.lamports,
"data": [account_meta_info.data.toString("base64"), "base64"],
"owner": account_meta_info.owner.toBase58(),
"executable": account_meta_info.executable,
"rentEpoch": 361,
"space": account_meta_info.data.length,
}}, null, 2), (err) => {
if (err) {
console.log("Error writing file:", err);
} else {
console.log("Successfully wrote file for account:", account_meta.pubkey.toBase58());
}
});
fs.appendFile("token-swap/tests/fixtures/accounts_for_test_validator.txt",
`[[test.validator.account]]\naddress = "${account_meta.pubkey.toBase58()}" \nfilename = "tests/fixtures/${account_meta.pubkey.toBase58()}.json"\n`, function (err) {
if (err) throw err;
console.log('Saved!');
});
} catch (e) {
console.log(account_meta);
console.log(account_meta_info);
console.log(e);
}
};

await provider.simulate(swapTx, [new Wallet(USER_KEYPAIR).payer]);

const txID = await provider.sendAndConfirm(swapTx, [USER_KEYPAIR]);
console.log({ txID });
console.log(`https://explorer.solana.com/tx/${txID}`);
// } catch (e) {
// console.log({ simulationResponse: e.simulationResponse });
// }

})().catch((error) => {
console.error(error);
process.exit(1);
});
Loading