Restrict who may hold
Create an asset only accounts attested investor.verified by the issuer may
hold, with at most two holders; watch a transfer to an unattested account be
refused, and read the rule that refused it.
1. Create the asset with a combined policy
POLICY="{\"all\":{\"rules\":[{\"credential\":{\"type\":\"investor.verified\",\"attesters\":[\"$ISSUER\"]}},{\"max_holders\":{\"n\":2}}]}}"
R=$(alias_tx asset create-asset ECONOMIC --settlement-denom aeusd --policy "$POLICY" --from issuer)
RASSET=$(wait_tx "$(jq -r .txhash <<<"$R")" | created_asset)
echo "restricted asset: $AEVA_EXPLORER/assets/$RASSET?tab=kinds"The issuer must pass the policy too, so it attests itself first, then issues:
R=$(alias_tx credential attest "$ISSUER" investor.verified +720h --from issuer); wait_tx "$(jq -r .txhash <<<"$R")" >/dev/null
R=$(alias_tx rights issue "$RASSET" 0 "$ISSUER" 100 --from issuer); wait_tx "$(jq -r .txhash <<<"$R")" >/dev/null
aevad query rights holder-count "$RASSET" 0 --node "$AEVA_NODE" -o json | jq -c .2. A transfer to an unattested account is refused
aevad keys add stranger --home "$AEVA_HOME" --keyring-backend test --no-backup >/dev/null 2>&1 || true
STRANGER=$(aevad keys show stranger -a --home "$AEVA_HOME" --keyring-backend test)
R=$(alias_tx rights transfer "$STRANGER" "$RASSET" 0:10 --from issuer)
wait_tx "$(jq -r .txhash <<<"$R")" >/dev/null || true
echo "refused tx: $AEVA_EXPLORER/tx/$(jq -r .txhash <<<"$R")"Expected output
tx refused: code 3 (policy): failed to execute message; message index: 0: policy denied at all[0].credential(investor.verified): …
refused tx: https://explorer.aevachain.com/tx/…wait_tx prints the refusal. The transaction is in a block — it paid its
fee — but failed; nothing moved.
The rule path all[0].credential(investor.verified) names the first rule of
the ALL that refused.
3. Attest the investor; the transfer goes through
R=$(alias_tx credential attest "$INVESTOR" investor.verified +720h --from issuer); wait_tx "$(jq -r .txhash <<<"$R")" >/dev/null
R=$(alias_tx rights transfer "$INVESTOR" "$RASSET" 0:10 --from issuer); wait_tx "$(jq -r .txhash <<<"$R")" >/dev/null
echo "transferred: $AEVA_EXPLORER/tx/$(jq -r .txhash <<<"$R")"4. The holder cap
The asset now has two holders (issuer and investor). Attest the stranger and try again — the credential passes, the cap refuses:
R=$(alias_tx credential attest "$STRANGER" investor.verified +720h --from issuer); wait_tx "$(jq -r .txhash <<<"$R")" >/dev/null
R=$(alias_tx rights transfer "$STRANGER" "$RASSET" 0:10 --from issuer)
wait_tx "$(jq -r .txhash <<<"$R")" >/dev/null || trueExpected output
tx refused: code 3 (policy): failed to execute message; message index: 0: policy denied at all[1].max_holders(2): …5. Revoke
Revoking a credential stops new transfers to that account; units already held stay where they are.
R=$(alias_tx credential revoke "$INVESTOR" investor.verified --from issuer); wait_tx "$(jq -r .txhash <<<"$R")" >/dev/null
aevad query credential valid "$INVESTOR" investor.verified "$ISSUER" --node "$AEVA_NODE" -o json | jq -r '"investor credential valid: \(.valid // false)"'
aevad query rights position "$RASSET" 0 "$INVESTOR" --node "$AEVA_NODE" -o json | jq -r '"investor still holds \(.position.units)"'Expected output
investor credential valid: false
investor still holds 10See Transfers & policies and Credentials.
Last updated