Skip to content

Commit

Permalink
class_metadata (#94)
Browse files Browse the repository at this point in the history
  • Loading branch information
sczembor authored Oct 9, 2023
1 parent aecc7cd commit 5680cf3
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
toolchain: 1.69.0
override: true
components: rustfmt, clippy

- name: Install wasm32 toolchain
if: env.GIT_DIFF
run: rustup target add wasm32-unknown-unknown
Expand Down
66 changes: 65 additions & 1 deletion contracts/oracle/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize};
use near_sdk::collections::{LazyOption, UnorderedSet};
use near_sdk::collections::{LazyOption, LookupMap, UnorderedSet};
use near_sdk::serde::Serialize;
use near_sdk::{
env, near_bindgen, require, AccountId, Balance, Gas, PanicOnDefault, Promise, PromiseError,
Expand All @@ -20,6 +20,7 @@ pub use crate::storage::*;
pub use crate::util::*;

mod errors;
mod migrate;
mod storage;
mod util;

Expand Down Expand Up @@ -52,6 +53,9 @@ pub struct Contract {

/// used for backend key rotation
pub admins: UnorderedSet<AccountId>,

/// class metadata
pub class_metadata: LookupMap<ClassId, ClassMetadata>,
}

// Implement the contract structure
Expand Down Expand Up @@ -85,6 +89,7 @@ impl Contract {
authority_pubkey: pubkey_from_b64(authority),
used_identities: UnorderedSet::new(StorageKey::UsedIdentities),
admins,
class_metadata: LookupMap::new(StorageKey::ClassMetadata),
}
}

Expand All @@ -106,6 +111,11 @@ impl Contract {
self.used_identities.contains(&normalised_id)
}

/// Returns `ClassMetadata` by class. Returns none if the class is not found.
pub fn class_metadata(&self, class: ClassId) -> Option<ClassMetadata> {
self.class_metadata.get(&class)
}

// all SBT queries should be done through registry

/**********
Expand Down Expand Up @@ -307,6 +317,22 @@ impl Contract {
);
}

/// Allows admin to update class metadata.
/// Panics if not admin or the class is not found (Currently oracle only supports classes: [1,2])
#[handle_result]
pub fn set_class_metadata(
&mut self,
class: ClassId,
metadata: ClassMetadata,
) -> Result<(), CtrError> {
self.assert_admin();
if class != 1 && class != 2 {
return Err(CtrError::BadRequest("class not found".to_string()));
}
self.class_metadata.insert(&class, &metadata);
Ok(())
}

// TODO:
// - fn sbt_renew
}
Expand Down Expand Up @@ -335,6 +361,7 @@ mod checks;
pub mod tests {
use crate::*;
use ed25519_dalek::Keypair;
use near_sdk::test_utils::test_env::alice;
use near_sdk::test_utils::VMContextBuilder;
use near_sdk::{testing_env, VMContext};

Expand Down Expand Up @@ -369,6 +396,16 @@ pub mod tests {
11 * SECOND
}

fn class_metadata() -> ClassMetadata {
ClassMetadata {
name: "test_1".to_string(),
symbol: None,
icon: None,
reference: None,
reference_hash: None,
}
}

/// SBT claim ttl in seconds
const CLAIM_TTL: u64 = 2;

Expand Down Expand Up @@ -610,4 +647,31 @@ pub mod tests {
assert!(res.is_err());
assert_bad_request(res, "IAH SBT cannot be mint during the elections period");
}

#[test]
#[should_panic(expected = "not an admin")]
fn set_class_metadata_not_admin() {
let (_, mut ctr, _) = setup(&alice(), &alice());
let _ = ctr.set_class_metadata(1, class_metadata());
}

#[test]
fn set_class_metadata_wrong_class() {
let (_, mut ctr, _) = setup(&alice(), &acc_admin());
match ctr.set_class_metadata(3, class_metadata()) {
Err(CtrError::BadRequest(_)) => (),
Err(error) => panic!("expected BadRequest, got: {:?}", error),
Ok(_) => panic!("expected BadRequest, got: Ok"),
}
}

#[test]
fn set_class_metadata() {
let (_, mut ctr, _) = setup(&alice(), &acc_admin());
match ctr.set_class_metadata(1, class_metadata()) {
Ok(_) => (),
Err(error) => panic!("expected Ok, got: {:?}", error),
}
assert_eq!(ctr.class_metadata(1).unwrap(), class_metadata());
}
}
41 changes: 41 additions & 0 deletions contracts/oracle/src/migrate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use crate::*;

// registry/v1.3.0
#[derive(BorshDeserialize, PanicOnDefault)]
pub struct OldState {
pub metadata: LazyOption<ContractMetadata>,
pub registry: AccountId,
pub claim_ttl: u64,
pub sbt_ttl_ms: u64,
pub authority_pubkey: [u8; PUBLIC_KEY_LEN],
pub used_identities: UnorderedSet<Vec<u8>>,
pub admins: UnorderedSet<AccountId>,
}

#[near_bindgen]
impl Contract {
#[private]
#[init(ignore_state)]
/* pub */
pub fn migrate(class_metadata: Vec<(ClassId, ClassMetadata)>) -> Self {
let old_state: OldState = env::state_read().expect("failed");
// new field in the smart contract :
// + class_metadata: LookupMap<ClassId, ClassMetadata>

let mut c_metadata = LookupMap::new(StorageKey::ClassMetadata);
for (class_id, class_metadata) in class_metadata {
c_metadata.insert(&class_id, &class_metadata);
}

Self {
metadata: old_state.metadata,
registry: old_state.registry,
claim_ttl: old_state.claim_ttl,
sbt_ttl_ms: old_state.sbt_ttl_ms,
authority_pubkey: old_state.authority_pubkey,
used_identities: old_state.used_identities,
admins: old_state.admins,
class_metadata: c_metadata,
}
}
}
1 change: 1 addition & 0 deletions contracts/oracle/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ pub enum StorageKey {
ContractMetadata,
UsedIdentities,
Admins,
ClassMetadata,
}
90 changes: 89 additions & 1 deletion contracts/oracle/tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::str::FromStr;
use chrono::Utc;
use near_crypto::{SecretKey, Signature};
use near_sdk::ONE_NEAR;
use near_units::parse_near;
use serde_json::json;
use test_util::{
deploy_contract, gen_user_account,
Expand All @@ -13,7 +14,7 @@ use workspaces::{types::Balance, Account, AccountId, Contract, DevNetwork, Worke

use near_sdk::borsh::BorshSerialize;
use oracle_sbt::{Claim, MINT_TOTAL_COST};
use sbt::ContractMetadata;
use sbt::{ClassMetadata, ContractMetadata};

const AUTHORITY_KEY: &str = "zqMwV9fTRoBOLXwt1mHxBAF3d0Rh9E9xwSAXR3/KL5E=";
const CLAIM_TTL: u64 = 3600 * 24 * 365 * 100;
Expand Down Expand Up @@ -341,3 +342,90 @@ async fn try_sbt_mint(
}
}
}

#[tokio::test]
async fn migration_mainnet() -> anyhow::Result<()> {
let worker_sandbox = workspaces::sandbox().await?;
let worker_mainnet = workspaces::mainnet().await?;
let oracle_address: AccountId = "fractal.i-am-human.near".parse()?;
let oracle = worker_sandbox
.import_contract(&oracle_address, &worker_mainnet)
.initial_balance(parse_near!("10000000 N"))
.transact()
.await?;

let admin = worker_sandbox.dev_create_account().await?;
let registry = worker_sandbox.dev_create_account().await?;

// init the contract
let res = oracle
.call("new")
.args_json(json!({
"authority": &String::from(AUTHORITY_KEY),
"admin": admin.id(),
"registry": registry.id(),
"claim_ttl": CLAIM_TTL,
"metadata": ContractMetadata{spec: "sbt".to_owned(), name: "oracle".to_owned(), symbol: "iah".to_owned(), icon: None, base_uri: None, reference: None, reference_hash: None},
}))
.max_gas()
.transact()
.await?;

assert!(res.is_success(), "{:?}", res.receipt_failures());

// deploy the new contract
let res = oracle
.as_account()
.deploy(include_bytes!("../../res/oracle_sbt.wasm"))
.await?;

assert!(res.is_success());

let new_oracle = res.into_result()?;

let class_metadata_1 = ClassMetadata {
name: "test_1".to_string(),
symbol: None,
icon: None,
reference: None,
reference_hash: None,
};
let class_metadata_2 = ClassMetadata {
name: "test_2".to_string(),
symbol: None,
icon: None,
reference: None,
reference_hash: None,
};

// call the migrate method
let res = new_oracle
.call("migrate")
.args_json(json!({"class_metadata": [[1, class_metadata_1], [2, class_metadata_2]]}))
.max_gas()
.transact()
.await?;
assert!(res.is_success(), "{:?}", res.receipt_failures());

let class_metdata: Option<ClassMetadata> = new_oracle
.call("class_metadata")
.args_json(json!({"class": 1}))
.max_gas()
.transact()
.await?
.json()?;

assert_eq!(class_metdata, Some(class_metadata_1));

let class_metdata: Option<ClassMetadata> = new_oracle
.call("class_metadata")
.args_json(json!({"class": 2}))
.max_gas()
.transact()
.await?
.json()?;

assert_eq!(class_metdata, Some(class_metadata_2));

Ok(())
}

0 comments on commit 5680cf3

Please sign in to comment.