Calling Functions
Synchronous function calls between smart contracts act very similar to how normal function calls work in any programming language, but with a slight twist. With normal function calls you share all the global memory that you can access with every function that you call. However, when calling a smart contract function you can only access the memory assigned to that specific smart contract. Remember, each smart contract runs in its own sandbox environment. Therefore, the only way to share data between smart contracts that call each other is through function parameters and return values.
Synchronous calls can only be made between contracts that are running on the same contract
chain. The ISC host knows about all the contracts it is running on a chain, and therefore
is able to dispatch the call to the correct contract function. The function descriptor is
used to specify the call parameters (if any) through its Params proxy, and
to invoke the function through its func
interface.
In addition, when the function that is called is not a View, it is possible
to pass tokens to the function call through this interface. Note that the only way to call
a function and properly pass tokens to it within the same chain is through the function
descriptor. Otherwise, the allowance()
function will not register any incoming tokens.
When the call is made, the calling function will be paused and wait for the called function to complete. After completion, it may access the returned values (if any) through the Results proxy of the function descriptor.
When calling a function from a View function, it is only possible to call other View
functions. The ScFuncs interface enforces this at compile-time because it expects an
ScViewContext
to be passed to the constructor that creates the function descriptor.
Here's how a smart contract would tell a dividend
contract on the same chain to divide
the 1000 tokens it passes to the function:
- Go
- Rust
- TypeScript
f := dividend.ScFuncs.Divide(ctx)
f.Func.TransferBaseTokens(1000).Call()
let f = dividend::ScFuncs::divide(ctx);
f.func.transfer_base_tokens(1000).call();
let f = dividend.ScFuncs.divide(ctx);
f.func.transferBaseTokens(1000).call();
And here is how a smart contract would ask a dividend
contract on the same chain to
return the dispersion factor for a specific address:
- Go
- Rust
- TypeScript
f := dividend.ScFuncs.GetFactor(ctx)
f.Params.Address().SetValue(address)
f.Func.Call()
factor := f.Results.Factor().Value()
let f = dividend::ScFuncs::get_factor(ctx);
f.params.address().set_value(&address);
f.func.call();
let factor = f.results.factor().value();
let f = dividend.ScFuncs.getFactor(ctx);
f.params.address().setValue(address);
f.func.call();
let factor = f.results.factor().value();
- Create a function descriptor for the desired function.
- Use the Params proxy in the descriptor to set its parameters.
- Direct the
func
member of the descriptor to call the associated function - Use the Results proxy in the descriptor to retrieve its results.
The function descriptors assume that the function to be called is associated with the
default Hname of the contract, in this case ScHname::new("dividend"). If you deployed the
contract that contains the function you want to call under a different name, then you
would have to provide its associated Hname to the func
member through the of_contract()
member function like this:
- Go
- Rust
- TypeScript
altContract := NewScHname("alternateName")
f := dividend.ScFuncs.Divide(ctx)
f.Func.OfContract(altContract).TransferBaseTokens(1000).Call()
let alt_contract = ScHname::new("alternateName");
let f = dividend::ScFuncs::divide(ctx);
f.func.of_contract(alt_contract).transfer_base_tokens(1000).call();
let altContract = ScHname.fromString('alternateName');
let f = dividend.ScFuncs.divide(ctx);
f.func.ofContract(altContract).transferBaseTokens(1000).call();
In the next section we will look at how to use function descriptors to asynchronously call smart contract functions on any chain.