Skip to main content

Custom instructions

The Flare Smart Accounts allow XRPL users to make custom function calls on Flare through instructions sent on XRPL. In this guide, we will look at how the custom instructions can be developed using a mock version of the MasterAccountController contract.

In a typical workflow, the user sends instructions as memo data on XRPL. Those then have to be verified by the FDC on the Flare chain. That process requires waiting, which is less than ideal in a development environment.

For that reason, a mock version of the MasterAccountController contract has been deployed. It implements two additional functions:

  • createFundPersonalAccount
  • executeCustomInstructionDevelopment

The createFundPersonalAccount function creates a new PersonalAccount for the user. It accepts as its argument a string _xrplAddress, which represents an address on XRPL. The string is then concatenated with the msg.sender value; the PersonalAccount is created for this address.

Thus, a developer can create multiple XRPL "addresses". This allows them to more easily test the interactions between different personal accounts. The "address" is combined with their Flare address, so that they can give meaningful names to their "addresses" without the danger of multiple developers' addresses crashing.

The createFundPersonalAccount function is a payable function. And funds sent with the transaction are deposited into the newly created personal account. That way, the personal account can interact with payable functions from the get-go.

The executeCustomInstructionDevelopment function sidesteps the XRP transaction and the FDC Payment verification process. It allows developers to send an array of custom instructions to the MasterAccountController contract directly.

The two parameters this function takes are the _xrplAddress string and an array of IMasterAccountController.CustomInstruction structs. It first concatenates the input string with the msg.sender value, the same way the createFundPersonalAccount function does. Then, it retrieves the PersonalAccount representing that address, and calls its custom function with the array of custom instructions it received as input.

A simple example

We will now use the Flare Smart Accounts CLI to interact with a simple contract. The contract Foo has a single payable function bar. The bar function accepts a uint256 value as input, and adds the fee paid with the transaction to a mapping.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

contract Foo {
mapping(uint256 => uint256) public map;

function bar(uint256 a) public payable {
map[a] = map[a] + msg.value;
}
}

We want to send 1 FLR to the contract, and save it under the number 42. The address of the Foo contract is 0x296432C15504Ed465fAce11E54Ce4aac50cCd8A3. Using an online ABI-encoding tool, we get the following hash for the bar function, with 42 as input: 0x0423a132000000000000000000000000000000000000000000000000000000000000002a.

warning

Encoding calldata by hand is error-prone. It is recommended to use established libraries, or an online tool (if you want to quickly check something).

There are two ways we can go about developing the custom instructions. We will start with an approach that is what the production code would take. Afterwards, we will use the mock functions to speed up the development.

Normal approach

Before we can execute the above instructions, we need to top up the smart account that will perform the function call. We run the following command, which fails because our account lacks funds on Flare. It is necessary to send some instructions, because that is what creates an account for us in the first place.

./smart_accounts.py bridge custom -a "0x296432C15504Ed465fAce11E54Ce4aac50cCd8A3" -v 1 -d "0423a132000000000000000000000000000000000000000000000000000000000000002a"

We then need to retrieve the smart account address.

./smart_accounts.py personal-account --from-env print

With the address, we can go to the Flare faucet and request C2FLR for the smart account address. We can also do this through the CLI.

./smart_accounts.py personal-account --from-env faucet

Afterwards, we can run the following command again. This time, it works.

./smart_accounts.py bridge custom -a "0x296432C15504Ed465fAce11E54Ce4aac50cCd8A3" -v 1 -d "0423a132000000000000000000000000000000000000000000000000000000000000002a"

Mocking

We can speed up the process, as well as simplify it, by using the Flare Smart Accounts CLI. First, we need to create a mock account, which we do with the command. This will only work if our Flare address has sufficient funds.

./smart_accounts.py debug mock-create-fund --seed "mockAccount" --value 1

Here, we arbitrarily chose the name mockAccount as the account address. Behind the scenes, the string mockAccount will be concatenated with our Flare address.

You can check the associated account with the command:

./smart_accounts.py debug mock-print -s <seed>

Then, we execute the custom instructions. We use the string mockAccount as the seed.

./smart_accounts.py debug mock-custom -s "mockAccount" -a "0x296432C15504Ed465fAce11E54Ce4aac50cCd8A3" -v 1 -d "0423a132000000000000000000000000000000000000000000000000000000000000002a"