PHP Algorand SDK

Hello everyone,

I’m Felipe Vieira, PHP developer and other languages.

I’m starting in the Algorand community, wonderful project, will be a pleasure develop and share solutions using this beautiful blockchain.

I created a SDK in PHP that will be the basis for my new projects, this is the first version, which will undergo updates and improvements over time, I count on everyone’s help for new improvements and I hope to help bring new projects using this language that has a huge community, feel free to use and give opinion too.

Follow the link on GitHub:

Video on Youtube:

I currently need help to create new features using the “POST /v1/transaction/sign” endpoint, I use CURL to make calls. I need to know in detail how to compose the “SignTransactionRequest”, more specifically the “transaction” field with the Pattern: "^ (?: [A-Za-z0-9 + /] {4}) * (?: [A-Za -z0-9 + /] {2} == \ | [A-Za-z0-9 + /] {3} =)? $ string (byte).

I didn’t find many details in the documentation, I tried to serialize in bytes, use msgpack, base64, etc … I’m doing something wrong.

Could someone send me an example of what this request would look like?

In my attempts it always returns the error:

[code] => 400
[message] => {
“error”: true,
“message”: “could not decode transaction”
}

I really appreciate the help,

Best Regards,

Felipe Vieira

4 Likes

Thanks for writing a PhP SDK!

It’s the base64 encoding of the msgpack-encoded Transaction object itself, but not the SignedTxn object.
The documentation needs to be updated.

Concretely, you can do the following (using jq):

$ goal clerk send --from OEOFA6R5HQA7KBQ5BGMI2B5FQ2U4S6TWF5DUEHWJ4PSYOFZ5A7LGXSM4TM --to OEOFA6R5HQA7KBQ5BGMI2B5FQ2U4S6TWF5DUEHWJ4PSYOFZ5A7LGXSM4TM --amount 1 --firstvalid 12771319 --out test.tx

$ msgpacktool -d -b32 < test.tx | jq '.txn' | msgpacktool -e -b32 > test.txforkmd

$ curl -s -H "Content-Type: application/json" -H "X-Kmd-API-Token: $(cat $ALGORAND_DATA/kmd-v0.5/kmd.token)" -X POST "$(cat $ALGORAND_DATA/kmd-v0.5/kmd.net)/v1/wallet/init" -d '{"wallet_id": "fc6a9a4570f3fe81a6a0d9a48a45f54a", "wallet_password": ""}' | jq ".wallet_handle_token" -r > wallet_token

$ WALLET_HANDLE_TOKEN=$(cat wallet_token)

$ TRANSACTION=$(base64 < test.txforkmd)

$ curl -H "Content-Type: application/json" -H "X-Kmd-API-Token: $(cat $ALGORAND_DATA/kmd-v0.5/kmd.token)" -X POST "$(cat $ALGORAND_DATA/kmd-v0.5/kmd.net)/v1/transaction/sign" -d '{"wallet_handle_token": "'"$WALLET_HANDLE_TOKEN"'","transaction": "'"$TRANSACTION"'","wallet_password": ""}'
{
  "signed_transaction": "gqNzaWfEQEOhbbpL2MfTq0fTwu9YB0LmZVsB3FR5eQGABNskF/XfOsGT/XZpQeKwamVm9d4JdokqwbMyOVWfXFsJMH1smAajdHhuiqNhbXQBo2ZlZc0D6KJmds4Awt/3o2dlbqx0ZXN0bmV0LXYxLjCiZ2jEIEhjtRiks8hOyBDyLU8QgcsPcfBZp6wg3sYvf3DlCToiomx2zgDC49+kbm90ZcQIGmpAsxfOByyjcmN2xCBxHFB6PTwB9QYdCZiNB6WGqcl6di9HQh7J4+WHFz0H1qNzbmTEIHEcUHo9PAH1Bh0JmI0HpYapyXp2L0dCHsnj5YcXPQfWpHR5cGWjcGF5"
}%

$ base64 -d <<< gqNzaWfEQEOhbbpL2MfTq0fTwu9YB0LmZVsB3FR5eQGABNskF/XfOsGT/XZpQeKwamVm9d4JdokqwbMyOVWfXFsJMH1smAajdHhuiqNhbXQBo2ZlZc0D6KJmds4Awt/3o2dlbqx0ZXN0bmV0LXYxLjCiZ2jEIEhjtRiks8hOyBDyLU8QgcsPcfBZp6wg3sYvf3DlCToiomx2zgDC49+kbm90ZcQIGmpAsxfOByyjcmN2xCBxHFB6PTwB9QYdCZiNB6WGqcl6di9HQh7J4+WHFz0H1qNzbmTEIHEcUHo9PAH1Bh0JmI0HpYapyXp2L0dCHsnj5YcXPQfWpHR5cGWjcGF5 > test.sig

The important command is:

msgpacktool -d -b32 < test.tx | jq '.txn' | msgpacktool -e -b32 > test.txforkmd

that extracts the transaction object from the SignedTxn object.

I’ve made a PR to improve the documentation:

1 Like

Hello Fabrice!

Thank you very much for the informations, I’ll try

Hi @fabrice,

Works fine with the msgpacktool, now I’m trying do using just PHP.

The REST API Already recognize the transaction now only with php, I just need to encode the addresses (snd and rcv) before encode all with Base 64, Can you help me with the specifications?

From:
snd�:DI65FPLNUXOJJR47FDTIB5TNNIA5G4EZFA44RZMRBE7AA4D453OYD2JCW4�type�pay

To: snd� =ҽm�ܔǟ(��mj�p�(9�� >p|�ݤtype�pay

stdClass Object
(
[error] => 1
[message] => key does not exist in this wallet
)

Thank you

Can you send me an example query you are doing?

Best is to debug using msgpacktool and see the difference between what you obtain by command line and what your PHP script generates.

Hi Fabrice, Sure.

Today the SDK works fine, but I’m trying to set up a CURL query only with php to create more facilities:

1- Transaction query builder

$transaction=array(
                "fee" => 1000,
                "fv" => 12435315,
                "gen" => "mainnet-v1.0",
                "gh" => "wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=",
                "lv" => 12437315,
                "snd" => "DI65FPLNUXOJJR47FDTIB5TNNIA5G4EZFA44RZMRBE7AA4D453OYD2JCW4",
                "type" => "pay",
                "rcv" => "IYVZLDFIF6KUMSDFVIKHPBT3FI5QVZJKJ6BPFSGIJDUJGUUASKNRA4HUHU",
                "amt" => 1000,
);

2- Transaction Encode :

$transaction=$algorand_kmd->txn_encode($transaction);

The txn_encode() Will return msgpack and base64 encode, it working fine but, I need encode only the fields gh, snd and rcv to match the return of msgpacktool.

Return from msgpacktool (without base64):
��amt�fee��fv�� �gen�mainnet-v1.0�gh� �a�������`K�V�?m��7�� ��9$�ߢlv���note����Q� ��rcv� F+���/�FHe�w�{; �O����H�R����snd� =ҽm�ܔǟ(��mj�p�(9�� >p|�ݤtype�pay

My current return with PHP (without base64)::
��amt��fee��fv���s�gen�mainnet-v1.0�gh�,wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=�lv���C�rcv�:IYVZLDFIF6KUMSDFVIKHPBT3FI5QVZJKJ6BPFSGIJDUJGUUASKNRA4HUHU�snd�:DI65FPLNUXOJJR47FDTIB5TNNIA5G4EZFA44RZMRBE7AA4D453OYD2JCW4�type�pay

3- Build request to POST /v1/transaction/sign (JSON)

$txns=$params['params']=array(
   "transaction" => $transaction,
   "wallet_handle_token" => $wallet_handle_token,
   "wallet_password" => "testes"
);

4- Send CURL request with PHP, like:

$ curl -H "Content-Type: application/json" -H "X-Kmd-API-Token: $(cat $ALGORAND_DATA/kmd-v0.5/kmd.token)" -X POST "$(cat $ALGORAND_DATA/kmd-v0.5/kmd.net)/v1/transaction/sign" -d '$txns'

$txns contains JSON with the transaction.

I’m trying to reverse engineer but it can take a long time.

Suggestion for the REST API, create a transaction builder, ex: POST /transaction/build and already base64 encoded.

Thank you very much for your attention

1 Like

Can you print the base64 of the returns because forum does not support the characters and so we cannot copy-paste them?

Return from msgpacktool (base64):
iqNhbXQBo2ZlZc0D6KJmds4AvxUKo2dlbqxtYWlubmV0LXYxLjCiZ2jEIMBhxNj8Hb3e0tdgS+RWjj9tBBmHrDe95LYgtas5JIrfomx2zgC/GPKkbm90ZcQIlgOChlGICZ2jcmN2xCBGK5WMqC+VRkhlqhR3hnsqOwrlKk+C8sjISOiTUoCSm6NzbmTEIBo90r1tpdyUx58o5oD2bWoB03CZKDnI5ZEJPgBwfO7dpHR5cGWjcGF5

My current return with PHP (base64)::
iaNhbXTNA+ijZmVlzQPoomZ2zgC/FQqjZ2VurG1haW5uZXQtdjEuMKJnaNksd0dIRTJQd2R2ZDdTMTJCTDVGYU9QMjBFR1llc043M2t0aUMxcXpra2l0OD2ibHbOAL8Y8qNyY3bZOklZVlpMREZJRjZLVU1TREZWSUtIUEJUM0ZJNVFWWkpLSjZCUEZTR0lKRFVKR1VVQVNLTlJBNEhVSFWjc25k2TpESTY1RlBMTlVYT0pKUjQ3RkRUSUI1VE5OSUE1RzRFWkZBNDRSWk1SQkU3QUE0RDQ1M09ZRDJKQ1c0pHR5cGWjcGF5

I think I found a clue with the command line: ./msgpacktool -d -b32 < test.txforkmd

{
  "amt": 1,
  "fee": 1000,
  "fv": 12522762,
  "gen": "mainnet-v1.0",
  "gh:b32": "YBQ4JWH4DW655UWXMBF6IVUOH5WQIGMHVQ333ZFWEC22WOJERLPQ====",
  "lv": 12523762,
  "note:b32": "SYBYFBSRRAEZ2===",
  "rcv:b32": "IYVZLDFIF6KUMSDFVIKHPBT3FI5QVZJKJ6BPFSGIJDUJGUUASKNQ====",
  "snd:b32": "DI65FPLNUXOJJR47FDTIB5TNNIA5G4EZFA44RZMRBE7AA4D453OQ====",
  "type": "pay"
}

The fields gh, note, rcv, and snd has b32, maybe i have to encrypt as base32?

Your own version is:

$ echo -n "iaNhbXTNA+ijZmVlzQPoomZ2zgC/FQqjZ2VurG1haW5uZXQtdjEuMKJnaNksd0dIRTJQd2R2ZDdTMTJCTDVGYU9QMjBFR1llc043M2t0aUMxcXpra2l0OD2ibHbOAL8Y8qNyY3bZOklZVlpMREZJRjZLVU1TREZWSUtIUEJUM0ZJNVFWWkpLSjZCUEZTR0lKRFVKR1VVQVNLTlJBNEhVSFWjc25k2TpESTY1RlBMTlVYT0pKUjQ3RkRUSUI1VE5OSUE1RzRFWkZBNDRSWk1SQkU3QUE0RDQ1M09ZRDJKQ1c0pHR5cGWjcGF5" | base64 -d | msgpacktool -d -b32
{
  "amt": 1000,
  "fee": 1000,
  "fv": 12522762,
  "gen": "mainnet-v1.0",
  "gh": "wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=",
  "lv": 12523762,
  "rcv": "IYVZLDFIF6KUMSDFVIKHPBT3FI5QVZJKJ6BPFSGIJDUJGUUASKNRA4HUHU",
  "snd": "DI65FPLNUXOJJR47FDTIB5TNNIA5G4EZFA44RZMRBE7AA4D453OYD2JCW4",
  "type": "pay"
}

You see 2 kinds of differences:

  1. You don’t see the suffix :b32 after gh, rcv and snd. This means that you encoded these fields as string instead of byte array. These fields must be the array of bytes corresponding to their content, not the base32/base64 corresponding string.
  2. The addresses include the checksum, while they should not. See Algorand Developer Docs for high-level and https://github.com/algorand/py-algorand-sdk/blob/8e0a83fd849e732522ff87e78337b3282ebf3629/algosdk/encoding.py#L153 how to do it concretely

Hi Fabrice!
All right! Now I got sign and create transactions using PHP only!
Your help was fundamental.
Tomorrow I will upload an SDK update with these new features.
Obrigado!

2 Likes

Hello ,

first , thank you for sdk.

I try to make a transaction with this code :

<?php
  
include('sdk/algorand.php');
$algorand = new Algorand_algod('xx','localhost',53898);
$algorand_kmd = new Algorand_kmd('xx','localhost',64988);

$transaction=array(
        "txn" => array(
                "fee" => 1000, //Fee
                "fv" => 12581127, //First Valid
                "gen" => "mainnet-v1.0", // GenesisID
                "gh" => "YBQ4JWH4DW655UWXMBF6IVUOH5WQIGMHVQ333ZFWEC22WOJERLPQ=", //Genesis Hash
                "lv" => 13626800, //Last Valid
                "note" => "test send  iot", //Your note
                "snd" => "xx", //Sender
                "type" => "pay", //Tx Type
                "rcv" => "xx", //Receiver
                "amt" => 1000, //Amount
            ),
);


#Wallet Init
$params['params']=array(
    "wallet_id" => 'algotest',
    "wallet_password" => 'test',
);
$return=$algorand_kmd->post("v1","wallet","init",$params);
$return_array=json_decode($return['response']);
$wallet_handle_token=$return_array->wallet_handle_token;

$params['params']=array(
   "transaction" => $algorand_kmd->txn_encode($transaction),
   "wallet_handle_token" => $wallet_handle_token,
   "wallet_password" => "test"
);

$return=$algorand_kmd->post("v1","transaction","sign",$params);
$r=json_decode($return['response']);
$txn=base64_decode($r->signed_transaction);



//$algorand = new Algorand_algod('{algod-token}',"localhost",53898);
$params['transaction']=$txn;
$return=$algorand->post("v2","transactions",$params);

?>

Result this error :

PHP Notice:  Undefined index: response in /var/www/html/algo/php-algorand-sdk/transact.php on line 29
PHP Notice:  Trying to get property 'wallet_handle_token' of non-object in /var/www/html/algo/php-algorand-sdk/transact.php on line 30
PHP Notice:  Undefined index: response in /var/www/html/algo/php-algorand-sdk/transact.php on line 39
PHP Notice:  Trying to get property 'signed_transaction' of non-object in /var/www/html/algo/php-algorand-sdk/transact.php on line 40

Any suggestions ?

Thanks
Marco

Hi Marco.

Probably the error_reporting directive of your php.ini is E_ALL, I recommend changing it to E_ALL ~E_NOTICE to have more compatibility with all versions of PHP and frameworks. You can also try to start your php code with:

error_reporting (E_ALL ^ E_NOTICE);
or
ini_set(‘error_reporting’, E_ALL ^ E_NOTICE);

I will do a review with E_NOTICE too

Thanks to Report

Hi Felipe ,

I put this error_reporting (E_ALL ^ E_NOTICE); and all ok , but now I have other error :

Array
(
    [code] => 400
    [message] => {"message":"msgpack decode error [pos 1]: only encoded map or array can be decoded into a struct"}
)

any help?

Thank you

Marco

Can you send the complete code?

Using you example, you need change xx for a valid account, at:

“snd” => “xx”, //Sender
“rcv” => “xx”, //Receiver

In “snd”, the account needs to be generated by KMD

What version of PHP are you using?

In my running example I use a real rcv and a real snd.

So , how can generate snd from kmd ? Can you post example ?

Thank you

Marco

p.s Php 7.2 .24 on Ubuntu 18.04

Hi Felipe ,

#Export a key
$params['params']=array(
    "address" => $response->address,
    "wallet_password" => 'password',
    "wallet_handle_token" => $wallet_handle_token
);
$return=$algorand_kmd->post("v1","key","export",$params);

Can I use this to read snd key ?

Marco

Hi Marco,

At this url you can see the complete example:

The export method needs an account already created.

See the examples folder for other use cases (Asset Management, Explorer, etc…)

If you need any more help let me know

Hi ,
oh yes , i read your examples but i need only a program that write a transaction to blockchain with a note from a http POST.

This is not a pay transaction.

The complete code is:

<?php
require_once 'algo/php-algorand-sdk/sdk/algorand.php';
$algorand = new Algorand_algod('algod.token','localhost',53898);
$algorand_kmd = new Algorand_kmd('kmd.token','localhost',64988);


#Take a POST 

//Make sure that it is a POST request.
if(strcasecmp($_SERVER['REQUEST_METHOD'], 'POST') != 0){
    throw new Exception('Request method must be POST!');
}

//Make sure that the content type of the POST request has been set to application/json
$contentType = isset($_SERVER["CONTENT_TYPE"]) ? trim($_SERVER["CONTENT_TYPE"]) : '';
if(strcasecmp($contentType, 'application/json') != 0){
    throw new Exception('Content type must be: application/json');
}

//Receive the RAW post data.
$content = trim(file_get_contents("php://input"));

//Attempt to decode the incoming RAW post data from JSON.
$decoded = json_decode($content, true);

//If json_decode failed, the JSON is invalid.
if(!is_array($decoded)){
    throw new Exception('Received content contained invalid JSON!');
}


#Start transaction routine
$transaction=array(
        "txn" => array(
                "fee" => 1000, //Fee
                "fv" => 12581127, //First Valid
                "gen" => "mainnet-v1.0", // GenesisID
                "gh" => "YBQ4JWH4DW655UWXMBF6IVUOH5WQIGMHVQ333ZFWEC22WOJERLPQ=", //Genesis Hash
                "lv" => 13626800, //Last Valid
                "note" => $content, //Your note
                "snd" => "account", //Sender
                "type" => "pay", //Tx Type
                "rcv" => "account", //Receiver
                "amt" => 1000, //Amount
            ),
);


#Wallet Init
$params['params']=array(
    "wallet_id" => 'algotest',
    "wallet_password" => 'password',
);
$return=$algorand_kmd->post("v1","wallet","init",$params);
$return_array=json_decode($return['response']);
$wallet_handle_token=$return_array->wallet_handle_token;

$params['params']=array(
   "transaction" => $algorand_kmd->txn_encode($transaction),
   "wallet_handle_token" => $wallet_handle_token,
   "wallet_password" => "password"
);

$return=$algorand_kmd->post("v1","transaction","sign",$params);
$r=json_decode($return['response']);
$txn=base64_decode($r->signed_transaction);


#Transaction 

$params['transaction']=$txn;
$return=$algorand->post("v2","transactions",$params);


#Debug
print_r($return);



// Debug on external file 
$fp = fopen('data.txt', 'a');//opens file in append mode  
fwrite($fp, $content);
fwrite($fp, "\n\n");
fwrite($fp, $return);
fclose($fp);


?>


Marco

To record notes you need to make a transaction and pay the fee, you can send to same address 1 microAlgo.

1- If you haven’t created a Wallet yet, use the code:

$algorand_kmd = new Algorand_kmd('{kmd-token}',"localhost",7833);

#Create Wallet same as cli: goal wallet new
$params['params']=array(
    "wallet_name" => "algotest",
    "wallet_password" => "password",
    "wallet_driver_name" => "sqlite",
);
$return=$algorand_kmd->post("v1","wallet",$params); 
//Will return the Wallet ID
/* Response:
{
  "wallet": {
    "driver_name": "sqlite",
    "driver_version": 1,
    "id": "8abb280fc4d974be077b3333dc9dea34", <<<< HERE
    "mnemonic_ux": false,
    "name": "algotest",
    "supported_txs": [
      "pay",
      "keyreg"
    ]
}
*/

2- To get your wallet ID again:

#Wallet List same as cli: goal wallet list
$return=$algorand_kmd->get("v1","wallets");

3- Create an account (key address):

$algorand_kmd = new Algorand_kmd('{kmd-token}',"localhost",7833);

#Wallet Init to get the handle token
$params['params']=array(
    "wallet_id" => "8abb280fc4d974be077b3333dc9dea34",
    "wallet_password" => "password",
);
$return=$algorand_kmd->post("v1","wallet","init",$params);
$return_array=json_decode($return['response']);
$wallet_handle_token=$return_array->wallet_handle_token;


#Generate a key
$params['params']=array(
    "display_mnemonic" => false,
    "wallet_handle_token" => $wallet_handle_token
);
$return=$algorand_kmd->post("v1","key",$params);

/* Response
{
  "address": "NBUTVWA7MYHLORN7SM73JT2I2S2MTXO2FPXOIUDXOB2DS36SFVGOQTI3AA"
}
*/

4- Transfer Algos to the newly created address.

The scripts above, you only need execute one time. To create the transaction use the code below:

$algorand_kmd = new Algorand_kmd('{kmd-token}',"localhost",7833); 

#Wallet Init to get the handle token
$params['params']=array(
    "wallet_id" => "8abb280fc4d974be077b3333dc9dea34",
    "wallet_password" => "password",
);
$return=$algorand_kmd->post("v1","wallet","init",$params);
$return_array=json_decode($return['response']);
$wallet_handle_token=$return_array->wallet_handle_token;

$transaction=array(
        "txn" => array(
                "type" => "pay", //Tx Type
                "fee" => 1000, //Fee
                "fv" => 13636245, //First Valid
                "gen" => "mainnet-v1.0", // GenesisID
                "gh" => "YBQ4JWH4DW655UWXMBF6IVUOH5WQIGMHVQ333ZFWEC22WOJERLPQ=", //Genesis Hash
                "lv" => 13637245, //Last Valid
                "note" => "Tests", //You note
                "snd" => "NBUTVWA7MYHLORN7SM73JT2I2S2MTXO2FPXOIUDXOB2DS36SFVGOQTI3AA", //Sender
                "rcv" => "NBUTVWA7MYHLORN7SM73JT2I2S2MTXO2FPXOIUDXOB2DS36SFVGOQTI3AA", //Receiver
                "amt" => 1000, //Amount
            ),
);

#Sign Transaction
$params['params']=array(
   "transaction" => $algorand_kmd->txn_encode($transaction),
   "wallet_handle_token" => $wallet_handle_token,
   "wallet_password" => "password",
);

$return=$algorand_kmd->post("v1","transaction","sign",$params);
$r=json_decode($return['response']);
$txn=base64_decode($r->signed_transaction);


#Broadcasts a raw transaction to the network.
$algorand = new Algorand_algod('4820e6e45f339e0026eaa2b74c2aa7d8735cbcb2db0cf0444fb492892e1c09b7',"localhost",53898); //Start Algod services
$params['transaction']=$txn;
$return=$algorand->post("v2","transactions",$params);
if(!empty($return['response']->txId)){
    $txId=$return['response']->txId;
    echo "txId: $txId";
}

Hi Felipe ,
thank you for example.

Marco