Policies in smart contract

Hey,

We are looking into policies for our smart contract, but can’t find any example of it used. It’s a bit hard to get started only through the rust docs, so we were wondering if there is any additional documentation or small example of it used somewhere?

We unfortunately do not have any real examples that are readily available other than what we have for testing the implementation. We never got around to writing them.

I will write a basic example that hopefully shows you how they can be used if you can wait until tomorrow evening, or Thursday morning.

1 Like

That would be great thanks!

Also could you point me to the testing of the implementation, I was looking for it, but couldn’t find it.

Sorry for the delay, but unrelated complications happened.

In any case, here is a simple example of using a policy to accept or reject transfers in a contract.
An entrypoint that rejects transfers from accounts which do not have a policy stating that their country of residence is Denmark.

#![cfg_attr(not(feature = "std"), no_std)]
/*!
 * A contract that acts like an account (can send, store and accept CCD),
 * but only allows deposits from accounts that have a specific policy.
 */

use concordium_std::*;

#[derive(Reject)]
enum ContractError {
    InvalidPolicy,
}

type ContractResult<A> = Result<A, ContractError>;

#[init(contract = "deposit", parameter = "()")]
#[inline(always)]
fn contract_init<S: HasStateApi>(
    _ctx: &impl HasInitContext,
    _state_builder: &mut StateBuilder<S>,
) -> Result<(), ContractError> {
    Ok(())
}

#[receive(contract = "deposit", name = "deposit", payable)]
#[inline(always)]
/// Check whether the sender has the correct policy, and based on that either
/// accept the transfer or not.
fn contract_receive_deposit<S: HasStateApi>(
    ctx: &impl HasReceiveContext,
    _host: &impl HasHost<(), StateApiType = S>,
    _amount: Amount,
) -> ContractResult<()> {
    let policies = ctx.policies();
    // iterate through the policies and try to find whether
    // there is a country of residence among the signed attributes.
    // if so, then accept the deposit.
    for mut policy in policies {
        let mut buf = [0u8; 31];
        while let Some((tag, len)) = policy.next_item(&mut buf) {
            if tag == attributes::COUNTRY_OF_RESIDENCE {
                if buf[0..len.into()] == b"DK"[..] {
                    return Ok(()); // accept the deposit.
                }
            }
        }
    }
    // the right attribute was not found, so reject the deposit.
    Err(ContractError::InvalidPolicy)
}

#[receive(contract = "deposit", name = "retrieve")]
#[inline(always)]
/// Send the current balance of the contract to the owner account.
fn contract_retrieve_deposit<S: HasStateApi>(
    ctx: &impl HasReceiveContext,
    host: &impl HasHost<(), StateApiType = S>,
) -> ContractResult<()> {
    // we know the receiver exists, and we have the funds. So this will succeed.
    host.invoke_transfer(&ctx.owner(), host.self_balance()).unwrap_abort();
    Ok(())
}

2 Likes

What would the policies be in case of a multi credential account sending a transaction?

It is the list of all the policies in all the credentials that are part of the account at the time the contract execution happens.

1 Like