From aede9546cff6ebdc99abcfad51c0f2d34be56342 Mon Sep 17 00:00:00 2001 From: Lucas Steuernagel Date: Thu, 17 Aug 2023 16:30:03 -0300 Subject: [PATCH] Represent contracts by their program id Signed-off-by: Lucas Steuernagel --- Cargo.toml | 1 + docs/examples/solana/contract_address.sol | 2 +- docs/examples/solana/contract_new.sol | 23 - docs/examples/solana/payer_annotation.sol | 8 +- docs/examples/solana/program_id.sol | 6 +- docs/language/builtins.rst | 4 - docs/language/contracts.rst | 12 +- docs/language/expressions.rst | 4 +- docs/language/managing_values.rst | 3 +- docs/targets/solana.rst | 8 +- integration/anchor/tests/anchor.ts | 2 +- integration/polkadot/call_flags.sol | 2 +- integration/polkadot/issue666.sol | 2 +- integration/solana/balances.spec.ts | 2 - integration/solana/builtins.spec.ts | 2 - integration/solana/calls.spec.ts | 51 +- integration/solana/create_contract.sol | 21 +- integration/solana/create_contract.spec.ts | 47 +- integration/solana/events.spec.ts | 1 - integration/solana/runtime_errors.spec.ts | 18 +- integration/solana/simple.spec.ts | 84 +-- integration/solana/simple_collectible.spec.ts | 1 - integration/solana/system_instruction.spec.ts | 13 - integration/solana/token.spec.ts | 9 - integration/solana/verify_sig.spec.ts | 2 - src/abi/tests.rs | 652 ++++-------------- src/bin/languageserver/mod.rs | 3 - src/codegen/cfg.rs | 8 +- src/codegen/constant_folding.rs | 2 + src/codegen/constructor.rs | 18 +- src/codegen/dispatch/solana.rs | 15 +- src/codegen/expression.rs | 15 +- src/codegen/mod.rs | 3 +- .../solana_accounts/account_collection.rs | 201 +++--- .../solana_accounts/account_management.rs | 179 ++--- src/codegen/solana_deploy.rs | 57 +- src/codegen/statements/try_catch.rs | 1 + .../subexpression_elimination/instruction.rs | 2 + src/emit/solana/mod.rs | 8 +- src/emit/solana/target.rs | 80 +-- src/sema/ast.rs | 23 +- src/sema/builtin.rs | 13 +- src/sema/contracts.rs | 9 +- src/sema/dotgraphviz.rs | 3 - src/sema/expression/constructor.rs | 58 +- src/sema/expression/function_call.rs | 90 +-- src/sema/expression/member_access.rs | 4 + src/sema/expression/mod.rs | 20 + src/sema/function_annotation.rs | 6 + src/sema/mutability.rs | 111 ++- src/sema/namespace.rs | 2 + src/sema/solana_accounts.rs | 13 +- src/sema/statements.rs | 22 +- src/sema/tests/data_account.rs | 224 ++++++ src/sema/tests/mod.rs | 9 +- src/sema/unused_variable.rs | 3 - src/sema/variables.rs | 8 +- src/sema/yul/tests/expression.rs | 93 +-- stdlib/solana.c | 60 +- .../solidity/constructor_with_metas.sol | 6 +- .../solidity/solana_bump.sol | 6 +- .../solidity/solana_payer_account.sol | 17 +- .../solidity/unused_variable_elimination.sol | 2 +- .../solana/account_meta.sol | 2 +- .../solana/accounts/constructor_in_loop.sol | 123 ++++ .../solana/accounts/data_account.sol | 26 + .../accounts/data_account_visibility.sol | 8 + .../solana/accounts/double_calls.sol | 51 ++ .../solana/address_cast.sol | 2 +- .../annotations/account_name_collision.sol | 4 +- .../constructor_external_function.sol | 13 +- .../solana/call/call_args_three_ways.sol | 2 +- .../solana/expressions/contract_no_init.sol | 2 +- .../solana/mapping_deletion.sol | 4 +- .../solana/yul/yul_switch.sol | 3 +- tests/imports_testcases/Dummy.json | 21 + ...6807c38c2116c0f3192e9a8d2bede4f34846d2.sol | 2 +- ...46b924357689419072fddf402c3ad3c10cd095.sol | 2 +- tests/polkadot_tests/calls.rs | 2 +- tests/polkadot_tests/contracts.rs | 2 +- tests/polkadot_tests/yul.rs | 2 +- tests/solana.rs | 8 +- tests/solana_tests/abi.rs | 20 +- tests/solana_tests/abi_decode.rs | 15 +- tests/solana_tests/abi_encode.rs | 32 +- tests/solana_tests/accessor.rs | 30 +- tests/solana_tests/account_info.rs | 42 +- tests/solana_tests/account_serialization.rs | 8 +- tests/solana_tests/arrays.rs | 99 +-- tests/solana_tests/balance.rs | 216 ++++-- tests/solana_tests/builtin.rs | 47 +- tests/solana_tests/call.rs | 58 +- tests/solana_tests/constant.rs | 12 +- tests/solana_tests/create_contract.rs | 147 ++-- tests/solana_tests/destructure.rs | 10 +- tests/solana_tests/events.rs | 8 +- tests/solana_tests/expressions.rs | 31 +- tests/solana_tests/hash.rs | 30 +- tests/solana_tests/math.rs | 6 - tests/solana_tests/metas.rs | 4 - tests/solana_tests/optimizations.rs | 38 +- tests/solana_tests/primitives.rs | 118 ++-- tests/solana_tests/rational.rs | 49 +- tests/solana_tests/returns.rs | 58 +- tests/solana_tests/runtime_errors.rs | 19 +- tests/solana_tests/signature_verify.rs | 15 +- tests/solana_tests/simple.rs | 28 +- tests/solana_tests/using.rs | 24 +- tests/solana_tests/vector_to_slice.rs | 6 +- tests/solana_tests/yul.rs | 39 +- 110 files changed, 1805 insertions(+), 2057 deletions(-) delete mode 100644 docs/examples/solana/contract_new.sol create mode 100644 src/sema/tests/data_account.rs create mode 100644 tests/contract_testcases/solana/accounts/constructor_in_loop.sol create mode 100644 tests/contract_testcases/solana/accounts/data_account.sol create mode 100644 tests/contract_testcases/solana/accounts/data_account_visibility.sol create mode 100644 tests/contract_testcases/solana/accounts/double_calls.sol create mode 100644 tests/imports_testcases/Dummy.json diff --git a/Cargo.toml b/Cargo.toml index ea5b81f19..98cda6185 100644 --- a/Cargo.toml +++ b/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" diff --git a/docs/examples/solana/contract_address.sol b/docs/examples/solana/contract_address.sol index 01dc96bf4..4626baf93 100644 --- a/docs/examples/solana/contract_address.sol +++ b/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"); } } diff --git a/docs/examples/solana/contract_new.sol b/docs/examples/solana/contract_new.sol deleted file mode 100644 index 8f7f5f78d..000000000 --- a/docs/examples/solana/contract_new.sol +++ /dev/null @@ -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)); - } -} diff --git a/docs/examples/solana/payer_annotation.sol b/docs/examples/solana/payer_annotation.sol index 4924d5761..bc6b44b78 100644 --- a/docs/examples/solana/payer_annotation.sol +++ b/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 { diff --git a/docs/examples/solana/program_id.sol b/docs/examples/solana/program_id.sol index dfe9da60b..8152d5f87 100644 --- a/docs/examples/solana/program_id.sol +++ b/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(); } } diff --git a/docs/language/builtins.rst b/docs/language/builtins.rst index d5c6c5d68..663abdf08 100644 --- a/docs/language/builtins.rst +++ b/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 ++++++++++++++++++++++ diff --git a/docs/language/contracts.rst b/docs/language/contracts.rst index 8ae3f39b3..cf8ea4990 100644 --- a/docs/language/contracts.rst +++ b/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 diff --git a/docs/language/expressions.rst b/docs/language/expressions.rst index 9c9878570..1c5e04c94 100644 --- a/docs/language/expressions.rst +++ b/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 __________________ diff --git a/docs/language/managing_values.rst b/docs/language/managing_values.rst index 38667378d..2b4382fe0 100644 --- a/docs/language/managing_values.rst +++ b/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 diff --git a/docs/targets/solana.rst b/docs/targets/solana.rst index 68e0398fb..3995281d0 100644 --- a/docs/targets/solana.rst +++ b/docs/targets/solana.rst @@ -45,9 +45,11 @@ Runtime - The Solana target requires `Solana `_ 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 ` 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 ` the necessary accounts for the transaction. +- The keyword ``this`` returns the contract's program account, also know as program id. + Compute budget ++++++++++++++ diff --git a/integration/anchor/tests/anchor.ts b/integration/anchor/tests/anchor.ts index 4f7f2ebf7..b4b0f5d86 100644 --- a/integration/anchor/tests/anchor.ts +++ b/integration/anchor/tests/anchor.ts @@ -17,7 +17,7 @@ describe("Anchor", () => { const program = anchor.workspace.Anchor as Program; - 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; diff --git a/integration/polkadot/call_flags.sol b/integration/polkadot/call_flags.sol index 9da059a43..fc903fc5f 100644 --- a/integration/polkadot/call_flags.sol +++ b/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); } } diff --git a/integration/polkadot/issue666.sol b/integration/polkadot/issue666.sol index 3dcc0dfbc..ae62d5f21 100644 --- a/integration/polkadot/issue666.sol +++ b/integration/polkadot/issue666.sol @@ -11,7 +11,7 @@ contract Inc { _flipper = _flipperContract; } - function superFlip () pure public { + function superFlip () view public { _flipper.flip(); } } diff --git a/integration/solana/balances.spec.ts b/integration/solana/balances.spec.ts index de3439237..d82efebc2 100644 --- a/integration/solana/balances.spec.ts +++ b/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 } diff --git a/integration/solana/builtins.spec.ts b/integration/solana/builtins.spec.ts index e46854603..20f41bd81 100644 --- a/integration/solana/builtins.spec.ts +++ b/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); diff --git a/integration/solana/calls.spec.ts b/integration/solana/calls.spec.ts index 7a8cfb748..9bafcdcc7 100644 --- a/integration/solana/calls.spec.ts +++ b/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)); diff --git a/integration/solana/create_contract.sol b/integration/solana/create_contract.sol index 36423ec62..4b12718b6 100644 --- a/integration/solana/create_contract.sol +++ b/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."); } } diff --git a/integration/solana/create_contract.spec.ts b/integration/solana/create_contract.spec.ts index aff92c313..4ebbd9488 100644 --- a/integration/solana/create_contract.spec.ts +++ b/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, { diff --git a/integration/solana/events.spec.ts b/integration/solana/events.spec.ts index d16fd242b..e676c4a9e 100644 --- a/integration/solana/events.spec.ts +++ b/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]; diff --git a/integration/solana/runtime_errors.spec.ts b/integration/solana/runtime_errors.spec.ts index 6166f2888..5ea4f7f6e 100644 --- a/integration/solana/runtime_errors.spec.ts +++ b/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, diff --git a/integration/solana/simple.spec.ts b/integration/solana/simple.spec.ts index 0c7ebe541..906736b28 100644 --- a/integration/solana/simple.spec.ts +++ b/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]); diff --git a/integration/solana/simple_collectible.spec.ts b/integration/solana/simple_collectible.spec.ts index 6e4115942..a9959401c 100644 --- a/integration/solana/simple_collectible.spec.ts +++ b/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 }, diff --git a/integration/solana/system_instruction.spec.ts b/integration/solana/system_instruction.spec.ts index 41a99cf87..930272f9a 100644 --- a/integration/solana/system_instruction.spec.ts +++ b/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(); }); }); diff --git a/integration/solana/token.spec.ts b/integration/solana/token.spec.ts index b090e1456..d50b5f883 100644 --- a/integration/solana/token.spec.ts +++ b/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(); diff --git a/integration/solana/verify_sig.spec.ts b/integration/solana/verify_sig.spec.ts index 474224037..120d239ed 100644 --- a/integration/solana/verify_sig.spec.ts +++ b/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(); diff --git a/src/abi/tests.rs b/src/abi/tests.rs index a42acea4d..a657746a8 100644 --- a/src/abi/tests.rs +++ b/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![], + }) +} diff --git a/src/bin/languageserver/mod.rs b/src/bin/languageserver/mod.rs index a4786b166..587f6a1ff 100644 --- a/src/bin/languageserver/mod.rs +++ b/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); } diff --git a/src/codegen/cfg.rs b/src/codegen/cfg.rs index bcecb16dc..fac877008 100644 --- a/src/codegen/cfg.rs +++ b/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, address: Option, accounts: Option, @@ -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, } #[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); } } diff --git a/src/codegen/constant_folding.rs b/src/codegen/constant_folding.rs index 164fcf153..32b54bc91 100644 --- a/src/codegen/constant_folding.rs +++ b/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, diff --git a/src/codegen/constructor.rs b/src/codegen/constructor.rs index 0926aee4d..2616c8ddc 100644 --- a/src/codegen/constructor.rs +++ b/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() diff --git a/src/codegen/dispatch/solana.rs b/src/codegen/dispatch/solana.rs index f21922917..82353b5b3 100644 --- a/src/codegen/dispatch/solana.rs +++ b/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); } diff --git a/src/codegen/expression.rs b/src/codegen/expression.rs index 82b243355..6fb36d0b7 100644 --- a/src/codegen/expression.rs +++ b/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, diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 4e934a6b6..cf22f3ed1 100644 --- a/src/codegen/mod.rs +++ b/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, diff --git a/src/codegen/solana_accounts/account_collection.rs b/src/codegen/solana_accounts/account_collection.rs index 90bc90274..dc94a2dd8 100644 --- a/src/codegen/solana_accounts/account_collection.rs +++ b/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)); +} diff --git a/src/codegen/solana_accounts/account_management.rs b/src/codegen/solana_accounts/account_management.rs index 36c05009b..a35f6c7e1 100644 --- a/src/codegen/solana_accounts/account_management.rs +++ b/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 = 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 = 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, - }; + _ => (), } } diff --git a/src/codegen/solana_deploy.rs b/src/codegen/solana_deploy.rs index 072cfcb43..5db682a5f 100644 --- a/src/codegen/solana_deploy.rs +++ b/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 { diff --git a/src/codegen/statements/try_catch.rs b/src/codegen/statements/try_catch.rs index aab518a67..909d46bbe 100644 --- a/src/codegen/statements/try_catch.rs +++ b/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, diff --git a/src/codegen/subexpression_elimination/instruction.rs b/src/codegen/subexpression_elimination/instruction.rs index 6488c8c36..b3e36def5 100644 --- a/src/codegen/subexpression_elimination/instruction.rs +++ b/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, diff --git a/src/emit/solana/mod.rs b/src/emit/solana/mod.rs index 2de319146..9cfbf728c 100644 --- a/src/emit/solana/mod.rs +++ b/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(), diff --git a/src/emit/solana/target.rs b/src/emit/solana/target.rs index 0fdb97746..f0e08a9c3 100644 --- a/src/emit/solana/target.rs +++ b/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, diff --git a/src/sema/ast.rs b/src/sema/ast.rs index cda906af8..e875be7ad 100644 --- a/src/sema/ast.rs +++ b/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 (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>, pub salt: Option>, pub value: Option>, - pub address: Option>, pub accounts: Option>, pub seeds: Option>, pub flags: Option>, @@ -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, diff --git a/src/sema/builtin.rs b/src/sema/builtin.rs index 4d123fa4d..2b8e38018 100644 --- a/src/sema/builtin.rs +++ b/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"), diff --git a/src/sema/contracts.rs b/src/sema/contracts.rs index f88ff0d97..abaf4a6b6 100644 --- a/src/sema/contracts.rs +++ b/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); diff --git a/src/sema/dotgraphviz.rs b/src/sema/dotgraphviz.rs index 90ec6082b..a92310715 100644 --- a/src/sema/dotgraphviz.rs +++ b/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")); } diff --git a/src/sema/expression/constructor.rs b/src/sema/expression/constructor.rs index c8a01554c..09ed0ee8a 100644 --- a/src/sema/expression/constructor.rs +++ b/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(), + )); + } + } +} diff --git a/src/sema/expression/function_call.rs b/src/sema/expression/function_call.rs index 15bf2af36..784012f72 100644 --- a/src/sema/expression/function_call.rs +++ b/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) diff --git a/src/sema/expression/member_access.rs b/src/sema/expression/member_access.rs index 06632fc17..c5105a2d9 100644 --- a/src/sema/expression/member_access.rs +++ b/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, diff --git a/src/sema/expression/mod.rs b/src/sema/expression/mod.rs index d63a167bf..a2e8cc981 100644 --- a/src/sema/expression/mod.rs +++ b/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 { diff --git a/src/sema/function_annotation.rs b/src/sema/function_annotation.rs index 47af14750..2a669fb9b 100644 --- a/src/sema/function_annotation.rs +++ b/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] diff --git a/src/sema/mutability.rs b/src/sema/mutability.rs index beecdf296..78f4ec065 100644 --- a/src/sema/mutability.rs +++ b/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, ns: &'a Namespace, + data_account: DataAccountUsage, } impl<'a> StateCheck<'a> { @@ -148,6 +163,7 @@ fn check_mutability(func: &Function, ns: &Namespace) -> Vec { 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 { } } + 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, diff --git a/src/sema/namespace.rs b/src/sema/namespace.rs index 30e70827e..69504fc66 100644 --- a/src/sema/namespace.rs +++ b/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(_) => {} diff --git a/src/sema/solana_accounts.rs b/src/sema/solana_accounts.rs index 00f5a9c33..88c20036f 100644 --- a/src/sema/solana_accounts.rs +++ b/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() } } diff --git a/src/sema/statements.rs b/src/sema/statements.rs index 991b46b0d..a03094cb3 100644 --- a/src/sema/statements.rs +++ b/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 = 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, - 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)>, Box)>, clause_stmts: &[pt::CatchClause], - context: &ExprContext, + context: &mut ExprContext, symtable: &mut Symtable, loops: &mut LoopScopes, ns: &mut Namespace, diff --git a/src/sema/tests/data_account.rs b/src/sema/tests/data_account.rs new file mode 100644 index 000000000..b10dab299 --- /dev/null +++ b/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 + ); +} diff --git a/src/sema/tests/mod.rs b/src/sema/tests/mod.rs index c7b790a88..0c04d70a2 100644 --- a/src/sema/tests/mod.rs +++ b/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" ); } diff --git a/src/sema/unused_variable.rs b/src/sema/unused_variable.rs index badf28f2b..b39dc576d 100644 --- a/src/sema/unused_variable.rs +++ b/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); } diff --git a/src/sema/variables.rs b/src/sema/variables.rs index 58c61dd28..d75c8bde3 100644 --- a/src/sema/variables.rs +++ b/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); diff --git a/src/sema/yul/tests/expression.rs b/src/sema/yul/tests/expression.rs index e1d78cb5a..c6141e468 100644 --- a/src/sema/yul/tests/expression.rs +++ b/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); diff --git a/stdlib/solana.c b/stdlib/solana.c index 071b37da7..4001c6b4e 100644 --- a/stdlib/solana.c +++ b/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 = ¶ms->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) diff --git a/tests/codegen_testcases/solidity/constructor_with_metas.sol b/tests/codegen_testcases/solidity/constructor_with_metas.sol index 8fb910fb5..c3060d08a 100644 --- a/tests/codegen_testcases/solidity/constructor_with_metas.sol +++ b/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"); } diff --git a/tests/codegen_testcases/solidity/solana_bump.sol b/tests/codegen_testcases/solidity/solana_bump.sol index 6402cde68..91be8f041 100644 --- a/tests/codegen_testcases/solidity/solana_bump.sol +++ b/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: } \ No newline at end of file diff --git a/tests/codegen_testcases/solidity/solana_payer_account.sol b/tests/codegen_testcases/solidity/solana_payer_account.sol index 56da9ead9..c4e59795c 100644 --- a/tests/codegen_testcases/solidity/solana_payer_account.sol +++ b/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 { diff --git a/tests/codegen_testcases/solidity/unused_variable_elimination.sol b/tests/codegen_testcases/solidity/unused_variable_elimination.sol index 4c6b34212..b952580ae 100644 --- a/tests/codegen_testcases/solidity/unused_variable_elimination.sol +++ b/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 diff --git a/tests/contract_testcases/solana/account_meta.sol b/tests/contract_testcases/solana/account_meta.sol index c187423a0..9a7c7c756 100644 --- a/tests/contract_testcases/solana/account_meta.sol +++ b/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' diff --git a/tests/contract_testcases/solana/accounts/constructor_in_loop.sol b/tests/contract_testcases/solana/accounts/constructor_in_loop.sol new file mode 100644 index 000000000..de1c502e0 --- /dev/null +++ b/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 diff --git a/tests/contract_testcases/solana/accounts/data_account.sol b/tests/contract_testcases/solana/accounts/data_account.sol new file mode 100644 index 000000000..7d3cfeff4 --- /dev/null +++ b/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 diff --git a/tests/contract_testcases/solana/accounts/data_account_visibility.sol b/tests/contract_testcases/solana/accounts/data_account_visibility.sol new file mode 100644 index 000000000..c39ddcd35 --- /dev/null +++ b/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 \ No newline at end of file diff --git a/tests/contract_testcases/solana/accounts/double_calls.sol b/tests/contract_testcases/solana/accounts/double_calls.sol new file mode 100644 index 000000000..fb271704e --- /dev/null +++ b/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 diff --git a/tests/contract_testcases/solana/address_cast.sol b/tests/contract_testcases/solana/address_cast.sol index ee6617814..bba23c1ae 100644 --- a/tests/contract_testcases/solana/address_cast.sol +++ b/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); } } diff --git a/tests/contract_testcases/solana/annotations/account_name_collision.sol b/tests/contract_testcases/solana/annotations/account_name_collision.sol index bbf2e24c9..1405b0a2e 100644 --- a/tests/contract_testcases/solana/annotations/account_name_collision.sol +++ b/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"); } } diff --git a/tests/contract_testcases/solana/annotations/constructor_external_function.sol b/tests/contract_testcases/solana/annotations/constructor_external_function.sol index a3e37d39d..0f10ac5cd 100644 --- a/tests/contract_testcases/solana/annotations/constructor_external_function.sol +++ b/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 \ No newline at end of file +// 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 diff --git a/tests/contract_testcases/solana/call/call_args_three_ways.sol b/tests/contract_testcases/solana/call/call_args_three_ways.sol index 6a038c87c..8e92be1af 100644 --- a/tests/contract_testcases/solana/call/call_args_three_ways.sol +++ b/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 diff --git a/tests/contract_testcases/solana/expressions/contract_no_init.sol b/tests/contract_testcases/solana/expressions/contract_no_init.sol index 6bd8e521e..ca162c075 100644 --- a/tests/contract_testcases/solana/expressions/contract_no_init.sol +++ b/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 diff --git a/tests/contract_testcases/solana/mapping_deletion.sol b/tests/contract_testcases/solana/mapping_deletion.sol index 8e90f879c..327df92e1 100644 --- a/tests/contract_testcases/solana/mapping_deletion.sol +++ b/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 \ No newline at end of file +// 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 diff --git a/tests/contract_testcases/solana/yul/yul_switch.sol b/tests/contract_testcases/solana/yul/yul_switch.sol index 968c4ebad..aa8bc5a88 100644 --- a/tests/contract_testcases/solana/yul/yul_switch.sol +++ b/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' \ No newline at end of file diff --git a/tests/imports_testcases/Dummy.json b/tests/imports_testcases/Dummy.json new file mode 100644 index 000000000..6a23f6aaa --- /dev/null +++ b/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": [] + } + ] +} \ No newline at end of file diff --git a/tests/optimization_testcases/programs/f46807c38c2116c0f3192e9a8d2bede4f34846d2.sol b/tests/optimization_testcases/programs/f46807c38c2116c0f3192e9a8d2bede4f34846d2.sol index e00dd56a2..2149254d4 100644 --- a/tests/optimization_testcases/programs/f46807c38c2116c0f3192e9a8d2bede4f34846d2.sol +++ b/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); } } diff --git a/tests/optimization_testcases/programs/f646b924357689419072fddf402c3ad3c10cd095.sol b/tests/optimization_testcases/programs/f646b924357689419072fddf402c3ad3c10cd095.sol index d3142dd35..3ab322486 100644 --- a/tests/optimization_testcases/programs/f646b924357689419072fddf402c3ad3c10cd095.sol +++ b/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; } diff --git a/tests/polkadot_tests/calls.rs b/tests/polkadot_tests/calls.rs index 14f87058b..d0bee1ecd 100644 --- a/tests/polkadot_tests/calls.rs +++ b/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; diff --git a/tests/polkadot_tests/contracts.rs b/tests/polkadot_tests/contracts.rs index eb0bd3281..ddd6844d1 100644 --- a/tests/polkadot_tests/contracts.rs +++ b/tests/polkadot_tests/contracts.rs @@ -205,7 +205,7 @@ fn issue666() { _flipper = _flipperContract; } - function superFlip () pure public { + function superFlip () view public { _flipper.flip(); } }"#, diff --git a/tests/polkadot_tests/yul.rs b/tests/polkadot_tests/yul.rs index ef6858e6f..3844658b4 100644 --- a/tests/polkadot_tests/yul.rs +++ b/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 diff --git a/tests/solana.rs b/tests/solana.rs index 8da8bb854..b2f3f0f57 100644 --- a/tests/solana.rs +++ b/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 } diff --git a/tests/solana_tests/abi.rs b/tests/solana_tests/abi.rs index 12415a61a..880a16cf0 100644 --- a/tests/solana_tests/abi.rs +++ b/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#" diff --git a/tests/solana_tests/abi_decode.rs b/tests/solana_tests/abi_decode.rs index 3f80af8f7..08c1270b8 100644 --- a/tests/solana_tests/abi_decode.rs +++ b/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(); } diff --git a/tests/solana_tests/abi_encode.rs b/tests/solana_tests/abi_encode.rs index 4ad751f85..a7244efbe 100644 --- a/tests/solana_tests/abi_encode.rs +++ b/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() diff --git a/tests/solana_tests/accessor.rs b/tests/solana_tests/accessor.rs index b4b1b0980..6efcbb39d 100644 --- a/tests/solana_tests/accessor.rs +++ b/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"); } }"#, diff --git a/tests/solana_tests/account_info.rs b/tests/solana_tests/account_info.rs index c1f92fff5..2d3d16109 100644 --- a/tests/solana_tests/account_info.rs +++ b/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, diff --git a/tests/solana_tests/account_serialization.rs b/tests/solana_tests/account_serialization.rs index 79a4bfbfc..c00487da1 100644 --- a/tests/solana_tests/account_serialization.rs +++ b/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(); } diff --git a/tests/solana_tests/arrays.rs b/tests/solana_tests/arrays.rs index dbfe2072c..f751b9c23 100644 --- a/tests/solana_tests/arrays.rs +++ b/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] diff --git a/tests/solana_tests/balance.rs b/tests/solana_tests/balance.rs index d4a224b58..fa096a8ff 100644 --- a/tests/solana_tests/balance.rs +++ b/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(); diff --git a/tests/solana_tests/builtin.rs b/tests/solana_tests/builtin.rs index 4449c0797..7342f29d2 100644 --- a/tests/solana_tests/builtin.rs +++ b/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(); diff --git a/tests/solana_tests/call.rs b/tests/solana_tests/call.rs index aa037b678..90c5a1499 100644 --- a/tests/solana_tests/call.rs +++ b/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(); } diff --git a/tests/solana_tests/constant.rs b/tests/solana_tests/constant.rs index 63b7cdd07..3718662c7 100644 --- a/tests/solana_tests/constant.rs +++ b/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 { diff --git a/tests/solana_tests/create_contract.rs b/tests/solana_tests/create_contract.rs index 749f86ecc..0a6a5dfaf 100644 --- a/tests/solana_tests/create_contract.rs +++ b/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]), ]) diff --git a/tests/solana_tests/destructure.rs b/tests/solana_tests/destructure.rs index 6a627904f..a5b5bdeda 100644 --- a/tests/solana_tests/destructure.rs +++ b/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"))); } diff --git a/tests/solana_tests/events.rs b/tests/solana_tests/events.rs index 6dbedb75a..d817dba05 100644 --- a/tests/solana_tests/events.rs +++ b/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); diff --git a/tests/solana_tests/expressions.rs b/tests/solana_tests/expressions.rs index 2c05f142c..e2a7e5b9f 100644 --- a/tests/solana_tests/expressions.rs +++ b/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(); diff --git a/tests/solana_tests/hash.rs b/tests/solana_tests/hash.rs index ffb5a0de4..a87b981bf 100644 --- a/tests/solana_tests/hash.rs +++ b/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(); diff --git a/tests/solana_tests/math.rs b/tests/solana_tests/math.rs index 9f83415f2..57944ee93 100644 --- a/tests/solana_tests/math.rs +++ b/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(); } diff --git a/tests/solana_tests/metas.rs b/tests/solana_tests/metas.rs index d3c00b45a..fb2057c48 100644 --- a/tests/solana_tests/metas.rs +++ b/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), diff --git a/tests/solana_tests/optimizations.rs b/tests/solana_tests/optimizations.rs index 9cc171fca..592da99ba 100644 --- a/tests/solana_tests/optimizations.rs +++ b/tests/solana_tests/optimizations.rs @@ -2,10 +2,12 @@ use crate::{ borsh_encoding::{visit_mut, VisitorMut}, - BorshToken, VirtualMachineBuilder, + AccountMeta, BorshToken, Pubkey, VirtualMachineBuilder, }; +use anchor_syn::idl::IdlAccountItem; use once_cell::sync::Lazy; -use rayon::prelude::*; +use rayon::iter::ParallelIterator; +use rayon::prelude::IntoParallelIterator; use serde::Deserialize; use solang::codegen::Options; use std::{ @@ -42,6 +44,7 @@ fn optimizations() { .map(|entry| entry.unwrap().path()) .collect::>(); tests.into_par_iter().for_each(|path| run_test(&path)); + //tests.iter().for_each(|path| run_test(path)); } } @@ -91,13 +94,38 @@ fn run_test_with_opts>(program: &str, calls: &Ca .call_with_error_code(), ); + let program_id = vm.stack[0].id; for (name, args) in &calls.function { - results_curr.push( + let needs_account = vm.stack[0] + .idl + .as_ref() + .unwrap() + .instructions + .iter() + .find(|instr| &instr.name == name) + .unwrap() + .accounts + .iter() + .any(|acc| match acc { + IdlAccountItem::IdlAccount(account) => account.name == "dataAccount", + IdlAccountItem::IdlAccounts(_) => false, + }); + + results_curr.push(if needs_account { vm.function(name) .arguments(args) .accounts(vec![("dataAccount", data_account)]) - .call_with_error_code(), - ); + .call_with_error_code() + } else { + vm.function(name) + .arguments(args) + .remaining_accounts(&[AccountMeta { + pubkey: Pubkey(program_id), + is_signer: false, + is_writable: false, + }]) + .call_with_error_code() + }); } for token in results_curr.iter_mut().flatten().flatten() { diff --git a/tests/solana_tests/primitives.rs b/tests/solana_tests/primitives.rs index 1fb64f91e..10537adab 100644 --- a/tests/solana_tests/primitives.rs +++ b/tests/solana_tests/primitives.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 -use crate::{build_solidity, BorshToken}; +use crate::{build_solidity, Account, BorshToken}; +use base58::FromBase58; use num_bigint::{BigInt, BigUint, RandBigInt, ToBigInt}; use num_traits::{One, Pow, ToPrimitive, Zero}; use rand::seq::SliceRandom; @@ -54,9 +55,7 @@ fn assert_true() { .accounts(vec![("dataAccount", data_account)]) .call(); - vm.function("assert_fails") - .accounts(vec![("dataAccount", data_account)]) - .call(); + vm.function("assert_fails").call(); } #[test] @@ -91,29 +90,19 @@ fn boolean() { .accounts(vec![("dataAccount", data_account)]) .call(); - let returns = vm - .function("return_true") - .accounts(vec![("dataAccount", data_account)]) - .call() - .unwrap(); + let returns = vm.function("return_true").call().unwrap(); assert_eq!(returns, BorshToken::Bool(true)); - let returns = vm - .function("return_false") - .accounts(vec![("dataAccount", data_account)]) - .call() - .unwrap(); + let returns = vm.function("return_false").call().unwrap(); assert_eq!(returns, BorshToken::Bool(false)); vm.function("true_arg") .arguments(&[BorshToken::Bool(true)]) - .accounts(vec![("dataAccount", data_account)]) .call(); vm.function("false_arg") .arguments(&[BorshToken::Bool(false)]) - .accounts(vec![("dataAccount", data_account)]) .call(); } @@ -142,11 +131,7 @@ fn address() { .accounts(vec![("dataAccount", data_account)]) .call(); - let returns = vm - .function("return_address") - .accounts(vec![("dataAccount", data_account)]) - .call() - .unwrap(); + let returns = vm.function("return_address").call().unwrap(); assert_eq!( returns, @@ -161,7 +146,6 @@ fn address() { 75, 161, 209, 89, 47, 84, 50, 13, 23, 127, 94, 21, 50, 249, 250, 185, 117, 49, 186, 134, 82, 130, 112, 97, 218, 24, 157, 198, 40, 105, 118, 27, ])]) - .accounts(vec![("dataAccount", data_account)]) .call(); } @@ -191,11 +175,7 @@ fn test_enum() { .accounts(vec![("dataAccount", data_account)]) .call(); - let returns = vm - .function("return_enum") - .accounts(vec![("dataAccount", data_account)]) - .call() - .unwrap(); + let returns = vm.function("return_enum").call().unwrap(); assert_eq!( returns, @@ -210,7 +190,6 @@ fn test_enum() { width: 8, value: BigInt::from(6u8), }]) - .accounts(vec![("dataAccount", data_account)]) .call(); } @@ -259,11 +238,7 @@ fn bytes() { .accounts(vec![("dataAccount", data_account)]) .call(); - let returns = vm - .function("return_literal") - .accounts(vec![("dataAccount", data_account)]) - .call() - .unwrap(); + let returns = vm.function("return_literal").call().unwrap(); assert_eq!( returns, @@ -273,7 +248,6 @@ fn bytes() { let returns = vm .function("return_arg") .arguments(&[BorshToken::FixedBytes(vec![1, 2, 3, 4, 5, 6, 7])]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -298,7 +272,6 @@ fn bytes() { BorshToken::FixedBytes(a.to_vec()), BorshToken::FixedBytes(b.to_vec()), ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -319,7 +292,6 @@ fn bytes() { BorshToken::FixedBytes(a.to_vec()), BorshToken::FixedBytes(b.to_vec()), ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -333,7 +305,6 @@ fn bytes() { BorshToken::FixedBytes(a.to_vec()), BorshToken::FixedBytes(b.to_vec()), ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -354,7 +325,6 @@ fn bytes() { value: BigInt::from(r), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -379,7 +349,6 @@ fn bytes() { value: BigInt::from(r), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -487,7 +456,6 @@ fn uint() { width, value: a.to_bigint().unwrap(), }]) - .accounts(vec![("dataAccount", data_account)]) .call(); println!("{a:x} = {res:?} o"); @@ -504,7 +472,6 @@ fn uint() { value: b.to_bigint().unwrap(), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -533,7 +500,6 @@ fn uint() { value: b.to_bigint().unwrap(), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -560,7 +526,6 @@ fn uint() { value: b.to_bigint().unwrap(), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -589,7 +554,6 @@ fn uint() { value: BigInt::from(n), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -619,7 +583,6 @@ fn uint() { value: b.to_bigint().unwrap(), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -647,7 +610,6 @@ fn uint() { value: b.to_bigint().unwrap(), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -676,7 +638,6 @@ fn uint() { value: b.to_bigint().unwrap(), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -703,7 +664,6 @@ fn uint() { value: b.to_bigint().unwrap(), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -730,7 +690,6 @@ fn uint() { value: b.to_bigint().unwrap(), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -759,7 +718,6 @@ fn uint() { value: BigInt::from(r), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -787,7 +745,6 @@ fn uint() { value: BigInt::from(r), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -850,7 +807,6 @@ fn test_power_overflow_boundaries() { value: BigInt::from(width - 1), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -876,7 +832,6 @@ fn test_power_overflow_boundaries() { value: BigInt::from(width + 1), }, ]) - .accounts(vec![("dataAccount", data_account)]) .must_fail(); assert_ne!(sesa.unwrap(), 0); @@ -923,7 +878,6 @@ fn test_overflow_boundaries() { value: second_op.clone(), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); assert_eq!( @@ -946,7 +900,6 @@ fn test_overflow_boundaries() { value: second_op.clone(), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); assert_eq!( @@ -980,7 +933,6 @@ fn test_overflow_boundaries() { value: BigInt::from(2u8), }, ]) - .accounts(vec![("dataAccount", data_account)]) .must_fail(); contract @@ -995,7 +947,6 @@ fn test_overflow_boundaries() { value: BigInt::from(2), }, ]) - .accounts(vec![("dataAccount", data_account)]) .must_fail(); contract @@ -1010,7 +961,6 @@ fn test_overflow_boundaries() { value: upper_boundary.clone(), }, ]) - .accounts(vec![("dataAccount", data_account)]) .must_fail(); contract @@ -1025,7 +975,6 @@ fn test_overflow_boundaries() { value: lower_boundary.clone(), }, ]) - .accounts(vec![("dataAccount", data_account)]) .must_fail(); contract @@ -1040,7 +989,6 @@ fn test_overflow_boundaries() { value: lower_boundary.clone(), }, ]) - .accounts(vec![("dataAccount", data_account)]) .must_fail(); } } @@ -1089,7 +1037,6 @@ fn test_mul_within_range_signed() { value: second_op.clone(), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -1146,7 +1093,6 @@ fn test_mul_within_range() { value: second_operand_rand.to_bigint().unwrap(), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); let res = first_operand_rand * second_operand_rand; @@ -1208,7 +1154,6 @@ fn test_overflow_detect_signed() { value: second_operand_rand.clone(), }, ]) - .accounts(vec![("dataAccount", data_account)]) .must_fail(); // The range of values that can be held in signed N bits is [-2^(N-1), 2^(N-1)-1] . @@ -1232,7 +1177,6 @@ fn test_overflow_detect_signed() { value: second_operand_rand.clone(), }, ]) - .accounts(vec![("dataAccount", data_account)]) .must_fail(); // neg fails when value -(2^N) @@ -1245,7 +1189,6 @@ fn test_overflow_detect_signed() { width: width as u16, value: lower_limit.clone(), }]) - .accounts(vec![("dataAccount", data_account)]) .must_fail(); lower_limit.add_assign(1usize); @@ -1258,7 +1201,6 @@ fn test_overflow_detect_signed() { width: width as u16, value: first_operand_rand, }]) - .accounts(vec![("dataAccount", data_account)]) .call(); } } @@ -1306,7 +1248,6 @@ fn test_overflow_detect_unsigned() { value: second_operand_rand.to_bigint().unwrap(), }, ]) - .accounts(vec![("dataAccount", data_account)]) .must_fail(); } } @@ -1394,7 +1335,6 @@ fn int() { value: b.clone(), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -1421,7 +1361,6 @@ fn int() { value: b.clone(), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -1448,7 +1387,6 @@ fn int() { value: b.clone(), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -1476,7 +1414,6 @@ fn int() { value: b.clone(), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -1503,7 +1440,6 @@ fn int() { value: b.clone(), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -1531,7 +1467,6 @@ fn int() { value: b.clone(), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -1558,7 +1493,6 @@ fn int() { value: b.clone(), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -1585,7 +1519,6 @@ fn int() { value: b.clone(), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -1614,7 +1547,6 @@ fn int() { value: BigInt::from(r), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -1642,7 +1574,6 @@ fn int() { value: BigInt::from(r), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -1695,7 +1626,6 @@ fn bytes_cast() { let returns = vm .function("to_bytes") .arguments(&[BorshToken::FixedBytes(b"abcd".to_vec())]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -1704,7 +1634,6 @@ fn bytes_cast() { let returns = vm .function("to_bytes5") .arguments(&[BorshToken::Bytes(b"abcde".to_vec())]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -1762,3 +1691,34 @@ fn shift_after_load() { } ); } + +#[test] +fn constant_program_id() { + let mut vm = build_solidity( + r#" + @program_id("5kQ3iJ43gHNDjqmSAtE1vDu18CiSAfNbRe4v5uoobh3U") +contract hatchling { + constructor() {} + + function getId() public view returns (address) { + return address(this); + } +} + "#, + ); + + let data_account = vm.initialize_data_account(); + vm.function("new") + .accounts(vec![("dataAccount", data_account)]) + .call(); + + let res = vm.function("getId").call().unwrap(); + + let program_id: Account = "5kQ3iJ43gHNDjqmSAtE1vDu18CiSAfNbRe4v5uoobh3U" + .from_base58() + .unwrap() + .try_into() + .unwrap(); + + assert_eq!(res, BorshToken::Address(program_id)); +} diff --git a/tests/solana_tests/rational.rs b/tests/solana_tests/rational.rs index dfac7d253..e7cb605b4 100644 --- a/tests/solana_tests/rational.rs +++ b/tests/solana_tests/rational.rs @@ -26,11 +26,7 @@ fn rational() { .accounts(vec![("dataAccount", data_account)]) .call(); - let returns = vm - .function("test") - .accounts(vec![("dataAccount", data_account)]) - .call() - .unwrap(); + let returns = vm.function("test").call().unwrap(); assert_eq!( returns, @@ -40,11 +36,7 @@ fn rational() { } ); - let returns = vm - .function("test2") - .accounts(vec![("dataAccount", data_account)]) - .call() - .unwrap(); + let returns = vm.function("test2").call().unwrap(); assert_eq!( returns, @@ -69,11 +61,7 @@ fn rational() { .accounts(vec![("dataAccount", data_account)]) .call(); - let returns = vm - .function("test") - .accounts(vec![("dataAccount", data_account)]) - .call() - .unwrap(); + let returns = vm.function("test").call().unwrap(); assert_eq!( returns, @@ -98,11 +86,7 @@ fn rational() { .accounts(vec![("dataAccount", data_account)]) .call(); - let returns = vm - .function("test") - .accounts(vec![("dataAccount", data_account)]) - .call() - .unwrap(); + let returns = vm.function("test").call().unwrap(); assert_eq!( returns, @@ -127,11 +111,7 @@ fn rational() { .accounts(vec![("dataAccount", data_account)]) .call(); - let returns = vm - .function("test") - .accounts(vec![("dataAccount", data_account)]) - .call() - .unwrap(); + let returns = vm.function("test").call().unwrap(); assert_eq!( returns, @@ -156,11 +136,7 @@ fn rational() { .accounts(vec![("dataAccount", data_account)]) .call(); - let returns = vm - .function("test") - .accounts(vec![("dataAccount", data_account)]) - .call() - .unwrap(); + let returns = vm.function("test").call().unwrap(); assert_eq!( returns, @@ -184,11 +160,7 @@ fn rational() { .accounts(vec![("dataAccount", data_account)]) .call(); - let returns = vm - .function("test") - .accounts(vec![("dataAccount", data_account)]) - .call() - .unwrap(); + let returns = vm.function("test").call().unwrap(); assert_eq!( returns, @@ -212,11 +184,7 @@ fn rational() { .accounts(vec![("dataAccount", data_account)]) .call(); - let returns = vm - .function("test") - .accounts(vec![("dataAccount", data_account)]) - .call() - .unwrap(); + let returns = vm.function("test").call().unwrap(); assert_eq!( returns, @@ -246,7 +214,6 @@ fn rational() { width: 64, value: BigInt::from(982451653u32), }]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap() .unwrap_tuple(); diff --git a/tests/solana_tests/returns.rs b/tests/solana_tests/returns.rs index c01669446..09c55255a 100644 --- a/tests/solana_tests/returns.rs +++ b/tests/solana_tests/returns.rs @@ -36,11 +36,7 @@ fn return_single() { .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 { @@ -49,11 +45,7 @@ fn return_single() { }, ); - let returns = vm - .function("g") - .accounts(vec![("dataAccount", data_account)]) - .call() - .unwrap(); + let returns = vm.function("g").call().unwrap(); assert_eq!( returns, BorshToken::Uint { @@ -62,11 +54,7 @@ fn return_single() { }, ); - let returns = vm - .function("h") - .accounts(vec![("dataAccount", data_account)]) - .call() - .unwrap(); + let returns = vm.function("h").call().unwrap(); assert_eq!( returns, BorshToken::Uint { @@ -75,11 +63,7 @@ fn return_single() { }, ); - let returns = vm - .function("i") - .accounts(vec![("dataAccount", data_account)]) - .call() - .unwrap(); + let returns = vm.function("i").call().unwrap(); assert_eq!( returns, BorshToken::Uint { @@ -88,11 +72,7 @@ fn return_single() { }, ); - let returns = vm - .function("j") - .accounts(vec![("dataAccount", data_account)]) - .call() - .unwrap(); + let returns = vm.function("j").call().unwrap(); assert_eq!( returns, BorshToken::Uint { @@ -117,12 +97,7 @@ fn return_ternary() { vm.function("new") .accounts(vec![("dataAccount", data_account)]) .call(); - let returns = vm - .function("f") - .accounts(vec![("dataAccount", data_account)]) - .call() - .unwrap() - .unwrap_tuple(); + let returns = vm.function("f").call().unwrap().unwrap_tuple(); assert_eq!( returns, @@ -151,12 +126,7 @@ fn return_ternary() { vm.function("new") .accounts(vec![("dataAccount", data_account)]) .call(); - let returns = vm - .function("f") - .accounts(vec![("dataAccount", data_account)]) - .call() - .unwrap() - .unwrap_tuple(); + let returns = vm.function("f").call().unwrap().unwrap_tuple(); assert_eq!( returns, @@ -286,12 +256,7 @@ fn return_function() { vm.function("new") .accounts(vec![("dataAccount", data_account)]) .call(); - let returns = vm - .function("f") - .accounts(vec![("dataAccount", data_account)]) - .call() - .unwrap() - .unwrap_tuple(); + let returns = vm.function("f").call().unwrap().unwrap_tuple(); assert_eq!( returns, @@ -324,12 +289,7 @@ fn return_function() { vm.function("new") .accounts(vec![("dataAccount", data_account)]) .call(); - let returns = vm - .function("f") - .accounts(vec![("dataAccount", data_account)]) - .call() - .unwrap() - .unwrap_tuple(); + let returns = vm.function("f").call().unwrap().unwrap_tuple(); assert_eq!( returns, diff --git a/tests/solana_tests/runtime_errors.rs b/tests/solana_tests/runtime_errors.rs index d2be702fe..3e987a4d0 100644 --- a/tests/solana_tests/runtime_errors.rs +++ b/tests/solana_tests/runtime_errors.rs @@ -146,7 +146,6 @@ contract calle_contract { width: 8, value: BigInt::from(10u8), }]) - .accounts(vec![("dataAccount", data_account)]) .must_fail(); assert_eq!( vm.logs, @@ -160,7 +159,6 @@ contract calle_contract { width: 256, value: BigInt::from(9u8), }]) - .accounts(vec![("dataAccount", data_account)]) .must_fail(); assert_eq!( @@ -199,7 +197,6 @@ contract calle_contract { width: 32, value: BigInt::from(2u8), }]) - .accounts(vec![("dataAccount", data_account)]) .must_fail(); assert_eq!( @@ -214,7 +211,6 @@ contract calle_contract { width: 256, value: BigInt::from(u128::MAX), }]) - .accounts(vec![("dataAccount", data_account)]) .must_fail(); assert_eq!( @@ -223,10 +219,7 @@ contract calle_contract { ); vm.logs.clear(); - _res = vm - .function("invalid_instruction") - .accounts(vec![("dataAccount", data_account)]) - .must_fail(); + _res = vm.function("invalid_instruction").must_fail(); assert_eq!( vm.logs, @@ -253,7 +246,6 @@ contract calle_contract { width: 256, value: BigInt::from(9u8), }]) - .accounts(vec![("dataAccount", data_account)]) .must_fail(); assert_eq!( @@ -269,7 +261,6 @@ contract calle_contract { width: 256, value: BigInt::from(9u8), }]) - .accounts(vec![("dataAccount", data_account)]) .must_fail(); println!("{}", vm.logs); assert_eq!( @@ -284,7 +275,6 @@ contract calle_contract { width: 256, value: BigInt::from(19u8), }]) - .accounts(vec![("dataAccount", data_account)]) .must_fail(); assert_eq!( @@ -300,7 +290,6 @@ contract calle_contract { width: 256, value: BigInt::from(1u8), }]) - .accounts(vec![("dataAccount", data_account)]) .must_fail(); assert_eq!( @@ -316,7 +305,6 @@ contract calle_contract { width: 256, value: BigInt::from(33u8), }]) - .accounts(vec![("dataAccount", data_account)]) .must_fail(); assert_eq!( @@ -326,10 +314,7 @@ contract calle_contract { vm.logs.clear(); - _res = vm - .function("i_will_revert") - .accounts(vec![("dataAccount", data_account)]) - .must_fail(); + _res = vm.function("i_will_revert").must_fail(); assert_eq!( vm.logs, diff --git a/tests/solana_tests/signature_verify.rs b/tests/solana_tests/signature_verify.rs index 251268ae9..d4e206cbe 100644 --- a/tests/solana_tests/signature_verify.rs +++ b/tests/solana_tests/signature_verify.rs @@ -81,10 +81,7 @@ fn verify() { BorshToken::Bytes(message.to_vec()), BorshToken::Bytes(signature_bs.clone()), ]) - .accounts(vec![ - ("dataAccount", data_account), - ("SysvarInstruction", instructions_account), - ]) + .accounts(vec![("SysvarInstruction", instructions_account)]) .call() .unwrap(); @@ -116,10 +113,7 @@ fn verify() { BorshToken::Bytes(message.to_vec()), BorshToken::Bytes(signature_bs.clone()), ]) - .accounts(vec![ - ("dataAccount", data_account), - ("SysvarInstruction", instructions_account), - ]) + .accounts(vec![("SysvarInstruction", instructions_account)]) .call() .unwrap(); @@ -153,10 +147,7 @@ fn verify() { BorshToken::Bytes(message.to_vec()), BorshToken::Bytes(signature_bs), ]) - .accounts(vec![ - ("dataAccount", data_account), - ("SysvarInstruction", instructions_account), - ]) + .accounts(vec![("SysvarInstruction", instructions_account)]) .call() .unwrap(); diff --git a/tests/solana_tests/simple.rs b/tests/solana_tests/simple.rs index c59da8ca4..f37525bdb 100644 --- a/tests/solana_tests/simple.rs +++ b/tests/solana_tests/simple.rs @@ -29,9 +29,7 @@ fn simple() { vm.logs.truncate(0); - vm.function("test") - .accounts(vec![("dataAccount", data_account)]) - .call(); + vm.function("test").call(); assert_eq!(vm.logs, "Hello from function"); } @@ -93,7 +91,6 @@ fn parameters() { value: BigInt::from(10u8), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call(); assert_eq!(vm.logs, "x is 10"); @@ -111,7 +108,6 @@ fn parameters() { value: BigInt::from(102u8), }, ]) - .accounts(vec![("dataAccount", data_account)]) .call(); assert_eq!(vm.logs, "y is 102"); @@ -139,7 +135,6 @@ fn returns() { width: 32, value: BigInt::from(10u8), }]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -171,7 +166,6 @@ fn returns() { width: 64, value: BigInt::from(982451653u64), }]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap() .unwrap_tuple(); @@ -484,7 +478,6 @@ contract test3 { width: 32, value: BigInt::from(i), }]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -509,7 +502,6 @@ contract test3 { }, BorshToken::Bool(true), ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -538,7 +530,6 @@ contract test3 { }, BorshToken::Bool(false), ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -568,7 +559,6 @@ contract test3 { width: 32, value: BigInt::from(i), }]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap(); @@ -601,11 +591,7 @@ fn overloading() { .accounts(vec![("dataAccount", data_account)]) .call(); - let returns = vm - .function("foo_") - .accounts(vec![("dataAccount", data_account)]) - .call() - .unwrap(); + let returns = vm.function("foo_").call().unwrap(); assert_eq!( returns, @@ -615,11 +601,7 @@ fn overloading() { } ); - let returns = vm - .function("foo_bar") - .accounts(vec![("dataAccount", data_account)]) - .call() - .unwrap(); + let returns = vm.function("foo_bar").call().unwrap(); assert_eq!( returns, @@ -631,7 +613,6 @@ fn overloading() { let returns = vm .function("foo_address") - .accounts(vec![("dataAccount", data_account)]) .arguments(&[BorshToken::Address([0u8; 32])]) .call() .unwrap(); @@ -646,7 +627,6 @@ fn overloading() { let returns = vm .function("foo_bytes32") - .accounts(vec![("dataAccount", data_account)]) .arguments(&[BorshToken::FixedBytes(vec![0u8; 32])]) .call() .unwrap(); @@ -660,7 +640,6 @@ fn overloading() { ); let returns = vm .function("foo_bytes") - .accounts(vec![("dataAccount", data_account)]) .arguments(&[BorshToken::Bytes(vec![])]) .call() .unwrap(); @@ -675,7 +654,6 @@ fn overloading() { let returns = vm .function("foo_string") - .accounts(vec![("dataAccount", data_account)]) .arguments(&[BorshToken::String("yo".into())]) .call() .unwrap(); diff --git a/tests/solana_tests/using.rs b/tests/solana_tests/using.rs index 22a13cfe0..7cd651406 100644 --- a/tests/solana_tests/using.rs +++ b/tests/solana_tests/using.rs @@ -34,10 +34,7 @@ fn using_for_contracts() { .function("new") .accounts(vec![("dataAccount", data_account)]) .call(); - runtime - .function("test") - .accounts(vec![("dataAccount", data_account)]) - .call(); + runtime.function("test").call(); assert_eq!(runtime.logs, "Hello"); @@ -84,10 +81,12 @@ fn using_for_contracts() { .function("new") .accounts(vec![("dataAccount", data_account)]) .call(); + + let program_id = runtime.stack[0].id; runtime .function("test") .accounts(vec![ - ("dataAccount", data_account), + ("I_programId", program_id), ("systemProgram", [0; 32]), ]) .call(); @@ -225,16 +224,7 @@ fn user_defined_oper() { .accounts(vec![("dataAccount", data_account)]) .call(); - runtime - .function("test_cmp") - .accounts(vec![("dataAccount", data_account)]) - .call(); - runtime - .function("test_arith") - .accounts(vec![("dataAccount", data_account)]) - .call(); - runtime - .function("test_bit") - .accounts(vec![("dataAccount", data_account)]) - .call(); + runtime.function("test_cmp").call(); + runtime.function("test_arith").call(); + runtime.function("test_bit").call(); } diff --git a/tests/solana_tests/vector_to_slice.rs b/tests/solana_tests/vector_to_slice.rs index 3278e58f2..54245f7c0 100644 --- a/tests/solana_tests/vector_to_slice.rs +++ b/tests/solana_tests/vector_to_slice.rs @@ -24,11 +24,7 @@ fn test_slice_in_phi() { vm.function("new") .accounts(vec![("dataAccount", data_account)]) .call(); - let returns = vm - .function("test") - .accounts(vec![("dataAccount", data_account)]) - .call() - .unwrap(); + let returns = vm.function("test").call().unwrap(); assert_eq!(returns, BorshToken::String(String::from("Hello!"))); } diff --git a/tests/solana_tests/yul.rs b/tests/solana_tests/yul.rs index 2428737f3..68eac1260 100644 --- a/tests/solana_tests/yul.rs +++ b/tests/solana_tests/yul.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -use crate::{build_solidity, BorshToken}; +use crate::{build_solidity, AccountMeta, BorshToken, Pubkey}; use num_bigint::{BigInt, Sign}; use num_traits::{One, Zero}; @@ -32,7 +32,7 @@ contract testing { return ret; } - function call_data_array(uint32[] calldata vl) public pure returns (uint256, uint256) { + function call_data_array(uint32[] calldata vl) public view returns (uint256, uint256) { uint256 ret1 = 98; uint256 ret2 = 99; assembly { @@ -120,17 +120,17 @@ contract testing { let returns = vm .function("selector_address") - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap() .unwrap_tuple(); + let program_id = vm.stack[0].id; assert_eq!( returns, vec![ BorshToken::Uint { width: 256, - value: BigInt::from_bytes_be(Sign::Plus, data_account.as_ref()) + value: BigInt::from_bytes_be(Sign::Plus, program_id.as_ref()) }, BorshToken::Uint { width: 256, @@ -191,7 +191,6 @@ contract testing { width: 64, value: BigInt::from(5u8), }]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap() .unwrap_tuple(); @@ -216,7 +215,6 @@ contract testing { width: 64, value: BigInt::from(78u8), }]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap() .unwrap_tuple(); @@ -241,7 +239,6 @@ contract testing { width: 64, value: BigInt::from(259u16), }]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap() .unwrap_tuple(); @@ -488,7 +485,6 @@ fn external_function() { }, BorshToken::FixedBytes(vec![1, 2, 3, 4, 5, 6, 7, 8]), ]) - .accounts(vec![("dataAccount", data_account)]) .call() .unwrap() .unwrap_tuple(); @@ -528,16 +524,13 @@ contract testing { ); let data_account = runtime.initialize_data_account(); + let program_id = runtime.stack[0].id; runtime .function("new") .accounts(vec![("dataAccount", data_account)]) .call(); - let returns = runtime - .function("test_address") - .accounts(vec![("dataAccount", data_account)]) - .call() - .unwrap(); + let returns = runtime.function("test_address").call().unwrap(); let addr = returns.into_bigint().unwrap(); let mut b_vec = addr.to_bytes_be().1; // test_address() returns address as uint256. If the highest bits are 0, then addr.to_bytes_be().1 @@ -545,16 +538,16 @@ contract testing { while b_vec.len() < 32 { b_vec.insert(0, 0); } - assert_eq!(&b_vec, data_account.as_ref()); + assert_eq!(&b_vec, program_id.as_ref()); - runtime - .account_data - .get_mut(&data_account) - .unwrap() - .lamports = 102; + runtime.account_data.get_mut(&program_id).unwrap().lamports = 102; let returns = runtime .function("test_balance") - .accounts(vec![("dataAccount", data_account)]) + .remaining_accounts(&[AccountMeta { + pubkey: Pubkey(program_id), + is_writable: false, + is_signer: false, + }]) .call() .unwrap(); assert_eq!( @@ -567,7 +560,11 @@ contract testing { let returns = runtime .function("test_selfbalance") - .accounts(vec![("dataAccount", data_account)]) + .remaining_accounts(&[AccountMeta { + pubkey: Pubkey(program_id), + is_signer: false, + is_writable: false, + }]) .call() .unwrap();