浏览代码

Represent contracts by their program id (#1474)

Lucas Steuernagel 2 年之前
父节点
当前提交
d5e72894c3
共有 100 个文件被更改,包括 1682 次插入1786 次删除
  1. 1 0
      Cargo.toml
  2. 1 1
      docs/examples/solana/contract_address.sol
  3. 0 23
      docs/examples/solana/contract_new.sol
  4. 4 4
      docs/examples/solana/payer_annotation.sol
  5. 3 3
      docs/examples/solana/program_id.sol
  6. 0 4
      docs/language/builtins.rst
  7. 8 4
      docs/language/contracts.rst
  8. 2 2
      docs/language/expressions.rst
  9. 2 1
      docs/language/managing_values.rst
  10. 5 3
      docs/targets/solana.rst
  11. 1 1
      integration/anchor/tests/anchor.ts
  12. 1 1
      integration/polkadot/call_flags.sol
  13. 1 1
      integration/polkadot/issue666.sol
  14. 0 2
      integration/solana/balances.spec.ts
  15. 0 2
      integration/solana/builtins.spec.ts
  16. 21 30
      integration/solana/calls.spec.ts
  17. 10 11
      integration/solana/create_contract.sol
  18. 19 28
      integration/solana/create_contract.spec.ts
  19. 0 1
      integration/solana/events.spec.ts
  20. 9 9
      integration/solana/runtime_errors.spec.ts
  21. 42 42
      integration/solana/simple.spec.ts
  22. 0 1
      integration/solana/simple_collectible.spec.ts
  23. 0 13
      integration/solana/system_instruction.spec.ts
  24. 0 9
      integration/solana/token.spec.ts
  25. 0 2
      integration/solana/verify_sig.spec.ts
  26. 149 503
      src/abi/tests.rs
  27. 0 3
      src/bin/languageserver/mod.rs
  28. 7 1
      src/codegen/cfg.rs
  29. 2 0
      src/codegen/constant_folding.rs
  30. 14 4
      src/codegen/constructor.rs
  31. 11 4
      src/codegen/dispatch/solana.rs
  32. 14 1
      src/codegen/expression.rs
  33. 1 2
      src/codegen/mod.rs
  34. 115 86
      src/codegen/solana_accounts/account_collection.rs
  35. 101 78
      src/codegen/solana_accounts/account_management.rs
  36. 38 19
      src/codegen/solana_deploy.rs
  37. 1 0
      src/codegen/statements/try_catch.rs
  38. 2 0
      src/codegen/subexpression_elimination/instruction.rs
  39. 3 5
      src/emit/solana/mod.rs
  40. 19 61
      src/emit/solana/target.rs
  41. 19 4
      src/sema/ast.rs
  42. 1 12
      src/sema/builtin.rs
  43. 3 6
      src/sema/contracts.rs
  44. 0 3
      src/sema/dotgraphviz.rs
  45. 48 10
      src/sema/expression/constructor.rs
  46. 17 73
      src/sema/expression/function_call.rs
  47. 4 0
      src/sema/expression/member_access.rs
  48. 20 0
      src/sema/expression/mod.rs
  49. 6 0
      src/sema/function_annotation.rs
  50. 102 9
      src/sema/mutability.rs
  51. 2 0
      src/sema/namespace.rs
  52. 9 4
      src/sema/solana_accounts.rs
  53. 15 7
      src/sema/statements.rs
  54. 224 0
      src/sema/tests/data_account.rs
  55. 6 3
      src/sema/tests/mod.rs
  56. 0 3
      src/sema/unused_variable.rs
  57. 3 5
      src/sema/variables.rs
  58. 12 81
      src/sema/yul/tests/expression.rs
  59. 7 53
      stdlib/solana.c
  60. 3 3
      tests/codegen_testcases/solidity/constructor_with_metas.sol
  61. 3 3
      tests/codegen_testcases/solidity/solana_bump.sol
  62. 9 8
      tests/codegen_testcases/solidity/solana_payer_account.sol
  63. 1 1
      tests/codegen_testcases/solidity/unused_variable_elimination.sol
  64. 1 1
      tests/contract_testcases/solana/account_meta.sol
  65. 123 0
      tests/contract_testcases/solana/accounts/constructor_in_loop.sol
  66. 26 0
      tests/contract_testcases/solana/accounts/data_account.sol
  67. 8 0
      tests/contract_testcases/solana/accounts/data_account_visibility.sol
  68. 51 0
      tests/contract_testcases/solana/accounts/double_calls.sol
  69. 1 1
      tests/contract_testcases/solana/address_cast.sol
  70. 2 2
      tests/contract_testcases/solana/annotations/account_name_collision.sol
  71. 9 4
      tests/contract_testcases/solana/annotations/constructor_external_function.sol
  72. 1 1
      tests/contract_testcases/solana/call/call_args_three_ways.sol
  73. 1 1
      tests/contract_testcases/solana/expressions/contract_no_init.sol
  74. 2 2
      tests/contract_testcases/solana/mapping_deletion.sol
  75. 2 1
      tests/contract_testcases/solana/yul/yul_switch.sol
  76. 21 0
      tests/imports_testcases/Dummy.json
  77. 1 1
      tests/optimization_testcases/programs/f46807c38c2116c0f3192e9a8d2bede4f34846d2.sol
  78. 1 1
      tests/optimization_testcases/programs/f646b924357689419072fddf402c3ad3c10cd095.sol
  79. 1 1
      tests/polkadot_tests/calls.rs
  80. 1 1
      tests/polkadot_tests/contracts.rs
  81. 1 1
      tests/polkadot_tests/yul.rs
  82. 7 1
      tests/solana.rs
  83. 5 15
      tests/solana_tests/abi.rs
  84. 2 13
      tests/solana_tests/abi_decode.rs
  85. 8 24
      tests/solana_tests/abi_encode.rs
  86. 11 19
      tests/solana_tests/accessor.rs
  87. 8 34
      tests/solana_tests/account_info.rs
  88. 3 5
      tests/solana_tests/account_serialization.rs
  89. 17 82
      tests/solana_tests/arrays.rs
  90. 144 72
      tests/solana_tests/balance.rs
  91. 9 38
      tests/solana_tests/builtin.rs
  92. 18 40
      tests/solana_tests/call.rs
  93. 2 10
      tests/solana_tests/constant.rs
  94. 60 87
      tests/solana_tests/create_contract.rs
  95. 1 9
      tests/solana_tests/destructure.rs
  96. 2 6
      tests/solana_tests/events.rs
  97. 5 26
      tests/solana_tests/expressions.rs
  98. 6 24
      tests/solana_tests/hash.rs
  99. 0 6
      tests/solana_tests/math.rs
  100. 0 4
      tests/solana_tests/metas.rs

+ 1 - 0
Cargo.toml

@@ -65,6 +65,7 @@ wasm-opt = { version = "0.112.0", optional = true }
 contract-build = { version = "3.0.1", optional = true }
 primitive-types = { version = "0.12", features = ["codec"] }
 normalize-path = "0.2.1"
+bitflags = "2.3.3"
 
 [dev-dependencies]
 num-derive = "0.4"

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

@@ -10,6 +10,6 @@ contract hatchling {
 
 contract adult {
     function test(address addr) external {
-        hatchling h = new hatchling{address: addr}("luna");
+        hatchling h = new hatchling("luna");
     }
 }

+ 0 - 23
docs/examples/solana/contract_new.sol

@@ -1,23 +0,0 @@
-@program_id("8scvhNyoxUUo7e3hRnUzcTtFtcZ3LdXHuy8b42Hd5d2T")
-contract hatchling {
-    string name;
-    address private origin;
-
-    constructor(string id, address parent) {
-        require(id != "", "name must be provided");
-        name = id;
-        origin = parent;
-    }
-
-    function root() public returns (address) {
-        return origin;
-    }
-}
-
-contract creator {
-    function create_hatchling(address new_address) external {
-        hatchling h;
-       
-	h = new hatchling{address: new_address}("luna", address(this));
-    }
-}

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

@@ -3,10 +3,10 @@ import 'solana';
 @program_id("SoLDxXQ9GMoa15i4NavZc61XGkas2aom4aNiWT6KUER")
 contract Builder {
     BeingBuilt other;
-    function build_this(address addr) external {
-        // When calling a constructor from an external function, the only call argument needed
-        // is the data account. The compiler automatically passes the necessary accounts to the call.
-        other = new BeingBuilt{address: addr}("my_seed");
+    function build_this() external {
+        // When calling a constructor from an external function, the data account for the contract
+        // 'BeingBuilt' should be passed as the 'BeingBuilt_dataAccount' in the client code.
+        other = new BeingBuilt("my_seed");
     }
 
     function build_that(address data_account, address payer_account) public {

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

@@ -8,11 +8,11 @@ contract Foo {
 contract Bar {
     Foo public foo;
 
-    function create_foo(address new_address) external {
-        foo = new Foo{address: new_address}();
+    function create_foo() external {
+        foo = new Foo();
     }
 
-    function call_foo() public pure {
+    function call_foo() public {
         foo.say_hello();
     }
 }

+ 0 - 4
docs/language/builtins.rst

@@ -108,10 +108,6 @@ AccountInfo[] ``tx.accounts``
 .. include:: ../examples/solana/accountinfo.sol
   :code: solidity
 
-address ``tx.program_id``
-    The address or account of the currently executing program. Only available on
-    Solana.
-
 ``block`` properties
 ++++++++++++++++++++++
 

+ 8 - 4
docs/language/contracts.rst

@@ -94,15 +94,19 @@ __________________________________
 
 On Solana, the contract being created must have the ``@program_id()`` annotation that specifies the program account to
 which the contract code has been deployed. This account holds only the contract's executable binary.
-When calling a constructor, one needs to provide an address that will serve as the contract's data account,
-by using the call argument ``address``:
+When calling a constructor only once from an external function, no call arguments are needed. The data account
+necessary to initialize the contract should be present in the IDL and is identified as ``contractName_dataAccount``.
+In the example below, the IDL for the instruction ``test`` requires the ``hatchling_dataAccount`` account to be
+initialized as the new contract's data account.
 
 .. include:: ../examples/solana/contract_address.sol
   :code: solidity
 
-When the contract's data account is passed through the ``address`` call argument, the compiler will automatically create
+When there are no call arguments to a constructor call, the compiler will automatically create
 the ``AccountMeta`` array the constructor call needs. Due to the impossibility to track account ordering in
-private, internal and public functions, such a call argument is only allowed in external functions.
+private, internal and public functions, such a call argument is only allowed in functions with ``external``
+visibility. This automatic account management only works, however, if there is a single instantiation of
+a particular contract type.
 
 Alternatively, the data account to be initialized can be provided using the ``accounts`` call argument. In this case,
 one needs to instantiate a fixed length array of type ``AccountMeta`` to pass to the call. The array must contain all

+ 2 - 2
docs/language/expressions.rst

@@ -124,8 +124,8 @@ this only works with public functions.
 
 .. note::
 
-    On Solana, this gives the account of contract data. If you want the account with the program code,
-    use ``tx.program_id``.
+    On Solana, ``this`` returns the program account. If you are looking for the data account, please
+    use ``tx.accounts.dataAccount.key``.
 
 type(..) operators
 __________________

+ 2 - 1
docs/language/managing_values.rst

@@ -24,8 +24,9 @@ is ``address(this).balance``.
     function to that contract like the one below, and call that function instead.
 
 .. note::
-    On Solana, checking the balance of an account different than the data account
+    On Solana, checking the balance of an account different than the program account
     requires that it be passed as an AccountMeta during the transaction.
+    It is not common practice for the program account to hold native Solana tokens.
 
 .. code-block:: solidity
 

+ 5 - 3
docs/targets/solana.rst

@@ -45,9 +45,11 @@ Runtime
 
 - The Solana target requires `Solana <https://www.solana.com/>`_ v1.8.1.
 - Function selectors are eight bytes wide and known as *discriminators*.
-- Solana provides different builtins, e.g. ``tx.program_id`` and ``tx.accounts``.
-- When creating a contract in Solidity using ``new``, one :ref:`needs to provide <solana_constructor>` the data account
-  address that is going to be initialized for the new contract.
+- Solana provides different builtins, e.g. ``block.slot`` and ``tx.accounts``.
+- When calling an external function or instantiating a contract using ``new``, one
+  :ref:`needs to provide <solana_constructor>` the necessary accounts for the transaction.
+- The keyword ``this`` returns the contract's program account, also know as program id.
+
 
 Compute budget
 ++++++++++++++

+ 1 - 1
integration/anchor/tests/anchor.ts

@@ -17,7 +17,7 @@ describe("Anchor", () => {
 
     const program = anchor.workspace.Anchor as Program<Anchor>;
 
-    const [myAccount, bump] = await anchor.web3.PublicKey.findProgramAddress([seed], program.programId);
+    const [myAccount, bump] = anchor.web3.PublicKey.findProgramAddressSync([seed], program.programId);
 
 
     const { SystemProgram } = anchor.web3;

+ 1 - 1
integration/polkadot/call_flags.sol

@@ -57,7 +57,7 @@ contract CallFlags {
     }
 
     // Does this.call() on this instead of address.call()
-    function call_this(uint32 _x) public pure returns (uint32) {
+    function call_this(uint32 _x) public view returns (uint32) {
         return this.foo{flags: bitflags([CallFlag.ALLOW_REENTRY])}(_x);
     }
 }

+ 1 - 1
integration/polkadot/issue666.sol

@@ -11,7 +11,7 @@ contract Inc {
 	_flipper = _flipperContract;
     }
 
-    function superFlip () pure public {
+    function superFlip () view public {
 	_flipper.flip();
     }
 }

+ 0 - 2
integration/solana/balances.spec.ts

@@ -12,7 +12,6 @@ describe('Deploy solang contract and test', function () {
         let { program, storage, payer, provider } = await loadContractAndCallConstructor('balances', []);
 
         let res = await program.methods.getBalance(payer.publicKey)
-            .accounts({ dataAccount: storage.publicKey })
             .remainingAccounts([{ pubkey: payer.publicKey, isSigner: false, isWritable: false }])
             .view();
 
@@ -38,7 +37,6 @@ describe('Deploy solang contract and test', function () {
         await sendAndConfirmTransaction(provider.connection, transaction, [payer]);
 
         await program.methods.send(payer.publicKey, new BN(500))
-            .accounts({ dataAccount: storage.publicKey })
             .remainingAccounts([
                 { pubkey: storage.publicKey, isSigner: true, isWritable: true },
                 { pubkey: payer.publicKey, isSigner: false, isWritable: true }

+ 0 - 2
integration/solana/builtins.spec.ts

@@ -29,7 +29,6 @@ describe('Testing builtins', function () {
         expect(res['return1']).toEqual([0xfe]);
 
         res = await program.methods.mrNow()
-            .accounts({ dataAccount: storage.publicKey })
             .view();
 
         let now = Math.floor(+new Date() / 1000);
@@ -40,7 +39,6 @@ describe('Testing builtins', function () {
         expect(ts).toBeGreaterThan(now - 120);
 
         res = await program.methods.mrSlot()
-            .accounts({ dataAccount: storage.publicKey })
             .view();
 
         let sol_slot = Number(res);

+ 21 - 30
integration/solana/calls.spec.ts

@@ -26,22 +26,16 @@ describe('Testing calls', function () {
 
         expect(res).toEqual(new BN(102));
 
-        let address_caller = caller.storage.publicKey;
-        let address_callee = callee.storage.publicKey;
-        let address_callee2 = callee2.storage.publicKey;
-
         res = await caller.program.methods.whoAmI()
-            .accounts({ dataAccount: caller.storage.publicKey })
             .view();
 
-        expect(res).toStrictEqual(address_caller);
+        expect(res).toStrictEqual(caller.program_key);
 
-        await caller.program.methods.doCall(address_callee, new BN(13123))
-            .accounts({ dataAccount: caller.storage.publicKey })
-            .remainingAccounts([
-                { pubkey: callee.storage.publicKey, isSigner: false, isWritable: true },
-                { pubkey: callee.program_key, isSigner: false, isWritable: false },
-            ])
+        await caller.program.methods.doCall(callee.program_key, new BN(13123))
+            .accounts({
+                callee_dataAccount: callee.storage.publicKey,
+                callee_programId: callee.program_key,
+                })
             .rpc();
 
         res = await callee.program.methods.getX()
@@ -50,33 +44,30 @@ describe('Testing calls', function () {
 
         expect(res).toEqual(new BN(13123));
 
-        res = await caller.program.methods.doCall2(address_callee, new BN(20000))
-            .accounts({ dataAccount: caller.storage.publicKey })
-            .remainingAccounts([
-                { pubkey: callee.storage.publicKey, isSigner: false, isWritable: true },
-                { pubkey: callee.program_key, isSigner: false, isWritable: false },
-                { pubkey: caller.program_key, isSigner: false, isWritable: false },
-            ])
+        res = await caller.program.methods.doCall2(callee.program_key, new BN(20000))
+            .accounts({
+                callee_dataAccount: callee.storage.publicKey,
+                callee_programId: callee.program_key,
+            })
             .view();
 
         expect(res).toEqual(new BN(33123));
 
-        let all_keys = [
-            { pubkey: callee.storage.publicKey, isSigner: false, isWritable: true },
-            { pubkey: callee.program_key, isSigner: false, isWritable: false },
-            { pubkey: callee2.storage.publicKey, isSigner: false, isWritable: true },
-            { pubkey: callee2.program_key, isSigner: false, isWritable: false },
-        ];
-
-        res = await caller.program.methods.doCall3(address_callee, address_callee2, [new BN(3), new BN(5), new BN(7), new BN(9)], "yo")
-            .remainingAccounts(all_keys)
+        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")
+            .accounts({
+                callee2_programId: callee2.program_key,
+                callee_programId: callee.program_key,
+            })
             .view();
 
         expect(res.return0).toEqual(new BN(24));
         expect(res.return1).toBe("my name is callee");
 
-        res = await caller.program.methods.doCall4(address_callee, address_callee2, [new BN(1), new BN(2), new BN(3), new BN(4)], "asda")
-            .remainingAccounts(all_keys)
+        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")
+            .accounts({
+                callee2_programId: callee2.program_key,
+                callee_programId: callee.program_key,
+            })
             .view();
 
         expect(res.return0).toEqual(new BN(10));

+ 10 - 11
integration/solana/create_contract.sol

@@ -4,25 +4,24 @@ contract creator {
     Child public c;
     Child public c_metas;
 
-    function create_child(address child) external {
+    function create_child() external {
         print("Going to create child");
-        c = new Child{address: child}();
+        c = new Child();
 
         c.say_hello();
     }
 
-    function create_seed1(address child, bytes seed, bytes1 bump, uint64 space) external {
+    function create_seed1(bytes seed, bytes1 bump, uint64 space) external {
         print("Going to create Seed1");
-        Seed1 s = new Seed1{address: child}(seed, bump, space);
+        Seed1 s = new Seed1(seed, bump, space);
 
         s.say_hello();
     }
 
-    function create_seed2(address child, bytes seed, uint32 space) external {
+    function create_seed2(bytes seed, uint32 space) external {
         print("Going to create Seed2");
 
-        new Seed2{address: child}(seed, space);
-
+        new Seed2(seed, space);
     }
 
     function create_child_with_metas(address child, address payer) public {
@@ -37,8 +36,8 @@ contract creator {
         c_metas.use_metas();
     }
 
-    function create_without_annotation(address child) external {
-        MyCreature cc = new MyCreature{address: child}();
+    function create_without_annotation() external {
+        MyCreature cc = new MyCreature();
         cc.say_my_name();
     }
 }
@@ -88,9 +87,9 @@ contract Seed2 {
     }
 
     function check() public view {
-        address pda = create_program_address([ "sunflower", my_seed ], tx.program_id);
+        address pda = create_program_address([ "sunflower", my_seed ], address(this));
 
-        if (pda == address(this)) {
+        if (pda == tx.accounts.dataAccount.key) {
             print("I am PDA.");
         }
     }

+ 19 - 28
integration/solana/create_contract.spec.ts

@@ -22,15 +22,13 @@ describe('ChildContract', function () {
         let child_program = new PublicKey("Chi1d5XD6nTAp2EyaNGqMxZzUjh6NvhXRxbGHP3D1RaT");
         let child = Keypair.generate();
 
-        const signature = await program.methods.createChild(child.publicKey)
+        const signature = await program.methods.createChild()
             .accounts({
                 dataAccount: storage.publicKey,
+                Child_programId: child_program,
                 payer: payer.publicKey,
+                Child_dataAccount: child.publicKey,
             })
-            .remainingAccounts([
-                { pubkey: child_program, isSigner: false, isWritable: false },
-                { pubkey: child.publicKey, isSigner: true, isWritable: true },
-            ])
             .signers([payer, child])
             .rpc({ commitment: 'confirmed' });
 
@@ -51,15 +49,12 @@ describe('ChildContract', function () {
         let [address, bump] = await PublicKey.findProgramAddress([seed], seed_program);
 
         const signature = await program.methods.createSeed1(
-            address, seed, Buffer.from([bump]), new BN(711))
+            seed, Buffer.from([bump]), new BN(711))
             .accounts({
-                dataAccount: storage.publicKey,
+                Seed1_programId: seed_program,
                 payer: payer.publicKey,
+                Seed1_dataAccount: address,
             })
-            .remainingAccounts([
-                { pubkey: seed_program, isSigner: false, isWritable: false },
-                { pubkey: address, isSigner: false, isWritable: true },
-            ])
             .signers([payer])
             .rpc({ commitment: 'confirmed' });
 
@@ -84,15 +79,12 @@ describe('ChildContract', function () {
         let seed = Buffer.concat([bare_seed, Buffer.from([bump])]);
 
         const signature = await program.methods.createSeed2(
-            address, seed, new BN(9889))
+            seed, new BN(9889))
             .accounts({
-                dataAccount: storage.publicKey,
-                payer: payer.publicKey
+                Seed2_programId: seed_program,
+                payer: payer.publicKey,
+                Seed2_dataAccount: address,
             })
-            .remainingAccounts([
-                { pubkey: seed_program, isSigner: false, isWritable: false },
-                { pubkey: address, isSigner: false, isWritable: true },
-            ])
             .signers([payer])
             .rpc({ commitment: 'confirmed' });
 
@@ -122,9 +114,11 @@ describe('ChildContract', function () {
         let child_program = new PublicKey("Chi1d5XD6nTAp2EyaNGqMxZzUjh6NvhXRxbGHP3D1RaT");
 
         const signature = await program.methods.createChildWithMetas(child.publicKey, payer.publicKey)
-            .accounts({ dataAccount: storage.publicKey })
+            .accounts({
+                dataAccount: storage.publicKey,
+                Child_programId: child_program,
+            })
             .remainingAccounts([
-                { pubkey: child_program, isSigner: false, isWritable: false },
                 { pubkey: child.publicKey, isSigner: true, isWritable: true },
                 { pubkey: payer.publicKey, isSigner: true, isWritable: true },
             ])
@@ -146,14 +140,11 @@ describe('ChildContract', function () {
         const child_program = new PublicKey("8gTkAidfM82u3DGbKcZpHwL5p47KQA16MDb4WmrHdmF6");
 
         await create_account(child, child_program, 8192);
-        const signature = await program.methods.createWithoutAnnotation(child.publicKey)
-            .accounts({ dataAccount: storage.publicKey })
-            .remainingAccounts(
-                [
-                    { pubkey: child_program, isSigner: false, isWritable: false },
-                    { pubkey: child.publicKey, isSigner: true, isWritable: true }
-                ]
-            ).signers([child])
+        const signature = await program.methods.createWithoutAnnotation()
+            .accounts({
+                MyCreature_dataAccount: child.publicKey,
+                MyCreature_programId: child_program,
+            })
             .rpc({ commitment: 'confirmed' });
 
         const tx = await provider.connection.getTransaction(signature, {

+ 0 - 1
integration/solana/events.spec.ts

@@ -10,7 +10,6 @@ describe('Test events', function () {
         const { program, storage } = await loadContractAndCallConstructor('MyContractEvents');
 
         const res = await program.methods.test()
-            .accounts({ dataAccount: storage.publicKey })
             .simulate();
 
         const event1 = res.events[0];

+ 9 - 9
integration/solana/runtime_errors.spec.ts

@@ -48,7 +48,7 @@ describe('Runtime Errors', function () {
         }
 
         try {
-            let res = await program.methods.invalidInstruction().accounts({ dataAccount: storage.publicKey }).simulate();
+            let res = await program.methods.invalidInstruction().simulate();
         } catch (e: any) {
             const logs = e.simulationResponse.logs;
             expect(logs).toContain(`Program log: runtime_error: reached invalid instruction in runtime_errors.sol:101:13-22,
@@ -57,7 +57,7 @@ describe('Runtime Errors', function () {
         }
 
         try {
-            let res = await program.methods.byteCastFailure(new BN(33)).accounts({ dataAccount: storage.publicKey }).simulate();
+            let res = await program.methods.byteCastFailure(new BN(33)).simulate();
         } catch (e: any) {
             const logs = e.simulationResponse.logs;
             expect(logs).toContain(`Program log: runtime_error: bytes cast error in runtime_errors.sol:107:23-40,
@@ -66,7 +66,7 @@ describe('Runtime Errors', function () {
         }
 
         try {
-            let res = await program.methods.iWillRevert().accounts({ dataAccount: storage.publicKey }).simulate();
+            let res = await program.methods.iWillRevert().simulate();
         } catch (e: any) {
             const logs = e.simulationResponse.logs;
             expect(logs).toContain(`Program log: runtime_error: revert encountered in runtime_errors.sol:69:9-17,
@@ -74,7 +74,7 @@ describe('Runtime Errors', function () {
         }
 
         try {
-            let res = await program.methods.assertTest(new BN(9)).accounts({ dataAccount: storage.publicKey }).simulate();
+            let res = await program.methods.assertTest(new BN(9)).simulate();
         } catch (e: any) {
             const logs = e.simulationResponse.logs;
             expect(logs).toContain(`Program log: runtime_error: assert failure in runtime_errors.sol:34:16-24,
@@ -82,7 +82,7 @@ describe('Runtime Errors', function () {
         }
 
         try {
-            let res = await program.methods.writeIntegerFailure(new BN(1)).accounts({ dataAccount: storage.publicKey }).simulate();
+            let res = await program.methods.writeIntegerFailure(new BN(1)).simulate();
         } catch (e: any) {
             const logs = e.simulationResponse.logs;
             expect(logs).toContain(`Program log: runtime_error: integer too large to write in buffer in runtime_errors.sol:74:18-31,
@@ -90,7 +90,7 @@ describe('Runtime Errors', function () {
         }
 
         try {
-            let res = await program.methods.writeBytesFailure(new BN(9)).accounts({ dataAccount: storage.publicKey }).simulate();
+            let res = await program.methods.writeBytesFailure(new BN(9)).simulate();
         } catch (e: any) {
             const logs = e.simulationResponse.logs;
             expect(logs).toContain(`Program log: runtime_error: data does not fit into buffer in runtime_errors.sol:80:18-28,
@@ -99,7 +99,7 @@ describe('Runtime Errors', function () {
 
 
         try {
-            let res = await program.methods.readIntegerFailure(new BN(2)).accounts({ dataAccount: storage.publicKey }).simulate();
+            let res = await program.methods.readIntegerFailure(new BN(2)).simulate();
         } catch (e: any) {
             const logs = e.simulationResponse.logs;
             expect(logs).toContain(`Program log: runtime_error: read integer out of bounds in runtime_errors.sol:85:18-30,
@@ -108,7 +108,7 @@ describe('Runtime Errors', function () {
 
 
         try {
-            let res = await program.methods.outOfBounds(new BN(19)).accounts({ dataAccount: storage.publicKey }).simulate();
+            let res = await program.methods.outOfBounds(new BN(19)).simulate();
         } catch (e: any) {
             const logs = e.simulationResponse.logs;
             expect(logs).toContain(`Program log: runtime_error: array index out of bounds in runtime_errors.sol:96:16-21,
@@ -117,7 +117,7 @@ describe('Runtime Errors', function () {
 
 
         try {
-            let res = await program.methods.truncFailure(new BN(99999999999999)).accounts({ dataAccount: storage.publicKey }).simulate();
+            let res = await program.methods.truncFailure(new BN(99999999999999)).simulate();
         } catch (e: any) {
             const logs = e.simulationResponse.logs;
             expect(logs).toContain(`Program log: runtime_error: truncated type overflows in runtime_errors.sol:90:37-42,

+ 42 - 42
integration/solana/simple.spec.ts

@@ -29,121 +29,121 @@ describe('Simple solang tests', function () {
         // TEST Basic enums
         // in ethereum, an enum is described as an uint8 so can't use the enum
         // names programmatically. 0 = add, 1 = sub, 2 = mul, 3 = div, 4 = mod, 5 = pow, 6 = shl, 7 = shr
-        let res = await program.methods.isMul({ mul: {} }).accounts({ dataAccount: storage.publicKey }).view();
+        let res = await program.methods.isMul({ mul: {} }).view();
         expect(res).toBe(true);
 
-        res = await program.methods.returnDiv().accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.returnDiv().view();
         expect(res.div).toBeDefined();
 
         // TEST uint and int types, and arithmetic/bitwise ops
         res = await program.methods.opI64({ add: {} }, new BN(1000), new BN(4100)).view();
         expect(Number(res)).toBe(5100);
-        res = await program.methods.opI64({ sub: {} }, new BN(1000), new BN(4100)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opI64({ sub: {} }, new BN(1000), new BN(4100)).view();
         expect(Number(res)).toStrictEqual(-3100);
-        res = await program.methods.opI64({ mul: {} }, new BN(1000), new BN(4100)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opI64({ mul: {} }, new BN(1000), new BN(4100)).view();
         expect(Number(res)).toBe(4100000);
-        res = await program.methods.opI64({ div: {} }, new BN(1000), new BN(10)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opI64({ div: {} }, new BN(1000), new BN(10)).view();
         expect(Number(res)).toBe(100);
-        res = await program.methods.opI64({ mod: {} }, new BN(1000), new BN(99)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opI64({ mod: {} }, new BN(1000), new BN(99)).view();
         expect(Number(res)).toBe(10);
-        res = await program.methods.opI64({ shl: {} }, new BN(-1000), new BN(8)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opI64({ shl: {} }, new BN(-1000), new BN(8)).view();
         expect(Number(res)).toBe(-256000);
-        res = await program.methods.opI64({ shr: {} }, new BN(-1000), new BN(8)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opI64({ shr: {} }, new BN(-1000), new BN(8)).view();
         expect(Number(res)).toBe(-4);
 
-        res = await program.methods.opU64({ add: {} }, new BN(1000), new BN(4100)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opU64({ add: {} }, new BN(1000), new BN(4100)).view();
         expect(Number(res)).toBe(5100);
-        res = await program.methods.opU64({ sub: {} }, new BN(1000), new BN(4100)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opU64({ sub: {} }, new BN(1000), new BN(4100)).view();
         expect(Number(res)).toBe(18446744073709548516); // (2^64)-18446744073709548516 = 3100
-        res = await program.methods.opU64({ mul: {} }, new BN(123456789), new BN(123456789)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opU64({ mul: {} }, new BN(123456789), new BN(123456789)).view();
         expect(Number(res)).toBe(15241578750190521);
-        res = await program.methods.opU64({ div: {} }, new BN(123456789), new BN(100)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opU64({ div: {} }, new BN(123456789), new BN(100)).view();
         expect(Number(res)).toBe(1234567);
-        res = await program.methods.opU64({ mod: {} }, new BN(123456789), new BN(100)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opU64({ mod: {} }, new BN(123456789), new BN(100)).view();
         expect(Number(res)).toBe(89);
-        res = await program.methods.opU64({ pow: {} }, new BN(3), new BN(7)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opU64({ pow: {} }, new BN(3), new BN(7)).view();
         expect(Number(res)).toBe(2187);
-        res = await program.methods.opI64({ shl: {} }, new BN(1000), new BN(8)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opI64({ shl: {} }, new BN(1000), new BN(8)).view();
         expect(Number(res)).toBe(256000);
-        res = await program.methods.opI64({ shr: {} }, new BN(1000), new BN(8)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opI64({ shr: {} }, new BN(1000), new BN(8)).view();
         expect(Number(res)).toBe(3);
 
         // now for 256 bit operations
-        res = await program.methods.opI256({ add: {} }, new BN(1000), new BN(4100)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opI256({ add: {} }, new BN(1000), new BN(4100)).view();
         expect(Number(res)).toBe(5100);
-        res = await program.methods.opI256({ sub: {} }, new BN(1000), new BN(4100)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opI256({ sub: {} }, new BN(1000), new BN(4100)).view();
         expect(res).toStrictEqual(new BN(-3100));
-        res = await program.methods.opI256({ mul: {} }, new BN(1000), new BN(4100)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opI256({ mul: {} }, new BN(1000), new BN(4100)).view();
         expect(Number(res)).toBe(4100000);
-        res = await program.methods.opI256({ div: {} }, new BN(1000), new BN(10)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opI256({ div: {} }, new BN(1000), new BN(10)).view();
         expect(Number(res)).toBe(100);
-        res = await program.methods.opI256({ mod: {} }, new BN(1000), new BN(99)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opI256({ mod: {} }, new BN(1000), new BN(99)).view();
         expect(Number(res)).toBe(10);
-        res = await program.methods.opI256({ shl: {} }, new BN(-10000000000000), new BN(8)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opI256({ shl: {} }, new BN(-10000000000000), new BN(8)).view();
         expect(Number(res)).toBe(-2560000000000000);
-        res = await program.methods.opI256({ shr: {} }, new BN(-10000000000000), new BN(8)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opI256({ shr: {} }, new BN(-10000000000000), new BN(8)).view();
         expect(Number(res)).toBe(-39062500000);
 
-        res = await program.methods.opU256({ add: {} }, new BN(1000), new BN(4100)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opU256({ add: {} }, new BN(1000), new BN(4100)).view();
         expect(Number(res)).toBe(5100);
-        res = await program.methods.opU256({ sub: {} }, new BN(1000), new BN(4100)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opU256({ sub: {} }, new BN(1000), new BN(4100)).view();
         expect(Number(res)).toBe(115792089237316195423570985008687907853269984665640564039457584007913129636836); // (2^64)-18446744073709548516 = 3100
-        res = await program.methods.opU256({ mul: {} }, new BN(123456789), new BN(123456789)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opU256({ mul: {} }, new BN(123456789), new BN(123456789)).view();
         expect(Number(res)).toBe(15241578750190521);
-        res = await program.methods.opU256({ div: {} }, new BN(123456789), new BN(100)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opU256({ div: {} }, new BN(123456789), new BN(100)).view();
         expect(Number(res)).toBe(1234567);
-        res = await program.methods.opU256({ mod: {} }, new BN(123456789), new BN(100)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opU256({ mod: {} }, new BN(123456789), new BN(100)).view();
         expect(Number(res)).toBe(89);
-        res = await program.methods.opU256({ pow: {} }, new BN(123456789), new BN(9)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opU256({ pow: {} }, new BN(123456789), new BN(9)).view();
         expect(Number(res)).toBe(6662462759719942007440037531362779472290810125440036903063319585255179509);
-        res = await program.methods.opU256({ shl: {} }, new BN(10000000000000), new BN(8)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opU256({ shl: {} }, new BN(10000000000000), new BN(8)).view();
         expect(Number(res)).toBe(2560000000000000);
-        res = await program.methods.opU256({ shr: {} }, new BN(10000000000000), new BN(8)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opU256({ shr: {} }, new BN(10000000000000), new BN(8)).view();
         expect(Number(res)).toBe(39062500000);
 
         // TEST bytesN
-        res = await program.methods.returnU86().accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.returnU86().view();
         expect(res).toStrictEqual([0x41, 0x42, 0x43, 0x44, 0x45, 0x46]);
 
         // TEST bytes5
         res = await program.methods.opU85Shift({ shl: {} },
-            Buffer.from("deadcafe59", "hex"), new BN(8)).accounts({ dataAccount: storage.publicKey }).view();
+            Buffer.from("deadcafe59", "hex"), new BN(8)).view();
         expect(res).toStrictEqual([0xad, 0xca, 0xfe, 0x59, 0x00]);
-        res = await program.methods.opU85Shift({ shr: {} }, Buffer.from("deadcafe59", "hex"), new BN(8)).accounts({ dataAccount: storage.publicKey }).view();
+        res = await program.methods.opU85Shift({ shr: {} }, Buffer.from("deadcafe59", "hex"), new BN(8)).view();
         expect(res).toStrictEqual([0x00, 0xde, 0xad, 0xca, 0xfe]);
         res = await program.methods.opU85({ or: {} },
             Buffer.from("deadcafe59", "hex"),
-            Buffer.from("0000000006", "hex")).accounts({ dataAccount: storage.publicKey }).view();
+            Buffer.from("0000000006", "hex")).view();
         expect(res).toStrictEqual([0xde, 0xad, 0xca, 0xfe, 0x5f]);
         res = await program.methods.opU85({ and: {} },
             Buffer.from("deadcafe59", "hex"),
-            Buffer.from("00000000ff", "hex")).accounts({ dataAccount: storage.publicKey }).view();
+            Buffer.from("00000000ff", "hex")).view();
         expect(res).toStrictEqual([0x00, 0x00, 0x00, 0x00, 0x59]);
         res = await program.methods.opU85({ xor: {} },
             Buffer.from("deadcafe59", "hex"),
-            Buffer.from("00000000ff", "hex")).accounts({ dataAccount: storage.publicKey }).view();
+            Buffer.from("00000000ff", "hex")).view();
         expect(res).toStrictEqual([0xde, 0xad, 0xca, 0xfe, 0xa6]);
 
         // TEST bytes14
         res = await program.methods.opU814Shift({ shl: {} },
             Buffer.from("deadcafe123456789abcdefbeef7", "hex"), new BN(9))
-            .accounts({ dataAccount: storage.publicKey }).view();
+            .view();
         expect(res).toStrictEqual([0x5b, 0x95, 0xfc, 0x24, 0x68, 0xac, 0xf1, 0x35, 0x79, 0xbd, 0xf7, 0xdd, 0xee, 0x00]);
         res = await program.methods.opU814Shift({ shr: {} },
-            Buffer.from("deadcafe123456789abcdefbeef7", "hex"), new BN(9)).accounts({ dataAccount: storage.publicKey }).view();
+            Buffer.from("deadcafe123456789abcdefbeef7", "hex"), new BN(9)).view();
         expect(res).toStrictEqual([0x00, 0x6f, 0x56, 0xe5, 0x7f, 0x09, 0x1a, 0x2b, 0x3c, 0x4d, 0x5e, 0x6f, 0x7d, 0xf7]);
         res = await program.methods.opU814({ or: {} },
             Buffer.from("deadcafe123456789abcdefbeef7", "hex"),
-            Buffer.from("0000060000000000000000000000", "hex")).accounts({ dataAccount: storage.publicKey }).view();
+            Buffer.from("0000060000000000000000000000", "hex")).view();
         expect(res).toStrictEqual([0xde, 0xad, 0xce, 0xfe, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xfb, 0xee, 0xf7]);
         res = await program.methods.opU814({ and: {} },
             Buffer.from("deadcafe123456789abcdefbeef7", "hex"),
-            Buffer.from("000000000000000000ff00000000", "hex")).accounts({ dataAccount: storage.publicKey }).view();
+            Buffer.from("000000000000000000ff00000000", "hex")).view();
         expect(res).toStrictEqual(
             [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0x00, 0x00, 0x00]);
         res = await program.methods.opU814({ xor: {} },
             Buffer.from("deadcafe123456789abcdefbeef7", "hex"),
-            Buffer.from("ff00000000000000000000000000", "hex")).accounts({ dataAccount: storage.publicKey }).view();
+            Buffer.from("ff00000000000000000000000000", "hex")).view();
         expect(res).toStrictEqual(
             [0x21, 0xad, 0xca, 0xfe, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xfb, 0xee, 0xf7]);
 

+ 0 - 1
integration/solana/simple_collectible.spec.ts

@@ -72,7 +72,6 @@ describe('Simple collectible', function () {
         await program.methods.transferOwnership(
             owner_token_account.address,
             new_owner_token_account.address)
-            .accounts({ dataAccount: storage.publicKey })
             .remainingAccounts([
                 { pubkey: new_owner_token_account.address, isSigner: false, isWritable: true },
                 { pubkey: owner_token_account.address, isSigner: false, isWritable: true },

+ 0 - 13
integration/solana/system_instruction.spec.ts

@@ -28,7 +28,6 @@ describe('Test system instructions', function () {
                 { pubkey: payer.publicKey, isSigner: true, isWritable: false },
                 { pubkey: to_key_pair.publicKey, isSigner: true, isWritable: true },
             ])
-            .accounts({ dataAccount: storage.publicKey })
             .signers([payer, to_key_pair]).rpc();
     });
 
@@ -51,7 +50,6 @@ describe('Test system instructions', function () {
                 { pubkey: to_key_pair, isSigner: false, isWritable: true },
                 { pubkey: base_keypair.publicKey, isSigner: true, isWritable: false },
             ])
-            .accounts({ dataAccount: storage.publicKey })
             .signers([payer, base_keypair]).rpc();
     });
 
@@ -67,7 +65,6 @@ describe('Test system instructions', function () {
                 { pubkey: payer.publicKey, isSigner: false, isWritable: false },
                 { pubkey: to_key_pair.publicKey, isSigner: true, isWritable: true },
             ])
-            .accounts({ dataAccount: storage.publicKey })
             .signers([payer, to_key_pair]).rpc();
     });
 
@@ -86,7 +83,6 @@ describe('Test system instructions', function () {
                 { pubkey: payer.publicKey, isSigner: false, isWritable: false },
                 { pubkey: to_key_pair, isSigner: false, isWritable: true },
             ])
-            .accounts({ dataAccount: storage.publicKey })
             .signers([payer]).rpc();
     });
 
@@ -102,7 +98,6 @@ describe('Test system instructions', function () {
                 { pubkey: payer.publicKey, isSigner: false, isWritable: true },
                 { pubkey: dest.publicKey, isSigner: false, isWritable: true },
             ])
-            .accounts({ dataAccount: storage.publicKey })
             .signers([payer]).rpc();
     });
 
@@ -128,7 +123,6 @@ describe('Test system instructions', function () {
                 { pubkey: dest.publicKey, isSigner: false, isWritable: true },
                 { pubkey: payer.publicKey, isSigner: true, isWritable: false },
             ])
-            .accounts({ dataAccount: storage.publicKey })
             .signers([payer]).rpc();
     });
 
@@ -142,7 +136,6 @@ describe('Test system instructions', function () {
             .remainingAccounts([
                 { pubkey: account.publicKey, isSigner: true, isWritable: true },
             ])
-            .accounts({ dataAccount: storage.publicKey })
             .signers([payer, account]).rpc();
     });
 
@@ -163,7 +156,6 @@ describe('Test system instructions', function () {
                 { pubkey: account.publicKey, isSigner: true, isWritable: false },
                 { pubkey: derived_key, isSigner: false, isWritable: true },
             ])
-            .accounts({ dataAccount: storage.publicKey })
             .signers([payer, account]).rpc();
     });
 
@@ -187,7 +179,6 @@ describe('Test system instructions', function () {
                 { pubkey: derived_account, isSigner: false, isWritable: true },
                 { pubkey: base_address.publicKey, isSigner: true, isWritable: true },
             ])
-            .accounts({ dataAccount: storage.publicKey })
             .signers([payer, base_address]).rpc();
     });
 
@@ -207,7 +198,6 @@ describe('Test system instructions', function () {
                 { pubkey: payer.publicKey, isSigner: false, isWritable: true },
                 { pubkey: nonce.publicKey, isSigner: true, isWritable: true },
             ])
-            .accounts({ dataAccount: storage.publicKey })
             .signers([payer, nonce]).rpc();
 
         await program.methods.advanceNonceAccount(
@@ -218,7 +208,6 @@ describe('Test system instructions', function () {
                 { pubkey: authority.publicKey, isSigner: true, isWritable: false },
                 { pubkey: nonce.publicKey, isSigner: false, isWritable: true },
             ])
-            .accounts({ dataAccount: storage.publicKey })
             .signers([authority]).rpc();
 
         await program.methods.withdrawNonceAccount(
@@ -233,7 +222,6 @@ describe('Test system instructions', function () {
                 { pubkey: nonce.publicKey, isSigner: false, isWritable: true },
                 { pubkey: payer.publicKey, isSigner: false, isWritable: true },
             ])
-            .accounts({ dataAccount: storage.publicKey })
             .signers([authority]).rpc();
 
         const new_authority = Keypair.generate();
@@ -246,7 +234,6 @@ describe('Test system instructions', function () {
                 { pubkey: authority.publicKey, isSigner: true, isWritable: false },
                 { pubkey: nonce.publicKey, isSigner: false, isWritable: true },
             ])
-            .accounts({ dataAccount: storage.publicKey })
             .signers([authority]).rpc();
     });
 });

+ 0 - 9
integration/solana/token.spec.ts

@@ -32,7 +32,6 @@ describe('Create spl-token and use from solidity', function () {
             .accounts({ dataAccount: storage.publicKey })
             .remainingAccounts([{ pubkey: mint, isSigner: false, isWritable: false }])
             .view();
-
         expect(total_supply.toNumber()).toBe(0);
 
         const tokenAccount = await getOrCreateAssociatedTokenAccount(
@@ -43,7 +42,6 @@ describe('Create spl-token and use from solidity', function () {
         )
 
         let balance = await program.methods.getBalance(tokenAccount.address)
-            .accounts({ dataAccount: storage.publicKey })
             .remainingAccounts([{ pubkey: tokenAccount.address, isSigner: false, isWritable: false }])
             .view();
 
@@ -71,7 +69,6 @@ describe('Create spl-token and use from solidity', function () {
 
         expect(total_supply.toNumber()).toBe(100000);
         balance = await program.methods.getBalance(tokenAccount.address)
-            .accounts({ dataAccount: storage.publicKey })
             .remainingAccounts([{ pubkey: tokenAccount.address, isSigner: false, isWritable: false }])
             .view();
 
@@ -92,7 +89,6 @@ describe('Create spl-token and use from solidity', function () {
             otherTokenAccount.address,
             payer.publicKey,
             new BN(70000))
-            .accounts({ dataAccount: storage.publicKey })
             .remainingAccounts([
                 { pubkey: otherTokenAccount.address, isSigner: false, isWritable: true },
                 { pubkey: tokenAccount.address, isSigner: false, isWritable: true },
@@ -108,14 +104,12 @@ describe('Create spl-token and use from solidity', function () {
 
         expect(total_supply.toNumber()).toBe(100000);
         balance = await program.methods.getBalance(tokenAccount.address)
-            .accounts({ dataAccount: storage.publicKey })
             .remainingAccounts([{ pubkey: tokenAccount.address, isSigner: false, isWritable: false }])
             .view();
 
         expect(balance.toNumber()).toBe(30000);
 
         balance = await program.methods.getBalance(otherTokenAccount.address)
-            .accounts({ dataAccount: storage.publicKey })
             .remainingAccounts([{ pubkey: otherTokenAccount.address, isSigner: false, isWritable: false }])
             .view();
 
@@ -135,7 +129,6 @@ describe('Create spl-token and use from solidity', function () {
             .signers([theOutsider])
             .rpc();
 
-
         total_supply = await program.methods.totalSupply()
             .accounts({ dataAccount: storage.publicKey })
             .remainingAccounts([{ pubkey: mint, isSigner: false, isWritable: false }])
@@ -143,14 +136,12 @@ describe('Create spl-token and use from solidity', function () {
 
         expect(total_supply.toNumber()).toBe(80000);
         balance = await program.methods.getBalance(tokenAccount.address)
-            .accounts({ dataAccount: storage.publicKey })
             .remainingAccounts([{ pubkey: tokenAccount.address, isSigner: false, isWritable: false }])
             .view();
 
         expect(balance.toNumber()).toBe(30000);
 
         balance = await program.methods.getBalance(otherTokenAccount.address)
-            .accounts({ dataAccount: storage.publicKey })
             .remainingAccounts([{ pubkey: otherTokenAccount.address, isSigner: false, isWritable: false }])
             .view();
 

+ 0 - 2
integration/solana/verify_sig.spec.ts

@@ -31,7 +31,6 @@ describe('Signature Check', function () {
         const result = await program.methods.verify(payer.publicKey, message, Buffer.from(signature))
             .preInstructions([instr1])
             .accounts({
-                dataAccount: storage.publicKey,
                 SysvarInstruction: SYSVAR_INSTRUCTIONS_PUBKEY
             })
             .view();
@@ -57,7 +56,6 @@ describe('Signature Check', function () {
         const result = await program.methods.verify(payer.publicKey, message, Buffer.from(broken_signature))
             .preInstructions([instr1])
             .accounts({
-                dataAccount: storage.publicKey,
                 SysvarInstruction: SYSVAR_INSTRUCTIONS_PUBKEY
             })
             .view();

+ 149 - 503
src/abi/tests.rs

@@ -190,15 +190,7 @@ fn instructions_and_types() {
     assert!(idl.instructions[0].docs.is_none());
     assert_eq!(
         idl.instructions[0].accounts,
-        vec![IdlAccountItem::IdlAccount(IdlAccount {
-            name: "dataAccount".to_string(),
-            is_mut: true,
-            is_signer: false,
-            is_optional: Some(false),
-            docs: None,
-            pda: None,
-            relations: vec![],
-        })]
+        vec![idl_account("dataAccount", true, false)]
     );
     assert!(idl.instructions[0].args.is_empty());
     assert!(idl.instructions[0].returns.is_none());
@@ -208,15 +200,7 @@ fn instructions_and_types() {
     assert!(idl.instructions[1].docs.is_none());
     assert_eq!(
         idl.instructions[1].accounts,
-        vec![IdlAccountItem::IdlAccount(IdlAccount {
-            name: "dataAccount".to_string(),
-            is_mut: false,
-            is_signer: false,
-            is_optional: Some(false),
-            docs: None,
-            pda: None,
-            relations: vec![],
-        }),]
+        vec![idl_account("dataAccount", false, false)]
     );
     assert!(idl.instructions[1].args.is_empty());
     assert_eq!(idl.instructions[1].returns, Some(IdlType::U64));
@@ -226,15 +210,7 @@ fn instructions_and_types() {
     assert!(idl.instructions[2].docs.is_none());
     assert_eq!(
         idl.instructions[2].accounts,
-        vec![IdlAccountItem::IdlAccount(IdlAccount {
-            name: "dataAccount".to_string(),
-            is_mut: false,
-            is_signer: false,
-            is_optional: Some(false),
-            docs: None,
-            pda: None,
-            relations: vec![],
-        }),]
+        vec![idl_account("dataAccount", false, false)]
     );
     assert_eq!(
         idl.instructions[2].args,
@@ -274,15 +250,7 @@ fn instructions_and_types() {
     );
     assert_eq!(
         idl.instructions[4].accounts,
-        vec![IdlAccountItem::IdlAccount(IdlAccount {
-            name: "dataAccount".to_string(),
-            is_mut: true,
-            is_signer: false,
-            is_optional: Some(false),
-            docs: None,
-            pda: None,
-            relations: vec![],
-        }),]
+        vec![idl_account("dataAccount", true, false)]
     );
     assert_eq!(
         idl.instructions[4].args,
@@ -301,15 +269,7 @@ fn instructions_and_types() {
     );
     assert_eq!(
         idl.instructions[5].accounts,
-        vec![IdlAccountItem::IdlAccount(IdlAccount {
-            name: "dataAccount".to_string(),
-            is_mut: false,
-            is_signer: false,
-            is_optional: Some(false),
-            docs: None,
-            pda: None,
-            relations: vec![],
-        }),]
+        vec![idl_account("dataAccount", false, false)]
     );
     assert!(idl.instructions[5].args.is_empty());
     assert_eq!(idl.instructions[5].returns, Some(IdlType::String));
@@ -318,15 +278,7 @@ fn instructions_and_types() {
     assert!(idl.instructions[6].docs.is_none());
     assert_eq!(
         idl.instructions[6].accounts,
-        vec![IdlAccountItem::IdlAccount(IdlAccount {
-            name: "dataAccount".to_string(),
-            is_mut: true,
-            is_signer: false,
-            is_optional: Some(false),
-            docs: None,
-            pda: None,
-            relations: vec![],
-        }),]
+        vec![idl_account("dataAccount", true, false),]
     );
     assert!(idl.instructions[6].args.is_empty());
     assert_eq!(
@@ -397,33 +349,14 @@ contract caller {
     assert!(idl.instructions[0].docs.is_none());
     assert_eq!(
         idl.instructions[0].accounts,
-        vec![IdlAccountItem::IdlAccount(IdlAccount {
-            name: "dataAccount".to_string(),
-            is_mut: true,
-            is_signer: false,
-            is_optional: Some(false),
-            docs: None,
-            pda: None,
-            relations: vec![],
-        })]
+        vec![idl_account("dataAccount", true, false)]
     );
     assert!(idl.instructions[0].args.is_empty());
     assert!(idl.instructions[0].returns.is_none());
 
     assert_eq!(idl.instructions[1].name, "emitAll");
     assert!(idl.instructions[1].docs.is_none());
-    assert_eq!(
-        idl.instructions[1].accounts,
-        vec![IdlAccountItem::IdlAccount(IdlAccount {
-            name: "dataAccount".to_string(),
-            is_mut: true,
-            is_signer: false,
-            is_optional: Some(false),
-            docs: None,
-            pda: None,
-            relations: vec![],
-        }),]
-    );
+    assert!(idl.instructions[1].accounts.is_empty());
     assert_eq!(
         idl.instructions[1].args,
         vec![
@@ -552,32 +485,13 @@ fn types() {
     assert!(idl.instructions[0].docs.is_none());
     assert_eq!(
         idl.instructions[0].accounts,
-        vec![IdlAccountItem::IdlAccount(IdlAccount {
-            name: "dataAccount".to_string(),
-            is_mut: true,
-            is_signer: false,
-            is_optional: Some(false),
-            docs: None,
-            pda: None,
-            relations: vec![],
-        })]
+        vec![idl_account("dataAccount", true, false)]
     );
     assert!(idl.instructions[0].args.is_empty());
     assert!(idl.instructions[0].returns.is_none());
 
     assert_eq!(idl.instructions[1].name, "myFunc");
-    assert_eq!(
-        idl.instructions[1].accounts,
-        vec![IdlAccountItem::IdlAccount(IdlAccount {
-            name: "dataAccount".to_string(),
-            is_mut: true,
-            is_signer: false,
-            is_optional: Some(false),
-            docs: None,
-            pda: None,
-            relations: vec![],
-        }),]
-    );
+    assert!(idl.instructions[1].accounts.is_empty());
     assert_eq!(
         idl.instructions[1].args,
         vec![
@@ -655,15 +569,7 @@ fn constructor() {
     assert!(idl.instructions[0].docs.is_none());
     assert_eq!(
         idl.instructions[0].accounts,
-        vec![IdlAccountItem::IdlAccount(IdlAccount {
-            name: "dataAccount".to_string(),
-            is_mut: true,
-            is_signer: false,
-            is_optional: Some(false),
-            docs: None,
-            pda: None,
-            relations: vec![],
-        }),]
+        vec![idl_account("dataAccount", true, false)]
     );
     assert_eq!(
         idl.instructions[0].args,
@@ -679,15 +585,7 @@ fn constructor() {
     assert!(idl.instructions[1].docs.is_none());
     assert_eq!(
         idl.instructions[1].accounts,
-        vec![IdlAccountItem::IdlAccount(IdlAccount {
-            name: "dataAccount".to_string(),
-            is_mut: false,
-            is_signer: false,
-            is_optional: Some(false),
-            docs: None,
-            pda: None,
-            relations: vec![],
-        }),]
+        vec![idl_account("dataAccount", false, false)]
     );
     assert!(idl.instructions[1].args.is_empty());
     assert_eq!(idl.instructions[1].returns, Some(IdlType::U64));
@@ -720,15 +618,7 @@ contract Testing {
     assert!(idl.instructions[0].args.is_empty());
     assert_eq!(
         idl.instructions[0].accounts,
-        vec![IdlAccountItem::IdlAccount(IdlAccount {
-            name: "dataAccount".to_string(),
-            is_mut: true,
-            is_signer: false,
-            is_optional: Some(false),
-            docs: None,
-            pda: None,
-            relations: vec![],
-        })]
+        vec![idl_account("dataAccount", true, false)]
     );
 
     assert_eq!(idl.instructions[1].name, "getNum");
@@ -1519,7 +1409,7 @@ fn data_account_signer() {
     contract caller {
         // signer required
         @payer(wallet)
-        constructor(address wallet) {}
+        constructor() {}
     }"#;
 
     let mut ns = generate_namespace(src);
@@ -1534,36 +1424,12 @@ fn data_account_signer() {
     assert_eq!(
         idl.instructions[0].accounts,
         vec![
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "dataAccount".to_string(),
-                is_mut: true,
-                is_signer: true,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![],
-            }),
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "wallet".to_string(),
-                is_mut: true,
-                is_signer: true,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![],
-            }),
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "systemProgram".to_string(),
-                is_mut: false,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![],
-            })
+            idl_account("dataAccount", true, true),
+            idl_account("wallet", true, true),
+            idl_account("systemProgram", false, false),
         ]
     );
-    assert!(idl.instructions[0].args.len() == 1);
+    assert!(idl.instructions[0].args.is_empty());
     assert!(idl.instructions[0].returns.is_none());
 
     let src = r#"
@@ -1585,33 +1451,9 @@ fn data_account_signer() {
     assert_eq!(
         idl.instructions[0].accounts,
         vec![
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "dataAccount".to_string(),
-                is_mut: true,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![],
-            }),
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "wallet".to_string(),
-                is_mut: true,
-                is_signer: true,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![],
-            }),
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "systemProgram".to_string(),
-                is_mut: false,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![],
-            })
+            idl_account("dataAccount", true, false),
+            idl_account("wallet", true, true),
+            idl_account("systemProgram", false, false)
         ]
     );
     assert!(idl.instructions[0].args.len() == 1);
@@ -1642,40 +1484,13 @@ fn accounts_call_chain() {
     assert_eq!(idl.instructions[0].name, "new");
     assert_eq!(
         idl.instructions[0].accounts,
-        vec![IdlAccountItem::IdlAccount(IdlAccount {
-            name: "dataAccount".to_string(),
-            is_mut: true,
-            is_signer: false,
-            is_optional: Some(false),
-            docs: None,
-            pda: None,
-            relations: vec![],
-        })]
+        vec![idl_account("dataAccount", true, false)]
     );
 
     assert_eq!(idl.instructions[1].name, "call_1");
     assert_eq!(
         idl.instructions[1].accounts,
-        vec![
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "dataAccount".to_string(),
-                is_mut: false,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![],
-            }),
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "clock".to_string(),
-                is_mut: false,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![]
-            })
-        ]
+        vec![idl_account("clock", false, false)]
     );
 
     assert_eq!(idl.instructions[2].name, "call_2");
@@ -1723,48 +1538,16 @@ fn accounts_on_recursion() {
     assert_eq!(idl.instructions[0].name, "new");
     assert_eq!(
         idl.instructions[0].accounts,
-        vec![IdlAccountItem::IdlAccount(IdlAccount {
-            name: "dataAccount".to_string(),
-            is_mut: true,
-            is_signer: false,
-            is_optional: Some(false),
-            docs: None,
-            pda: None,
-            relations: vec![],
-        })]
+        vec![idl_account("dataAccount", true, false)]
     );
 
     assert_eq!(idl.instructions[1].name, "call_1");
     assert_eq!(
         idl.instructions[1].accounts,
         vec![
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "dataAccount".to_string(),
-                is_mut: false,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![],
-            }),
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "SysvarInstruction".to_string(),
-                is_mut: false,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![]
-            }),
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "clock".to_string(),
-                is_mut: false,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![]
-            })
+            idl_account("dataAccount", false, false),
+            idl_account("SysvarInstruction", false, false),
+            idl_account("clock", false, false),
         ]
     );
 
@@ -1814,33 +1597,9 @@ fn system_account_for_payer_annotation() {
     assert_eq!(
         idl.instructions[0].accounts,
         vec![
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "dataAccount".to_string(),
-                is_mut: true,
-                is_signer: true,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![],
-            }),
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "addr_".to_string(),
-                is_mut: true,
-                is_signer: true,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![]
-            }),
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "systemProgram".to_string(),
-                is_mut: false,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![]
-            })
+            idl_account("dataAccount", true, true),
+            idl_account("addr_", true, true),
+            idl_account("systemProgram", false, false),
         ]
     );
 }
@@ -1884,26 +1643,7 @@ contract Test {
     assert_eq!(idl.instructions[1].name, "call_1");
     assert_eq!(
         idl.instructions[1].accounts,
-        vec![
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "dataAccount".to_string(),
-                is_mut: true,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![],
-            }),
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "systemProgram".to_string(),
-                is_mut: false,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![]
-            })
-        ]
+        vec![idl_account("systemProgram", false, false)]
     );
 }
 
@@ -1946,33 +1686,8 @@ contract Test {
     assert_eq!(
         idl.instructions[1].accounts,
         vec![
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "dataAccount".to_string(),
-                is_mut: true,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![],
-            }),
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "tokenProgram".to_string(),
-                is_mut: false,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![],
-            }),
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "systemProgram".to_string(),
-                is_mut: false,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![]
-            })
+            idl_account("tokenProgram", false, false),
+            idl_account("systemProgram", false, false),
         ]
     );
 }
@@ -2037,48 +1752,15 @@ contract Test {
     assert_eq!(idl.instructions[0].name, "new");
     assert_eq!(
         idl.instructions[0].accounts,
-        vec![IdlAccountItem::IdlAccount(IdlAccount {
-            name: "dataAccount".to_string(),
-            is_mut: true,
-            is_signer: false,
-            is_optional: Some(false),
-            docs: None,
-            pda: None,
-            relations: vec![],
-        }),]
+        vec![idl_account("dataAccount", true, false)]
     );
 
     assert_eq!(idl.instructions[1].name, "call_1");
     assert_eq!(
         idl.instructions[1].accounts,
         vec![
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "dataAccount".to_string(),
-                is_mut: true,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![],
-            }),
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "rent".to_string(),
-                is_mut: false,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![],
-            }),
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "systemProgram".to_string(),
-                is_mut: false,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![]
-            })
+            idl_account("rent", false, false),
+            idl_account("systemProgram", false, false),
         ]
     );
 
@@ -2086,33 +1768,8 @@ contract Test {
     assert_eq!(
         idl.instructions[2].accounts,
         vec![
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "dataAccount".to_string(),
-                is_mut: true,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![],
-            }),
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "associatedTokenProgram".to_string(),
-                is_mut: false,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![],
-            }),
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "systemProgram".to_string(),
-                is_mut: false,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![]
-            })
+            idl_account("associatedTokenProgram", false, false),
+            idl_account("systemProgram", false, false),
         ]
     );
 
@@ -2120,33 +1777,8 @@ contract Test {
     assert_eq!(
         idl.instructions[3].accounts,
         vec![
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "dataAccount".to_string(),
-                is_mut: true,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![],
-            }),
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "clock".to_string(),
-                is_mut: false,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![],
-            }),
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "systemProgram".to_string(),
-                is_mut: false,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![]
-            })
+            idl_account("clock", false, false),
+            idl_account("systemProgram", false, false),
         ]
     );
 
@@ -2154,24 +1786,8 @@ contract Test {
     assert_eq!(
         idl.instructions[4].accounts,
         vec![
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "dataAccount".to_string(),
-                is_mut: true,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![],
-            }),
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "systemProgram".to_string(),
-                is_mut: false,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![]
-            })
+            idl_account("systemProgram", false, false),
+            idl_account("other_interface_programId", false, false),
         ]
     );
 }
@@ -2184,9 +1800,9 @@ fn multiple_contracts() {
 contract creator {
     Child public c;
 
-    function create_child(address child, address payer) external returns (uint64) {
+    function create_child() external returns (uint64) {
         print("Going to create child");
-        c = new Child{address: child}(payer);
+        c = new Child();
 
         return c.say_hello();
     }
@@ -2196,7 +1812,7 @@ contract creator {
 contract Child {
     @payer(payer)
     @space(511 + 7)
-    constructor(address payer) {
+    constructor() {
         print("In child constructor");
     }
 
@@ -2218,42 +1834,12 @@ contract Child {
     assert_eq!(
         idl.instructions[2].accounts,
         vec![
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "dataAccount".to_string(),
-                is_mut: true,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![],
-            }),
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "payer".to_string(),
-                is_mut: true,
-                is_signer: true,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![],
-            }),
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "systemProgram".to_string(),
-                is_mut: false,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![],
-            }),
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "clock".to_string(),
-                is_mut: false,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![]
-            })
+            idl_account("dataAccount", true, false),
+            idl_account("payer", true, true),
+            idl_account("Child_dataAccount", true, true),
+            idl_account("Child_programId", false, false),
+            idl_account("systemProgram", false, false),
+            idl_account("clock", false, false),
         ]
     );
 }
@@ -2268,8 +1854,8 @@ contract Builder {
     BeingBuilt other;
 
     @payer(payer_account)
-    constructor(address addr) {
-        other = new BeingBuilt{address: addr}("abc");
+    constructor() {
+        other = new BeingBuilt("abc");
     }
 }
 
@@ -2294,42 +1880,12 @@ contract BeingBuilt {
     assert_eq!(
         idl.instructions[0].accounts,
         vec![
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "dataAccount".to_string(),
-                is_mut: true,
-                is_signer: true,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![],
-            }),
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "payer_account".to_string(),
-                is_mut: true,
-                is_signer: true,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![],
-            }),
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "systemProgram".to_string(),
-                is_mut: false,
-                is_signer: false,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![],
-            }),
-            IdlAccountItem::IdlAccount(IdlAccount {
-                name: "other_account".to_string(),
-                is_mut: true,
-                is_signer: true,
-                is_optional: Some(false),
-                docs: None,
-                pda: None,
-                relations: vec![]
-            })
+            idl_account("dataAccount", true, true),
+            idl_account("payer_account", true, true),
+            idl_account("systemProgram", false, false),
+            idl_account("other_account", true, true),
+            idl_account("BeingBuilt_dataAccount", true, false),
+            idl_account("BeingBuilt_programId", false, false),
         ]
     );
 }
@@ -2395,3 +1951,93 @@ contract starter {
 
     assert_eq!(idl1, idl2);
 }
+
+#[test]
+fn account_transfer_recursive() {
+    let src = r#"
+contract CT3 {
+    CT2 ct2;
+    @payer(three_payer)
+    constructor() {
+        ct2 = new CT2();
+    }
+}
+
+@program_id("Ha2EGxARbSYpqNZkkvZUUGEyx3pu7Mg9pvMsuEJuWNjH")
+contract CT2 {
+    CT1 ct1;
+    @payer(two_payer)
+    constructor() {
+        ct1 = new CT1(block.timestamp);
+    }
+}
+
+
+@program_id("5kQ3iJ43gHNDjqmSAtE1vDu18CiSAfNbRe4v5uoobh3U")
+contract CT1 {
+    @payer(one_payer)
+    constructor(uint64 time) {
+        print("{}".format(time));
+    }
+}
+    "#;
+
+    let mut ns = generate_namespace(src);
+    codegen(&mut ns, &Options::default());
+    let ct3 = generate_anchor_idl(0, &ns, "0.1.0");
+    let ct2 = generate_anchor_idl(1, &ns, "0.1.0");
+    let ct1 = generate_anchor_idl(2, &ns, "0.1.0");
+
+    assert_eq!(ct1.instructions[0].name, "new");
+    assert_eq!(
+        ct1.instructions[0].accounts,
+        vec![
+            idl_account("dataAccount", true, true),
+            idl_account("one_payer", true, true),
+            idl_account("systemProgram", false, false),
+        ]
+    );
+
+    assert_eq!(ct2.instructions[0].name, "new");
+    assert_eq!(
+        ct2.instructions[0].accounts,
+        vec![
+            idl_account("dataAccount", true, true),
+            idl_account("two_payer", true, true),
+            idl_account("clock", false, false),
+            idl_account("one_payer", true, true),
+            idl_account("CT1_dataAccount", true, true),
+            idl_account("CT1_programId", false, false),
+            idl_account("systemProgram", false, false),
+        ]
+    );
+
+    assert_eq!(ct3.instructions[0].name, "new");
+    assert_eq!(
+        ct3.instructions[0].accounts,
+        vec![
+            idl_account("dataAccount", true, true),
+            idl_account("three_payer", true, true),
+            idl_account("systemProgram", false, false),
+            idl_account("two_payer", true, true),
+            idl_account("CT2_dataAccount", true, true),
+            idl_account("CT2_programId", false, false),
+            idl_account("clock", false, false),
+            idl_account("one_payer", true, true),
+            idl_account("CT1_dataAccount", true, true),
+            idl_account("CT1_programId", false, false),
+        ]
+    );
+}
+
+fn idl_account(name: &str, is_mut: bool, is_signer: bool) -> IdlAccountItem {
+    IdlAccountItem::IdlAccount(IdlAccount {
+        name: name.to_string(),
+        is_mut,
+        is_signer,
+        is_optional: Some(false),
+        docs: None,
+        pda: None,
+        relations: vec![],
+    })
+}

+ 0 - 3
src/bin/languageserver/mod.rs

@@ -1095,9 +1095,6 @@ impl<'a> Builder<'a> {
                 if let Some(optsalt) = &call_args.salt {
                     self.expression(optsalt, symtab);
                 }
-                if let Some(address) = &call_args.address {
-                    self.expression(address, symtab);
-                }
                 if let Some(seeds) = &call_args.seeds {
                     self.expression(seeds, symtab);
                 }

+ 7 - 1
src/codegen/cfg.rs

@@ -132,6 +132,7 @@ pub enum Instr {
     /// Call external functions. If the call fails, set the success failure
     /// or abort if this is None
     ExternalCall {
+        loc: Loc,
         success: Option<usize>,
         address: Option<Expression>,
         accounts: Option<Expression>,
@@ -417,6 +418,8 @@ pub struct ControlFlowGraph {
     current: usize,
     // A mapping between the res of an array and the res of the temp var holding its length.
     pub array_lengths_temps: ArrayLengthVars,
+    /// Is this a modifier dispatch for which function number?
+    pub modifier: Option<usize>,
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -488,6 +491,7 @@ impl ControlFlowGraph {
             selector: Vec::new(),
             current: 0,
             array_lengths_temps: IndexMap::new(),
+            modifier: None,
         };
 
         cfg.new_basic_block("entry".to_string());
@@ -510,6 +514,7 @@ impl ControlFlowGraph {
             selector: Vec::new(),
             current: 0,
             array_lengths_temps: IndexMap::new(),
+            modifier: None,
         }
     }
 
@@ -1181,7 +1186,7 @@ impl ControlFlowGraph {
                 gas,
                 callty,
                 contract_function_no,
-                flags
+                flags, ..
             } => {
                 format!(
                     "{} = external call::{} address:{} payload:{} value:{} gas:{} accounts:{} seeds:{} contract|function:{} flags:{}",
@@ -1511,6 +1516,7 @@ pub fn generate_cfg(
             cfg.public = public;
             cfg.nonpayable = nonpayable;
             cfg.selector = ns.functions[func_no].selector(ns, &contract_no);
+            cfg.modifier = Some(func_no);
         }
     }
 

+ 2 - 0
src/codegen/constant_folding.rs

@@ -267,6 +267,7 @@ pub fn constant_folding(cfg: &mut ControlFlowGraph, dry_run: bool, ns: &mut Name
                     }
                 }
                 Instr::ExternalCall {
+                    loc,
                     success,
                     address,
                     payload,
@@ -296,6 +297,7 @@ pub fn constant_folding(cfg: &mut ControlFlowGraph, dry_run: bool, ns: &mut Name
 
                     if !dry_run {
                         cfg.blocks[block_no].instr[instr_no] = Instr::ExternalCall {
+                            loc: *loc,
                             success: *success,
                             address,
                             accounts,

+ 14 - 4
src/codegen/constructor.rs

@@ -8,6 +8,8 @@ use crate::sema::{
     ast,
     ast::{CallArgs, Function, Namespace, Type},
 };
+use crate::Target;
+use num_bigint::{BigInt, Sign};
 use solang_parser::pt::Loc;
 
 use super::encoding::abi_encode;
@@ -44,10 +46,18 @@ pub(super) fn call_constructor(
         .salt
         .as_ref()
         .map(|e| expression(e, cfg, callee_contract_no, func, ns, vartab, opt));
-    let address = call_args
-        .address
-        .as_ref()
-        .map(|e| expression(e, cfg, callee_contract_no, func, ns, vartab, opt));
+    let address = if ns.target == Target::Solana {
+        Some(Expression::NumberLiteral {
+            loc: Loc::Codegen,
+            ty: Type::Address(false),
+            value: BigInt::from_bytes_be(
+                Sign::Plus,
+                ns.contracts[contract_no].program_id.as_ref().unwrap(),
+            ),
+        })
+    } else {
+        None
+    };
     let seeds = call_args
         .seeds
         .as_ref()

+ 11 - 4
src/codegen/dispatch/solana.rs

@@ -12,6 +12,7 @@ use num_traits::Zero;
 use solang_parser::{pt, pt::Loc};
 
 use crate::codegen::encoding::{abi_decode, abi_encode};
+use crate::sema::solana_accounts::BuiltinAccounts;
 
 pub const SOLANA_DISPATCH_CFG_NAME: &str = "solang_dispatch";
 
@@ -268,13 +269,19 @@ fn add_function_dispatch_case(
     let entry = cfg.new_basic_block(format!("function_cfg_{cfg_no}"));
     cfg.set_basic_block(entry);
 
-    let needs_account = if let ASTFunction::SolidityFunction(func_no) = func_cfg.function_no {
-        !ns.functions[func_no].is_pure()
+    let ast_func_no = if let ASTFunction::SolidityFunction(func_no) = func_cfg.function_no {
+        func_no
+    } else if let Some(func_no) = func_cfg.modifier {
+        func_no
     } else {
-        true
+        unreachable!("should not dispatch this function")
     };
 
-    if needs_account {
+    if ns.functions[ast_func_no]
+        .solana_accounts
+        .borrow()
+        .contains_key(BuiltinAccounts::DataAccount.as_str())
+    {
         check_magic(ns.contracts[contract_no].selector(), cfg, vartab);
     }
 

+ 14 - 1
src/codegen/expression.rs

@@ -28,7 +28,7 @@ use crate::sema::{
     expression::ResolveTo,
 };
 use crate::Target;
-use num_bigint::BigInt;
+use num_bigint::{BigInt, Sign};
 use num_traits::{FromPrimitive, One, ToPrimitive, Zero};
 use solang_parser::pt;
 use solang_parser::pt::{CodeLocation, Loc};
@@ -1589,6 +1589,7 @@ fn payable_send(
         cfg.add(
             vartab,
             Instr::ExternalCall {
+                loc: *loc,
                 success: Some(success),
                 address: Some(address),
                 accounts: None,
@@ -1658,6 +1659,7 @@ fn payable_transfer(
         cfg.add(
             vartab,
             Instr::ExternalCall {
+                loc: *loc,
                 success: None,
                 accounts: None,
                 seeds: None,
@@ -2162,6 +2164,14 @@ fn expr_builtin(
             }
         }
         ast::Builtin::GetAddress => {
+            if let Some(constant_id) = &ns.contracts[contract_no].program_id {
+                return Expression::NumberLiteral {
+                    loc: *loc,
+                    ty: Type::Address(false),
+                    value: BigInt::from_bytes_be(Sign::Plus, constant_id),
+                };
+            }
+
             // In emit, GetAddress returns a pointer to the address
             let codegen_expr = Expression::Builtin {
                 loc: *loc,
@@ -2794,6 +2804,7 @@ pub fn emit_function_call(
             cfg.add(
                 vartab,
                 Instr::ExternalCall {
+                    loc: *loc,
                     success: Some(success),
                     address: Some(address),
                     payload: args,
@@ -2908,6 +2919,7 @@ pub fn emit_function_call(
                 cfg.add(
                     vartab,
                     Instr::ExternalCall {
+                        loc: *loc,
                         success,
                         accounts,
                         address: Some(address),
@@ -2994,6 +3006,7 @@ pub fn emit_function_call(
                 cfg.add(
                     vartab,
                     Instr::ExternalCall {
+                        loc: *loc,
                         success,
                         accounts: None,
                         seeds: None,

+ 1 - 2
src/codegen/mod.rs

@@ -179,6 +179,7 @@ pub fn codegen(ns: &mut Namespace, opt: &Options) {
             }
         }
     }
+    ns.diagnostics.sort_and_dedup();
 }
 
 fn contract(contract_no: usize, ns: &mut Namespace, opt: &Options) {
@@ -1755,7 +1756,6 @@ pub enum Builtin {
     Ripemd160,
     Sender,
     Slot,
-    ProgramId,
     Sha256,
     Signature,
     SignatureVerify,
@@ -1813,7 +1813,6 @@ impl From<&ast::Builtin> for Builtin {
             ast::Builtin::Ripemd160 => Builtin::Ripemd160,
             ast::Builtin::Sender => Builtin::Sender,
             ast::Builtin::Slot => Builtin::Slot,
-            ast::Builtin::ProgramId => Builtin::ProgramId,
             ast::Builtin::Sha256 => Builtin::Sha256,
             ast::Builtin::Signature => Builtin::Signature,
             ast::Builtin::SignatureVerify => Builtin::SignatureVerify,

+ 115 - 86
src/codegen/solana_accounts/account_collection.rs

@@ -3,12 +3,13 @@
 use crate::codegen::cfg::{ASTFunction, ControlFlowGraph, Instr, InternalCallTy};
 use crate::codegen::solana_accounts::account_from_number;
 use crate::codegen::{Builtin, Expression};
-use crate::sema::ast::{Contract, Function, Mutability, Namespace, SolanaAccount};
+use crate::sema::ast::{Contract, Function, Namespace, SolanaAccount};
 use crate::sema::diagnostics::Diagnostics;
 use crate::sema::solana_accounts::BuiltinAccounts;
 use crate::sema::Recurse;
 use indexmap::IndexSet;
 use solang_parser::diagnostics::Diagnostic;
+use solang_parser::pt;
 use solang_parser::pt::{FunctionTy, Loc};
 use std::collections::{HashSet, VecDeque};
 
@@ -33,7 +34,7 @@ struct RecurseData<'a> {
 
 impl RecurseData<'_> {
     /// Add an account to the function's indexmap
-    fn add_account(&mut self, account_name: String, account: SolanaAccount) {
+    fn add_account(&mut self, account_name: String, account: &SolanaAccount) {
         let (is_signer, is_writer) = self.functions[self.ast_no]
             .solana_accounts
             .borrow()
@@ -63,7 +64,7 @@ impl RecurseData<'_> {
     fn add_system_account(&mut self) {
         self.add_account(
             BuiltinAccounts::SystemAccount.to_string(),
-            SolanaAccount {
+            &SolanaAccount {
                 loc: Loc::Codegen,
                 is_writer: false,
                 is_signer: false,
@@ -86,41 +87,17 @@ pub(crate) fn collect_accounts_from_contract(contract_no: usize, ns: &Namespace)
             )
         {
             let func = &ns.functions[*func_no];
-            match &func.mutability {
-                Mutability::Pure(_) => (),
-                Mutability::View(_) => {
-                    let (idx, _) = func.solana_accounts.borrow_mut().insert_full(
-                        BuiltinAccounts::DataAccount.to_string(),
-                        SolanaAccount {
-                            loc: Loc::Codegen,
-                            is_writer: false,
-                            is_signer: false,
-                            generated: true,
-                        },
-                    );
-                    func.solana_accounts.borrow_mut().move_index(idx, 0);
-                }
-                _ => {
-                    let (idx, _) = func.solana_accounts.borrow_mut().insert_full(
-                        BuiltinAccounts::DataAccount.to_string(),
-                        SolanaAccount {
-                            loc: Loc::Codegen,
-                            is_writer: true,
-                            /// With a @payer annotation, the account is created on-chain and needs a signer. The client
-                            /// provides an address that does not exist yet, so SystemProgram.CreateAccount is called
-                            /// on-chain.
-                            ///
-                            /// However, if a @seed is also provided, the program can sign for the account
-                            /// with the seed using program derived address (pda) when SystemProgram.CreateAccount is called,
-                            /// so no signer is required from the client.
-                            is_signer: func.has_payer_annotation() && !func.has_seed_annotation(),
-                            generated: true,
-                        },
-                    );
-
-                    func.solana_accounts.borrow_mut().move_index(idx, 0);
-                }
+            let index = func
+                .solana_accounts
+                .borrow()
+                .get_index_of(BuiltinAccounts::DataAccount.as_str());
+            if let Some(data_account_index) = index {
+                // Enforce the data account to be the first
+                func.solana_accounts
+                    .borrow_mut()
+                    .move_index(data_account_index, 0);
             }
+
             if func.is_constructor() && func.has_payer_annotation() {
                 func.solana_accounts.borrow_mut().insert(
                     BuiltinAccounts::SystemAccount.to_string(),
@@ -234,7 +211,7 @@ fn check_instruction(instr: &Instr, data: &mut RecurseData) {
                         let accounts_to_add =
                             data.functions[*ast_no].solana_accounts.borrow().clone();
                         for (account_name, account) in accounts_to_add {
-                            data.add_account(account_name, account);
+                            data.add_account(account_name, &account);
                         }
                     }
                     _ => (),
@@ -324,6 +301,7 @@ fn check_instruction(instr: &Instr, data: &mut RecurseData) {
             value.recurse(data, check_expression);
         }
         Instr::Constructor {
+            loc,
             encoded_args,
             value,
             gas,
@@ -332,6 +310,7 @@ fn check_instruction(instr: &Instr, data: &mut RecurseData) {
             seeds,
             accounts,
             constructor_no,
+            contract_no,
             ..
         } => {
             encoded_args.recurse(data, check_expression);
@@ -354,43 +333,14 @@ fn check_instruction(instr: &Instr, data: &mut RecurseData) {
                 // If the one passes the AccountMeta vector to the constructor call, there is no
                 // need to collect accounts for the IDL.
                 if let Some(constructor_no) = constructor_no {
-                    let accounts_to_add = data.functions[*constructor_no]
-                        .solana_accounts
-                        .borrow()
-                        .clone();
-                    for (name, account) in accounts_to_add {
-                        if name == BuiltinAccounts::DataAccount {
-                            continue;
-                        }
-
-                        if let Some(other_account) = data.functions[data.ast_no]
-                            .solana_accounts
-                            .borrow()
-                            .get(&name)
-                        {
-                            // If the compiler did not generate this account entry, we have a name
-                            // collision.
-                            if !other_account.generated {
-                                data.diagnostics.push(
-                                    Diagnostic::error_with_note(
-                                        other_account.loc,
-                                        "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".to_string(),
-                                        account.loc,
-                                        "other declaration".to_string()
-                                    )
-                                );
-                            }
-                        }
-                        data.add_account(name, account);
-                    }
+                    transfer_accounts(loc, *contract_no, *constructor_no, data, false);
                 }
             }
 
             data.add_system_account();
         }
         Instr::ExternalCall {
+            loc,
             address,
             accounts,
             seeds,
@@ -400,6 +350,14 @@ fn check_instruction(instr: &Instr, data: &mut RecurseData) {
             contract_function_no,
             ..
         } => {
+            // When we generate an external call in codegen, we have already taken care of the
+            // accounts we need (the payer for deploying creating the data accounts and the system
+            // program).
+            if *loc == Loc::Codegen {
+                return;
+            }
+
+            let mut program_id_populated = false;
             if let Some(address) = address {
                 address.recurse(data, check_expression);
                 if let Expression::NumberLiteral { value, .. } = address {
@@ -407,19 +365,17 @@ fn check_instruction(instr: &Instr, data: &mut RecurseData) {
                     if let Some(account) = account_from_number(value) {
                         data.add_account(
                             account,
-                            SolanaAccount {
+                            &SolanaAccount {
                                 loc: Loc::Codegen,
                                 is_signer: false,
                                 is_writer: false,
                                 generated: true,
                             },
                         );
+                        program_id_populated = true;
                     }
                 }
             }
-            if let Some(accounts) = accounts {
-                accounts.recurse(data, check_expression);
-            }
             if let Some(seeds) = seeds {
                 seeds.recurse(data, check_expression);
             }
@@ -428,17 +384,11 @@ fn check_instruction(instr: &Instr, data: &mut RecurseData) {
             gas.recurse(data, check_expression);
             // External calls always need the system account
             data.add_system_account();
-            if let Some((contract_no, function_no)) = contract_function_no {
-                let cfg_no = data.contracts[*contract_no].all_functions[function_no];
-                let accounts_to_add = data.functions[*function_no]
-                    .solana_accounts
-                    .borrow()
-                    .clone();
-                for (account_name, account) in accounts_to_add {
-                    data.add_account(account_name, account);
-                }
-                data.next_queue.insert((*contract_no, cfg_no));
-                data.next_queue.insert((data.contract_no, data.cfg_func_no));
+
+            if let Some(accounts) = accounts {
+                accounts.recurse(data, check_expression);
+            } else if let Some((contract_no, function_no)) = contract_function_no {
+                transfer_accounts(loc, *contract_no, *function_no, data, program_id_populated);
             }
         }
         Instr::EmitEvent {
@@ -471,7 +421,7 @@ fn check_expression(expr: &Expression, data: &mut RecurseData) -> bool {
         } => {
             data.add_account(
                 BuiltinAccounts::ClockAccount.to_string(),
-                SolanaAccount {
+                &SolanaAccount {
                     loc: Loc::Codegen,
                     is_signer: false,
                     is_writer: false,
@@ -485,7 +435,7 @@ fn check_expression(expr: &Expression, data: &mut RecurseData) -> bool {
         } => {
             data.add_account(
                 BuiltinAccounts::InstructionAccount.to_string(),
-                SolanaAccount {
+                &SolanaAccount {
                     loc: Loc::Codegen,
                     is_writer: false,
                     is_signer: false,
@@ -505,3 +455,82 @@ fn check_expression(expr: &Expression, data: &mut RecurseData) -> bool {
 
     true
 }
+
+/// When we make an external call from function A to function B, function A must know all the
+/// accounts function B needs. The 'transfer_accounts' function takes care to transfer the accounts
+/// from B's IDL to A's IDL.
+fn transfer_accounts(
+    loc: &pt::Loc,
+    contract_no: usize,
+    function_no: usize,
+    data: &mut RecurseData,
+    program_id_present: bool,
+) {
+    let accounts_to_add = data.functions[function_no].solana_accounts.borrow().clone();
+
+    for (name, mut account) in accounts_to_add {
+        if name == BuiltinAccounts::DataAccount {
+            let idl_name = format!("{}_dataAccount", data.contracts[contract_no].name);
+            if let Some(acc) = data.functions[data.ast_no]
+                .solana_accounts
+                .borrow()
+                .get(&idl_name)
+            {
+                if acc.loc != *loc {
+                    data.diagnostics.push(
+                        Diagnostic::error_with_note(
+                            *loc,
+                            format!("contract '{}' is called more than once in this function, so automatic account collection cannot happen. \
+                                         Please, provide the necessary accounts using the {{accounts:..}} call argument", data.contracts[contract_no].name),
+                            acc.loc,
+                            "other call".to_string(),
+                        )
+                    );
+                }
+                continue;
+            }
+            account.loc = *loc;
+            data.add_account(idl_name, &account);
+            continue;
+        }
+
+        if let Some(other_account) = data.functions[data.ast_no]
+            .solana_accounts
+            .borrow()
+            .get(&name)
+        {
+            if !other_account.generated {
+                data.diagnostics.push(
+                    Diagnostic::error_with_note(
+                        other_account.loc,
+                        "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".to_string(),
+                        account.loc,
+                        "other declaration".to_string(),
+                    )
+                );
+            }
+        }
+        data.add_account(name, &account);
+    }
+
+    if !program_id_present {
+        data.functions[data.ast_no]
+            .solana_accounts
+            .borrow_mut()
+            .insert(
+                format!("{}_programId", data.contracts[contract_no].name),
+                SolanaAccount {
+                    is_signer: false,
+                    is_writer: false,
+                    generated: true,
+                    loc: *loc,
+                },
+            );
+    }
+
+    let cfg_no = data.contracts[contract_no].all_functions[&function_no];
+    data.next_queue.insert((contract_no, cfg_no));
+    data.next_queue.insert((data.contract_no, data.cfg_func_no));
+}

+ 101 - 78
src/codegen/solana_accounts/account_management.rs

@@ -1,12 +1,11 @@
 // SPDX-License-Identifier: Apache-2.0
 
-use crate::codegen::cfg::{ControlFlowGraph, Instr};
+use crate::codegen::cfg::Instr;
 use crate::codegen::dispatch::solana::SOLANA_DISPATCH_CFG_NAME;
 use crate::codegen::{Builtin, Expression};
-use crate::sema::ast::{ArrayLength, Function, Namespace, StructType, Type};
+use crate::sema::ast::{ArrayLength, Contract, Function, Namespace, StructType, Type};
 use crate::sema::solana_accounts::BuiltinAccounts;
 use num_bigint::BigInt;
-use num_traits::Zero;
 use solang_parser::pt::Loc;
 use std::collections::{HashSet, VecDeque};
 
@@ -26,8 +25,10 @@ pub(crate) fn manage_contract_accounts(contract_no: usize, ns: &mut Namespace) {
             .copied()
             .unwrap();
         traverse_cfg(
-            &mut ns.contracts[contract_no].cfg[cfg_no],
+            &mut ns.contracts,
+            contract_no,
             &ns.functions,
+            cfg_no,
             *function_no,
         );
     }
@@ -35,16 +36,28 @@ pub(crate) fn manage_contract_accounts(contract_no: usize, ns: &mut Namespace) {
     if let Some(constructor) = constructor_no {
         let dispatch = ns.contracts[contract_no]
             .cfg
-            .iter_mut()
-            .find(|cfg| cfg.name == SOLANA_DISPATCH_CFG_NAME)
+            .iter()
+            .position(|cfg| cfg.name == SOLANA_DISPATCH_CFG_NAME)
             .expect("dispatch CFG is always generated");
-        traverse_cfg(dispatch, &ns.functions, constructor);
+        traverse_cfg(
+            &mut ns.contracts,
+            contract_no,
+            &ns.functions,
+            dispatch,
+            constructor,
+        );
     }
 }
 
 /// This function walks over the CFG to process its instructions for the account management.
-fn traverse_cfg(cfg: &mut ControlFlowGraph, functions: &[Function], ast_no: usize) {
-    if cfg.blocks.is_empty() {
+fn traverse_cfg(
+    contracts: &mut [Contract],
+    contract_no: usize,
+    functions: &[Function],
+    cfg_no: usize,
+    ast_no: usize,
+) {
+    if contracts[contract_no].cfg[cfg_no].blocks.is_empty() {
         return;
     }
 
@@ -54,11 +67,22 @@ fn traverse_cfg(cfg: &mut ControlFlowGraph, functions: &[Function], ast_no: usiz
     visited.insert(0);
 
     while let Some(cur_block) = queue.pop_front() {
-        for instr in cfg.blocks[cur_block].instr.iter_mut() {
-            process_instruction(instr, functions, ast_no);
+        for instr_no in 0..contracts[contract_no].cfg[cfg_no].blocks[cur_block]
+            .instr
+            .len()
+        {
+            process_instruction(
+                cfg_no,
+                instr_no,
+                cur_block,
+                functions,
+                contracts,
+                ast_no,
+                contract_no,
+            );
         }
 
-        for edge in cfg.blocks[cur_block].successors() {
+        for edge in contracts[contract_no].cfg[cfg_no].blocks[cur_block].successors() {
             if !visited.contains(&edge) {
                 queue.push_back(edge);
                 visited.insert(edge);
@@ -69,48 +93,45 @@ fn traverse_cfg(cfg: &mut ControlFlowGraph, functions: &[Function], ast_no: usiz
 
 /// This function processes the instruction, creating the AccountMeta array when possible.
 /// Presently, we only check the Instr::Constructor, but more will come later.
-fn process_instruction(instr: &mut Instr, functions: &[Function], ast_no: usize) {
-    if let Instr::Constructor {
-        accounts,
-        address,
-        constructor_no,
-        ..
-    } = instr
-    {
-        if accounts.is_some() || constructor_no.is_none() {
-            return;
+fn process_instruction(
+    cfg_no: usize,
+    instr_no: usize,
+    block_no: usize,
+    functions: &[Function],
+    contracts: &mut [Contract],
+    ast_no: usize,
+    contract_no: usize,
+) {
+    let instr = &mut contracts[contract_no].cfg[cfg_no].blocks[block_no].instr[instr_no];
+    match instr {
+        Instr::Constructor {
+            accounts,
+            constructor_no: Some(func_no),
+            contract_no,
+            ..
         }
+        | Instr::ExternalCall {
+            accounts,
+            contract_function_no: Some((contract_no, func_no)),
+            ..
+        } => {
+            if accounts.is_some() {
+                return;
+            }
 
-        let mut account_metas: Vec<Expression> = Vec::new();
-        let constructor_func = &functions[constructor_no.unwrap()];
-        for (name, account) in constructor_func.solana_accounts.borrow().iter() {
-            if name == BuiltinAccounts::DataAccount {
-                let address_ref = Expression::GetRef {
-                    loc: Loc::Codegen,
-                    ty: Type::Ref(Box::new(Type::Address(false))),
-                    expr: Box::new(address.as_ref().unwrap().clone()),
-                };
-                let struct_literal =
-                    account_meta_literal(address_ref, account.is_signer, account.is_writer);
-                account_metas.push(struct_literal);
-            } else if name == BuiltinAccounts::SystemAccount {
-                let system_address = Expression::NumberLiteral {
-                    loc: Loc::Codegen,
-                    ty: Type::Address(false),
-                    value: BigInt::zero(),
-                };
-                let system_ref = Expression::GetRef {
-                    loc: Loc::Codegen,
-                    ty: Type::Ref(Box::new(Type::Address(false))),
-                    expr: Box::new(system_address),
+            let mut account_metas: Vec<Expression> = Vec::new();
+            let constructor_func = &functions[*func_no];
+            for (name, account) in constructor_func.solana_accounts.borrow().iter() {
+                let name_to_index = if name == BuiltinAccounts::DataAccount {
+                    format!("{}_dataAccount", contracts[*contract_no].name)
+                } else {
+                    name.clone()
                 };
-                let struct_literal = account_meta_literal(system_ref, false, false);
-                account_metas.push(struct_literal);
-            } else {
+
                 let account_index = functions[ast_no]
                     .solana_accounts
                     .borrow()
-                    .get_index_of(name)
+                    .get_index_of(&name_to_index)
                     .unwrap();
                 let ptr_to_address = accounts_vector_key_at_index(account_index);
                 account_metas.push(account_meta_literal(
@@ -119,38 +140,40 @@ fn process_instruction(instr: &mut Instr, functions: &[Function], ast_no: usize)
                     account.is_writer,
                 ));
             }
+
+            let metas_vector = Expression::ArrayLiteral {
+                loc: Loc::Codegen,
+                ty: Type::Array(
+                    Box::new(Type::Struct(StructType::AccountMeta)),
+                    vec![ArrayLength::Fixed(BigInt::from(account_metas.len()))],
+                ),
+                dimensions: vec![account_metas.len() as u32],
+                values: account_metas,
+            };
+            *accounts = Some(metas_vector);
+        }
+        Instr::AccountAccess { loc, name, var_no } => {
+            // This could have been an Expression::AccountAccess if we had a three-address form.
+            // The amount of code necessary to traverse all Instructions and all expressions recursively
+            // (Expressions form a tree) makes the usage of Expression::AccountAccess too burdensome.
+
+            // Alternatively, we can create a codegen::Expression::AccountAccess when we have the
+            // new SSA IR complete.
+            let account_index = functions[ast_no]
+                .solana_accounts
+                .borrow()
+                .get_index_of(name)
+                .unwrap();
+            let expr = index_accounts_vector(account_index);
+
+            *instr = Instr::Set {
+                loc: *loc,
+                res: *var_no,
+                expr,
+            };
         }
-        let metas_vector = Expression::ArrayLiteral {
-            loc: Loc::Codegen,
-            ty: Type::Array(
-                Box::new(Type::Struct(StructType::AccountMeta)),
-                vec![ArrayLength::Fixed(BigInt::from(account_metas.len()))],
-            ),
-            dimensions: vec![account_metas.len() as u32],
-            values: account_metas,
-        };
-
-        *address = None;
-        *accounts = Some(metas_vector);
-    } else if let Instr::AccountAccess { loc, name, var_no } = instr {
-        // This could have been an Expression::AccountAccess if we had a three-address form.
-        // The amount of code necessary to traverse all Instructions and all expressions recursively
-        // (Expressions form a tree) makes the usage of Expression::AccountAccess too burdensome.
-
-        // Alternatively, we can create a codegen::Expression::AccountAccess when we have the
-        // new SSA IR complete.
-        let account_index = functions[ast_no]
-            .solana_accounts
-            .borrow()
-            .get_index_of(name)
-            .unwrap();
-        let expr = index_accounts_vector(account_index);
 
-        *instr = Instr::Set {
-            loc: *loc,
-            res: *var_no,
-            expr,
-        };
+        _ => (),
     }
 }
 

+ 38 - 19
src/codegen/solana_deploy.rs

@@ -11,6 +11,7 @@ use crate::sema::ast::{
     self, ArrayLength, CallTy, ConstructorAnnotation, Function, FunctionAttributes, Namespace,
     StructType,
 };
+use crate::sema::solana_accounts::BuiltinAccounts;
 use base58::ToBase58;
 use num_bigint::{BigInt, Sign};
 use num_traits::{ToPrimitive, Zero};
@@ -49,12 +50,17 @@ pub(super) fn solana_deploy(
                 ty: Type::Address(false),
                 value: BigInt::from_bytes_be(Sign::Plus, program_id),
             }),
-            right: Box::new(Expression::Builtin {
+            right: Expression::Load {
                 loc: Loc::Codegen,
-                tys: vec![Type::Address(false)],
-                kind: Builtin::ProgramId,
-                args: Vec::new(),
-            }),
+                ty: Type::Address(false),
+                expr: Box::new(Expression::Builtin {
+                    loc: Loc::Codegen,
+                    tys: vec![Type::Ref(Box::new(Type::Address(false)))],
+                    kind: Builtin::GetAddress,
+                    args: Vec::new(),
+                }),
+            }
+            .into(),
         };
 
         let id_fail = cfg.new_basic_block("program_id_fail".to_string());
@@ -265,14 +271,30 @@ pub(super) fn solana_deploy(
                 var_no: account_info_var,
             },
         );
+        let data_account_info_var = vartab.temp_anonymous(&account_info_ty);
+        cfg.add(
+            vartab,
+            Instr::AccountAccess {
+                loc: Loc::Codegen,
+                name: BuiltinAccounts::DataAccount.to_string(),
+                var_no: data_account_info_var,
+            },
+        );
 
         let account_var = Expression::Variable {
             loc: Loc::Codegen,
-            ty: account_info_ty,
+            ty: account_info_ty.clone(),
             var_no: account_info_var,
         };
 
+        let data_acc_var = Expression::Variable {
+            loc: Loc::Codegen,
+            ty: account_info_ty,
+            var_no: data_account_info_var,
+        };
+
         let ptr_to_address = retrieve_key_from_account_info(account_var);
+        let ptr_to_data_acc = retrieve_key_from_account_info(data_acc_var);
 
         cfg.add(
             vartab,
@@ -285,16 +307,7 @@ pub(super) fn solana_deploy(
                     dimensions: vec![2],
                     values: vec![
                         account_meta_literal(ptr_to_address, true, true),
-                        account_meta_literal(
-                            Expression::Builtin {
-                                loc: Loc::Codegen,
-                                tys: vec![Type::Ref(Box::new(Type::Address(false)))],
-                                kind: Builtin::GetAddress,
-                                args: vec![],
-                            },
-                            true,
-                            true,
-                        ),
+                        account_meta_literal(ptr_to_data_acc, true, true),
                     ],
                 },
             },
@@ -460,11 +473,16 @@ pub(super) fn solana_deploy(
                         value: BigInt::from_bytes_be(Sign::Plus, program_id),
                     }
                 } else {
-                    Expression::Builtin {
+                    let addr_ptr = Expression::Builtin {
                         loc: Loc::Codegen,
-                        tys: vec![Type::Address(false)],
-                        kind: Builtin::ProgramId,
+                        tys: vec![Type::Ref(Box::new(Type::Address(false)))],
+                        kind: Builtin::GetAddress,
                         args: vec![],
+                    };
+                    Expression::Load {
+                        loc: Loc::Codegen,
+                        ty: Type::Address(false),
+                        expr: Box::new(addr_ptr),
                     }
                 },
                 offset: Expression::NumberLiteral {
@@ -537,6 +555,7 @@ pub(super) fn solana_deploy(
         cfg.add(
             vartab,
             Instr::ExternalCall {
+                loc: Loc::Codegen,
                 success: None,
                 seeds,
                 address: Some(Expression::NumberLiteral {

+ 1 - 0
src/codegen/statements/try_catch.rs

@@ -257,6 +257,7 @@ fn exec_try(
                 cfg.add(
                     vartab,
                     Instr::ExternalCall {
+                        loc: *loc,
                         success: Some(success),
                         address: Some(address),
                         accounts: None,

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

@@ -376,6 +376,7 @@ impl<'a, 'b: 'a> AvailableExpressionSet<'a> {
             }
 
             Instr::ExternalCall {
+                loc,
                 success,
                 address,
                 accounts,
@@ -404,6 +405,7 @@ impl<'a, 'b: 'a> AvailableExpressionSet<'a> {
                     .map(|expr| self.regenerate_expression(expr, ave, cst).1);
 
                 Instr::ExternalCall {
+                    loc: *loc,
                     success: *success,
                     address: new_address,
                     accounts: new_accounts,

+ 3 - 5
src/emit/solana/mod.rs

@@ -1054,7 +1054,6 @@ impl SolanaTarget {
     fn build_external_call<'b>(
         &self,
         binary: &Binary,
-        address: PointerValue<'b>,
         payload: PointerValue<'b>,
         payload_len: IntValue<'b>,
         contract_args: ContractArgs<'b>,
@@ -1077,16 +1076,16 @@ impl SolanaTarget {
                     seeds,
                     binary.builder.build_int_cast(
                         len,
-                        external_call.get_type().get_param_types()[5].into_int_type(),
+                        external_call.get_type().get_param_types()[4].into_int_type(),
                         "len",
                     ),
                 )
             })
             .unwrap_or((
-                external_call.get_type().get_param_types()[4]
+                external_call.get_type().get_param_types()[3]
                     .ptr_type(AddressSpace::default())
                     .const_null(),
-                external_call.get_type().get_param_types()[5]
+                external_call.get_type().get_param_types()[4]
                     .into_int_type()
                     .const_zero(),
             ));
@@ -1096,7 +1095,6 @@ impl SolanaTarget {
             &[
                 payload.into(),
                 payload_len.into(),
-                address.into(),
                 program_id.into(),
                 seeds.into(),
                 seeds_len.into(),

+ 19 - 61
src/emit/solana/target.rs

@@ -10,8 +10,7 @@ use crate::emit::{ContractArgs, TargetRuntime, Variable};
 use crate::sema::ast::{self, Namespace};
 use inkwell::types::{BasicType, BasicTypeEnum, IntType};
 use inkwell::values::{
-    ArrayValue, BasicMetadataValueEnum, BasicValue, BasicValueEnum, FunctionValue, IntValue,
-    PointerValue,
+    ArrayValue, BasicMetadataValueEnum, BasicValueEnum, FunctionValue, IntValue, PointerValue,
 };
 use inkwell::{AddressSpace, IntPredicate};
 use num_traits::ToPrimitive;
@@ -1350,11 +1349,11 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
     ) {
         let address = address.unwrap();
 
+        contract_args.program_id = Some(address);
         if contract_args.accounts.is_some() {
-            contract_args.program_id = Some(address);
             self.build_invoke_signed_c(binary, function, payload, payload_len, contract_args);
         } else {
-            self.build_external_call(binary, address, payload, payload_len, contract_args, ns);
+            self.build_external_call(binary, payload, payload_len, contract_args, ns);
         }
     }
 
@@ -1662,64 +1661,23 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
 
                 let parameters = self.sol_parameters(binary);
 
-                let key = unsafe {
-                    binary.builder.build_gep(
-                        binary
-                            .module
-                            .get_struct_type("struct.SolParameters")
-                            .unwrap(),
-                        parameters,
-                        &[
-                            binary.context.i32_type().const_int(0, false), // first SolParameters
-                            binary.context.i32_type().const_int(0, false), // first field of SolParameters
-                            binary.context.i32_type().const_int(0, false), // first element of ka[]
-                            binary.context.i32_type().const_int(0, false), // first field of SolAccountInfo (key)
-                        ],
-                        "key",
-                    )
-                };
-
-                let key_pointer = binary.builder.build_load(
-                    binary.address_type(ns).ptr_type(AddressSpace::default()),
-                    key,
-                    "key_pointer",
-                );
-
-                key_pointer.as_basic_value_enum()
-            }
-            codegen::Expression::Builtin {
-                kind: codegen::Builtin::ProgramId,
-                args,
-                ..
-            } => {
-                assert_eq!(args.len(), 0);
-
-                let parameters = self.sol_parameters(binary);
-
                 let sol_pubkey_type = binary.module.get_struct_type("struct.SolPubkey").unwrap();
-                let account_id = binary
-                    .builder
-                    .build_load(
-                        sol_pubkey_type.ptr_type(AddressSpace::default()),
-                        binary
-                            .builder
-                            .build_struct_gep(
-                                binary
-                                    .module
-                                    .get_struct_type("struct.SolParameters")
-                                    .unwrap(),
-                                parameters,
-                                4,
-                                "program_id",
-                            )
-                            .unwrap(),
-                        "program_id",
-                    )
-                    .into_pointer_value();
-
-                binary
-                    .builder
-                    .build_load(binary.address_type(ns), account_id, "program_id")
+                binary.builder.build_load(
+                    sol_pubkey_type.ptr_type(AddressSpace::default()),
+                    binary
+                        .builder
+                        .build_struct_gep(
+                            binary
+                                .module
+                                .get_struct_type("struct.SolParameters")
+                                .unwrap(),
+                            parameters,
+                            4,
+                            "program_id",
+                        )
+                        .unwrap(),
+                    "program_id",
+                )
             }
             codegen::Expression::Builtin {
                 kind: codegen::Builtin::Calldata,

+ 19 - 4
src/sema/ast.rs

@@ -342,7 +342,7 @@ pub struct Function {
 
 /// This struct represents a Solana account. There is no name field, because
 /// it is stored in a IndexMap<String, SolanaAccount> (see above)
-#[derive(Clone, Copy, Debug)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub struct SolanaAccount {
     pub loc: pt::Loc,
     pub is_signer: bool,
@@ -1208,7 +1208,6 @@ pub struct CallArgs {
     pub gas: Option<Box<Expression>>,
     pub salt: Option<Box<Expression>>,
     pub value: Option<Box<Expression>>,
-    pub address: Option<Box<Expression>>,
     pub accounts: Option<Box<Expression>>,
     pub seeds: Option<Box<Expression>>,
     pub flags: Option<Box<Expression>>,
@@ -1263,6 +1262,8 @@ impl Recurse for Expression {
                 | Expression::Not { expr, .. }
                 | Expression::BitwiseNot { expr, .. }
                 | Expression::Negate { expr, .. }
+                | Expression::GetRef { expr, .. }
+                | Expression::NamedMember { array: expr, .. }
                 | Expression::StructMember { expr, .. } => expr.recurse(cx, f),
 
                 Expression::Add { left, right, .. }
@@ -1371,7 +1372,22 @@ impl Recurse for Expression {
                     }
                 }
 
-                _ => (),
+                Expression::FormatString { format, .. } => {
+                    for (_, arg) in format {
+                        arg.recurse(cx, f);
+                    }
+                }
+
+                Expression::NumberLiteral { .. }
+                | Expression::InterfaceId { .. }
+                | Expression::InternalFunction { .. }
+                | Expression::ConstantVariable { .. }
+                | Expression::StorageVariable { .. }
+                | Expression::Variable { .. }
+                | Expression::RationalNumberLiteral { .. }
+                | Expression::CodeLiteral { .. }
+                | Expression::BytesLiteral { .. }
+                | Expression::BoolLiteral { .. } => (),
             }
         }
     }
@@ -1575,7 +1591,6 @@ pub enum Builtin {
     GasLimit,
     BlockNumber,
     Slot,
-    ProgramId,
     Timestamp,
     Calldata,
     Sender,

+ 1 - 12
src/sema/builtin.rs

@@ -309,7 +309,7 @@ static BUILTIN_FUNCTIONS: Lazy<[Prototype; 24]> = Lazy::new(|| {
 });
 
 // A list of all Solidity builtins variables
-static BUILTIN_VARIABLE: Lazy<[Prototype; 18]> = Lazy::new(|| {
+static BUILTIN_VARIABLE: Lazy<[Prototype; 17]> = Lazy::new(|| {
     [
         Prototype {
             builtin: Builtin::BlockCoinbase,
@@ -366,17 +366,6 @@ static BUILTIN_VARIABLE: Lazy<[Prototype; 18]> = Lazy::new(|| {
             doc: "Current slot number",
             constant: false,
         },
-        Prototype {
-            builtin: Builtin::ProgramId,
-            namespace: Some("tx"),
-            method: vec![],
-            name: "program_id",
-            params: vec![],
-            ret: vec![Type::Address(false)],
-            target: vec![Target::Solana],
-            doc: "Program ID of currently executing program",
-            constant: false,
-        },
         Prototype {
             builtin: Builtin::Timestamp,
             namespace: Some("block"),

+ 3 - 6
src/sema/contracts.rs

@@ -193,13 +193,9 @@ fn resolve_base_args(contracts: &[ContractDefinition], file_no: usize, ns: &mut
     // for every contract, if we have a base which resolved successfully, resolve any constructor args
     for contract in contracts {
         let context = ExprContext {
-            function_no: None,
-            contract_no: Some(contract.contract_no),
             file_no,
-            unchecked: false,
-            constant: false,
-            lvalue: false,
-            yul_function: false,
+            contract_no: Some(contract.contract_no),
+            ..Default::default()
         };
 
         for base in &contract.base {
@@ -231,6 +227,7 @@ fn resolve_base_args(contracts: &[ContractDefinition], file_no: usize, ns: &mut
                 }
             }
         }
+        context.drop();
     }
 
     ns.diagnostics.extend(diagnostics);

+ 0 - 3
src/sema/dotgraphviz.rs

@@ -1477,9 +1477,6 @@ impl Dot {
         if let Some(salt) = &call_args.salt {
             self.add_expression(salt, func, ns, node, String::from("salt"));
         }
-        if let Some(address) = &call_args.address {
-            self.add_expression(address, func, ns, node, String::from("address"));
-        }
         if let Some(accounts) = &call_args.accounts {
             self.add_expression(accounts, func, ns, node, String::from("accounts"));
         }

+ 48 - 10
src/sema/expression/constructor.rs

@@ -10,7 +10,7 @@ use crate::sema::unused_variable::used_variable;
 use crate::Target;
 use solang_parser::diagnostics::Diagnostic;
 use solang_parser::pt;
-use solang_parser::pt::CodeLocation;
+use solang_parser::pt::{CodeLocation, Visibility};
 use std::collections::BTreeMap;
 
 /// Resolve an new contract expression with positional arguments
@@ -60,15 +60,7 @@ fn constructor(
         return Err(());
     }
 
-    if ns.target == Target::Solana && ns.contracts[no].program_id.is_none() {
-        diagnostics.push(Diagnostic::error(
-            *loc,
-            format!(
-                "in order to instantiate contract '{}', a @program_id is required on contract '{}'",
-                ns.contracts[no].name, ns.contracts[no].name
-            ),
-        ));
-    }
+    solana_constructor_check(loc, no, diagnostics, context, &call_args, ns);
 
     // check for circular references
     if circular_reference(no, context_contract_no, ns) {
@@ -268,6 +260,8 @@ pub fn constructor_named_args(
         return Err(());
     }
 
+    solana_constructor_check(loc, no, diagnostics, context, &call_args, ns);
+
     // check for circular references
     if circular_reference(no, context_contract_no, ns) {
         diagnostics.push(Diagnostic::error(
@@ -580,3 +574,47 @@ pub(super) fn deprecated_constructor_arguments(
 
     Ok(())
 }
+
+/// When calling a constructor on Solana, we must verify it the contract we are instantiating has
+/// a program id annotation and require the accounts call argument if the call is inside a loop.
+fn solana_constructor_check(
+    loc: &pt::Loc,
+    constructor_contract_no: usize,
+    diagnostics: &mut Diagnostics,
+    context: &ExprContext,
+    call_args: &CallArgs,
+    ns: &Namespace,
+) {
+    if ns.target != Target::Solana {
+        return;
+    }
+
+    if ns.contracts[constructor_contract_no].program_id.is_none() {
+        diagnostics.push(Diagnostic::error(
+            *loc,
+            format!(
+                "in order to instantiate contract '{}', a @program_id is required on contract '{}'",
+                ns.contracts[constructor_contract_no].name,
+                ns.contracts[constructor_contract_no].name
+            ),
+        ));
+    }
+
+    if !context.in_a_loop() || call_args.accounts.is_some() {
+        return;
+    }
+
+    if let Some(function_no) = context.function_no {
+        if matches!(
+            ns.functions[function_no].visibility,
+            Visibility::External(_)
+        ) {
+            diagnostics.push(Diagnostic::error(
+                *loc,
+                "the {accounts: ..} call argument is needed since the constructor may be \
+                called multiple times"
+                    .to_string(),
+            ));
+        }
+    }
+}

+ 17 - 73
src/sema/expression/function_call.rs

@@ -2033,45 +2033,6 @@ pub(super) fn parse_call_args(
                     diagnostics,
                 )?));
             }
-            "address" => {
-                if ns.target != Target::Solana {
-                    diagnostics.push(Diagnostic::error(
-                        arg.loc,
-                        format!(
-                            "'address' not permitted for external calls or constructors on {}",
-                            ns.target
-                        ),
-                    ));
-                    return Err(());
-                }
-
-                if external_call {
-                    diagnostics.push(Diagnostic::error(
-                        arg.loc,
-                        "'address' not valid for external calls".to_string(),
-                    ));
-                    return Err(());
-                }
-
-                let ty = Type::Address(false);
-
-                let expr = expression(
-                    &arg.expr,
-                    context,
-                    ns,
-                    symtable,
-                    diagnostics,
-                    ResolveTo::Type(&ty),
-                )?;
-
-                res.address = Some(Box::new(expr.cast(
-                    &arg.expr.loc(),
-                    &ty,
-                    true,
-                    ns,
-                    diagnostics,
-                )?));
-            }
             "salt" => {
                 if ns.target == Target::Solana {
                     diagnostics.push(Diagnostic::error(
@@ -2219,40 +2180,23 @@ pub(super) fn parse_call_args(
     }
 
     // address is required on solana constructors
-    if ns.target == Target::Solana && !external_call {
-        if res.address.is_none() && res.accounts.is_none() {
-            diagnostics.push(Diagnostic::error(
-                *loc,
-                format!(
-                    "either 'address' or 'accounts' call argument is required on {}",
-                    ns.target
-                ),
-            ));
-            return Err(());
-        } else if res.address.is_some() && res.accounts.is_some() {
-            diagnostics.push(Diagnostic::error(
-                *loc,
-                "'address' and 'accounts' call arguments cannot be used together. \
-                The first address provided on the accounts vector must be the contract's address."
-                    .to_string(),
-            ));
-            return Err(());
-        } else if res.accounts.is_none()
-            && !matches!(
-                ns.functions[context.function_no.unwrap()].visibility,
-                Visibility::External(_)
-            )
-            && !ns.functions[context.function_no.unwrap()].is_constructor()
-        {
-            diagnostics.push(Diagnostic::error(
-                *loc,
-                "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"
-                    .to_string(),
-            ));
-            return Err(());
-        }
+    if ns.target == Target::Solana
+        && !external_call
+        && res.accounts.is_none()
+        && !matches!(
+            ns.functions[context.function_no.unwrap()].visibility,
+            Visibility::External(_)
+        )
+        && !ns.functions[context.function_no.unwrap()].is_constructor()
+    {
+        diagnostics.push(Diagnostic::error(
+            *loc,
+            "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"
+                .to_string(),
+        ));
+        return Err(());
     }
 
     Ok(res)

+ 4 - 0
src/sema/expression/member_access.rs

@@ -10,8 +10,10 @@ use crate::sema::expression::function_call::function_type;
 use crate::sema::expression::integers::bigint_to_expression;
 use crate::sema::expression::resolve_expression::expression;
 use crate::sema::expression::{ExprContext, ResolveTo};
+use crate::sema::solana_accounts::BuiltinAccounts;
 use crate::sema::symtable::Symtable;
 use crate::sema::unused_variable::{assigned_variable, used_variable};
+use crate::Target;
 use num_bigint::{BigInt, Sign};
 use num_traits::{FromPrimitive, One, Zero};
 use solang_parser::diagnostics::Diagnostic;
@@ -246,11 +248,13 @@ pub(super) fn member_access(
                 };
             } else if matches!(*elem_ty, Type::Struct(StructType::AccountInfo))
                 && context.function_no.is_some()
+                && ns.target == Target::Solana
             {
                 return if ns.functions[context.function_no.unwrap()]
                     .solana_accounts
                     .borrow()
                     .contains_key(&id.name)
+                    || id.name == BuiltinAccounts::DataAccount
                 {
                     Ok(Expression::NamedMember {
                         loc: *loc,

+ 20 - 0
src/sema/expression/mod.rs

@@ -66,6 +66,26 @@ pub struct ExprContext {
     pub lvalue: bool,
     /// Are we resolving a yul function (it cannot have external dependencies)
     pub yul_function: bool,
+    /// How many loops are we in? (i.e how many nested loops de we have?)
+    pub loop_nesting_level: usize,
+}
+
+impl ExprContext {
+    pub fn enter_loop(&mut self) {
+        self.loop_nesting_level += 1;
+    }
+
+    pub fn exit_loop(&mut self) {
+        self.loop_nesting_level -= 1;
+    }
+
+    pub fn in_a_loop(&self) -> bool {
+        self.loop_nesting_level > 0
+    }
+
+    pub fn drop(&self) {
+        assert_eq!(self.loop_nesting_level, 0);
+    }
 }
 
 impl Expression {

+ 6 - 0
src/sema/function_annotation.rs

@@ -255,6 +255,12 @@ pub(super) fn function_body_annotations(
                             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]

+ 102 - 9
src/sema/mutability.rs

@@ -8,6 +8,11 @@ use super::{
     yul::ast::{YulExpression, YulStatement},
     Recurse,
 };
+use crate::sema::ast::SolanaAccount;
+use crate::sema::solana_accounts::BuiltinAccounts;
+use crate::sema::yul::builtin::YulBuiltInFunction;
+use bitflags::bitflags;
+use solang_parser::pt::Loc;
 use solang_parser::{helpers::CodeLocation, pt};
 
 #[derive(PartialEq, PartialOrd)]
@@ -18,6 +23,15 @@ enum Access {
     Value,
 }
 
+bitflags! {
+    #[derive(PartialEq, Eq, Copy, Clone, Debug)]
+    struct DataAccountUsage: u8 {
+        const NONE = 0;
+        const READ = 1;
+        const WRITE = 2;
+    }
+}
+
 impl Access {
     fn increase_to(&mut self, other: Access) {
         if *self < other {
@@ -49,6 +63,7 @@ struct StateCheck<'a> {
     func: &'a Function,
     modifier: Option<pt::Loc>,
     ns: &'a Namespace,
+    data_account: DataAccountUsage,
 }
 
 impl<'a> StateCheck<'a> {
@@ -148,6 +163,7 @@ fn check_mutability(func: &Function, ns: &Namespace) -> Vec<Diagnostic> {
         func,
         modifier: None,
         ns,
+        data_account: DataAccountUsage::NONE,
     };
 
     for arg in &func.modifiers {
@@ -219,6 +235,38 @@ fn check_mutability(func: &Function, ns: &Namespace) -> Vec<Diagnostic> {
         }
     }
 
+    if state.data_account != DataAccountUsage::NONE {
+        func.solana_accounts.borrow_mut().insert(
+            BuiltinAccounts::DataAccount.to_string(),
+            SolanaAccount {
+                loc: Loc::Codegen,
+                is_signer: false,
+                is_writer: (state.data_account & DataAccountUsage::WRITE)
+                    == DataAccountUsage::WRITE,
+                generated: true,
+            },
+        );
+    }
+
+    if func.is_constructor() {
+        func.solana_accounts.borrow_mut().insert(
+            BuiltinAccounts::DataAccount.to_string(),
+            SolanaAccount {
+                loc: Loc::Codegen,
+                is_writer: true,
+                /// With a @payer annotation, the account is created on-chain and needs a signer. The client
+                /// provides an address that does not exist yet, so SystemProgram.CreateAccount is called
+                /// on-chain.
+                ///
+                /// However, if a @seed is also provided, the program can sign for the account
+                /// with the seed using program derived address (pda) when SystemProgram.CreateAccount is called,
+                /// so no signer is required from the client.
+                is_signer: func.has_payer_annotation() && !func.has_seed_annotation(),
+                generated: true,
+            },
+        );
+    }
+
     state.diagnostics
 }
 
@@ -260,7 +308,10 @@ fn recurse_statements(stmts: &[Statement], ns: &Namespace, state: &mut StateChec
             Statement::Expression(_, _, expr) => {
                 expr.recurse(state, read_expression);
             }
-            Statement::Delete(loc, _, _) => state.write(loc),
+            Statement::Delete(loc, _, _) => {
+                state.data_account |= DataAccountUsage::WRITE;
+                state.write(loc)
+            }
             Statement::Destructure(_, fields, expr) => {
                 // This is either a list or internal/external function call
                 expr.recurse(state, read_expression);
@@ -309,17 +360,31 @@ fn read_expression(expr: &Expression, state: &mut StateCheck) -> bool {
         | Expression::PostIncrement { expr, .. }
         | Expression::PostDecrement { expr, .. } => {
             expr.recurse(state, write_expression);
+            return false;
         }
         Expression::Assign { left, right, .. } => {
             right.recurse(state, read_expression);
             left.recurse(state, write_expression);
+            return false;
         }
-        Expression::StorageVariable { loc, .. } => state.read(loc),
         Expression::StorageArrayLength { loc, .. } | Expression::StorageLoad { loc, .. } => {
-            state.read(loc)
+            state.data_account |= DataAccountUsage::READ;
+            state.read(loc);
+            return false;
         }
         Expression::Subscript { loc, array_ty, .. } if array_ty.is_contract_storage() => {
-            state.read(loc)
+            state.data_account |= DataAccountUsage::READ;
+            state.read(loc);
+            return false;
+        }
+        Expression::Variable { ty, .. } => {
+            if ty.is_contract_storage() {
+                state.data_account |= DataAccountUsage::READ;
+            }
+        }
+        Expression::StorageVariable { loc, .. } => {
+            state.data_account |= DataAccountUsage::READ;
+            state.read(loc);
         }
         Expression::Builtin {
             kind: Builtin::FunctionSelector,
@@ -340,7 +405,6 @@ fn read_expression(expr: &Expression, state: &mut StateCheck) -> bool {
                 | Builtin::BlockNumber
                 | Builtin::Slot
                 | Builtin::Timestamp
-                | Builtin::ProgramId
                 | Builtin::BlockCoinbase
                 | Builtin::BlockDifficulty
                 | Builtin::BlockHash
@@ -354,6 +418,7 @@ fn read_expression(expr: &Expression, state: &mut StateCheck) -> bool {
                 | Builtin::Accounts,
             ..
         } => state.read(loc),
+
         Expression::Builtin {
             loc,
             kind: Builtin::PayableSend | Builtin::PayableTransfer | Builtin::SelfDestruct,
@@ -377,7 +442,10 @@ fn read_expression(expr: &Expression, state: &mut StateCheck) -> bool {
             kind: Builtin::ArrayPush | Builtin::ArrayPop,
             args,
             ..
-        } if args[0].ty().is_contract_storage() => state.write(loc),
+        } if args[0].ty().is_contract_storage() => {
+            state.data_account |= DataAccountUsage::WRITE;
+            state.write(loc)
+        }
 
         Expression::Constructor { loc, .. } => {
             state.write(loc);
@@ -398,11 +466,12 @@ fn read_expression(expr: &Expression, state: &mut StateCheck) -> bool {
             CallTy::Static => state.read(loc),
             CallTy::Delegate | CallTy::Regular => state.write(loc),
         },
-        _ => {
-            return true;
+        Expression::NamedMember { name, .. } if name == BuiltinAccounts::DataAccount => {
+            state.data_account |= DataAccountUsage::READ;
         }
+        _ => (),
     }
-    false
+    true
 }
 
 fn write_expression(expr: &Expression, state: &mut StateCheck) -> bool {
@@ -412,20 +481,33 @@ fn write_expression(expr: &Expression, state: &mut StateCheck) -> bool {
         }
         | Expression::Subscript { loc, array, .. } => {
             if array.ty().is_contract_storage() {
+                state.data_account |= DataAccountUsage::WRITE;
                 state.write(loc);
                 return false;
             }
         }
         Expression::Variable { loc, ty, var_no: _ } => {
             if ty.is_contract_storage() && !expr.ty().is_contract_storage() {
+                state.data_account |= DataAccountUsage::WRITE;
                 state.write(loc);
                 return false;
             }
         }
         Expression::StorageVariable { loc, .. } => {
+            state.data_account |= DataAccountUsage::WRITE;
             state.write(loc);
             return false;
         }
+        Expression::Builtin {
+            loc,
+            kind: Builtin::Accounts,
+            ..
+        } => {
+            state.write(loc);
+        }
+        Expression::NamedMember { name, .. } if name == BuiltinAccounts::DataAccount => {
+            state.data_account |= DataAccountUsage::WRITE;
+        }
         _ => (),
     }
 
@@ -504,6 +586,17 @@ fn check_expression_mutability_yul(expr: &YulExpression, state: &mut StateCheck)
             } else if builtin_ty.modify_state() {
                 state.write(loc);
             }
+
+            match builtin_ty {
+                YulBuiltInFunction::SStore => {
+                    state.data_account |= DataAccountUsage::WRITE;
+                }
+                YulBuiltInFunction::SLoad => {
+                    state.data_account |= DataAccountUsage::READ;
+                }
+                _ => (),
+            }
+
             true
         }
         YulExpression::FunctionCall(..) => true,

+ 2 - 0
src/sema/namespace.rs

@@ -1498,6 +1498,7 @@ impl Namespace {
             constant: true,
             lvalue: false,
             yul_function: false,
+            loop_nesting_level: 0,
         };
 
         let size_expr = expression(
@@ -1508,6 +1509,7 @@ impl Namespace {
             diagnostics,
             ResolveTo::Type(&Type::Uint(256)),
         )?;
+        context.drop();
 
         match size_expr.ty() {
             Type::Uint(_) | Type::Int(_) => {}

+ 9 - 4
src/sema/solana_accounts.rs

@@ -17,9 +17,9 @@ pub enum BuiltinAccounts {
     InstructionAccount,
 }
 
-impl ToString for BuiltinAccounts {
-    fn to_string(&self) -> String {
-        let str = match self {
+impl BuiltinAccounts {
+    pub fn as_str(&self) -> &'static str {
+        match self {
             BuiltinAccounts::ClockAccount => "clock",
             BuiltinAccounts::SystemAccount => "systemProgram",
             BuiltinAccounts::AssociatedTokenProgram => "associatedTokenProgram",
@@ -27,8 +27,13 @@ impl ToString for BuiltinAccounts {
             BuiltinAccounts::TokenProgramId => "tokenProgram",
             BuiltinAccounts::DataAccount => "dataAccount",
             BuiltinAccounts::InstructionAccount => "SysvarInstruction",
-        };
+        }
+    }
+}
 
+impl ToString for BuiltinAccounts {
+    fn to_string(&self) -> String {
+        let str = self.as_str();
         str.to_string()
     }
 }

+ 15 - 7
src/sema/statements.rs

@@ -41,7 +41,7 @@ pub fn resolve_function_body(
     let mut symtable = Symtable::new();
     let mut loops = LoopScopes::new();
     let mut res = Vec::new();
-    let context = ExprContext {
+    let mut context = ExprContext {
         file_no,
         contract_no,
         function_no: Some(function_no),
@@ -49,6 +49,7 @@ pub fn resolve_function_body(
         constant: false,
         lvalue: false,
         yul_function: false,
+        loop_nesting_level: 0,
     };
 
     let mut unresolved_annotation: Vec<UnresolvedAnnotation> = Vec::new();
@@ -220,6 +221,7 @@ pub fn resolve_function_body(
             }
         }
 
+        context.drop();
         ns.diagnostics.extend(diagnostics);
         ns.functions[function_no].modifiers = modifiers;
     }
@@ -285,7 +287,7 @@ pub fn resolve_function_body(
     let reachable = statement(
         body,
         &mut res,
-        &context,
+        &mut context,
         &mut symtable,
         &mut loops,
         ns,
@@ -342,7 +344,7 @@ pub fn resolve_function_body(
 fn statement(
     stmt: &pt::Statement,
     res: &mut Vec<Statement>,
-    context: &ExprContext,
+    context: &mut ExprContext,
     symtable: &mut Symtable,
     loops: &mut LoopScopes,
     ns: &mut Namespace,
@@ -432,7 +434,7 @@ fn statement(
                     ));
                     return Err(());
                 }
-                reachable = statement(stmt, res, &context, symtable, loops, ns, diagnostics)?;
+                reachable = statement(stmt, res, &mut context, symtable, loops, ns, diagnostics)?;
             }
 
             symtable.leave_scope();
@@ -464,6 +466,7 @@ fn statement(
             }
         }
         pt::Statement::While(loc, cond_expr, body) => {
+            context.enter_loop();
             let expr = expression(
                 cond_expr,
                 context,
@@ -491,10 +494,11 @@ fn statement(
             loops.leave_scope();
 
             res.push(Statement::While(*loc, true, cond, body_stmts));
-
+            context.exit_loop();
             Ok(true)
         }
         pt::Statement::DoWhile(loc, body, cond_expr) => {
+            context.enter_loop();
             let expr = expression(
                 cond_expr,
                 context,
@@ -522,6 +526,7 @@ fn statement(
             loops.leave_scope();
 
             res.push(Statement::DoWhile(*loc, true, body_stmts, cond));
+            context.exit_loop();
             Ok(true)
         }
         pt::Statement::If(loc, cond_expr, then, else_) => {
@@ -597,6 +602,7 @@ fn statement(
             }
 
             loops.new_scope();
+            context.enter_loop();
 
             let mut body = Vec::new();
 
@@ -637,7 +643,7 @@ fn statement(
                 cond: None,
                 body,
             });
-
+            context.exit_loop();
             Ok(reachable)
         }
         pt::Statement::For(loc, init_stmt, Some(cond_expr), next_expr, body_stmt) => {
@@ -659,6 +665,7 @@ fn statement(
                 )?;
             }
 
+            context.enter_loop();
             let cond = expression(
                 cond_expr,
                 context,
@@ -716,6 +723,7 @@ fn statement(
                 body,
             });
 
+            context.exit_loop();
             Ok(true)
         }
         pt::Statement::Return(loc, None) => {
@@ -2111,7 +2119,7 @@ fn try_catch(
     expr: &pt::Expression,
     returns_and_ok: &Option<(Vec<(pt::Loc, Option<pt::Parameter>)>, Box<pt::Statement>)>,
     clause_stmts: &[pt::CatchClause],
-    context: &ExprContext,
+    context: &mut ExprContext,
     symtable: &mut Symtable,
     loops: &mut LoopScopes,
     ns: &mut Namespace,

+ 224 - 0
src/sema/tests/data_account.rs

@@ -0,0 +1,224 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#![cfg(test)]
+
+use crate::file_resolver::FileResolver;
+use crate::sema::ast::{Namespace, SolanaAccount};
+use crate::{parse_and_resolve, Target};
+use solang_parser::pt::Loc;
+use std::ffi::OsStr;
+
+fn generate_namespace(src: &'static str) -> Namespace {
+    let mut cache = FileResolver::default();
+    cache.set_file_contents("test.sol", src.to_string());
+    parse_and_resolve(OsStr::new("test.sol"), &mut cache, Target::Solana)
+}
+
+#[test]
+fn read_account() {
+    let src = r#"
+    import 'solana';
+contract Test {
+    struct myStr {
+        uint64 a;
+        uint64 b;
+    }
+    mapping(string => myStr) mp;
+    int var1;
+    uint[] arr;
+
+    myStr ss1;
+    address add;
+
+    function read1() public view returns (int) {
+        return var1;
+    }
+
+    function read2() public view returns (uint32) {
+        return arr.length;
+    }
+
+    function read3() public view returns (uint[]) {
+        uint[] memory ret = arr;
+        return ret;
+    }
+
+    function read4(uint32 idx) public view returns (uint) {
+        return arr[idx];
+    }
+
+    function read5() public view returns (uint64) {
+        return ss1.a;
+    }
+
+    function read6() public view returns (address) {
+        return tx.accounts.dataAccount.key;
+    }
+
+    function read7() public view returns (address) {
+        AccountMeta[2] meta = [
+            AccountMeta({pubkey: add, is_signer: false, is_writable: true}),
+            AccountMeta({pubkey: address(this), is_signer: false, is_writable: true})
+        ];
+
+        return meta[0].pubkey;
+    }
+}
+    "#;
+    let ns = generate_namespace(src);
+
+    let data_account = SolanaAccount {
+        loc: Loc::Codegen,
+        is_writer: false,
+        is_signer: false,
+        generated: true,
+    };
+
+    let read1 = ns.functions.iter().find(|f| f.name == "read1").unwrap();
+    assert_eq!(
+        *read1.solana_accounts.borrow().get("dataAccount").unwrap(),
+        data_account
+    );
+
+    let read2 = ns.functions.iter().find(|f| f.name == "read2").unwrap();
+    assert_eq!(
+        *read2.solana_accounts.borrow().get("dataAccount").unwrap(),
+        data_account
+    );
+
+    let read3 = ns.functions.iter().find(|f| f.name == "read3").unwrap();
+    assert_eq!(
+        *read3.solana_accounts.borrow().get("dataAccount").unwrap(),
+        data_account
+    );
+
+    let read4 = ns.functions.iter().find(|f| f.name == "read4").unwrap();
+    assert_eq!(
+        *read4.solana_accounts.borrow().get("dataAccount").unwrap(),
+        data_account
+    );
+
+    let read5 = ns.functions.iter().find(|f| f.name == "read5").unwrap();
+    assert_eq!(
+        *read5.solana_accounts.borrow().get("dataAccount").unwrap(),
+        data_account
+    );
+
+    let read6 = ns.functions.iter().find(|f| f.name == "read6").unwrap();
+    assert_eq!(
+        *read6.solana_accounts.borrow().get("dataAccount").unwrap(),
+        data_account
+    );
+
+    let read7 = ns.functions.iter().find(|f| f.name == "read7").unwrap();
+    assert_eq!(
+        *read7.solana_accounts.borrow().get("dataAccount").unwrap(),
+        data_account
+    );
+}
+
+#[test]
+fn write_account() {
+    let src = r#"
+    contract Test {
+    struct myStr {
+        uint64 a;
+        uint64 b;
+    }
+    mapping(string => myStr) mp;
+    int var1;
+    uint[] arr;
+
+    myStr ss1;
+
+    function write1(string id) public {
+        delete mp[id];
+    }
+
+    function write2(uint n) public {
+        arr.push(n);
+    }
+
+    function write3() public {
+        arr.pop();
+    }
+
+    function write4(uint num, uint32 idx) public {
+        arr[idx] = num;
+    }
+
+    function write5(uint64 num) public {
+        ss1.b = num;
+    }
+
+    function write6(string id) public {
+        myStr storage ref = mp[id];
+        ref.a = 2;
+        ref.b = 78;
+    }
+
+    function write7(int num) public {
+        var1 = num;
+    }
+
+    function write8(uint64 val) public {
+        tx.accounts.dataAccount.lamports += val;
+    }
+}
+    "#;
+    let ns = generate_namespace(src);
+    let data_account = SolanaAccount {
+        loc: Loc::Codegen,
+        is_writer: true,
+        is_signer: false,
+        generated: true,
+    };
+
+    let write1 = ns.functions.iter().find(|f| f.name == "write1").unwrap();
+    assert_eq!(
+        *write1.solana_accounts.borrow().get("dataAccount").unwrap(),
+        data_account
+    );
+
+    let write2 = ns.functions.iter().find(|f| f.name == "write2").unwrap();
+    assert_eq!(
+        *write2.solana_accounts.borrow().get("dataAccount").unwrap(),
+        data_account
+    );
+
+    let write3 = ns.functions.iter().find(|f| f.name == "write3").unwrap();
+    assert_eq!(
+        *write3.solana_accounts.borrow().get("dataAccount").unwrap(),
+        data_account
+    );
+
+    let write4 = ns.functions.iter().find(|f| f.name == "write4").unwrap();
+    assert_eq!(
+        *write4.solana_accounts.borrow().get("dataAccount").unwrap(),
+        data_account
+    );
+
+    let write5 = ns.functions.iter().find(|f| f.name == "write5").unwrap();
+    assert_eq!(
+        *write5.solana_accounts.borrow().get("dataAccount").unwrap(),
+        data_account
+    );
+
+    let write6 = ns.functions.iter().find(|f| f.name == "write6").unwrap();
+    assert_eq!(
+        *write6.solana_accounts.borrow().get("dataAccount").unwrap(),
+        data_account
+    );
+
+    let write6 = ns.functions.iter().find(|f| f.name == "write6").unwrap();
+    assert_eq!(
+        *write6.solana_accounts.borrow().get("dataAccount").unwrap(),
+        data_account
+    );
+
+    let write7 = ns.functions.iter().find(|f| f.name == "write7").unwrap();
+    assert_eq!(
+        *write7.solana_accounts.borrow().get("dataAccount").unwrap(),
+        data_account
+    );
+}

+ 6 - 3
src/sema/tests/mod.rs

@@ -1,6 +1,9 @@
 // SPDX-License-Identifier: Apache-2.0
 
 #![cfg(test)]
+
+mod data_account;
+
 use crate::sema::ast::{Expression, Parameter, Statement, TryCatch, Type};
 use crate::sema::yul::ast::InlineAssembly;
 use crate::{parse_and_resolve, sema::ast, FileResolver, Target};
@@ -452,8 +455,8 @@ contract aborting {
 }
 
 contract runner {
-    function test(address a) external pure {
-        aborting abort = new aborting{address: a}();
+    function test() external pure {
+        aborting abort = new aborting();
 
         try abort.abort() returns (int32 a, bool b) {
             // call succeeded; return values are in a and b
@@ -606,7 +609,7 @@ contract Child {
     assert_eq!(errors.len(), 1);
     assert_eq!(
         errors[0].message,
-        "either 'address' or 'accounts' call argument is required on Solana"
+        "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"
     );
 }
 

+ 0 - 3
src/sema/unused_variable.rs

@@ -272,9 +272,6 @@ fn check_call_args(ns: &mut Namespace, call_args: &CallArgs, symtable: &mut Symt
     if let Some(value) = &call_args.value {
         used_variable(ns, value.as_ref(), symtable);
     }
-    if let Some(address) = &call_args.address {
-        used_variable(ns, address.as_ref(), symtable);
-    }
     if let Some(accounts) = &call_args.accounts {
         used_variable(ns, accounts.as_ref(), symtable);
     }

+ 3 - 5
src/sema/variables.rs

@@ -326,6 +326,7 @@ pub fn variable_decl<'a>(
                 constant,
                 lvalue: false,
                 yul_function: false,
+                loop_nesting_level: 0,
             };
             match expression(
                 initializer,
@@ -782,12 +783,8 @@ pub fn resolve_initializers(
 
         let context = ExprContext {
             file_no,
-            unchecked: false,
             contract_no: Some(*contract_no),
-            function_no: None,
-            constant: false,
-            lvalue: false,
-            yul_function: false,
+            ..Default::default()
         };
 
         if let Ok(res) = expression(
@@ -803,6 +800,7 @@ pub fn resolve_initializers(
                 ns.contracts[*contract_no].variables[*var_no].initializer = Some(res);
             }
         }
+        context.drop();
     }
 
     ns.diagnostics.extend(diagnostics);

+ 12 - 81
src/sema/yul/tests/expression.rs

@@ -23,15 +23,7 @@ use std::sync::Arc;
 
 #[test]
 fn resolve_bool_literal() {
-    let ctx = ExprContext {
-        file_no: 0,
-        contract_no: None,
-        function_no: None,
-        unchecked: false,
-        constant: false,
-        lvalue: false,
-        yul_function: false,
-    };
+    let ctx = ExprContext::default();
     let mut symtable = Symtable::new();
     let mut function_table = FunctionsTable::new(0);
 
@@ -71,15 +63,7 @@ fn resolve_bool_literal() {
 
 #[test]
 fn resolve_number_literal() {
-    let ctx = ExprContext {
-        file_no: 0,
-        contract_no: None,
-        function_no: None,
-        unchecked: false,
-        constant: false,
-        lvalue: false,
-        yul_function: false,
-    };
+    let ctx = ExprContext::default();
     let mut symtable = Symtable::new();
     let mut function_table = FunctionsTable::new(0);
 
@@ -133,15 +117,7 @@ fn resolve_number_literal() {
 
 #[test]
 fn resolve_hex_number_literal() {
-    let ctx = ExprContext {
-        file_no: 0,
-        contract_no: None,
-        function_no: None,
-        unchecked: false,
-        constant: false,
-        lvalue: false,
-        yul_function: false,
-    };
+    let ctx = ExprContext::default();
     let mut symtable = Symtable::new();
     let mut function_table = FunctionsTable::new(0);
 
@@ -184,15 +160,7 @@ fn resolve_hex_number_literal() {
 
 #[test]
 fn resolve_hex_string_literal() {
-    let ctx = ExprContext {
-        file_no: 0,
-        contract_no: None,
-        function_no: None,
-        unchecked: false,
-        constant: false,
-        lvalue: false,
-        yul_function: false,
-    };
+    let ctx = ExprContext::default();
     let mut symtable = Symtable::new();
     let mut function_table = FunctionsTable::new(0);
 
@@ -255,15 +223,7 @@ fn resolve_hex_string_literal() {
 
 #[test]
 fn resolve_string_literal() {
-    let ctx = ExprContext {
-        file_no: 0,
-        contract_no: None,
-        function_no: None,
-        unchecked: false,
-        constant: false,
-        lvalue: false,
-        yul_function: false,
-    };
+    let ctx = ExprContext::default();
     let mut symtable = Symtable::new();
     let mut function_table = FunctionsTable::new(0);
 
@@ -292,15 +252,7 @@ fn resolve_string_literal() {
 
 #[test]
 fn resolve_variable_local() {
-    let context = ExprContext {
-        file_no: 0,
-        contract_no: Some(0),
-        function_no: Some(0),
-        unchecked: false,
-        constant: false,
-        lvalue: false,
-        yul_function: false,
-    };
+    let context = ExprContext::default();
     let mut symtable = Symtable::new();
     let mut function_table = FunctionsTable::new(0);
     let mut ns = Namespace::new(Target::EVM);
@@ -378,6 +330,7 @@ fn resolve_variable_contract() {
         constant: false,
         lvalue: false,
         yul_function: false,
+        loop_nesting_level: 0,
     };
     let mut symtable = Symtable::new();
     let mut function_table = FunctionsTable::new(0);
@@ -530,15 +483,7 @@ fn resolve_variable_contract() {
 
 #[test]
 fn function_call() {
-    let context = ExprContext {
-        file_no: 0,
-        contract_no: Some(0),
-        function_no: Some(0),
-        unchecked: false,
-        constant: false,
-        lvalue: false,
-        yul_function: false,
-    };
+    let context = ExprContext::default();
     let mut symtable = Symtable::new();
     let mut function_table = FunctionsTable::new(0);
     function_table.new_scope();
@@ -691,15 +636,7 @@ fn function_call() {
 
 #[test]
 fn check_arguments() {
-    let context = ExprContext {
-        file_no: 0,
-        contract_no: Some(0),
-        function_no: Some(0),
-        unchecked: false,
-        constant: false,
-        lvalue: false,
-        yul_function: false,
-    };
+    let context = ExprContext::default();
     let mut symtable = Symtable::new();
     let mut function_table = FunctionsTable::new(0);
     function_table.new_scope();
@@ -840,7 +777,9 @@ fn test_member_access() {
         constant: false,
         lvalue: false,
         yul_function: false,
+        loop_nesting_level: 0,
     };
+
     let mut symtable = Symtable::new();
     let mut function_table = FunctionsTable::new(0);
     let mut ns = Namespace::new(Target::EVM);
@@ -938,15 +877,7 @@ fn test_check_types() {
         0,
     );
 
-    let context = ExprContext {
-        file_no: 0,
-        contract_no: Some(0),
-        function_no: Some(0),
-        unchecked: false,
-        constant: false,
-        lvalue: false,
-        yul_function: false,
-    };
+    let context = ExprContext::default();
 
     let mut ns = Namespace::new(Target::EVM);
     let mut contract = ast::Contract::new("test", ContractTy::Contract(loc), vec![], loc);

+ 7 - 53
stdlib/solana.c

@@ -60,8 +60,8 @@ uint64_t sol_invoke_signed_c(const SolInstruction *instruction, const SolAccount
 
 // 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 *address, SolPubkey *program_id,
-                       const SolSignerSeeds *seeds, int seeds_len, SolParameters *params)
+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 = {
@@ -72,62 +72,16 @@ uint64_t external_call(uint8_t *input, uint32_t input_len, SolPubkey *address, S
         .data_len = input_len,
     };
 
-    int meta_no = 1;
-    int new_address_idx = -1;
-
+    // 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];
-
-        // The address for the new contract should go first. Note that there
-        // may be duplicate entries, the order of those does not matter.
-        if (new_address_idx < 0 && SolPubkey_same(address, acc->key))
-        {
-            metas[0].pubkey = acc->key;
-            metas[0].is_writable = acc->is_writable;
-            metas[0].is_signer = acc->is_signer;
-            new_address_idx = account_no;
-        }
-        else
-        {
-            metas[meta_no].pubkey = acc->key;
-            metas[meta_no].is_writable = acc->is_writable;
-            metas[meta_no].is_signer = acc->is_signer;
-            meta_no += 1;
-        }
+        metas[account_no].pubkey = acc->key;
+        metas[account_no].is_writable = acc->is_writable;
+        metas[account_no].is_signer = acc->is_signer;
     }
 
-    // If the program_id is null, we are dealing with an external call
-    if (!program_id)
-    {
-        if (new_address_idx < 0)
-        {
-            sol_log("call to account not in transaction");
-            sol_panic();
-
-            return ERROR_INVALID_ACCOUNT_DATA;
-        }
-        else
-        {
-            instruction.program_id = params->ka[new_address_idx].owner;
-            return sol_invoke_signed_c(&instruction, params->ka, params->ka_num, NULL, 0);
-        }
-    }
-    else
-    {
-        // This is a constructor call
-        if (new_address_idx < 0)
-        {
-            sol_log("new account needed");
-            sol_panic();
-
-            return ERROR_NEW_ACCOUNT_NEEDED;
-        }
-        else
-        {
-            return sol_invoke_signed_c(&instruction, params->ka, params->ka_num, seeds, seeds_len);
-        }
-    }
+    return sol_invoke_signed_c(&instruction, params->ka, params->ka_num, seeds, seeds_len);
 }
 
 uint64_t *sol_account_lamport(uint8_t *address, SolParameters *params)

+ 3 - 3
tests/codegen_testcases/solidity/constructor_with_metas.sol

@@ -10,8 +10,8 @@ contract creator {
             AccountMeta({pubkey: child, is_signer: false, is_writable: false}),
             AccountMeta({pubkey: payer, is_signer: true, is_writable: true})
         ];
-        // CHECK: constructor(no: 4) salt: value: gas:uint64 0 address: seeds: Child encoded buffer: %abi_encoded.temp.17 accounts: %metas
-        c = new Child{accounts: metas}(payer);
+        // CHECK: constructor(no: 4) salt: value: gas:uint64 0 address:address 0xadde28d6c5697771bb24a668136224c7aac8e8ba974c2881484973b2e762fb74 seeds: Child encoded buffer: %abi_encoded.temp.15 accounts: %metas
+        c = new Child{accounts: metas}();
 
         c.say_hello();
     }
@@ -21,7 +21,7 @@ contract creator {
 contract Child {
     @payer(payer)
     @space(511 + 7)
-    constructor(address payer) {
+    constructor() {
         print("In child constructor");
     }
 

+ 3 - 3
tests/codegen_testcases/solidity/solana_bump.sol

@@ -9,7 +9,7 @@ contract C1 {
     }
     // BEGIN-CHECK: solang_dispatch
     // 25 must be the last seed in the call.
-    // CHECK: external call::regular address:address 0x0 payload:%instruction.temp.14 value:uint64 0 gas:uint64 0 accounts:%metas.temp.11 seeds:[1] [ [2] [ bytes(%my_seed), bytes(bytes from:bytes1 (bytes1 25)) ] ] contract|function:_ flags:
+    // CHECK: external call::regular address:address 0x0 payload:%instruction.temp.15 value:uint64 0 gas:uint64 0 accounts:%metas.temp.11 seeds:[1] [ [2] [ bytes(%my_seed), bytes(bytes from:bytes1 (bytes1 25)) ] ] contract|function:_ flags:
 }
 
 contract C2 {
@@ -23,7 +23,7 @@ contract C2 {
     }
     // BEGIN-CHECK: solang_dispatch
     // 12 must be the last seed in the call.
-    // CHECK: external call::regular address:address 0x0 payload:%instruction.temp.25 value:uint64 0 gas:uint64 0 accounts:%metas.temp.22 seeds:[1] [ [4] [ (alloc slice bytes1 uint32 5 "apple"), (alloc slice bytes1 uint32 9 "pine_tree"), bytes(%my_seed), bytes(bytes from:bytes1 (bytes1 12)) ] ] contract|function:_ flags:
+    // CHECK: external call::regular address:address 0x0 payload:%instruction.temp.27 value:uint64 0 gas:uint64 0 accounts:%metas.temp.23 seeds:[1] [ [4] [ (alloc slice bytes1 uint32 5 "apple"), (alloc slice bytes1 uint32 9 "pine_tree"), bytes(%my_seed), bytes(bytes from:bytes1 (bytes1 12)) ] ] contract|function:_ flags:
 }
 
 contract C3 {
@@ -36,5 +36,5 @@ contract C3 {
     }
     // BEGIN-CHECK: solang_dispatch
     // bp must be the last seed in the call
-    // CHECK: external call::regular address:address 0x0 payload:%instruction.temp.37 value:uint64 0 gas:uint64 0 accounts:%metas.temp.34 seeds:[1] [ [4] [ (alloc slice bytes1 uint32 9 "pineapple"), (alloc slice bytes1 uint32 7 "avocado"), bytes(%my_seed), bytes(bytes from:bytes1 (%bp)) ] ] contract|function:_ flags:
+    // CHECK: external call::regular address:address 0x0 payload:%instruction.temp.40 value:uint64 0 gas:uint64 0 accounts:%metas.temp.36 seeds:[1] [ [4] [ (alloc slice bytes1 uint32 9 "pineapple"), (alloc slice bytes1 uint32 7 "avocado"), bytes(%my_seed), bytes(bytes from:bytes1 (%bp)) ] ] contract|function:_ flags:
 }

+ 9 - 8
tests/codegen_testcases/solidity/solana_payer_account.sol

@@ -3,13 +3,13 @@
 @program_id("SoLDxXQ9GMoa15i4NavZc61XGkas2aom4aNiWT6KUER")
 contract Builder {
     Built other;
-    // BEGIN-CHECK: Builder::Builder::function::build_this__address
-    function build_this(address addr) external {
-        // CHECK: constructor(no: 4) salt: value: gas:uint64 0 address: seeds: Built encoded buffer: %abi_encoded.temp.18 accounts: [3] [ struct { (deref (arg #0), true, false }, struct { (load (struct (subscript struct AccountInfo[] (builtin Accounts ())[uint32 1]) field 0)), true, true }, struct { (deref address 0x0, false, false } ]
-        other = new Built{address: addr}("my_seed");
+    // BEGIN-CHECK: Builder::Builder::function::build_this
+    function build_this() external {
+        // CHECK: constructor(no: 4) salt: value: gas:uint64 0 address:address 0x69be884fd55a2306354c305323cc6b7ce91768be33d32a021155ef608806bcb seeds: Built encoded buffer: %abi_encoded.temp.18 accounts: [3] [ struct { (load (struct (subscript struct AccountInfo[] (builtin Accounts ())[uint32 2]) field 0)), true, false }, struct { (load (struct (subscript struct AccountInfo[] (builtin Accounts ())[uint32 1]) field 0)), true, true }, struct { (load (struct (subscript struct AccountInfo[] (builtin Accounts ())[uint32 4]) field 0)), false, false } ]
+        other = new Built("my_seed");
     }
 
-    function call_that() public pure {
+    function call_that() public view {
         other.say_this("Hold up! I'm calling!");
     }
 }
@@ -21,9 +21,10 @@ contract Built {
     @payer(payer_account)
     constructor(@seed bytes my_seed) {}
     // BEGIN-CHECK: solang_dispatch
-    // CHECK: ty:struct AccountInfo %temp.11 = (subscript struct AccountInfo[] (builtin Accounts ())[uint32 1])
-	// CHECK: ty:struct AccountMeta[2] %metas.temp.10 = [2] [ struct { (load (struct %temp.11 field 0)), true, true }, struct { (builtin GetAddress ()), true, true } ]
-	
+    // CHECK: ty:struct AccountInfo %temp.10 = (subscript struct AccountInfo[] (builtin Accounts ())[uint32 1])
+	// CHECK: ty:struct AccountInfo %temp.11 = (subscript struct AccountInfo[] (builtin Accounts ())[uint32 0])
+	// CHECK: ty:struct AccountMeta[2] %metas.temp.9 = [2] [ struct { (load (struct %temp.10 field 0)), true, true }, struct { (load (struct %temp.11 field 0)), true, true } ]
+
     // The account metas should have the proper index in the AccountInfo array: 1
 
     function say_this(string text) public pure {

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

@@ -214,7 +214,7 @@ contract c3 {
     }
 
     // BEGIN-CHECK: c3::function::test18
-    function test18(address payable addr) public returns (bool) {
+    function test18(address payable addr) public payable returns (bool) {
         bool p;
         p = false || addr.send(msg.value);
         // CHECK: value transfer address

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

@@ -18,5 +18,5 @@ contract spl {
 }
 
 // ---- Expect: diagnostics ----
-// warning: 4:2-47: function can be declared 'pure'
+// warning: 4:2-47: function can be declared 'view'
 // warning: 9:2-56: function can be declared 'pure'

+ 123 - 0
tests/contract_testcases/solana/accounts/constructor_in_loop.sol

@@ -0,0 +1,123 @@
+contract foo {
+    X a;
+    Y ay;
+
+    function contruct() external {
+        a = new X({varia: 2, varb: 5});
+    }
+
+	function test1() external returns (uint) {
+        // This is allowed
+        uint j = a.vara(1, 2);
+		for (uint i = 0; i < 64; i++) {
+			j += a.vara(i, j);
+		}
+        return j;
+	}
+
+    function test2() external returns (uint) {
+        uint j = 3;
+		for (uint i = 0; i < 64; i++) {
+			a = new X(i, j);
+		}
+        return j;
+	}
+
+    function test3() external returns (uint) {
+        uint j = 3;
+        uint n=0;
+		for (uint i = 0; i < 64; i++) {
+			n += i;
+		}
+        a = new X(n, j);
+        return j;
+	}
+
+    function test4(uint v1, string v2) external {
+        ay = new Y({b: v2, a: v1});
+    }
+
+    function test5() external returns (uint) {
+        uint j = 3;
+        uint i=0;
+		while (i < 64) {
+			a = new X(i, j);
+            i++;
+		}
+        return j;
+	}
+
+    function test6() external returns (uint) {
+        uint j = 3;
+        uint i=0;
+		while (i < 64) {
+            i++;
+		}
+        a = new X(i, j);
+        return j;
+	}
+
+    function test7() external returns (uint) {
+        uint j = 3;
+        uint i=0;
+		do {
+			a = new X(i, j);
+            i++;
+		} while (i < 64);
+        return j;
+    }
+
+    function test8() external returns (uint) {
+        uint j = 3;
+        uint i=0;
+		do {
+            i++;
+		} while (i < 64);
+        a = new X(i, j);
+        return j;
+    }
+
+    function test9() external returns (uint) {
+        uint j=9;
+        uint n=0;
+        while (j<90) {
+            j++;
+            for(uint i=0; i<80; i++) {
+                n +=i;
+            }
+            a = new X(j, n);
+        }
+
+        return j;
+    }
+}
+
+@program_id("Foo5mMfYo5RhRcWa4NZ2bwFn4Kdhe8rNK5jchxsKrivA")
+contract X {
+    uint a;
+    uint b;
+    constructor(uint varia, uint varb) {
+        a = varia;
+        b = varb;
+    }
+
+    function vara(uint g, uint h) public view returns (uint) {
+        return g + h + a + b;
+    } 
+}
+
+contract Y {
+    uint za;
+    string zv;
+    constructor(uint a, string b) {
+        za = a;
+        zv = b;
+    }
+}
+
+// ---- Expect: diagnostics ----
+// error: 21:8-19: the {accounts: ..} call argument is needed since the constructor may be called multiple times
+// error: 37:14-35: in order to instantiate contract 'Y', a @program_id is required on contract 'Y'
+// error: 44:8-19: the {accounts: ..} call argument is needed since the constructor may be called multiple times
+// error: 64:8-19: the {accounts: ..} call argument is needed since the constructor may be called multiple times
+// error: 88:17-28: the {accounts: ..} call argument is needed since the constructor may be called multiple times

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

@@ -0,0 +1,26 @@
+contract FooFoo {
+    @payer(dataAccount)
+    constructor() {
+
+    }
+}
+
+contract FooBar {
+    @payer(my_dataAccount)
+    constructor() {
+
+    }
+}
+
+contract BarFoo {
+    @payer(otherdataAccount)
+    constructor() {
+        
+    }
+}
+
+
+// ---- Expect: diagnostics ----
+// error: 2:12-23: 'dataAccount' is a reserved account name
+// error: 9:12-26: account names that contain 'dataAccount' are reserved
+// error: 16:12-28: account names that contain 'dataAccount' are reserved

+ 8 - 0
tests/contract_testcases/solana/accounts/data_account_visibility.sol

@@ -0,0 +1,8 @@
+contract Foo {
+    function addr_account() public pure returns (address) {
+        return tx.accounts.dataAccount.key;
+    }
+}
+
+// ---- Expect: diagnostics ----
+// error: 3:16-27: function declared 'pure' but this expression reads from state

+ 51 - 0
tests/contract_testcases/solana/accounts/double_calls.sol

@@ -0,0 +1,51 @@
+contract adult {
+    hatchling hh;
+    function test() external {
+        hatchling h1 = new hatchling("luna");
+        hatchling h2 = new hatchling("sol");
+    }
+
+    function create(string id) external {
+        hh = new hatchling(id);
+    }
+
+    function call2() external {
+        hh.call_me("id");
+        hh.call_me("not_id");
+    }
+
+    function create2(string id2) external {
+        hh = new hatchling(id2);
+        hh.call_me(id2);
+    }
+}
+
+
+@program_id("5kQ3iJ43gHNDjqmSAtE1vDu18CiSAfNbRe4v5uoobh3U")
+contract hatchling {
+    string name;
+
+    constructor(string id) payable {
+        require(id != "", "name must be provided");
+        name = id;
+    }
+
+    function call_me(string name2) view external {
+        if (name != name2) {
+                print("This is not my name");
+        } else {
+                print("Have I heard my name?");
+        }
+    }
+}
+
+// ---- Expect: diagnostics ----
+// warning: 4:19-21: local variable 'h1' is unused
+// warning: 5:19-21: local variable 'h2' is unused
+// error: 5:24-44: contract 'hatchling' is called more than once in this function, so automatic account collection cannot happen. Please, provide the necessary accounts using the {accounts:..} call argument
+// 	note 4:24-45: other call
+// warning: 12:5-30: function can be declared 'view'
+// error: 14:9-29: contract 'hatchling' is called more than once in this function, so automatic account collection cannot happen. Please, provide the necessary accounts using the {accounts:..} call argument
+// 	note 13:9-25: other call
+// error: 19:9-24: contract 'hatchling' is called more than once in this function, so automatic account collection cannot happen. Please, provide the necessary accounts using the {accounts:..} call argument
+// 	note 18:14-32: other call

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

@@ -3,7 +3,7 @@ contract c {
 		// We have code that cast address type to ref address
 		// in fn sema::cast(). Ensure that this does not cause
 		// address values to be assignable.
-		address(0) = tx.program_id;
+		address(0) = address(this);
 	}
 }
 

+ 2 - 2
tests/contract_testcases/solana/annotations/account_name_collision.sol

@@ -3,8 +3,8 @@ contract Builder {
     BeingBuilt other;
     
     @payer(payer_account)
-    constructor(address addr) {
-        other = new BeingBuilt{address: addr}("abc");
+    constructor() {
+        other = new BeingBuilt("abc");
     }
 }
 

+ 9 - 4
tests/contract_testcases/solana/annotations/constructor_external_function.sol

@@ -9,13 +9,17 @@ contract Bar {
     Foo public foo;
 
     function external_create_foo(address addr) external {
-        // This is allowed
+        // This not is allowed
         foo = new Foo{address: addr}();
     }
 
-    function create_foo(address new_address) public {
+    function create_foo() public {
         // This is not allowed
-        foo = new Foo{address: new_address}();
+        foo = new Foo();
+    }
+
+    function this_is_allowed() external {
+        foo = new Foo();
     }
 
     function call_foo() public pure {
@@ -24,4 +28,5 @@ contract Bar {
 }
 
 // ---- Expect: diagnostics ----
-// error: 18:15-46: 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: 13:23-36: 'address' not a valid call parameter
+// error: 18:15-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

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

@@ -20,7 +20,7 @@ contract D {
 }
 
 // ---- Expect: diagnostics ----
-// error: 4:9-28: either 'address' or 'accounts' call argument is required on Solana
+// error: 4:9-28: 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: 4:16-24: Solana Cross Program Invocation (CPI) cannot transfer native value. See https://solang.readthedocs.io/en/latest/language/functions.html#value_transfer
 // error: 10:10-18: Solana Cross Program Invocation (CPI) cannot transfer native value. See https://solang.readthedocs.io/en/latest/language/functions.html#value_transfer
 // error: 11:12-20: Solana Cross Program Invocation (CPI) cannot transfer native value. See https://solang.readthedocs.io/en/latest/language/functions.html#value_transfer

+ 1 - 1
tests/contract_testcases/solana/expressions/contract_no_init.sol

@@ -15,4 +15,4 @@
             }
         }
 // ---- Expect: diagnostics ----
-// error: 11:25-36: either 'address' or 'accounts' call argument is required on Solana
+// error: 11:25-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

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

@@ -19,7 +19,7 @@ contract DeleteTest {
     mapping(uint => savedTest) example2;
 
     function addData() public  {
-        data_struct dt = data_struct({addr1: address(this), addr2: tx.program_id});
+        data_struct dt = data_struct({addr1: address(this), addr2: tx.accounts.dataAccount.key});
         uint id = 1;
         example[id] = dt;
         savedTest tt = new savedTest(4);
@@ -39,4 +39,4 @@ contract DeleteTest {
 
 }
 // ---- Expect: diagnostics ----
-// error: 25:24-40: either 'address' or 'accounts' call argument is required on Solana
+// error: 25:24-40: 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 - 1
tests/contract_testcases/solana/yul/yul_switch.sol

@@ -1,6 +1,6 @@
 contract testTypes {
     uint256 b;
-    function testAsm(uint128[] calldata vl) public pure returns (uint256) {
+    function testAsm(uint128[] calldata vl) public view returns (uint256) {
         uint256 y = 0;
         assembly {
             switch vl.length
@@ -17,3 +17,4 @@ contract testTypes {
     }
 } 
 // ---- Expect: diagnostics ----
+// warning: 3:5-74: function declared 'view' can be declared 'pure'

+ 21 - 0
tests/imports_testcases/Dummy.json

@@ -0,0 +1,21 @@
+{
+  "version": "0.0.1",
+  "name": "Dummy",
+  "docs": [
+    "notice: This is a dummy contract to test stuff like redundant (and unneeded)\nimportmaps"
+  ],
+  "instructions": [
+    {
+      "name": "new",
+      "accounts": [
+        {
+          "name": "dataAccount",
+          "isMut": true,
+          "isSigner": false,
+          "isOptional": false
+        }
+      ],
+      "args": []
+    }
+  ]
+}

+ 1 - 1
tests/optimization_testcases/programs/f46807c38c2116c0f3192e9a8d2bede4f34846d2.sol

@@ -17,7 +17,7 @@ contract c {
         for (uint32 i = 0; i < tx.accounts.length; i++) {
             AccountInfo ai = tx.accounts[i];
 
-            if (ai.key == address(this)) {
+            if (ai.key == tx.accounts.dataAccount.key) {
                 return (ai.data.readUint32LE(1), ai.data.length);
             }
         }

+ 1 - 1
tests/optimization_testcases/programs/f646b924357689419072fddf402c3ad3c10cd095.sol

@@ -9,7 +9,7 @@ contract foo {
     function test2() public returns (bytes) {
         bytes bs = new bytes(34);
         bs.writeUint16LE(0x4142, 0);
-        bs.writeAddress(tx.program_id, 2);
+        bs.writeAddress(address(this), 2);
         return bs;
     }
 

+ 1 - 1
tests/polkadot_tests/calls.rs

@@ -295,7 +295,7 @@ fn try_catch_external_calls() {
                 return c.get_a();
             }
 
-            function test() public pure returns (int32) {
+            function test() public returns (int32) {
                 try c.go_bang() returns (int32 l) {
                     print("try call success");
                     return 8000;

+ 1 - 1
tests/polkadot_tests/contracts.rs

@@ -205,7 +205,7 @@ fn issue666() {
                 _flipper = _flipperContract;
             }
 
-            function superFlip () pure public {
+            function superFlip () view public {
                 _flipper.flip();
             }
         }"#,

+ 1 - 1
tests/polkadot_tests/yul.rs

@@ -63,7 +63,7 @@ contract testing  {
         }
     }
 
-    function calldata_vec(uint256 input, uint32[] calldata vec) public pure returns (uint256 ret) {
+    function calldata_vec(uint256 input, uint32[] calldata vec) public view returns (uint256 ret) {
         assembly {
             vec.offset := add(input, 7)
             input := 9

+ 7 - 1
tests/solana.rs

@@ -1667,7 +1667,7 @@ impl<'a, 'b> VmFunction<'a, 'b> {
                             accounts
                                 .get(account.name.as_str())
                                 .cloned()
-                                .expect("an account is missing"),
+                                .unwrap_or_else(|| panic!("account '{}' is missing", account.name)),
                         ),
                         is_writable: account.is_mut,
                         is_signer: account.is_signer,
@@ -1677,6 +1677,12 @@ impl<'a, 'b> VmFunction<'a, 'b> {
             }
         }
 
+        assert_eq!(
+            accounts.len(),
+            metas.len(),
+            "Number of accounts does not match IDL"
+        );
+
         self.accounts = metas;
         self
     }

+ 5 - 15
tests/solana_tests/abi.rs

@@ -51,21 +51,13 @@ fn packed() {
         .accounts(vec![("dataAccount", account)])
         .call();
 
-    vm.function("test")
-        .accounts(vec![("dataAccount", account)])
-        .call();
+    vm.function("test").call();
 
-    vm.function("test2")
-        .accounts(vec![("dataAccount", account)])
-        .call();
+    vm.function("test2").call();
 
-    vm.function("test3")
-        .accounts(vec![("dataAccount", account)])
-        .call();
+    vm.function("test3").call();
 
-    vm.function("test4")
-        .accounts(vec![("dataAccount", account)])
-        .call();
+    vm.function("test4").call();
 }
 
 #[test]
@@ -85,9 +77,7 @@ fn inherited() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
-    vm.function("test")
-        .accounts(vec![("dataAccount", data_account)])
-        .call();
+    vm.function("test").call();
 
     let mut vm = build_solidity(
         r#"

+ 2 - 13
tests/solana_tests/abi_decode.rs

@@ -125,14 +125,13 @@ fn decode_address() {
         .call();
 
     let input = Data {
-        address: data_account,
-        this: data_account,
+        address: vm.stack[0].id,
+        this: vm.stack[0].id,
     };
     let encoded = input.try_to_vec().unwrap();
     let _ = vm
         .function("testAddress")
         .arguments(&[BorshToken::Bytes(encoded)])
-        .accounts(vec![("dataAccount", data_account)])
         .call();
 }
 
@@ -169,7 +168,6 @@ fn string_and_bytes() {
     let _ = vm
         .function("testStringAndBytes")
         .arguments(&[BorshToken::Bytes(encoded)])
-        .accounts(vec![("dataAccount", data_account)])
         .call();
 }
 
@@ -633,7 +631,6 @@ fn arrays() {
     let _ = vm
         .function("decodeComplex")
         .arguments(&[BorshToken::Bytes(encoded)])
-        .accounts(vec![("dataAccount", data_account)])
         .call();
 
     let input = Input2 {
@@ -643,7 +640,6 @@ fn arrays() {
     let _ = vm
         .function("dynamicArray")
         .arguments(&[BorshToken::Bytes(encoded)])
-        .accounts(vec![("dataAccount", data_account)])
         .call();
 
     let input = Input3 {
@@ -653,7 +649,6 @@ fn arrays() {
     let _ = vm
         .function("decodeMultiDim")
         .arguments(&[BorshToken::Bytes(encoded)])
-        .accounts(vec![("dataAccount", data_account)])
         .call();
 }
 
@@ -920,7 +915,6 @@ fn external_function() {
     let returns = vm
         .function("testExternalFunction")
         .arguments(&[BorshToken::Bytes(encoded)])
-        .accounts(vec![("dataAccount", data_account)])
         .call()
         .unwrap()
         .unwrap_tuple();
@@ -969,7 +963,6 @@ fn bytes_arrays() {
     let _ = vm
         .function("testByteArrays")
         .arguments(&[BorshToken::Bytes(encoded)])
-        .accounts(vec![("dataAccount", data_account)])
         .call();
 }
 
@@ -1008,7 +1001,6 @@ fn different_types() {
     let _ = vm
         .function("testByteArrays")
         .arguments(&[BorshToken::Bytes(encoded)])
-        .accounts(vec![("dataAccount", data_account)])
         .call();
 }
 
@@ -1042,7 +1034,6 @@ fn more_elements() {
     let _ = vm
         .function("wrongNumber")
         .arguments(&[BorshToken::Bytes(encoded)])
-        .accounts(vec![("dataAccount", data_account)])
         .call();
 }
 
@@ -1149,7 +1140,6 @@ fn longer_buffer() {
     let _ = vm
         .function("testLongerBuffer")
         .arguments(&[BorshToken::Bytes(encoded)])
-        .accounts(vec![("dataAccount", data_account)])
         .call();
 }
 
@@ -1188,7 +1178,6 @@ fn longer_buffer_array() {
     let _ = vm
         .function("testLongerBuffer")
         .arguments(&[BorshToken::Bytes(encoded)])
-        .accounts(vec![("dataAccount", data_account)])
         .call();
 }
 

+ 8 - 24
tests/solana_tests/abi_encode.rs

@@ -115,15 +115,11 @@ contract Testing {
     vm.function("new")
         .accounts(vec![("dataAccount", data_account)])
         .call();
-    let returns = vm
-        .function("getThis")
-        .accounts(vec![("dataAccount", data_account)])
-        .call()
-        .unwrap();
+    let returns = vm.function("getThis").call().unwrap();
     let encoded = returns.into_bytes().unwrap();
     let decoded = Response::try_from_slice(&encoded).unwrap();
-    assert_eq!(decoded.address, data_account);
-    assert_eq!(decoded.this, data_account);
+    assert_eq!(decoded.address, vm.stack[0].id);
+    assert_eq!(decoded.this, vm.stack[0].id);
 }
 
 #[test]
@@ -526,11 +522,7 @@ fn struct_in_array() {
     assert_eq!(decoded.item_3[0], NoPadStruct { a: 1, b: 2 });
     assert_eq!(decoded.item_3[1], NoPadStruct { a: 3, b: 4 });
 
-    let returns = vm
-        .function("primitiveDynamicArray")
-        .accounts(vec![("dataAccount", data_account)])
-        .call()
-        .unwrap();
+    let returns = vm.function("primitiveDynamicArray").call().unwrap();
     let encoded = returns.into_bytes().unwrap();
     let decoded = Res3::try_from_slice(&encoded).unwrap();
 
@@ -626,11 +618,7 @@ fn arrays() {
     assert_eq!(decoded.vec_1[1], 5523);
     assert_eq!(decoded.vec_1[2], -89);
 
-    let returns = vm
-        .function("encodeComplex")
-        .accounts(vec![("dataAccount", data_account)])
-        .call()
-        .unwrap();
+    let returns = vm.function("encodeComplex").call().unwrap();
     let encoded = returns.into_bytes().unwrap();
     let decoded = Res2::try_from_slice(&encoded).unwrap();
 
@@ -911,12 +899,7 @@ fn external_function() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
-    let returns = vm
-        .function("doThat")
-        .accounts(vec![("dataAccount", data_account)])
-        .call()
-        .unwrap()
-        .unwrap_tuple();
+    let returns = vm.function("doThat").call().unwrap().unwrap_tuple();
     let encoded = returns[2].clone().into_bytes().unwrap();
     let decoded = Res::try_from_slice(&encoded).unwrap();
 
@@ -1032,11 +1015,12 @@ contract caller {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
+    let caller_program_id = vm.stack[0].id;
     let returns = vm
         .function("do_call")
         .accounts(vec![
-            ("dataAccount", data_account),
             ("systemProgram", [0; 32]),
+            ("caller_programId", caller_program_id),
         ])
         .call()
         .unwrap()

+ 11 - 19
tests/solana_tests/accessor.rs

@@ -183,11 +183,7 @@ fn constant() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
-    let returns = vm
-        .function("z")
-        .accounts(vec![("dataAccount", data_account)])
-        .call()
-        .unwrap();
+    let returns = vm.function("z").call().unwrap();
 
     assert_eq!(
         returns,
@@ -209,11 +205,7 @@ fn constant() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
-    let returns = vm
-        .function("z")
-        .accounts(vec![("dataAccount", data_account)])
-        .call()
-        .unwrap();
+    let returns = vm.function("z").call().unwrap();
 
     assert_eq!(
         returns,
@@ -235,11 +227,7 @@ fn constant() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
-    let returns = vm
-        .function("z")
-        .accounts(vec![("dataAccount", data_account)])
-        .call()
-        .unwrap();
+    let returns = vm.function("z").call().unwrap();
 
     assert_eq!(
         returns,
@@ -254,6 +242,7 @@ fn constant() {
 fn struct_accessor() {
     let mut vm = build_solidity(
         r#"
+        import 'solana';
         contract C {
             struct E {
                 bytes4 b4;
@@ -274,13 +263,16 @@ fn struct_accessor() {
             }
 
             function f() public view {
-                (int64 a1, bool b, E memory c) = this.a();
+                AccountMeta[1] meta = [
+                    AccountMeta({pubkey: tx.accounts.dataAccount.key, is_writable: false, is_signer: false})
+                ];
+                (int64 a1, bool b, E memory c) = this.a{accounts: meta}();
                 require(a1 == -63 && !b && c.b4 == "nuff", "a");
-                (a1, b, c) = this.s(99);
+                (a1, b, c) = this.s{accounts: meta}(99);
                 require(a1 == 65535 && b && c.b4 == "naff", "b");
-                (a1, b, c) = this.m(1023413412);
+                (a1, b, c) = this.m{accounts: meta}(1023413412);
                 require(a1 == 414243 && b && c.b4 == "niff", "c");
-                c.b4 = this.e();
+                c.b4 = this.e{accounts: meta}();
                 require(a1 == 414243 && b && c.b4 == "cons", "E");
             }
         }"#,

+ 8 - 34
tests/solana_tests/account_info.rs

@@ -45,7 +45,6 @@ fn lamports() {
     let returns = vm
         .function("test")
         .arguments(&[BorshToken::Address(acc)])
-        .accounts(vec![("dataAccount", data_account)])
         .remaining_accounts(&[AccountMeta {
             pubkey: Pubkey(acc),
             is_writable: true,
@@ -70,15 +69,7 @@ fn owner() {
         import 'solana';
         contract c {
             function test() public payable returns (address) {
-                for (uint32 i = 0; i < tx.accounts.length; i++) {
-                    AccountInfo ai = tx.accounts[i];
-
-                    if (ai.key == address(this)) {
-                        return ai.owner;
-                    }
-                }
-
-                revert("account not found");
+                return tx.accounts.dataAccount.owner;
             }
         }"#,
     );
@@ -106,27 +97,12 @@ fn data() {
         import 'solana';
         contract c {
             function test(uint32 index) public payable returns (uint8) {
-                for (uint32 i = 0; i < tx.accounts.length; i++) {
-                    AccountInfo ai = tx.accounts[i];
-
-                    if (ai.key == address(this)) {
-                        return ai.data[index];
-                    }
-                }
-
-                revert("account not found");
+                return tx.accounts.dataAccount.data[index];
             }
 
             function test2() public payable returns (uint32, uint32) {
-                for (uint32 i = 0; i < tx.accounts.length; i++) {
-                    AccountInfo ai = tx.accounts[i];
-
-                    if (ai.key == address(this)) {
-                        return (ai.data.readUint32LE(1), ai.data.length);
-                    }
-                }
-
-                revert("account not found");
+                AccountInfo ai = tx.accounts.dataAccount;
+                return (ai.data.readUint32LE(1), ai.data.length);
             }
         }"#,
     );
@@ -193,9 +169,9 @@ import 'solana';
 
 contract starter {
     function createNewAccount(uint64 lamport1, uint64 lamport2, uint64 lamport3) public {
-        AccountInfo acc1 = tx.accounts[1];
-        AccountInfo acc2 = tx.accounts[2];
-        AccountInfo acc3 = tx.accounts[3];
+        AccountInfo acc1 = tx.accounts[0];
+        AccountInfo acc2 = tx.accounts[1];
+        AccountInfo acc3 = tx.accounts[2];
 
         acc1.lamports -= lamport1;
         acc2.lamports = lamport2;
@@ -272,7 +248,6 @@ contract starter {
                 value: BigInt::from(9u8),
             },
         ])
-        .accounts(vec![("dataAccount", data_account)])
         .remaining_accounts(&metas)
         .call();
 
@@ -289,7 +264,7 @@ import 'solana';
 
 contract C {
 	function test() external {
-		AccountInfo ai = tx.accounts[1];
+		AccountInfo ai = tx.accounts[0];
 		ai.data[0] = 0xca;
 		ai.data[1] = 0xff;
 		ai.data[2] = 0xee;
@@ -315,7 +290,6 @@ contract C {
     );
 
     vm.function("test")
-        .accounts(vec![("dataAccount", data_account)])
         .remaining_accounts(&[AccountMeta {
             pubkey: Pubkey(other_account),
             is_writable: true,

+ 3 - 5
tests/solana_tests/account_serialization.rs

@@ -13,7 +13,7 @@ fn deserialize_duplicate_account() {
         assert(tx.accounts[1].is_signer == tx.accounts[2].is_signer);
         assert(tx.accounts[1].is_writable == tx.accounts[2].is_writable);
 
-        assert(my_address == tx.program_id);
+        assert(my_address == tx.accounts.dataAccount.key);
     }
 }
         "#,
@@ -44,9 +44,8 @@ fn deserialize_duplicate_account() {
         },
     );
 
-    let program_id = vm.stack[0].id;
     vm.function("check_deserialization")
-        .arguments(&[BorshToken::Address(program_id)])
+        .arguments(&[BorshToken::Address(data_account)])
         .accounts(vec![("dataAccount", data_account)])
         .remaining_accounts(&[
             AccountMeta {
@@ -71,7 +70,7 @@ fn more_than_10_accounts() {
     function check_deserialization(address my_address) public view {
         // This assertion ensure the padding is correctly added when
         // deserializing accounts
-        assert(my_address == tx.program_id);
+        assert(my_address == address(this));
     }
 }
         "#,
@@ -119,7 +118,6 @@ fn more_than_10_accounts() {
     let program_id = vm.stack[0].id;
     vm.function("check_deserialization")
         .arguments(&[BorshToken::Address(program_id)])
-        .accounts(vec![("dataAccount", data_account)])
         .remaining_accounts(&metas)
         .call();
 }

+ 17 - 82
tests/solana_tests/arrays.rs

@@ -25,12 +25,7 @@ fn fixed_array() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
-    let returns = vm
-        .function("get")
-        .accounts(vec![("dataAccount", data_account)])
-        .call()
-        .unwrap()
-        .unwrap_tuple();
+    let returns = vm.function("get").call().unwrap().unwrap_tuple();
 
     assert_eq!(
         returns,
@@ -79,11 +74,7 @@ fn fixed_array() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
-    let returns = vm
-        .function("get")
-        .accounts(vec![("dataAccount", data_account)])
-        .call()
-        .unwrap();
+    let returns = vm.function("get").call().unwrap();
 
     assert_eq!(
         returns,
@@ -155,11 +146,7 @@ fn fixed_array() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
-    let returns = vm
-        .function("get")
-        .accounts(vec![("dataAccount", data_account)])
-        .call()
-        .unwrap();
+    let returns = vm.function("get").call().unwrap();
 
     assert_eq!(
         returns,
@@ -211,7 +198,6 @@ fn fixed_array() {
             ]),
             BorshToken::Bool(true),
         ])])
-        .accounts(vec![("dataAccount", data_account)])
         .call()
         .unwrap();
 
@@ -287,7 +273,6 @@ fn dynamic_array_fixed_elements() {
                 value: BigInt::from(102u8),
             },
         ])
-        .accounts(vec![("dataAccount", data_account)])
         .call()
         .unwrap();
 
@@ -300,12 +285,7 @@ fn dynamic_array_fixed_elements() {
     );
 
     // test that the abi encoder can handle fixed arrays
-    let returns = vm
-        .function("set")
-        .accounts(vec![("dataAccount", data_account)])
-        .call()
-        .unwrap()
-        .unwrap_tuple();
+    let returns = vm.function("set").call().unwrap().unwrap_tuple();
 
     assert_eq!(
         returns,
@@ -391,7 +371,6 @@ fn fixed_array_dynamic_elements() {
                 value: BigInt::from(102u8),
             },
         ])
-        .accounts(vec![("dataAccount", data_account)])
         .call()
         .unwrap();
 
@@ -403,12 +382,7 @@ fn fixed_array_dynamic_elements() {
         }
     );
 
-    let returns = vm
-        .function("set")
-        .accounts(vec![("dataAccount", data_account)])
-        .call()
-        .unwrap()
-        .unwrap_tuple();
+    let returns = vm.function("set").call().unwrap().unwrap_tuple();
 
     assert_eq!(
         returns,
@@ -486,7 +460,6 @@ fn dynamic_array_dynamic_elements() {
                 value: BigInt::from(102u8),
             },
         ])
-        .accounts(vec![("dataAccount", data_account)])
         .call()
         .unwrap();
 
@@ -498,12 +471,7 @@ fn dynamic_array_dynamic_elements() {
         }
     );
 
-    let returns = vm
-        .function("set")
-        .accounts(vec![("dataAccount", data_account)])
-        .call()
-        .unwrap()
-        .unwrap_tuple();
+    let returns = vm.function("set").call().unwrap().unwrap_tuple();
 
     assert_eq!(
         returns,
@@ -1754,10 +1722,7 @@ fn dynamic_array_push() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
-    runtime
-        .function("test")
-        .accounts(vec![("dataAccount", data_account)])
-        .call();
+    runtime.function("test").call();
 
     let mut runtime = build_solidity(
         r#"
@@ -1783,10 +1748,7 @@ fn dynamic_array_push() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
-    runtime
-        .function("test")
-        .accounts(vec![("dataAccount", data_account)])
-        .call();
+    runtime.function("test").call();
 
     let mut runtime = build_solidity(
         r#"
@@ -1817,10 +1779,7 @@ fn dynamic_array_push() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
-    runtime
-        .function("test")
-        .accounts(vec![("dataAccount", data_account)])
-        .call();
+    runtime.function("test").call();
 
     let mut runtime = build_solidity(
         r#"
@@ -1847,10 +1806,7 @@ fn dynamic_array_push() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
-    runtime
-        .function("test")
-        .accounts(vec![("dataAccount", data_account)])
-        .call();
+    runtime.function("test").call();
 
     // push() returns a reference to the thing
     let mut runtime = build_solidity(
@@ -1881,10 +1837,7 @@ fn dynamic_array_push() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
-    runtime
-        .function("test")
-        .accounts(vec![("dataAccount", data_account)])
-        .call();
+    runtime.function("test").call();
 }
 
 #[test]
@@ -1913,10 +1866,7 @@ fn dynamic_array_pop() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
-    runtime
-        .function("test")
-        .accounts(vec![("dataAccount", data_account)])
-        .call();
+    runtime.function("test").call();
 
     let mut runtime = build_solidity(
         r#"
@@ -1942,10 +1892,7 @@ fn dynamic_array_pop() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
-    runtime
-        .function("test")
-        .accounts(vec![("dataAccount", data_account)])
-        .call();
+    runtime.function("test").call();
 
     let mut runtime = build_solidity(
         r#"
@@ -1978,10 +1925,7 @@ fn dynamic_array_pop() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
-    runtime
-        .function("test")
-        .accounts(vec![("dataAccount", data_account)])
-        .call();
+    runtime.function("test").call();
 
     let mut runtime = build_solidity(
         r#"
@@ -2008,10 +1952,7 @@ fn dynamic_array_pop() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
-    runtime
-        .function("test")
-        .accounts(vec![("dataAccount", data_account)])
-        .call();
+    runtime.function("test").call();
 }
 
 #[test]
@@ -2113,10 +2054,7 @@ fn dynamic_array_push_pop_loop() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
-    runtime
-        .function("test")
-        .accounts(vec![("dataAccount", data_account)])
-        .call();
+    runtime.function("test").call();
 
     let mut runtime = build_solidity(
         r#"
@@ -2160,10 +2098,7 @@ fn dynamic_array_push_pop_loop() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
-    runtime
-        .function("test")
-        .accounts(vec![("dataAccount", data_account)])
-        .call();
+    runtime.function("test").call();
 }
 
 #[test]

+ 144 - 72
tests/solana_tests/balance.rs

@@ -34,12 +34,18 @@ fn get_balance() {
     let returns = vm
         .function("test")
         .arguments(&[BorshToken::Address(new)])
-        .accounts(vec![("dataAccount", data_account)])
-        .remaining_accounts(&[AccountMeta {
-            pubkey: Pubkey(new),
-            is_signer: false,
-            is_writable: false,
-        }])
+        .remaining_accounts(&[
+            AccountMeta {
+                pubkey: Pubkey(data_account),
+                is_signer: false,
+                is_writable: false,
+            },
+            AccountMeta {
+                pubkey: Pubkey(new),
+                is_signer: false,
+                is_writable: false,
+            },
+        ])
         .call()
         .unwrap();
 
@@ -88,12 +94,18 @@ fn send_fails() {
                 value: BigInt::from(102u8),
             },
         ])
-        .accounts(vec![("dataAccount", data_account)])
-        .remaining_accounts(&[AccountMeta {
-            pubkey: Pubkey(new),
-            is_signer: false,
-            is_writable: true,
-        }])
+        .remaining_accounts(&[
+            AccountMeta {
+                pubkey: Pubkey(data_account),
+                is_signer: true,
+                is_writable: true,
+            },
+            AccountMeta {
+                pubkey: Pubkey(new),
+                is_signer: false,
+                is_writable: true,
+            },
+        ])
         .call()
         .unwrap();
 
@@ -140,12 +152,18 @@ fn send_succeeds() {
                 value: BigInt::from(102u8),
             },
         ])
-        .accounts(vec![("dataAccount", data_account)])
-        .remaining_accounts(&[AccountMeta {
-            pubkey: Pubkey(new),
-            is_signer: false,
-            is_writable: true,
-        }])
+        .remaining_accounts(&[
+            AccountMeta {
+                pubkey: Pubkey(data_account),
+                is_signer: true,
+                is_writable: true,
+            },
+            AccountMeta {
+                pubkey: Pubkey(new),
+                is_signer: false,
+                is_writable: true,
+            },
+        ])
         .call()
         .unwrap();
 
@@ -195,12 +213,18 @@ fn send_overflows() {
                 value: BigInt::from(102u8),
             },
         ])
-        .accounts(vec![("dataAccount", data_account)])
-        .remaining_accounts(&[AccountMeta {
-            pubkey: Pubkey(new),
-            is_signer: false,
-            is_writable: true,
-        }])
+        .remaining_accounts(&[
+            AccountMeta {
+                pubkey: Pubkey(data_account),
+                is_signer: true,
+                is_writable: true,
+            },
+            AccountMeta {
+                pubkey: Pubkey(new),
+                is_signer: false,
+                is_writable: true,
+            },
+        ])
         .call()
         .unwrap();
 
@@ -254,12 +278,18 @@ fn transfer_succeeds() {
                 value: BigInt::from(102u8),
             },
         ])
-        .accounts(vec![("dataAccount", data_account)])
-        .remaining_accounts(&[AccountMeta {
-            pubkey: Pubkey(new),
-            is_signer: false,
-            is_writable: true,
-        }])
+        .remaining_accounts(&[
+            AccountMeta {
+                pubkey: Pubkey(data_account),
+                is_signer: true,
+                is_writable: true,
+            },
+            AccountMeta {
+                pubkey: Pubkey(new),
+                is_signer: false,
+                is_writable: true,
+            },
+        ])
         .call();
 
     assert_eq!(vm.account_data.get_mut(&new).unwrap().lamports, 107);
@@ -305,12 +335,18 @@ fn transfer_fails_not_enough() {
                 value: BigInt::from(104u8),
             },
         ])
-        .accounts(vec![("dataAccount", data_account)])
-        .remaining_accounts(&[AccountMeta {
-            pubkey: Pubkey(new),
-            is_signer: false,
-            is_writable: true,
-        }])
+        .remaining_accounts(&[
+            AccountMeta {
+                pubkey: Pubkey(data_account),
+                is_signer: true,
+                is_writable: true,
+            },
+            AccountMeta {
+                pubkey: Pubkey(new),
+                is_signer: false,
+                is_writable: true,
+            },
+        ])
         .must_fail();
     assert!(res.is_err());
 
@@ -326,12 +362,18 @@ fn transfer_fails_not_enough() {
                 value: BigInt::from(103u8),
             },
         ])
-        .accounts(vec![("dataAccount", data_account)])
-        .remaining_accounts(&[AccountMeta {
-            pubkey: Pubkey(new),
-            is_signer: false,
-            is_writable: true,
-        }])
+        .remaining_accounts(&[
+            AccountMeta {
+                pubkey: Pubkey(data_account),
+                is_signer: true,
+                is_writable: true,
+            },
+            AccountMeta {
+                pubkey: Pubkey(new),
+                is_signer: false,
+                is_writable: true,
+            },
+        ])
         .call();
 
     assert_eq!(vm.account_data[&data_account].lamports, 0);
@@ -378,12 +420,18 @@ fn transfer_fails_overflow() {
                 value: BigInt::from(104u8),
             },
         ])
-        .accounts(vec![("dataAccount", data_account)])
-        .remaining_accounts(&[AccountMeta {
-            pubkey: Pubkey(new),
-            is_writable: false,
-            is_signer: true,
-        }])
+        .remaining_accounts(&[
+            AccountMeta {
+                pubkey: Pubkey(data_account),
+                is_signer: true,
+                is_writable: true,
+            },
+            AccountMeta {
+                pubkey: Pubkey(new),
+                is_writable: false,
+                is_signer: true,
+            },
+        ])
         .must_fail();
     assert!(res.is_err());
 
@@ -399,12 +447,18 @@ fn transfer_fails_overflow() {
                 value: BigInt::from(100u8),
             },
         ])
-        .accounts(vec![("dataAccount", data_account)])
-        .remaining_accounts(&[AccountMeta {
-            pubkey: Pubkey(new),
-            is_writable: false,
-            is_signer: true,
-        }])
+        .remaining_accounts(&[
+            AccountMeta {
+                pubkey: Pubkey(data_account),
+                is_signer: true,
+                is_writable: true,
+            },
+            AccountMeta {
+                pubkey: Pubkey(new),
+                is_writable: false,
+                is_signer: true,
+            },
+        ])
         .call();
 
     assert_eq!(vm.account_data[&new].lamports, u64::MAX);
@@ -486,12 +540,18 @@ fn value_overflows() {
                 value: BigInt::from(u64::MAX as u128 + 1),
             },
         ])
-        .accounts(vec![("dataAccount", data_account)])
-        .remaining_accounts(&[AccountMeta {
-            pubkey: Pubkey(new),
-            is_signer: false,
-            is_writable: true,
-        }])
+        .remaining_accounts(&[
+            AccountMeta {
+                pubkey: Pubkey(data_account),
+                is_signer: true,
+                is_writable: true,
+            },
+            AccountMeta {
+                pubkey: Pubkey(new),
+                is_signer: false,
+                is_writable: true,
+            },
+        ])
         .must_fail();
     assert_eq!(res.unwrap(), 4294967296);
 
@@ -504,12 +564,18 @@ fn value_overflows() {
                 value: BigInt::from(u128::MAX),
             },
         ])
-        .accounts(vec![("dataAccount", data_account)])
-        .remaining_accounts(&[AccountMeta {
-            pubkey: Pubkey(new),
-            is_signer: false,
-            is_writable: true,
-        }])
+        .remaining_accounts(&[
+            AccountMeta {
+                pubkey: Pubkey(data_account),
+                is_signer: true,
+                is_writable: true,
+            },
+            AccountMeta {
+                pubkey: Pubkey(new),
+                is_signer: false,
+                is_writable: true,
+            },
+        ])
         .must_fail();
 
     assert_eq!(res.unwrap(), 4294967296);
@@ -523,12 +589,18 @@ fn value_overflows() {
                 value: BigInt::from(102u8),
             },
         ])
-        .accounts(vec![("dataAccount", data_account)])
-        .remaining_accounts(&[AccountMeta {
-            pubkey: Pubkey(new),
-            is_signer: false,
-            is_writable: true,
-        }])
+        .remaining_accounts(&[
+            AccountMeta {
+                pubkey: Pubkey(data_account),
+                is_signer: true,
+                is_writable: true,
+            },
+            AccountMeta {
+                pubkey: Pubkey(new),
+                is_signer: false,
+                is_writable: true,
+            },
+        ])
         .call()
         .unwrap();
 

+ 9 - 38
tests/solana_tests/builtin.rs

@@ -25,7 +25,7 @@ fn builtins() {
                 return msg.sig;
             }
             function prog() public returns (address) {
-                return tx.program_id;
+                return address(this);
             }
         }"#,
     );
@@ -43,10 +43,7 @@ fn builtins() {
     .unwrap();
     let returns = vm
         .function("mr_now")
-        .accounts(vec![
-            ("dataAccount", data_account),
-            ("clock", clock_account),
-        ])
+        .accounts(vec![("clock", clock_account)])
         .call()
         .unwrap();
 
@@ -60,10 +57,7 @@ fn builtins() {
 
     let returns = vm
         .function("mr_slot")
-        .accounts(vec![
-            ("dataAccount", data_account),
-            ("clock", clock_account),
-        ])
+        .accounts(vec![("clock", clock_account)])
         .call()
         .unwrap();
 
@@ -77,10 +71,7 @@ fn builtins() {
 
     let returns = vm
         .function("mr_blocknumber")
-        .accounts(vec![
-            ("dataAccount", data_account),
-            ("clock", clock_account),
-        ])
+        .accounts(vec![("clock", clock_account)])
         .call()
         .unwrap();
 
@@ -98,7 +89,6 @@ fn builtins() {
             width: 32,
             value: BigInt::from(0xdeadcafeu32),
         }])
-        .accounts(vec![("dataAccount", data_account)])
         .call()
         .unwrap();
 
@@ -111,11 +101,7 @@ fn builtins() {
         BorshToken::Bytes(hex::decode("a73fcaa3b216e85afecaadde").unwrap())
     );
 
-    let returns = vm
-        .function("sig")
-        .accounts(vec![("dataAccount", data_account)])
-        .call()
-        .unwrap();
+    let returns = vm.function("sig").call().unwrap();
 
     if let Some(v) = returns.clone().into_fixed_bytes() {
         println!("{}", hex::encode(v));
@@ -126,11 +112,7 @@ fn builtins() {
         BorshToken::uint8_fixed_array(hex::decode("4b22101a3c98d6cb").unwrap())
     );
 
-    let returns = vm
-        .function("prog")
-        .accounts(vec![("dataAccount", data_account)])
-        .call()
-        .unwrap();
+    let returns = vm.function("prog").call().unwrap();
 
     assert_eq!(returns, BorshToken::Address(vm.stack[0].id));
 }
@@ -179,10 +161,7 @@ fn pda() {
     let returns = vm
         .function("create_pda")
         .arguments(&[BorshToken::Bool(true)])
-        .accounts(vec![
-            ("dataAccount", data_account),
-            ("systemProgram", [0; 32]),
-        ])
+        .accounts(vec![("systemProgram", [0; 32])])
         .call()
         .unwrap();
 
@@ -198,10 +177,7 @@ fn pda() {
     let returns = vm
         .function("create_pda")
         .arguments(&[BorshToken::Bool(false)])
-        .accounts(vec![
-            ("dataAccount", data_account),
-            ("systemProgram", [0; 32]),
-        ])
+        .accounts(vec![("systemProgram", [0; 32])])
         .call()
         .unwrap();
 
@@ -220,10 +196,7 @@ fn pda() {
             BorshToken::Bytes(b"Talking".to_vec()),
             BorshToken::Bytes(b"Squirrels".to_vec()),
         ])
-        .accounts(vec![
-            ("dataAccount", data_account),
-            ("systemProgram", [0; 32]),
-        ])
+        .accounts(vec![("systemProgram", [0; 32])])
         .call()
         .unwrap();
 
@@ -239,7 +212,6 @@ fn pda() {
     let returns = vm
         .function("create_pda2_bump")
         .arguments(&[BorshToken::Bool(true)])
-        .accounts(vec![("dataAccount", data_account)])
         .call()
         .unwrap()
         .unwrap_tuple();
@@ -258,7 +230,6 @@ fn pda() {
     let returns = vm
         .function("create_pda2_bump")
         .arguments(&[BorshToken::Bool(false)])
-        .accounts(vec![("dataAccount", data_account)])
         .call()
         .unwrap()
         .unwrap_tuple();

+ 18 - 40
tests/solana_tests/call.rs

@@ -4,7 +4,7 @@ use crate::{
     build_solidity, create_program_address, AccountMeta, AccountState, BorshToken, Instruction,
     Pubkey, VirtualMachine,
 };
-use base58::FromBase58;
+use base58::{FromBase58, ToBase58};
 use num_bigint::BigInt;
 use num_traits::One;
 
@@ -36,7 +36,6 @@ fn simple_external_call() {
         .call();
 
     vm.function("test_bar")
-        .accounts(vec![("dataAccount", bar1_account)])
         .arguments(&[BorshToken::String(String::from("yo"))])
         .call();
 
@@ -52,7 +51,6 @@ fn simple_external_call() {
         .call();
 
     vm.function("test_bar")
-        .accounts(vec![("dataAccount", bar0_account)])
         .arguments(&[BorshToken::String(String::from("uncle beau"))])
         .call();
 
@@ -62,7 +60,7 @@ fn simple_external_call() {
 
     vm.function("test_other")
         .accounts(vec![
-            ("dataAccount", bar0_account),
+            ("bar1_programId", bar1_program_id),
             ("systemProgram", [0; 32]),
         ])
         .remaining_accounts(&[
@@ -77,7 +75,7 @@ fn simple_external_call() {
                 is_writable: false,
             },
         ])
-        .arguments(&[BorshToken::Address(bar1_account)])
+        .arguments(&[BorshToken::Address(bar1_program_id)])
         .call();
 
     assert_eq!(vm.logs, "bar1 says: cross contract call");
@@ -108,7 +106,6 @@ fn external_call_with_returns() {
 
     let res = vm
         .function("test_bar")
-        .accounts(vec![("dataAccount", bar1_account)])
         .arguments(&[BorshToken::Int {
             width: 64,
             value: BigInt::from(21),
@@ -133,9 +130,9 @@ fn external_call_with_returns() {
 
     let res = vm
         .function("test_other")
-        .arguments(&[BorshToken::Address(bar1_account)])
+        .arguments(&[BorshToken::Address(bar1_program_id)])
         .accounts(vec![
-            ("dataAccount", bar0_account),
+            ("bar1_programId", bar1_program_id),
             ("systemProgram", [0; 32]),
         ])
         .remaining_accounts(&[
@@ -198,7 +195,6 @@ fn external_raw_call_with_returns() {
             width: 64,
             value: BigInt::from(21u8),
         }])
-        .accounts(vec![("dataAccount", bar1_account)])
         .call()
         .unwrap();
 
@@ -219,11 +215,8 @@ fn external_raw_call_with_returns() {
 
     let res = vm
         .function("test_other")
-        .arguments(&[BorshToken::Address(bar1_account)])
-        .accounts(vec![
-            ("dataAccount", bar0_account),
-            ("systemProgram", [0; 32]),
-        ])
+        .arguments(&[BorshToken::Address(bar1_program_id)])
+        .accounts(vec![("systemProgram", [0; 32])])
         .remaining_accounts(&[
             AccountMeta {
                 pubkey: Pubkey(bar1_account),
@@ -275,10 +268,7 @@ fn call_external_func_type() {
 
     let res = vm
         .function("doTest")
-        .accounts(vec![
-            ("dataAccount", data_account),
-            ("systemProgram", [0; 32]),
-        ])
+        .accounts(vec![("systemProgram", [0; 32])])
         .call()
         .unwrap()
         .unwrap_tuple();
@@ -338,7 +328,6 @@ fn external_call_with_string_returns() {
             width: 64,
             value: BigInt::from(22u8),
         }])
-        .accounts(vec![("dataAccount", bar1_account)])
         .call()
         .unwrap();
 
@@ -353,9 +342,9 @@ fn external_call_with_string_returns() {
 
     let res = vm
         .function("test_other")
-        .arguments(&[BorshToken::Address(bar1_account)])
+        .arguments(&[BorshToken::Address(bar1_program_id)])
         .accounts(vec![
-            ("dataAccount", bar0_account),
+            ("bar1_programId", bar1_program_id),
             ("systemProgram", [0; 32]),
         ])
         .remaining_accounts(&[
@@ -376,9 +365,9 @@ fn external_call_with_string_returns() {
     assert_eq!(res, BorshToken::String(String::from("foo:7")));
 
     vm.function("test_this")
-        .arguments(&[BorshToken::Address(bar1_account)])
+        .arguments(&[BorshToken::Address(bar1_program_id)])
         .accounts(vec![
-            ("dataAccount", bar0_account),
+            ("bar1_programId", bar1_program_id),
             ("systemProgram", [0; 32]),
         ])
         .remaining_accounts(&[
@@ -414,7 +403,7 @@ fn encode_call() {
         }
 
         contract bar1 {
-            function test_bar(int64 y) public returns (int64) {
+            function test_bar(int64 y) public pure returns (int64) {
                 return 3 + y;
             }
         }"#,
@@ -432,7 +421,6 @@ fn encode_call() {
             width: 64,
             value: BigInt::from(21u8),
         }])
-        .accounts(vec![("dataAccount", bar1_account)])
         .call()
         .unwrap();
 
@@ -451,13 +439,11 @@ fn encode_call() {
         .accounts(vec![("dataAccount", bar0_account)])
         .call();
 
+    std::println!("bar_acc: {}", bar1_account.to_base58());
     let res = vm
         .function("test_other")
-        .arguments(&[BorshToken::Address(bar1_account)])
-        .accounts(vec![
-            ("dataAccount", bar0_account),
-            ("systemProgram", [0; 32]),
-        ])
+        .arguments(&[BorshToken::Address(bar1_program_id)])
+        .accounts(vec![("systemProgram", [0; 32])])
         .remaining_accounts(&[
             AccountMeta {
                 pubkey: Pubkey(bar1_account),
@@ -677,11 +663,7 @@ fn raw_call_accounts() {
             BorshToken::Address(b"quinquagintaquadringentilliardth".to_owned()),
             BorshToken::Address(b"quinquagintaquadringentillionths".to_owned()),
         ])
-        .accounts(vec![
-            ("dataAccount", data_account),
-            ("tokenProgram", token.0),
-            ("systemProgram", [0; 32]),
-        ])
+        .accounts(vec![("tokenProgram", token.0), ("systemProgram", [0; 32])])
         .call();
 }
 
@@ -749,10 +731,6 @@ fn pda() {
     vm.call_params_check.insert(token.clone(), test_args);
 
     vm.function("test")
-        .accounts(vec![
-            ("dataAccount", data_account),
-            ("tokenProgram", token.0),
-            ("systemProgram", [0; 32]),
-        ])
+        .accounts(vec![("tokenProgram", token.0), ("systemProgram", [0; 32])])
         .call();
 }

+ 2 - 10
tests/solana_tests/constant.rs

@@ -25,11 +25,7 @@ fn constant() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
-    let returns = vm
-        .function("f")
-        .accounts(vec![("dataAccount", data_account)])
-        .call()
-        .unwrap();
+    let returns = vm.function("f").call().unwrap();
     assert_eq!(
         returns,
         BorshToken::Uint {
@@ -58,11 +54,7 @@ fn constant() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
-    let returns = vm
-        .function("f")
-        .accounts(vec![("dataAccount", data_account)])
-        .call()
-        .unwrap();
+    let returns = vm.function("f").call().unwrap();
     assert_eq!(
         returns,
         BorshToken::Uint {

+ 60 - 87
tests/solana_tests/create_contract.rs

@@ -11,8 +11,8 @@ fn simple_create_contract_no_seed() {
     let mut vm = build_solidity(
         r#"
         contract bar0 {
-            function test_other(address foo) external returns (bar1) {
-                bar1 x = new bar1{address: foo}("yo from bar0");
+            function test_other() external returns (bar1) {
+                bar1 x = new bar1("yo from bar0");
 
                 return x;
             }
@@ -65,9 +65,9 @@ fn simple_create_contract_no_seed() {
 
     let bar1 = vm
         .function("test_other")
-        .arguments(&[BorshToken::Address(acc)])
         .accounts(vec![
-            ("dataAccount", data_account),
+            ("bar1_dataAccount", acc),
+            ("bar1_programId", program_id),
             ("payer", payer),
             ("systemProgram", [0; 32]),
         ])
@@ -88,21 +88,9 @@ fn simple_create_contract_no_seed() {
     vm.function("call_bar1_at_address")
         .arguments(&[bar1, BorshToken::String(String::from("xywoleh"))])
         .accounts(vec![
-            ("dataAccount", data_account),
+            ("bar1_programId", program_id),
             ("systemProgram", [0; 32]),
         ])
-        .remaining_accounts(&[
-            AccountMeta {
-                pubkey: Pubkey(acc),
-                is_signer: false,
-                is_writable: false,
-            },
-            AccountMeta {
-                pubkey: Pubkey(program_id),
-                is_writable: false,
-                is_signer: false,
-            },
-        ])
         .call();
 
     assert_eq!(vm.logs, "Hello xywoleh");
@@ -113,8 +101,8 @@ fn simple_create_contract() {
     let mut vm = build_solidity(
         r#"
         contract bar0 {
-            function test_other(address foo) external returns (bar1) {
-                bar1 x = new bar1{address: foo}("yo from bar0");
+            function test_other() external returns (bar1) {
+                bar1 x = new bar1("yo from bar0");
 
                 return x;
             }
@@ -156,17 +144,12 @@ fn simple_create_contract() {
 
     let bar1 = vm
         .function("test_other")
-        .arguments(&[BorshToken::Address(seed.0)])
         .accounts(vec![
-            ("dataAccount", data_account),
+            ("bar1_dataAccount", seed.0),
+            ("bar1_programId", program_id),
             ("pay", payer),
             ("systemProgram", [0; 32]),
         ])
-        .remaining_accounts(&[AccountMeta {
-            pubkey: Pubkey(seed.0),
-            is_signer: false,
-            is_writable: true,
-        }])
         .call()
         .unwrap();
 
@@ -179,21 +162,9 @@ fn simple_create_contract() {
     vm.function("call_bar1_at_address")
         .arguments(&[bar1, BorshToken::String(String::from("xywoleh"))])
         .accounts(vec![
-            ("dataAccount", data_account),
+            ("bar1_programId", program_id),
             ("systemProgram", [0; 32]),
         ])
-        .remaining_accounts(&[
-            AccountMeta {
-                pubkey: Pubkey(seed.0),
-                is_signer: false,
-                is_writable: false,
-            },
-            AccountMeta {
-                pubkey: Pubkey(program_id),
-                is_writable: false,
-                is_signer: false,
-            },
-        ])
         .call();
 
     assert_eq!(vm.logs, "Hello xywoleh");
@@ -309,8 +280,8 @@ fn missing_contract() {
     let mut vm = build_solidity(
         r#"
         contract bar0 {
-            function test_other(address foo) external returns (bar1) {
-                bar1 x = new bar1{address: foo}("yo from bar0");
+            function test_other() external returns (bar1) {
+                bar1 x = new bar1("yo from bar0");
 
                 return x;
             }
@@ -341,6 +312,7 @@ fn missing_contract() {
     let missing = account_new();
     vm.logs.clear();
     vm.account_data.insert(missing, AccountState::default());
+
     let program_id: Account = "7vJKRaKLGCNUPuHWdeHCTknkYf3dHXXEZ6ri7dc6ngeV"
         .from_base58()
         .unwrap()
@@ -350,23 +322,11 @@ fn missing_contract() {
     // There is no payer account, so the external call fails.
     let _ = vm
         .function("test_other")
-        .arguments(&[BorshToken::Address(missing)])
         .accounts(vec![
-            ("dataAccount", data_account),
+            ("bar1_programId", program_id),
+            ("bar1_dataAccount", missing),
             ("systemProgram", [0; 32]),
         ])
-        .remaining_accounts(&[
-            AccountMeta {
-                pubkey: Pubkey(missing),
-                is_signer: true,
-                is_writable: false,
-            },
-            AccountMeta {
-                pubkey: Pubkey(program_id),
-                is_writable: false,
-                is_signer: false,
-            },
-        ])
         .must_fail();
 }
 
@@ -374,10 +334,20 @@ fn missing_contract() {
 fn two_contracts() {
     let mut vm = build_solidity(
         r#"
+        import 'solana';
+
         contract bar0 {
-            function test_other(address a, address b) external returns (bar1) {
-                bar1 x = new bar1{address: a}("yo from bar0");
-                bar1 y = new bar1{address: b}("hi from bar0");
+            function test_other(address a, address b, address payer) external returns (bar1) {
+                AccountMeta[2] bar1_metas = [
+                    AccountMeta({pubkey: a, is_writable: true, is_signer: true}),
+                    AccountMeta({pubkey: payer, is_writable: true, is_signer: true})
+                ];
+                AccountMeta[2] bar2_metas = [
+                    AccountMeta({pubkey: b, is_writable: true, is_signer: true}),
+                    AccountMeta({pubkey: payer, is_writable: true, is_signer: true})
+                ];
+                bar1 x = new bar1{accounts: bar1_metas}("yo from bar0");
+                bar1 y = new bar1{accounts: bar2_metas}("hi from bar0");
 
                 return x;
             }
@@ -407,18 +377,19 @@ fn two_contracts() {
     let seed1 = vm.create_pda(&program_id, 5);
     let seed2 = vm.create_pda(&program_id, 5);
     let payer = account_new();
+
     vm.account_data.insert(seed1.0, AccountState::default());
     vm.account_data.insert(seed2.0, AccountState::default());
     vm.account_data.insert(payer, AccountState::default());
 
     let _bar1 = vm
         .function("test_other")
-        .arguments(&[BorshToken::Address(seed1.0), BorshToken::Address(seed2.0)])
-        .accounts(vec![
-            ("dataAccount", data_account),
-            ("systemProgram", [0; 32]),
-            ("payer_account", payer),
+        .arguments(&[
+            BorshToken::Address(seed1.0),
+            BorshToken::Address(seed2.0),
+            BorshToken::Address(payer),
         ])
+        .accounts(vec![("systemProgram", [0; 32])])
         .remaining_accounts(&[
             AccountMeta {
                 pubkey: Pubkey(seed1.0),
@@ -435,6 +406,11 @@ fn two_contracts() {
                 is_writable: false,
                 is_signer: false,
             },
+            AccountMeta {
+                pubkey: Pubkey(payer),
+                is_signer: true,
+                is_writable: true,
+            },
         ])
         .call();
 
@@ -507,11 +483,7 @@ fn account_with_space() {
         306
     );
 
-    let ret = vm
-        .function("hello")
-        .accounts(vec![("dataAccount", data_account)])
-        .call()
-        .unwrap();
+    let ret = vm.function("hello").call().unwrap();
 
     assert_eq!(ret, BorshToken::Bool(true));
 }
@@ -552,11 +524,7 @@ fn account_with_seed() {
         511 + 102
     );
 
-    let ret = vm
-        .function("hello")
-        .accounts(vec![("dataAccount", seed.0)])
-        .call()
-        .unwrap();
+    let ret = vm.function("hello").call().unwrap();
 
     assert_eq!(ret, BorshToken::Bool(true));
 }
@@ -607,11 +575,7 @@ fn account_with_seed_bump() {
         511 + 102
     );
 
-    let ret = vm
-        .function("hello")
-        .accounts(vec![("dataAccount", address)])
-        .call()
-        .unwrap();
+    let ret = vm.function("hello").call().unwrap();
 
     assert_eq!(ret, BorshToken::Bool(true));
 }
@@ -655,11 +619,7 @@ fn account_with_seed_bump_literals() {
         8192
     );
 
-    let ret = vm
-        .function("hello")
-        .accounts(vec![("dataAccount", account.0)])
-        .call()
-        .unwrap();
+    let ret = vm.function("hello").call().unwrap();
 
     assert_eq!(ret, BorshToken::Bool(true));
 }
@@ -671,10 +631,9 @@ fn create_child() {
         contract creator {
             Child public c;
 
-            function create_child(address child) external {
+            function create_child() external {
                 print("Going to create child");
-                c = new Child{address: child}();
-
+                c = new Child();
                 c.say_hello();
             }
         }
@@ -699,6 +658,12 @@ fn create_child() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
+    let child_program_id: Account = "Chi1d5XD6nTAp2EyaNGqMxZzUjh6NvhXRxbGHP3D1RaT"
+        .from_base58()
+        .unwrap()
+        .try_into()
+        .unwrap();
+
     let payer = account_new();
     let program_id = vm.stack[0].id;
 
@@ -707,8 +672,9 @@ fn create_child() {
     vm.account_data.insert(seed.0, AccountState::default());
 
     vm.function("create_child")
-        .arguments(&[BorshToken::Address(seed.0)])
         .accounts(vec![
+            ("Child_dataAccount", seed.0),
+            ("Child_programId", child_program_id),
             ("dataAccount", data_account),
             ("payer", payer),
             ("systemProgram", [0; 32]),
@@ -774,9 +740,16 @@ contract Child {
     vm.account_data.insert(seed.0, AccountState::default());
     vm.account_data.insert(payer, AccountState::default());
 
+    let child_program_id: Account = "Chi1d5XD6nTAp2EyaNGqMxZzUjh6NvhXRxbGHP3D1RaT"
+        .from_base58()
+        .unwrap()
+        .try_into()
+        .unwrap();
+
     vm.function("create_child_with_meta")
         .arguments(&[BorshToken::Address(seed.0), BorshToken::Address(payer)])
         .accounts(vec![
+            ("Child_programId", child_program_id),
             ("dataAccount", data_account),
             ("systemProgram", [0; 32]),
         ])

+ 1 - 9
tests/solana_tests/destructure.rs

@@ -26,7 +26,6 @@ fn conditional_destructure() {
     let returns = vm
         .function("f")
         .arguments(&[BorshToken::Bool(true), BorshToken::Bool(true)])
-        .accounts(vec![("dataAccount", data_account)])
         .call()
         .unwrap()
         .unwrap_tuple();
@@ -48,7 +47,6 @@ fn conditional_destructure() {
     let returns = vm
         .function("f")
         .arguments(&[BorshToken::Bool(true), BorshToken::Bool(false)])
-        .accounts(vec![("dataAccount", data_account)])
         .call()
         .unwrap()
         .unwrap_tuple();
@@ -70,7 +68,6 @@ fn conditional_destructure() {
     let returns = vm
         .function("f")
         .arguments(&[BorshToken::Bool(false), BorshToken::Bool(false)])
-        .accounts(vec![("dataAccount", data_account)])
         .call()
         .unwrap()
         .unwrap_tuple();
@@ -92,7 +89,6 @@ fn conditional_destructure() {
     let returns = vm
         .function("f")
         .arguments(&[BorshToken::Bool(false), BorshToken::Bool(true)])
-        .accounts(vec![("dataAccount", data_account)])
         .call()
         .unwrap()
         .unwrap_tuple();
@@ -169,11 +165,7 @@ fn casting_destructure() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
-    let returns = vm
-        .function("f")
-        .accounts(vec![("dataAccount", data_account)])
-        .call()
-        .unwrap();
+    let returns = vm.function("f").call().unwrap();
 
     assert_eq!(returns, BorshToken::String(String::from("Hello")));
 }

+ 2 - 6
tests/solana_tests/events.rs

@@ -28,9 +28,7 @@ fn simple_event() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
-    vm.function("go")
-        .accounts(vec![("dataAccount", data_account)])
-        .call();
+    vm.function("go").call();
 
     assert_eq!(vm.events.len(), 1);
     assert_eq!(vm.events[0].len(), 1);
@@ -87,9 +85,7 @@ fn less_simple_event() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
-    vm.function("go")
-        .accounts(vec![("dataAccount", data_account)])
-        .call();
+    vm.function("go").call();
 
     assert_eq!(vm.events.len(), 1);
     assert_eq!(vm.events[0].len(), 1);

+ 5 - 26
tests/solana_tests/expressions.rs

@@ -25,11 +25,7 @@ fn interfaceid() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
-    let returns = vm
-        .function("get")
-        .accounts(vec![("dataAccount", data_account)])
-        .call()
-        .unwrap();
+    let returns = vm.function("get").call().unwrap();
 
     assert_eq!(
         returns,
@@ -52,7 +48,7 @@ fn write_buffer() {
             function test2() public returns (bytes) {
                 bytes bs = new bytes(34);
                 bs.writeUint16LE(0x4142, 0);
-                bs.writeAddress(tx.program_id, 2);
+                bs.writeAddress(address(this), 2);
                 return bs;
             }
 
@@ -69,32 +65,21 @@ fn write_buffer() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
-    let returns = vm
-        .function("test1")
-        .accounts(vec![("dataAccount", data_account)])
-        .call()
-        .unwrap();
+    let returns = vm.function("test1").call().unwrap();
 
     assert_eq!(
         returns,
         BorshToken::Bytes([0xbc, 0xbc, 0xbd, 0xbe, 8, 7, 6, 5, 4, 3, 2, 1].to_vec())
     );
 
-    let returns = vm
-        .function("test2")
-        .accounts(vec![("dataAccount", data_account)])
-        .call()
-        .unwrap();
+    let returns = vm.function("test2").call().unwrap();
 
     let mut buf = vec![0x42u8, 0x41u8];
     buf.extend_from_slice(&vm.stack[0].id);
 
     assert_eq!(returns, BorshToken::Bytes(buf));
 
-    let res = vm
-        .function("test3")
-        .accounts(vec![("dataAccount", data_account)])
-        .must_fail();
+    let res = vm.function("test3").must_fail();
     assert_eq!(res.unwrap(), 4294967296);
 }
 
@@ -123,7 +108,6 @@ fn read_buffer() {
         .arguments(&[BorshToken::Bytes(
             [0xbc, 0xbc, 0xbd, 0xbe, 8, 7, 6, 5, 4, 3, 2, 1].to_vec(),
         )])
-        .accounts(vec![("dataAccount", data_account)])
         .call()
         .unwrap()
         .unwrap_tuple();
@@ -147,7 +131,6 @@ fn read_buffer() {
         .arguments(&[BorshToken::Bytes(
             [0xbc, 0xbc, 0xbd, 0xbe, 8, 7, 6, 5, 4, 3, 2].to_vec(),
         )])
-        .accounts(vec![("dataAccount", data_account)])
         .must_fail();
     assert_eq!(res.unwrap(), 4294967296);
 
@@ -158,7 +141,6 @@ fn read_buffer() {
     let returns = vm
         .function("test2")
         .arguments(&[BorshToken::Bytes(buf.clone())])
-        .accounts(vec![("dataAccount", data_account)])
         .call()
         .unwrap()
         .unwrap_tuple();
@@ -179,7 +161,6 @@ fn read_buffer() {
     let res = vm
         .function("test2")
         .arguments(&[BorshToken::Bytes(buf)])
-        .accounts(vec![("dataAccount", data_account)])
         .must_fail();
     assert_eq!(res.unwrap(), 4294967296);
 }
@@ -207,7 +188,6 @@ fn bytes_compare() {
     let returns = vm
         .function("test1")
         .arguments(&[BorshToken::FixedBytes([0xbc, 0xbc, 0xbd, 0xbe].to_vec())])
-        .accounts(vec![("dataAccount", data_account)])
         .call()
         .unwrap();
 
@@ -216,7 +196,6 @@ fn bytes_compare() {
     let returns = vm
         .function("test2")
         .arguments(&[BorshToken::FixedBytes([0xbc, 0xbc, 0xbd, 0xbe].to_vec())])
-        .accounts(vec![("dataAccount", data_account)])
         .call()
         .unwrap();
 

+ 6 - 24
tests/solana_tests/hash.rs

@@ -21,10 +21,7 @@ fn constants_hash_tests() {
         .accounts(vec![("dataAccount", data_account)])
         .call();
 
-    runtime
-        .function("test")
-        .accounts(vec![("dataAccount", data_account)])
-        .call();
+    runtime.function("test").call();
 
     let mut runtime = build_solidity(
         r#"
@@ -42,10 +39,7 @@ fn constants_hash_tests() {
         .function("new")
         .accounts(vec![("dataAccount", data_account)])
         .call();
-    runtime
-        .function("test")
-        .accounts(vec![("dataAccount", data_account)])
-        .call();
+    runtime.function("test").call();
 
     let mut runtime = build_solidity(
         r#"
@@ -63,10 +57,7 @@ fn constants_hash_tests() {
         .function("new")
         .accounts(vec![("dataAccount", data_account)])
         .call();
-    runtime
-        .function("test")
-        .accounts(vec![("dataAccount", data_account)])
-        .call();
+    runtime.function("test").call();
 }
 
 #[test]
@@ -90,10 +81,7 @@ fn hash_tests() {
     let hash = runtime
         .function("test")
         .arguments(&[BorshToken::Bytes(b"Hello, World!".to_vec())])
-        .accounts(vec![
-            ("dataAccount", data_account),
-            ("systemProgram", [0; 32]),
-        ])
+        .accounts(vec![("systemProgram", [0; 32])])
         .call()
         .unwrap();
 
@@ -123,10 +111,7 @@ fn hash_tests() {
     let hash = runtime
         .function("test")
         .arguments(&[BorshToken::Bytes(b"Hello, World!".to_vec())])
-        .accounts(vec![
-            ("dataAccount", data_account),
-            ("systemProgram", [0; 32]),
-        ])
+        .accounts(vec![("systemProgram", [0; 32])])
         .call()
         .unwrap();
 
@@ -157,10 +142,7 @@ fn hash_tests() {
     let hash = runtime
         .function("test")
         .arguments(&[BorshToken::Bytes(b"Hello, World!".to_vec())])
-        .accounts(vec![
-            ("dataAccount", data_account),
-            ("systemProgram", [0; 32]),
-        ])
+        .accounts(vec![("systemProgram", [0; 32])])
         .call()
         .unwrap();
 

+ 0 - 6
tests/solana_tests/math.rs

@@ -56,7 +56,6 @@ fn safe_math() {
                 value: BigInt::from_str("4000000000000000000").unwrap(),
             },
         ])
-        .accounts(vec![("dataAccount", data_account)])
         .call()
         .unwrap();
 
@@ -80,7 +79,6 @@ fn safe_math() {
                 value: BigInt::from_str("4000000000000000000").unwrap(),
             },
         ])
-        .accounts(vec![("dataAccount", data_account)])
         .call()
         .unwrap();
 
@@ -104,7 +102,6 @@ fn safe_math() {
                 value: BigInt::from_str("1000000000000000000").unwrap(),
             },
         ])
-        .accounts(vec![("dataAccount", data_account)])
         .call()
         .unwrap();
 
@@ -127,7 +124,6 @@ fn safe_math() {
                 value: BigInt::from_str("400000000000000000000000000000000000000").unwrap(),
             },
         ])
-        .accounts(vec![("dataAccount", data_account)])
         .must_fail();
 
     vm.function(
@@ -144,7 +140,6 @@ fn safe_math() {
             },
         ],
     )
-        .accounts(vec![("dataAccount", data_account)])
         .must_fail();
 
     vm.function("sub_test")
@@ -158,6 +153,5 @@ fn safe_math() {
                 value: BigInt::from_str("4000000000000000000").unwrap(),
             },
         ])
-        .accounts(vec![("dataAccount", data_account)])
         .must_fail();
 }

+ 0 - 4
tests/solana_tests/metas.rs

@@ -141,7 +141,6 @@ contract Foo {
     let res = vm
         .function("token_account")
         .arguments(&[BorshToken::Address(account)])
-        .accounts(vec![("dataAccount", data_account)])
         .remaining_accounts(&[AccountMeta {
             pubkey: Pubkey(account),
             is_signer: false,
@@ -190,7 +189,6 @@ contract Foo {
     let res = vm
         .function("token_account")
         .arguments(&[BorshToken::Address(account)])
-        .accounts(vec![("dataAccount", data_account)])
         .remaining_accounts(&[AccountMeta {
             pubkey: Pubkey(account),
             is_signer: false,
@@ -289,7 +287,6 @@ contract Foo {
 
     let res = vm
         .function("mint_account")
-        .accounts(vec![("dataAccount", data_account)])
         .arguments(&[BorshToken::Address(account)])
         .remaining_accounts(&[AccountMeta {
             pubkey: Pubkey(account),
@@ -327,7 +324,6 @@ contract Foo {
 
     let res = vm
         .function("mint_account")
-        .accounts(vec![("dataAccount", data_account)])
         .arguments(&[BorshToken::Address(account)])
         .remaining_accounts(&[AccountMeta {
             pubkey: Pubkey(account),

部分文件因为文件数量过多而无法显示