0.4.7-pre.002
This commit is contained in:
@@ -18,7 +18,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.4.7"
|
||||
version = "0.4.7-pre.2"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
repository = "https://git.sasedev.com/Sasedev/khadhroony-bot3"
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
<!-- file: docs/validation/V0_4_7_PRE_002_METAPLEX_COVERAGE_AND_FACT_OWNERSHIP.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# `0.4.7-pre.002` — couverture Metaplex et propriété des faits
|
||||
|
||||
## Résultat
|
||||
|
||||
Le contrat couvre les 58 discriminants `0..57` sans trou ni statut ambigu. La source des comptes et signers est l’IDL Metaplex archivée `V1_14_0`; la constructibilité courante est confrontée à l’inventaire `mpl-token-metadata 5.1.1`.
|
||||
|
||||
### Classification fermée
|
||||
|
||||
- `decode_only_bridge_boundary` : 1 instruction(s) ;
|
||||
- `decode_only_obsolete` : 7 instruction(s) ;
|
||||
- `decode_only_replaced` : 9 instruction(s) ;
|
||||
- `executable_current` : 41 instruction(s) ;
|
||||
|
||||
Les opérations `executable_current` doivent toutes posséder un intent et un builder à la fin de `pre.006`. Les statuts decode-only ne peuvent être utilisés que pour une frontière Bubblegum, une opération obsolète ou une opération remplacée par une variante canonique.
|
||||
|
||||
## Contrat par instruction
|
||||
|
||||
| Disc. | Instruction | Statut | Exécution | Cible | Signers IDL |
|
||||
|------:|-------------------------------------------------------------|-------------------------------|------------------------------------|-----------|---------------------------------------------------------------------------------------------------------|
|
||||
| 0 | `CreateMetadataAccount` | `decode_only_replaced` | `forbidden_replaced` | `—` | mintAuthority, payer |
|
||||
| 1 | `UpdateMetadataAccount` | `decode_only_replaced` | `forbidden_replaced` | `—` | updateAuthority |
|
||||
| 2 | `DeprecatedCreateMasterEdition` | `decode_only_obsolete` | `forbidden_obsolete` | `—` | updateAuthority, printingMintAuthority, mintAuthority, payer, oneTimePrintingAuthorizationMintAuthority |
|
||||
| 3 | `DeprecatedMintNewEditionFromMasterEditionViaPrintingToken` | `decode_only_obsolete` | `forbidden_obsolete` | `—` | mintAuthority, burnAuthority, payer |
|
||||
| 4 | `UpdatePrimarySaleHappenedViaToken` | `executable_current` | `required` | `pre.004` | owner |
|
||||
| 5 | `DeprecatedSetReservationList` | `decode_only_obsolete` | `forbidden_obsolete` | `—` | resource |
|
||||
| 6 | `DeprecatedCreateReservationList` | `decode_only_obsolete` | `forbidden_obsolete` | `—` | payer, updateAuthority |
|
||||
| 7 | `SignMetadata` | `executable_current` | `required` | `pre.004` | creator |
|
||||
| 8 | `DeprecatedMintPrintingTokensViaToken` | `decode_only_obsolete` | `forbidden_obsolete` | `—` | burnAuthority |
|
||||
| 9 | `DeprecatedMintPrintingTokens` | `decode_only_obsolete` | `forbidden_obsolete` | `—` | updateAuthority |
|
||||
| 10 | `CreateMasterEdition` | `decode_only_replaced` | `forbidden_replaced` | `—` | updateAuthority, mintAuthority, payer |
|
||||
| 11 | `MintNewEditionFromMasterEditionViaToken` | `executable_current` | `required` | `pre.006` | newMintAuthority, payer, tokenAccountOwner |
|
||||
| 12 | `ConvertMasterEditionV1ToV2` | `decode_only_replaced` | `forbidden_replaced` | `—` | aucun |
|
||||
| 13 | `MintNewEditionFromMasterEditionViaVaultProxy` | `decode_only_obsolete` | `forbidden_obsolete` | `—` | newMintAuthority, payer, vaultAuthority |
|
||||
| 14 | `PuffMetadata` | `executable_current` | `required` | `pre.003` | aucun |
|
||||
| 15 | `UpdateMetadataAccountV2` | `executable_current` | `required` | `pre.003` | updateAuthority |
|
||||
| 16 | `CreateMetadataAccountV2` | `decode_only_replaced` | `forbidden_replaced` | `—` | mintAuthority, payer |
|
||||
| 17 | `CreateMasterEditionV3` | `executable_current` | `required` | `pre.006` | updateAuthority, mintAuthority, payer |
|
||||
| 18 | `VerifyCollection` | `executable_current` | `required` | `pre.004` | collectionAuthority, payer |
|
||||
| 19 | `Utilize` | `executable_current` | `required` | `pre.006` | useAuthority |
|
||||
| 20 | `ApproveUseAuthority` | `executable_current` | `required` | `pre.006` | owner, payer |
|
||||
| 21 | `RevokeUseAuthority` | `executable_current` | `required` | `pre.006` | owner |
|
||||
| 22 | `UnverifyCollection` | `executable_current` | `required` | `pre.004` | collectionAuthority |
|
||||
| 23 | `ApproveCollectionAuthority` | `executable_current` | `required` | `pre.004` | updateAuthority, payer |
|
||||
| 24 | `RevokeCollectionAuthority` | `executable_current` | `required` | `pre.004` | revokeAuthority |
|
||||
| 25 | `SetAndVerifyCollection` | `executable_current` | `required` | `pre.004` | collectionAuthority, payer |
|
||||
| 26 | `FreezeDelegatedAccount` | `decode_only_replaced` | `forbidden_replaced` | `—` | delegate |
|
||||
| 27 | `ThawDelegatedAccount` | `decode_only_replaced` | `forbidden_replaced` | `—` | delegate |
|
||||
| 28 | `RemoveCreatorVerification` | `executable_current` | `required` | `pre.004` | creator |
|
||||
| 29 | `BurnNft` | `decode_only_replaced` | `forbidden_replaced` | `—` | owner |
|
||||
| 30 | `VerifySizedCollectionItem` | `executable_current` | `required` | `pre.004` | collectionAuthority, payer |
|
||||
| 31 | `UnverifySizedCollectionItem` | `executable_current` | `required` | `pre.004` | collectionAuthority, payer |
|
||||
| 32 | `SetAndVerifySizedCollectionItem` | `executable_current` | `required` | `pre.004` | collectionAuthority, payer |
|
||||
| 33 | `CreateMetadataAccountV3` | `executable_current` | `required` | `pre.003` | mintAuthority, payer |
|
||||
| 34 | `SetCollectionSize` | `executable_current` | `required` | `pre.004` | collectionAuthority |
|
||||
| 35 | `SetTokenStandard` | `executable_current` | `required` | `pre.003` | updateAuthority |
|
||||
| 36 | `BubblegumSetCollectionSize` | `decode_only_bridge_boundary` | `forbidden_cross_program_boundary` | `—` | collectionAuthority, bubblegumSigner |
|
||||
| 37 | `BurnEditionNft` | `decode_only_replaced` | `forbidden_replaced` | `—` | owner |
|
||||
| 38 | `CreateEscrowAccount` | `executable_current` | `required` | `pre.006` | payer, authority |
|
||||
| 39 | `CloseEscrowAccount` | `executable_current` | `required` | `pre.006` | payer |
|
||||
| 40 | `TransferOutOfEscrow` | `executable_current` | `required` | `pre.006` | payer, authority |
|
||||
| 41 | `Burn` | `executable_current` | `required` | `pre.005` | authority |
|
||||
| 42 | `Create` | `executable_current` | `required` | `pre.003` | authority, payer |
|
||||
| 43 | `Mint` | `executable_current` | `required` | `pre.006` | authority, payer |
|
||||
| 44 | `Delegate` | `executable_current` | `required` | `pre.005` | authority, payer |
|
||||
| 45 | `Revoke` | `executable_current` | `required` | `pre.005` | authority, payer |
|
||||
| 46 | `Lock` | `executable_current` | `required` | `pre.005` | authority, payer |
|
||||
| 47 | `Unlock` | `executable_current` | `required` | `pre.005` | authority, payer |
|
||||
| 48 | `Migrate` | `executable_current` | `required` | `pre.006` | payer, authority |
|
||||
| 49 | `Transfer` | `executable_current` | `required` | `pre.005` | authority, payer |
|
||||
| 50 | `Update` | `executable_current` | `required` | `pre.003` | authority, payer |
|
||||
| 51 | `Use` | `executable_current` | `required` | `pre.006` | authority, payer |
|
||||
| 52 | `Verify` | `executable_current` | `required` | `pre.004` | authority |
|
||||
| 53 | `Unverify` | `executable_current` | `required` | `pre.004` | authority |
|
||||
| 54 | `Collect` | `executable_current` | `required` | `pre.006` | authority |
|
||||
| 55 | `Print` | `executable_current` | `required` | `pre.006` | editionMintAuthority, payer |
|
||||
| 56 | `Resize` | `executable_current` | `required` | `pre.006` | authority |
|
||||
| 57 | `CloseAccounts` | `executable_current` | `required` | `pre.005` | authority |
|
||||
|
||||
Les listes positionnelles complètes des comptes, leurs flags writable/signer, les préflights et les postconditions sont conservés sous `instruction_execution_contract` dans la matrice JSON active.
|
||||
|
||||
## Propriété unique des faits
|
||||
|
||||
| Fait | Propriétaire | Source autoritative |
|
||||
|--------------------------------------|-------------------|---------------------------------------------------|
|
||||
| `metadata.name` | `metadata` | `MetadataV1.name` |
|
||||
| `metadata.symbol` | `metadata` | `MetadataV1.symbol` |
|
||||
| `metadata.uri` | `metadata` | `MetadataV1.uri` |
|
||||
| `metadata.seller_fee_basis_points` | `metadata` | `MetadataV1.seller_fee_basis_points` |
|
||||
| `metadata.creators` | `metadata` | `MetadataV1.data.creators` |
|
||||
| `metadata.collection` | `metadata` | `MetadataV1.collection` |
|
||||
| `metadata.uses` | `metadata` | `MetadataV1.uses` |
|
||||
| `metadata.token_standard` | `metadata` | `MetadataV1.token_standard` |
|
||||
| `metadata.is_mutable` | `metadata` | `MetadataV1.is_mutable` |
|
||||
| `metadata.programmable_config` | `metadata` | `MetadataV1.programmable_config` |
|
||||
| `admin.update_authority` | `admin` | `MetadataV1.update_authority` |
|
||||
| `admin.collection_authority` | `admin` | `CollectionAuthorityRecord` |
|
||||
| `admin.use_authority` | `admin` | `UseAuthorityRecord` |
|
||||
| `admin.delegate` | `admin` | `MetadataDelegate/HolderDelegate/TokenRecord` |
|
||||
| `lifecycle.primary_sale_happened` | `lifecycle` | `MetadataV1.primary_sale_happened` |
|
||||
| `lifecycle.edition` | `lifecycle` | `Edition/MasterEdition` |
|
||||
| `lifecycle.edition_marker` | `lifecycle` | `EditionMarker/EditionMarkerV2` |
|
||||
| `lifecycle.programmable_token_state` | `lifecycle` | `TokenRecord.state` |
|
||||
| `lifecycle.locked_transfer` | `lifecycle` | `TokenRecord.locked_transfer` |
|
||||
| `lifecycle.token_owned_escrow` | `lifecycle` | `TokenOwnedEscrow` |
|
||||
| `lifecycle.historical_reservation` | `lifecycle` | `ReservationListV1/V2` |
|
||||
| `risk.royalty_configuration` | `risk_compliance` | `seller_fee_basis_points fact reference` |
|
||||
| `risk.unverified_creators` | `risk_compliance` | `creators verification fact reference` |
|
||||
| `risk.mutable_metadata` | `risk_compliance` | `is_mutable fact reference` |
|
||||
| `risk.rule_set` | `risk_compliance` | `programmable_config rule_set fact reference` |
|
||||
| `risk.sensitive_delegation` | `risk_compliance` | `delegate records and TokenRecord fact reference` |
|
||||
|
||||
Les observations d’instruction décrivent uniquement les mutations. Les snapshots de comptes possédés restent autoritatifs pour l’état final et ne doivent jamais être dupliqués par une projection concurrente.
|
||||
|
||||
## Non-fusion Token-2022
|
||||
|
||||
- domaine Metaplex : `metaplex_token_metadata` ;
|
||||
- domaine Token-2022 : `spl_token_2022_embedded_metadata` ;
|
||||
- aucune identité, URI ou autorité n’est fusionnée implicitement entre les deux domaines ;
|
||||
- une corrélation ultérieure doit conserver les deux provenances et signaler les conflits.
|
||||
|
||||
## Validation disponible dans cet environnement
|
||||
|
||||
- parsing JSON : effectué ;
|
||||
- inventaire `0..57` : effectué ;
|
||||
- unicité des faits : effectuée ;
|
||||
- tests Cargo : non exécutés, car `cargo` est absent de l’environnement de préparation.
|
||||
@@ -1,8 +1,16 @@
|
||||
<!-- file: kb-lib/CHANGELOG.md -->
|
||||
<!-- version: 6 -->
|
||||
<!-- version: 7 -->
|
||||
|
||||
# CHANGELOG — kb-lib
|
||||
|
||||
## 0.4.7-pre.002 — contrat Metaplex fermé
|
||||
|
||||
- classification exhaustive des 58 discriminants Metaplex Token Metadata ;
|
||||
- comptes positionnels et signers importés de l’IDL archivée `V1_14_0` ;
|
||||
- propriété unique des faits metadata, admin, lifecycle et risk/compliance ;
|
||||
- exposition directe des créateurs, collection, uses, token standard et programmable config dans le snapshot metadata autoritatif ;
|
||||
- tests contractuels d’exhaustivité, d’unicité et de séparation avec Token-2022.
|
||||
|
||||
## 0.4.6
|
||||
|
||||
- alignement de la crate sur la version fonctionnelle bot3 `0.4.6` ;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: kb-lib/README.md -->
|
||||
<!-- version: 14 -->
|
||||
<!-- version: 15 -->
|
||||
|
||||
# kb-lib
|
||||
|
||||
@@ -56,7 +56,7 @@ Les matrices actives sont maintenues sous [`../test-fixtures/contract-matrices/`
|
||||
|
||||
## Statut et limites
|
||||
|
||||
La migration des surfaces historiques jusqu’à un périmètre proche de bot2 `0.4.6` est largement réalisée. Le décodeur et les matérialisations Metaplex Token Metadata sont présents, mais l’exécuteur et la clôture fonctionnelle de cette surface restent à réaliser. Le registre ElGamal n’est pas déclaré validé sur Devnet ou Mainnet.
|
||||
La migration des surfaces historiques jusqu’à un périmètre proche de bot2 `0.4.6` est largement réalisée. Le décodeur Metaplex Token Metadata couvre les 58 discriminants et les matérialisations possèdent désormais un contrat fermé de propriété des faits. L’exécuteur et la clôture fonctionnelle de cette surface restent à réaliser entre `0.4.7-pre.003` et `pre.006`. Le registre ElGamal n’est pas déclaré validé sur Devnet ou Mainnet.
|
||||
|
||||
## Documents
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<!-- file: kb-lib/TODO.md -->
|
||||
<!-- version: 3 -->
|
||||
<!-- version: 4 -->
|
||||
|
||||
# TODO — kb-lib
|
||||
|
||||
## Entre `0.4.6` et `0.4.7` — Metaplex Token Metadata
|
||||
|
||||
- [ ] Matérialisation - vérifier la couverture exacte migrée depuis bot2.
|
||||
- [ ] Matérialisation - compléter uniquement les projections réellement manquantes.
|
||||
- [x] Matérialisation - vérifier la couverture exacte migrée depuis bot2.
|
||||
- [x] Matérialisation - compléter uniquement les projections réellement manquantes et fermer leur propriété unique.
|
||||
- [ ] Exécution - implémenter l’exécuteur à partir des contrats officiels vérifiés.
|
||||
- [ ] Tests - ajouter les tests unitaires et contractuels de l’exécuteur.
|
||||
- [ ] Intégration - préparer les contrats nécessaires au pipeline et aux démonstrations.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-lib/src/decoder/metadata/metaplex_token_metadata/decoder.rs
|
||||
// version: 30
|
||||
// version: 31
|
||||
|
||||
//! Exact contextual dispatch for Metaplex Token Metadata.
|
||||
|
||||
@@ -3051,6 +3051,80 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_002_contract_closes_all_instruction_statuses_and_idl_accounts() {
|
||||
let matrix: serde_json::Value = match serde_json::from_str(AUDIT_MATRIX_JSON) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => panic!("matrix parse failed: {error}"),
|
||||
};
|
||||
let entries = match matrix
|
||||
.get("instruction_execution_contract")
|
||||
.and_then(serde_json::Value::as_array)
|
||||
{
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => panic!("instruction_execution_contract missing"),
|
||||
};
|
||||
assert_eq!(entries.len(), 58);
|
||||
let mut discriminators = std::collections::BTreeSet::new();
|
||||
for entry in entries {
|
||||
let discriminator = match entry
|
||||
.get("discriminator")
|
||||
.and_then(serde_json::Value::as_u64)
|
||||
{
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => panic!("instruction discriminator missing"),
|
||||
};
|
||||
assert!(discriminators.insert(discriminator));
|
||||
assert!(entry.get("accounts").is_some_and(serde_json::Value::is_array));
|
||||
assert!(entry.get("signers").is_some_and(serde_json::Value::is_array));
|
||||
assert!(entry
|
||||
.get("coverage_status")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.is_some_and(|value| return value != "pending" && !value.is_empty()));
|
||||
assert!(entry
|
||||
.get("execute")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.is_some_and(|value| return value != "pending" && !value.is_empty()));
|
||||
}
|
||||
assert_eq!(discriminators, (0u64..58u64).collect());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_002_fact_ownership_is_unique_and_keeps_metadata_domains_separate() {
|
||||
let matrix: serde_json::Value = match serde_json::from_str(AUDIT_MATRIX_JSON) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => panic!("matrix parse failed: {error}"),
|
||||
};
|
||||
let facts = match matrix
|
||||
.get("fact_ownership_contract")
|
||||
.and_then(serde_json::Value::as_array)
|
||||
{
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => panic!("fact_ownership_contract missing"),
|
||||
};
|
||||
let mut names = std::collections::BTreeSet::new();
|
||||
for entry in facts {
|
||||
let fact = match entry.get("fact").and_then(serde_json::Value::as_str) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => panic!("fact name missing"),
|
||||
};
|
||||
assert!(names.insert(fact));
|
||||
assert!(entry
|
||||
.get("owner")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.is_some_and(|value| return !value.is_empty()));
|
||||
}
|
||||
let forbidden = match matrix
|
||||
.pointer("/fact_ownership_proof/cross_domain_merge_forbidden")
|
||||
.and_then(serde_json::Value::as_array)
|
||||
{
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => panic!("cross-domain proof missing"),
|
||||
};
|
||||
assert!(forbidden.contains(&serde_json::json!("metaplex_token_metadata")));
|
||||
assert!(forbidden.contains(&serde_json::json!("spl_token_2022_embedded_metadata")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn omitted_historical_discriminators_reject_invalid_wires() {
|
||||
let decoder = crate::DcMetadataMetaplexTokenMetadataDecoder;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-lib/src/materializer/metadata/core.rs
|
||||
// version: 5
|
||||
// version: 6
|
||||
|
||||
//! Stable Token-2022 and Metaplex metadata snapshot materializer.
|
||||
|
||||
@@ -134,6 +134,11 @@ pub fn materializer_metadata_materialize_metaplex_metadata_snapshot(
|
||||
"symbol": metadata.symbol,
|
||||
"uri": metadata.uri,
|
||||
"sellerFeeBasisPoints": metadata.seller_fee_basis_points,
|
||||
"creators": metadata.payload_json.get("creators").cloned().unwrap_or(serde_json::Value::Null),
|
||||
"collection": metadata.payload_json.get("collection").cloned().unwrap_or(serde_json::Value::Null),
|
||||
"uses": metadata.payload_json.get("uses").cloned().unwrap_or(serde_json::Value::Null),
|
||||
"tokenStandard": metadata.payload_json.get("tokenStandard").cloned().unwrap_or(serde_json::Value::Null),
|
||||
"programmableConfig": metadata.payload_json.get("programmableConfig").cloned().unwrap_or(serde_json::Value::Null),
|
||||
"primarySaleHappened": metadata.primary_sale_happened,
|
||||
"isMutable": metadata.is_mutable,
|
||||
"layout": snapshot.identity.layout,
|
||||
@@ -218,6 +223,18 @@ mod tests_metaplex_metadata_state {
|
||||
first.as_ref().map(|value| return value.payload_json["metadataSource"].clone()),
|
||||
std::result::Result::Ok(serde_json::json!("metaplex_owned_account"))
|
||||
);
|
||||
assert_eq!(
|
||||
first.as_ref().map(|value| return value.payload_json["creators"].clone()),
|
||||
std::result::Result::Ok(
|
||||
serde_json::json!([{"address":"creator111","verified":true,"share":100}])
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
first
|
||||
.as_ref()
|
||||
.map(|value| return value.payload_json["programmableConfig"].clone()),
|
||||
std::result::Result::Ok(serde_json::json!({"ruleSet":"rules111"}))
|
||||
);
|
||||
assert_eq!(
|
||||
first
|
||||
.as_ref()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user