-
-
Save sevazhidkov/867580170dd81c8eb8fed7801a6447d5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const axios = require('axios').default; | |
const { Keypair, PublicKey, Connection, Transaction, sendAndConfirmRawTransaction, LAMPORTS_PER_SOL } = require('@solana/web3.js'); | |
const { | |
createMint, getOrCreateAssociatedTokenAccount, Account, | |
mintTo, createTransferInstruction, createAccount, getAccount, | |
} = require('@solana/spl-token'); | |
const base58 = require('bs58'); | |
const baseURL = 'https://octane-devnet.breakroom.show/api'; | |
async function main() { | |
const connection = new Connection('https://api.devnet.solana.com', 'confirmed'); | |
// (1) Preparation | |
// We need some SPL tokens to pay for the transaction fees and rent instead of SOL. | |
// On mainnet, we'll enable paying fees with popular and liquid tokens (like USDC). | |
// On devnet, 'octane-devnet.breakroom.show' endpoint will accept any token. | |
// It makes it easier to debug and run e2e tests. However, you'll need to | |
// pass overrideMint and overrideAccount body params to specify what token and token destination should octane expect. | |
// Additionally, we ask you to top up the our SOL balances to make sure our fee payer has enough SOL to pay the fees. | |
// Let's now create a new token that would be used as payment to octane node. We'll need some SOL to create a new token. | |
const tokenKeypair = Keypair.generate(); | |
const airdropSignature = await connection.requestAirdrop( | |
tokenKeypair.publicKey, LAMPORTS_PER_SOL, | |
); | |
await connection.confirmTransaction(airdropSignature); | |
const mint = await createMint(connection, tokenKeypair, tokenKeypair.publicKey, null, 9, undefined, {commitment: 'confirmed'}); | |
// Now, we should create an account that Octane will use to receive payments. | |
// On mainnet, this account will be already created by node owner (in this case, Breakroom) for each accepted token. | |
const tokenAccount = await getOrCreateAssociatedTokenAccount( | |
connection, | |
tokenKeypair, | |
mint, | |
tokenKeypair.publicKey, | |
undefined, | |
'confirmed', | |
{commitment: 'confirmed'} | |
); | |
// Let's now connect to Octane instance and get some needed params: | |
// 1) how much we should pay for transaction in tokens | |
// 2) which account will pay for transaction in SOL (feePayer) | |
// Normally, you should also learn which kind of tokens supported and | |
// get price in each of them, but devnet endpoint accepts any token through | |
// config overrides. | |
const response = (await axios.get(baseURL, { | |
headers: {'Accept': 'application/json'} | |
})).data; | |
const feePayer = new PublicKey(response.feePayer); | |
const simpleTransactionFee = response.endpoints.transfer.tokens[0].fee; | |
const newAccountFee = response.endpoints.createAccount.tokens[0].fee; // how much it costs to create a new associated token account | |
// Only on devnet: add some SOL to octane's fee payer, since it's a shared resource. | |
// On mainnet, this would taken care by node owner. | |
const airdropSignature2 = await connection.requestAirdrop( | |
feePayer, LAMPORTS_PER_SOL | |
); | |
await connection.confirmTransaction(airdropSignature2); | |
// (2) Prepare the hypothetical end user account | |
// We shouldn't airdrop any SOL to this keypair to prove fee paying works | |
endUserKeypair = Keypair.generate(); | |
endUserTokenAccount = await createAccount(connection, tokenKeypair, mint, endUserKeypair.publicKey, undefined, {commitment: 'confirmed'}); | |
// However, the user still needs some tokens to pay for the transaction. | |
await mintTo(connection, tokenKeypair, mint, endUserTokenAccount, tokenKeypair.publicKey, 1e9, undefined, {commitment: 'confirmed'}); | |
// (3) Create the transaction — we'll transfer some tokens to targetKeypair as our first gasless transaction | |
const targetKeypair = Keypair.generate(); | |
const targetAccount = await createAccount(connection, tokenKeypair, mint, targetKeypair.publicKey, undefined, {commitment: 'confirmed'}); // for now, we assume it's already created | |
// The transaction would consist of two instructions: | |
// 1) token transfer to octane's token account as payment for running transaction gasless | |
// 2) the payload, actual instruction we want to execute. in this case, it's also a token transfer to targetKeypair, but it could be anything. | |
const transaction = new Transaction(); | |
transaction.add(createTransferInstruction(endUserTokenAccount, tokenAccount.address, endUserKeypair.publicKey, simpleTransactionFee)); | |
transaction.add(createTransferInstruction(endUserTokenAccount, targetAccount, endUserKeypair.publicKey, 100)); | |
transaction.feePayer = feePayer; | |
transaction.recentBlockhash = (await connection.getRecentBlockhash()).blockhash; | |
transaction.partialSign(endUserKeypair); // in client code, you should use wallet adapter instead | |
// (4) Send the transaction to octane to sign and attach signature to transaction | |
const octaneResponse = (await axios.post(baseURL + '/transfer', { | |
transaction: base58.encode(transaction.serialize({requireAllSignatures: false})), | |
overrideAccount: tokenAccount.address.toBase58(), | |
overrideMint: mint.toBase58() | |
})).data; | |
transaction.addSignature(feePayer, base58.decode(octaneResponse.signature)); | |
// (5) Send transaction to network | |
await sendAndConfirmRawTransaction(connection, transaction.serialize(), { commitment: 'confirmed' }); | |
console.log('Find on devnet explorer ///', octaneResponse.signature); | |
} | |
main(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment