From 134d8ca0774e43b1a6c42f5f349e39127cf6c444 Mon Sep 17 00:00:00 2001 From: sczembor Date: Wed, 4 Oct 2023 20:01:23 +0200 Subject: [PATCH] class_metadata --- contracts/oracle/src/lib.rs | 20 ++++- contracts/oracle/src/migrate.rs | 41 ++++++++++ contracts/oracle/src/storage.rs | 1 + contracts/oracle/tests/integration_test.rs | 90 +++++++++++++++++++++- 4 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 contracts/oracle/src/migrate.rs diff --git a/contracts/oracle/src/lib.rs b/contracts/oracle/src/lib.rs index f000e44e..34feabc9 100644 --- a/contracts/oracle/src/lib.rs +++ b/contracts/oracle/src/lib.rs @@ -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, @@ -20,6 +20,7 @@ pub use crate::storage::*; pub use crate::util::*; mod errors; +mod migrate; mod storage; mod util; @@ -52,6 +53,9 @@ pub struct Contract { /// used for backend key rotation pub admins: UnorderedSet, + + /// class metadata + pub class_metadata: LookupMap, } // Implement the contract structure @@ -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), } } @@ -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 { + self.class_metadata.get(&class) + } + // all SBT queries should be done through registry /********** @@ -307,6 +317,14 @@ impl Contract { ); } + /// Allows admin to update class metadata. + /// Panics if the class is not found (Currently oracle only supports classes: [1,2]) + pub fn set_class_metadata(&mut self, class: ClassId, metadata: ClassMetadata) { + self.assert_admin(); + require!(class == 1 || class == 2, "class not found"); + self.class_metadata.insert(&class, &metadata); + } + // TODO: // - fn sbt_renew } diff --git a/contracts/oracle/src/migrate.rs b/contracts/oracle/src/migrate.rs new file mode 100644 index 00000000..18c4f5db --- /dev/null +++ b/contracts/oracle/src/migrate.rs @@ -0,0 +1,41 @@ +use crate::*; + +// registry/v1.3.0 +#[derive(BorshDeserialize, PanicOnDefault)] +pub struct OldState { + pub metadata: LazyOption, + pub registry: AccountId, + pub claim_ttl: u64, + pub sbt_ttl_ms: u64, + pub authority_pubkey: [u8; PUBLIC_KEY_LEN], + pub used_identities: UnorderedSet>, + pub admins: UnorderedSet, +} + +#[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 + + 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, + } + } +} diff --git a/contracts/oracle/src/storage.rs b/contracts/oracle/src/storage.rs index 2ee08aa5..489b0041 100644 --- a/contracts/oracle/src/storage.rs +++ b/contracts/oracle/src/storage.rs @@ -9,4 +9,5 @@ pub enum StorageKey { ContractMetadata, UsedIdentities, Admins, + ClassMetadata, } diff --git a/contracts/oracle/tests/integration_test.rs b/contracts/oracle/tests/integration_test.rs index a582085f..4e5a87e6 100644 --- a/contracts/oracle/tests/integration_test.rs +++ b/contracts/oracle/tests/integration_test.rs @@ -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, @@ -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; @@ -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?; + // .into_result()?; + 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 = 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 = new_oracle + .call("class_metadata") + .args_json(json!({"class": 2})) + .max_gas() + .transact() + .await? + .json()?; + + assert_eq!(class_metdata, Some(class_metadata_2)); + + Ok(()) +}