solana.rst 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. Solana
  2. ======
  3. Solana Overview
  4. _______________
  5. As the underlying Solana environment is different than that of Ethereum, Solidity inner workings
  6. have been modified to function properly. For example, A Solidity contract on Solana utilizes two accounts: a
  7. data account and a program account. The program account stores the contract's executable binary and owns
  8. the data account, which holds all the storage variables. On Ethereum a single account can store executable
  9. code and data.
  10. Contract upgrades
  11. +++++++++++++++++
  12. Provided that the data layout from a new contract is compatible with that of an old one, it is possible
  13. to update the binary in the program account and retain the same data, rendering
  14. contract upgrades implemented in Solidity unnecessary. Solana's CLI tool provides a command to both do
  15. an initial deploy of a program, and redeploy it later.:
  16. .. code-block:: bash
  17. solana program deploy --program-id <KEYPAIR_FILEPATH> <PROGRAM_FILEPATH>
  18. where ``<KEYPAIR_FILEPATH>`` is the program's keypair json file and ``<PROGRAM_FILEPATH>``
  19. is the program binary ``.so`` file. For more information about redeploying a program,
  20. check `Solana's documentation <https://docs.solana.com/cli/deploy-a-program#redeploy-a-program>`_.
  21. Data types
  22. ++++++++++
  23. - An account address consists of a 32-bytes key, which is represented by the ``address`` type. This data model
  24. differs from Ethereum 20-bytes addresses.
  25. - Solana's virtual machine registers are 64-bit wide, so 64-bit integers ``uint64`` and ``int64`` are preferable over
  26. ``uint256`` and ``int256``. An operation with types wider than 64-bits is split into multiple operations, making
  27. it slower and consuming more compute units. This is the case, for instance, with multiplication, division and modulo
  28. using `uint256`.
  29. - Likewise, all balances and values on Solana are 64-bit wide, so the builtin functions for
  30. *address* ``.balance``, ``.transfer()`` and ``.send()`` use 64-bit integers.
  31. - An address literal has to be specified using the ``address"36VtvSbE6jVGGQytYWSaDPG7uZphaxEjpJHUUpuUbq4D"`` syntax.
  32. - Ethereum syntax for addresses ``0xE0f5206BBD039e7b0592d8918820024e2a7437b9`` is not supported.
  33. Runtime
  34. +++++++
  35. - The Solana target requires `Solana <https://www.solana.com/>`_ v1.8.1.
  36. - Function selectors are eight bytes wide and known as *discriminators*.
  37. - Solana provides different builtins, e.g. ``tx.program_id`` and ``tx.accounts``.
  38. - When creating a contract in Solidity using ``new``, one :ref:`needs to provide <solana_constructor>` the data account
  39. address that is going to be initialized for the new contract.
  40. Compute budget
  41. ++++++++++++++
  42. On Ethereum, when calling a smart contract function, one needs to specify the amount of gas the operation is allowed
  43. to use. Gas serves to pay for a contract execution on chain and can be a way for giving a contract priority execution
  44. when extra gas is offered in a transaction. Each EVM instruction has an associated gas value, which translates to real
  45. ETH cost. Provided that one can afford all the gas expenses, there is no upper boundary for the amount of gas limit
  46. one can provide in a transaction, so Solidity for Ethereum has gas builtins, like ``gasleft``, ``block.gaslimit``,
  47. ``tx.gasprice`` or the Yul ``gas()`` builtin, which returns the amount of gas left for execution.
  48. On the other hand, Solana is optimized for low latency and high transaction throughput and has an equivalent concept to
  49. gas: compute unit. Every smart contract function is allowed the same quantity of compute units (currently that
  50. value is 200k), and every instruction of a contract consumes exactly one compute unit. There is no need to provide
  51. an amount of compute units for a transaction and they are not charged, except when one wants priority execution on
  52. chain, in which case one would pay per compute unit consumed. Therefore, functions for gas are not available on
  53. Solidity for Solana.
  54. Solidity for Solana incompatibilities with Solidity for Ethereum
  55. ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  56. - ``msg.sender`` is :ref:`not available on Solana <msg_sender_solana>`.
  57. - There is no ``ecrecover()`` builtin function because Solana does not use the ECDSA algorithm, but there
  58. is a ``signatureVerify()`` function, which can check ed25519 signatures. As a consequence, it is not possible to
  59. recover a signer from a signature.
  60. - Try-catch statements do not work on Solana. If any external call or contract creation fails, the runtime will
  61. halt execution and revert the entire transaction.
  62. - Error definitions and reverts with error messages are not yet working for Solana.
  63. - Value transfer with function call :ref:`does not work <value_transfer>`.
  64. - Many Yul builtins are not available, as specified in the :ref:`availability table <yul_builtins>`.
  65. - External calls on Solana require that accounts be specified, as in :ref:`this example <solana_external_call>`.
  66. - The ERC-20 interface is not compatible with Solana at the moment.
  67. Build your Solidity for Solana
  68. ______________________________
  69. .. code-block:: bash
  70. solang compile --target solana flipper.sol -v
  71. This will produce two files called ``flipper.json`` and ``flipper.so``. The former is an Anchor style IDL file and the latter is
  72. the Solana ELF shared object containing the program. For each contract in the source code, Solang will create both an IDL file
  73. and a binary file.
  74. Each program will need to be deployed to a program_id. Usually, the program_id is a well-known account which is specified
  75. in the Solidity source code using the ``@program_id("F1ipperKF9EfD821ZbbYjS319LXYiBmjhzkkf5a26rC")`` annotation on the contract.
  76. A private key for the account is needed to deploy. You can generate your own private key using the command line tool
  77. ``solana-keygen``.
  78. .. code-block:: bash
  79. echo "[4,10,246,143,43,1,234,17,159,249,41,16,230,9,198,162,107,221,233,124,34,15,16,57,205,53,237,217,149,17,229,195,3,150,242,90,91,222,117,26,196,224,214,105,82,62,237,137,92,67,213,23,14,206,230,155,43,36,85,254,247,11,226,145]" > flipper-keypair.json
  80. solana program deploy flipper.so
  81. After deploying the program, you can start on the client side, which needs the anchor npm library:
  82. .. code-block:: bash
  83. npm install @coral-xyz/anchor
  84. Write the following javascript to a file called ``flipper.js``.
  85. .. code-block:: javascript
  86. const { readFileSync } = require('fs');
  87. const anchor = require('@coral-xyz/anchor');
  88. const IDL = JSON.parse(readFileSync('./flipper.json', 'utf8'));
  89. const PROGRAM_SO = readFileSync('./flipper.so');
  90. (async function () {
  91. const provider = anchor.AnchorProvider.env();
  92. const dataAccount = anchor.web3.Keypair.generate();
  93. const programId = new anchor.web3.PublicKey(IDL.metadata.address);
  94. const wallet = provider.wallet.publicKey;
  95. const program = new anchor.Program(IDL, programId, provider);
  96. await program.methods.new(wallet, true)
  97. .accounts({ dataAccount: dataAccount.publicKey })
  98. .signers([dataAccount]).rpc();
  99. const val1 = await program.methods.get()
  100. .accounts({ dataAccount: dataAccount.publicKey })
  101. .view();
  102. console.log(`state: ${val1}`);
  103. await program.methods.flip()
  104. .accounts({ dataAccount: dataAccount.publicKey })
  105. .rpc();
  106. const val2 = await program.methods.get()
  107. .accounts({ dataAccount: dataAccount.publicKey })
  108. .view();
  109. console.log(`state: ${val2}`);
  110. })();
  111. Now you'll have to set the ``ANCHOR_WALLET`` and ``ANCHOR_PROVIDER_URL`` environment variables to the correct values in order to run the example.
  112. .. code-block:: bash
  113. export ANCHOR_WALLET=$HOME/.config/solana/id.json
  114. export ANCHOR_PROVIDER_URL=http://127.0.0.1:8899
  115. node flipper.js
  116. For more examples, see the
  117. `solang's integration tests <https://github.com/hyperledger/solang/tree/main/integration/solana>`_.
  118. Using the Anchor client library
  119. _______________________________
  120. Some notes on using the anchor javascript npm library.
  121. * Solidity function names are converted to camelCase. This means that if in Solidity a function is called ``foo_bar()``,
  122. you must write ``fooBar()`` in your javascript.
  123. * Anchor only allows you to call ``.view()`` on Solidity functions which are declared ``view`` or ``pure``.
  124. * Named return values in Solidity are also converted to camelCase. Unnamed returned are given the name ``return0``, ``return1``, etc,
  125. depending on the position in the returns values.
  126. * Only return values from ``view`` and ``pure`` functions can be decoded. Return values from other functions and are not accessible.
  127. This is a limitation in the Anchor library. Possibly this can be fixed.
  128. * In the case of an error, no return data is decoded. This means that the reason provided in ``revert('reason');`` is not
  129. available as a return value.
  130. * Number arguments for functions are expressed as ``BN`` values and not plain javascript ``Number`` or ``BigInt``.
  131. .. _call_anchor:
  132. Calling Anchor Programs from Solidity
  133. _____________________________________
  134. It is possible to call `Anchor Programs <https://github.com/coral-xyz/anchor>`_
  135. from Solidity. You first have to generate a Solidity interface file from the IDL file using
  136. the :ref:`idl_command`. Then, import the Solidity file in your Solidity using the
  137. ``import "...";`` syntax. Say you have an anchor program called ``bobcat`` with a
  138. function ``pounce``, you can call it like so:
  139. .. include:: ../examples/solana/call_anchor.sol
  140. :code: solidity
  141. Setting the program_id for a contract
  142. _____________________________________
  143. When developing contracts for Solana, programs are usually deployed to a well
  144. known account. The account can be specified in the source code using an annotation
  145. ``@program_id``. If you want to instantiate a contract using the
  146. ``new ContractName()`` syntax, then the contract must have a program_id annotation.
  147. .. include:: ../examples/solana/program_id.sol
  148. :code: solidity
  149. .. note::
  150. The program_id ``Foo5mMfYo5RhRcWa4NZ2bwFn4Kdhe8rNK5jchxsKrivA`` was generated using
  151. the command line:
  152. .. code-block:: bash
  153. solana-keygen grind --starts-with Foo:1
  154. .. _payer_seeds_bump:
  155. Setting the payer, seeds, bump, and space for a contract
  156. _________________________________________________________
  157. When a contract is instantiated, there are two accounts required: the program account to hold
  158. the executable code and the data account to save the state variables of the contract. The
  159. program account is deployed once and can be reused for updating the contract. When each
  160. Solidity contract is instantiated (also known as deployed), the data account has to be
  161. created. This can be done by the client-side code, and then the created blank account
  162. is passed to the transaction that runs the constructor code.
  163. Alternatively, the data account can be created by the constructor, on chain. When
  164. this method is used, some parameters must be specified for the account
  165. using annotations. Annotations placed above a constructor can only contain literals or
  166. constant expressions, as is the case for first ``@seed`` and ``@space`` in the following example.
  167. Annotations can also refer to constructor arguments when placed next to them, as the second ``@seed`` and
  168. the ``@bump`` examples below. The ``@payer`` annotation is a special annotation that
  169. :ref:`declares an account <account_management>`.
  170. If the contract has no constructor, annotations can be paired with an empty constructor.
  171. .. include:: ../examples/solana/constructor_annotations.sol
  172. :code: solidity
  173. Creating an account needs a payer, so at a minimum the ``@payer`` annotation must be
  174. specified. If it is missing, then the data account must be created client-side.
  175. The ``@payer`` annotation :ref:`declares a Solana account <account_management>` that must be passed in the transaction.
  176. The size of the data account can be specified with ``@space``. This is a
  177. ``uint64`` expression which can either be a constant or use one of the constructor
  178. arguments. The ``@space`` should at least be the size given when you run ``solang -v``:
  179. .. code-block:: bash
  180. $ solang compile --target solana -v examples/solana/flipper.sol
  181. ...
  182. info: contract flipper uses at least 17 bytes account data
  183. ...
  184. If the data account is going to be a
  185. `program derived address <https://docs.solana.com/developing/programming-model/calling-between-programs#program-derived-addresses>`_,
  186. then the seeds and bump have to be provided. There can be multiple seeds, and an optional
  187. single bump. If the bump is not provided, then the seeds must not create an
  188. account that falls on the curve. When placed above the constructor, the ``@seed`` can be a string literal,
  189. or a hex string with the format ``hex"4142"``. If before an argument, the seed annotation must refer to an argument
  190. of type ``bytes``. The ``@bump`` must a single byte of type ``bytes1``.
  191. .. _value_transfer:
  192. Transferring native value with a function call
  193. ______________________________________________
  194. The Solidity language on Ethereum allows value transfers with an external call
  195. or constructor, using the ``auction.bid{value: 501}()`` syntax.
  196. Solana Cross Program Invocation (CPI) does not support this, which means that:
  197. - Specifying ``value:`` on an external call or constructor is not permitted
  198. - The ``payable`` keyword has no effect
  199. - ``msg.value`` is not supported
  200. .. note::
  201. A naive way to implement this is to let the caller transfer
  202. native balance and then inform the callee about the amount transferred by
  203. specifying this in the instruction data. However, it would be trivial to
  204. forge such an operation.
  205. Receive function
  206. ________________
  207. In Solidity the ``receive()`` function, when defined, is called whenever the native
  208. balance for an account gets credited, for example through a contract calling
  209. ``account.transfer(value);``. On Solana, there is no method that implements
  210. this. The balance of an account can be credited without any code being executed.
  211. ``receive()`` functions are not permitted on the Solana target.
  212. .. _msg_sender_solana:
  213. ``msg.sender`` not available on Solana
  214. ______________________________________
  215. On Ethereum, ``msg.sender`` is used to identify either the account that submitted
  216. the transaction, or the caller when one contract calls another. On Ethereum, each
  217. contract execution can only use a single account, which provides the code and data.
  218. On Solana, each contract execution uses many accounts. Consider a rust contract which
  219. calls a Solidity contract: the rust contract can access a few data accounts, and which
  220. of those would be considered the caller? So in many cases there is not a single account
  221. which can be identified as a caller. In addition to that, the Solana VM has no
  222. mechanism for fetching the caller accounts. This means there is no way to implement
  223. ``msg.sender``.
  224. The way to implement this on Solana is to have an authority account for the contract
  225. that must be a signer for the transaction (note that on Solana there
  226. can be many signers too). This is a common construct on Solana contracts.
  227. .. include:: ../examples/solana/use_authority.sol
  228. :code: solidity
  229. Builtin Imports
  230. ________________
  231. Some builtin functionality is only available after importing. The following structs
  232. can be imported via the special builtin import file ``solana``.
  233. .. code-block:: solidity
  234. import {AccountMeta, AccountInfo} from 'solana';
  235. Note that ``{AccountMeta, AccountInfo}`` can be omitted, renamed or imported via
  236. import object.
  237. .. code-block:: solidity
  238. // Now AccountMeta will be known as AM
  239. import {AccountMeta as AM} from 'solana';
  240. // Now AccountMeta will be available as solana.AccountMeta
  241. import 'solana' as solana;
  242. .. note::
  243. The import file ``solana`` is only available when compiling for the Solana
  244. target.
  245. .. _account_info:
  246. Builtin AccountInfo
  247. +++++++++++++++++++
  248. The account info of all the accounts passed into the transaction. ``AccountInfo`` is a builtin
  249. structure with the following fields:
  250. address ``key``
  251. The address (or public key) of the account
  252. uint64 ``lamports``
  253. The lamports of the accounts. This field can be modified, however the lamports need to be
  254. balanced for all accounts by the end of the transaction.
  255. bytes ``data```
  256. The account data. This field can be modified, but use with caution.
  257. address ``owner``
  258. The program that owns this account
  259. uint64 ``rent_epoch``
  260. The next epoch when rent is due.
  261. bool ``is_signer``
  262. Did this account sign the transaction
  263. bool ``is_writable``
  264. Is this account writable in this transaction
  265. bool ``executable``
  266. Is this account a program
  267. .. _account_meta:
  268. Builtin AccountMeta
  269. +++++++++++++++++++
  270. When doing an external call (aka CPI), ``AccountMeta`` specifies which accounts
  271. should be passed to the callee.
  272. address ``pubkey``
  273. The address (or public key) of the account
  274. bool ``is_writable``
  275. Can the callee write to this account
  276. bool ``is_signer``
  277. Can the callee assume this account signed the transaction
  278. Builtin create_program_address
  279. ++++++++++++++++++++++++++++++
  280. This function returns the program derived address for a program address and
  281. the provided seeds. See the Solana documentation on
  282. `program derived addresses <https://edge.docs.solana.com/developing/programming-model/calling-between-programs#program-derived-addresses>`_.
  283. .. include:: ../examples/solana/builtin_create_program_address.sol
  284. :code: solidity
  285. Builtin try_find_program_address
  286. ++++++++++++++++++++++++++++++++
  287. This function returns the program derived address for a program address and
  288. the provided seeds, along with a seed bump. See the Solana documentation on
  289. `program derived addresses <https://edge.docs.solana.com/developing/programming-model/calling-between-programs#program-derived-addresses>`_.
  290. .. include:: ../examples/solana/builtin_try_find_program_address.sol
  291. :code: solidity
  292. Solana Library
  293. ______________
  294. In Solang's Github repository, there is a directory called ``solana-library``. It contains libraries for Solidity contracts
  295. to interact with Solana specific instructions. We provide two libraries: one for SPL tokens and another
  296. for Solana's system instructions. In order to use those functionalities, copy the correspondent library
  297. file to your project and import it.
  298. SPL-token
  299. +++++++++
  300. `spl-token <https://spl.solana.com/token>`_ is the Solana native way of creating tokens, minting, burning and
  301. transferring token. This is the Solana equivalent of
  302. `ERC-20 <https://ethereum.org/en/developers/docs/standards/tokens/erc-20/>`_ and
  303. `ERC-721 <https://ethereum.org/en/developers/docs/standards/tokens/erc-721/>`_. Solang's repository contains
  304. a library ``SplToken`` to use spl-token from Solidity. The file
  305. `spl_token.sol <https://github.com/hyperledger/solang/blob/main/solana-library/spl_token.sol>`_ should be copied into
  306. your source tree, and then imported in your solidity files where it is required. The ``SplToken`` library has doc
  307. comments explaining how it should be used.
  308. There is an example in our integration tests of how this should be used. See
  309. `token.sol <https://github.com/hyperledger/solang/blob/main/integration/solana/token.sol>`_ and
  310. `token.spec.ts <https://github.com/hyperledger/solang/blob/main/integration/solana/token.spec.ts>`_.
  311. .. _system_instruction_library:
  312. System Instructions
  313. +++++++++++++++++++
  314. Solana's system instructions enable developers to interact with Solana's System Program. There are functions to
  315. create new accounts, allocate account data, assign accounts to owning programs, transfer lamports from System Program
  316. owned accounts and pay transaction fees. More information about the functions offered can be found both on
  317. `Solana documentation <https://docs.rs/solana-program/1.11.10/solana_program/system_instruction/enum.SystemInstruction.html>`_
  318. and on Solang's `system_instruction.sol <https://github.com/hyperledger/solang/blob/main/solana-library/system_instruction.sol>`_ file.
  319. The usage of system instructions needs the correct setting of writable and signer accounts when interacting with Solidity
  320. contracts on chain. Examples are available on Solang's integration tests.
  321. See `system_instruction_example.sol <https://github.com/hyperledger/solang/blob/main/integration/solana/system_instruction_example.sol>`_
  322. and `system_instruction.spec.ts <https://github.com/hyperledger/solang/blob/main/integration/solana/system_instruction.spec.ts>`_
  323. .. _account_management:
  324. Solana Account Management
  325. _________________________
  326. In a contract constructor, one can optionally write the ``@payer`` annotation, which receives a character sequence as
  327. an argument. This annotation defines a Solana account that is going to pay for the initialization of the contract's data
  328. account. The syntax ``@payer(my_account)`` declares an account named ``my_account``, which will be
  329. required for every call to the constructor.
  330. In any Solana cross program invocation, including constructor calls, all the accounts a transaction needs must be
  331. informed. Whenever possible, the compiler will automatically generate the ``AccountMeta`` array that satisfies
  332. this requirement. Currently, that only works if the constructor call is done in an function declared external, as shown
  333. in the example below. In any other case, the ``AccountMeta`` array must be manually created, following an account ordering
  334. the IDL file specifies.
  335. The following example shows two correct ways of calling a constructor. Note that the IDL for the ``BeingBuilt`` contract
  336. has an instruction called ``new``, representing the contract's constructor, whose accounts are specified in the
  337. following order: ``dataAccount``, ``payer_account``, ``systemAccount``. That is the order one must follow when invoking
  338. such a constructor.
  339. .. include:: ../examples/solana/payer_annotation.sol
  340. :code: solidity