verze-app / solana-php-sdk

Simple PHP SDK for Solana JSON RPC endpoints
MIT License
88 stars 45 forks source link

how to transfer tokens using this SDK i.e. spl-token transfer? #21

Open mkocurek opened 2 years ago

mkocurek commented 2 years ago

not an issue but a question or a feature request

is it possible to transfer tokens using this SDK like with the Solana CLI using a terminal?

Terminal Example: $spl-token transfer --allow-unfunded-recipient --fund-recipient [token address] [amount of tokens] [recipients wallet]

neverything commented 2 years ago

I believe it's not yet fully integrated, but haven't tested it https://github.com/tighten/solana-php-sdk#transactions

ghost commented 2 years ago

Anymore info on this would be highly appreciated ❤️

neverything commented 2 years ago

Seems like it's being worked on https://github.com/tighten/solana-php-sdk/pull/28

impozzible commented 2 years ago

@suchpro Were you able to figure this out

impozzible commented 2 years ago

I can transfer tokens now, but the amount sent is not encoded properly I believe to the right decimal so it sends a wrong value

ghost commented 2 years ago

@impozzible haven't tested it yet

impozzible commented 2 years ago

Figured it out, incase someone else runs into this issue


public function createTransferInstruction(
        PublicKey $source,
        PublicKey $destination,
        PublicKey $owner,
        int $amount
    ): TransactionInstruction {
        $data = [
            // uint32
            ...unpack("C*", pack("C", 3)),
            // int64
            ...unpack("C*", pack("P", $amount)),
        ];
        // dd($data);
        $keys =  [
            new AccountMeta($source, false, true),
            new AccountMeta($destination, false, true),
            new AccountMeta($owner,true,true),
        ];

        return new TransactionInstruction(
            new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"),
            $keys,
            $data
        );
    }```
Igor360 commented 2 years ago

'Transfer' instruction is deprecated for now and token developers recommend to use 'TransferChecked' instruction, sample code:

    public function createTransferCheckedInstruction(
        string $source,
        string $destination,
        string $owner,
        string $mint,
        int    $amount,
        int    $decimals
    ): TransactionInstruction
    {
        /**
         * TokenInstructions :
         *   Transfer = 3,
         *   TransferChecked = 12,
         */
        $data = [
            // u8
            ...unpack("C*", pack("C", 12)),  // token instruction index
            // u64
            ...unpack("C*", pack("P", $amount)), // amount without decimals
            // u8
            ...unpack("C*", pack("C", $decimals)),   // token decimals
        ];
        $keys = [
            new AccountMeta(new PublicKey($source), false, true), // source account address
            new AccountMeta(new PublicKey($mint), false, false), // mint token address
            new AccountMeta(new PublicKey($destination), false, true), // destination account address, if not exist need call
            new AccountMeta(new PublicKey($owner), true, true), // singer address, wallet who will be sing tx
        ];

        return new TransactionInstruction(
            new PublicKey(SplTokenProgram::SOLANA_TOKEN_PROGRAM_ID),
            $keys,
            $data
        );
    }

In case when you doesn't have token Account address you must to create it, for this action get associated token address with following function

  public static function getAssociatedTokenAccount(string $mint, string $publicKeyAddress): PublicKey
    {
        $publicKeyAccount = new PublicKey($publicKeyAddress);
        $publicKeyMint = new PublicKey($mint);
        $programId = new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
        $seed = [
            $publicKeyAccount->toBuffer(),
            $programId->toBuffer(),
            $publicKeyMint->toBuffer()
        ];
        return \Arr::first(PublicKey::findProgramAddress($seed, new PublicKey("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"))); // ATTENTION : It's function not working correctly in latest version of package, now latest is "^0.3.2", for solve it change package version to "dev-main
    }

Then add instruction for create token address into your transaction, but if account already created not add this instruction to transaction object

public static function createAssociatedTokenAccountInstruction(
        string $payer, // tx payer address. it address wallet whose using for sining transaction 
        string $associatedToken, // generated token address, for get call function "getAssociatedTokenAccount"
        string $owner, //  wallet address for which you want to create associated token wallet
        string $mint,   // Token mint address
        string $programId = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
        string $associatedTokenProgramId = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
    ): TransactionInstruction
    {
        $data = [];
        $keys = [
            new AccountMeta(new PublicKey($payer), true, true),
            new AccountMeta(new PublicKey($associatedToken), false, true),
            new AccountMeta(new PublicKey($owner), false, false),
            new AccountMeta(new PublicKey($mint), false, false),
            new AccountMeta(new PublicKey("11111111111111111111111111111111"), false, false),
            new AccountMeta(new PublicKey($programId), false, false),
            new AccountMeta(new PublicKey("SysvarRent111111111111111111111111111111111"), false, false),
        ];

        return new TransactionInstruction(
            new PublicKey($associatedTokenProgramId),
            $keys,
            $data
        );
    }
Scrivs commented 1 year ago

Does anyone have an example of full code on this working? I keep getting a

Transaction simulation failed: Error processing Instruction 0: invalid account data for instruction

error

Scrivs commented 1 year ago

Figured it out, incase someone else runs into this issue

public function createTransferInstruction(
        PublicKey $source,
        PublicKey $destination,
        PublicKey $owner,
        int $amount
    ): TransactionInstruction {
        $data = [
            // uint32
            ...unpack("C*", pack("C", 3)),
            // int64
            ...unpack("C*", pack("P", $amount)),
        ];
        // dd($data);
        $keys =  [
            new AccountMeta($source, false, true),
            new AccountMeta($destination, false, true),
            new AccountMeta($owner,true,true),
        ];

        return new TransactionInstruction(
            new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"),
            $keys,
            $data
        );
    }```

Can you post your full code for sending a token please?