فهرست منبع

Remove balance, transfer, and send from Solana (#1574)

Lucas Steuernagel 2 سال پیش
والد
کامیت
979cad9890

+ 71 - 49
docs/language/managing_values.rst

@@ -12,27 +12,36 @@ Value in Solidity is represented by ``uint128``.
     need support for a different type, please raise an
     `issue <https://github.com/hyperledger/solang/issues>`_.
 
+.. _balance:
+
 Checking your balance
 _____________________
 
-The balance of a contract can be checked with `address` ``.balance``, so your own balance
-is ``address(this).balance``.
+.. tabs::
 
-.. note::
-    Polkadot cannot check the balance for contracts other than the current
-    one. If you need to check the balance of another contract, then add a balance
-    function to that contract like the one below, and call that function instead.
+    .. group-tab:: Polkadot
 
-.. note::
-    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.
+        Polkadot cannot check the balance for contracts other than the current
+        one. If you need to check the balance of another contract, then add a balance
+        function to that contract like the one below, and call that function instead.
 
-.. code-block:: solidity
+        .. code-block:: solidity
+
+            function balance() public returns (uint128) {
+                return address(this).balance;
+            }
 
-    function balance() public returns (uint128) {
-        return address(this).balance;
-    }
+    .. group-tab:: Solana
+
+        On Solana, the balance of an account can be accessed using the ``lamports`` member of the ``AccountInfo``
+        struct. Every account whose value we want to check must be declared with an account annotation.
+
+        .. code-block::
+
+            @account(my_acc)
+            function balance() external returns (uint64) {
+                return tx.accounts.my_acc.lamports;
+            }
 
 Creating contracts with an initial value
 ________________________________________
@@ -53,50 +62,63 @@ explained in :ref:`passing value and gas with external calls <passing_value_gas>
 Sending value using ``send()`` and ``transfer()``
 _________________________________________________
 
-The ``send()`` and ``transfer()`` functions are available as method on a
-``address payable`` variable. The single arguments is the amount of value you
-would like to send. The difference between the two functions is what happens
-in the failure case: ``transfer()`` will revert the current call, ``send()``
-returns a ``bool`` which will be ``false``.
+.. tabs::
 
-In order for the receiving contract to receive the value, it needs a ``receive()``
-function, see :ref:`fallback() and receive() function <fallback_receive>`.
+    .. group-tab:: Polkadot
 
-Here is an example:
+        The ``send()`` and ``transfer()`` functions are available as method on a
+        ``address payable`` variable. The single arguments is the amount of value you
+        would like to send. The difference between the two functions is what happens
+        in the failure case: ``transfer()`` will revert the current call, ``send()``
+        returns a ``bool`` which will be ``false``.
 
-.. code-block:: solidity
+        In order for the receiving contract to receive the value, it needs a ``receive()``
+        function, see :ref:`fallback() and receive() function <fallback_receive>`.
 
-    contract A {
-        B other;
+        Here is an example:
 
-        constructor() {
-            other = new B();
+        .. code-block:: solidity
 
-            bool complete = payable(other).transfer(100);
+            contract A {
+                B other;
 
-            if (!complete) {
-                // oops
+                constructor() {
+                    other = new B();
+
+                    bool complete = payable(other).transfer(100);
+
+                    if (!complete) {
+                        // oops
+                    }
+
+                    // if the following fails, our transaction will fail
+                    other.send(100);
+                }
             }
 
-            // if the following fails, our transaction will fail
-            other.send(100);
-        }
-    }
+            contract B {
+                receive() payable external {
+                    // ..
+                }
+            }
 
-    contract B {
-        receive() payable external {
-            // ..
-        }
-    }
+        .. note::
+            On Subtrate, this uses the ``seal_transfer()`` mechanism rather than ``seal_call()``, since this
+            does not come with gas overhead. This means the ``receive()`` function is not required in the
+            receiving contract, and it will not be called if it is present. If you want the ``receive()``
+            function to be called, use ``address.call{value: 100}("")`` instead.
 
-.. note::
-    On Subtrate, this uses the ``seal_transfer()`` mechanism rather than ``seal_call()``, since this
-    does not come with gas overhead. This means the ``receive()`` function is not required in the
-    receiving contract, and it will not be called if it is present. If you want the ``receive()``
-    function to be called, use ``address.call{value: 100}("")`` instead.
+    .. group-tab:: Solana
 
-.. note::
-    On Solana, ``send()`` and ``transfer()`` can only transfer native tokens between accounts owned
-    by the contract's program account, since only the account owner can modify its balance.
-    Use the :ref:`system instruction library <system_instruction_library>` to transfer
-    native tokens between accounts owned by Solana's system program.
+        On Solana, there are no ``transfer`` and ``send`` functions. In order to alter the balance of accounts,
+        one might increment or decrement the ``lamports`` field from the ``AccountInfo`` struct directly. This
+        is only possible if the accounts whose balance is being changed are owned by the program.
+
+        .. code-block::
+
+            @mutableAccount(acc1)
+            @mutableAccount(acc2)
+            function transfer(uint64 amount) external {
+                tx.accounts.acc1.lamports += amount;
+                tx.accounts.acc2.lamports -= amount;
+            }

+ 2 - 0
docs/targets/solana.rst

@@ -52,6 +52,8 @@ Runtime
 - Contracts :ref:`cannot be types <contracts_not_types>` on Solana and :ref:`calls to contracts <solana_contract_call>`
   follow a different syntax.
 - Accounts can be declared on functions using :ref:`annotations <account_management>`.
+- :ref:`Retrieving the balance <balance>` and :ref:`transferring values <send_transfer>`
+  utilize the ``AccountInfo`` struct.
 
 
 Compute budget

+ 8 - 7
integration/solana/balances.sol

@@ -1,13 +1,14 @@
 contract balances {
-	function get_balance(address addr) public view returns (uint64) {
-		return addr.balance;
+    @account(acc1)
+	function get_balance() external view returns (uint64) {
+		return tx.accounts.acc1.lamports;
 	}
 
-	function transfer(address payable addr, uint64 amount) public {
-		addr.transfer(amount);
+    @mutableAccount(acc1)
+    @mutableAccount(acc2)
+	function transfer(uint64 amount) external {
+		tx.accounts.acc1.lamports -= amount;
+        tx.accounts.acc2.lamports += amount;
 	}
 
-	function send(address payable addr, uint64 amount) public returns (bool) {
-		return addr.send(amount);
-	}
 }

+ 17 - 16
integration/solana/balances.spec.ts

@@ -1,9 +1,9 @@
 // SPDX-License-Identifier: Apache-2.0
 
 import expect from 'expect';
-import { Transaction, SystemProgram, sendAndConfirmTransaction } from '@solana/web3.js';
+import {Transaction, SystemProgram, sendAndConfirmTransaction, Keypair, LAMPORTS_PER_SOL} from '@solana/web3.js';
 import { loadContractAndCallConstructor } from './setup';
-import { BN } from '@coral-xyz/anchor';
+import {BN} from '@coral-xyz/anchor';
 
 describe('Deploy solang contract and test', function () {
     this.timeout(500000);
@@ -11,8 +11,10 @@ describe('Deploy solang contract and test', function () {
     it('balances', async function () {
         let { program, storage, payer, provider } = await loadContractAndCallConstructor('balances', []);
 
-        let res = await program.methods.getBalance(payer.publicKey)
-            .remainingAccounts([{ pubkey: payer.publicKey, isSigner: false, isWritable: false }])
+        let res = await program.methods.getBalance()
+            .accounts({
+                acc1: payer.publicKey
+            })
             .view();
 
         let bal = Number(res);
@@ -21,29 +23,28 @@ describe('Deploy solang contract and test', function () {
 
         expect(bal + 5000).toBe(rpc_bal);
 
-        // we wish to test the `.send()` function, so first top up the storage balance
-        let before_bal = await provider.connection.getBalance(storage.publicKey);
-
         /// transfer some lamports to the storage account
-        const transaction = new Transaction().add(
+        let transaction = new Transaction().add(
             SystemProgram.transfer({
                 fromPubkey: payer.publicKey,
                 toPubkey: storage.publicKey,
-                lamports: 1500,
+                lamports: LAMPORTS_PER_SOL,
             }),
         );
 
         // Sign transaction, broadcast, and confirm
         await sendAndConfirmTransaction(provider.connection, transaction, [payer]);
 
-        await program.methods.send(payer.publicKey, new BN(500))
-            .remainingAccounts([
-                { pubkey: storage.publicKey, isSigner: true, isWritable: true },
-                { pubkey: payer.publicKey, isSigner: false, isWritable: true }
-            ])
-            .signers([storage])
+        const new_account = Keypair.generate();
+        const lamports = await provider.connection.getMinimumBalanceForRentExemption(100);
+
+        await program.methods.transfer(new BN(lamports))
+            .accounts({
+                acc1: storage.publicKey,
+                acc2: new_account.publicKey,
+            })
             .rpc();
 
-        expect(await provider.connection.getBalance(storage.publicKey)).toBe(before_bal + 1000);
+        expect(await provider.connection.getBalance(new_account.publicKey)).toBe(lamports);
     });
 });

+ 0 - 6
integration/solana/runtime_errors.sol

@@ -49,12 +49,6 @@ contract RuntimeErrors {
         return sesa;
     }
 
-    // value transfer failure
-    function transfer_abort() public {
-        address a = address(0);
-        payable(a).transfer(1e15);
-    }
-
     //  pop from empty storage array
     function pop_empty_storage() public {
         arr.pop();

+ 15 - 28
integration/solana/runtime_errors.spec.ts

@@ -1,10 +1,9 @@
 // SPDX-License-Identifier: Apache-2.0
 
-import { Keypair, PublicKey, sendAndConfirmTransaction, SystemProgram, Transaction } from '@solana/web3.js';
+import { Keypair } from '@solana/web3.js';
 import expect from 'expect';
 import { loadContractAndCallConstructor } from './setup';
-import { Program, Provider, BN, AnchorProvider } from '@coral-xyz/anchor';
-import { createAccount } from "@solana/spl-token";
+import { Program, Provider, BN } from '@coral-xyz/anchor';
 
 describe('Runtime Errors', function () {
     this.timeout(150000);
@@ -25,25 +24,22 @@ describe('Runtime Errors', function () {
         }
         catch (e: any) {
             const logs = e.simulationResponse.logs;
-            expect(logs).toContain(`Program log: runtime_error: storage index out of bounds in runtime_errors.sol:41:11-12,
-`);
+            expect(logs.toString()).toContain(`Program log: runtime_error: storage index out of bounds in runtime_errors.sol:41:11-12,`);
         }
 
         try {
-            let res = await program.methods.getStorageBytes().accounts({ dataAccount: storage.publicKey }).simulate();;
+            let res = await program.methods.getStorageBytes().accounts({ dataAccount: storage.publicKey }).simulate();
         }
         catch (e: any) {
             const logs = e.simulationResponse.logs;
-            expect(logs).toContain(`Program log: runtime_error: storage array index out of bounds in runtime_errors.sol:48:19-23,
-`);
+            expect(logs.toString()).toContain(`Program log: runtime_error: storage array index out of bounds in runtime_errors.sol:48:19-23,`);
         }
 
         try {
             let res = await program.methods.popEmptyStorage().accounts({ dataAccount: storage.publicKey }).simulate();
         } catch (e: any) {
             const logs = e.simulationResponse.logs;
-            expect(logs).toContain(`Program log: runtime_error: pop from empty storage array in runtime_errors.sol:60:9-12,
-`)
+            expect(logs.toString()).toContain(`Program log: runtime_error: pop from empty storage array in runtime_errors.sol:54:9-12,`)
 
         }
 
@@ -51,8 +47,7 @@ describe('Runtime Errors', function () {
             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,
-`)
+            expect(logs.toString()).toContain(`Program log: runtime_error: reached invalid instruction in runtime_errors.sol:95:13-22,`)
 
         }
 
@@ -60,8 +55,7 @@ describe('Runtime Errors', function () {
             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,
-`)
+            expect(logs.toString()).toContain(`Program log: runtime_error: bytes cast error in runtime_errors.sol:101:23-40,`)
 
         }
 
@@ -69,32 +63,28 @@ describe('Runtime Errors', function () {
             let res = await program.methods.iWillRevert().simulate();
         } catch (e: any) {
             const logs = e.simulationResponse.logs;
-            expect(logs).toContain(`Program log: runtime_error: unspecified revert encountered in runtime_errors.sol:69:9-17,
-`)
+            expect(logs.toString()).toContain(`Program log: runtime_error: unspecified revert encountered in runtime_errors.sol:63:9-17`)
         }
 
         try {
             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,
-`)
+            expect(logs.toString()).toContain(`Program log: runtime_error: assert failure in runtime_errors.sol:34:16-24`)
         }
 
         try {
             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,
-`)
+            expect(logs.toString()).toContain(`Program log: runtime_error: integer too large to write in buffer in runtime_errors.sol:68:18-31,`)
         }
 
         try {
             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,
-`)
+            expect(logs.toString()).toContain(`Program log: runtime_error: data does not fit into buffer in runtime_errors.sol:74:18-28,`)
         }
 
 
@@ -102,8 +92,7 @@ describe('Runtime Errors', function () {
             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,
-`)
+            expect(logs.toString()).toContain(`Program log: runtime_error: read integer out of bounds in runtime_errors.sol:79:18-30,`)
         }
 
 
@@ -111,8 +100,7 @@ describe('Runtime Errors', function () {
             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,
-`)
+            expect(logs.toString()).toContain(`Program log: runtime_error: array index out of bounds in runtime_errors.sol:90:16-21,`)
         }
 
 
@@ -120,8 +108,7 @@ describe('Runtime Errors', function () {
             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,
-`)
+            expect(logs.toString()).toContain(`Program log: runtime_error: truncated type overflows in runtime_errors.sol:84:37-42,`)
         }
 
     });

+ 2 - 6
src/codegen/expression.rs

@@ -1626,14 +1626,10 @@ fn payable_send(
         },
     );
 
-    if ns.target.is_polkadot() {
+    if ns.target != Target::Solana {
         polkadot::check_transfer_ret(loc, success, cfg, ns, opt, vartab, false).unwrap()
     } else {
-        Expression::Variable {
-            loc: *loc,
-            ty: Type::Bool,
-            var_no: success,
-        }
+        unreachable!("Value transfer does not exist on Solana");
     }
 }
 

+ 3 - 6
src/codegen/solana_accounts/account_collection.rs

@@ -263,12 +263,7 @@ fn check_instruction(instr: &Instr, data: &mut RecurseData) {
             }
         }
 
-        Instr::ValueTransfer {
-            address: expr1,
-            value: expr2,
-            ..
-        }
-        | Instr::ReturnData {
+        Instr::ReturnData {
             data: expr1,
             data_len: expr2,
         }
@@ -445,6 +440,8 @@ fn check_instruction(instr: &Instr, data: &mut RecurseData) {
                 expr.recurse(data, check_expression);
             }
         }
+
+        Instr::ValueTransfer { .. } => unreachable!("Value transfer does not exist on Solana"),
         Instr::AccountAccess { .. } => (),
     }
 }

+ 5 - 54
src/emit/solana/target.rs

@@ -1484,34 +1484,15 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
     /// Send value to address
     fn value_transfer<'b>(
         &self,
-        binary: &Binary<'b>,
+        _binary: &Binary<'b>,
         _function: FunctionValue,
-        success: Option<&mut BasicValueEnum<'b>>,
-        address: PointerValue<'b>,
-        value: IntValue<'b>,
+        _success: Option<&mut BasicValueEnum<'b>>,
+        _address: PointerValue<'b>,
+        _value: IntValue<'b>,
         _ns: &ast::Namespace,
         _loc: Loc,
     ) {
-        let parameters = self.sol_parameters(binary);
-
-        if let Some(success) = success {
-            *success = binary
-                .builder
-                .build_call(
-                    binary.module.get_function("sol_try_transfer").unwrap(),
-                    &[address.into(), value.into(), parameters.into()],
-                    "success",
-                )
-                .try_as_basic_value()
-                .left()
-                .unwrap();
-        } else {
-            binary.builder.build_call(
-                binary.module.get_function("sol_transfer").unwrap(),
-                &[address.into(), value.into(), parameters.into()],
-                "",
-            );
-        }
+        unreachable!();
     }
 
     /// Terminate execution, destroy binary and send remaining funds to addr
@@ -1840,36 +1821,6 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
                     )
                     .into()
             }
-            codegen::Expression::Builtin {
-                kind: codegen::Builtin::Balance,
-                args,
-                ..
-            } => {
-                assert_eq!(args.len(), 1);
-
-                let address = binary.build_alloca(function, binary.address_type(ns), "address");
-
-                binary.builder.build_store(
-                    address,
-                    expression(self, binary, &args[0], vartab, function, ns).into_array_value(),
-                );
-
-                let account_lamport = binary.module.get_function("sol_account_lamport").unwrap();
-
-                let parameters = self.sol_parameters(binary);
-
-                let lamport = binary
-                    .builder
-                    .build_call(account_lamport, &[address.into(), parameters.into()], "")
-                    .try_as_basic_value()
-                    .left()
-                    .unwrap()
-                    .into_pointer_value();
-
-                binary
-                    .builder
-                    .build_load(binary.context.i64_type(), lamport, "lamport")
-            }
             codegen::Expression::Builtin {
                 kind: codegen::Builtin::Accounts,
                 args,

+ 13 - 0
src/sema/expression/function_call.rs

@@ -1133,6 +1133,19 @@ fn try_type_method(
 
         Type::Address(is_payable) => {
             if func.name == "transfer" || func.name == "send" {
+                if ns.target == Target::Solana {
+                    diagnostics.push(Diagnostic::error(
+                        *loc,
+                        format!(
+                            "method '{}' not available on Solana. Use the lamports \
+                        field from the AccountInfo struct directly to operate on balances.",
+                            func.name
+                        ),
+                    ));
+
+                    return Err(());
+                }
+
                 if !is_payable {
                     diagnostics.push(Diagnostic::error(
                         *loc,

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

@@ -369,6 +369,14 @@ pub(super) fn member_access(
                     ));
                     return Err(());
                 }
+            } else if ns.target == Target::Solana {
+                diagnostics.push(Diagnostic::error(
+                    expr.loc(),
+                    "balance is not available on Solana. Use \
+                    tx.accounts.account_name.lamports to fetch the balance."
+                        .to_string(),
+                ));
+                return Err(());
             }
             used_variable(ns, &expr, symtable);
             return Ok(Expression::Builtin {

+ 2 - 2
src/sema/yul/builtin.rs

@@ -608,7 +608,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 77] =
             doc: "balance(a) returns the wei balance at address a",
             ty: YulBuiltInFunction::Balance,
             stops_execution: false,
-            availability: [true, true, true],
+            availability: [true, true, false],
         },
         YulBuiltinPrototype {
             name: "selfbalance",
@@ -617,7 +617,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 77] =
             doc: "Returns the wei balance at the address of the current contract / execution context",
             ty: YulBuiltInFunction::SelfBalance,
             stops_execution: false,
-            availability: [true, true, true],
+            availability: [true, true, false],
         },
         YulBuiltinPrototype {
             name: "caller",

+ 0 - 67
stdlib/solana.c

@@ -55,73 +55,6 @@ uint64_t entrypoint(const uint8_t *input)
     return solang_dispatch(&params);
 }
 
-uint64_t *sol_account_lamport(uint8_t *address, SolParameters *params)
-{
-    SolPubkey *pubkey = (SolPubkey *)address;
-
-    for (int i = 0; i < params->ka_num; i++)
-    {
-        if (SolPubkey_same(pubkey, params->ka[i].key))
-        {
-            return params->ka[i].lamports;
-        }
-    }
-
-    sol_log_pubkey(pubkey);
-    sol_log("account missing from transaction");
-    sol_panic();
-
-    return NULL;
-}
-
-void sol_transfer(uint8_t *to_address, uint64_t lamports, SolParameters *params)
-{
-    uint64_t *from = params->ka[0].lamports;
-    uint64_t *to = sol_account_lamport(to_address, params);
-
-    uint64_t from_balance;
-    uint64_t to_balance;
-
-    if (__builtin_sub_overflow(*from, lamports, &from_balance))
-    {
-        sol_log("sender does not have enough balance");
-        sol_panic();
-    }
-
-    if (__builtin_add_overflow(*to, lamports, &to_balance))
-    {
-        sol_log("recipient lamports overflows");
-        sol_panic();
-    }
-
-    *from = from_balance;
-    *to = to_balance;
-}
-
-bool sol_try_transfer(uint8_t *to_address, uint64_t lamports, SolParameters *params)
-{
-    uint64_t *from = params->ka[0].lamports;
-    uint64_t *to = sol_account_lamport(to_address, params);
-
-    uint64_t from_balance;
-    uint64_t to_balance;
-
-    if (__builtin_sub_overflow(*from, lamports, &from_balance))
-    {
-        return false;
-    }
-
-    if (__builtin_add_overflow(*to, lamports, &to_balance))
-    {
-        return false;
-    }
-
-    *from = from_balance;
-    *to = to_balance;
-
-    return true;
-}
-
 #endif
 
 uint64_t address_hash(uint8_t data[32])

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

@@ -5,6 +5,6 @@ contract DTron {
     function moneyDeposit(address[] memory thanksCash, uint256[] memory amount) public payable {
         // CHECK: ty:address payable %receiver = address payable(address((sext uint256 (trunc uint160 uint256((load (subscript address[] (arg #0)[uint32 2])))))))
         address payable receiver = payable(address(uint160(thanksCash[2])));
-        receiver.transfer(amount[2]);
+        assert(receiver == address(this));
     }
 }

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

@@ -46,3 +46,4 @@ contract MyContract {
 
 
 // ---- Expect: diagnostics ----
+// error: 34:13-57: method 'transfer' not available on Solana. Use the lamports field from the AccountInfo struct directly to operate on balances.

+ 22 - 0
tests/contract_testcases/solana/expressions/address_send_transfer_balance.sol

@@ -0,0 +1,22 @@
+contract c {
+    function test(address addr) public view returns (uint64) {
+        return addr.balance;
+    }
+}
+
+contract c1 {
+    function send(address payable addr, uint64 amount) public returns (bool) {
+        return addr.send(amount);
+    }
+}
+
+ contract c2 {
+    function transfer(address payable addr, uint64 amount) public {
+        addr.transfer(amount);
+    }
+}
+
+// ---- Expect: diagnostics ----
+// error: 3:16-20: balance is not available on Solana. Use tx.accounts.account_name.lamports to fetch the balance.
+// error: 9:16-33: method 'send' not available on Solana. Use the lamports field from the AccountInfo struct directly to operate on balances.
+// error: 15:9-30: method 'transfer' not available on Solana. Use the lamports field from the AccountInfo struct directly to operate on balances.

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

@@ -56,3 +56,4 @@ contract Shares {
 }
 
 // ---- Expect: diagnostics ----
+// error: 43:29-42: balance is not available on Solana. Use tx.accounts.account_name.lamports to fetch the balance.

+ 0 - 13
tests/optimization_testcases/calls/76880609bcc541b65aedb1feef5ba79a8b3b63c8.json

@@ -1,13 +0,0 @@
-{
-  "constructor": [],
-  "function": [
-    [
-      "test_balance",
-      []
-    ],
-    [
-      "test_selfbalance",
-      []
-    ]
-  ]
-}

+ 0 - 22
tests/optimization_testcases/programs/76880609bcc541b65aedb1feef5ba79a8b3b63c8.sol

@@ -1,22 +0,0 @@
-contract testing {
-    function test_address() public view returns (uint256 ret) {
-        assembly {
-            let a := address()
-            ret := a
-        }
-    }
-
-    function test_balance() public view returns (uint256 ret) {
-        assembly {
-            let a := address()
-            ret := balance(a)
-        }
-    }
-
-    function test_selfbalance() public view returns (uint256 ret) {
-        assembly {
-            let a := selfbalance()
-            ret := a
-        }
-    }
-}

+ 1 - 580
tests/solana_tests/balance.rs

@@ -1,469 +1,7 @@
 // SPDX-License-Identifier: Apache-2.0
 
-use crate::{account_new, build_solidity, AccountMeta, AccountState, BorshToken, Pubkey};
+use crate::build_solidity;
 use anchor_syn::idl::types::IdlInstruction;
-use num_bigint::BigInt;
-
-#[test]
-fn get_balance() {
-    let mut vm = build_solidity(
-        r#"
-        contract c {
-            function test(address addr) public view returns (uint64) {
-                return addr.balance;
-            }
-        }"#,
-    );
-
-    let data_account = vm.initialize_data_account();
-    vm.function("new")
-        .accounts(vec![("dataAccount", data_account)])
-        .call();
-
-    let new = account_new();
-
-    vm.account_data.insert(
-        new,
-        AccountState {
-            data: Vec::new(),
-            owner: None,
-            lamports: 102,
-        },
-    );
-
-    let returns = vm
-        .function("test")
-        .arguments(&[BorshToken::Address(new)])
-        .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();
-
-    assert_eq!(
-        returns,
-        BorshToken::Uint {
-            width: 64,
-            value: BigInt::from(102u8),
-        }
-    );
-}
-
-#[test]
-fn send_fails() {
-    let mut vm = build_solidity(
-        r#"
-        contract c {
-            function send(address payable addr, uint64 amount) public returns (bool) {
-                return addr.send(amount);
-            }
-        }"#,
-    );
-
-    let data_account = vm.initialize_data_account();
-    vm.function("new")
-        .accounts(vec![("dataAccount", data_account)])
-        .call();
-
-    let new = account_new();
-
-    vm.account_data.insert(
-        new,
-        AccountState {
-            data: Vec::new(),
-            owner: None,
-            lamports: 0,
-        },
-    );
-
-    let returns = vm
-        .function("send")
-        .arguments(&[
-            BorshToken::Address(new),
-            BorshToken::Uint {
-                width: 64,
-                value: BigInt::from(102u8),
-            },
-        ])
-        .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();
-
-    assert_eq!(returns, BorshToken::Bool(false));
-}
-
-#[test]
-fn send_succeeds() {
-    let mut vm = build_solidity(
-        r#"
-        contract c {
-            constructor() payable {}
-
-            function send(address payable addr, uint64 amount) public returns (bool) {
-                return addr.send(amount);
-            }
-        }"#,
-    );
-
-    let data_account = vm.initialize_data_account();
-
-    vm.account_data.get_mut(&data_account).unwrap().lamports = 103;
-
-    vm.function("new")
-        .accounts(vec![("dataAccount", data_account)])
-        .call();
-
-    let new = account_new();
-    vm.account_data.insert(
-        new,
-        AccountState {
-            data: Vec::new(),
-            owner: None,
-            lamports: 5,
-        },
-    );
-
-    let returns = vm
-        .function("send")
-        .arguments(&[
-            BorshToken::FixedBytes(new.to_vec()),
-            BorshToken::Uint {
-                width: 64,
-                value: BigInt::from(102u8),
-            },
-        ])
-        .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();
-
-    assert_eq!(returns, BorshToken::Bool(true));
-
-    assert_eq!(vm.account_data.get_mut(&new).unwrap().lamports, 107);
-
-    assert_eq!(vm.account_data.get_mut(&data_account).unwrap().lamports, 1);
-}
-
-#[test]
-fn send_overflows() {
-    let mut vm = build_solidity(
-        r#"
-        contract c {
-            function send(address payable addr, uint64 amount) public returns (bool) {
-                return addr.send(amount);
-            }
-        }"#,
-    );
-
-    let data_account = vm.initialize_data_account();
-
-    vm.account_data.get_mut(&data_account).unwrap().lamports = 103;
-
-    vm.function("new")
-        .accounts(vec![("dataAccount", data_account)])
-        .call();
-
-    let new = account_new();
-
-    vm.account_data.insert(
-        new,
-        AccountState {
-            data: Vec::new(),
-            owner: None,
-            lamports: u64::MAX - 101,
-        },
-    );
-
-    let returns = vm
-        .function("send")
-        .arguments(&[
-            BorshToken::Address(new),
-            BorshToken::Uint {
-                width: 64,
-                value: BigInt::from(102u8),
-            },
-        ])
-        .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();
-
-    assert_eq!(returns, BorshToken::Bool(false));
-
-    assert_eq!(
-        vm.account_data.get_mut(&new).unwrap().lamports,
-        u64::MAX - 101
-    );
-
-    assert_eq!(
-        vm.account_data.get_mut(&data_account).unwrap().lamports,
-        103
-    );
-}
-
-#[test]
-fn transfer_succeeds() {
-    let mut vm = build_solidity(
-        r#"
-        contract c {
-            function transfer(address payable addr, uint64 amount) public {
-                addr.transfer(amount);
-            }
-        }"#,
-    );
-
-    let data_account = vm.initialize_data_account();
-    vm.account_data.get_mut(&data_account).unwrap().lamports = 103;
-
-    vm.function("new")
-        .accounts(vec![("dataAccount", data_account)])
-        .call();
-
-    let new = account_new();
-
-    vm.account_data.insert(
-        new,
-        AccountState {
-            data: Vec::new(),
-            owner: None,
-            lamports: 5,
-        },
-    );
-
-    vm.function("transfer")
-        .arguments(&[
-            BorshToken::Address(new),
-            BorshToken::Uint {
-                width: 64,
-                value: BigInt::from(102u8),
-            },
-        ])
-        .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);
-
-    assert_eq!(vm.account_data.get_mut(&data_account).unwrap().lamports, 1);
-}
-
-#[test]
-fn transfer_fails_not_enough() {
-    let mut vm = build_solidity(
-        r#"
-        contract c {
-            function transfer(address payable addr, uint64 amount) public {
-                addr.transfer(amount);
-            }
-        }"#,
-    );
-
-    let data_account = vm.initialize_data_account();
-    vm.account_data.get_mut(&data_account).unwrap().lamports = 103;
-
-    vm.function("new")
-        .accounts(vec![("dataAccount", data_account)])
-        .call();
-
-    let new = account_new();
-
-    vm.account_data.insert(
-        new,
-        AccountState {
-            data: Vec::new(),
-            owner: None,
-            lamports: 5,
-        },
-    );
-
-    let res = vm
-        .function("transfer")
-        .arguments(&[
-            BorshToken::Address(new),
-            BorshToken::Uint {
-                width: 64,
-                value: BigInt::from(104u8),
-            },
-        ])
-        .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());
-
-    // Ensure the balance in the account has not overflowed
-    assert_eq!(vm.account_data[&data_account].lamports, 103);
-    assert_eq!(vm.account_data[&new].lamports, 5);
-
-    vm.function("transfer")
-        .arguments(&[
-            BorshToken::Address(new),
-            BorshToken::Uint {
-                width: 64,
-                value: BigInt::from(103u8),
-            },
-        ])
-        .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);
-    assert_eq!(vm.account_data[&new].lamports, 108);
-}
-
-#[test]
-fn transfer_fails_overflow() {
-    let mut vm = build_solidity(
-        r#"
-        contract c {
-            constructor() payable {}
-
-            function transfer(address payable addr, uint64 amount) public {
-                addr.transfer(amount);
-            }
-        }"#,
-    );
-
-    let data_account = vm.initialize_data_account();
-    vm.account_data.get_mut(&data_account).unwrap().lamports = 104;
-
-    vm.function("new")
-        .accounts(vec![("dataAccount", data_account)])
-        .call();
-
-    let new = account_new();
-
-    vm.account_data.insert(
-        new,
-        AccountState {
-            data: Vec::new(),
-            owner: None,
-            lamports: u64::MAX - 100,
-        },
-    );
-
-    let res = vm
-        .function("transfer")
-        .arguments(&[
-            BorshToken::FixedBytes(new.to_vec()),
-            BorshToken::Uint {
-                width: 64,
-                value: BigInt::from(104u8),
-            },
-        ])
-        .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());
-
-    // Ensure no change in the values
-    assert_eq!(vm.account_data[&new].lamports, u64::MAX - 100);
-    assert_eq!(vm.account_data[&data_account].lamports, 104);
-
-    vm.function("transfer")
-        .arguments(&[
-            BorshToken::FixedBytes(new.to_vec()),
-            BorshToken::Uint {
-                width: 64,
-                value: BigInt::from(100u8),
-            },
-        ])
-        .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);
-    assert_eq!(vm.account_data[&data_account].lamports, 4);
-}
 
 #[test]
 fn fallback() {
@@ -499,120 +37,3 @@ fn fallback() {
 
     assert_eq!(vm.logs, "fallback");
 }
-
-#[test]
-fn value_overflows() {
-    let mut vm = build_solidity(
-        r#"
-        contract c {
-            constructor() payable {}
-
-            function send(address payable addr, uint128 amount) public returns (bool) {
-                return addr.send(amount);
-            }
-        }"#,
-    );
-
-    let data_account = vm.initialize_data_account();
-    vm.account_data.get_mut(&data_account).unwrap().lamports = 103;
-
-    vm.function("new")
-        .accounts(vec![("dataAccount", data_account)])
-        .call();
-
-    let new = account_new();
-
-    vm.account_data.insert(
-        new,
-        AccountState {
-            data: Vec::new(),
-            owner: None,
-            lamports: u64::MAX - 101,
-        },
-    );
-
-    let res = vm
-        .function("send")
-        .arguments(&[
-            BorshToken::Address(new),
-            BorshToken::Uint {
-                width: 128,
-                value: BigInt::from(u64::MAX as u128 + 1),
-            },
-        ])
-        .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);
-
-    let res = vm
-        .function("send")
-        .arguments(&[
-            BorshToken::Address(new),
-            BorshToken::Uint {
-                width: 128,
-                value: BigInt::from(u128::MAX),
-            },
-        ])
-        .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);
-
-    let returns = vm
-        .function("send")
-        .arguments(&[
-            BorshToken::Address(new),
-            BorshToken::Uint {
-                width: 128,
-                value: BigInt::from(102u8),
-            },
-        ])
-        .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();
-
-    assert_eq!(returns, BorshToken::Bool(false));
-
-    assert_eq!(
-        vm.account_data.get_mut(&new).unwrap().lamports,
-        u64::MAX - 101
-    );
-
-    assert_eq!(
-        vm.account_data.get_mut(&data_account).unwrap().lamports,
-        103
-    );
-}

+ 1 - 51
tests/solana_tests/yul.rs

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: Apache-2.0
 
-use crate::{build_solidity, AccountMeta, BorshToken, Pubkey};
+use crate::{build_solidity, BorshToken};
 use num_bigint::{BigInt, Sign};
 use num_traits::{One, Zero};
 
@@ -506,20 +506,6 @@ contract testing  {
             ret := a
         }
     }
-
-    function test_balance() public view returns (uint256 ret) {
-        assembly {
-            let a := address()
-            ret := balance(a)
-        }
-    }
-
-    function test_selfbalance() public view returns (uint256 ret) {
-        assembly {
-            let a := selfbalance()
-            ret := a
-        }
-    }
 }"#,
     );
 
@@ -539,42 +525,6 @@ contract testing  {
         b_vec.insert(0, 0);
     }
     assert_eq!(&b_vec, program_id.as_ref());
-
-    runtime.account_data.get_mut(&program_id).unwrap().lamports = 102;
-    let returns = runtime
-        .function("test_balance")
-        .remaining_accounts(&[AccountMeta {
-            pubkey: Pubkey(program_id),
-            is_writable: false,
-            is_signer: false,
-        }])
-        .call()
-        .unwrap();
-    assert_eq!(
-        returns,
-        BorshToken::Uint {
-            width: 256,
-            value: BigInt::from(102u8),
-        }
-    );
-
-    let returns = runtime
-        .function("test_selfbalance")
-        .remaining_accounts(&[AccountMeta {
-            pubkey: Pubkey(program_id),
-            is_signer: false,
-            is_writable: false,
-        }])
-        .call()
-        .unwrap();
-
-    assert_eq!(
-        returns,
-        BorshToken::Uint {
-            width: 256,
-            value: BigInt::from(102u8),
-        },
-    );
 }
 
 #[test]