Skip to content

Commit

Permalink
Add return value to sbt_revoke_by_owner (#88)
Browse files Browse the repository at this point in the history
Co-authored-by: Amit Yadav <[email protected]>
Co-authored-by: Robert Zaremba <[email protected]>
  • Loading branch information
3 people authored Sep 25, 2023
1 parent cab7c4a commit cfc92b1
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 12 deletions.
2 changes: 2 additions & 0 deletions contracts/community-sbt/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ Change log entries are to be added to the Unreleased section. Example entry:

### Breaking Changes

- The registry method `sbt_revoke_by_owner` now returns `boolean` indicating if all the tokens were revoked. `true` if all the tokens were revoked succesfully or `false` if not and the method needs to be called agian.

### Bug Fixes

## v4.3.0 (2023-09-07)
Expand Down
52 changes: 48 additions & 4 deletions contracts/registry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2266,8 +2266,8 @@ mod tests {

ctr.sbt_mint(vec![(alice(), vec![m1_1.clone(), m1_2.clone()])]);

// revoke (burn) tokens minted for alice from issuer2
ctr.sbt_revoke_by_owner(alice(), true);
let res = ctr.sbt_revoke_by_owner(alice(), true);
assert!(res);

let log_burn = mk_log_str(
"burn",
Expand Down Expand Up @@ -2302,7 +2302,8 @@ mod tests {
ctx.predecessor_account_id = issuer1();
testing_env!(ctx.clone());
assert_eq!(test_utils::get_logs().len(), 0);
ctr.sbt_revoke_by_owner(alice(), false);
let res = ctr.sbt_revoke_by_owner(alice(), false);
assert!(res);

let log_revoke = mk_log_str(
"revoke",
Expand All @@ -2320,7 +2321,7 @@ mod tests {
assert!(res_with_expired.is_empty());
let res_without_expired = ctr.sbt_tokens_by_owner(alice(), None, None, None, Some(true));
assert!(res_without_expired.len() == 1);
assert_eq!(res[0].1.len(), 2);
assert_eq!(res_without_expired[0].1.len(), 2);
assert_eq!(ctr.sbt_supply(issuer1()), 2);
assert_eq!(ctr.sbt_supply(issuer2()), 0);
assert_eq!(ctr.sbt_supply_by_class(issuer1(), 1), 1);
Expand Down Expand Up @@ -2381,6 +2382,49 @@ mod tests {
assert_eq!(ctr.sbt_supply(issuer2()), 9);
}

#[test]
fn sbt_revoke_by_owner_limit() {
let (mut ctx, mut ctr) = setup(&issuer1(), 200 * MINT_DEPOSIT);

// mint tokens to alice from issuer1
let batch_metadata = mk_batch_metadata(100);
ctr.sbt_mint(vec![(alice(), batch_metadata[..50].to_vec())]);

// mint tokens to alice from issuer2
ctx.predecessor_account_id = issuer2();
ctx.prepaid_gas = max_gas();
testing_env!(ctx.clone());
ctr.sbt_mint(vec![(alice(), batch_metadata[..50].to_vec())]);

ctx.prepaid_gas = max_gas();
testing_env!(ctx.clone());
let res = ctr.sbt_tokens_by_owner(alice(), None, None, None, None);
assert_eq!(res[0].1.len(), 50);
assert_eq!(res[1].1.len(), 50);

assert_eq!(ctr.sbt_supply(issuer1()), 50);
assert_eq!(ctr.sbt_supply(issuer2()), 50);

ctx.prepaid_gas = max_gas();
testing_env!(ctx.clone());
// revoke (burn) tokens minted for alice from issuer2
let res = ctr.sbt_revoke_by_owner(alice(), true);
assert!(!res);

ctx.prepaid_gas = max_gas();
testing_env!(ctx);
// revoke (burn) tokens minted for alice from issuer2
let res = ctr.sbt_revoke_by_owner(alice(), true);
assert!(res);

// make sure the balances are updated correctly
let res = ctr.sbt_tokens_by_owner(alice(), None, None, None, None);
assert_eq!(res[0].1.len(), 50);

assert_eq!(ctr.sbt_supply(issuer1()), 50);
assert_eq!(ctr.sbt_supply(issuer2()), 0);
}

#[test]
fn is_human() {
let (mut ctx, mut ctr) = setup(&fractal_mainnet(), 150 * MINT_DEPOSIT);
Expand Down
24 changes: 17 additions & 7 deletions contracts/registry/src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use near_sdk::{json_types::Base64VecU8, near_bindgen, AccountId};
use crate::*;

const MAX_LIMIT: u32 = 1000;
const MAX_REVOKE_PER_CALL: u32 = 25;

#[near_bindgen]
impl SBTRegistry for Contract {
Expand Down Expand Up @@ -350,20 +351,28 @@ impl SBTRegistry for Contract {
}

/// Revokes owners SBTs issued by the caller either by burning or updating their expire
/// time. The function will try to revoke at most `MAX_LIMIT` tokens (to fit into the tx
/// time. The function will try to revoke at most `MAX_REVOKE_PER_CALL` tokens (to fit into the tx
/// gas limit), so when an owner has many tokens from the issuer, the issuer may need to
/// call this function multiple times, until all tokens are revoked. Issuer should query
/// `sbt_supply_by_owner` to check if the function should be called again.
/// call this function multiple times, until all tokens are revoked.
/// Retuns true if all the tokens were revoked, false otherwise.
/// If false is returned issuer must call the method until true is returned
/// Must be called by an SBT contract.
/// Must emit `Revoke` event.
/// Must also emit `Burn` event if the SBT tokens are burned (removed).
fn sbt_revoke_by_owner(&mut self, owner: AccountId, burn: bool) {
fn sbt_revoke_by_owner(&mut self, owner: AccountId, burn: bool) -> bool {
let issuer = env::predecessor_account_id();
let issuer_id = self.assert_issuer(&issuer);
let mut tokens_by_owner =
self.sbt_tokens_by_owner(owner.clone(), Some(issuer.clone()), None, None, Some(true));
let mut tokens_by_owner = self.sbt_tokens_by_owner(
owner.clone(),
Some(issuer.clone()),
None,
Some(MAX_REVOKE_PER_CALL),
Some(true),
);
let remaining_supply = self.sbt_supply_by_owner(owner.clone(), issuer.clone(), None);
let tokens_len = tokens_by_owner[0].1.len();
if tokens_by_owner.is_empty() {
return;
return true;
};
let (_, tokens) = tokens_by_owner.pop().unwrap();

Expand Down Expand Up @@ -441,6 +450,7 @@ impl SBTRegistry for Contract {
tokens: token_ids,
}
.emit_revoke();
tokens_len >= remaining_supply as usize
}

/// Allows issuer to update token metadata reference and reference_hash.
Expand Down
2 changes: 1 addition & 1 deletion contracts/sbt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ pub trait SBTRegistry {
/// Must be called by an SBT contract.
/// Must emit `Revoke` event.
/// Must also emit `Burn` event if the SBT tokens are burned (removed).
fn sbt_revoke_by_owner(&mut self, owner: AccountId, burn: bool);
fn sbt_revoke_by_owner(&mut self, owner: AccountId, burn: bool) -> bool;

/// Allows issuer to update token metadata reference and reference_hash.
/// * `updates` is a list of triples: (token ID, reference, reference hash).
Expand Down

0 comments on commit cfc92b1

Please sign in to comment.