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

New syntax for contract method calls #859

Merged

Conversation

mohammadfawaz
Copy link
Contributor

@mohammadfawaz mohammadfawaz commented Mar 2, 2022

Closes #298

Syntax

Old syntax:

abi Wallet {
    fn send_funds(gas: u64, coins_to_forward: u64, asset_id: b256, req: SendFundsRequest);
}

impl Wallet for Contract {
     fn send_funds(gas_to_forward: u64, coins_to_forward: u64, asset_id: b256, req: SendFundsRequest) {
        ...
    }
}
...
fn main() {
    let contract_address = 0x9299da6c73e6dc03eeabcce242bb347de3f5f56cd1c70926d76526d7ed199b8b;
    let caller = abi(Wallet, contract_address);
    let req = SendFundsRequest {
        amount_to_send: 200,
        recipient_address: 0x9299da6c73e6dc03eeabcce242bb347de3f5f56cd1c70926d76526d7ed199b8b,
    };
    caller.send_funds(10000, 0, ETH_ID, req);
}

New syntax:

abi Wallet {
    fn send_funds(amount_to_send: u64, recipient_address: b256);
}

impl Wallet for Contract {
     fn send_funds(amount_to_send: u64, recipient_address: b256) {
        ...
    }
}
...
fn main() {
    let contract_address = 0x9299da6c73e6dc03eeabcce242bb347de3f5f56cd1c70926d76526d7ed199b8b;
    let caller = abi(Wallet, contract_address);
    
    caller.send_funds{
        gas: 10000, // Can be skipped
        coins: 0, // Can be skipped or set to something other than the default value
        asset_id: ETH_ID // Can be skipped or set to something other than the default value
    } 
    (200, 0x9299da6c73e6dc03eeabcce242bb347de3f5f56cd1c70926d76526d7ed199b8b);
}
  • There is no need to wrap the user arguments into a struct anymore and any number of arguments can be used.
  • There is no need to pass () when no arguments are required.
  • The contract parameters gas, coins, and asset_id are optional and they can be specified in any order. If a parameter is skipped, its default value is used in the CALL instruction. The default values for gas, coins, and asset_id are $cgas, 0, and ETH_ID respectively. ETH_ID is stored as a compiler constant.
  • When none of the call parameters are set, both of these syntaxes work: caller.send_funs{}(...) or caller.send_funds().
  • Specifying any of the parameters more than once is a compile error.
  • Specifying a parameter other than gas, coins, or asset_id is a compile error.

Implementation

The implementation is simple:

  • On the caller side, gather the contract parameters, assign them to registers, and use them in the CALL instruction as before. Then, create a struct of all user arguments and follow the same flow that is used for user-defined struct.
  • On the contract side, create a struct type that contains the user arguments and re-use some of the code used for subfields to handle field offsets and so on.
  • On both sides, no wrapper struct is created if only a single argument is passed.

Testing

All contract tests were failing before this change because the hardcoded contract IDs in the calling scripts were outdated (+ some other minor issues). I updated all the required contract IDs here but there are 2 tests that continue to fail (even on master), namely:

  • ("context_testing_contract", "caller_context_test")
  • ("contract_abi_impl", "contract_call")

Editi. Updated the default values for the parameters.

Generated assembly on the caller side in the default case:

lw   $r1 data_7               ; load fn selector for call
lw   $r5 $cgas i0             ; loading $cgas (gas) into abi function
lw   $r4 data_4               ; loading the default coins value for call
lw   $r3 data_5               ; loading the default asset_id value for call
...
data_4 .u64 0x00
data_5 .b256 0x0000000000000000000000000000000000000000000000000000000000000000
call $r0 $r4 $r3 $r5

Generated assembly on the caller side in the non-default case:

lw   $r5 data_3               ; literal instantiation
lw   $r4 data_8               ; literal instantiation
lw   $r3 data_9               ; literal instantiation
...
call $r0 $r4 $r3 $r5          ; call external contract
...
data_3 .u64 0x2710
data_8 .u64 0x32
data_9 .b256 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0

@mohammadfawaz mohammadfawaz marked this pull request as draft March 2, 2022 04:24
@adlerjohn adlerjohn added compiler General compiler. Should eventually become more specific as the issue is triaged enhancement New feature or request labels Mar 2, 2022
@mohammadfawaz mohammadfawaz force-pushed the new_contract_call_syntax branch 2 times, most recently from efdf1f4 to dd708a0 Compare March 3, 2022 02:27
@mohammadfawaz mohammadfawaz force-pushed the new_contract_call_syntax branch 2 times, most recently from 409de4c to 5878ff8 Compare March 3, 2022 04:32
@mohammadfawaz mohammadfawaz force-pushed the new_contract_call_syntax branch from 5878ff8 to fa23795 Compare March 3, 2022 04:33
@mohammadfawaz
Copy link
Contributor Author

@adlerjohn @nfurfaro FYI - You can check out the new syntax in action in the updated tests :)

@mohammadfawaz mohammadfawaz marked this pull request as ready for review March 3, 2022 04:43
otrho
otrho previously approved these changes Mar 3, 2022
Copy link
Contributor

@otrho otrho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool! I noticed in the tests it's generally all or nothing with the contract args? If e.g., coins was zero can you just leave it out of that map thing?

@mohammadfawaz
Copy link
Contributor Author

Cool! I noticed in the tests it's generally all or nothing with the contract args? If e.g., coins was zero can you just leave it out of that map thing?

Actually, I'd like someone to confirm that what I did was correct in the default case. I assumed that the default values (that #298 talks about) are basically the contents of $cgas, $bal, and $fp for gas, coins, and asset_id respectively. Is this correct? Or are there some default constants that I should be aware of?

Copy link
Contributor

@adlerjohn adlerjohn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default value for a parameter is extracted from the appropriate special register ($cgas for gas, $bal for coins, and $fp for asset_id)

Wait no, those are more for receiving than sending. The defaults should be $cgas, 0, and ETH_ID, in order.

adlerjohn
adlerjohn previously approved these changes Mar 3, 2022
Copy link
Contributor

@adlerjohn adlerjohn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, but will defer to @otrho and @sezna

@nfurfaro
Copy link
Contributor

nfurfaro commented Mar 3, 2022

I think the new syntax looks great @mohammadfawaz !
As for the failing test ("context_testing_contract", "caller_context_test"), I'm working to port the context tests to the stdlib repo using the SDK anyway, so will remove the redundant ones here when I'm done.

otrho
otrho previously approved these changes Mar 4, 2022
@mohammadfawaz mohammadfawaz dismissed stale reviews from otrho and adlerjohn via 16f0c3b March 4, 2022 14:32
Copy link
Contributor

@adlerjohn adlerjohn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, actually.

Instead of using tag, can you use branch = "master" and a Forc lockfile?

@mohammadfawaz
Copy link
Contributor Author

Wait, actually.

Instead of using tag, can you use branch = "master" and a Forc lockfile?

#876 should bulk change all the tests right? I'm not really adding any new tests here.

Copy link
Contributor

@adlerjohn adlerjohn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right it's too early in the morning and I forget that wasn't actually changed yet

@adlerjohn
Copy link
Contributor

Could you update the PR description on how the new ABI syntax will look? Currently it still has the 4 parameters.

@mohammadfawaz
Copy link
Contributor Author

Could you update the PR description on how the new ABI syntax will look? Currently it still has the 4 parameters.

Ha! Good catch. Fixed!

Copy link
Contributor

@sezna sezna left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM -- left one comment

}

// evaluate the gas to forward to the contract. If no user-specified gas parameter is found,
// simply load $cgas.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, I think loading $zero would also be valid here as 0 is shorthand for forwarding all remaining gas. CC @adlerjohn for a correctness check on that

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I merged for now while I can. Will follow up with John on Monday and change if necessary :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler General compiler. Should eventually become more specific as the issue is triaged enhancement New feature or request
Projects
Archived in project
5 participants