Ripple Custody is a custody provider. It decides who must approve each request that DALP files with it, in the custody approval step before it signs. Ripple calls that request an intent and decides with its policies. This page lists the properties DALP sets on every intent, so that a policy can match them. A deployment that upgrades from 3.1 also changes the policies that name an old operation, as Operation names that changed from 3.1 to 3.2 lists. A new 3.2 installation can skip that section.
A policy that matches nothing stops the intent. Ripple runs only the intents that a policy covers, so a policy that names an old value leaves the matching operations unable to run. Set your policies before you onboard investors, and test them with one write of each kind you use.
How Ripple picks a policy
Ripple runs one policy for each intent. Among the policies that apply to the intent and whose condition matches, Ripple runs the one with the highest rank, or the older one at equal rank. An intent that no policy matches cannot run.
How Ripple picks one policy for an intent from its properties and the rank of each matching policy.
DALP signs each intent that it files, so Ripple treats it as a user-signed proposal. A policy that omits the intent origin matches it. A policy for system-signed proposals only does not. Under Ripple's CoerceDescendants governing strategy, a policy in a parent domain overrides a policy in a child domain, whatever its rank.
Ripple matches a value exactly with is, or matches text inside it with contains, and combines conditions with AND and OR, as its policy reference describes.
What DALP sets on every intent
Each intent carries a description for the approver and the custom properties below. The description starts with the name and symbol of the contract the request calls, such as Acme Bond (ACME): mint(..., when the request calls one contract that has a name. It names the action, the asset or contract, the amount, and the counterparty, and DALP keeps it to 250 characters. A call that also sends the chain's coin ends with that amount, such as deposit. A list of addresses, numbers or true/false values shows its first three entries and how many more follow. A list of roles shows the role names, and any other list shows its count, such as [2 items]. A list that holds a number of more than 32 digits, such as a list of claim topic ids, shows its count. A country code passed as a number in its own argument shows as its two letters and its number, such as SA (682). A country code inside a structure does not read this way: a token created from an instrument template passes its settings as one structure, so its description shows the name, symbol, type and decimals of the token and not its country. A description that runs past 250 characters is cut from the end of the call, so a call that lists addresses can lose its last arguments, such as the country list of a batch registration. DALP limits each property value to 100 characters. DALP writes the description and every property itself, and Ripple does not check them against the request, so a policy that matches them trusts DALP's account of the request.
The operation in dalpKind depends on the request:
- A data feed, settlement, token sale or trading venue transaction sends its own operation, for example
feeds.submit,xvp.execute,token-sale.buyortradingvenue.halt-market. - A custody withdrawal payout sends
custody-.ops. withdrawal- payout - When a settlement or token sale write first sends an ERC20 allowance, that transaction carries the same kind as the write, for example
token-sale.buy, anddalpContractnames the token. - A transaction that names no operation sends
txqueue.enqueue, the generic name of DALP's transaction queue. - A smart account runs its operations as user operations. When Ripple holds the owner key of a smart account, the user operation that Ripple signs sends the operation that it runs, for example
token.mint, so a policy ondalpKindistoken.mintmatches that mint too. DALP sendsuserOponly for a user operation that carries no operation name. - On a multisig wallet, a smart account that needs signatures from more than one member, the signature of the member who starts the operation also sends the operation, and each approval sends
user-operation.approve. - When Ripple holds the key that submits user operations to the chain, that transaction sends
aa.bundle. - With named endpoints on, the request that files a ticker sends
custody.display.ticker, and the request that files an endpoint sendscustody.display.endpoint.
DALP 3.1 sent every property in this table except dalpRolesUndescribed and the dalpEip712 properties. The dalpKind values changed, as the last section lists.
Name contract destinations
RIPPLE_NAMED_ENDPOINTS, the config key signer., is off by default. While it is off, DALP makes no ticker, endpoint or ledger call, every order goes to the bare address it calls, and the intent description names the contract. The chart has no value for it. Set it in the signer.ripple config override that a Ripple signer needs.
With it on, DALP files a Ripple ticker for each token it creates and a named endpoint for each organisation smart wallet, system contract and token. An order to a contract goes to its endpoint once Ripple approves the endpoint and leaves it unlocked. Any other order keeps the bare address. The setting also sends text messages as EIP-191 content, so an approver reads the message instead of prefixed bytes.
A policy written for orders to an address can stop matching an order to an endpoint, and the same holds for a message of the EIP-191 type. Check both before you turn the setting on. Only tokens and contracts deployed after that get an endpoint, unless an administrator adds older ones by hand.
Policies for role changes
A role change is the request most worth a stricter workflow. DALP marks each role change so that a policy can find it.
When the role list is longer than 100 characters, DALP sends the leading role names that fit, adds +N for the N roles it left out, and sets dalpRolesUndescribed to true. A grant of twelve roles on the platform's access manager contract, for example, can send admin,auditor,system.
DALP also sets dalpRolesUndescribed to true, with no dalpRoles, when it cannot name every role, or when the roles are not all on one contract:
- A role ID that DALP does not know.
- A batch that changes roles and also calls another contract.
- A call that passes a role change on to another contract to run.
- A call that carries other calls inside it and that DALP cannot unpack, even when it changes no role.
DALP reads roles only from calls to functions that it knows. A call to a function that DALP does not know adds no name to dalpRoles and does not set dalpRolesUndescribed, even when it changes a role, so that intent gets the policy that its other properties match.
Add a dalpRolesUndescribed policy and rank it above your role policies, because Ripple runs only one policy for each intent.
- Add one policy that matches
dalpRolesUndescribedistrue. Apply it to every intent type, intent origin and domain that your role policies apply to, and give it your strictest workflow for role changes. - Give it a higher rank than every policy that a role change can also match.
- Keep it in the domain of your role policies or in a parent domain, because of the
CoerceDescendantsrule above.
A request with dalpRolesUndescribed set then gets that workflow, never a looser one that matched only the names that fit. A role change through a function DALP does not know sets neither property, so this policy does not catch it.
Role changes reach Ripple as transaction orders and as manifests, so apply each role policy to both intent types. To match a single role, use contains, because is on dalpRoles matches only the complete list. Before you use contains, check that no longer role name includes the same text.
One request can change more than one role, and Ripple still runs only one policy. Rank your role policies by how strict their workflows are, with the strictest highest, and above any other policy that a role change can also match, such as a policy on dalpKind. A grant of auditor and admin together then gets the stricter admin workflow. Never give two role policies with different workflows the same rank, because at equal rank Ripple runs the older policy.
Keep the paymaster and bundler wallets approval-free
Two DALP wallets sign for every user, not for one investor. The paymaster wallet signs a sponsorship ticket, with dalpKind paymaster.sponsorship, for each operation that DALP pays gas for. The wallet that submits user operations to the chain sends aa.bundle. A policy that holds either intent for approval stops every sponsored operation on that network, not one.
Give these two intents a policy whose workflow needs no approver, and rank it ahead of every approval policy that they also match. When a policy holds a sponsorship ticket, DALP refuses the operation with “The paymaster wallet's Ripple policy holds sponsorship ticket signing”, and names the Ripple intent. For each hold on either wallet, DALP logs one warning and adds one to the dalp_ counter.
Operation names that changed from 3.1 to 3.2
A policy whose condition names a 3.1 value stops matching after the upgrade. Its intents then go to the next policy that matches, or cannot run when no policy matches. Until every deployment you run is on 3.2, match both values with an OR condition, so that an intent from either version finds its policy.
DALP 3.1 sent contract. and the contract function name when a workflow wrote to a contract directly, for example during organisation setup, invitations, token creation and system migration. Writes that came through the API sent the operation name of their route, and many of those names changed too. 3.2 sends an operation name for both. The tables pair each 3.1 value with its 3.2 name. The values token.mint, token.burn, token.pause, token.unpause and xvp.execute kept their 3.1 names. For any other value that the tables do not list, run the operation on a 3.2 test deployment and match the value that Ripple shows.
Direct contract writes
Token operations
The pairs above are the values the API sends when you attach, replace or detach a feature on an existing token. A token that you create with its features, and a feature that a creation flow replaces, are filed under token.feature. and the feature ID, then .create or .attach. A replacement in a creation flow keeps token. for the create step and sends token. for the attach step.
System, identity and wallet operations
Operations that 3.2 no longer sends
These 3.1 values have no 3.2 successor. Remove a policy condition that names only one of them.
paymaster.and the paymaster addressset- signer- rollback. refund,Splitter. recover Erc20 refundSplitter. set Sponsorship token.delegateBySigtoken-,sale. add- payment- currency token-,sale. remove- payment- currency token-,sale. configure- vesting token-,sale. set- purchase- limits token-sale.set-soft-capsystem.and the wallet, replaced byidentity- delete. paired. system.identity-delete
Check your policies before you upgrade
- Run the operations that your policies cover on a 3.2 test deployment.
- Compare the
dalpKindthat Ripple shows for each intent with the condition of the policy you expect to run. - Check each policy against the property table above, and add the
dalpRolesUndescribedpolicy.
Troubleshooting
Ripple sources
- Policy reference: the conditions a policy can use, and how Ripple ranks and picks a policy.