Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Represent contracts by their program id #1474

Merged
merged 1 commit into from
Sep 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/solana/contract_address.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
}
23 changes: 0 additions & 23 deletions docs/examples/solana/contract_new.sol

This file was deleted.

8 changes: 4 additions & 4 deletions docs/examples/solana/payer_annotation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
6 changes: 3 additions & 3 deletions docs/examples/solana/program_id.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
4 changes: 0 additions & 4 deletions docs/language/builtins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
++++++++++++++++++++++

Expand Down
12 changes: 8 additions & 4 deletions docs/language/contracts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions docs/language/expressions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
__________________
Expand Down
3 changes: 2 additions & 1 deletion docs/language/managing_values.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
8 changes: 5 additions & 3 deletions docs/targets/solana.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@ Runtime

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


Compute budget
++++++++++++++
Expand Down
2 changes: 1 addition & 1 deletion integration/anchor/tests/anchor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe("Anchor", () => {

const program = anchor.workspace.Anchor as Program<Anchor>;

const [myAccount, bump] = await anchor.web3.PublicKey.findProgramAddress([seed], program.programId);
const [myAccount, bump] = anchor.web3.PublicKey.findProgramAddressSync([seed], program.programId);


const { SystemProgram } = anchor.web3;
Expand Down
2 changes: 1 addition & 1 deletion integration/polkadot/call_flags.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
2 changes: 1 addition & 1 deletion integration/polkadot/issue666.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ contract Inc {
_flipper = _flipperContract;
}

function superFlip () pure public {
function superFlip () view public {
_flipper.flip();
}
}
2 changes: 0 additions & 2 deletions integration/solana/balances.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -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 }
Expand Down
2 changes: 0 additions & 2 deletions integration/solana/builtins.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down
51 changes: 21 additions & 30 deletions integration/solana/calls.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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));
Expand Down
21 changes: 10 additions & 11 deletions integration/solana/create_contract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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();
}
}
Expand Down Expand Up @@ -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.");
}
}
Expand Down
Loading