soliditysolanasolana-web3js

Calling specific methods on a Solana Solidity program


I've built a simple smart contract to run on the Ethereum blockchain and I'm trying to replicate some of it's behavior on Solana. After making some slight changes I've managed to compile the program with Solang targeting Solana, but I'm not sure how to go about calling the methods; there doesn't seem to be a great wealth of documentation or examples on this. For example, if my program were written as follows:

contract Example {
  function foo(...) { ... }
  function bar(...) { ... }
}

How would I specify a call to foo vs a call to bar? Furthermore, how would I encode the arguments for these method calls?

Currently my approach is to use the @solana/buffer-layout library to encode my arguments as a struct, starting with lo.u8('instruction') to specify the method call (in the example case, I assume 0 would refer to foo and 1 would refer to bar). I've taken this approach based on looking at the source code for @solana/spl-token (specifically this file and it's dependencies) but I'm not sure if it will work for a program compiled using Solang, and the buffer layout encoding has been throwing an unexpected error as well:

TypeError: Blob.encode requires (length 32) Uint8Array as src

The code throwing this error is as follows:

const method = lo.struct([
  lo.u8('instruction'),
  lo.seq(
    lo.blob(32),
    lo.greedy(lo.blob(32).span),
    'publicKeys',
  ),
])

const data = Buffer.alloc(64); // Using method.span here results in an error, as method.span == -1
method.encode(
  {
    instruction: 0,
    publicKeys: [firstPublicKey, secondPublicKey],
  },
  data,
);

While this type error seems obvious, it doesn't line up with the sample code in the solana-labs/solana-program-library repository. I'm pretty sure this problem has to do with my use of lo.seq() but I'm not sure what the problem is.

Is my approach to this correct besides this type error, or is my approach fundamentally wrong? How can I call the intended method with encoded arguments? Thank you for any help.


Solution

  • There's a better library for you to use, @solana/solidity, which has a Contract class to encapsulate calls on the contract.

    For example, in your case, you could do:

    const { Connection, LAMPORTS_PER_SOL, Keypair } = require('@solana/web3.js');
    const { Contract, Program } = require('@solana/solidity');
    const { readFileSync } = require('fs');
    
    const EXAMPLE_ABI = JSON.parse(readFileSync('./example.abi', 'utf8'));
    const PROGRAM_SO = readFileSync('./example.so');
    
    (async function () {
        console.log('Connecting to your local Solana node ...');
        const connection = new Connection('http://localhost:8899', 'confirmed');
    
        const payer = Keypair.generate();
    
        console.log('Airdropping SOL to a new wallet ...');
        const signature = await connection.requestAirdrop(payer.publicKey, LAMPORTS_PER_SOL);
        await connection.confirmTransaction(signature, 'confirmed');
    
        const program = Keypair.generate();
        const storage = Keypair.generate();
    
        const contract = new Contract(connection, program.publicKey, storage.publicKey, EXAMPLE_ABI, payer);
    
        await contract.load(program, PROGRAM_SO);
    
        console.log('Program deployment finished, deploying the example contract ...');
    
        await contract.deploy('example', [true], program, storage);
    
        const res = await contract.functions.foo();
        console.log('foo: ' + res.result);
    
        const res2 = await contract.functions.bar();
        console.log('bar: ' + res2.result);
    })();
    

    Example adapted from https://github.com/hyperledger-labs/solang#build-for-solana

    More information about the package at https://www.npmjs.com/package/@solana/solidity