Calling methods inside smart contracts
This tutorial outlines how to interact with the tutorial smart contracts given the developer tools provided by =nil;.
Prerequisites
Complete the following tutorials before proceeding.
Via the contract factory
Nil.js
exposes the contract factory, a Hardhat-like tool for interacting with smart contracts.
To order a new product via the contract factory:
const hashFunds = await faucet.withdrawToWithRetry(retailerAddress, convertEthToWei(1));
await waitTillCompleted(client, hashFunds);
const retailerContract = getContract({
client,
RETAILER_ABI,
address: retailerAddress,
wallet: wallet,
});
const manufacturerContract = getContract({
client,
MANUFACTURER_ABI,
address: manufacturerAddress,
wallet: wallet,
});
const res = await retailerContract.read.orderProduct([manufacturerAddress, "new-product"]);
To attain the result:
const res2 = await manufacturerContract.read.getProducts();
console.log(res2);
Using internal messages
Via the =nil; CLI
To call the orderProduct()
function inside the retailer contract and trigger an async call:
nil wallet send-tokens RETAILER_ADDRESS 5000000
nil wallet send-message RETAILER_ADDRESS orderProduct MANUFACTURER_ADDRESS new-product --abi path/to/Retailer.abi --fee-credit FEECREDIT
Make a readonly call to access the value:
nil contract call-readonly MANUFACTURER_ADDRESS getProducts --abi path/to/Manufacturer.abi
The CLI should output the decoded values automatically.
Via the client library
To send funds to the retailer product and call the orderProduct
function:
const hashFunds = await faucet.withdrawToWithRetry(retailerAddress, convertEthToWei(1));
await waitTillCompleted(client, hashFunds);
const hashProduct = await wallet.sendMessage({
to: retailerAddress,
data: encodeFunctionData({
abi: RETAILER_ABI,
functionName: "orderProduct",
args: [manufacturerAddress, "another-product"],
}),
feeCredit: 3_000_000n,
});
const productReceipts = await waitTillCompleted(client, hashProduct);
To receive and decode the currently created products:
const resultsCall = await client.call(
{
from: manufacturerAddress,
to: manufacturerAddress,
data: encodeFunctionData({
abi: MANUFACTURER_ABI,
functionName: "getProducts",
args: [],
}),
},
"latest",
);
console.log(
"getProducts",
decodeFunctionResult({
abi: MANUFACTURER_ABI,
functionName: "getProducts",
data: resultsCall,
}),
);
Using external messages
Via the =nil; CLI
The CLI currently does not support sending external messages that modify the internal state of smart contracts or spawn internal messages. As a result, sending an external message to a function that performs an async call to another function will fail.
In the retailer contract, the orderProduct()
function cannot be called externally as it makes an async call to the manufacturer.
Via the client library
To order a new product using the retailer:
const orderMessage = new ExternalMessageEnvelope({
isDeploy: false,
to: hexToBytes(addressRetailer),
chainId,
data: hexToBytes(
encodeFunctionData({
abi: RETAILER_ABI,
functionName: "orderProduct",
args: [addressManufacturer, "new-product"],
}),
),
authData: new Uint8Array(0),
seqno: await client.getMessageCount(addressRetailer),
});
const encodedOrderMessage = orderMessage.encode();
let success = false;
let orderMessageHash;
while (!success) {
try {
orderMessageHash = await client.sendRawMessage(bytesToHex(encodedOrderMessage));
success = true;
} catch (error) {
await new Promise((resolve) => setTimeout(resolve, 1000));
}
}
const orderReceipts = await waitTillCompleted(client, orderMessageHash);
To receive and decode the currently created products:
const resultsCall = await client.call(
{
from: manufacturerAddress,
to: manufacturerAddress,
data: encodeFunctionData({
abi: MANUFACTURER_ABI,
functionName: "getProducts",
args: [],
}),
},
"latest",
);
console.log(
"getProducts",
decodeFunctionResult({
abi: MANUFACTURER_ABI,
functionName: "getProducts",
data: resultsCall,
}),
);