Skip to main content

Protection and Recovery

State​

note

An authenticated client is necessary for protection/recovery operations.

The global state of protection or recovery can be obtained (and later on, refreshed) with:

const protectionState = await authenticatedClient.protectionState();
const refreshedState = await protectionState.refresh();
caution

All user operations (activateProtection), as well as refresh, do return a new state. Always use the most recent state, and discard the former state. In the example above, the var protectionState must not be used any more as soon as refreshedState is available.

Protection​

The setup of a Polkadot account protection is a 2-steps process:

  • Choose 2 officers among the official list of Logion Legal Officers.
  • activate the protection on-chain given a valid (i.e. closed and non-void) Identity LOC with each LLO.

Choose LLOs and LOCs​

For below example to work, you will need a valid Identity LOC with both alice and bob.

const legalOfficers = authenticatedClient.getLegalOfficers();
const locsState = await authenticatedClient.locsState();

const alice = authenticatedClient.getLegalOfficer(
ValidAccountId.polkadot("vQx5kESPn8dWyX4KxMCKqUyCaWUwtui1isX6PVNcZh2Ghjitr")
);
const bob = authenticatedClient.getLegalOfficer(
ValidAccountId.polkadot("vQvWaxNDdzuX5N3qSvGMtjdHcQdw1TAcPNgx4S1Utd3MTxYeN")
);
const aliceLoc = locsState.closedLocs.Identity.find(
loc => loc.data().ownerAccountId.equals(alice.account)
&& loc.data().voidInfo === undefined
);
const bobLoc = locsState.closedLocs.Identity.find(
loc => loc.data().ownerAccountId.equals(bob.account)
&& loc.data().voidInfo === undefined
);

Activate your protection​

const noProtection = await authenticatedClient.protectionState() as NoProtection;
const pending = await noProtection.activateProtection({
payload: {
legalOfficer1: alice,
legalOfficer2: bob,
requesterIdentityLoc1: aliceLoc,
requesterIdentityLoc2: bobLoc,
},
signer,
});

Vault​

Operations require an activated protection (see above).

Transfer from vault​

let activeProtection = (await authenticatedClient.protectionState()) as ActiveProtection;

let vaultState = await activeProtection.vaultState();
const vaultAddress = vaultState.vaultAddress;
vaultState = await vaultState.createVaultTransferRequest({
legalOfficer: alice,
amount: Lgnt.from(1n),
destination: ValidAccountId.polkadot("vQvZF2YMgKuQhzfF7T3xDjHjuEmcPSUVEoUDPy1mzuSXzFgca"),
signer,
});
const pendingRequest = vaultState.pendingVaultTransferRequests[0];

Transfer to vault​

A transfer to the vault is similar to any other transfer. Simply use vaultState.vaultAddress as destination.

Check vault balance​

You must fist wait for one Legal Officer acceptance, and then see the effect of the transfer on the vault balance:

vaultState = await vaultState.refresh();

const balance = vaultState.balances[0];
console.log("Balance :%s", `${balance.balance.coefficient.toInteger()}.${balance.balance.coefficient.toFixedPrecisionDecimals(2)}${balance.balance.prefix.symbol}`)

Recovery​

In order to recover a previously protected account, you must first create a new Polkadot account. Then follow these 3-stpes process:

  • Request a recovery to the Legal Officers who protected the lost account.
  • Upon acceptance and vouching of the recovery, activate the protection (of the new account) on the chain.
  • Claim the recovery.

One claimed, you can recover the lost assets.

Overview of the whole process​

Recovery State Diagram

Request a recovery​

Recovery must be requested to the same Legal Officers who accepted to protect the lost account (in this case, Alice and Bob).

const noProtection = await authenticatedClient.protectionState() as NoProtection;
const pending = await noProtection.requestRecovery({
payload: {
recoveredAddress: ValidAccountId.polkadot("vQv6hV73oeb7cJHXaWsw3K4bmgHjzxj9zm3nZtZPsxbsvhcTk"),
legalOfficer1: alice,
legalOfficer2: bob,
requesterIdentityLoc1: aliceLoc,
requesterIdentityLoc2: bobLoc,
},
signer,
});

Activate the new protection​

You must first wait for LO's acceptance and vouching, and then activate the new protection:

const accepted = (await pending.refresh()) as AcceptedProtection;

let pendingRecovery = await accepted.activate(signer) as PendingRecovery;

Claim the recovery​

Finally, the recovery is claimed:

pendingRecovery = await pendingRecovery.waitForFullyReady();
const claimed = await pendingRecovery.claimRecovery(signer);

Recover the lost balance​

const recoveredBalance = await claimed.recoveredBalanceState();
await recoveredBalance.transfer({
signer,
destination: ValidAccountId.polkadot("vQuQm7K3Qt1nQYDzqQzCPQj6b4AQFPZgD5FLnEpBTvCx1rVix"),
amount: recoveredBalance.balances[0].available,
});
info

The destination may be any address, not necessarily the new account address.

Recover the lost vault​

As for any transfer from a vault, you must wait for one LO's approval.

const newVault = await claimed.vaultState();
let recoveredVault = await claimed.recoveredVaultState();
recoveredVault = await recoveredVault.createVaultTransferRequest({
legalOfficer: alice,
amount: recoveredVault.balances[0].available,
destination: newVault.vaultAddress,
signer,
});

// ... Wait for LO's acceptance ...
newVault = await newVault.refresh();

// Check Vault balance
const newBalance = newVault.balances[0];
console.log("Balance :%s", `${newBalance.balance.coefficient.toInteger()}.${newBalance.balance.coefficient.toFixedPrecisionDecimals(2)}${newBalance.balance.prefix.symbol}`);
info

The destination may be any address, not necessarily the new vault address.

Rejection Management​

If rejected by one or more Legal Officer, you may either

  • Resubmit the request to the officer who rejected,
  • Or completely cancel your recovery request.
Resubmit to LO who rejected
let rejectedRecovery = (await authenticatedClient.protectionState()) as RejectedRecovery;
const rejecter = rejectedRecovery.protectionParameters.states.find(state => state.status === "REJECTED")!.legalOfficer;
rejectedRecovery.resubmit(rejecter);
Cancel the recovery request
let rejectedRecovery = (await authenticatedClient.protectionState()) as RejectedRecovery;
rejectedRecovery.cancel();