Explorar o código

Add function annotations 🚀 (#1557)

Lucas Steuernagel %!s(int64=2) %!d(string=hai) anos
pai
achega
2c29fd7571
Modificáronse 82 ficheiros con 1407 adicións e 961 borrados
  1. 18 0
      docs/examples/solana/account_access.sol
  2. 9 6
      docs/examples/solana/create_contract_with_metas.sol
  3. 1 1
      docs/examples/solana/expression_this_external_call.sol
  4. 1 1
      docs/examples/solana/function_call.sol
  5. 2 2
      docs/examples/solana/function_call_external.sol
  6. 2 2
      docs/examples/solana/function_type_callback.sol
  7. 4 0
      docs/examples/solana/payer_annotation.sol
  8. 1 1
      docs/examples/solana/program_id.sol
  9. 6 16
      docs/examples/solana/use_authority.sol
  10. 10 0
      docs/language/functions.rst
  11. 10 0
      docs/language/types.rst
  12. 30 13
      docs/targets/solana.rst
  13. 6 4
      integration/solana/account_data.sol
  14. 12 42
      integration/solana/account_data.spec.ts
  15. 19 18
      integration/solana/calls.spec.ts
  16. 20 23
      integration/solana/create_contract.sol
  17. 7 10
      integration/solana/create_contract.spec.ts
  18. 20 10
      integration/solana/external_call.sol
  19. 1 1
      integration/solana/runtime_errors.sol
  20. 27 39
      integration/solana/simple_collectible.sol
  21. 23 37
      integration/solana/simple_collectible.spec.ts
  22. 68 85
      integration/solana/system_instruction.spec.ts
  23. 80 27
      integration/solana/system_instruction_example.sol
  24. 35 10
      integration/solana/token.sol
  25. 47 45
      integration/solana/token.spec.ts
  26. 11 15
      solana-library/spl_token.sol
  27. 13 0
      solang-parser/src/pt.rs
  28. 2 2
      solang-parser/src/solidity.lalrpop
  29. 11 0
      solang-parser/src/tests.rs
  30. 63 7
      src/abi/tests.rs
  31. 7 7
      src/codegen/cfg.rs
  32. 2 6
      src/codegen/constant_folding.rs
  33. 1 2
      src/codegen/constructor.rs
  34. 4 5
      src/codegen/expression.rs
  35. 34 27
      src/codegen/solana_accounts/account_collection.rs
  36. 6 5
      src/codegen/solana_accounts/account_management.rs
  37. 3 2
      src/codegen/solana_deploy.rs
  38. 3 2
      src/codegen/statements/try_catch.rs
  39. 2 2
      src/codegen/strength_reduce/mod.rs
  40. 3 2
      src/codegen/subexpression_elimination/instruction.rs
  41. 2 2
      src/codegen/subexpression_elimination/tests.rs
  42. 24 4
      src/emit/binary.rs
  43. 3 33
      src/emit/instructions.rs
  44. 53 59
      src/emit/solana/mod.rs
  45. 14 7
      src/emit/solana/target.rs
  46. 60 2
      src/sema/ast.rs
  47. 105 2
      src/sema/contracts.rs
  48. 1 1
      src/sema/dotgraphviz.rs
  49. 1 1
      src/sema/expression/assign.rs
  50. 1 1
      src/sema/expression/constructor.rs
  51. 11 5
      src/sema/expression/function_call.rs
  52. 102 59
      src/sema/function_annotation.rs
  53. 2 1
      src/sema/mutability.rs
  54. 1 1
      src/sema/tests/mod.rs
  55. 3 2
      src/sema/unused_variable.rs
  56. 0 29
      stdlib/solana.c
  57. 1 1
      stdlib/solana_sdk.h
  58. 1 1
      tests/codegen_testcases/solidity/import_ext_call.sol
  59. 1 1
      tests/codegen_testcases/solidity/solana_payer_account.sol
  60. 15 0
      tests/contract_testcases/polkadot/annotations/solana_annotations.sol
  61. 1 0
      tests/contract_testcases/solana/abstract_interface.sol
  62. 26 0
      tests/contract_testcases/solana/accounts/account_collision.sol
  63. 22 0
      tests/contract_testcases/solana/accounts/accounts_required.sol
  64. 29 0
      tests/contract_testcases/solana/accounts/incorrect_annotations.sol
  65. 15 0
      tests/contract_testcases/solana/accounts/repated_declaration.sol
  66. 37 0
      tests/contract_testcases/solana/annotations/abstract_function.sol
  67. 22 0
      tests/contract_testcases/solana/annotations/accounts_on_interface.sol
  68. 1 0
      tests/contract_testcases/solana/annotations/constructor_external_function.sol
  69. 2 2
      tests/contract_testcases/solana/call/call_args_three_ways.sol
  70. 1 1
      tests/contract_testcases/solana/constant/not_constant.sol
  71. 1 1
      tests/contract_testcases/solana/destructure_assign_struct_member_2.sol
  72. 25 0
      tests/contract_testcases/solana/empty_vector.sol
  73. 2 1
      tests/contract_testcases/solana/functions/external_functions.sol
  74. 2 2
      tests/contract_testcases/solana/garbage_function_args.sol
  75. 1 0
      tests/contract_testcases/solana/type_decl_import.sol
  76. 5 7
      tests/solana_tests/abi_encode.rs
  77. 5 3
      tests/solana_tests/accessor.rs
  78. 77 0
      tests/solana_tests/account_access.rs
  79. 22 47
      tests/solana_tests/account_info.rs
  80. 32 110
      tests/solana_tests/call.rs
  81. 25 59
      tests/solana_tests/create_contract.rs
  82. 29 39
      tests/solana_tests/metas.rs

+ 18 - 0
docs/examples/solana/account_access.sol

@@ -0,0 +1,18 @@
+
+contract Foo {
+    @account(oneAccount)
+    @signer(mySigner)
+    @mutableAccount(otherAccount)
+    @mutableSigner(otherSigner)
+    function bar() external returns (uint64) {
+        assert(tx.accounts.mySigner.is_signer);
+        assert(tx.accounts.otherSigner.is_signer);
+        assert(tx.accounts.otherSigner.is_writable);
+        assert(tx.accounts.otherAccount.is_writable);
+
+        tx.accounts.otherAccount.data[0] = 0xca;
+        tx.accounts.otherSigner.data[1] = 0xfe;
+
+        return tx.accounts.oneAccount.lamports;
+    }
+}

+ 9 - 6
docs/examples/solana/create_contract_with_metas.sol

@@ -1,14 +1,17 @@
 import 'solana';
 import 'solana';
 
 
 contract creator {
 contract creator {
-    function create_with_metas(address data_account_to_initialize, address payer) public {
+
+    @mutableSigner(data_account_to_initialize)
+    @mutableSigner(payer)
+    function create_with_metas() external {
         AccountMeta[3] metas = [
         AccountMeta[3] metas = [
             AccountMeta({
             AccountMeta({
-                pubkey: data_account_to_initialize,
+                pubkey: tx.accounts.data_account_to_initialize.key,
                 is_signer: true, 
                 is_signer: true, 
                 is_writable: true}),
                 is_writable: true}),
             AccountMeta({
             AccountMeta({
-                pubkey: payer,
+                pubkey: tx.accounts.payer.key,
                 is_signer: true,
                 is_signer: true,
                 is_writable: true}),
                 is_writable: true}),
             AccountMeta({
             AccountMeta({
@@ -17,16 +20,16 @@ contract creator {
                 is_signer: false})
                 is_signer: false})
         ];
         ];
 
 
-        Child.new{accounts: metas}(payer);        
+        Child.new{accounts: metas}();        
   
   
-        Child.use_metas();
+        Child.use_metas{accounts: []}();
     }
     }
 }
 }
 
 
 @program_id("Chi1d5XD6nTAp2EyaNGqMxZzUjh6NvhXRxbGHP3D1RaT")
 @program_id("Chi1d5XD6nTAp2EyaNGqMxZzUjh6NvhXRxbGHP3D1RaT")
 contract Child {
 contract Child {
     @payer(payer)
     @payer(payer)
-    constructor(address payer) {
+    constructor() {
         print("In child constructor");
         print("In child constructor");
     }
     }
 
 

+ 1 - 1
docs/examples/solana/expression_this_external_call.sol

@@ -1,7 +1,7 @@
 @program_id("H3AthiA2C1pcMahg17nEwqr9628gkXUnnzWJJ3iSDekL")
 @program_id("H3AthiA2C1pcMahg17nEwqr9628gkXUnnzWJJ3iSDekL")
 contract kadowari {
 contract kadowari {
     function nomi() public {
     function nomi() public {
-        this.nokogiri(102);
+        this.nokogiri{accounts: []}(102);
     }
     }
 
 
     function nokogiri(int256 a) public {
     function nokogiri(int256 a) public {

+ 1 - 1
docs/examples/solana/function_call.sol

@@ -10,7 +10,7 @@ contract A {
             uint32(5)
             uint32(5)
         );
         );
 
 
-        (bool success, bytes rawresult) = v.call(data);
+        (bool success, bytes rawresult) = v.call{accounts: []}(data);
 
 
         assert(success == true);
         assert(success == true);
 
 

+ 2 - 2
docs/examples/solana/function_call_external.sol

@@ -10,7 +10,7 @@ contract foo {
 
 
 contract bar {
 contract bar {
     function test(address f) public {
     function test(address f) public {
-        (address f1, bytes32 f2) = foo.bar1{program_id: f}(102, false);
-        bool f3 = foo.bar2{program_id: f}({x: 255, y: true});
+        (address f1, bytes32 f2) = foo.bar1{program_id: f, accounts: []}(102, false);
+        bool f3 = foo.bar2{program_id: f, accounts: []}({x: 255, y: true});
     }
     }
 }
 }

+ 2 - 2
docs/examples/solana/function_type_callback.sol

@@ -1,5 +1,5 @@
 contract ft {
 contract ft {
-    function test(address p) public {
+    function test(address p) external {
         // this.callback can be used as an external function type value
         // this.callback can be used as an external function type value
         paffling.set_callback{program_id: p}(this.callback);
         paffling.set_callback{program_id: p}(this.callback);
     }
     }
@@ -19,6 +19,6 @@ contract paffling {
     }
     }
 
 
     function piffle() public {
     function piffle() public {
-        callback(1, "paffled");
+        callback{accounts: []}(1, "paffled");
     }
     }
 }
 }

+ 4 - 0
docs/examples/solana/payer_annotation.sol

@@ -30,6 +30,10 @@ contract Builder {
                 })
                 })
         ];
         ];
         BeingBuilt.new{accounts: metas}("my_seed");
         BeingBuilt.new{accounts: metas}("my_seed");
+
+
+        // No accounts are needed in this call, so we pass an empty vector.
+        BeingBuilt.say_this{accounts: []}("It's summertime!");
     }
     }
 }
 }
 
 

+ 1 - 1
docs/examples/solana/program_id.sol

@@ -17,7 +17,7 @@ contract Bar {
     }
     }
 
 
     function call_foo() public {
     function call_foo() public {
-        Foo.say_hello();
+        Foo.say_hello{accounts: []}();
     }
     }
 
 
     function foo_at_another_address(address other_foo_id) external {
     function foo_at_another_address(address other_foo_id) external {

+ 6 - 16
docs/examples/solana/use_authority.sol

@@ -4,29 +4,19 @@ contract AuthorityExample {
     address authority;
     address authority;
     uint64 counter;
     uint64 counter;
 
 
-    modifier needs_authority() {
-        for (uint64 i = 0; i < tx.accounts.length; i++) {
-            AccountInfo ai = tx.accounts[i];
-
-            if (ai.key == authority && ai.is_signer) {
-                _;
-                return;
-            }
-        }
-
-        print("not signed by authority");
-        revert();
-    }
-
     constructor(address initial_authority) {
     constructor(address initial_authority) {
         authority = initial_authority;
         authority = initial_authority;
     }
     }
 
 
-    function set_new_authority(address new_authority) needs_authority public {
+    @signer(authorityAccount)
+    function set_new_authority(address new_authority) external {
+        assert(tx.accounts.authorityAccount.key == authority && tx.accounts.authorityAccount.is_signer);
         authority = new_authority;
         authority = new_authority;
     }
     }
 
 
-    function inc() needs_authority public {
+    @signer(authorityAccount)
+    function inc() external {
+        assert(tx.accounts.authorityAccount.key == authority && tx.accounts.authorityAccount.is_signer);
         counter += 1;
         counter += 1;
     }
     }
 
 

+ 10 - 0
docs/language/functions.rst

@@ -325,6 +325,16 @@ calling.
         (bool success, bytes rawresult) = foo.call{value: 102, gas: 1000}(rawcalldata);
         (bool success, bytes rawresult) = foo.call{value: 102, gas: 1000}(rawcalldata);
     }
     }
 
 
+External calls with the ``call()`` method on Solana must have the ``accounts`` call argument, regardless of the
+callee function visibility, because the compiler has no information about the caller function to generate the
+``AccountMeta`` array automatically.
+
+.. code-block:: solidity
+
+    function test(address foo, bytes rawcalldata) public {
+        (bool success, bytes rawresult) = foo.call{accounts: []}(rawcalldata);
+    }
+
 .. _fallback_receive:
 .. _fallback_receive:
 
 
 Calling an external function using ``delegatecall``
 Calling an external function using ``delegatecall``

+ 10 - 0
docs/language/types.rst

@@ -507,6 +507,16 @@ a function on particular contract instance.
             :code: solidity
             :code: solidity
 
 
 
 
+On Solana, external calls from variables of type external functions require the ``accounts`` call argument. The
+compiler cannot determine the accounts such a function needs, so it does not automatically generate the
+``AccountsMeta`` array.
+
+.. code-block:: solidity
+
+    function test(function(int32, string) external myFunc) public {
+        myFunc{accounts: []}(24, "accounts");
+    }
+
 Storage References
 Storage References
 __________________
 __________________
 
 

+ 30 - 13
docs/targets/solana.rst

@@ -47,10 +47,11 @@ Runtime
 - Function selectors are eight bytes wide and known as *discriminators*.
 - Function selectors are eight bytes wide and known as *discriminators*.
 - Solana provides different builtins, e.g. ``block.slot`` and ``tx.accounts``.
 - Solana provides different builtins, e.g. ``block.slot`` and ``tx.accounts``.
 - When calling an external function or invoking a contract's constructor, one
 - When calling an external function or invoking a contract's constructor, one
-  :ref:`needs to provide <solana_constructor>` the necessary accounts for the transaction.
+  :ref:`needs to provide <solana_cpi_accounts>` the necessary accounts for the transaction.
 - The keyword ``this`` returns the contract's program account, also know as program id.
 - The keyword ``this`` returns the contract's program account, also know as program id.
 - Contracts :ref:`cannot be types <contracts_not_types>` on Solana and :ref:`calls to contracts <solana_contract_call>`
 - Contracts :ref:`cannot be types <contracts_not_types>` on Solana and :ref:`calls to contracts <solana_contract_call>`
   follow a different syntax.
   follow a different syntax.
+- Accounts can be declared on functions using :ref:`annotations <account_management>`.
 
 
 
 
 Compute budget
 Compute budget
@@ -399,13 +400,36 @@ an argument. This annotation defines a Solana account that is going to pay for t
 account. The syntax ``@payer(my_account)`` declares an account named ``my_account``, which will be
 account. The syntax ``@payer(my_account)`` declares an account named ``my_account``, which will be
 required for every call to the constructor.
 required for every call to the constructor.
 
 
+Similarly, for external functions in a contract, one can declare the necessary accounts, using function annotations.
+``@account(myAcc)`` declares a read only account ``myAcc``, while ``@mutableAccount(otherAcc)`` declares a mutable
+account ``otherAcc``. For signer accounts, the annotations follow the syntax ``@signer(mySigner)`` and
+``@mutableSigner(myOtherSigner)``.
+
+
+Accessing accounts' data
+++++++++++++++++++++++++
+
+Accounts declared on a constructor using the ``@payer`` annotation are available for access inside it. Likewise,
+accounts declared on external functions with any of the aforementioned annotations are also available in the
+``tx.accounts`` vector for easy access. For an account declared as ``@account(funder)``, the access follows the
+syntax ``tx.accounts.funder``, which returns the :ref:`AccountInfo builtin struct <account_info>`.
+
+.. include:: ../examples/solana/account_access.sol
+  :code: solidity
+
+.. _solana_cpi_accounts:
+
+External calls with accounts
+++++++++++++++++++++++++++++
+
 In any Solana cross program invocation, including constructor calls, all the accounts a transaction needs must be
 In any Solana cross program invocation, including constructor calls, all the accounts a transaction needs must be
-informed. Whenever possible, the compiler will automatically generate the ``AccountMeta`` array that satisfies
-this requirement. Currently, that only works if the constructor call is done in an function declared external, as shown
-in the example below. In any other case, the ``AccountMeta`` array must be manually created, following an account ordering
-the IDL file specifies.
+informed. If the ``{accounts: ...}`` call argument is missing from an external call, the compiler will automatically
+generate the ``AccountMeta`` array that satisfies such a requirement. Currently, that only works if the call is done
+in an function declared external, as shown in the example below. In any other case, the ``AccountMeta`` array must
+be manually created, following the account ordering the IDL file specifies. If a certain call does not need any accounts,
+an empty vector must be passed ``{accounts: []}``.
 
 
-The following example shows two correct ways of calling a constructor. Note that the IDL for the ``BeingBuilt`` contract
+The following example shows two correct ways of calling a contract. Note that the IDL for the ``BeingBuilt`` contract
 has an instruction called ``new``, representing the contract's constructor, whose accounts are specified in the
 has an instruction called ``new``, representing the contract's constructor, whose accounts are specified in the
 following order: ``dataAccount``, ``payer_account``, ``systemAccount``. That is the order one must follow when invoking
 following order: ``dataAccount``, ``payer_account``, ``systemAccount``. That is the order one must follow when invoking
 such a constructor.
 such a constructor.
@@ -413,10 +437,3 @@ such a constructor.
 .. include:: ../examples/solana/payer_annotation.sol
 .. include:: ../examples/solana/payer_annotation.sol
   :code: solidity
   :code: solidity
 
 
-
-Accessing accounts' data
-________________________
-
-Accounts declared on a constructor using the ``@payer`` annotation are available for access inside it.
-For an account declared as ``@payer(funder)``, the access follows the syntax ``tx.accounts.funder``, which returns
-the :ref:`AccountInfo builtin struct <account_info>`.

+ 6 - 4
integration/solana/account_data.sol

@@ -1,11 +1,13 @@
 import '../../solana-library/spl_token.sol';
 import '../../solana-library/spl_token.sol';
 
 
 contract AccountData {
 contract AccountData {
-    function token_account(address addr) view public returns (SplToken.TokenAccountData) {
-        return SplToken.get_token_account_data(addr);
+    @account(addr)
+    function token_account() view external returns (SplToken.TokenAccountData) {
+        return SplToken.get_token_account_data(tx.accounts.addr);
     }
     }
 
 
-    function mint_account(address addr) view public returns (SplToken.MintAccountData) {
-        return SplToken.get_mint_account_data(addr);
+    @account(addr)
+    function mint_account() view external returns (SplToken.MintAccountData) {
+        return SplToken.get_mint_account_data(tx.accounts.addr);
     }
     }
 }
 }

+ 12 - 42
integration/solana/account_data.spec.ts

@@ -48,13 +48,8 @@ describe('Deserialize account data', function () {
             owner.publicKey
             owner.publicKey
         );
         );
 
 
-        let res = await program.methods.tokenAccount(token_account.address)
-            .accounts({dataAccount: storage.publicKey})
-            .remainingAccounts(
-                [
-                    {pubkey: token_account.address, isSigner: false, isWritable: false}
-                ]
-            )
+        let res = await program.methods.tokenAccount()
+            .accounts({addr: token_account.address})
             .view();
             .view();
 
 
         expect(res.mintAccount).toEqual(token_account.mint);
         expect(res.mintAccount).toEqual(token_account.mint);
@@ -86,13 +81,8 @@ describe('Deserialize account data', function () {
         );
         );
         token_account = await getAccount(connection, token_account.address);
         token_account = await getAccount(connection, token_account.address);
 
 
-        res = await program.methods.tokenAccount(token_account.address)
-            .accounts({dataAccount: storage.publicKey})
-            .remainingAccounts(
-                [
-                    {pubkey: token_account.address, isSigner: false, isWritable: false}
-                ]
-            )
+        res = await program.methods.tokenAccount()
+            .accounts({addr: token_account.address})
             .view();
             .view();
 
 
         // The delegate account should be present now
         // The delegate account should be present now
@@ -112,13 +102,8 @@ describe('Deserialize account data', function () {
         );
         );
         token_account = await getAccount(connection, token_account.address);
         token_account = await getAccount(connection, token_account.address);
 
 
-        res = await program.methods.tokenAccount(token_account.address)
-            .accounts({dataAccount: storage.publicKey})
-            .remainingAccounts(
-                [
-                    {pubkey: token_account.address, isSigner: false, isWritable: false}
-                ]
-            )
+        res = await program.methods.tokenAccount()
+            .accounts({addr: token_account.address})
             .view();
             .view();
 
 
         // The close authority should be present
         // The close authority should be present
@@ -134,13 +119,8 @@ describe('Deserialize account data', function () {
             owner.publicKey
             owner.publicKey
         );
         );
 
 
-        res = await program.methods.tokenAccount(token_account.address)
-            .accounts({dataAccount: storage.publicKey})
-            .remainingAccounts(
-                [
-                    {pubkey: token_account.address, isSigner: false, isWritable: false}
-                ]
-            )
+        res = await program.methods.tokenAccount()
+            .accounts({addr: token_account.address})
             .view();
             .view();
 
 
         // Is native must be present
         // Is native must be present
@@ -182,13 +162,8 @@ describe('Deserialize account data', function () {
 
 
         let mint_data = await getMint(connection, mint);
         let mint_data = await getMint(connection, mint);
 
 
-        let res = await program.methods.mintAccount(mint)
-            .accounts({dataAccount: storage.publicKey})
-            .remainingAccounts(
-                [
-                    {pubkey: mint, isWritable: false, isSigner: false}
-                ]
-            )
+        let res = await program.methods.mintAccount()
+            .accounts({addr: mint})
             .view();
             .view();
 
 
         // Authorities are present
         // Authorities are present
@@ -222,13 +197,8 @@ describe('Deserialize account data', function () {
 
 
         mint_data = await getMint(connection, mint);
         mint_data = await getMint(connection, mint);
 
 
-        res = await program.methods.mintAccount(mint)
-            .accounts({dataAccount: storage.publicKey})
-            .remainingAccounts(
-                [
-                    {pubkey: mint, isWritable: false, isSigner: false}
-                ]
-            )
+        res = await program.methods.mintAccount()
+            .accounts({addr: mint})
             .view();
             .view();
 
 
         // Authorities are not present
         // Authorities are not present

+ 19 - 18
integration/solana/calls.spec.ts

@@ -18,57 +18,58 @@ describe('Testing calls', function () {
 
 
         await callee.program.methods.setX(new BN(102))
         await callee.program.methods.setX(new BN(102))
             .accounts({ dataAccount: callee.storage.publicKey })
             .accounts({ dataAccount: callee.storage.publicKey })
-            .rpc();
+            .rpc({commitment: "confirmed"});
 
 
         let res = await callee.program.methods.getX()
         let res = await callee.program.methods.getX()
             .accounts({ dataAccount: callee.storage.publicKey })
             .accounts({ dataAccount: callee.storage.publicKey })
-            .view();
+            .view({commitment: "confirmed"});
 
 
         expect(res).toEqual(new BN(102));
         expect(res).toEqual(new BN(102));
 
 
         res = await caller.program.methods.whoAmI()
         res = await caller.program.methods.whoAmI()
-            .view();
+            .view({commitment: "confirmed"});
 
 
         expect(res).toStrictEqual(caller.program_key);
         expect(res).toStrictEqual(caller.program_key);
 
 
-        await caller.program.methods.doCall(callee.program_key, new BN(13123))
+        await caller.program.methods.doCall(new BN(13123))
             .accounts({
             .accounts({
                 callee_dataAccount: callee.storage.publicKey,
                 callee_dataAccount: callee.storage.publicKey,
-                callee_programId: callee.program_key,
+                callee_pid: callee.program_key,
                 })
                 })
-            .rpc();
+            .rpc({commitment: "confirmed"});
 
 
         res = await callee.program.methods.getX()
         res = await callee.program.methods.getX()
             .accounts({ dataAccount: callee.storage.publicKey })
             .accounts({ dataAccount: callee.storage.publicKey })
-            .view();
+            .view({commitment: "confirmed"});
 
 
         expect(res).toEqual(new BN(13123));
         expect(res).toEqual(new BN(13123));
 
 
-        res = await caller.program.methods.doCall2(callee.program_key, new BN(20000))
+        res = await caller.program.methods.doCall2(new BN(20000))
             .accounts({
             .accounts({
                 callee_dataAccount: callee.storage.publicKey,
                 callee_dataAccount: callee.storage.publicKey,
-                callee_programId: callee.program_key,
+                callee_pid: callee.program_key,
             })
             })
-            .view();
+            .view({commitment: "confirmed"});
 
 
         expect(res).toEqual(new BN(33123));
         expect(res).toEqual(new BN(33123));
 
 
-        res = await caller.program.methods.doCall3(callee.program_key, callee2.program_key, [new BN(3), new BN(5), new BN(7), new BN(9)], "yo")
+        res = await caller.program.methods.doCall3([new BN(3), new BN(5), new BN(7), new BN(9)], "yo")
             .accounts({
             .accounts({
-                callee2_programId: callee2.program_key,
-                callee_programId: callee.program_key,
+                callee2_pid: callee2.program_key,
+                callee_pid: callee.program_key,
             })
             })
-            .view();
+            .view({commitment: "confirmed"});
 
 
         expect(res.return0).toEqual(new BN(24));
         expect(res.return0).toEqual(new BN(24));
         expect(res.return1).toBe("my name is callee");
         expect(res.return1).toBe("my name is callee");
 
 
-        res = await caller.program.methods.doCall4(callee.program_key, callee2.program_key, [new BN(1), new BN(2), new BN(3), new BN(4)], "asda")
+        res = await caller.program.methods.doCall4([new BN(1), new BN(2), new BN(3), new BN(4)], "asda")
             .accounts({
             .accounts({
-                callee2_programId: callee2.program_key,
-                callee_programId: callee.program_key,
+                callee2_pid: callee2.program_key,
+                callee_pid: callee.program_key,
+                other_callee2: callee2.program_key,
             })
             })
-            .view();
+            .view({commitment: "confirmed"});
 
 
         expect(res.return0).toEqual(new BN(10));
         expect(res.return0).toEqual(new BN(10));
         expect(res.return1).toBe("x:asda");
         expect(res.return1).toBe("x:asda");

+ 20 - 23
integration/solana/create_contract.sol

@@ -22,11 +22,13 @@ contract creator {
         Seed2.new(seed, space);
         Seed2.new(seed, space);
     }
     }
 
 
-    function create_child_with_metas(address child, address payer) public {
+    @mutableSigner(child)
+    @mutableSigner(payer)
+    function create_child_with_metas() external {
         print("Going to create child with metas");
         print("Going to create child with metas");
         AccountMeta[3] metas = [
         AccountMeta[3] metas = [
-            AccountMeta({pubkey: child, is_signer: true, is_writable: true}),
-            AccountMeta({pubkey: payer, is_signer: true, is_writable: true}),
+            AccountMeta({pubkey: tx.accounts.child.key, is_signer: true, is_writable: true}),
+            AccountMeta({pubkey: tx.accounts.payer.key, is_signer: true, is_writable: true}),
             AccountMeta({pubkey: address"11111111111111111111111111111111", is_writable: false, is_signer: false})
             AccountMeta({pubkey: address"11111111111111111111111111111111", is_writable: false, is_signer: false})
         ];
         ];
 
 
@@ -36,19 +38,14 @@ contract creator {
 
 
     function create_without_annotation() external {
     function create_without_annotation() external {
         MyCreature.new();
         MyCreature.new();
-        MyCreature.say_my_name();
+        MyCreature.say_my_name{accounts: []}();
     }
     }
 
 
-    function call_with_signer(address signer) view public {
-        for(uint32 i=0; i < tx.accounts.length; i++) {
-            if (tx.accounts[i].key == signer) {
-                require(tx.accounts[i].is_signer, "the signer must sign the transaction");
-                print("Signer found");
-                return;
-            }
-        }
-
-        revert("The signer account is missing");    }
+    @signer(my_signer)
+    function call_with_signer() view external {
+        require(tx.accounts.my_signer.is_signer, "the signer must sign the transaction");
+        print("Signer found");
+    }
 }
 }
 
 
 @program_id("Chi1d5XD6nTAp2EyaNGqMxZzUjh6NvhXRxbGHP3D1RaT")
 @program_id("Chi1d5XD6nTAp2EyaNGqMxZzUjh6NvhXRxbGHP3D1RaT")
@@ -86,16 +83,15 @@ contract Seed1 {
         print("Hello from Seed1");
         print("Hello from Seed1");
     }
     }
 
 
-    function sign(address creator_program_id) view public {
+    @account(creator_program_id)
+    function sign() view external {
         AccountMeta[1] metas = [
         AccountMeta[1] metas = [
             AccountMeta({pubkey: tx.accounts.dataAccount.key, is_signer: true, is_writable: false})
             AccountMeta({pubkey: tx.accounts.dataAccount.key, is_signer: true, is_writable: false})
         ];
         ];
 
 
-        creator.call_with_signer{
-            seeds: [ [ saved_seed, saved_bump ] ],
-            program_id: creator_program_id,
-            accounts: metas
-        }(tx.accounts.dataAccount.key);
+        creator.call_with_signer{seeds: [ [ saved_seed, saved_bump ] ],
+        accounts: metas, 
+        program_id: tx.accounts.creator_program_id.key}();
     }
     }
 }
 }
 
 
@@ -119,10 +115,11 @@ contract Seed2 {
         }
         }
     }
     }
 
 
-    function sign(address creator_program_id) view public {
+    @account(creator_program_id)
+    function sign() view external {
         bytes[2][1] seeds = [ [ "sunflower", my_seed ] ];
         bytes[2][1] seeds = [ [ "sunflower", my_seed ] ];
 
 
-        sign2(seeds, tx.accounts.dataAccount.key, creator_program_id);
+        sign2(seeds, tx.accounts.dataAccount.key, tx.accounts.creator_program_id.key);
     }
     }
 
 
     function sign2(bytes[2][1] seeds, address child, address creator_program_id) view internal {
     function sign2(bytes[2][1] seeds, address child, address creator_program_id) view internal {
@@ -130,7 +127,7 @@ contract Seed2 {
             AccountMeta({pubkey: child, is_signer: true, is_writable: false})
             AccountMeta({pubkey: child, is_signer: true, is_writable: false})
         ];
         ];
 
 
-        creator.call_with_signer{seeds: seeds, accounts: metas, program_id: creator_program_id}(child);
+        creator.call_with_signer{seeds: seeds, accounts: metas, program_id: creator_program_id}();
     }
     }
 }
 }
 
 

+ 7 - 10
integration/solana/create_contract.spec.ts

@@ -74,8 +74,8 @@ describe('ChildContract', function () {
 
 
         const seed1 = new Program(idl, seed_program, provider);
         const seed1 = new Program(idl, seed_program, provider);
 
 
-        let res = await seed1.methods.sign(program_key)
-            .accounts({ dataAccount: address, creator_programId: program_key })
+        const res = await seed1.methods.sign()
+            .accounts({ dataAccount: address, creator_program_id: program_key })
             .simulate();
             .simulate();
 
 
         expect(res.raw.toString()).toContain('Signer found');
         expect(res.raw.toString()).toContain('Signer found');
@@ -119,8 +119,8 @@ describe('ChildContract', function () {
 
 
         expect(res.raw.toString()).toContain('I am PDA.');
         expect(res.raw.toString()).toContain('I am PDA.');
 
 
-        res = await seed2.methods.sign(program_key)
-            .accounts({ dataAccount: address, creator_programId: program_key })
+        res = await seed2.methods.sign()
+            .accounts({ dataAccount: address, creator_program_id: program_key })
             .simulate();
             .simulate();
 
 
         expect(res.raw.toString()).toContain('Signer found');
         expect(res.raw.toString()).toContain('Signer found');
@@ -130,15 +130,12 @@ describe('ChildContract', function () {
         let child = Keypair.generate();
         let child = Keypair.generate();
         let child_program = new PublicKey("Chi1d5XD6nTAp2EyaNGqMxZzUjh6NvhXRxbGHP3D1RaT");
         let child_program = new PublicKey("Chi1d5XD6nTAp2EyaNGqMxZzUjh6NvhXRxbGHP3D1RaT");
 
 
-        const signature = await program.methods.createChildWithMetas(child.publicKey, payer.publicKey)
+        const signature = await program.methods.createChildWithMetas()
             .accounts({
             .accounts({
-                dataAccount: storage.publicKey,
+                child: child.publicKey,
+                payer: payer.publicKey,
                 Child_programId: child_program,
                 Child_programId: child_program,
             })
             })
-            .remainingAccounts([
-                { pubkey: child.publicKey, isSigner: true, isWritable: true },
-                { pubkey: payer.publicKey, isSigner: true, isWritable: true },
-            ])
             .signers([payer, child])
             .signers([payer, child])
             .rpc({ commitment: 'confirmed' });
             .rpc({ commitment: 'confirmed' });
 
 

+ 20 - 10
integration/solana/external_call.sol

@@ -1,21 +1,30 @@
 
 
 contract caller {
 contract caller {
-    function do_call(address e, int64 v) public {
-        callee.set_x{program_id: e}(v);
+    
+    @account(callee_pid)
+    function do_call(int64 v) external {
+        callee.set_x{program_id: tx.accounts.callee_pid.key}(v);
     }
     }
 
 
-    function do_call2(address e, int64 v) view public returns (int64) {
-        return v + callee.get_x{program_id: e}();
+    @account(callee_pid)
+    function do_call2(int64 v) view external returns (int64) {
+        return v + callee.get_x{program_id: tx.accounts.callee_pid.key}();
     }
     }
 
 
     // call two different functions
     // call two different functions
-    function do_call3(address e, address e2, int64[4] memory x, string memory y) pure public returns (int64, string memory) {
-        return (callee2.do_stuff{program_id: e2}(x), callee.get_name{program_id: e}());
+    @account(callee_pid)
+    @account(callee2_pid)
+    function do_call3(int64[4] memory x, string memory y) external returns (int64, string memory) {
+        return (callee2.do_stuff{program_id: tx.accounts.callee2_pid.key}(x), 
+                callee.get_name{program_id: tx.accounts.callee_pid.key}());
     }
     }
 
 
     // call two different functions
     // call two different functions
-    function do_call4(address e, address e2, int64[4] memory x, string memory y) pure public returns (int64, string memory) {
-        return (callee2.do_stuff{program_id: e2}(x), callee.call2{program_id: e}(e2, y));
+    @account(callee_pid)
+    @account(callee2_pid)
+    function do_call4(int64[4] memory x, string memory y) external returns (int64, string memory) {
+        return (callee2.do_stuff{program_id: tx.accounts.callee2_pid.key}(x), 
+                callee.call2{program_id: tx.accounts.callee_pid.key}(y));
     }
     }
 
 
     function who_am_i() public view returns (address) {
     function who_am_i() public view returns (address) {
@@ -34,8 +43,9 @@ contract callee {
         return x;
         return x;
     }
     }
 
 
-    function call2(address e2, string s) public pure returns (string) {
-        return callee2.do_stuff2{program_id: e2}(s);
+    @account(other_callee2)
+    function call2(string s) external returns (string) {
+        return callee2.do_stuff2{program_id: tx.accounts.other_callee2.key}(s);
     }
     }
 
 
     function get_name() public pure returns (string) {
     function get_name() public pure returns (string) {

+ 1 - 1
integration/solana/runtime_errors.sol

@@ -62,7 +62,7 @@ contract RuntimeErrors {
 
 
     // external call failed
     // external call failed
     function call_ext() public {
     function call_ext() public {
-        Creature.say_my_name();
+        Creature.say_my_name{accounts: []}();
     }
     }
 
 
     function i_will_revert() public {
     function i_will_revert() public {

+ 27 - 39
integration/solana/simple_collectible.sol

@@ -23,29 +23,24 @@ contract SimpleCollectible {
         metadataAuthority = _metadataAuthority;
         metadataAuthority = _metadataAuthority;
     }
     }
 
 
-    /// Create a new NFT and associate it to an URI
-    ///
-    /// @param tokenURI a URI that leads to the NFT resource
-    /// @param mintAuthority an account that signs each new mint
-    /// @param ownerTokenAccount the owner associated token account
-    function createCollectible(string memory tokenURI, address mintAuthority, address ownerTokenAccount) public {
-        SplToken.TokenAccountData token_data = SplToken.get_token_account_data(ownerTokenAccount);
-
-        // The mint will only work if the associated token account points to the mint account in this contract
-        // This assert is not necessary. The transaction will fail if this does not hold true.
-        assert(mintAccount == token_data.mintAccount);
-        SplToken.MintAccountData mint_data = SplToken.get_mint_account_data(token_data.mintAccount);
+    /// Create a new NFT
+    @mutableAccount(mintAccount) // The account of the mint. Its address must be the same as that of the 'mintAccount' contract variable.
+    @mutableAccount(ownerTokenAccount) // The owner's associated token account
+    @signer(mintAuthority) // The account that signs each new mint
+    function createCollectible() external {
+        SplToken.TokenAccountData token_data = SplToken.get_token_account_data(tx.accounts.ownerTokenAccount);
+
+        SplToken.MintAccountData mint_data = SplToken.get_mint_account_data(tx.accounts.mintAccount);
         // Ensure the supply is zero. Otherwise, this is not an NFT.
         // Ensure the supply is zero. Otherwise, this is not an NFT.
         assert(mint_data.supply == 0);
         assert(mint_data.supply == 0);
 
 
         // An NFT on Solana is a SPL-Token with only one minted token.
         // An NFT on Solana is a SPL-Token with only one minted token.
         // The token account saves the owner of the tokens minted with the mint account, the respective mint account and the number
         // The token account saves the owner of the tokens minted with the mint account, the respective mint account and the number
         // of tokens the owner account owns
         // of tokens the owner account owns
-        SplToken.mint_to(token_data.mintAccount, ownerTokenAccount, mintAuthority, 1);
-        updateNftUri(tokenURI);
+        SplToken.mint_to(tx.accounts.mintAccount.key, tx.accounts.ownerTokenAccount.key, tx.accounts.mintAuthority.key, 1);
 
 
         // Set the mint authority to null. This prevents that any other new tokens be minted, ensuring we have an NFT.
         // Set the mint authority to null. This prevents that any other new tokens be minted, ensuring we have an NFT.
-        SplToken.remove_mint_authority(mintAccount, mintAuthority);
+        SplToken.remove_mint_authority(tx.accounts.mintAccount.key, tx.accounts.mintAuthority.key);
 
 
         // Log on blockchain records information about the created token
         // Log on blockchain records information about the created token
         emit NFTMinted(token_data.owner, token_data.mintAccount);
         emit NFTMinted(token_data.owner, token_data.mintAccount);
@@ -54,18 +49,21 @@ contract SimpleCollectible {
     /// Transfer ownership of this NFT from one account to another
     /// Transfer ownership of this NFT from one account to another
     /// This function only wraps the innate SPL transfer, which can be used outside this contract.
     /// This function only wraps the innate SPL transfer, which can be used outside this contract.
     /// However, the difference here is the event 'NFTSold' exclusive to this function
     /// However, the difference here is the event 'NFTSold' exclusive to this function
-    ///
-    /// @param oldTokenAccount the token account for the current owner
-    /// @param newTokenAccount the token account for the new owner
-    function transferOwnership(address oldTokenAccount, address newTokenAccount) public {
+    @mutableAccount(oldTokenAccount) // The token account for the current owner
+    @mutableAccount(newTokenAccount) // The token account for the new owner
+    @signer(oldOwner)
+    function transferOwnership() external {
         // The current owner does not need to be the caller of this functions, but they need to sign the transaction
         // The current owner does not need to be the caller of this functions, but they need to sign the transaction
         // with their private key.
         // with their private key.
-        SplToken.TokenAccountData old_data = SplToken.get_token_account_data(oldTokenAccount);
-        SplToken.TokenAccountData new_data = SplToken.get_token_account_data(newTokenAccount);
+        SplToken.TokenAccountData old_data = SplToken.get_token_account_data(tx.accounts.oldTokenAccount);
+        SplToken.TokenAccountData new_data = SplToken.get_token_account_data(tx.accounts.newTokenAccount);
 
 
         // To transfer the ownership of a token, we need the current owner and the new owner. The payer account is the account used to derive
         // To transfer the ownership of a token, we need the current owner and the new owner. The payer account is the account used to derive
         // the correspondent token account in TypeScript.
         // the correspondent token account in TypeScript.
-        SplToken.transfer(oldTokenAccount, newTokenAccount, old_data.owner, 1);
+        SplToken.transfer(
+            tx.accounts.oldTokenAccount.key, 
+            tx.accounts.newTokenAccount.key, 
+            tx.accounts.oldOwner.key, 1);
         emit NFTSold(old_data.owner, new_data.owner);
         emit NFTSold(old_data.owner, new_data.owner);
     }
     }
 
 
@@ -77,9 +75,9 @@ contract SimpleCollectible {
     /// Check if an NFT is owned by @param owner
     /// Check if an NFT is owned by @param owner
     ///
     ///
     /// @param owner the account whose ownership we want to verify
     /// @param owner the account whose ownership we want to verify
-    /// @param tokenAccount the owner's associated token account
-    function isOwner(address owner, address tokenAccount) public view returns (bool) {
-        SplToken.TokenAccountData data = SplToken.get_token_account_data(tokenAccount);
+    @account(tokenAccount) // The owner's associated token account
+    function isOwner(address owner) external view returns (bool) {
+        SplToken.TokenAccountData data = SplToken.get_token_account_data(tx.accounts.tokenAccount);
 
 
         return owner == data.owner && mintAccount == data.mintAccount && data.balance == 1;
         return owner == data.owner && mintAccount == data.mintAccount && data.balance == 1;
     }
     }
@@ -88,20 +86,10 @@ contract SimpleCollectible {
     /// The metadata authority must sign the transaction so that the update can succeed.
     /// The metadata authority must sign the transaction so that the update can succeed.
     ///
     ///
     /// @param newUri a new URI for the NFT
     /// @param newUri a new URI for the NFT
-    function updateNftUri(string newUri) public {
-        requireMetadataSigner();
+    @signer(metadataSigner) // The metadata authority that can authorize changes in the NFT data.
+    function updateNftUri(string newUri) external {
+        require(tx.accounts.metadataSigner.is_signer, "the metadata authority must sign the transaction");
+        assert(tx.accounts.metadataSigner.key == metadataAuthority);
         uri = newUri;
         uri = newUri;
     }
     }
-
-    /// Requires the signature of the metadata authority.
-    function requireMetadataSigner() private {
-        for(uint32 i=0; i < tx.accounts.length; i++) {
-            if (tx.accounts[i].key == metadataAuthority) {
-                require(tx.accounts[i].is_signer, "the metadata authority must sign the transaction");
-                return;
-            }
-        }
-
-        revert("The metadata authority is missing");
-    }
 }
 }

+ 23 - 37
integration/solana/simple_collectible.spec.ts

@@ -43,18 +43,14 @@ describe('Simple collectible', function () {
         const nft_uri = "www.nft.com";
         const nft_uri = "www.nft.com";
 
 
         // Create a collectible for an owner given a mint authority.
         // Create a collectible for an owner given a mint authority.
-        await program.methods.createCollectible(
-            nft_uri,
-            mint_authority.publicKey,
-            owner_token_account.address)
-            .accounts({ dataAccount: storage.publicKey })
-            .remainingAccounts([
-                { pubkey: mint, isSigner: false, isWritable: true },
-                { pubkey: owner_token_account.address, isSigner: false, isWritable: true },
-                { pubkey: mint_authority.publicKey, isSigner: true, isWritable: false },
-                { pubkey: metadata_authority.publicKey, isSigner: true, isWritable: true }
-            ])
-            .signers([mint_authority, metadata_authority])
+        await program.methods.createCollectible()
+            .accounts({
+                dataAccount: storage.publicKey,
+                mintAccount: mint,
+                ownerTokenAccount: owner_token_account.address,
+                mintAuthority: mint_authority.publicKey,
+            })
+            .signers([mint_authority])
             .rpc();
             .rpc();
 
 
         const new_owner = Keypair.generate();
         const new_owner = Keypair.generate();
@@ -69,43 +65,33 @@ describe('Simple collectible', function () {
 
 
 
 
         // Transfer ownership to another owner
         // Transfer ownership to another owner
-        await program.methods.transferOwnership(
-            owner_token_account.address,
-            new_owner_token_account.address)
-            .remainingAccounts([
-                { pubkey: new_owner_token_account.address, isSigner: false, isWritable: true },
-                { pubkey: owner_token_account.address, isSigner: false, isWritable: true },
-                { pubkey: nft_owner.publicKey, isSigner: true, isWritable: false },
-            ])
+        await program.methods.transferOwnership()
+            .accounts({
+                oldTokenAccount: owner_token_account.address,
+                newTokenAccount: new_owner_token_account.address,
+                oldOwner: nft_owner.publicKey
+            })
             .signers([nft_owner])
             .signers([nft_owner])
             .rpc();
             .rpc();
 
 
         // Confirm that the ownership transference worked
         // Confirm that the ownership transference worked
         const verify_transfer_result = await program.methods.isOwner(
         const verify_transfer_result = await program.methods.isOwner(
-            new_owner.publicKey,
-            new_owner_token_account.address)
-            .accounts({ dataAccount: storage.publicKey })
-            .remainingAccounts([
-                { pubkey: new_owner_token_account.address, isSigner: false, isWritable: false },
-            ])
+            new_owner.publicKey)
+            .accounts({
+                dataAccount: storage.publicKey,
+                tokenAccount: new_owner_token_account.address
+            })
             .view();
             .view();
 
 
         expect(verify_transfer_result).toBe(true);
         expect(verify_transfer_result).toBe(true);
 
 
-        // Retrieve information about the NFT
-        const token_uri = await program.methods.getNftUri()
-            .accounts({ dataAccount: storage.publicKey })
-            .view();
-
-        expect(token_uri).toBe(nft_uri);
-
         // Update the NFT URI
         // Update the NFT URI
         const new_uri = "www.token.com";
         const new_uri = "www.token.com";
         await program.methods.updateNftUri(new_uri)
         await program.methods.updateNftUri(new_uri)
-            .accounts({ dataAccount: storage.publicKey })
-            .remainingAccounts([
-                { pubkey: metadata_authority.publicKey, isSigner: true, isWritable: true },
-            ])
+            .accounts({
+                dataAccount: storage.publicKey,
+                metadataSigner: metadata_authority.publicKey
+            })
             .signers([metadata_authority])
             .signers([metadata_authority])
             .rpc();
             .rpc();
 
 

+ 68 - 85
integration/solana/system_instruction.spec.ts

@@ -18,54 +18,55 @@ describe('Test system instructions', function () {
         const to_key_pair = Keypair.generate();
         const to_key_pair = Keypair.generate();
 
 
         await program.methods.createAccount(
         await program.methods.createAccount(
-            payer.publicKey,
-            to_key_pair.publicKey,
             new BN(100000000),
             new BN(100000000),
             new BN(5),
             new BN(5),
-            TOKEN_PROGRAM_ID)
+            TOKEN_PROGRAM_ID
+        ).accounts(
+                {
+                    from: payer.publicKey,
+                    to: to_key_pair.publicKey
+                }
+            )
             .remainingAccounts([
             .remainingAccounts([
                 { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
                 { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
-                { pubkey: payer.publicKey, isSigner: true, isWritable: false },
-                { pubkey: to_key_pair.publicKey, isSigner: true, isWritable: true },
             ])
             ])
             .signers([payer, to_key_pair]).rpc();
             .signers([payer, to_key_pair]).rpc();
     });
     });
 
 
     it('create account with seed', async function create_account_with_seed() {
     it('create account with seed', async function create_account_with_seed() {
-        const { storage, payer, program } = await loadContractAndCallConstructor('TestingInstruction');
+        const { payer, program } = await loadContractAndCallConstructor('TestingInstruction');
         const base_keypair = Keypair.generate();
         const base_keypair = Keypair.generate();
         const to_key_pair = await PublicKey.createWithSeed(base_keypair.publicKey, seed, TOKEN_PROGRAM_ID);
         const to_key_pair = await PublicKey.createWithSeed(base_keypair.publicKey, seed, TOKEN_PROGRAM_ID);
 
 
         await program.methods.createAccountWithSeed(
         await program.methods.createAccountWithSeed(
-            payer.publicKey,
-            to_key_pair,
-            base_keypair.publicKey,
             seed,
             seed,
             new BN(100000000),
             new BN(100000000),
             new BN(5),
             new BN(5),
             TOKEN_PROGRAM_ID)
             TOKEN_PROGRAM_ID)
+            .accounts({
+                from: payer.publicKey,
+                to: to_key_pair,
+                base: base_keypair.publicKey
+            })
             .remainingAccounts([
             .remainingAccounts([
                 { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
                 { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
-                { pubkey: payer.publicKey, isSigner: true, isWritable: false },
-                { pubkey: to_key_pair, isSigner: false, isWritable: true },
-                { pubkey: base_keypair.publicKey, isSigner: true, isWritable: false },
             ])
             ])
             .signers([payer, base_keypair]).rpc();
             .signers([payer, base_keypair]).rpc();
     });
     });
 
 
     it('assign', async function assign() {
     it('assign', async function assign() {
-        const { storage, payer, program } = await loadContractAndCallConstructor('TestingInstruction');
+        const { program } = await loadContractAndCallConstructor('TestingInstruction');
         const to_key_pair = Keypair.generate();
         const to_key_pair = Keypair.generate();
 
 
         const assign_account = new PublicKey('AddressLookupTab1e1111111111111111111111111');
         const assign_account = new PublicKey('AddressLookupTab1e1111111111111111111111111');
         await program.methods.assign(
         await program.methods.assign(
-            to_key_pair.publicKey,
             assign_account)
             assign_account)
-            .remainingAccounts([
-                { pubkey: payer.publicKey, isSigner: false, isWritable: false },
-                { pubkey: to_key_pair.publicKey, isSigner: true, isWritable: true },
-            ])
-            .signers([payer, to_key_pair]).rpc();
+            .accounts(
+                {
+                    assignAccount: to_key_pair.publicKey,
+                }
+            )
+            .signers([to_key_pair]).rpc();
     });
     });
 
 
     it('assign with seed', async function assign_with_with_seed() {
     it('assign with seed', async function assign_with_with_seed() {
@@ -74,15 +75,12 @@ describe('Test system instructions', function () {
         const to_key_pair = await PublicKey.createWithSeed(payer.publicKey, seed, assign_account);
         const to_key_pair = await PublicKey.createWithSeed(payer.publicKey, seed, assign_account);
 
 
         await program.methods.assignWithSeed(
         await program.methods.assignWithSeed(
-            to_key_pair,
-            payer.publicKey,
             seed,
             seed,
             assign_account)
             assign_account)
-            .remainingAccounts([
-                { pubkey: assign_account, isSigner: false, isWritable: false },
-                { pubkey: payer.publicKey, isSigner: false, isWritable: false },
-                { pubkey: to_key_pair, isSigner: false, isWritable: true },
-            ])
+            .accounts({
+                assignAccount: to_key_pair,
+                owner: payer.publicKey,
+            })
             .signers([payer]).rpc();
             .signers([payer]).rpc();
     });
     });
 
 
@@ -91,18 +89,16 @@ describe('Test system instructions', function () {
         const dest = new Keypair();
         const dest = new Keypair();
 
 
         await program.methods.transfer(
         await program.methods.transfer(
-            payer.publicKey,
-            dest.publicKey,
             new BN(100000000))
             new BN(100000000))
-            .remainingAccounts([
-                { pubkey: payer.publicKey, isSigner: false, isWritable: true },
-                { pubkey: dest.publicKey, isSigner: false, isWritable: true },
-            ])
+            .accounts({
+                from: payer.publicKey,
+                to: dest.publicKey,
+            })
             .signers([payer]).rpc();
             .signers([payer]).rpc();
     });
     });
 
 
     it('transfer with seed', async function transfer_with_seed() {
     it('transfer with seed', async function transfer_with_seed() {
-        const { storage, payer, provider, program } = await loadContractAndCallConstructor('TestingInstruction');
+        const { payer, provider, program } = await loadContractAndCallConstructor('TestingInstruction');
         const dest = new Keypair();
         const dest = new Keypair();
         const assign_account = new PublicKey('AddressLookupTab1e1111111111111111111111111');
         const assign_account = new PublicKey('AddressLookupTab1e1111111111111111111111111');
         const derived_payer = await PublicKey.createWithSeed(payer.publicKey, seed, assign_account);
         const derived_payer = await PublicKey.createWithSeed(payer.publicKey, seed, assign_account);
@@ -111,32 +107,27 @@ describe('Test system instructions', function () {
         await provider.connection.confirmTransaction(signature, 'confirmed');
         await provider.connection.confirmTransaction(signature, 'confirmed');
 
 
         await program.methods.transferWithSeed(
         await program.methods.transferWithSeed(
-            derived_payer, // from_pubkey
-            payer.publicKey, // from_base
             seed, // seed
             seed, // seed
             assign_account, // from_owner
             assign_account, // from_owner
-            dest.publicKey, // to_pubkey
             new BN(100000000))
             new BN(100000000))
-            .remainingAccounts([
-                { pubkey: assign_account, isSigner: false, isWritable: false },
-                { pubkey: derived_payer, isSigner: false, isWritable: true },
-                { pubkey: dest.publicKey, isSigner: false, isWritable: true },
-                { pubkey: payer.publicKey, isSigner: true, isWritable: false },
-            ])
-            .signers([payer]).rpc();
+            .accounts(
+                {
+                    fromKey: derived_payer,
+                    fromBase: payer.publicKey,
+                    toKey: dest.publicKey,
+                }
+            )
+            .signers([payer]).rpc({commitment: "confirmed"});
     });
     });
 
 
     it('allocate', async function allocate() {
     it('allocate', async function allocate() {
-        const { storage, payer, program } = await loadContractAndCallConstructor('TestingInstruction');
+        const {  program } = await loadContractAndCallConstructor('TestingInstruction');
         const account = Keypair.generate();
         const account = Keypair.generate();
 
 
         await program.methods.allocate(
         await program.methods.allocate(
-            account.publicKey,
             new BN(2))
             new BN(2))
-            .remainingAccounts([
-                { pubkey: account.publicKey, isSigner: true, isWritable: true },
-            ])
-            .signers([payer, account]).rpc();
+            .accounts({accKey: account.publicKey})
+            .signers([account]).rpc();
     });
     });
 
 
     it('allocate with seed', async function allocate_with_seed() {
     it('allocate with seed', async function allocate_with_seed() {
@@ -146,17 +137,14 @@ describe('Test system instructions', function () {
         const derived_key = await PublicKey.createWithSeed(account.publicKey, seed, owner);
         const derived_key = await PublicKey.createWithSeed(account.publicKey, seed, owner);
 
 
         await program.methods.allocateWithSeed(
         await program.methods.allocateWithSeed(
-            derived_key,
-            account.publicKey,
             seed,
             seed,
             new BN(200),
             new BN(200),
             owner)
             owner)
-            .remainingAccounts([
-                { pubkey: owner, isSigner: false, isWritable: false },
-                { pubkey: account.publicKey, isSigner: true, isWritable: false },
-                { pubkey: derived_key, isSigner: false, isWritable: true },
-            ])
-            .signers([payer, account]).rpc();
+            .accounts({
+                accKey: derived_key,
+                base: account.publicKey,
+            })
+            .signers([account]).rpc();
     });
     });
 
 
     it('create nonce account with seed', async function create_nonce_account_with_seed() {
     it('create nonce account with seed', async function create_nonce_account_with_seed() {
@@ -166,20 +154,19 @@ describe('Test system instructions', function () {
         const authority = Keypair.generate();
         const authority = Keypair.generate();
 
 
         await program.methods.createNonceAccountWithSeed(
         await program.methods.createNonceAccountWithSeed(
-            payer.publicKey,
-            derived_account,
-            base_address.publicKey,
             seed,
             seed,
             authority.publicKey,
             authority.publicKey,
             new BN(100000000))
             new BN(100000000))
+            .accounts(
+                {from: payer.publicKey,
+                nonce: derived_account,
+                base: base_address.publicKey}
+            )
             .remainingAccounts([
             .remainingAccounts([
                 { pubkey: recent_block_hashes, isSigner: false, isWritable: false },
                 { pubkey: recent_block_hashes, isSigner: false, isWritable: false },
                 { pubkey: rentAddress, isSigner: false, isWritable: false },
                 { pubkey: rentAddress, isSigner: false, isWritable: false },
-                { pubkey: payer.publicKey, isSigner: false, isWritable: true },
-                { pubkey: derived_account, isSigner: false, isWritable: true },
-                { pubkey: base_address.publicKey, isSigner: true, isWritable: true },
             ])
             ])
-            .signers([payer, base_address]).rpc();
+            .signers([payer, base_address]).rpc({commitment: "confirmed"});
     });
     });
 
 
     it('nonce accounts', async function nonce_accounts() {
     it('nonce accounts', async function nonce_accounts() {
@@ -188,52 +175,48 @@ describe('Test system instructions', function () {
         const authority = Keypair.generate();
         const authority = Keypair.generate();
 
 
         await program.methods.createNonceAccount(
         await program.methods.createNonceAccount(
-            payer.publicKey,
-            nonce.publicKey,
             authority.publicKey,
             authority.publicKey,
             new BN(100000000))
             new BN(100000000))
+            .accounts(
+                {
+                    from: payer.publicKey,
+                    nonce: nonce.publicKey
+                }
+            )
             .remainingAccounts([
             .remainingAccounts([
                 { pubkey: recent_block_hashes, isSigner: false, isWritable: false },
                 { pubkey: recent_block_hashes, isSigner: false, isWritable: false },
                 { pubkey: rentAddress, isSigner: false, isWritable: false },
                 { pubkey: rentAddress, isSigner: false, isWritable: false },
-                { pubkey: payer.publicKey, isSigner: false, isWritable: true },
-                { pubkey: nonce.publicKey, isSigner: true, isWritable: true },
             ])
             ])
             .signers([payer, nonce]).rpc();
             .signers([payer, nonce]).rpc();
 
 
-        await program.methods.advanceNonceAccount(
-            nonce.publicKey,
-            authority.publicKey)
+        await program.methods.advanceNonceAccount(authority.publicKey)
+            .accounts({nonce: nonce.publicKey})
             .remainingAccounts([
             .remainingAccounts([
                 { pubkey: recent_block_hashes, isSigner: false, isWritable: false },
                 { pubkey: recent_block_hashes, isSigner: false, isWritable: false },
                 { pubkey: authority.publicKey, isSigner: true, isWritable: false },
                 { pubkey: authority.publicKey, isSigner: true, isWritable: false },
-                { pubkey: nonce.publicKey, isSigner: false, isWritable: true },
             ])
             ])
             .signers([authority]).rpc();
             .signers([authority]).rpc();
 
 
         await program.methods.withdrawNonceAccount(
         await program.methods.withdrawNonceAccount(
-            nonce.publicKey,
-            authority.publicKey,
-            payer.publicKey,
             new BN(1000))
             new BN(1000))
+            .accounts({
+                nonce: nonce.publicKey,
+                to: payer.publicKey,
+                authority: authority.publicKey
+            })
             .remainingAccounts([
             .remainingAccounts([
                 { pubkey: recent_block_hashes, isSigner: false, isWritable: false },
                 { pubkey: recent_block_hashes, isSigner: false, isWritable: false },
                 { pubkey: rentAddress, isSigner: false, isWritable: false },
                 { pubkey: rentAddress, isSigner: false, isWritable: false },
-                { pubkey: authority.publicKey, isSigner: true, isWritable: false },
-                { pubkey: nonce.publicKey, isSigner: false, isWritable: true },
-                { pubkey: payer.publicKey, isSigner: false, isWritable: true },
             ])
             ])
             .signers([authority]).rpc();
             .signers([authority]).rpc();
 
 
         const new_authority = Keypair.generate();
         const new_authority = Keypair.generate();
         await program.methods.authorizeNonceAccount(
         await program.methods.authorizeNonceAccount(
-            nonce.publicKey,
-            authority.publicKey,
             new_authority.publicKey)
             new_authority.publicKey)
-
-            .remainingAccounts([
-                { pubkey: authority.publicKey, isSigner: true, isWritable: false },
-                { pubkey: nonce.publicKey, isSigner: false, isWritable: true },
-            ])
+            .accounts({
+                nonce: nonce.publicKey,
+                authority: authority.publicKey
+            })
             .signers([authority]).rpc();
             .signers([authority]).rpc();
     });
     });
 });
 });

+ 80 - 27
integration/solana/system_instruction_example.sol

@@ -3,60 +3,113 @@
 import '../../solana-library/system_instruction.sol';
 import '../../solana-library/system_instruction.sol';
 
 
 contract TestingInstruction {
 contract TestingInstruction {
-    function create_account(address from, address to, uint64 lamports, uint64 space, address owner) public {
-        SystemInstruction.create_account(from, to, lamports, space, owner);
+
+    @mutableSigner(from)
+    @mutableSigner(to)
+    function create_account(uint64 lamports, uint64 space, address owner) external {
+        SystemInstruction.create_account(tx.accounts.from.key, tx.accounts.to.key, lamports, space, owner);
     }
     }
 
 
-    function create_account_with_seed(address from, address to, address base, string seed, uint64 lamports, uint64 space, address owner) public {
-        SystemInstruction.create_account_with_seed(from, to, base, seed, lamports, space, owner);
+    @mutableSigner(from)
+    @mutableAccount(to)
+    @signer(base)
+    function create_account_with_seed(string seed, uint64 lamports, uint64 space, address owner) external {
+        SystemInstruction.create_account_with_seed(
+            tx.accounts.from.key, tx.accounts.to.key, tx.accounts.base.key, seed, lamports, space, owner);
     }
     }
 
 
-    function assign(address account, address owner) public {
-        SystemInstruction.assign(account, owner);
+    @mutableSigner(assignAccount)
+    function assign(address owner) external {
+        SystemInstruction.assign(tx.accounts.assignAccount.key, owner);
     }
     }
 
 
-    function assign_with_seed(address account, address base, string seed, address owner) public {
-        SystemInstruction.assign_with_seed(account, base, seed, owner);
+    @mutableAccount(assignAccount)
+    @signer(base)
+    function assign_with_seed(string seed, address owner) external {
+        SystemInstruction.assign_with_seed(tx.accounts.assignAccount.key, tx.accounts.base.key, seed, owner);
     }
     }
 
 
-    function transfer(address from, address to, uint64 lamports) public {
-        SystemInstruction.transfer(from, to, lamports);
+    @mutableSigner(from)
+    @mutableAccount(to)
+    function transfer(uint64 lamports) external {
+        SystemInstruction.transfer(tx.accounts.from.key, tx.accounts.to.key, lamports);
     }
     }
 
 
-    function transfer_with_seed(address from_pubkey, address from_base, string seed, address from_owner, address to_pubkey, uint64 lamports) public {
-        SystemInstruction.transfer_with_seed(from_pubkey, from_base, seed, from_owner, to_pubkey, lamports);
+    @mutableAccount(fromKey)
+    @signer(fromBase)
+    @mutableAccount(toKey)
+    function transfer_with_seed(string seed, address from_owner, uint64 lamports) external {
+        SystemInstruction.transfer_with_seed(
+            tx.accounts.fromKey.key, 
+            tx.accounts.fromBase.key, 
+            seed, 
+            from_owner, 
+            tx.accounts.toKey.key, 
+            lamports);
     }
     }
 
 
-    function allocate(address pub_key, uint64 space) public {
-        SystemInstruction.allocate(pub_key, space);
+    @mutableSigner(accKey)
+    function allocate(uint64 space) external {
+        SystemInstruction.allocate(tx.accounts.accKey.key, space);
     }
     }
 
 
-    function allocate_with_seed(address addr, address base, string seed, uint64 space, address owner) public {
-        SystemInstruction.allocate_with_seed(addr, base, seed, space, owner);
+    @mutableAccount(accKey)
+    @signer(base)
+    function allocate_with_seed(string seed, uint64 space, address owner) external {
+        SystemInstruction.allocate_with_seed(
+            tx.accounts.accKey.key, 
+            tx.accounts.base.key, 
+            seed, 
+            space, 
+            owner);
     }
     }
 
 
-    function create_nonce_account_with_seed(address from, address nonce, address base, string seed, address authority, uint64 lamports) public {
-        SystemInstruction.create_nonce_account_with_seed(from, nonce, base, seed, authority, lamports);
+    @mutableSigner(from)
+    @mutableAccount(nonce)
+    @signer(base)
+    function create_nonce_account_with_seed(string seed, address authority, uint64 lamports) external {
+        SystemInstruction.create_nonce_account_with_seed(
+            tx.accounts.from.key, 
+            tx.accounts.nonce.key,
+            tx.accounts.base.key,
+            seed, 
+            authority,
+            lamports);
     }
     }
 
 
-    function create_nonce_account(address from, address nonce, address authority, uint64 lamports) public {
-        SystemInstruction.create_nonce_account(from, nonce, authority, lamports);
+    @mutableSigner(from)
+    @mutableSigner(nonce)
+    function create_nonce_account(address authority, uint64 lamports) external {
+        SystemInstruction.create_nonce_account(tx.accounts.from.key,
+         tx.accounts.nonce.key, authority, lamports);
     }
     }
 
 
-    function advance_nonce_account(address nonce, address authorized) public {
-        SystemInstruction.advance_nonce_account(nonce, authorized);
+    @mutableAccount(nonce)
+    function advance_nonce_account(address authorized) external {
+        SystemInstruction.advance_nonce_account(
+            tx.accounts.nonce.key, authorized);
     }
     }
 
 
-    function withdraw_nonce_account(address nonce, address authority, address to, uint64 lamports) public {
-        SystemInstruction.withdraw_nonce_account(nonce, authority, to, lamports);
+    @mutableAccount(nonce)
+    @mutableAccount(to)
+    @signer(authority)
+    function withdraw_nonce_account(uint64 lamports) external {
+        SystemInstruction.withdraw_nonce_account(
+            tx.accounts.nonce.key, 
+            tx.accounts.authority.key, 
+            tx.accounts.to.key, lamports);
     }
     }
 
 
-    function authorize_nonce_account(address nonce, address authority, address new_authority) public {
-        SystemInstruction.authorize_nonce_account(nonce, authority, new_authority);
+    @mutableAccount(nonce)
+    @signer(authority)
+    function authorize_nonce_account(address new_authority) external {
+        SystemInstruction.authorize_nonce_account(
+            tx.accounts.nonce.key, 
+            tx.accounts.authority.key, new_authority);
     }
     }
 
 
     // This is not available on Solana v1.9.15
     // This is not available on Solana v1.9.15
-    // function upgrade_nonce_account(address nonce) public {
+    // function upgrade_nonce_account(address nonce) external {
     //     SystemInstruction.upgrade_nonce_account(nonce);
     //     SystemInstruction.upgrade_nonce_account(nonce);
     // }
     // }
 }
 }

+ 35 - 10
integration/solana/token.sol

@@ -7,23 +7,48 @@ contract Token {
         mint = _mint;
         mint = _mint;
     }
     }
 
 
-    function total_supply() public view returns (uint64) {
-        return SplToken.total_supply(mint);
+    @account(mint)
+    function total_supply() external view returns (uint64) {
+        assert(tx.accounts.mint.key == mint);
+        return SplToken.total_supply(tx.accounts.mint);
     }
     }
 
 
-    function get_balance(address account) public view returns (uint64) {
-        return SplToken.get_balance(account);
+    @account(account)
+    function get_balance() external view returns (uint64) {
+        return SplToken.get_balance(tx.accounts.account);
     }
     }
 
 
-    function mint_to(address account, address authority, uint64 amount) public {
-        SplToken.mint_to(mint, account, authority, amount);
+    @mutableAccount(mint)
+    @mutableAccount(account)
+    @signer(authority)
+    function mint_to(uint64 amount) external {
+        assert(tx.accounts.mint.key == mint);
+        SplToken.mint_to(
+            tx.accounts.mint.key, 
+            tx.accounts.account.key, 
+            tx.accounts.authority.key, 
+            amount);
     }
     }
 
 
-    function transfer(address from, address to, address owner, uint64 amount) public {
-        SplToken.transfer(from, to, owner, amount);
+    @mutableAccount(from)
+    @mutableAccount(to)
+    @signer(owner)
+    function transfer(uint64 amount) external {
+        SplToken.transfer(
+            tx.accounts.from.key, 
+            tx.accounts.to.key, 
+            tx.accounts.owner.key,
+            amount);
     }
     }
 
 
-    function burn(address account, address owner, uint64 amount) public {
-        SplToken.burn(account, mint, owner, amount);
+    @mutableAccount(account)
+    @mutableAccount(mint)
+    @signer(owner)
+    function burn(uint64 amount) external {
+        SplToken.burn(
+            tx.accounts.account.key, 
+            tx.accounts.mint.key, 
+            tx.accounts.owner.key,
+            amount);
     }
     }
 }
 }

+ 47 - 45
integration/solana/token.spec.ts

@@ -29,8 +29,10 @@ describe('Create spl-token and use from solidity', function () {
             .rpc();
             .rpc();
 
 
         let total_supply = await program.methods.totalSupply()
         let total_supply = await program.methods.totalSupply()
-            .accounts({ dataAccount: storage.publicKey })
-            .remainingAccounts([{ pubkey: mint, isSigner: false, isWritable: false }])
+            .accounts({
+                dataAccount: storage.publicKey,
+                mint: mint
+            })
             .view();
             .view();
         expect(total_supply.toNumber()).toBe(0);
         expect(total_supply.toNumber()).toBe(0);
 
 
@@ -41,35 +43,36 @@ describe('Create spl-token and use from solidity', function () {
             payer.publicKey
             payer.publicKey
         )
         )
 
 
-        let balance = await program.methods.getBalance(tokenAccount.address)
-            .remainingAccounts([{ pubkey: tokenAccount.address, isSigner: false, isWritable: false }])
+        let balance = await program.methods.getBalance()
+            .accounts({account: tokenAccount.address})
             .view();
             .view();
 
 
         expect(balance.toNumber()).toBe(0);
         expect(balance.toNumber()).toBe(0);
 
 
         // Now let's mint some tokens
         // Now let's mint some tokens
         await program.methods.mintTo(
         await program.methods.mintTo(
-            tokenAccount.address,
-            mintAuthority.publicKey,
             new BN(100000))
             new BN(100000))
-            .accounts({ dataAccount: storage.publicKey })
-            .remainingAccounts([
-                { pubkey: mint, isSigner: false, isWritable: true },
-                { pubkey: tokenAccount.address, isSigner: false, isWritable: true },
-                { pubkey: mintAuthority.publicKey, isSigner: true, isWritable: false },
-            ])
+            .accounts({
+                dataAccount: storage.publicKey,
+                mint: mint,
+                account: tokenAccount.address,
+                authority: mintAuthority.publicKey,
+            })
             .signers([mintAuthority])
             .signers([mintAuthority])
             .rpc();
             .rpc();
 
 
         // let's check the balances
         // let's check the balances
         total_supply = await program.methods.totalSupply()
         total_supply = await program.methods.totalSupply()
-            .accounts({ dataAccount: storage.publicKey })
-            .remainingAccounts([{ pubkey: mint, isSigner: false, isWritable: false }])
+            .accounts({
+                dataAccount: storage.publicKey,
+                mint: mint,
+            })
             .view();
             .view();
 
 
         expect(total_supply.toNumber()).toBe(100000);
         expect(total_supply.toNumber()).toBe(100000);
-        balance = await program.methods.getBalance(tokenAccount.address)
-            .remainingAccounts([{ pubkey: tokenAccount.address, isSigner: false, isWritable: false }])
+
+        balance = await program.methods.getBalance()
+            .accounts({account: tokenAccount.address})
             .view();
             .view();
 
 
         expect(balance.toNumber()).toBe(100000);
         expect(balance.toNumber()).toBe(100000);
@@ -85,64 +88,63 @@ describe('Create spl-token and use from solidity', function () {
         )
         )
 
 
         await program.methods.transfer(
         await program.methods.transfer(
-            tokenAccount.address,
-            otherTokenAccount.address,
-            payer.publicKey,
             new BN(70000))
             new BN(70000))
-            .remainingAccounts([
-                { pubkey: otherTokenAccount.address, isSigner: false, isWritable: true },
-                { pubkey: tokenAccount.address, isSigner: false, isWritable: true },
-                { pubkey: payer.publicKey, isSigner: true, isWritable: false },
-            ])
+            .accounts(
+                {
+                    from: tokenAccount.address,
+                    to: otherTokenAccount.address,
+                    owner: payer.publicKey
+                }
+            )
             .signers([payer])
             .signers([payer])
             .rpc();
             .rpc();
 
 
         total_supply = await program.methods.totalSupply()
         total_supply = await program.methods.totalSupply()
-            .accounts({ dataAccount: storage.publicKey })
-            .remainingAccounts([{ pubkey: mint, isSigner: false, isWritable: false }])
+            .accounts({
+                dataAccount: storage.publicKey,
+                mint: mint,
+            })
             .view();
             .view();
 
 
         expect(total_supply.toNumber()).toBe(100000);
         expect(total_supply.toNumber()).toBe(100000);
-        balance = await program.methods.getBalance(tokenAccount.address)
-            .remainingAccounts([{ pubkey: tokenAccount.address, isSigner: false, isWritable: false }])
+        balance = await program.methods.getBalance()
+            .accounts({account: tokenAccount.address})
             .view();
             .view();
 
 
         expect(balance.toNumber()).toBe(30000);
         expect(balance.toNumber()).toBe(30000);
 
 
-        balance = await program.methods.getBalance(otherTokenAccount.address)
-            .remainingAccounts([{ pubkey: otherTokenAccount.address, isSigner: false, isWritable: false }])
+        balance = await program.methods.getBalance()
+            .accounts({account: otherTokenAccount.address})
             .view();
             .view();
 
 
         expect(balance.toNumber()).toBe(70000);
         expect(balance.toNumber()).toBe(70000);
 
 
         // burn
         // burn
         await program.methods.burn(
         await program.methods.burn(
-            otherTokenAccount.address,
-            theOutsider.publicKey,
             new BN(20000))
             new BN(20000))
-            .accounts({ dataAccount: storage.publicKey })
-            .remainingAccounts([
-                { pubkey: otherTokenAccount.address, isSigner: false, isWritable: true },
-                { pubkey: mint, isSigner: false, isWritable: true },
-                { pubkey: theOutsider.publicKey, isSigner: true, isWritable: false },
-            ])
+            .accounts({
+                mint: mint,
+                account: otherTokenAccount.address,
+                owner: theOutsider.publicKey,
+            })
             .signers([theOutsider])
             .signers([theOutsider])
             .rpc();
             .rpc();
 
 
         total_supply = await program.methods.totalSupply()
         total_supply = await program.methods.totalSupply()
-            .accounts({ dataAccount: storage.publicKey })
-            .remainingAccounts([{ pubkey: mint, isSigner: false, isWritable: false }])
+            .accounts({
+                dataAccount: storage.publicKey,
+                mint: mint,
+            })
             .view();
             .view();
 
 
         expect(total_supply.toNumber()).toBe(80000);
         expect(total_supply.toNumber()).toBe(80000);
-        balance = await program.methods.getBalance(tokenAccount.address)
-            .remainingAccounts([{ pubkey: tokenAccount.address, isSigner: false, isWritable: false }])
+        balance = await program.methods.getBalance()
+            .accounts({account: tokenAccount.address})
             .view();
             .view();
 
 
         expect(balance.toNumber()).toBe(30000);
         expect(balance.toNumber()).toBe(30000);
-
-        balance = await program.methods.getBalance(otherTokenAccount.address)
-            .remainingAccounts([{ pubkey: otherTokenAccount.address, isSigner: false, isWritable: false }])
+        balance = await program.methods.getBalance()
+            .accounts({account: otherTokenAccount.address})
             .view();
             .view();
 
 
         expect(balance.toNumber()).toBe(50000);
         expect(balance.toNumber()).toBe(50000);

+ 11 - 15
solana-library/spl_token.sol

@@ -146,20 +146,18 @@ library SplToken {
 	}
 	}
 
 
 	/// Get the total supply for the mint, i.e. the total amount in circulation
 	/// Get the total supply for the mint, i.e. the total amount in circulation
-	/// @param mint the mint for this token
-	function total_supply(address mint) internal view returns (uint64) {
-		AccountInfo account = get_account_info(mint);
-
+	/// @param account The AccountInfo struct for the mint account
+	function total_supply(AccountInfo account) internal view returns (uint64) {
+	
 		return account.data.readUint64LE(36);
 		return account.data.readUint64LE(36);
 	}
 	}
 
 
 	/// Get the balance for an account.
 	/// Get the balance for an account.
 	///
 	///
-	/// @param account the account for which we want to know a balance
-	function get_balance(address account) internal view returns (uint64) {
-		AccountInfo ai = get_account_info(account);
+	/// @param account the struct AccountInfo whose account balance we want to retrive
+	function get_balance(AccountInfo account) internal view returns (uint64) {
 
 
-		return ai.data.readUint64LE(64);
+		return account.data.readUint64LE(64);
 	}
 	}
 
 
 	/// Get the account info for an account. This walks the transaction account infos
 	/// Get the account info for an account. This walks the transaction account infos
@@ -201,11 +199,10 @@ library SplToken {
 
 
 	/// Fetch the owner, mint account and balance for an associated token account.
 	/// Fetch the owner, mint account and balance for an associated token account.
 	///
 	///
-	/// @param tokenAccount The token account
+	/// @param ai the AccountInfo struct for the token account
 	/// @return struct TokenAccountData
 	/// @return struct TokenAccountData
-	function get_token_account_data(address tokenAccount) public view returns (TokenAccountData) {
-		AccountInfo ai = get_account_info(tokenAccount);
-
+	function get_token_account_data(AccountInfo ai) public pure returns (TokenAccountData) {
+		
 		TokenAccountData data = TokenAccountData(
 		TokenAccountData data = TokenAccountData(
 			{
 			{
 				mintAccount: ai.data.readAddress(0), 
 				mintAccount: ai.data.readAddress(0), 
@@ -238,10 +235,9 @@ library SplToken {
 
 
 	/// Retrieve the information saved in a mint account
 	/// Retrieve the information saved in a mint account
 	///
 	///
-	/// @param mintAccount the account whose information we want to retrive
+	/// @param ai the AccountInfo struct for the mint accounts
 	/// @return the MintAccountData struct
 	/// @return the MintAccountData struct
-	function get_mint_account_data(address mintAccount) public view returns (MintAccountData) {
-		AccountInfo ai = get_account_info(mintAccount);
+	function get_mint_account_data(AccountInfo ai) public pure returns (MintAccountData) {
 
 
 		uint32 authority_present = ai.data.readUint32LE(0);
 		uint32 authority_present = ai.data.readUint32LE(0);
 		uint32 freeze_authority_present = ai.data.readUint32LE(46);
 		uint32 freeze_authority_present = ai.data.readUint32LE(46);

+ 13 - 0
solang-parser/src/pt.rs

@@ -222,6 +222,19 @@ impl Loc {
             _ => not_a_file(),
             _ => not_a_file(),
         }
         }
     }
     }
+
+    /// Performs the union of two locations
+    pub fn union(&mut self, other: &Self) {
+        match (self, other) {
+            (Self::File(r_file, r_start, r_end), Self::File(l_file, l_start, l_end)) => {
+                assert_eq!(r_file, l_file, "cannot perform union in different files");
+                *r_start = std::cmp::min(*r_start, *l_start);
+                *r_end = std::cmp::max(*r_end, *l_end);
+            }
+
+            _ => unimplemented!("cannot perform union in non File Loc"),
+        }
+    }
 }
 }
 
 
 /// An identifier.
 /// An identifier.

+ 2 - 2
solang-parser/src/solidity.lalrpop

@@ -442,7 +442,7 @@ NamedArgument: NamedArgument = {
         let name = Identifier { loc:  Loc::File(file_no, l, ar), name: "address".into() };
         let name = Identifier { loc:  Loc::File(file_no, l, ar), name: "address".into() };
 
 
         NamedArgument{ loc: Loc::File(file_no, l, r), name, expr }
         NamedArgument{ loc: Loc::File(file_no, l, r), name, expr }
-    }
+    },
 }
 }
 
 
 FunctionCall: Expression = {
 FunctionCall: Expression = {
@@ -488,7 +488,7 @@ NoFunctionTyPrecedence0: Expression = {
             Identifier { loc: Loc::File(file_no, al, b), name: "new".to_string() })
             Identifier { loc: Loc::File(file_no, al, b), name: "new".to_string() })
     },
     },
     <l:@L> <ty:NoFunctionType> <r:@R> => Expression::Type(Loc::File(file_no, l, r), ty),
     <l:@L> <ty:NoFunctionType> <r:@R> => Expression::Type(Loc::File(file_no, l, r), ty),
-    <a:@L> "[" <v:CommaOne<Expression>> "]" <b:@R> => {
+    <a:@L> "[" <v:Comma<Expression>> "]" <b:@R> => {
         Expression::ArrayLiteral(Loc::File(file_no, a, b), v)
         Expression::ArrayLiteral(Loc::File(file_no, a, b), v)
     },
     },
     <SolNoRevertIdentifier> => Expression::Variable(<>),
     <SolNoRevertIdentifier> => Expression::Variable(<>),

+ 11 - 0
solang-parser/src/tests.rs

@@ -1334,3 +1334,14 @@ contract MyTest {
 
 
     assert_eq!(expected_tree, actual_parse_tree);
     assert_eq!(expected_tree, actual_parse_tree);
 }
 }
+
+#[test]
+fn loc_union() {
+    let mut first = Loc::File(1, 10, 24);
+    let mut second = Loc::File(1, 4, 15);
+    let other_first = first;
+    first.union(&second);
+    assert_eq!(first, Loc::File(1, 4, 24));
+    second.union(&other_first);
+    assert_eq!(second, Loc::File(1, 4, 24));
+}

+ 63 - 7
src/abi/tests.rs

@@ -1723,19 +1723,19 @@ interface other_interface {
 
 
 contract Test {
 contract Test {
     function call_1() public {
     function call_1() public {
-        anchor.initialize(true);
+        anchor.initialize{accounts: []}(true);
     }
     }
 
 
     function call_2() public {
     function call_2() public {
-        associated.initialize(false);
+        associated.initialize{accounts: []}(false);
     }
     }
 
 
     function call_3() public {
     function call_3() public {
-        clock_interface.initialize(true);
+        clock_interface.initialize{accounts: []}(true);
     }
     }
 
 
     function call_4() public {
     function call_4() public {
-        other_interface.initialize(false);
+        other_interface.initialize{accounts: []}(false);
     }
     }
 }
 }
     "#;
     "#;
@@ -2027,8 +2027,9 @@ contract Foo {
 }
 }
 
 
 contract Other {
 contract Other {
-    function call_foo(address id) external {
-        Foo.new{program_id: id}();
+    @account(foo_pid)
+    function call_foo() external {
+        Foo.new{program_id: tx.accounts.foo_pid.key}();
     }
     }
 }
 }
     "#;
     "#;
@@ -2041,13 +2042,68 @@ contract Other {
     assert_eq!(
     assert_eq!(
         idl.instructions[1].accounts,
         idl.instructions[1].accounts,
         vec![
         vec![
+            idl_account("foo_pid", false, false),
             idl_account("Foo_dataAccount", true, false),
             idl_account("Foo_dataAccount", true, false),
-            idl_account("Foo_programId", false, false),
             idl_account("systemProgram", false, false)
             idl_account("systemProgram", false, false)
         ]
         ]
     );
     );
 }
 }
 
 
+#[test]
+fn function_annotations() {
+    let src = r#"
+contract Test1 {
+    @account(foo)
+    @mutableAccount(bar)
+    @signer(signerFoo)
+    @mutableSigner(signerBar)
+    function doThis() external returns (uint64) {
+        assert(tx.accounts.signerFoo.is_signer);
+        assert(tx.accounts.signerBar.is_signer);
+
+        return tx.accounts.foo.lamports;
+    }
+}
+
+contract Test2 {
+    @account(t1Id)
+    function callThat() external returns (uint64) {
+        uint64 res = Test1.doThis{program_id: tx.accounts.t1Id.key}();
+        return res;
+    }
+}
+    "#;
+
+    let mut ns = generate_namespace(src);
+    codegen(&mut ns, &Options::default());
+    let idl_1 = generate_anchor_idl(0, &ns, "0.1.0");
+    let idl_2 = generate_anchor_idl(1, &ns, "0.1.0");
+
+    assert_eq!(idl_1.instructions[1].name, "doThis");
+    assert_eq!(
+        idl_1.instructions[1].accounts,
+        vec![
+            idl_account("foo", false, false),
+            idl_account("bar", true, false),
+            idl_account("signerFoo", false, true),
+            idl_account("signerBar", true, true),
+        ]
+    );
+
+    assert_eq!(idl_2.instructions[1].name, "callThat");
+    assert_eq!(
+        idl_2.instructions[1].accounts,
+        vec![
+            idl_account("t1Id", false, false),
+            idl_account("systemProgram", false, false),
+            idl_account("foo", false, false),
+            idl_account("bar", true, false),
+            idl_account("signerFoo", false, true),
+            idl_account("signerBar", true, true),
+        ]
+    );
+}
+
 fn idl_account(name: &str, is_mut: bool, is_signer: bool) -> IdlAccountItem {
 fn idl_account(name: &str, is_mut: bool, is_signer: bool) -> IdlAccountItem {
     IdlAccountItem::IdlAccount(IdlAccount {
     IdlAccountItem::IdlAccount(IdlAccount {
         name: name.to_string(),
         name: name.to_string(),

+ 7 - 7
src/codegen/cfg.rs

@@ -11,8 +11,8 @@ use super::{
 use crate::codegen::subexpression_elimination::common_sub_expression_elimination;
 use crate::codegen::subexpression_elimination::common_sub_expression_elimination;
 use crate::codegen::{undefined_variable, Expression, LLVMName};
 use crate::codegen::{undefined_variable, Expression, LLVMName};
 use crate::sema::ast::{
 use crate::sema::ast::{
-    CallTy, Contract, FunctionAttributes, Namespace, Parameter, RetrieveType, Statement,
-    StringLocation, StructType, Type,
+    CallTy, Contract, ExternalCallAccounts, FunctionAttributes, Namespace, Parameter, RetrieveType,
+    Statement, StringLocation, StructType, Type,
 };
 };
 use crate::sema::{contracts::collect_base_args, diagnostics::Diagnostics, Recurse};
 use crate::sema::{contracts::collect_base_args, diagnostics::Diagnostics, Recurse};
 use crate::{sema::ast, Target};
 use crate::{sema::ast, Target};
@@ -126,7 +126,7 @@ pub enum Instr {
         salt: Option<Expression>,
         salt: Option<Expression>,
         address: Option<Expression>,
         address: Option<Expression>,
         seeds: Option<Expression>,
         seeds: Option<Expression>,
-        accounts: Option<Expression>,
+        accounts: ExternalCallAccounts<Expression>,
         loc: Loc,
         loc: Loc,
     },
     },
     /// Call external functions. If the call fails, set the success failure
     /// Call external functions. If the call fails, set the success failure
@@ -135,7 +135,7 @@ pub enum Instr {
         loc: Loc,
         loc: Loc,
         success: Option<usize>,
         success: Option<usize>,
         address: Option<Expression>,
         address: Option<Expression>,
-        accounts: Option<Expression>,
+        accounts: ExternalCallAccounts<Expression>,
         seeds: Option<Expression>,
         seeds: Option<Expression>,
         payload: Expression,
         payload: Expression,
         value: Expression,
         value: Expression,
@@ -299,7 +299,7 @@ impl Instr {
                     expr.recurse(cx, f);
                     expr.recurse(cx, f);
                 }
                 }
 
 
-                if let Some(expr) = accounts {
+                if let ExternalCallAccounts::Present(expr) = accounts {
                     expr.recurse(cx, f);
                     expr.recurse(cx, f);
                 }
                 }
             }
             }
@@ -1202,7 +1202,7 @@ impl ControlFlowGraph {
                     self.expr_to_string(contract, ns, payload),
                     self.expr_to_string(contract, ns, payload),
                     self.expr_to_string(contract, ns, value),
                     self.expr_to_string(contract, ns, value),
                     self.expr_to_string(contract, ns, gas),
                     self.expr_to_string(contract, ns, gas),
-                    if let Some(accounts) = accounts {
+                    if let ExternalCallAccounts::Present(accounts) = accounts {
                         self.expr_to_string(contract, ns, accounts)
                         self.expr_to_string(contract, ns, accounts)
                     } else {
                     } else {
                         String::new()
                         String::new()
@@ -1285,7 +1285,7 @@ impl ControlFlowGraph {
                 },
                 },
                 ns.contracts[*contract_no].name,
                 ns.contracts[*contract_no].name,
                 self.expr_to_string(contract, ns, encoded_args),
                 self.expr_to_string(contract, ns, encoded_args),
-                if let Some(accounts) = accounts {
+                if let ExternalCallAccounts::Present(accounts) = accounts {
                     self.expr_to_string(contract, ns, accounts)
                     self.expr_to_string(contract, ns, accounts)
                 } else {
                 } else {
                     String::new()
                     String::new()

+ 2 - 6
src/codegen/constant_folding.rs

@@ -245,9 +245,7 @@ pub fn constant_folding(cfg: &mut ControlFlowGraph, dry_run: bool, ns: &mut Name
                     let seeds = seeds
                     let seeds = seeds
                         .as_ref()
                         .as_ref()
                         .map(|expr| expression(expr, Some(&vars), cfg, ns).0);
                         .map(|expr| expression(expr, Some(&vars), cfg, ns).0);
-                    let accounts = accounts
-                        .as_ref()
-                        .map(|expr| expression(expr, Some(&vars), cfg, ns).0);
+                    let accounts = accounts.map(|expr| expression(expr, Some(&vars), cfg, ns).0);
 
 
                     if !dry_run {
                     if !dry_run {
                         cfg.blocks[block_no].instr[instr_no] = Instr::Constructor {
                         cfg.blocks[block_no].instr[instr_no] = Instr::Constructor {
@@ -285,9 +283,7 @@ pub fn constant_folding(cfg: &mut ControlFlowGraph, dry_run: bool, ns: &mut Name
                     let address = address
                     let address = address
                         .as_ref()
                         .as_ref()
                         .map(|expr| expression(expr, Some(&vars), cfg, ns).0);
                         .map(|expr| expression(expr, Some(&vars), cfg, ns).0);
-                    let accounts = accounts
-                        .as_ref()
-                        .map(|expr| expression(expr, Some(&vars), cfg, ns).0);
+                    let accounts = accounts.map(|expr| expression(expr, Some(&vars), cfg, ns).0);
                     let seeds = seeds
                     let seeds = seeds
                         .as_ref()
                         .as_ref()
                         .map(|expr| expression(expr, Some(&vars), cfg, ns).0);
                         .map(|expr| expression(expr, Some(&vars), cfg, ns).0);

+ 1 - 2
src/codegen/constructor.rs

@@ -74,8 +74,7 @@ pub(super) fn call_constructor(
         .map(|e| expression(e, cfg, callee_contract_no, func, ns, vartab, opt));
         .map(|e| expression(e, cfg, callee_contract_no, func, ns, vartab, opt));
     let accounts = call_args
     let accounts = call_args
         .accounts
         .accounts
-        .as_ref()
-        .map(|e| expression(e, cfg, callee_contract_no, func, ns, vartab, opt));
+        .map(|expr| expression(expr, cfg, contract_no, func, ns, vartab, opt));
 
 
     let mut constructor_args = constructor_args
     let mut constructor_args = constructor_args
         .iter()
         .iter()

+ 4 - 5
src/codegen/expression.rs

@@ -16,6 +16,7 @@ use crate::codegen::array_boundary::handle_array_assign;
 use crate::codegen::constructor::call_constructor;
 use crate::codegen::constructor::call_constructor;
 use crate::codegen::unused_variable::should_remove_assignment;
 use crate::codegen::unused_variable::should_remove_assignment;
 use crate::codegen::{Builtin, Expression};
 use crate::codegen::{Builtin, Expression};
+use crate::sema::ast::ExternalCallAccounts;
 use crate::sema::{
 use crate::sema::{
     ast,
     ast,
     ast::{
     ast::{
@@ -1586,7 +1587,7 @@ fn payable_send(
                 loc: *loc,
                 loc: *loc,
                 success: Some(success),
                 success: Some(success),
                 address: Some(address),
                 address: Some(address),
-                accounts: None,
+                accounts: ExternalCallAccounts::AbsentArgument,
                 seeds: None,
                 seeds: None,
                 payload: Expression::AllocDynamicBytes {
                 payload: Expression::AllocDynamicBytes {
                     loc: *loc,
                     loc: *loc,
@@ -1655,7 +1656,7 @@ fn payable_transfer(
             Instr::ExternalCall {
             Instr::ExternalCall {
                 loc: *loc,
                 loc: *loc,
                 success: None,
                 success: None,
-                accounts: None,
+                accounts: ExternalCallAccounts::AbsentArgument,
                 seeds: None,
                 seeds: None,
                 address: Some(address),
                 address: Some(address),
                 payload: Expression::AllocDynamicBytes {
                 payload: Expression::AllocDynamicBytes {
@@ -2844,7 +2845,6 @@ pub fn emit_function_call(
             };
             };
             let accounts = call_args
             let accounts = call_args
                 .accounts
                 .accounts
-                .as_ref()
                 .map(|expr| expression(expr, cfg, caller_contract_no, func, ns, vartab, opt));
                 .map(|expr| expression(expr, cfg, caller_contract_no, func, ns, vartab, opt));
             let seeds = call_args
             let seeds = call_args
                 .seeds
                 .seeds
@@ -2932,7 +2932,6 @@ pub fn emit_function_call(
                 };
                 };
                 let accounts = call_args
                 let accounts = call_args
                     .accounts
                     .accounts
-                    .as_ref()
                     .map(|expr| expression(expr, cfg, caller_contract_no, func, ns, vartab, opt));
                     .map(|expr| expression(expr, cfg, caller_contract_no, func, ns, vartab, opt));
                 let seeds = call_args
                 let seeds = call_args
                     .seeds
                     .seeds
@@ -3065,7 +3064,7 @@ pub fn emit_function_call(
                     Instr::ExternalCall {
                     Instr::ExternalCall {
                         loc: *loc,
                         loc: *loc,
                         success,
                         success,
-                        accounts: None,
+                        accounts: ExternalCallAccounts::AbsentArgument,
                         seeds: None,
                         seeds: None,
                         address: Some(address),
                         address: Some(address),
                         payload,
                         payload,

+ 34 - 27
src/codegen/solana_accounts/account_collection.rs

@@ -3,7 +3,7 @@
 use crate::codegen::cfg::{ASTFunction, ControlFlowGraph, Instr, InternalCallTy};
 use crate::codegen::cfg::{ASTFunction, ControlFlowGraph, Instr, InternalCallTy};
 use crate::codegen::solana_accounts::account_from_number;
 use crate::codegen::solana_accounts::account_from_number;
 use crate::codegen::{Builtin, Expression};
 use crate::codegen::{Builtin, Expression};
-use crate::sema::ast::{Contract, Function, Namespace, SolanaAccount};
+use crate::sema::ast::{Contract, ExternalCallAccounts, Function, Namespace, SolanaAccount};
 use crate::sema::diagnostics::Diagnostics;
 use crate::sema::diagnostics::Diagnostics;
 use crate::sema::solana_accounts::BuiltinAccounts;
 use crate::sema::solana_accounts::BuiltinAccounts;
 use crate::sema::Recurse;
 use crate::sema::Recurse;
@@ -334,39 +334,43 @@ fn check_instruction(instr: &Instr, data: &mut RecurseData) {
                 salt.recurse(data, check_expression);
                 salt.recurse(data, check_expression);
             }
             }
             if let Some(address) = address {
             if let Some(address) = address {
+                // If the address is a number literal, it comes from the `@program_id` annotation,
+                // so we need to include it in the IDL.
+                // If it is not a literal, we assume users are fetching it from a declared account
+                // (@account(my_id) => tx.accounts.my_id.key)
+                if matches!(address, Expression::NumberLiteral { .. }) {
+                    data.add_program_id(&data.contracts[*contract_no].name);
+                }
+
                 address.recurse(data, check_expression);
                 address.recurse(data, check_expression);
             }
             }
             if let Some(seeds) = seeds {
             if let Some(seeds) = seeds {
                 seeds.recurse(data, check_expression);
                 seeds.recurse(data, check_expression);
             }
             }
-            if let Some(accounts) = accounts {
+            if let ExternalCallAccounts::Present(accounts) = accounts {
                 accounts.recurse(data, check_expression);
                 accounts.recurse(data, check_expression);
-            } else {
-                // If the one passes the AccountMeta vector to the constructor call, there is no
+            } else if let Some(constructor_no) = constructor_no {
+                // If one passes the AccountMeta vector to the constructor call, there is no
                 // need to collect accounts for the IDL.
                 // need to collect accounts for the IDL.
-                if let Some(constructor_no) = constructor_no {
-                    transfer_accounts(loc, *contract_no, *constructor_no, data);
-                } else {
-                    data.add_account(
-                        format!("{}_dataAccount", data.contracts[*contract_no].name),
-                        &SolanaAccount {
-                            loc: *loc,
-                            is_signer: false,
-                            is_writer: true,
-                            generated: true,
-                        },
-                    );
-                }
+                transfer_accounts(loc, *contract_no, *constructor_no, data);
+            } else {
+                data.add_account(
+                    format!("{}_dataAccount", data.contracts[*contract_no].name),
+                    &SolanaAccount {
+                        loc: *loc,
+                        is_signer: false,
+                        is_writer: true,
+                        generated: true,
+                    },
+                );
             }
             }
 
 
-            data.add_program_id(&data.contracts[*contract_no].name);
             data.add_system_account();
             data.add_system_account();
         }
         }
         Instr::ExternalCall {
         Instr::ExternalCall {
             loc,
             loc,
             address,
             address,
             accounts,
             accounts,
-            seeds,
             payload,
             payload,
             value,
             value,
             gas,
             gas,
@@ -380,7 +384,7 @@ fn check_instruction(instr: &Instr, data: &mut RecurseData) {
                 return;
                 return;
             }
             }
 
 
-            let mut program_id_populated = false;
+            let mut should_add_program_id = false;
             if let Some(address) = address {
             if let Some(address) = address {
                 address.recurse(data, check_expression);
                 address.recurse(data, check_expression);
                 if let Expression::NumberLiteral { value, .. } = address {
                 if let Expression::NumberLiteral { value, .. } = address {
@@ -395,28 +399,31 @@ fn check_instruction(instr: &Instr, data: &mut RecurseData) {
                                 generated: true,
                                 generated: true,
                             },
                             },
                         );
                         );
-                        program_id_populated = true;
+                    } else {
+                        // If the address is a literal, it came from the @program_id annotation,
+                        // so it is not in the IDL.
+                        should_add_program_id = true;
+                        // If it is not a literal, we assume it is an account declared with @account,
+                        // in which case it is already in the IDL.
                     }
                     }
                 }
                 }
             }
             }
-            if let Some(seeds) = seeds {
-                seeds.recurse(data, check_expression);
-            }
+
             payload.recurse(data, check_expression);
             payload.recurse(data, check_expression);
             value.recurse(data, check_expression);
             value.recurse(data, check_expression);
             gas.recurse(data, check_expression);
             gas.recurse(data, check_expression);
             // External calls always need the system account
             // External calls always need the system account
             data.add_system_account();
             data.add_system_account();
 
 
-            if let Some(accounts) = accounts {
+            if let ExternalCallAccounts::Present(accounts) = accounts {
                 accounts.recurse(data, check_expression);
                 accounts.recurse(data, check_expression);
             }
             }
 
 
             if let Some((contract_no, function_no)) = contract_function_no {
             if let Some((contract_no, function_no)) = contract_function_no {
-                if !program_id_populated {
+                if should_add_program_id {
                     data.add_program_id(&data.contracts[*contract_no].name);
                     data.add_program_id(&data.contracts[*contract_no].name);
                 }
                 }
-                if accounts.is_none() {
+                if accounts.is_absent() {
                     transfer_accounts(loc, *contract_no, *function_no, data);
                     transfer_accounts(loc, *contract_no, *function_no, data);
                 }
                 }
             }
             }

+ 6 - 5
src/codegen/solana_accounts/account_management.rs

@@ -3,7 +3,9 @@
 use crate::codegen::cfg::Instr;
 use crate::codegen::cfg::Instr;
 use crate::codegen::dispatch::solana::SOLANA_DISPATCH_CFG_NAME;
 use crate::codegen::dispatch::solana::SOLANA_DISPATCH_CFG_NAME;
 use crate::codegen::{Builtin, Expression};
 use crate::codegen::{Builtin, Expression};
-use crate::sema::ast::{ArrayLength, Contract, Function, Namespace, StructType, Type};
+use crate::sema::ast::{
+    ArrayLength, Contract, ExternalCallAccounts, Function, Namespace, StructType, Type,
+};
 use crate::sema::solana_accounts::BuiltinAccounts;
 use crate::sema::solana_accounts::BuiltinAccounts;
 use num_bigint::BigInt;
 use num_bigint::BigInt;
 use solang_parser::pt::Loc;
 use solang_parser::pt::Loc;
@@ -115,7 +117,7 @@ fn process_instruction(
             contract_function_no: Some((contract_no, func_no)),
             contract_function_no: Some((contract_no, func_no)),
             ..
             ..
         } => {
         } => {
-            if accounts.is_some() {
+            if !accounts.is_absent() {
                 return;
                 return;
             }
             }
 
 
@@ -150,7 +152,7 @@ fn process_instruction(
                 dimensions: vec![account_metas.len() as u32],
                 dimensions: vec![account_metas.len() as u32],
                 values: account_metas,
                 values: account_metas,
             };
             };
-            *accounts = Some(metas_vector);
+            *accounts = ExternalCallAccounts::Present(metas_vector);
         }
         }
         Instr::Constructor {
         Instr::Constructor {
             contract_no,
             contract_no,
@@ -175,7 +177,7 @@ fn process_instruction(
                 dimensions: vec![1],
                 dimensions: vec![1],
                 values: account_metas,
                 values: account_metas,
             };
             };
-            *accounts = Some(metas_vector);
+            *accounts = ExternalCallAccounts::Present(metas_vector);
         }
         }
         Instr::AccountAccess { loc, name, var_no } => {
         Instr::AccountAccess { loc, name, var_no } => {
             // This could have been an Expression::AccountAccess if we had a three-address form.
             // This could have been an Expression::AccountAccess if we had a three-address form.
@@ -197,7 +199,6 @@ fn process_instruction(
                 expr,
                 expr,
             };
             };
         }
         }
-
         _ => (),
         _ => (),
     }
     }
 }
 }

+ 3 - 2
src/codegen/solana_deploy.rs

@@ -9,7 +9,8 @@ use crate::codegen::solana_accounts::account_management::{
     account_meta_literal, retrieve_key_from_account_info,
     account_meta_literal, retrieve_key_from_account_info,
 };
 };
 use crate::sema::ast::{
 use crate::sema::ast::{
-    self, ArrayLength, CallTy, Function, FunctionAttributes, Namespace, StructType,
+    self, ArrayLength, CallTy, ExternalCallAccounts, Function, FunctionAttributes, Namespace,
+    StructType,
 };
 };
 use crate::sema::diagnostics::Diagnostics;
 use crate::sema::diagnostics::Diagnostics;
 use crate::sema::eval::eval_const_number;
 use crate::sema::eval::eval_const_number;
@@ -589,7 +590,7 @@ pub(super) fn solana_deploy(
                     ty: Type::Address(false),
                     ty: Type::Address(false),
                     value: BigInt::from(0),
                     value: BigInt::from(0),
                 }), // SystemProgram 11111111111111111111111111111111
                 }), // SystemProgram 11111111111111111111111111111111
-                accounts: Some(Expression::Variable {
+                accounts: ExternalCallAccounts::Present(Expression::Variable {
                     loc: Loc::Codegen,
                     loc: Loc::Codegen,
                     ty: metas_ty,
                     ty: metas_ty,
                     var_no: metas,
                     var_no: metas,

+ 3 - 2
src/codegen/statements/try_catch.rs

@@ -12,7 +12,8 @@ use crate::codegen::{
     Expression,
     Expression,
 };
 };
 use crate::sema::ast::{
 use crate::sema::ast::{
-    self, CallTy, Function, Namespace, RetrieveType, TryCatch, Type, Type::Uint,
+    self, CallTy, ExternalCallAccounts, Function, Namespace, RetrieveType, TryCatch, Type,
+    Type::Uint,
 };
 };
 use num_bigint::{BigInt, Sign};
 use num_bigint::{BigInt, Sign};
 use num_traits::Zero;
 use num_traits::Zero;
@@ -260,7 +261,7 @@ fn exec_try(
                         loc: *loc,
                         loc: *loc,
                         success: Some(success),
                         success: Some(success),
                         address: Some(address),
                         address: Some(address),
-                        accounts: None,
+                        accounts: ExternalCallAccounts::AbsentArgument,
                         seeds: None,
                         seeds: None,
                         payload,
                         payload,
                         value,
                         value,

+ 2 - 2
src/codegen/strength_reduce/mod.rs

@@ -7,7 +7,7 @@ mod value;
 
 
 use super::cfg::{ControlFlowGraph, Instr};
 use super::cfg::{ControlFlowGraph, Instr};
 use crate::codegen::Expression;
 use crate::codegen::Expression;
-use crate::sema::ast::{Namespace, Type};
+use crate::sema::ast::{ExternalCallAccounts, Namespace, Type};
 use bitvec::prelude::*;
 use bitvec::prelude::*;
 use expression_values::expression_values;
 use expression_values::expression_values;
 use num_bigint::{BigInt, Sign};
 use num_bigint::{BigInt, Sign};
@@ -174,7 +174,7 @@ fn block_reduce(
                 if let Some(salt) = salt {
                 if let Some(salt) = salt {
                     *salt = expression_reduce(salt, &vars, ns);
                     *salt = expression_reduce(salt, &vars, ns);
                 }
                 }
-                if let Some(accounts) = accounts {
+                if let ExternalCallAccounts::Present(accounts) = accounts {
                     *accounts = expression_reduce(accounts, &vars, ns);
                     *accounts = expression_reduce(accounts, &vars, ns);
                 }
                 }
                 *gas = expression_reduce(gas, &vars, ns);
                 *gas = expression_reduce(gas, &vars, ns);

+ 3 - 2
src/codegen/subexpression_elimination/instruction.rs

@@ -5,6 +5,7 @@ use crate::codegen::subexpression_elimination::common_subexpression_tracker::Com
 use crate::codegen::subexpression_elimination::AvailableExpression;
 use crate::codegen::subexpression_elimination::AvailableExpression;
 use crate::codegen::subexpression_elimination::{AvailableExpressionSet, AvailableVariable};
 use crate::codegen::subexpression_elimination::{AvailableExpressionSet, AvailableVariable};
 use crate::codegen::Expression;
 use crate::codegen::Expression;
+use crate::sema::ast::ExternalCallAccounts;
 
 
 impl<'a, 'b: 'a> AvailableExpressionSet<'a> {
 impl<'a, 'b: 'a> AvailableExpressionSet<'a> {
     /// Check if we can add the expressions of an instruction to the graph
     /// Check if we can add the expressions of an instruction to the graph
@@ -126,7 +127,7 @@ impl<'a, 'b: 'a> AvailableExpressionSet<'a> {
                     let _ = self.gen_expression(expr, ave, cst);
                     let _ = self.gen_expression(expr, ave, cst);
                 }
                 }
 
 
-                if let Some(expr) = accounts {
+                if let ExternalCallAccounts::Present(expr) = accounts {
                     let _ = self.gen_expression(expr, ave, cst);
                     let _ = self.gen_expression(expr, ave, cst);
                 }
                 }
             }
             }
@@ -143,7 +144,7 @@ impl<'a, 'b: 'a> AvailableExpressionSet<'a> {
                 if let Some(expr) = address {
                 if let Some(expr) = address {
                     let _ = self.gen_expression(expr, ave, cst);
                     let _ = self.gen_expression(expr, ave, cst);
                 }
                 }
-                if let Some(expr) = accounts {
+                if let ExternalCallAccounts::Present(expr) = accounts {
                     let _ = self.gen_expression(expr, ave, cst);
                     let _ = self.gen_expression(expr, ave, cst);
                 }
                 }
                 if let Some(expr) = seeds {
                 if let Some(expr) = seeds {

+ 2 - 2
src/codegen/subexpression_elimination/tests.rs

@@ -7,7 +7,7 @@ use crate::codegen::subexpression_elimination::anticipated_expressions::Anticipa
 use crate::codegen::subexpression_elimination::common_subexpression_tracker::CommonSubExpressionTracker;
 use crate::codegen::subexpression_elimination::common_subexpression_tracker::CommonSubExpressionTracker;
 use crate::codegen::subexpression_elimination::{AvailableExpression, AvailableExpressionSet};
 use crate::codegen::subexpression_elimination::{AvailableExpression, AvailableExpressionSet};
 use crate::codegen::Expression;
 use crate::codegen::Expression;
-use crate::sema::ast::{StringLocation, Type};
+use crate::sema::ast::{ExternalCallAccounts, StringLocation, Type};
 use num_bigint::{BigInt, Sign};
 use num_bigint::{BigInt, Sign};
 use num_rational::BigRational;
 use num_rational::BigRational;
 use solang_parser::pt::Loc;
 use solang_parser::pt::Loc;
@@ -433,7 +433,7 @@ fn string() {
         address: None,
         address: None,
         seeds: None,
         seeds: None,
         loc: Loc::Codegen,
         loc: Loc::Codegen,
-        accounts: None,
+        accounts: ExternalCallAccounts::AbsentArgument,
     };
     };
 
 
     let mut ave = AvailableExpression::default();
     let mut ave = AvailableExpression::default();

+ 24 - 4
src/emit/binary.rs

@@ -126,6 +126,13 @@ macro_rules! emit_context {
                 )
                 )
             };
             };
         }
         }
+
+        #[allow(unused_macros)]
+        macro_rules! i8_basic_type_enum {
+            () => {
+                $binary.context.i8_type().as_basic_type_enum()
+            };
+        }
     };
     };
 }
 }
 
 
@@ -866,12 +873,25 @@ impl<'a> Binary<'a> {
 
 
     /// Return the llvm type for the resolved type.
     /// Return the llvm type for the resolved type.
     pub(crate) fn llvm_type(&self, ty: &Type, ns: &Namespace) -> BasicTypeEnum<'a> {
     pub(crate) fn llvm_type(&self, ty: &Type, ns: &Namespace) -> BasicTypeEnum<'a> {
+        emit_context!(self);
         if ty.is_builtin_struct() == Some(StructType::AccountInfo) {
         if ty.is_builtin_struct() == Some(StructType::AccountInfo) {
             return self
             return self
-                .module
-                .get_struct_type("struct.SolAccountInfo")
-                .unwrap()
-                .into();
+                .context
+                .struct_type(
+                    &[
+                        byte_ptr!().as_basic_type_enum(),             // SolPubkey *
+                        byte_ptr!().as_basic_type_enum(),             // uint64_t *
+                        self.context.i64_type().as_basic_type_enum(), // uint64_t
+                        byte_ptr!().as_basic_type_enum(),             // uint8_t *
+                        byte_ptr!().as_basic_type_enum(),             // SolPubkey *
+                        self.context.i64_type().as_basic_type_enum(), // uint64_t
+                        i8_basic_type_enum!(),                        // bool
+                        i8_basic_type_enum!(),                        // bool
+                        i8_basic_type_enum!(),                        // bool
+                    ],
+                    false,
+                )
+                .as_basic_type_enum();
         } else {
         } else {
             match ty {
             match ty {
                 Type::Bool => BasicTypeEnum::IntType(self.context.bool_type()),
                 Type::Bool => BasicTypeEnum::IntType(self.context.bool_type()),

+ 3 - 33
src/emit/instructions.rs

@@ -9,7 +9,7 @@ use crate::emit::binary::Binary;
 use crate::emit::cfg::{create_block, BasicBlock, Work};
 use crate::emit::cfg::{create_block, BasicBlock, Work};
 use crate::emit::expression::expression;
 use crate::emit::expression::expression;
 use crate::emit::{ContractArgs, TargetRuntime, Variable};
 use crate::emit::{ContractArgs, TargetRuntime, Variable};
-use crate::sema::ast::{Contract, Namespace, RetrieveType, Type};
+use crate::sema::ast::{Contract, ExternalCallAccounts, Namespace, RetrieveType, Type};
 use crate::Target;
 use crate::Target;
 use inkwell::types::BasicType;
 use inkwell::types::BasicType;
 use inkwell::values::{
 use inkwell::values::{
@@ -700,36 +700,6 @@ pub(super) fn process_instruction<'a, T: TargetRuntime<'a> + ?Sized>(
                     expression(target, bin, address, &w.vars, function, ns).into_array_value();
                     expression(target, bin, address, &w.vars, function, ns).into_array_value();
 
 
                 bin.builder.build_store(address_stack, address);
                 bin.builder.build_store(address_stack, address);
-            } else if let Some((account_metas, _)) = llvm_accounts {
-                // On Solana, we must return the address of a contract after instantiating it with
-                // 'new'. When the 'address' parameter is not provided to the call argument,
-                // we fetch the address from the AccountMeta array provided in the 'accounts'
-                // call argument. We assume the data account will always be the first element in
-                // the array.
-                let vector_ty = bin.llvm_type(&accounts.as_ref().unwrap().ty(), ns);
-                let elem_ptr = unsafe {
-                    bin.builder.build_gep(
-                        vector_ty,
-                        account_metas,
-                        &[
-                            bin.context.i32_type().const_zero(),
-                            bin.context.i32_type().const_zero(),
-                            bin.context.i32_type().const_zero(),
-                        ],
-                        "contract_account",
-                    )
-                };
-                let loaded_ptr = bin.builder.build_load(
-                    bin.address_type(ns).ptr_type(AddressSpace::default()),
-                    elem_ptr,
-                    "",
-                );
-                let loaded_address = bin.builder.build_load(
-                    bin.address_type(ns),
-                    loaded_ptr.into_pointer_value(),
-                    "",
-                );
-                bin.builder.build_store(address_stack, loaded_address);
             }
             }
 
 
             let seeds = if let Some(seeds) = seeds {
             let seeds = if let Some(seeds) = seeds {
@@ -1092,13 +1062,13 @@ fn add_or_retrieve_block<'a>(
 /// we process its codegen representation here and return the pointer to it and its size.
 /// we process its codegen representation here and return the pointer to it and its size.
 fn process_account_metas<'a, T: TargetRuntime<'a> + ?Sized>(
 fn process_account_metas<'a, T: TargetRuntime<'a> + ?Sized>(
     target: &T,
     target: &T,
-    accounts: &Option<Expression>,
+    accounts: &ExternalCallAccounts<Expression>,
     bin: &Binary<'a>,
     bin: &Binary<'a>,
     vartab: &HashMap<usize, Variable<'a>>,
     vartab: &HashMap<usize, Variable<'a>>,
     function: FunctionValue<'a>,
     function: FunctionValue<'a>,
     ns: &Namespace,
     ns: &Namespace,
 ) -> Option<(PointerValue<'a>, IntValue<'a>)> {
 ) -> Option<(PointerValue<'a>, IntValue<'a>)> {
-    if let Some(accounts) = accounts {
+    if let ExternalCallAccounts::Present(accounts) = accounts {
         let ty = accounts.ty();
         let ty = accounts.ty();
         let expr = expression(target, bin, accounts, vartab, function, ns);
         let expr = expression(target, bin, accounts, vartab, function, ns);
 
 

+ 53 - 59
src/emit/solana/mod.rs

@@ -7,7 +7,7 @@ use crate::Target;
 use std::cmp::Ordering;
 use std::cmp::Ordering;
 
 
 use crate::codegen::{cfg::ReturnCode, Options};
 use crate::codegen::{cfg::ReturnCode, Options};
-use crate::sema::ast::Type;
+use crate::sema::ast::{Namespace, StructType, Type};
 use inkwell::module::{Linkage, Module};
 use inkwell::module::{Linkage, Module};
 use inkwell::types::BasicType;
 use inkwell::types::BasicType;
 use inkwell::values::{
 use inkwell::values::{
@@ -217,6 +217,33 @@ impl SolanaTarget {
         function
         function
             .as_global_value()
             .as_global_value()
             .set_unnamed_address(UnnamedAddress::Local);
             .set_unnamed_address(UnnamedAddress::Local);
+
+        let function = binary.module.add_function(
+            "sol_invoke_signed_c",
+            u64_ty.fn_type(
+                &[
+                    u8_ptr.into(),
+                    binary
+                        .module
+                        .get_struct_type("struct.SolAccountInfo")
+                        .unwrap()
+                        .ptr_type(AddressSpace::default())
+                        .into(),
+                    binary.context.i32_type().into(),
+                    binary
+                        .context
+                        .i8_type()
+                        .ptr_type(AddressSpace::default())
+                        .into(),
+                    binary.context.i32_type().into(),
+                ],
+                false,
+            ),
+            None,
+        );
+        function
+            .as_global_value()
+            .set_unnamed_address(UnnamedAddress::Local);
     }
     }
 
 
     /// Returns the SolAccountInfo of the executing binary
     /// Returns the SolAccountInfo of the executing binary
@@ -1050,60 +1077,6 @@ impl SolanaTarget {
             .as_basic_value_enum()
             .as_basic_value_enum()
     }
     }
 
 
-    /// Construct the LLVM-IR to call 'external_call' from solana.c
-    fn build_external_call<'b>(
-        &self,
-        binary: &Binary,
-        payload: PointerValue<'b>,
-        payload_len: IntValue<'b>,
-        contract_args: ContractArgs<'b>,
-        ns: &ast::Namespace,
-    ) {
-        let parameters = self.sol_parameters(binary);
-        let external_call = binary.module.get_function("external_call").unwrap();
-
-        let program_id = contract_args.program_id.unwrap_or_else(|| {
-            binary
-                .llvm_type(&Type::Address(false), ns)
-                .ptr_type(AddressSpace::default())
-                .const_null()
-        });
-
-        let (seeds, seeds_len) = contract_args
-            .seeds
-            .map(|(seeds, len)| {
-                (
-                    seeds,
-                    binary.builder.build_int_cast(
-                        len,
-                        external_call.get_type().get_param_types()[4].into_int_type(),
-                        "len",
-                    ),
-                )
-            })
-            .unwrap_or((
-                external_call.get_type().get_param_types()[3]
-                    .ptr_type(AddressSpace::default())
-                    .const_null(),
-                external_call.get_type().get_param_types()[4]
-                    .into_int_type()
-                    .const_zero(),
-            ));
-
-        binary.builder.build_call(
-            external_call,
-            &[
-                payload.into(),
-                payload_len.into(),
-                program_id.into(),
-                seeds.into(),
-                seeds_len.into(),
-                parameters.into(),
-            ],
-            "",
-        );
-    }
-
     /// Construct the LLVM-IR to call 'sol_invoke_signed_c'.
     /// Construct the LLVM-IR to call 'sol_invoke_signed_c'.
     fn build_invoke_signed_c<'b>(
     fn build_invoke_signed_c<'b>(
         &self,
         &self,
@@ -1112,12 +1085,33 @@ impl SolanaTarget {
         payload: PointerValue<'b>,
         payload: PointerValue<'b>,
         payload_len: IntValue<'b>,
         payload_len: IntValue<'b>,
         contract_args: ContractArgs<'b>,
         contract_args: ContractArgs<'b>,
+        ns: &Namespace,
     ) {
     ) {
         let instruction_ty: BasicTypeEnum = binary
         let instruction_ty: BasicTypeEnum = binary
-            .module
-            .get_struct_type("struct.SolInstruction")
-            .unwrap()
-            .into();
+            .context
+            .struct_type(
+                &[
+                    binary
+                        .module
+                        .get_struct_type("struct.SolPubkey")
+                        .unwrap()
+                        .ptr_type(AddressSpace::default())
+                        .as_basic_type_enum(),
+                    binary
+                        .llvm_type(&Type::Struct(StructType::AccountMeta), ns)
+                        .ptr_type(AddressSpace::default())
+                        .as_basic_type_enum(),
+                    binary.context.i64_type().as_basic_type_enum(),
+                    binary
+                        .context
+                        .i8_type()
+                        .ptr_type(AddressSpace::default())
+                        .as_basic_type_enum(),
+                    binary.context.i64_type().as_basic_type_enum(),
+                ],
+                false,
+            )
+            .as_basic_type_enum();
 
 
         let instruction = binary.build_alloca(function, instruction_ty, "instruction");
         let instruction = binary.build_alloca(function, instruction_ty, "instruction");
 
 

+ 14 - 7
src/emit/solana/target.rs

@@ -1232,7 +1232,7 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
         encoded_args: BasicValueEnum<'b>,
         encoded_args: BasicValueEnum<'b>,
         encoded_args_len: BasicValueEnum<'b>,
         encoded_args_len: BasicValueEnum<'b>,
         mut contract_args: ContractArgs<'b>,
         mut contract_args: ContractArgs<'b>,
-        _ns: &ast::Namespace,
+        ns: &ast::Namespace,
         _loc: Loc,
         _loc: Loc,
     ) {
     ) {
         contract_args.program_id = Some(address);
         contract_args.program_id = Some(address);
@@ -1242,7 +1242,7 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
 
 
         assert!(contract_args.accounts.is_some());
         assert!(contract_args.accounts.is_some());
         // The AccountMeta array is always present for Solana contracts
         // The AccountMeta array is always present for Solana contracts
-        self.build_invoke_signed_c(binary, function, payload, payload_len, contract_args);
+        self.build_invoke_signed_c(binary, function, payload, payload_len, contract_args, ns);
     }
     }
 
 
     fn builtin_function(
     fn builtin_function(
@@ -1344,12 +1344,19 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
     ) {
     ) {
         let address = address.unwrap();
         let address = address.unwrap();
 
 
+        if contract_args.accounts.is_none() {
+            contract_args.accounts = Some((
+                binary
+                    .context
+                    .i64_type()
+                    .ptr_type(AddressSpace::default())
+                    .const_zero(),
+                binary.context.i32_type().const_zero(),
+            ))
+        };
+
         contract_args.program_id = Some(address);
         contract_args.program_id = Some(address);
-        if contract_args.accounts.is_some() {
-            self.build_invoke_signed_c(binary, function, payload, payload_len, contract_args);
-        } else {
-            self.build_external_call(binary, payload, payload_len, contract_args, ns);
-        }
+        self.build_invoke_signed_c(binary, function, payload, payload_len, contract_args, ns);
     }
     }
 
 
     /// Get return buffer for external call
     /// Get return buffer for external call

+ 60 - 2
src/sema/ast.rs

@@ -4,6 +4,7 @@ use super::symtable::Symtable;
 use crate::abi::anchor::discriminator;
 use crate::abi::anchor::discriminator;
 use crate::codegen::cfg::{ControlFlowGraph, Instr};
 use crate::codegen::cfg::{ControlFlowGraph, Instr};
 use crate::diagnostics::Diagnostics;
 use crate::diagnostics::Diagnostics;
+use crate::sema::ast::ExternalCallAccounts::{AbsentArgument, NoAccount};
 use crate::sema::yul::ast::{InlineAssembly, YulFunction};
 use crate::sema::yul::ast::{InlineAssembly, YulFunction};
 use crate::sema::Recurse;
 use crate::sema::Recurse;
 use crate::{codegen, Target};
 use crate::{codegen, Target};
@@ -1216,12 +1217,69 @@ pub struct CallArgs {
     pub gas: Option<Box<Expression>>,
     pub gas: Option<Box<Expression>>,
     pub salt: Option<Box<Expression>>,
     pub salt: Option<Box<Expression>>,
     pub value: Option<Box<Expression>>,
     pub value: Option<Box<Expression>>,
-    pub accounts: Option<Box<Expression>>,
+    pub accounts: ExternalCallAccounts<Box<Expression>>,
     pub seeds: Option<Box<Expression>>,
     pub seeds: Option<Box<Expression>>,
     pub flags: Option<Box<Expression>>,
     pub flags: Option<Box<Expression>>,
     pub program_id: Option<Box<Expression>>,
     pub program_id: Option<Box<Expression>>,
 }
 }
 
 
+/// This enum manages the accounts in an external call on Solana. There can be three options:
+/// 1. The developer explicitly specifies there are not accounts for the call (`NoAccount`).
+/// 2. The accounts call argument is absent, in which case we attempt to generate the AccountMetas
+/// vector automatically (`AbsentArgumet`).
+/// 3. There are accounts specified in the accounts call argument (Present).
+#[derive(PartialEq, Eq, Clone, Debug, Default)]
+pub enum ExternalCallAccounts<T> {
+    NoAccount,
+    #[default]
+    AbsentArgument,
+    Present(T),
+}
+
+impl<T> ExternalCallAccounts<T> {
+    /// Is the accounts call argument missing?
+    pub fn is_absent(&self) -> bool {
+        matches!(self, ExternalCallAccounts::AbsentArgument)
+    }
+
+    /// Returns if the accounts call argument was present in the call
+    pub fn argument_provided(&self) -> bool {
+        matches!(
+            self,
+            ExternalCallAccounts::Present(_) | ExternalCallAccounts::NoAccount
+        )
+    }
+
+    /// Applies a function on the nested objects
+    pub fn map<P, F>(&self, func: F) -> ExternalCallAccounts<P>
+    where
+        F: FnOnce(&T) -> P,
+    {
+        match self {
+            NoAccount => NoAccount,
+            AbsentArgument => AbsentArgument,
+            ExternalCallAccounts::Present(value) => ExternalCallAccounts::Present(func(value)),
+        }
+    }
+
+    /// Transform the nested object into a reference
+    pub const fn as_ref(&self) -> ExternalCallAccounts<&T> {
+        match self {
+            ExternalCallAccounts::Present(value) => ExternalCallAccounts::Present(value),
+            NoAccount => NoAccount,
+            AbsentArgument => AbsentArgument,
+        }
+    }
+
+    /// Return a reference to the nested object
+    pub fn unwrap(&self) -> &T {
+        match self {
+            ExternalCallAccounts::Present(value) => value,
+            _ => panic!("unwrap called at variant without a nested object"),
+        }
+    }
+}
+
 impl Recurse for CallArgs {
 impl Recurse for CallArgs {
     type ArgType = Expression;
     type ArgType = Expression;
     fn recurse<T>(&self, cx: &mut T, f: fn(expr: &Expression, ctx: &mut T) -> bool) {
     fn recurse<T>(&self, cx: &mut T, f: fn(expr: &Expression, ctx: &mut T) -> bool) {
@@ -1234,7 +1292,7 @@ impl Recurse for CallArgs {
         if let Some(value) = &self.value {
         if let Some(value) = &self.value {
             value.recurse(cx, f);
             value.recurse(cx, f);
         }
         }
-        if let Some(accounts) = &self.accounts {
+        if let ExternalCallAccounts::Present(accounts) = &self.accounts {
             accounts.recurse(cx, f);
             accounts.recurse(cx, f);
         }
         }
         if let Some(flags) = &self.flags {
         if let Some(flags) = &self.flags {

+ 105 - 2
src/sema/contracts.rs

@@ -8,12 +8,14 @@ use super::{
     symtable::Symtable,
     symtable::Symtable,
     using, variables, ContractDefinition,
     using, variables, ContractDefinition,
 };
 };
+use crate::sema::ast::SolanaAccount;
 use crate::sema::expression::constructor::match_constructor_to_args;
 use crate::sema::expression::constructor::match_constructor_to_args;
 use crate::{sema::ast::Namespace, sema::unused_variable::emit_warning_local_variable};
 use crate::{sema::ast::Namespace, sema::unused_variable::emit_warning_local_variable};
+use indexmap::{IndexMap, IndexSet};
 use num_bigint::BigInt;
 use num_bigint::BigInt;
 use num_traits::Zero;
 use num_traits::Zero;
 use once_cell::unsync::OnceCell;
 use once_cell::unsync::OnceCell;
-use solang_parser::diagnostics::Diagnostic;
+use solang_parser::diagnostics::{Diagnostic, Note};
 use solang_parser::pt::FunctionTy;
 use solang_parser::pt::FunctionTy;
 use solang_parser::pt::{self, CodeLocation};
 use solang_parser::pt::{self, CodeLocation};
 use std::collections::{BTreeMap, HashMap, HashSet};
 use std::collections::{BTreeMap, HashMap, HashSet};
@@ -651,7 +653,6 @@ fn check_inheritance(contract_no: usize, ns: &mut ast::Namespace) {
             .skip(1)
             .skip(1)
             .map(|(_, function_no)| {
             .map(|(_, function_no)| {
                 let func = &ns.functions[*function_no];
                 let func = &ns.functions[*function_no];
-
                 ast::Note {
                 ast::Note {
                     loc: func.loc,
                     loc: func.loc,
                     message: format!("previous definition of function '{}'", func.name),
                     message: format!("previous definition of function '{}'", func.name),
@@ -833,6 +834,108 @@ fn base_function_compatible(
         // rust compile wants this, already handled in first arm
         // rust compile wants this, already handled in first arm
         (None, None) => (),
         (None, None) => (),
     }
     }
+
+    let mut no_correspondence: Vec<(pt::Loc, &String)> = Vec::new();
+    let mut incorrect_flag: IndexSet<(pt::Loc, pt::Loc, &String)> = IndexSet::new();
+    let func_accounts = &*func.solana_accounts.borrow();
+    let base_accounts = &*base.solana_accounts.borrow();
+    let mut correct_ordering = true;
+
+    let (correct, func_acc_locations) = check_override_accounts_compatible(
+        base_accounts,
+        func_accounts,
+        &mut no_correspondence,
+        &mut incorrect_flag,
+        false,
+    );
+    correct_ordering &= correct;
+
+    let (correct, base_acc_locations) = check_override_accounts_compatible(
+        func_accounts,
+        base_accounts,
+        &mut no_correspondence,
+        &mut incorrect_flag,
+        true,
+    );
+    correct_ordering &= correct;
+
+    if !no_correspondence.is_empty() {
+        let notes = no_correspondence
+            .iter()
+            .map(|(loc, account_name)| Note {
+                loc: *loc,
+                message: format!("corresponding account '{}' is missing", account_name),
+            })
+            .collect::<Vec<Note>>();
+
+        diagnostics.push(Diagnostic::error_with_notes(
+            func.loc,
+            "functions must have the same declared accounts for correct overriding".to_string(),
+            notes,
+        ));
+    }
+
+    if !incorrect_flag.is_empty() {
+        for (loc_1, loc_2, account_name) in &incorrect_flag {
+            diagnostics.push(Diagnostic::error_with_note(
+                *loc_1,
+                format!(
+                    "account '{}' must be declared with the same annotation for overriding",
+                    account_name
+                ),
+                *loc_2,
+                "location of other declaration".to_string(),
+            ));
+        }
+    }
+
+    if !correct_ordering {
+        diagnostics.push(Diagnostic::error_with_note(
+            func_acc_locations.unwrap(),
+            "accounts must be declared in the same order for overriding".to_string(),
+            base_acc_locations.unwrap(),
+            "location of base function accounts".to_string(),
+        ));
+    }
+}
+
+/// Checks if the accounts from the virtual function and the overriding one are compatible.
+/// Returns true if the accounts have been declared in the same order in both functions and
+/// the location of all the account declarations.
+fn check_override_accounts_compatible<'a>(
+    func_accounts: &'a IndexMap<String, SolanaAccount>,
+    other_accounts: &'a IndexMap<String, SolanaAccount>,
+    no_correspondence: &mut Vec<(pt::Loc, &'a String)>,
+    incorrect_flag: &mut IndexSet<(pt::Loc, pt::Loc, &'a String)>,
+    reverse: bool,
+) -> (bool, Option<pt::Loc>) {
+    let mut correct_order = true;
+    let mut locations = if let Some((_, acc)) = other_accounts.get_index(0) {
+        Some(acc.loc)
+    } else {
+        None
+    };
+
+    for (account_no, (account_name, account_flags)) in other_accounts.iter().enumerate() {
+        locations.as_mut().unwrap().union(&account_flags.loc);
+        if let Some((other_no, _, other_account)) = func_accounts.get_full(account_name) {
+            if other_account.is_signer != account_flags.is_signer
+                || other_account.is_writer != account_flags.is_writer
+            {
+                if reverse {
+                    incorrect_flag.insert((other_account.loc, account_flags.loc, account_name));
+                } else {
+                    incorrect_flag.insert((account_flags.loc, other_account.loc, account_name));
+                }
+            } else if account_no != other_no {
+                correct_order = false;
+            }
+        } else {
+            no_correspondence.push((account_flags.loc, account_name));
+        }
+    }
+
+    (correct_order, locations)
 }
 }
 
 
 /// Function body which should be resolved.
 /// Function body which should be resolved.

+ 1 - 1
src/sema/dotgraphviz.rs

@@ -1474,7 +1474,7 @@ impl Dot {
         if let Some(salt) = &call_args.salt {
         if let Some(salt) = &call_args.salt {
             self.add_expression(salt, func, ns, node, String::from("salt"));
             self.add_expression(salt, func, ns, node, String::from("salt"));
         }
         }
-        if let Some(accounts) = &call_args.accounts {
+        if let ExternalCallAccounts::Present(accounts) = &call_args.accounts {
             self.add_expression(accounts, func, ns, node, String::from("accounts"));
             self.add_expression(accounts, func, ns, node, String::from("accounts"));
         }
         }
         if let Some(seeds) = &call_args.seeds {
         if let Some(seeds) = &call_args.seeds {

+ 1 - 1
src/sema/expression/assign.rs

@@ -197,7 +197,7 @@ pub(super) fn assign_expr(
     ) {
     ) {
         ResolveTo::Unknown
         ResolveTo::Unknown
     } else {
     } else {
-        ResolveTo::Type(var_ty.deref_any())
+        ResolveTo::Type(var_ty.deref_any().deref_any())
     };
     };
 
 
     let set = expression(right, context, ns, symtable, diagnostics, resolve_to)?;
     let set = expression(right, context, ns, symtable, diagnostics, resolve_to)?;

+ 1 - 1
src/sema/expression/constructor.rs

@@ -647,7 +647,7 @@ pub(super) fn solana_constructor_check(
         ));
         ));
     }
     }
 
 
-    if !context.in_a_loop() || call_args.accounts.is_some() {
+    if !context.in_a_loop() || !call_args.accounts.is_absent() {
         return;
         return;
     }
     }
 
 

+ 11 - 5
src/sema/expression/function_call.rs

@@ -1,8 +1,8 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-License-Identifier: Apache-2.0
 
 
 use crate::sema::ast::{
 use crate::sema::ast::{
-    ArrayLength, Builtin, CallArgs, CallTy, Expression, Function, Mutability, Namespace,
-    RetrieveType, StructType, Symbol, Type,
+    ArrayLength, Builtin, CallArgs, CallTy, Expression, ExternalCallAccounts, Function, Mutability,
+    Namespace, RetrieveType, StructType, Symbol, Type,
 };
 };
 use crate::sema::contracts::is_base;
 use crate::sema::contracts::is_base;
 use crate::sema::diagnostics::Diagnostics;
 use crate::sema::diagnostics::Diagnostics;
@@ -1912,6 +1912,13 @@ pub(super) fn parse_call_args(
                     return Err(());
                     return Err(());
                 }
                 }
 
 
+                if let pt::Expression::ArrayLiteral(_, vec) = &arg.expr {
+                    if vec.is_empty() {
+                        res.accounts = ExternalCallAccounts::NoAccount;
+                        continue;
+                    }
+                }
+
                 let expr = expression(
                 let expr = expression(
                     &arg.expr,
                     &arg.expr,
                     context,
                     context,
@@ -1949,7 +1956,7 @@ pub(super) fn parse_call_args(
                     ));
                     ));
                 }
                 }
 
 
-                res.accounts = Some(Box::new(expr));
+                res.accounts = ExternalCallAccounts::Present(Box::new(expr));
             }
             }
             "seeds" => {
             "seeds" => {
                 if ns.target != Target::Solana {
                 if ns.target != Target::Solana {
@@ -2036,8 +2043,7 @@ pub(super) fn parse_call_args(
     }
     }
 
 
     if ns.target == Target::Solana {
     if ns.target == Target::Solana {
-        if !external_call
-            && res.accounts.is_none()
+        if res.accounts.is_absent()
             && !matches!(
             && !matches!(
                 ns.functions[context.function_no.unwrap()].visibility,
                 ns.functions[context.function_no.unwrap()].visibility,
                 Visibility::External(_)
                 Visibility::External(_)

+ 102 - 59
src/sema/function_annotation.rs

@@ -16,7 +16,7 @@ use crate::sema::solana_accounts::BuiltinAccounts;
 use crate::Target;
 use crate::Target;
 use indexmap::map::Entry;
 use indexmap::map::Entry;
 use num_traits::ToPrimitive;
 use num_traits::ToPrimitive;
-use solang_parser::pt::{self, Annotation, CodeLocation};
+use solang_parser::pt::{self, Annotation, CodeLocation, Visibility};
 use std::str::FromStr;
 use std::str::FromStr;
 
 
 /// Annotations are processed in two different places during sema. When we are resolving the
 /// Annotations are processed in two different places during sema. When we are resolving the
@@ -48,6 +48,28 @@ pub fn function_prototype_annotations(
     for annotation in annotations {
     for annotation in annotations {
         match annotation.id.name.as_str() {
         match annotation.id.name.as_str() {
             "selector" => function_selector(func, annotation, &mut diagnostics, ns),
             "selector" => function_selector(func, annotation, &mut diagnostics, ns),
+            "account" | "signer" | "mutableAccount" | "mutableSigner"
+                if ns.target == Target::Solana =>
+            {
+                if !func.is_constructor() && !matches!(func.visibility, Visibility::External(..)) {
+                    diagnostics.push(Diagnostic::error(
+                        annotation.loc,
+                        "account declarations are only valid in functions declared as external"
+                            .to_string(),
+                    ));
+                    continue;
+                }
+
+                account_declaration(
+                    &annotation.loc,
+                    annotation.value.as_ref().unwrap(),
+                    func,
+                    annotation.id.name.as_str(),
+                    &mut ns.diagnostics,
+                    &mut ConstructorAnnotations::default(),
+                );
+            }
+
             _ if !func.has_body => {
             _ if !func.has_body => {
                 // function_body_annotations() is called iff there is a body
                 // function_body_annotations() is called iff there is a body
                 diagnostics.push(Diagnostic::error(
                 diagnostics.push(Diagnostic::error(
@@ -180,10 +202,8 @@ pub(super) fn function_body_annotations(
 
 
     // On Solana, the seeds and bump for a constructor can be specified using annotations, for example
     // On Solana, the seeds and bump for a constructor can be specified using annotations, for example
     //
     //
-    // @seed(param1)
     // @seed("fizbaz")
     // @seed("fizbaz")
-    // @bump(param2)
-    // constructor(bytes param1, uint8 param2) {}
+    // constructor(@seed bytes param1, @bump uint8 param2) {}
 
 
     let mut has_annotation = false;
     let mut has_annotation = false;
 
 
@@ -249,62 +269,19 @@ pub(super) fn function_body_annotations(
                 );
                 );
             }
             }
             "payer" if is_solana_constructor => {
             "payer" if is_solana_constructor => {
-                let loc = note.loc;
-                if let pt::Expression::Variable(id) = note.value.as_ref().unwrap() {
-                    if BuiltinAccounts::from_str(&id.name).is_ok() {
-                        diagnostics.push(Diagnostic::error(
-                            id.loc,
-                            format!("'{}' is a reserved account name", id.name),
-                        ));
-                        continue;
-                    } else if id.name.contains(BuiltinAccounts::DataAccount.as_str()) {
-                        diagnostics.push(Diagnostic::error(
-                            id.loc,
-                            "account names that contain 'dataAccount' are reserved".to_string(),
-                        ));
-                        continue;
-                    }
-
-                    match ns.functions[function_no]
-                        .solana_accounts
-                        .borrow_mut()
-                        .entry(id.name.clone())
-                    {
-                        Entry::Occupied(other_account) => {
-                            diagnostics.push(Diagnostic::error_with_note(
-                                id.loc,
-                                format!("account '{}' already defined", id.name),
-                                other_account.get().loc,
-                                "previous definition".to_string(),
-                            ));
-                        }
-                        Entry::Vacant(vacancy) => {
-                            if let Some((prev, _)) = &annotations.payer {
-                                duplicate_annotation(
-                                    &mut diagnostics,
-                                    "payer",
-                                    loc,
-                                    *prev,
-                                    ns.functions[function_no].ty.as_str(),
-                                );
-                            } else {
-                                vacancy.insert(SolanaAccount {
-                                    loc: note.loc,
-                                    is_signer: true,
-                                    is_writer: true,
-                                    generated: false,
-                                });
-                                annotations.payer = Some((loc, id.name.clone()));
-                            }
-                        }
-                    }
-                } else {
-                    diagnostics.push(Diagnostic::error(
-                        note.loc,
-                        "invalid parameter for annotation".to_string(),
-                    ));
-                }
+                account_declaration(
+                    &note.loc,
+                    note.value.as_ref().unwrap(),
+                    &ns.functions[function_no],
+                    note.id.name.as_str(),
+                    &mut diagnostics,
+                    &mut annotations,
+                );
             }
             }
+            "account" | "signer" | "mutableAccount" | "mutableSigner"
+            // We already deal with these cases in `function_prototype_annotation`
+                if ns.target == Target::Solana => (),
+
             _ => diagnostics.push(Diagnostic::error(
             _ => diagnostics.push(Diagnostic::error(
                 note.loc,
                 note.loc,
                 format!(
                 format!(
@@ -520,3 +497,69 @@ fn duplicate_annotation(
         format!("previous @{}", name),
         format!("previous @{}", name),
     ));
     ));
 }
 }
+
+fn account_declaration(
+    loc: &pt::Loc,
+    expr: &pt::Expression,
+    func: &Function,
+    annotation_name: &str,
+    diagnostics: &mut Diagnostics,
+    resolved_annotations: &mut ConstructorAnnotations,
+) {
+    if let pt::Expression::Variable(id) = expr {
+        if BuiltinAccounts::from_str(&id.name).is_ok() {
+            diagnostics.push(Diagnostic::error(
+                id.loc,
+                format!("'{}' is a reserved account name", id.name),
+            ));
+            return;
+        } else if id.name.contains("dataAccount") {
+            diagnostics.push(Diagnostic::error(
+                id.loc,
+                "account names that contain 'dataAccount' are reserved".to_string(),
+            ));
+            return;
+        }
+
+        match func.solana_accounts.borrow_mut().entry(id.name.clone()) {
+            Entry::Occupied(other_account) => {
+                diagnostics.push(Diagnostic::error_with_note(
+                    id.loc,
+                    format!("account '{}' already defined", id.name),
+                    other_account.get().loc,
+                    "previous definition".to_string(),
+                ));
+            }
+            Entry::Vacant(vacancy) => {
+                if let Some(prev) = &resolved_annotations.payer {
+                    duplicate_annotation(
+                        diagnostics,
+                        annotation_name,
+                        *loc,
+                        prev.0,
+                        func.ty.as_str(),
+                    );
+                } else {
+                    vacancy.insert(SolanaAccount {
+                        loc: *loc,
+                        is_signer: matches!(annotation_name, "payer" | "signer" | "mutableSigner"),
+                        is_writer: matches!(
+                            annotation_name,
+                            "mutableAccount" | "payer" | "mutableSigner"
+                        ),
+                        generated: false,
+                    });
+
+                    if annotation_name == "payer" {
+                        resolved_annotations.payer = Some((*loc, id.name.clone()));
+                    }
+                }
+            }
+        }
+    } else {
+        diagnostics.push(Diagnostic::error(
+            *loc,
+            "invalid parameter for annotation".to_string(),
+        ));
+    }
+}

+ 2 - 1
src/sema/mutability.rs

@@ -11,6 +11,7 @@ use super::{
 use crate::sema::ast::SolanaAccount;
 use crate::sema::ast::SolanaAccount;
 use crate::sema::solana_accounts::BuiltinAccounts;
 use crate::sema::solana_accounts::BuiltinAccounts;
 use crate::sema::yul::builtin::YulBuiltInFunction;
 use crate::sema::yul::builtin::YulBuiltInFunction;
+use crate::Target;
 use bitflags::bitflags;
 use bitflags::bitflags;
 use solang_parser::pt::Loc;
 use solang_parser::pt::Loc;
 use solang_parser::{helpers::CodeLocation, pt};
 use solang_parser::{helpers::CodeLocation, pt};
@@ -238,7 +239,7 @@ fn check_mutability(func: &Function, ns: &Namespace) -> Vec<Diagnostic> {
         }
         }
     }
     }
 
 
-    if state.data_account != DataAccountUsage::NONE {
+    if state.data_account != DataAccountUsage::NONE && ns.target == Target::Solana {
         func.solana_accounts.borrow_mut().insert(
         func.solana_accounts.borrow_mut().insert(
             BuiltinAccounts::DataAccount.to_string(),
             BuiltinAccounts::DataAccount.to_string(),
             SolanaAccount {
             SolanaAccount {

+ 1 - 1
src/sema/tests/mod.rs

@@ -542,7 +542,7 @@ contract creator {
 
 
         Child.new{accounts: metas}(payer);
         Child.new{accounts: metas}(payer);
 
 
-        Child.say_hello();
+        Child.say_hello{accounts: []}();
     }
     }
 }
 }
 
 

+ 3 - 2
src/sema/unused_variable.rs

@@ -1,7 +1,8 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-License-Identifier: Apache-2.0
 
 
 use crate::sema::ast::{
 use crate::sema::ast::{
-    Builtin, CallArgs, Diagnostic, EventDecl, Expression, Namespace, RetrieveType,
+    Builtin, CallArgs, Diagnostic, EventDecl, Expression, ExternalCallAccounts, Namespace,
+    RetrieveType,
 };
 };
 use crate::sema::symtable::{Symtable, VariableUsage};
 use crate::sema::symtable::{Symtable, VariableUsage};
 use crate::sema::{ast, symtable};
 use crate::sema::{ast, symtable};
@@ -272,7 +273,7 @@ fn check_call_args(ns: &mut Namespace, call_args: &CallArgs, symtable: &mut Symt
     if let Some(value) = &call_args.value {
     if let Some(value) = &call_args.value {
         used_variable(ns, value.as_ref(), symtable);
         used_variable(ns, value.as_ref(), symtable);
     }
     }
-    if let Some(accounts) = &call_args.accounts {
+    if let ExternalCallAccounts::Present(accounts) = &call_args.accounts {
         used_variable(ns, accounts.as_ref(), symtable);
         used_variable(ns, accounts.as_ref(), symtable);
     }
     }
     if let Some(seeds) = &call_args.seeds {
     if let Some(seeds) = &call_args.seeds {

+ 0 - 29
stdlib/solana.c

@@ -55,35 +55,6 @@ uint64_t entrypoint(const uint8_t *input)
     return solang_dispatch(&params);
     return solang_dispatch(&params);
 }
 }
 
 
-uint64_t sol_invoke_signed_c(const SolInstruction *instruction, const SolAccountInfo *account_infos,
-                             int account_infos_len, const SolSignerSeeds *signers_seeds, int signers_seeds_len);
-
-// Calls an external function when 'program_id' is NULL or
-// creates a new contract and calls its constructor.
-uint64_t external_call(uint8_t *input, uint32_t input_len, SolPubkey *program_id, const SolSignerSeeds *seeds,
-                       int seeds_len, SolParameters *params)
-{
-    SolAccountMeta metas[10];
-    SolInstruction instruction = {
-        .program_id = program_id,
-        .accounts = metas,
-        .account_len = params->ka_num,
-        .data = input,
-        .data_len = input_len,
-    };
-
-    // When the '{accounts: ...}' call argument is missing, we pass on all the accounts in the transaction.
-    for (int account_no = 0; account_no < params->ka_num; account_no++)
-    {
-        SolAccountInfo *acc = &params->ka[account_no];
-        metas[account_no].pubkey = acc->key;
-        metas[account_no].is_writable = acc->is_writable;
-        metas[account_no].is_signer = acc->is_signer;
-    }
-
-    return sol_invoke_signed_c(&instruction, params->ka, params->ka_num, seeds, seeds_len);
-}
-
 uint64_t *sol_account_lamport(uint8_t *address, SolParameters *params)
 uint64_t *sol_account_lamport(uint8_t *address, SolParameters *params)
 {
 {
     SolPubkey *pubkey = (SolPubkey *)address;
     SolPubkey *pubkey = (SolPubkey *)address;

+ 1 - 1
stdlib/solana_sdk.h

@@ -400,7 +400,7 @@ typedef struct
  */
  */
 typedef struct
 typedef struct
 {
 {
-    const SolSignerSeed *addr; /** An arry of a signer's seeds */
+    const SolSignerSeed *addr; /** An array of a signer's seeds */
     uint64_t len;              /** Number of seeds */
     uint64_t len;              /** Number of seeds */
 } SolSignerSeeds;
 } SolSignerSeeds;
 
 

+ 1 - 1
tests/codegen_testcases/solidity/import_ext_call.sol

@@ -4,7 +4,7 @@ import '../import_test.sol' as My;
 @program_id("6qEm4QUJGFvqKNJGjTrAEiFhbVBY4ashpBjDHEFvEUmW")
 @program_id("6qEm4QUJGFvqKNJGjTrAEiFhbVBY4ashpBjDHEFvEUmW")
 contract Foo {
 contract Foo {
     // BEGIN-CHECK: Foo::Foo::function::get_b
     // BEGIN-CHECK: Foo::Foo::function::get_b
-    function get_b(address id) public pure {
+    function get_b(address id) external pure {
         // External calls
         // External calls
         // CHECK: external call::regular address:(arg #0) payload:%abi_encoded.temp.2 value:uint64 0 gas:uint64 0 accounts:[0] [  ] seeds: contract|function:(2, 2) flags:
         // CHECK: external call::regular address:(arg #0) payload:%abi_encoded.temp.2 value:uint64 0 gas:uint64 0 accounts:[0] [  ] seeds: contract|function:(2, 2) flags:
         My.Dog.barks{program_id: id}("woof");
         My.Dog.barks{program_id: id}("woof");

+ 1 - 1
tests/codegen_testcases/solidity/solana_payer_account.sol

@@ -8,7 +8,7 @@ contract Builder {
         Built.new("my_seed");
         Built.new("my_seed");
     }
     }
 
 
-    function call_that() public view {
+    function call_that() external view {
         Built.say_this("Hold up! I'm calling!");
         Built.say_this("Hold up! I'm calling!");
     }
     }
 }
 }

+ 15 - 0
tests/contract_testcases/polkadot/annotations/solana_annotations.sol

@@ -0,0 +1,15 @@
+contract Test1 {
+    @account(foo)
+    @mutableAccount(bar)
+    @signer(signerFoo)
+    @mutableSigner(signerBar)
+    function doThis() external returns (uint64) {
+        return 64;
+    }
+}
+
+// ---- Expect: diagnostics ----
+// error: 2:5-18: unknown annotation account for function
+// error: 3:5-25: unknown annotation mutableAccount for function
+// error: 4:5-23: unknown annotation signer for function
+// error: 5:5-30: unknown annotation mutableSigner for function

+ 1 - 0
tests/contract_testcases/solana/abstract_interface.sol

@@ -8,3 +8,4 @@ contract C {
 }
 }
 
 
 // ---- Expect: diagnostics ----
 // ---- Expect: diagnostics ----
+// error: 6:3-25: accounts are required for calling a contract. You can either provide the accounts with the {accounts: ...} call argument or change this function's visibility to external

+ 26 - 0
tests/contract_testcases/solana/accounts/account_collision.sol

@@ -0,0 +1,26 @@
+contract Test1 {
+    @account(foo)
+    @mutableAccount(bar)
+    @signer(signerFoo)
+    @mutableSigner(signerBar)
+    function doThis() external returns (uint64) {
+        assert(tx.accounts.signerFoo.is_signer);
+        assert(tx.accounts.signerBar.is_signer);
+
+        return tx.accounts.foo.lamports;
+    }
+}
+
+contract Test2 {
+    @account(t1Id)
+    @account(foo)
+    function callThat() external returns (uint64) {
+        uint64 res = Test1.doThis{program_id: tx.accounts.t1Id.key}();
+        return res;
+    }
+}
+
+// ---- Expect: diagnostics ----
+// warning: 6:5-48: function can be declared 'view'
+// error: 16:5-18: account name collision encountered. Calling a function that requires an account whose name is also defined in the current function will create duplicate names in the IDL. Please, rename one of the accounts
+// 	note 2:5-18: other declaration

+ 22 - 0
tests/contract_testcases/solana/accounts/accounts_required.sol

@@ -0,0 +1,22 @@
+@program_id("Ex9GgvN2ypqwFRGnsSZfAnXAnw5eRPDHyqRFnDWugxrb")
+contract Test1 {
+    function doThis() external returns (uint64) {
+        return 3;
+    }
+}
+
+contract Test2 {
+    function callThat() public returns (uint64) {
+        uint64 res = Test1.doThis();
+        return res;
+    }
+
+    function callThat2() public returns (uint64) {
+        // This is correct
+        uint64 res = Test1.doThis{accounts: []}();
+        return res;
+    }
+}
+
+// ---- Expect: diagnostics ----
+// error: 10:22-36: accounts are required for calling a contract. You can either provide the accounts with the {accounts: ...} call argument or change this function's visibility to external

+ 29 - 0
tests/contract_testcases/solana/accounts/incorrect_annotations.sol

@@ -0,0 +1,29 @@
+contract Test1 {
+
+    uint32 g;
+    @account(foo)
+    @mutableAccount(bar)
+    function doThis() public returns (uint64) {
+
+        return tx.accounts.foo.lamports;
+    }
+
+    @account(32)
+    @signer("Hello")
+    function invalid_paramter() external view returns (uint32) {
+        return g;
+    }
+
+    @bar(foo)
+    function invalid_annotation() public view returns (uint32) {
+        return g;
+    }
+}
+
+// ---- Expect: diagnostics ----
+// error: 4:5-18: account declarations are only valid in functions declared as external
+// error: 5:5-25: account declarations are only valid in functions declared as external
+// error: 8:28-31: unrecognized account
+// error: 11:5-17: invalid parameter for annotation
+// error: 12:5-21: invalid parameter for annotation
+// error: 17:5-14: unknown annotation bar for function

+ 15 - 0
tests/contract_testcases/solana/accounts/repated_declaration.sol

@@ -0,0 +1,15 @@
+contract Test0 {
+    @account(foo)
+    @mutableAccount(foo)
+    @signer(signerFoo)
+    @mutableSigner(signerFoo)
+    function doThis() external returns (uint64) {
+        return 64;
+    }
+}
+
+// ---- Expect: diagnostics ----
+// error: 3:21-24: account 'foo' already defined
+// 	note 2:5-18: previous definition
+// error: 5:20-29: account 'signerFoo' already defined
+// 	note 4:5-23: previous definition

+ 37 - 0
tests/contract_testcases/solana/annotations/abstract_function.sol

@@ -0,0 +1,37 @@
+
+abstract contract Base1 {
+    @account(acc1)
+    function test(uint64 val) virtual external {}
+}
+
+abstract contract Base2 {
+    @mutableAccount(acc2)
+    function test(uint64 val) virtual external {}
+}
+
+contract Derived1 is Base1 {
+    bool b;
+    @signer(other)
+    function test(uint64 val) override (Base1) external {
+        b = (tx.accounts.dataAccount.key == address(this));
+    }
+}
+
+contract Derived2 is Base1, Base2 {
+    bool b;
+    @account(acc1)
+    function test(uint64 val) override(Base1, Base2) external {
+        b = true;
+    }
+}
+
+// ---- Expect: diagnostics ----
+// error: 4:5-47: functions must have the same declared accounts for correct overriding
+// 	note 3:5-19: corresponding account 'acc1' is missing
+// 	note 8:5-26: corresponding account 'acc2' is missing
+// error: 15:5-56: functions must have the same declared accounts for correct overriding
+// 	note 14:5-19: corresponding account 'other' is missing
+// 	note 3:5-19: corresponding account 'acc1' is missing
+// error: 23:5-62: functions must have the same declared accounts for correct overriding
+// 	note 22:5-19: corresponding account 'acc1' is missing
+// 	note 8:5-26: corresponding account 'acc2' is missing

+ 22 - 0
tests/contract_testcases/solana/annotations/accounts_on_interface.sol

@@ -0,0 +1,22 @@
+
+interface Foo {
+    @signer(acc1)
+    @account(acc2)
+    function Bar() external;
+}
+
+contract Derived is Foo {
+
+    bool b;
+    @mutableSigner(acc1)
+    @mutableAccount(acc2)
+    function Bar()  external {
+        b = false;
+    }
+}
+
+// ---- Expect: diagnostics ----
+// error: 11:5-25: account 'acc1' must be declared with the same annotation for overriding
+// 	note 3:5-18: location of other declaration
+// error: 12:5-26: account 'acc2' must be declared with the same annotation for overriding
+// 	note 4:5-19: location of other declaration

+ 1 - 0
tests/contract_testcases/solana/annotations/constructor_external_function.sol

@@ -28,3 +28,4 @@ contract Bar {
 // ---- Expect: diagnostics ----
 // ---- Expect: diagnostics ----
 // error: 11:17-30: 'address' not a valid call parameter
 // error: 11:17-30: 'address' not a valid call parameter
 // error: 16:9-18: accounts are required for calling a contract. You can either provide the accounts with the {accounts: ...} call argument or change this function's visibility to external
 // error: 16:9-18: accounts are required for calling a contract. You can either provide the accounts with the {accounts: ...} call argument or change this function's visibility to external
+// error: 24:9-24: accounts are required for calling a contract. You can either provide the accounts with the {accounts: ...} call argument or change this function's visibility to external

+ 2 - 2
tests/contract_testcases/solana/call/call_args_three_ways.sol

@@ -5,7 +5,7 @@ contract C {
 		(D.new){value: 1}();
 		(D.new){value: 1}();
 		D.new{value: 1}();
 		D.new{value: 1}();
 	}
 	}
-	function g() public {
+	function g() external {
 		// Three different parse tree for callargs
 		// Three different parse tree for callargs
 		D.func{value: 1}();
 		D.func{value: 1}();
 		(D.func){value: 1}();
 		(D.func){value: 1}();
@@ -16,7 +16,7 @@ contract C {
 @program_id("A2tWahcQqU7Mic5o4nGWPKt9rQaLVyh7cyF4MmCXksJt")
 @program_id("A2tWahcQqU7Mic5o4nGWPKt9rQaLVyh7cyF4MmCXksJt")
 contract D {
 contract D {
 	constructor() payable {}
 	constructor() payable {}
-	function func() payable public {}
+	function func() payable external {}
 }
 }
 
 
 // ---- Expect: diagnostics ----
 // ---- Expect: diagnostics ----

+ 1 - 1
tests/contract_testcases/solana/constant/not_constant.sol

@@ -4,7 +4,7 @@
         }
         }
 
 
         contract foo {
         contract foo {
-            function f() public returns (uint) {
+            function f() external returns (uint) {
                 uint a = C.STATIC();
                 uint a = C.STATIC();
                 return a;
                 return a;
             }
             }

+ 1 - 1
tests/contract_testcases/solana/destructure_assign_struct_member_2.sol

@@ -13,7 +13,7 @@ contract Contract {
         uint256 b;
         uint256 b;
     }
     }
 
 
-    function test(address[] memory _tokens) public view {
+    function test(address[] memory _tokens) external view {
 	uint size = 3;
 	uint size = 3;
 
 
         // get shares and eth required for each share
         // get shares and eth required for each share

+ 25 - 0
tests/contract_testcases/solana/empty_vector.sol

@@ -0,0 +1,25 @@
+contract Test {
+    function testThis() public returns (uint64[]) {
+        uint64[] var = [];
+        return var;
+    }
+
+    function initialize() public returns (string[2]) {
+        string[2] st = [];
+        return st;
+    }
+
+    function callThat() public returns (uint32) {
+        changeThis([]);
+        return 2;
+    }
+
+    function changeThis(uint32[] var) private view {
+        var[2] = 5;
+    }
+}
+
+// ---- Expect: diagnostics ----
+// error: 3:24-26: array requires at least one element
+// error: 8:24-26: array requires at least one element
+// error: 13:20-22: array requires at least one element

+ 2 - 1
tests/contract_testcases/solana/functions/external_functions.sol

@@ -30,7 +30,7 @@ contract bar2 is bar1 {
         return hello({b: g, a: f});
         return hello({b: g, a: f});
     }
     }
 
 
-    function test4(int c, int d) public returns (int) {
+    function test4(int c, int d) external returns (int) {
         // This is allowed
         // This is allowed
         return this.this_is_external(c, d) + this.hello(d, c);
         return this.this_is_external(c, d) + this.hello(d, c);
     }
     }
@@ -42,6 +42,7 @@ contract bar2 is bar1 {
 }
 }
 
 
 // ---- Expect: diagnostics ----
 // ---- Expect: diagnostics ----
+// error: 4:12-55: accounts are required for calling a contract. You can either provide the accounts with the {accounts: ...} call argument or change this function's visibility to external
 // error: 25:16-27: functions declared external cannot be called via an internal function call
 // error: 25:16-27: functions declared external cannot be called via an internal function call
 // 	note 19:5-61: declaration of function 'hello'
 // 	note 19:5-61: declaration of function 'hello'
 // error: 30:16-35: functions declared external cannot be called via an internal function call
 // error: 30:16-35: functions declared external cannot be called via an internal function call

+ 2 - 2
tests/contract_testcases/solana/garbage_function_args.sol

@@ -9,10 +9,10 @@ contract c {
 	function g() public {
 	function g() public {
 		g({x: foo>1});
 		g({x: foo>1});
 	}
 	}
-	function g(int) public {
+	function g(int) external {
 		this.g(oo);
 		this.g(oo);
 	}
 	}
-	function g(bool) public {
+	function g(bool) external {
 		this.g({x: foo>1});
 		this.g({x: foo>1});
 	}
 	}
 }
 }

+ 1 - 0
tests/contract_testcases/solana/type_decl_import.sol

@@ -10,4 +10,5 @@ contract d {
 }
 }
 
 
 // ---- Expect: diagnostics ----
 // ---- Expect: diagnostics ----
+// error: 8:3-8: accounts are required for calling a contract. You can either provide the accounts with the {accounts: ...} call argument or change this function's visibility to external
 // warning: 7:2-33: function can be declared 'pure'
 // warning: 7:2-33: function can be declared 'pure'

+ 5 - 7
tests/solana_tests/abi_encode.rs

@@ -1003,8 +1003,10 @@ contract caller {
         return b + 3;
         return b + 3;
     }
     }
 
 
-    function do_call(address pid) view public returns (int64, int32) {
-        return (this.doThis{program_id: pid}(5), this.doThat{program_id: pid}(3));
+    @account(pid)
+    function do_call() view external returns (int64, int32) {
+        return (this.doThis{program_id: tx.accounts.pid.key, accounts: []}(5),
+         this.doThat{program_id: tx.accounts.pid.key, accounts: []}(3));
     }
     }
 }"#,
 }"#,
     );
     );
@@ -1017,11 +1019,7 @@ contract caller {
     let caller_program_id = vm.stack[0].id;
     let caller_program_id = vm.stack[0].id;
     let returns = vm
     let returns = vm
         .function("do_call")
         .function("do_call")
-        .arguments(&[BorshToken::Address(caller_program_id)])
-        .accounts(vec![
-            ("systemProgram", [0; 32]),
-            ("caller_programId", caller_program_id),
-        ])
+        .accounts(vec![("systemProgram", [0; 32]), ("pid", caller_program_id)])
         .call()
         .call()
         .unwrap()
         .unwrap()
         .unwrap_tuple();
         .unwrap_tuple();

+ 5 - 3
tests/solana_tests/accessor.rs

@@ -262,10 +262,13 @@ fn struct_accessor() {
                 m[1023413412] = S({f1: 414243, f2: true, f3: E("niff")});
                 m[1023413412] = S({f1: 414243, f2: true, f3: E("niff")});
             }
             }
 
 
-            function f(address pid) public view {
+            @account(pid)
+            function f() external view {
                 AccountMeta[1] meta = [
                 AccountMeta[1] meta = [
                     AccountMeta({pubkey: tx.accounts.dataAccount.key, is_writable: false, is_signer: false})
                     AccountMeta({pubkey: tx.accounts.dataAccount.key, is_writable: false, is_signer: false})
                 ];
                 ];
+
+                address pid = tx.accounts.pid.key;
                 (int64 a1, bool b, E memory c) = this.a{accounts: meta, program_id: pid}();
                 (int64 a1, bool b, E memory c) = this.a{accounts: meta, program_id: pid}();
                 require(a1 == -63 && !b && c.b4 == "nuff", "a");
                 require(a1 == -63 && !b && c.b4 == "nuff", "a");
                 (a1, b, c) = this.s{accounts: meta, program_id: pid}(99);
                 (a1, b, c) = this.s{accounts: meta, program_id: pid}(99);
@@ -285,11 +288,10 @@ fn struct_accessor() {
 
 
     let program_id = vm.stack[0].id;
     let program_id = vm.stack[0].id;
     vm.function("f")
     vm.function("f")
-        .arguments(&[BorshToken::Address(program_id)])
         .accounts(vec![
         .accounts(vec![
             ("dataAccount", data_account),
             ("dataAccount", data_account),
             ("systemProgram", [0; 32]),
             ("systemProgram", [0; 32]),
-            ("C_programId", program_id),
+            ("pid", program_id),
         ])
         ])
         .call();
         .call();
 }
 }

+ 77 - 0
tests/solana_tests/account_access.rs

@@ -121,3 +121,80 @@ contract hatchling {
 
 
     assert_eq!(res.unwrap(), 2);
     assert_eq!(res.unwrap(), 2);
 }
 }
+
+#[test]
+fn accounts_on_constructors() {
+    let mut vm = build_solidity(
+        r#"
+        contract Test {
+    @payer(my_payer)
+    @account(acc1)
+    @mutableAccount(acc2)
+    @signer(acc3)
+    @mutableSigner(acc4)
+    constructor () {
+        assert(tx.accounts.acc3.is_signer);
+        assert(tx.accounts.acc4.is_signer);
+
+        assert(tx.accounts.acc1.lamports == 5);
+
+        tx.accounts.acc2.lamports -= 7;
+        tx.accounts.acc4.lamports += 7;
+    }
+}
+        "#,
+    );
+
+    let data_account = vm.initialize_data_account();
+    let acc1 = account_new();
+    let acc2 = account_new();
+    let acc3 = account_new();
+    let acc4 = account_new();
+    let my_payer = account_new();
+
+    vm.account_data.insert(
+        acc1,
+        AccountState {
+            data: vec![],
+            owner: None,
+            lamports: 5,
+        },
+    );
+
+    vm.account_data.insert(
+        acc2,
+        AccountState {
+            data: vec![],
+            owner: None,
+            lamports: 8,
+        },
+    );
+
+    vm.account_data.insert(acc3, AccountState::default());
+
+    vm.account_data.insert(
+        acc4,
+        AccountState {
+            data: vec![],
+            owner: None,
+            lamports: 7,
+        },
+    );
+
+    vm.account_data.insert(my_payer, AccountState::default());
+
+    vm.function("new")
+        .accounts(vec![
+            ("dataAccount", data_account),
+            ("acc1", acc1),
+            ("acc2", acc2),
+            ("acc3", acc3),
+            ("acc4", acc4),
+            ("my_payer", my_payer),
+            ("systemProgram", [0; 32]),
+        ])
+        .call();
+
+    assert_eq!(vm.account_data[&acc2].lamports, 1);
+    assert_eq!(vm.account_data[&acc4].lamports, 14);
+}

+ 22 - 47
tests/solana_tests/account_info.rs

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-License-Identifier: Apache-2.0
 
 
-use crate::{account_new, build_solidity, AccountMeta, AccountState, BorshToken, Pubkey};
+use crate::{account_new, build_solidity, AccountState, BorshToken};
 use num_bigint::BigInt;
 use num_bigint::BigInt;
 
 
 #[test]
 #[test]
@@ -9,20 +9,16 @@ fn lamports() {
         r#"
         r#"
         import 'solana';
         import 'solana';
         contract c {
         contract c {
-            function test(address needle) public payable returns (uint64) {
-                for (uint32 i = 0; i < tx.accounts.length; i++) {
-                    AccountInfo ai = tx.accounts[i];
 
 
-                    assert(ai.is_writable);
-                    assert(!ai.is_signer);
-                    assert(ai.executable);
+            @mutableAccount(needle)
+            function test() external payable returns (uint64) {
+                AccountInfo ai = tx.accounts.needle;
 
 
-                    if (ai.key == needle) {
-                        return ai.lamports;
-                    }
-                }
+                assert(ai.is_writable);
+                assert(!ai.is_signer);
+                assert(ai.executable);
 
 
-                revert("account not found");
+                return ai.lamports;
             }
             }
         }"#,
         }"#,
     );
     );
@@ -44,12 +40,7 @@ fn lamports() {
 
 
     let returns = vm
     let returns = vm
         .function("test")
         .function("test")
-        .arguments(&[BorshToken::Address(acc)])
-        .remaining_accounts(&[AccountMeta {
-            pubkey: Pubkey(acc),
-            is_writable: true,
-            is_signer: false,
-        }])
+        .accounts(vec![("needle", acc)])
         .call()
         .call()
         .unwrap();
         .unwrap();
 
 
@@ -168,10 +159,14 @@ fn modify_lamports() {
 import 'solana';
 import 'solana';
 
 
 contract starter {
 contract starter {
-    function createNewAccount(uint64 lamport1, uint64 lamport2, uint64 lamport3) public {
-        AccountInfo acc1 = tx.accounts[0];
-        AccountInfo acc2 = tx.accounts[1];
-        AccountInfo acc3 = tx.accounts[2];
+
+    @mutableAccount(acc1)
+    @mutableAccount(acc2)
+    @mutableAccount(acc3)
+    function createNewAccount(uint64 lamport1, uint64 lamport2, uint64 lamport3) external {
+        AccountInfo acc1 = tx.accounts.acc1;
+        AccountInfo acc2 = tx.accounts.acc2;
+        AccountInfo acc3 = tx.accounts.acc3;
 
 
         acc1.lamports -= lamport1;
         acc1.lamports -= lamport1;
         acc2.lamports = lamport2;
         acc2.lamports = lamport2;
@@ -214,24 +209,6 @@ contract starter {
         },
         },
     );
     );
 
 
-    let metas = vec![
-        AccountMeta {
-            pubkey: Pubkey(acc1),
-            is_writable: true,
-            is_signer: false,
-        },
-        AccountMeta {
-            pubkey: Pubkey(acc2),
-            is_writable: true,
-            is_signer: false,
-        },
-        AccountMeta {
-            pubkey: Pubkey(acc3),
-            is_writable: true,
-            is_signer: false,
-        },
-    ];
-
     let _ = vm
     let _ = vm
         .function("createNewAccount")
         .function("createNewAccount")
         .arguments(&[
         .arguments(&[
@@ -248,7 +225,7 @@ contract starter {
                 value: BigInt::from(9u8),
                 value: BigInt::from(9u8),
             },
             },
         ])
         ])
-        .remaining_accounts(&metas)
+        .accounts(vec![("acc1", acc1), ("acc2", acc2), ("acc3", acc3)])
         .call();
         .call();
 
 
     assert_eq!(vm.account_data.get(&acc1).unwrap().lamports, 5);
     assert_eq!(vm.account_data.get(&acc1).unwrap().lamports, 5);
@@ -263,8 +240,10 @@ fn account_data() {
 import 'solana';
 import 'solana';
 
 
 contract C {
 contract C {
+
+    @mutableAccount(acc)
 	function test() external {
 	function test() external {
-		AccountInfo ai = tx.accounts[0];
+		AccountInfo ai = tx.accounts.acc;
 		ai.data[0] = 0xca;
 		ai.data[0] = 0xca;
 		ai.data[1] = 0xff;
 		ai.data[1] = 0xff;
 		ai.data[2] = 0xee;
 		ai.data[2] = 0xee;
@@ -290,11 +269,7 @@ contract C {
     );
     );
 
 
     vm.function("test")
     vm.function("test")
-        .remaining_accounts(&[AccountMeta {
-            pubkey: Pubkey(other_account),
-            is_writable: true,
-            is_signer: false,
-        }])
+        .accounts(vec![("acc", other_account)])
         .call();
         .call();
 
 
     assert_eq!(vm.account_data[&other_account].data[0], 0xca);
     assert_eq!(vm.account_data[&other_account].data[0], 0xca);

+ 32 - 110
tests/solana_tests/call.rs

@@ -1,8 +1,8 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-License-Identifier: Apache-2.0
 
 
 use crate::{
 use crate::{
-    build_solidity, create_program_address, AccountMeta, AccountState, BorshToken, Instruction,
-    Pubkey, VirtualMachine,
+    build_solidity, create_program_address, AccountState, BorshToken, Instruction, Pubkey,
+    VirtualMachine,
 };
 };
 use base58::FromBase58;
 use base58::FromBase58;
 use num_bigint::BigInt;
 use num_bigint::BigInt;
@@ -17,8 +17,9 @@ fn simple_external_call() {
                 print("bar0 says: " + v);
                 print("bar0 says: " + v);
             }
             }
 
 
-            function test_other(address x) public {
-                bar1.test_bar{program_id: x}("cross contract call");
+            @account(pid)
+            function test_other() external {
+                bar1.test_bar{program_id: tx.accounts.pid.key}("cross contract call");
             }
             }
         }
         }
 
 
@@ -59,23 +60,7 @@ fn simple_external_call() {
     vm.logs.truncate(0);
     vm.logs.truncate(0);
 
 
     vm.function("test_other")
     vm.function("test_other")
-        .accounts(vec![
-            ("bar1_programId", bar1_program_id),
-            ("systemProgram", [0; 32]),
-        ])
-        .remaining_accounts(&[
-            AccountMeta {
-                pubkey: Pubkey(bar1_account),
-                is_writable: false,
-                is_signer: false,
-            },
-            AccountMeta {
-                pubkey: Pubkey(bar1_program_id),
-                is_signer: false,
-                is_writable: false,
-            },
-        ])
-        .arguments(&[BorshToken::Address(bar1_program_id)])
+        .accounts(vec![("pid", bar1_program_id), ("systemProgram", [0; 32])])
         .call();
         .call();
 
 
     assert_eq!(vm.logs, "bar1 says: cross contract call");
     assert_eq!(vm.logs, "bar1 says: cross contract call");
@@ -86,8 +71,9 @@ fn external_call_with_returns() {
     let mut vm = build_solidity(
     let mut vm = build_solidity(
         r#"
         r#"
         contract bar0 {
         contract bar0 {
-            function test_other(address x) public returns (int64) {
-                return bar1.test_bar{program_id: x}(7) + 5;
+            @account(pid)
+            function test_other() external returns (int64) {
+                return bar1.test_bar{program_id: tx.accounts.pid.key}(7) + 5;
             }
             }
         }
         }
 
 
@@ -130,23 +116,7 @@ fn external_call_with_returns() {
 
 
     let res = vm
     let res = vm
         .function("test_other")
         .function("test_other")
-        .arguments(&[BorshToken::Address(bar1_program_id)])
-        .accounts(vec![
-            ("bar1_programId", bar1_program_id),
-            ("systemProgram", [0; 32]),
-        ])
-        .remaining_accounts(&[
-            AccountMeta {
-                pubkey: Pubkey(bar1_account),
-                is_writable: false,
-                is_signer: false,
-            },
-            AccountMeta {
-                pubkey: Pubkey(bar1_program_id),
-                is_signer: false,
-                is_writable: false,
-            },
-        ])
+        .accounts(vec![("pid", bar1_program_id), ("systemProgram", [0; 32])])
         .call()
         .call()
         .unwrap();
         .unwrap();
 
 
@@ -166,11 +136,12 @@ fn external_raw_call_with_returns() {
         contract bar0 {
         contract bar0 {
             bytes8 private constant SELECTOR = bytes8(sha256(bytes('global:test_bar')));
             bytes8 private constant SELECTOR = bytes8(sha256(bytes('global:test_bar')));
 
 
-            function test_other(address x) public returns (int64) {
+            @account(bar1_pid)
+            function test_other() external returns (int64) {
                 bytes select = abi.encodeWithSelector(SELECTOR, int64(7));
                 bytes select = abi.encodeWithSelector(SELECTOR, int64(7));
                 bytes signature = abi.encodeWithSignature("global:test_bar", int64(7));
                 bytes signature = abi.encodeWithSignature("global:test_bar", int64(7));
                 require(select == signature, "must be the same");
                 require(select == signature, "must be the same");
-                (, bytes raw) = address(x).call(signature);
+                (, bytes raw) = tx.accounts.bar1_pid.key.call{accounts: []}(signature);
                 (int64 v) = abi.decode(raw, (int64));
                 (int64 v) = abi.decode(raw, (int64));
                 return v + 5;
                 return v + 5;
             }
             }
@@ -215,19 +186,9 @@ fn external_raw_call_with_returns() {
 
 
     let res = vm
     let res = vm
         .function("test_other")
         .function("test_other")
-        .arguments(&[BorshToken::Address(bar1_program_id)])
-        .accounts(vec![("systemProgram", [0; 32])])
-        .remaining_accounts(&[
-            AccountMeta {
-                pubkey: Pubkey(bar1_account),
-                is_writable: false,
-                is_signer: false,
-            },
-            AccountMeta {
-                pubkey: Pubkey(bar1_program_id),
-                is_signer: false,
-                is_writable: false,
-            },
+        .accounts(vec![
+            ("bar1_pid", bar1_program_id),
+            ("systemProgram", [0; 32]),
         ])
         ])
         .call()
         .call()
         .unwrap();
         .unwrap();
@@ -254,7 +215,7 @@ fn call_external_func_type() {
     function doTest() public view returns (int, int) {
     function doTest() public view returns (int, int) {
     function(int) external pure returns (int, int) sfPtr = this.testPtr;
     function(int) external pure returns (int, int) sfPtr = this.testPtr;
 
 
-       (int a, int b) = sfPtr(2);
+       (int a, int b) = sfPtr{accounts: []}(2);
        return (a, b);
        return (a, b);
     }
     }
 }
 }
@@ -293,15 +254,17 @@ fn external_call_with_string_returns() {
     let mut vm = build_solidity(
     let mut vm = build_solidity(
         r#"
         r#"
         contract bar0 {
         contract bar0 {
-            function test_other(address x) public returns (string) {
-                string y = bar1.test_bar{program_id: x}(7);
+            @account(pid)
+            function test_other() external returns (string) {
+                string y = bar1.test_bar{program_id: tx.accounts.pid.key}(7);
                 print(y);
                 print(y);
                 return y;
                 return y;
             }
             }
 
 
-            function test_this(address x) public {
-                address a = bar1.who_am_i{program_id: x}();
-                assert(a == address(x));
+            @account(pid)
+            function test_this() external {
+                address a = bar1.who_am_i{program_id: tx.accounts.pid.key}();
+                assert(a == tx.accounts.pid.key);
             }
             }
         }
         }
 
 
@@ -342,46 +305,14 @@ fn external_call_with_string_returns() {
 
 
     let res = vm
     let res = vm
         .function("test_other")
         .function("test_other")
-        .arguments(&[BorshToken::Address(bar1_program_id)])
-        .accounts(vec![
-            ("bar1_programId", bar1_program_id),
-            ("systemProgram", [0; 32]),
-        ])
-        .remaining_accounts(&[
-            AccountMeta {
-                pubkey: Pubkey(bar1_account),
-                is_writable: false,
-                is_signer: false,
-            },
-            AccountMeta {
-                pubkey: Pubkey(bar1_program_id),
-                is_signer: false,
-                is_writable: false,
-            },
-        ])
+        .accounts(vec![("pid", bar1_program_id), ("systemProgram", [0; 32])])
         .call()
         .call()
         .unwrap();
         .unwrap();
 
 
     assert_eq!(res, BorshToken::String(String::from("foo:7")));
     assert_eq!(res, BorshToken::String(String::from("foo:7")));
 
 
     vm.function("test_this")
     vm.function("test_this")
-        .arguments(&[BorshToken::Address(bar1_program_id)])
-        .accounts(vec![
-            ("bar1_programId", bar1_program_id),
-            ("systemProgram", [0; 32]),
-        ])
-        .remaining_accounts(&[
-            AccountMeta {
-                pubkey: Pubkey(bar1_account),
-                is_writable: false,
-                is_signer: false,
-            },
-            AccountMeta {
-                pubkey: Pubkey(bar1_program_id),
-                is_signer: false,
-                is_writable: false,
-            },
-        ])
+        .accounts(vec![("pid", bar1_program_id), ("systemProgram", [0; 32])])
         .call();
         .call();
 }
 }
 
 
@@ -392,11 +323,12 @@ fn encode_call() {
         contract bar0 {
         contract bar0 {
             bytes8 private constant SELECTOR = bytes8(sha256(bytes('global:test_bar')));
             bytes8 private constant SELECTOR = bytes8(sha256(bytes('global:test_bar')));
 
 
-            function test_other(address x) public returns (int64) {
+            @account(bar1_pid)
+            function test_other() external returns (int64) {
                 bytes select = abi.encodeWithSelector(SELECTOR, int64(7));
                 bytes select = abi.encodeWithSelector(SELECTOR, int64(7));
                 bytes signature = abi.encodeCall(bar1.test_bar, 7);
                 bytes signature = abi.encodeCall(bar1.test_bar, 7);
                 require(select == signature, "must be the same");
                 require(select == signature, "must be the same");
-                (, bytes raw) = address(x).call(signature);
+                (, bytes raw) = tx.accounts.bar1_pid.key.call{accounts: []}(signature);
                 (int64 v) = abi.decode(raw, (int64));
                 (int64 v) = abi.decode(raw, (int64));
                 return v + 5;
                 return v + 5;
             }
             }
@@ -441,19 +373,9 @@ fn encode_call() {
 
 
     let res = vm
     let res = vm
         .function("test_other")
         .function("test_other")
-        .arguments(&[BorshToken::Address(bar1_program_id)])
-        .accounts(vec![("systemProgram", [0; 32])])
-        .remaining_accounts(&[
-            AccountMeta {
-                pubkey: Pubkey(bar1_account),
-                is_writable: false,
-                is_signer: false,
-            },
-            AccountMeta {
-                pubkey: Pubkey(bar1_program_id),
-                is_signer: false,
-                is_writable: false,
-            },
+        .accounts(vec![
+            ("bar1_pid", bar1_program_id),
+            ("systemProgram", [0; 32]),
         ])
         ])
         .call()
         .call()
         .unwrap();
         .unwrap();

+ 25 - 59
tests/solana_tests/create_contract.rs

@@ -1,8 +1,7 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-License-Identifier: Apache-2.0
 
 
 use crate::{
 use crate::{
-    account_new, build_solidity, create_program_address, Account, AccountMeta, AccountState,
-    BorshToken, Pubkey,
+    account_new, build_solidity, create_program_address, Account, AccountState, BorshToken,
 };
 };
 use base58::{FromBase58, ToBase58};
 use base58::{FromBase58, ToBase58};
 use num_bigint::BigInt;
 use num_bigint::BigInt;
@@ -16,7 +15,7 @@ fn simple_create_contract_no_seed() {
                 bar1.new("yo from bar0");
                 bar1.new("yo from bar0");
             }
             }
 
 
-            function call_bar1_at_address(string x) public {
+            function call_bar1_at_address(string x) external {
                 bar1.say_hello(x);
                 bar1.say_hello(x);
             }
             }
         }
         }
@@ -69,11 +68,6 @@ fn simple_create_contract_no_seed() {
             ("payer", payer),
             ("payer", payer),
             ("systemProgram", [0; 32]),
             ("systemProgram", [0; 32]),
         ])
         ])
-        .remaining_accounts(&[AccountMeta {
-            pubkey: Pubkey(acc),
-            is_writable: true,
-            is_signer: true,
-        }])
         .call();
         .call();
 
 
     assert_eq!(vm.logs, "bar1 says: yo from bar0");
     assert_eq!(vm.logs, "bar1 says: yo from bar0");
@@ -102,7 +96,7 @@ fn simple_create_contract() {
                 bar1.new("yo from bar0");
                 bar1.new("yo from bar0");
             }
             }
 
 
-            function call_bar1_at_address(string x) public {
+            function call_bar1_at_address(string x) external {
                 bar1.say_hello(x);
                 bar1.say_hello(x);
             }
             }
         }
         }
@@ -275,7 +269,7 @@ fn missing_contract() {
                 bar1.new("yo from bar0");
                 bar1.new("yo from bar0");
             }
             }
 
 
-            function call_bar1_at_address(string x) public {
+            function call_bar1_at_address(string x) external {
                 bar1.say_hello(x);
                 bar1.say_hello(x);
             }
             }
         }
         }
@@ -326,14 +320,18 @@ fn two_contracts() {
         import 'solana';
         import 'solana';
 
 
         contract bar0 {
         contract bar0 {
-            function test_other(address a, address b, address payer) external {
+
+            @mutableSigner(a)
+            @mutableSigner(b)
+            @mutableSigner(payer)
+            function test_other() external {
                 AccountMeta[2] bar1_metas = [
                 AccountMeta[2] bar1_metas = [
-                    AccountMeta({pubkey: a, is_writable: true, is_signer: true}),
-                    AccountMeta({pubkey: payer, is_writable: true, is_signer: true})
+                    AccountMeta({pubkey: tx.accounts.a.key, is_writable: true, is_signer: true}),
+                    AccountMeta({pubkey: tx.accounts.payer.key, is_writable: true, is_signer: true})
                 ];
                 ];
                 AccountMeta[2] bar2_metas = [
                 AccountMeta[2] bar2_metas = [
-                    AccountMeta({pubkey: b, is_writable: true, is_signer: true}),
-                    AccountMeta({pubkey: payer, is_writable: true, is_signer: true})
+                    AccountMeta({pubkey: tx.accounts.b.key, is_writable: true, is_signer: true}),
+                    AccountMeta({pubkey: tx.accounts.payer.key, is_writable: true, is_signer: true})
                 ];
                 ];
                 bar1.new{accounts: bar1_metas}("yo from bar0");
                 bar1.new{accounts: bar1_metas}("yo from bar0");
                 bar1.new{accounts: bar2_metas}("hi from bar0");
                 bar1.new{accounts: bar2_metas}("hi from bar0");
@@ -370,32 +368,13 @@ fn two_contracts() {
     vm.account_data.insert(payer, AccountState::default());
     vm.account_data.insert(payer, AccountState::default());
 
 
     vm.function("test_other")
     vm.function("test_other")
-        .arguments(&[
-            BorshToken::Address(seed1.0),
-            BorshToken::Address(seed2.0),
-            BorshToken::Address(payer),
-        ])
         .accounts(vec![
         .accounts(vec![
+            ("a", seed1.0),
+            ("b", seed2.0),
+            ("payer", payer),
             ("systemProgram", [0; 32]),
             ("systemProgram", [0; 32]),
             ("bar1_programId", program_id),
             ("bar1_programId", program_id),
         ])
         ])
-        .remaining_accounts(&[
-            AccountMeta {
-                pubkey: Pubkey(seed1.0),
-                is_signer: true,
-                is_writable: true,
-            },
-            AccountMeta {
-                pubkey: Pubkey(seed2.0),
-                is_signer: true,
-                is_writable: true,
-            },
-            AccountMeta {
-                pubkey: Pubkey(payer),
-                is_signer: true,
-                is_writable: true,
-            },
-        ])
         .call();
         .call();
 
 
     assert_eq!(vm.logs, "bar1 says: yo from bar0bar1 says: hi from bar0");
     assert_eq!(vm.logs, "bar1 says: yo from bar0bar1 says: hi from bar0");
@@ -660,11 +639,6 @@ fn create_child() {
             ("payer", payer),
             ("payer", payer),
             ("systemProgram", [0; 32]),
             ("systemProgram", [0; 32]),
         ])
         ])
-        .remaining_accounts(&[AccountMeta {
-            pubkey: Pubkey(seed.0),
-            is_signer: true,
-            is_writable: true,
-        }])
         .call();
         .call();
 
 
     assert_eq!(
     assert_eq!(
@@ -680,16 +654,19 @@ fn create_child_with_meta() {
         import 'solana';
         import 'solana';
 
 
 contract creator {
 contract creator {
-    function create_child_with_meta(address child, address payer) public {
+
+    @mutableSigner(child)
+    @mutableSigner(payer)
+    function create_child_with_meta() external {
         print("Going to create child");
         print("Going to create child");
         AccountMeta[2] metas = [
         AccountMeta[2] metas = [
-            AccountMeta({pubkey: child, is_signer: true, is_writable: true}),
-            AccountMeta({pubkey: payer, is_signer: true, is_writable: true})
+            AccountMeta({pubkey: tx.accounts.child.key, is_signer: true, is_writable: true}),
+            AccountMeta({pubkey: tx.accounts.payer.key, is_signer: true, is_writable: true})
             // Passing the system account here crashes the VM, even if I add it to vm.account_data
             // Passing the system account here crashes the VM, even if I add it to vm.account_data
             // AccountMeta({pubkey: address"11111111111111111111111111111111", is_writable: false, is_signer: false})
             // AccountMeta({pubkey: address"11111111111111111111111111111111", is_writable: false, is_signer: false})
         ];
         ];
         Child.new{accounts: metas}();
         Child.new{accounts: metas}();
-        Child.say_hello();
+        Child.say_hello{accounts: []}();
     }
     }
 }
 }
 
 
@@ -727,23 +704,12 @@ contract Child {
         .unwrap();
         .unwrap();
 
 
     vm.function("create_child_with_meta")
     vm.function("create_child_with_meta")
-        .arguments(&[BorshToken::Address(seed.0), BorshToken::Address(payer)])
         .accounts(vec![
         .accounts(vec![
             ("Child_programId", child_program_id),
             ("Child_programId", child_program_id),
+            ("child", seed.0),
+            ("payer", payer),
             ("systemProgram", [0; 32]),
             ("systemProgram", [0; 32]),
         ])
         ])
-        .remaining_accounts(&[
-            AccountMeta {
-                pubkey: Pubkey(seed.0),
-                is_signer: false,
-                is_writable: false,
-            },
-            AccountMeta {
-                pubkey: Pubkey(payer),
-                is_signer: true,
-                is_writable: false,
-            },
-        ])
         .call();
         .call();
 
 
     assert_eq!(
     assert_eq!(

+ 29 - 39
tests/solana_tests/metas.rs

@@ -1,9 +1,6 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-License-Identifier: Apache-2.0
 
 
-use crate::{
-    account_new, build_solidity, build_solidity_with_cache, AccountMeta, AccountState, BorshToken,
-    Pubkey,
-};
+use crate::{account_new, build_solidity, build_solidity_with_cache, AccountState, BorshToken};
 use borsh::BorshSerialize;
 use borsh::BorshSerialize;
 use num_bigint::BigInt;
 use num_bigint::BigInt;
 use solang::file_resolver::FileResolver;
 use solang::file_resolver::FileResolver;
@@ -13,6 +10,7 @@ fn use_authority() {
     let mut vm = build_solidity(include_str!("../../docs/examples/solana/use_authority.sol"));
     let mut vm = build_solidity(include_str!("../../docs/examples/solana/use_authority.sol"));
 
 
     let authority = account_new();
     let authority = account_new();
+    let another_authority = account_new();
 
 
     vm.account_data.insert(
     vm.account_data.insert(
         authority,
         authority,
@@ -23,6 +21,15 @@ fn use_authority() {
         },
         },
     );
     );
 
 
+    vm.account_data.insert(
+        another_authority,
+        AccountState {
+            data: vec![],
+            owner: Some([0; 32]),
+            lamports: 0,
+        },
+    );
+
     let data_account = vm.initialize_data_account();
     let data_account = vm.initialize_data_account();
     vm.function("new")
     vm.function("new")
         .arguments(&[BorshToken::Address(authority)])
         .arguments(&[BorshToken::Address(authority)])
@@ -31,7 +38,10 @@ fn use_authority() {
 
 
     let res = vm
     let res = vm
         .function("inc")
         .function("inc")
-        .accounts(vec![("dataAccount", data_account)])
+        .accounts(vec![
+            ("dataAccount", data_account),
+            ("authorityAccount", another_authority),
+        ])
         .must_fail()
         .must_fail()
         .unwrap();
         .unwrap();
     assert_ne!(res, 0);
     assert_ne!(res, 0);
@@ -50,12 +60,10 @@ fn use_authority() {
     );
     );
 
 
     vm.function("inc")
     vm.function("inc")
-        .accounts(vec![("dataAccount", data_account)])
-        .remaining_accounts(&[AccountMeta {
-            pubkey: Pubkey(authority),
-            is_signer: true,
-            is_writable: false,
-        }])
+        .accounts(vec![
+            ("dataAccount", data_account),
+            ("authorityAccount", authority),
+        ])
         .call();
         .call();
 
 
     let res = vm
     let res = vm
@@ -83,8 +91,9 @@ fn token_account() {
     import './spl_token.sol';
     import './spl_token.sol';
 
 
 contract Foo {
 contract Foo {
-    function token_account(address add) public returns (SplToken.TokenAccountData) {
-        return SplToken.get_token_account_data(add);
+    @account(add)
+    function token_account() external returns (SplToken.TokenAccountData) {
+        return SplToken.get_token_account_data(tx.accounts.add);
     }
     }
 }
 }
     "#;
     "#;
@@ -140,12 +149,7 @@ contract Foo {
 
 
     let res = vm
     let res = vm
         .function("token_account")
         .function("token_account")
-        .arguments(&[BorshToken::Address(account)])
-        .remaining_accounts(&[AccountMeta {
-            pubkey: Pubkey(account),
-            is_signer: false,
-            is_writable: false,
-        }])
+        .accounts(vec![("add", account)])
         .call()
         .call()
         .unwrap()
         .unwrap()
         .unwrap_tuple();
         .unwrap_tuple();
@@ -188,12 +192,7 @@ contract Foo {
 
 
     let res = vm
     let res = vm
         .function("token_account")
         .function("token_account")
-        .arguments(&[BorshToken::Address(account)])
-        .remaining_accounts(&[AccountMeta {
-            pubkey: Pubkey(account),
-            is_signer: false,
-            is_writable: false,
-        }])
+        .accounts(vec![("add", account)])
         .call()
         .call()
         .unwrap()
         .unwrap()
         .unwrap_tuple();
         .unwrap_tuple();
@@ -239,8 +238,9 @@ fn mint_account() {
     import './spl_token.sol';
     import './spl_token.sol';
 
 
 contract Foo {
 contract Foo {
-    function mint_account(address add) public returns (SplToken.MintAccountData) {
-        return SplToken.get_mint_account_data(add);
+    @account(add)
+    function mint_account() external returns (SplToken.MintAccountData) {
+        return SplToken.get_mint_account_data(tx.accounts.add);
     }
     }
 }
 }
     "#;
     "#;
@@ -287,12 +287,7 @@ contract Foo {
 
 
     let res = vm
     let res = vm
         .function("mint_account")
         .function("mint_account")
-        .arguments(&[BorshToken::Address(account)])
-        .remaining_accounts(&[AccountMeta {
-            pubkey: Pubkey(account),
-            is_writable: false,
-            is_signer: false,
-        }])
+        .accounts(vec![("add", account)])
         .call()
         .call()
         .unwrap()
         .unwrap()
         .unwrap_tuple();
         .unwrap_tuple();
@@ -324,12 +319,7 @@ contract Foo {
 
 
     let res = vm
     let res = vm
         .function("mint_account")
         .function("mint_account")
-        .arguments(&[BorshToken::Address(account)])
-        .remaining_accounts(&[AccountMeta {
-            pubkey: Pubkey(account),
-            is_writable: false,
-            is_signer: false,
-        }])
+        .accounts(vec![("add", account)])
         .call()
         .call()
         .unwrap()
         .unwrap()
         .unwrap_tuple();
         .unwrap_tuple();