Skip to content

Commit

Permalink
implement is_human_call_lock (#96)
Browse files Browse the repository at this point in the history
Co-authored-by: sczembor <[email protected]>
  • Loading branch information
robert-zaremba and sczembor authored Oct 31, 2023
1 parent 0641d65 commit babd065
Show file tree
Hide file tree
Showing 7 changed files with 324 additions and 74 deletions.
6 changes: 5 additions & 1 deletion contracts/registry/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,13 @@ Change log entries are to be added to the Unreleased section. Example entry:

- Added `authorized_flaggers` query.
- Added `admin_add_authorized_flagger` method.
- added `is_human_call_lock` method: allows dapp to lock an account for soul transfers and calls a recipient contract when the predecessor has a proof of personhood.

### Breaking Changes

- New contract field: `transfer_lock`.
- `sbt_soul_transfer` will fail if an account has an active transfer lock.

### Bug Fixes

## v1.6.0 (2023-10-08)
Expand All @@ -48,7 +52,7 @@ Change log entries are to be added to the Unreleased section. Example entry:

### Breaking Changes

- Recommended `cost.mint_deposit` is decreased by 0.001 miliNEAR (in total).
- Recommended `cost.mint_deposit` is decreased by 0.001 milliNEAR (in total).
- `soul_transfer` conflict resolution is updated to panic.
- Default `registry.sbt_soul_transfer` limit is decreased from 25 to 20.

Expand Down
23 changes: 19 additions & 4 deletions contracts/registry/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@ The IAH Registry supports the following extra queries, which are not part of the
See the function docs for more complete documentation.

- `sbt_mint_iah(token_spec: Vec<(AccountId, Vec<TokenMetadata>)>) -> Vec<TokenId>` is a wrapper around `sbt_mint` and `is_human`. It mints SBTs only when all recipients are humans.
- `is_human_call(ctr: AccountId, function: String, payload: JSONString)` checks if the predecessor account (_caller_) account is human (using `is_human` method). If it's not, then it panics and returns the deposit. Otherwise it makes a cross contract call passing the deposit:

- `sbt_burn(issuer: AccountId, tokens: Vec<TokenId>, memo: Option<String>)` - every holder can burn some of his tokens.

- `sbt_burn_all()` - method to burn all caller tokens (from all issuers). To efficiently burn all tokens, the method must be called repeatedly until true is returned.

- `is_human_call(ctr: AccountId, function: String, payload: JSONString)` checks if the predecessor account (_caller_) account is human (using `is_human` method). If it's not, then it panics and returns the deposit. Otherwise it makes a cross contract call passing the provided deposit:

```python
ctr.function(caller=predecessor_account_id,
Expand All @@ -44,11 +49,19 @@ See the function docs for more complete documentation.
Classical example will registering an action (for poll participation), only when a user is a human.
Instead of `Poll --is_human--> Registry -> Poll`, we can simplify and do `Registry.is_human_call --> Poll`.

See the function documentation for more details and [integration test](https://github.com/near-ndc/i-am-human/blob/780e8cf8326fd0a7976c48afbbafd4553cc7b639/contracts/human_checker/tests/workspaces.rs#L131) for usage.o
See the function documentation for more details and [integration test](https://github.com/near-ndc/i-am-human/blob/780e8cf8326fd0a7976c48afbbafd4553cc7b639/contracts/human_checker/tests/workspaces.rs#L131) for usage.

- `sbt_burn(issuer: AccountId, tokens: Vec<TokenId>, memo: Option<String>)` - every holder can burn some of his tokens.
- `is_human_call_lock(ctr: AccountId, function: String, lock_duration: u64, with_proof: bool)` checks if the predecessor account (_caller_) account is human (using `is_human` method). If it's not, then it panics and returns the deposit. Otherwise it will extend the _account soul transfer lock_ (blocking account ability to execute soul transfers) and make a cross contract call passing the provided deposit:

- `sbt_burn_all()` - method to burn all caller tokens (from all issuers). To efficiently burn all tokens, the method must be called repeatedly until true is returned.
```python
ctr.function(caller=predecessor_account_id,
locked_until: time_in_ms_until_when_the_account_is_locked,
iah_proof=tokens_prooving_caller_humanity,
payload)
```

Classical example will be a voting: we need to assure that an account won't migrate to other one using a soul transfer, and vote from two different accounts. Alternative would be to records humanity proof (SBTs) - this approach
may be more difficult to implement, especially if we are going to supply more humanity proofs.

### Admin functions

Expand All @@ -61,3 +74,5 @@ See the function docs for more complete documentation.

The registry enables atomic `soul_transfers`. It Transfers all SBT tokens from one account to another account.
Additionally, it attempts to transfer the associated account flags. For example, if the 'from' account is blacklisted and initiates a soul transfer, the recipient account will also be flagged as blacklisted. If a conflict arises between the caller's and recipient's flags, the transfer will fail.

Soul transfer is blocked, if there is an active soul transfer lock. The lock may be requested by dapps, that relay on unique personhood linked to an account over a period of time (for example: voting, games).
30 changes: 30 additions & 0 deletions contracts/registry/src/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use near_sdk::env::panic_str;
use near_sdk::FunctionError;

#[cfg_attr(not(target_arch = "wasm32"), derive(PartialEq, Debug))]
pub enum IsHumanCallErr {
NotHuman,
}

impl FunctionError for IsHumanCallErr {
fn panic(&self) -> ! {
match self {
IsHumanCallErr::NotHuman => panic_str("caller is not a human"),
}
}
}

#[cfg_attr(not(target_arch = "wasm32"), derive(PartialEq, Debug))]
pub enum SoulTransferErr {
TransferLocked,
}

impl FunctionError for SoulTransferErr {
fn panic(&self) -> ! {
match self {
SoulTransferErr::TransferLocked => {
panic_str("soul transfer not possible: owner has a transfer lock")
}
}
}
}
10 changes: 9 additions & 1 deletion contracts/registry/src/events.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use near_sdk::{serde::Serialize, AccountId};
use near_sdk::{serde::Serialize, serde_json::json, AccountId};
use sbt::{EventPayload, NearEvent};

use crate::storage::AccountFlag;
Expand Down Expand Up @@ -31,6 +31,14 @@ pub(crate) fn emit_iah_unflag_accounts(accounts: Vec<AccountId>) {
});
}

/// `locked_until`: time in milliseconds until when the new account lock is established.
pub(crate) fn emit_transfer_lock(account: AccountId, locked_until: u64) {
emit_iah_event(EventPayload {
event: "transfer_lock",
data: json!({ "account": account, "locked_until": locked_until}),
});
}

#[cfg(test)]
mod tests {
use near_sdk::test_utils;
Expand Down
Loading

0 comments on commit babd065

Please sign in to comment.