In #solana/web3.js when you create a transaction instruction, you specify pubkeys of your accounts, program id, and just raw bytes of your "data" parameter. In anchor program you declare your module to be a program with corresponding attribute, and now your pub functions become instructions. I could not find in the anchor book, how specifically they serialize their instruction name. How do I specify for my JavaScript frontend which instruction do I want it to execute?
How do I specify for my JavaScript frontend which instruction I want it to execute?
Anchor uses IDL(Interface Description Language) for this purpose. whenever you complete your Solana program you can build it(anchor build). With this command, you have exported idl in root/target/idl folder. You can deploy this file to Solana network and fetch it and make a program in any client like ts(typescript) because of its mapping. You can open up one of the IDL.json files for better understanding. with this file, you can call instructions or use accounts of your Solana program.
Also, you have another file with ts extension inside root/target/types. We use this file inside anchor test for creating a program with that and also for specifying which instruction or account we want to use. Also, this file is useful for creating anchor programs inside clients. Because this file contains "export const IDL.....". So, we can use this file for creating program like this:
import { PROGRAM_ID } from "./constants";//program account public key
import { IDL } from "./your_directory_of_programs";// directory of copy/paste types/your_program.ts file
export function getProgramInstance(connection, wallet) {
if (!wallet.publicKey) return;
const provider = new anchor.AnchorProvider(
connection,
wallet,
anchor.AnchorProvider.defaultOptions()
);
// Read the generated IDL.
const idl = IDL;
// Address of the deployed program.
const programId = PROGRAM_ID;
// Generate the program client from IDL.
const program = new anchor.Program(idl, programId, provider);
return program;
}
and call any instruction like this:
await program.methods
.yourInstruction()
.accounts({
})
.signers()
.rpc();
Read this part of the Solana Cookbook for more details of what's going on when we want to call instruction of program from TS or any other clients.
Related
I have a pretty standard application with a frontend, a backend and some options in the frontend for modifying data. My backend fires events when data is modified (eg. record created, record updated, user logged in, etc.).
Now what I want to do is for my customers to be able to code their own functions and "hook" them into these events.
So far the approaches I have thought of are:
Allowing users in the frontend to write some code in a codeeditor like codemirror, but this whole storing code and executing it with some eval() seems kind of risky and unstable.
My second approach is illustrated below (to the best of my ability at least). The point is that the CRUD API calls a different "hook" web service that has these (recordUpdated, recordCreated, userLoggedIn,...) hook methods exposed. Then the client library needs to extend some predefined interfaces for the different hooks I expose. This still seems doable, but my issue is I can't figure out how my customers would deploy their library into the running "hook" service.
So it's kind of like webhooks, except I already know the exact hooks to be created which I figured could allow for an easier setup than customers having to create their own web services from scratch, but instead just create a library that is then deployed into an existing API (or something like that...). Preferably the infrastructure details should be hidden from the customers so they can focus solely on making business logic inside their custom hooks.
It's kind of hard to explain, but hopefully someone will get and can tell me if I'm on the right track or if there is a more standard way of doing hooks like these?
Currently the entire backend is written in C# but that is not a requirement.
I'll just draft out the main framework, then wait for your feedback to fill in anything unclear.
Disclaimer: I don't really have expertise with security and sandboxing. I just know it's an important thing, but really, it's beyond me. You go figure it out 😂
Suppose we're now in a safe sandbox where all malicious behaviors are magically taken care, let's write some Node.js code for that "hook engine".
How users deploy their plugin code.
Let's assume we use file-base deployment. The interface you need to implement is a PluginRegistry.
class PluginRegistry {
constructor() {
/**
The plugin registry holds records of plugin info:
type IPluginInfo = {
userId: string,
hash: string,
filePath: string,
module: null | object,
}
*/
this.records = new Map()
}
register(userId, info) {
this.records.set(userId, info)
}
query(userId) {
return this.records.get(userId)
}
}
// plugin registry should be a singleton in your app.
const pluginRegistrySingleton = new PluginRegistry()
// app opens a http endpoint
// that accepts plugin registration
// this is how you receive user provided code
server.listen(port, (req, res) => {
if (isPluginRegistration(req)) {
let { userId, fileBytes, hash } = processRequest(req)
let filePath = pluginDir + '/' + hash + '.js'
let pluginInfo = {
userId,
// you should use some kind of hash
// to uniquely identify plugin
hash,
filePath,
// "module" field is left empty
// it will be lazy-loaded when
// plugin code is actually needed
module: null,
}
let existingPluginInfo = pluginRegistrySingleton.query(userId)
if (existingPluginInfo.hash === hash) {
// already exist, skip
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('ok');
} else {
// plugin code written down somewhere
fs.writeFile(filePath, fileBytes, (err) => {
pluginRegistrySingleton.register(userId, pluginInfo)
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('ok');
})
}
}
})
From the perspective of hook engine, it simply opens a HTTP endpoint to accept plugin registration, agnostic to the source.
Whether it's from CI/CD pipeline, or plain web interface upload, it doesn't matter. If you have CI/CD setup for your user, it is just a dedicated build machine that runs bash scripts isn't it? So just fire a curl call to this endpoint to upload whatever you need. Same applies to web interface.
How we would execute plugin code
User plugin code is just normal Node.js module code. You might instruct them to expose certain API and conform to your protocol.
class HookEngine {
constructor(pluginRegistry) {
// dependency injection
this.pluginRegistry = pluginRegistry
}
// hook
oncreate(payload) {
// hook call payload should identify the user
const pluginInfo = this.pluginRegistry.query(payload.user.id)
// lazy-load the plugin module when needed
if (pluginInfo && !pluginInfo.module) {
pluginInfo.module = require(pluginInfo.filePath)
}
// user plugin module is just normal Node.js module
// you load it with `require`, and you call what ever function you need.
pluginInfo.module.oncreate(payload)
}
}
I have been trying to run the execute_sale ix from the mpl-auction-house package but I get this error in the logs I have got the sellInstruction and buyInstruction working
This is my Code
const executeSellInstructionAccounts:ExecuteSaleInstructionAccounts = {
buyer:buyerwallet.publicKey,
seller:Sellerwallet.publicKey,
tokenAccount:tokenAccountKey,
tokenMint:mint,
metadata:await getMetadata(mint),
treasuryMint:new anchor.web3.PublicKey(AuctionHouse.mint),
auctionHouse:new anchor.web3.PublicKey(AuctionHouse.address),
auctionHouseFeeAccount:new anchor.web3.PublicKey(AuctionHouse.feeAccount),
authority:new anchor.web3.PublicKey(AuctionHouse.authority),
programAsSigner:programAsSigner,
auctionHouseTreasury:new anchor.web3.PublicKey(AuctionHouse.treasuryAccount),
buyerReceiptTokenAccount:buyerATA.address,
sellerPaymentReceiptAccount:Sellerwallet.publicKey,
buyerTradeState:BuyertradeState,
escrowPaymentAccount:escrowPaymentAccount,
freeTradeState:freeTradeState,
sellerTradeState:SellertradeState,
}
const executeSellInstructionArgs:ExecuteSaleInstructionArgs = {
escrowPaymentBump:escrowBump,
freeTradeStateBump:freeTradeBump,
programAsSignerBump:programAsSignerBump,
buyerPrice:buyPriceAdjusted,
tokenSize:tokenSizeAdjusted,
}
const execute_sale_ix = createExecuteSaleInstruction(
executeSellInstructionAccounts,executeSellInstructionArgs
)
const execute_sale_tx = new anchor.web3.Transaction(
{
recentBlockhash: blockhash,
feePayer: Sellerwallet.publicKey,
}
)
execute_sale_tx.add(execute_sale_ix);
const execute_sale_res = await sprovider.sendAndConfirm(execute_sale_tx);
There is currently a discrepancy between the published AuctionHouse SDK and the underlying Rust program.
The console reference implementation is here: https://github.com/metaplex-foundation/metaplex/blob/master/js/packages/cli/src/auction-house-cli.ts
The console reference implementation works because it loads the idl directly from the chain and is therefore up to date. It bypasses the AuctionHouse SDK completely.
However, if you're doing this in the browser, you probably don't want to load the IDL from the chain. You'd need things like a decompression library and that would blow up your package size quite a bit.
To work around this, I've forked metaplex repo here: https://github.com/neftworld/metaplex
The fork above has the following changes:
Including the IDL definition as a typescript src file (correct as at 30 May 2022)
Fetching auctionHouse program from local IDL definition instead getting it from the chain
Hence, you can use this as a base for your web implementation. To make this work on the web, you will need to remove references to keypair - console uses a key pair file - and use the browser wallet to sign the transaction before sending.
I'm trying to retrieve with Rust the unique identifier of the MSV authentication package. For that, i'm trying to use the Windows API function LsaLookupAuthenticationPackage, but it is returning the NTSTATUS constant 0xC00000FE (STATUS_NO_SUCH_PACKAGE), which acording to the official documentation it means "A specified authentication package is unknown".
To call LsaLookupAuthenticationPackage, I'm using the official crate to access the Windows API. Below I detail the activities that I perform before calling LsaLookupAuthenticationPackage:
1.- I run my code as System.
2.- I successfully enable SeTcbPrivilege using the function RtlAdjustPrivilege.
3.- I successfully call LsaRegisterLogonProcess to open a handle to start the interaction with the LSA.
4.- I use this handle to call LsaLookupAuthenticationPackage. Below, I show the code that I'm using to call LsaLookupAuthenticationPackage:
let package:[char;20]= ['M','S','V','1','_','0','_','P','A','C','K','A','G','E','_','N','A','M','E','\0'];
let auth_package: STRING = STRING {Length:19, MaximumLength:19, Buffer:transmute(package.as_ptr())};
let auth_package_ptr: *const STRING = transmute(&auth_package);
let auth_id: *mut u32 = transmute(&u32::default());
let ret = LsaLookupAuthenticationPackage(handle,auth_package_ptr,auth_id);
I also tried to use an &str instead of the char array to store the auth package name, but it didn't work either.
For me it's obvious that the function is working, so the problem seems to be that I'm not passing correctly the auth package name.
I'm trying to test a program on localnet which makes numerous cross-program invocations (CPIs).
Is there an easy way to initialize a localnet cluster with all the accounts copied over from mainnet-beta?
I know there is a clone flag on the solana-test-validator command however it would be impractical to use clone for all the accounts I need copied over.
It is impractical to invoke solana-test-validator from the command line to do this.
The approach I've taken is to use solana account to get the accounts to local files, then use "in code" initialization of the solana test validator to load those accounts and then test.
For the first part, you could rig up a script to invoke:
solana account -o LOCALFILE.json --output json-compact PUBLIC_KEY where it will fetch account associated with PUBLIC_KEY and put in LOCALFILE.json
Then, in rust (just an example using 2 accounts but it could be many more. More than likely you'd want to walk a well known directory to load from and just loop that to build the input Vec:
fn load_stored(tvg: &mut TestValidatorGenesis) -> &mut TestValidatorGenesis {
let mut avec = Vec::<AccountInfo>::new();
for i in 0..2 {
let akp = get_keypair(USER_ACCOUNT_LIST[i]).unwrap();
avec.push(AccountInfo {
address: akp.pubkey(),
filename: USER_STORED_LIST[i],
});
}
tvg.add_accounts_from_json_files(&avec)
}
/// Setup the test validator with predefined properties
pub fn setup_validator() -> Result<(TestValidator, Keypair), Box<dyn error::Error>> {
let vwallet = get_keypair(WALLET_ACCOUNT).unwrap();
std::env::set_var("BPF_OUT_DIR", PROG_PATH);
let mut test_validator = TestValidatorGenesis::default();
test_validator.ledger_path(LEDGER_PATH);
test_validator.add_program(PROG_NAME, PROG_KEY);
load_stored(&mut test_validator);
// solana_logger::setup_with_default("solana=error");
let test_validator =
test_validator.start_with_mint_address(vwallet.pubkey(), SocketAddrSpace::new(true))?;
Ok((test_validator, vwallet))
}
You can launch the validator with -um -c ADDRESS to preload accounts with the content of mainnet-beta. In practice that's often not feasible, as you simply would need to many accounts, but for small programs it does work.
As another alternative, you can try using this fork of the Solana monorepo, which aims to clone the entire state of the ledger from mainnet, and spins up a validator from it: https://github.com/DappioWonderland/solana
Note that I haven't used it and haven't audited it to be sure it doesn't do anything shady, but if it lives up to the promise, it should be exactly what you need!
At the present I'm pasting a Javascript into the console of FF and I'm calling the functions from the console:
function fill (i){
if(i==1){
SINGLE_START();
}
else if(i==2){
DUAL_START();
}
else if(i==3){
INTEGRATED_START();
}
else{
alert("=======================\n Tool Filler\n=======================\n\n1 or 2");
}
}
It is used to scrape the content of the website and e.g. create a file or generate an email from certain parts of the website, e.g.:
function SINGLE_START(){
//Focus:
let d = $(document).activeElement.contentDocument.activeElement.contentDocument.activeElement.contentDocument;
etc.
I thougt, there could be a way to use it through an extension and so I installed Tampermonkey and saved the script as userscript within the extension. But than I have a problem that I'm not able to call the desired function from the script as I need it, not just start the script as the website loads.
Does anyone has an idea how to call the functions one by one from within Tampermonkey (or Greasemonkey), or any other extension?
Thanks in advance!
This is because Tampermonkey scripts run in isolated context. There are two kinds:
1. No special privilegies
If you're not using any special GM functions that are unlocked by #grant GM_doThisAndThat and instead you use #grant none, then what internally happens is something like this:
function TheScript() {
// Here is your script that you added to tampermonkey
}
TheScript();
This if you have a function there, it is only available in the script. You need to explicitly expose it to window context:
function fill (i){
... code here ...
}
window.myFill = fill;
Then in console you write myFill(42) and it will execute.
I would recommend that you avoid assigning fill to window since it's a name that could likely conflict with something, but that's up to you.
2. Special privilegies
If you're using some of the GM features, you need to add #grant unsafeWindow and then assign to unsafeWindow variable. Be careful what you expose like this, you don't want to allow the website to access any of the GM_function features as they could access your private data on other websites and your computer!