- 22 Aug 2024
- 22 Minutes to read
- DarkLight
- PDF
Account
- Updated on 22 Aug 2024
- 22 Minutes to read
- DarkLight
- PDF
Unlike most blockchain platforms, an account on Radix are not simply associated with your public and private key. Instead, an account is a component, instantiated from a built-in account blueprint provided by the system which exist in the application layer. Even though accounts are implemented in the application layer, account components are unique in that they’re afforded some unique features that are not afforded to other normal components. As a result, accounts on Radix can contain resources and have special logic built into it.
Naturally, an account has an owner which controls the account, and the owner of the account is able to:
Lock a fee from the XRD vault that the account may hold to pay for transaction fees.
Manage the account by withdrawing and depositing resources.
Create proofs against resources that the account stores.
Configure which deposits the account accepts and rejects.
Furthermore, account components have a security model which employs a Role Based Access Control (RBAC) model where there are pre-configured roles which are mapped to privileged methods that only these roles have access to. These are roles to delineate who is allowed to do what with the account.
The account blueprint has two pre-configured roles by default: Owner
and Securify
.
The
Owner
role is given the ability to call all the privileged methods on the account (such as methods that withdraw and deposit resources or lock XRD for fee payment).The
Securify
role is the role that can call the appropriate methods that "securifies" the account. This allows to expand the accounts authorization model and enables things like multi-factor control. More information on account securificaton is provided in the Account Securification section.
While the Owner
can expand who is allowed to access their account by re-configuring the roles, by default at instantiation, both roles are associated to the owner.
The diagram below shows a complete list of the account methods and the roles that they map to, in other terms, it shows the roles that are authorized to call these methods. It also shows the mapping of the roles to the access rules. All the methods seen in the diagram below are explained in detail in the API Reference section of the document.
How the Account Component Works
Use of Radix accounts is done through calls to component methods using the transaction manifest (as with any component).
For example, a simple transfer of 10 XRD tokens from Alice’s account to Bob’s is accomplished by creating a transaction manifest that describes these steps:
Lock fees to pay for the transaction
Call the
withdraw
method on Alice’s account component, requesting 10 XRDTake the returned tokens from the worktop and put them in a bucket
Pass this bucket to the
try_deposit_or_x
method of Bob’s account component
As long as Alice’s account does in fact have 10 XRD to withdraw (and is authorized to withdraw from that component - more on this below), the 10 XRD are returned and deposited in Bob’s account (as long as Bob’s account is not configured to deny XRD). If any of these assumptions are incorrect, the whole transaction fails.
In this way, using a Radix-style account is intuitively like getting cash from your wallet when you want to pay for something.
Account Addresses and Pre-allocation
There are two main types of account address:
Addresses for pre-allocated accounts, which are implicitly instantiated via on-ledger interactions. These addresses encode either a
Secp256k1
orEd25519
public key hash, and at instantiation time, the initialOwner
role is assigned to require a signature of the corresponding public key.Addresses for explicitly-allocated accounts.
The difference between these addresses is what happens if no account has been instantiated at that address yet. A pre-allocated account effectively already exists in a “virtual” state before it is instantiated, and the Core API and Gateway API return data for an un-instantiated pre-allocated account address in this “virtual” state, as if it already existed. The first time it is interacted with on ledger (say, because it receives a deposit), the account shell gets instantiated automatically. The transaction which interacts with that account for the first time pays additional fees to instantiate the account.
The following is the algorithm used to derive the pre-allocated account address associated with a public key. This method is also made available by the Radix Engine Toolkit:
Take an Ecdsa compressed Secp256k1 public key, or the standard Ed25519 public key.
Hash the public key through Blake2b with 256 bit digests.
Construct a 30 byte array by setting the first byte to
0xD1
for a Secp256k1 public key or0x51
for an Ed25519 public key, and then appending the last 29 bytes of the public key hash.Bech32m encode the above with the
account_${network_specifier}
HRP where the network_specifier depends on the network that the address will be used for (see addressing).
Account Metadata and Owner Keys
More to come here once we have implemented messages and owner keys for accounts.
Configuring Account Deposit Modes and Resource Preference Map
There are two types of deposits for two different sets of callers:
Privileged methods such as
deposit
anddeposit_batch
which accepts all deposits and can only be called by the account owner.Unprivileged methods such as
try_deposit_or_abort
,try_deposit_or_refund
,try_deposit_batch_or_abort
, andtry_deposit_batch_or_refund
that are reserved for third-parties who wish to deposit resources to an account.
Because privileged deposit*
methods can only be called by the account owner, third-parties who wish to deposit resources to an account are required to use try_deposit*
methods. The try_deposit*
methods are reserved to allow account owners to configure how resources deposited to their account by third-parties are treated. As such, there are two settings that the owner of an account component can set up to configure how resources deposited are treated: the Resource Preference Map and the Account Deposit Mode.
The Resource Preference Map is a granular per resource configuration that account owners can specify.
The Account Deposit Mode is a fall-back configuration which determines how resources deposited into an account are broadly treated.
The resource preference map is the primary and first place that the account looks at when determining if a resource can be deposited or not and always supersedes configurations within the account deposit mode or any other account state.
Resource Preference Map
The account component contains a "resource preference" map in its state. This map stores the preference configuration of each specific resource and can be thought of as the “allow list” and “deny list”. Technically speaking, the resource preference map is defined in code as KeyValueStore<ResourceAddress, ResourcePreference>
where each address of a resource (indicated by their ResourceAddress
) is mapped to a ResourcePreference
which is an enum variant of Allowed
and Disallowed
.
In summary:
If the
ResourcePreference
for some resource isAllowed
then it is guaranteed to be deposited into the account, if theResourcePreference
for some resource isDisallowed
then its guaranteed to be rejected by the account component, no other state matters.A resource cannot be in the “allow list” and “deny list” at the same time since a
KeyValueStore
does not allow for duplicate entries with the same key, thus, a resource can only be:Allowed
,Disallowed
, or has no entry in the resource preferences map.
The resource preference map of an account component can be configured through the set_resource_preference
and remove_resource_preference
methods by the owner. It’s important to note that resources are not special-cased to this resource preference mapping. Any resource can be put as a Allowed
resource and any resource can be put as a Disallowed
resource. This means even XRD can be Disallowed
from being deposited into an account through unprivileged deposit methods.
Account Deposit Mode
When an account doesn’t have a specific resource configured in the resource preference map, the account component then uses a broader mechanism: account deposit mode. The account deposit mode is a default treatment of resource deposits that are not specified in the resource preference map. An account deposit mode can be configured in the following ways:
Accept: If the account doesn’t have a preference for a particular resource then permit the deposit of the resource.
Reject: If the account doesn’t have a preference for a particular resource then reject the deposit of the resource.
Allow Existing (or XRD): If the account doesn’t have a preference for a particular resource then permit the deposit of the resource IF the account has ever held the resource before or the resource is XRD.
Any resource that the account has a vault for can be deposited into the account while in this mode even if the vault in the account is empty for that particular resource.
If the user wishes to make exceptions to this (to e.g. prevent XRD deposits, or deposits of a previously-held resource), they can configure these explicitly using the resource preference map.
The default deposit mode of an account can be configured through the set_default_deposit_rule
method by the owner.
In summary:
If a resource isn’t configured in the resource preference map then the account’s default deposit mode comes into the picture which can either be
Accept
,Reject
, orAllowExisting
.
Authorized Depositors
With these account deposit configuration in mind, another feature to note is that the owner of an account can also specify authorized depositors. When owners specify authorized depositors to their account, the authorized depositors have special privileges which allows resource deposits into the account regardless of the account resource preferences and deposit mode setting. This effectively gives authorized depositors the same deposit privileges as the owner. Except of course the privileges starts and stops there and the owner can always revoke those privileges.
A small caveat is that, authorized depositors will still need to use try_deposit*
methods even with their deposit privileges as the protected deposit*
methods are only reserved for the owner.
When the owner wants to add an authorized depositor, they may do so by calling the add_authorized_depositor
method. When calling this method, the owner can specify the ResourceOrNonFungible
of an existing badge that the prospective authorized depositor may have or create one for the prospective authorized depositor to receive. The ResourceOrNonFungible
is an input which accepts either a ResourceAddress
if the badge used is a fungible resource or a NonFungibleGlobalId
if the badge used is a non-fungible resource. Once specified, the badge will not be added to the account’s list of authorized depositor and the authorized depositor must have the badge present when making privileged deposits to the account.
For example, if the authorized depositor deposits a resource that have been specified as Disallowed
in the resource preference map or that the account deposit mode is set to Reject
, the account does the following:
Check if the badge passed is in the account’s set of authorized depositors.
Assert the presence of this badge in the auth zone.
Permit the deposit.
The following is a complete flow chart showing the logic to determine if a deposit is allowed or not.
Account Securification
Owners of an account component are defined by the Owner role which is mapped to an access rule. By default, the access rule is configured to its key pairs derived from a seed phrase to control the account. Thereby, the process of securifying the account is to update the associated access rule of the Owner from its default setting to a new one which instead specifies a badge (otherwise known as an "owner badge") to control the account.
An account can be securified by calling the securify
method on the account, this method returns a bucket that contains the account’s owner badge (which the Owner access rule is now updated to). This badge must then be stored somewhere, ideally in an access controller.
The securify
method is only callable by the Securify
role. When an account is first created, the Securify
role is pre-configured to also be the Owner
. However, after the account has been securified the Securify
role is re-configured to DenyAll
, meaning it can’t ever be changed again and the method can no longer be called.
Effectively, the securification process is the process of switching from signature mode (from key pairs) to badge mode, this is because the process changes the owner’s access rule from requiring a signature to requiring a badge instead. Switching the account authorization from signature mode to badge mode offers expanded authorization configuration for the owner as detailed in this article: How Multi-Factor Radix Smart Accounts Work and What They Can Do.
The securification process is an important process for the wallet. The wallet will have a dedicated flow for securifying accounts and creating an access controller to store the account’s owner badge into. The API Reference section contains an example of a manifest that securifies an account.
Blueprint API - Function Reference
Create
Name |
|
Type | Function |
Callable by | Public |
Arguments | None |
Returns |
|
Description | Creates a new global securified explicitly-allocated account and returns the This function is used to create a new global securified explicitly-allocated account. It mints an owner badge and sets it as the owner role giving it the authority to call privileged methods on the account. |
This function will never be called by the wallet for any of the wallet flows. It is documented here for the sake of completeness only. The wallet only creates pre-allocated accounts and none of the current flows include the use of explicitly-allocated accounts.
Transaction Manifest
CREATE_ACCOUNT;
TAKE_ALL_FROM_WORKTOP
Address("${account_owner_badge_address}")
Bucket("owner_badge");
# Do something with the owner badge, ideally create an access controller and deposit
# it there.
Note that the transaction manifest above is not complete, more specifically, the account owner badge returned from the create function is not deposited anywhere in this manifest. Ideally, the account owner badge would be stored in an access controller.
Create Advanced
Name |
|
Type | Function |
Callable by | Public |
Arguments |
|
Returns |
|
Description | Creates a new global allocated account with the owner rule specified by the caller and returns the ComponenAddress of the account component. While the create function automatically mints an owner badge and sets that as the owner role, this function allows the caller to specify the AccessRule associated with the owner role giving the caller more freedom on who can call privileged methods on the account. This is useful for any application where the creator of the account wishes to have some kind of an m-of-n multi-signature account, a 1-of-n account, or an account whose owner is any arbitrarily complex AccessRule. An example of where this might be used is for exchanges which might want to have an account controlled by 4-of-6 signatures to ensure that a single compromised key does not result in the loss of funds. Most users of this function do not particularly need to have the multi-factor authentication and recovery logic of an access controller. |
This function will never be called by the wallet for any of the wallet flows. It is documented here for the sake of completeness only. The wallet only creates pre-allocated accounts and none of the current flows include the use of global explicitly-allocated accounts.
Transaction Manifest
CREATE_ACCOUNT_ADVANCED
Enum<OwnerRole::Updatable>(
# Contrived example to show AccessRule configuration
Enum<AccessRule::AllowAll>()
);
Component API - Method Reference
Lock fee
Name |
|
Type | Method |
Callable by |
|
Arguments |
|
Returns | None |
Description | Locks some amount of XRD in fees from the account’s XRD vault. |
Transaction Manifest
CALL_METHOD
Address("${account_component_address}")
"lock_fee"
Decimal("${amount_of_xrd_to_lock_for_fees}");
Lock contingent fee
Name |
|
Type | Method |
Callable by |
|
Arguments |
|
Returns | None |
Description | Locks some amount of XRD in fees from the account’s XRD vault which is contingent on the success of the transaction. If the transaction succeeds, then the locked XRD may be used for fees, if the transaction fails then the locked XRD is not used for fees. Because of this restriction, this fee doesn’t count towards payment during the transaction itself, so can’t be used on its own to pay the transaction fee during execution. |
Transaction Manifest
CALL_METHOD
Address("${account_component_address}")
"lock_contingent_fee"
Decimal("${amount_of_xrd_to_lock_for_fees}");
Deposit
Name |
|
Type | Method |
Callable by |
|
Arguments |
|
Returns | None |
Description | Deposits a bucket of resources into the account. This method is only callable by the account owner and does not do any of the checks discussed in the Account Deposit Modes section. It permits all deposits since it requires the owner authority to be present when calling it. This method is intended to be used for transactions (such as dApp transactions) where the owner is present, and skips deposit mode checks. If building a transfer or deposit where the owner isn’t present, use |
Transaction Manifest
CALL_METHOD
Address("${account_component_address}")
"deposit"
Bucket("some_bucket");
Deposit batch
Name |
|
Type | Method |
Callable by |
|
Arguments |
|
Returns | None |
Description | Deposits multiple buckets of resources into the account. This method is identical to This method is intended to be used for transactions (such as dApp transactions) where the owner is present, and skips deposit mode checks. If building a transfer or deposit where the owner isn’t present, use |
Transaction Manifest
CALL_METHOD
Address("${account_component_address}")
"deposit_batch"
Expression("ENTIRE_WORKTOP");
Withdraw
Name |
|
Type | Method |
Callable by |
|
Arguments |
|
Returns |
|
Description | Withdraws resources from the account by amount. This method withdraws a resource of the given address and amount from the account vaults and returns it in a |
Transaction Manifest
CALL_METHOD
Address("${account_component_address}")
"withdraw"
Address("${resource_address}")
Decimal("${amount}");
Withdraw non-fungibles
Name |
|
Type | Method |
Callable by |
|
Arguments |
|
Returns |
|
Description | Withdraws resources from the account by This method withdraws a resource of the given address and non-fungible local ids from the account vaults and returns it in a |
Transaction Manifest
CALL_METHOD
Address("${account_component_address}")
"withdraw_non_fungibles"
Address("${resource_address}")
Array<NonFungibleLocalId>(NonFungibleLocalId("${some_non_fungible_local_id}"));
Lock fee and withdraw
Name |
|
Type | Method |
Callable by |
|
Arguments |
|
Returns |
|
Description | Locks some amount of XRD for fees and withdraws resources from the account by amount. This is a composite method which calls both |
Transaction Manifest
CALL_METHOD
Address("${account_component_address}")
"lock_fee_and_withdraw"
Decimal("${amount_of_xrd_to_lock_for_fees}")
Address("${resource_address}")
Decimal("${amount}");
Lock fee and withdraw non-fungibles
Name |
|
Type | Method |
Callable by |
|
Arguments |
|
Returns |
|
Description | Locks some amount of XRD for fees and withdraws resources from the account by non-fungible local ids This is a composite method which calls both |
Transaction Manifest
CALL_METHOD
Address("${account_component_address}")
"lock_fee_and_withdraw_non_fungibles"
Decimal("${amount_of_xrd_to_lock_for_fees}")
Address("${resource_address}")
Array<NonFungibleLocalId>(NonFungibleLocalId("${some_non_fungible_local_id}"));
Create proof of amount
Name |
|
Type | Method |
Callable by |
|
Arguments |
|
Returns |
|
Description | Creates a proof of the specified resource and amount. This method creates a |
Transaction Manifest
CALL_METHOD
Address("${account_component_address}")
"create_proof_of_amount"
Address("${resource_address}")
Decimal("${amount}");
Create proof of non-fungibles
Name |
|
Type | Method |
Callable by |
|
Arguments |
|
Returns |
|
Description | Creates a proof of the specified resource of and non-fungible local ids. This method creates a Proof of the resource and non-fungible local ids specified to the method and returns it. |
Transaction Manifest
CALL_METHOD
Address("${account_component_address}")
"create_proof_of_non_fungibles"
Address("${resource_address}")
Array<NonFungibleLocalId>(NonFungibleLocalId("${some_non_fungible_local_id}"));
Burn
Name |
|
Type | Method |
Callable by |
|
Arguments |
|
Returns | None |
Description | Burns the amount of the resource directly from the account’s vault. |
Transaction Manifest
CALL_METHOD
Address("${account_component_address}")
"burn"
Address("${resource_address}")
Decimal("${amount}");
Burn non-fungibles
Name |
|
Type | Method |
Callable by |
|
Arguments |
|
Returns | None |
Description | Burns the non-fungibles of the resource directly from the account’s vault. |
Transaction Manifest
CALL_METHOD
Address("${account_component_address}")
"burn_non_fungibles"
Address("${resource_address}")
Array<NonFungibleLocalId>(NonFungibleLocalId("${some_non_fungible_local_id}"));
Set default deposit rule
Name |
|
Type | Method |
Callable by |
|
Arguments |
|
Returns | None |
Description | Sets the default deposit rule of the account This method changes the default deposit rule of the account changing the behavior of how the account handles deposits from third-parties of resources that it does not have a specific rule for. |
Transaction Manifest
CALL_METHOD
Address("${account_component_address}")
"set_default_deposit_rule"
Enum<DefaultDepositRule::Accept>();
A more complete manifest example of this can be found here.
Set resource preference
Name |
|
Type | Method |
Callable by |
|
Arguments |
|
Returns | None |
Description | Sets the resource preference of a resource. This method sets and overrides the preference of the resource in the account. |
Transaction Manifest
CALL_METHOD
Address("${account_component_address}")
"set_resource_preference"
Address("${resource_address}")
Enum<ResourcePreference::Allowed>();
A more complete manifest example of this can be found here.
Remove resource preference
Name |
|
Type | Method |
Callable by |
|
Arguments |
|
Returns | None |
Description | Removes the preference of a resource making it use the default deposit rule instead. If no preference for this resource exists then nothing happens. |
Transaction Manifest
CALL_METHOD
Address("${account_component_address}")
"remove_resource_preference"
Address("${resource_address}");
Add authorized depositor
Name |
|
Type | Method |
Callable by |
|
Arguments |
|
Returns | None |
Description | Adds an authorized depositor badge to the set of authorized depositors. |
Transaction Manifest
CALL_METHOD
Address("${account_component_address}")
"add_authorized_depositor"
Enum<1u8>(Address("${resource_address}"));
A more complete manifest example of this can be found here.
Remove authorized depositor
Name |
|
Type | Method |
Callable by |
|
Arguments |
|
Returns | None |
Description | Removes an authorized depositor badge to the set of authorized depositors. |
Transaction Manifest
CALL_METHOD
Address("${account_component_address}")
"remove_authorized_depositor"
Enum<1u8>(Address("${resource_address}"));
A more complete manifest example of this can be found here.
Securify
Name |
|
Type | Method |
Callable by |
|
Arguments | None |
Returns |
|
Description | Securifies the account, transitioning it from operating in signature mode to operating in badge mode. This method securifies the account minting a new account owner badge and changing the account’s current owner access rule to a new access rule of the account owner badge and returns the minted owner badge. The returned badge then must be stored somewhere, ideally in an access controller. This method is only callable by the |
Transaction Manifest
CALL_METHOD
Account("${account_component_address}")
"securify";
TAKE_ALL_FROM_WORKTOP
Address("${account_owner_badge_address}")
Bucket("owner_badge");
# Do something with the owner badge, ideally create an access controller and deposit
# it there.
Note that the transaction manifest above is not complete, more specifically, the account owner badge returned from the securify method is not deposited anywhere in this manifest. Ideally, the account owner badge would be stored in an access controller.
Try deposit or abort
Name |
|
Type | Method |
Callable by | Public |
Arguments |
|
Returns | None |
Description | Attempts to deposit resources in the account, aborting the transaction if the deposit fails because the account has been configured to disallow the deposit. This method is intended to be used for transfers or deposits where the owner isn’t present. If the owner is present, use |
Transaction Manifest
CALL_METHOD
Address("${account_component_address}")
"try_deposit_or_abort"
Bucket("some_bucket")
None;
Try deposit or refund
Name |
|
Type | Method |
Callable by |
|
Arguments |
|
Returns |
|
Description | Attempts to deposit resources in the account, refunding them returns them if the deposit fails. This method attempts to deposit a bucket of resources into the account, if the account is configured to disallow deposits of this resource then they’re returned and refunded back as a bucket. This method is intended to be used for automated airdrop scenarios where the owner isn’t present. If the owner is present, use Using this method causes the manifest to be non-conforming. |
Transaction Manifest
CALL_METHOD
Address("${account_component_address}")
"try_deposit_or_refund"
Bucket("some_bucket")
None;
Try deposit batch or abort
Name |
|
Type | Method |
Callable by | Public |
Arguments |
|
Returns | None |
Description | Attempts to deposit buckets of resources into the account aborts the transaction if any of them can’t be deposited. This method attempts to deposit buckets of resources into the account, if the account is configured to disallow deposit of any the resources then the transaction aborts. This method is intended to be used for transfers or deposits where the owner isn’t present. If the owner is present, use |
Transaction Manifest
CALL_METHOD
Address("${account_component_address}")
"try_deposit_batch_or_abort"
Expression("ENTIRE_WORKTOP")
None;
Try deposit batch or refund
Name |
|
Type | Method |
Callable by | Public |
Arguments |
|
Returns |
|
Description | Attempts to deposit buckets of resources into the account and refunds all of them if any of them can’t be deposited. This method attempts to deposit buckets of resources into the account, if the account is configured to disallow deposit of any the resources then they’re all returned and refunded back as buckets. This method is intended to be used for automated airdrop scenarios where the owner isn’t present. If the owner is present, use Using this method causes the manifest to be non-conforming. |
Transaction Manifest
CALL_METHOD
Address("${account_component_address}")
"try_deposit_batch_or_refund"
Expression("ENTIRE_WORKTOP")
None;