Skip to content

Commit

Permalink
Make ABI impl methods part of contract interface (FuelLabs#4843)
Browse files Browse the repository at this point in the history
Fixes FuelLabs#4841

## Description


## Checklist

- [x] I have linked to any relevant issues.
- [x] I have commented my code, particularly in hard-to-understand
areas.
- [x] I have updated the documentation where relevant (API docs, the
reference, and the Sway book).
- [x] I have added tests that prove my fix is effective or that my
feature works.
- [x] I have added (or requested a maintainer to add) the necessary
`Breaking*` or `New Feature` labels where relevant.
- [x] I have done my best to ensure that my PR adheres to [the Fuel Labs
Code Review
Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md).
- [x] I have requested a review from the relevant team or maintainers.
  • Loading branch information
anton-trunov authored Jul 21, 2023
1 parent b33b63c commit 5e04dfa
Show file tree
Hide file tree
Showing 26 changed files with 237 additions and 2 deletions.
2 changes: 1 addition & 1 deletion docs/book/src/sway-program-types/smart_contracts.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ Implementing an ABI for a contract is accomplished with `impl <ABI name> for Con
{{#include ../../../../examples/wallet_smart_contract/src/main.sw:abi_impl}}
```

You may notice once again the similarities between [traits](../advanced/traits.md) and ABIs. And, indeed, as a bonus, you can specify methods in addition to the interface surface of an ABI, just like a trait. By implementing the methods in the interface surface, you get the extra method implementations For Free™.
You may notice once again the similarities between [traits](../advanced/traits.md) and ABIs. And, indeed, as a bonus, you can define methods in addition to the interface surface of an ABI, just like a trait. These pre-implemented ABI methods automatically become available as part of the contract interface that implements the corresponding ABI.

Note that the above implementation of the ABI follows the [Checks, Effects, Interactions](https://docs.soliditylang.org/en/v0.6.11/security-considerations.html#re-entrancy) pattern.

Expand Down
8 changes: 7 additions & 1 deletion sway-core/src/semantic_analysis/ast_node/declaration/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ impl ty::TyAbiDecl {
let mut new_items = vec![];
for method in methods.into_iter() {
let method = check!(
ty::TyFunctionDecl::type_check(ctx.by_ref(), method.clone(), true, false),
ty::TyFunctionDecl::type_check(ctx.by_ref(), method.clone(), false, false),
ty::TyFunctionDecl::error(method.clone()),
warnings,
errors
Expand All @@ -139,6 +139,12 @@ impl ty::TyAbiDecl {
})
}
}
if !ids.insert(method.name.clone()) {
errors.push(CompileError::MultipleDefinitionsOfName {
name: method.name.clone(),
span: method.name.span(),
})
}
new_items.push(TyTraitItem::Fn(ctx.engines.de().insert(method)));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[[package]]
name = 'abi_impl_method_duplicate'
source = 'member'
dependencies = ['std']

[[package]]
name = 'core'
source = 'path+from-root-DA324EF751E6D833'

[[package]]
name = 'std'
source = 'path+from-root-DA324EF751E6D833'
dependencies = ['core']
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[project]
name = "abi_impl_method_duplicate"
authors = ["Fuel Labs <[email protected]>"]
entry = "main.sw"
license = "Apache-2.0"

[dependencies]
std = { path = "../../../../../../../sway-lib-std" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
contract;

abi MyAbi
{
// no interface methods
}
{
fn method() -> u64 { 42 }
fn method() -> u64 { 43 } // error: duplicate impl method
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
category = "fail"

# check: $()error
# check: $()fn method() -> u64 { 43 } // error: duplicate impl method
# check: $()Name "method" is defined multiple times.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[[package]]
name = 'abi_impl_method_shadows_interface_method'
source = 'member'
dependencies = ['std']

[[package]]
name = 'core'
source = 'path+from-root-4058F74E887BDDB9'

[[package]]
name = 'std'
source = 'path+from-root-4058F74E887BDDB9'
dependencies = ['core']
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[project]
name = "abi_impl_method_shadows_interface_method"
authors = ["Fuel Labs <[email protected]>"]
entry = "main.sw"
license = "Apache-2.0"

[dependencies]
std = { path = "../../../../../../../sway-lib-std" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
contract;

abi MyAbi
{
fn method() -> u64;
}
{
// impl methods cannot shadow interface methods
fn method() -> u64 { 42 }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
category = "fail"

# check: $()error
# check: $()fn method() -> u64 { 42 }
# check: $()Name "method" is defined multiple times.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[[package]]
name = 'abi_impl_methods_callable'
source = 'member'
dependencies = ['std']

[[package]]
name = 'core'
source = 'path+from-root-214E8589AEAE48E5'

[[package]]
name = 'std'
source = 'path+from-root-214E8589AEAE48E5'
dependencies = ['core']
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[project]
name = "abi_impl_methods_callable"
authors = ["Fuel Labs <[email protected]>"]
entry = "main.sw"
license = "Apache-2.0"

[dependencies]
std = { path = "../../../../../../../sway-lib-std" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
contract;

abi MyAbi { }
{
fn impl_method() -> u64 { 42 }
}

impl MyAbi for Contract { }

#[test]
fn tests() {
let caller = abi(MyAbi, CONTRACT_ID);
assert(caller.impl_method() == 42)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
category = "unit_tests_pass"
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[[package]]
name = 'abi_impl_methods_in_json_abi'
source = 'member'
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[project]
name = "abi_impl_methods_in_json_abi"
authors = ["Fuel Labs <[email protected]>"]
entry = "main.sw"
license = "Apache-2.0"
implicit-std = false
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"configurables": [],
"functions": [
{
"attributes": null,
"inputs": [],
"name": "interface_method",
"output": {
"name": "",
"type": 0,
"typeArguments": null
}
},
{
"attributes": null,
"inputs": [],
"name": "impl_method",
"output": {
"name": "",
"type": 0,
"typeArguments": null
}
}
],
"loggedTypes": [],
"messagesTypes": [],
"types": [
{
"components": [],
"type": "()",
"typeId": 0,
"typeParameters": null
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
contract;

abi MyAbi
{
fn interface_method();
}
{
fn impl_method() { }
}

impl MyAbi for Contract {
fn interface_method() { }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
category = "compile"

validate_abi = true
validate_storage_slots = false
expected_warnings = 0
4 changes: 4 additions & 0 deletions test/src/sdk-harness/Forc.lock
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
[[package]]
name = 'abi_impl_methods_callable'
source = 'member'

[[package]]
name = 'auth_caller_contract'
source = 'member'
Expand Down
1 change: 1 addition & 0 deletions test/src/sdk-harness/Forc.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[workspace]
members = [
"test_projects/abi_impl_methods_callable",
"test_projects/block",
"test_projects/call_frames",
"test_projects/configurables_in_contract",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[[package]]
name = 'abi_impl_methods_callable'
source = 'member'
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[project]
name = "abi_impl_methods_callable"
authors = ["Fuel Labs <[email protected]>"]
entry = "main.sw"
license = "Apache-2.0"
implicit-std = false
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use fuels::accounts::wallet::WalletUnlocked;
use fuels::prelude::*;

abigen!(Contract(
name = "AbiImplMethodsCallable",
abi = "test_projects/abi_impl_methods_callable/out/debug/abi_impl_methods_callable-abi.json"
));

async fn get_abi_impl_methods_callable_instance() -> AbiImplMethodsCallable<WalletUnlocked> {
let wallet = launch_provider_and_get_wallet().await;
let id = Contract::load_from(
"test_projects/abi_impl_methods_callable/out/debug/abi_impl_methods_callable.bin",
LoadConfiguration::default(),
)
.unwrap()
.deploy(&wallet, TxParameters::default())
.await
.unwrap();
AbiImplMethodsCallable::new(id.clone(), wallet)
}

#[tokio::test]
async fn impl_method_test() -> Result<()> {
let instance = get_abi_impl_methods_callable_instance().await;
let contract_methods = instance.methods();

let response = contract_methods.impl_method().call().await?;
assert_eq!(42, response.value);

Ok(())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
contract;

abi MyAbi
{
fn interface_method();
}
{
fn impl_method() -> u64 { 42 }
}

impl MyAbi for Contract {
fn interface_method() { }
}
1 change: 1 addition & 0 deletions test/src/sdk-harness/test_projects/harness.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Add test modules here:

mod abi_impl_methods_callable;
mod auth;
mod block;
mod call_frames;
Expand Down

0 comments on commit 5e04dfa

Please sign in to comment.