From 9aab8f9c02d015f72f8e01a7a8765cb19ec53ddd Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Wed, 25 Jun 2025 16:11:06 -0300 Subject: [PATCH 01/41] docs: review base CW20 --- README.md | 10 ++++++ contracts/cw20-base/src/allowances.rs | 4 +-- contracts/cw20-base/src/contract.rs | 52 +++++++++++++-------------- contracts/cw20-base/src/enumerable.rs | 6 ++-- contracts/cw20-base/src/msg.rs | 8 ++--- 5 files changed, 45 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index a26a924..983325c 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,11 @@ Extends the CW20 standard with T-REX functionalities and implements permissioned #### Storage - `token_info`: TokenInfo + - Name, symbol, decimals, supply and minter(opt) + - Minter has an addr and can have a cap - `balances`: Map - `allowances`: Map<(Addr, Addr), AllowanceInfo> + - uint with an expiration date - `identity_registry`: Addr - `compliance`: Addr @@ -28,6 +31,13 @@ Extends the CW20 standard with T-REX functionalities and implements permissioned - Description: Transfers tokens on behalf of the owner if the recipient is verified and compliant. - Interaction: Similar to `transfer`, but checks allowances first. +- `send(contract: Addr, amount: Uint128, msg: Binary) -> Result` + - Msg is weird `Binary::from(r#"{"some":123}"#.as_bytes());` +- `burn(amount: Uint128) -> Result` +- `mint(contract: Addr, amount: Uint128) -> Result` +- Increase/Decrease Allowance + - Can also change expiration + #### Query - `balance(address: Addr) -> BalanceResponse` - `token_info() -> TokenInfoResponse` diff --git a/contracts/cw20-base/src/allowances.rs b/contracts/cw20-base/src/allowances.rs index f5d0519..f653f46 100644 --- a/contracts/cw20-base/src/allowances.rs +++ b/contracts/cw20-base/src/allowances.rs @@ -261,7 +261,7 @@ mod tests { use cw20::{Cw20Coin, TokenInfoResponse}; use crate::contract::{execute, instantiate, query_balance, query_token_info}; - use crate::msg::{ExecuteMsg, InstantiateMsg, InstantiateTokenInfo, Registeries}; + use crate::msg::{ExecuteMsg, InstantiateMsg, InstantiateTokenInfo, Registries}; fn get_balance>(deps: Deps, address: T) -> Uint128 { query_balance(deps, address.into()).unwrap().balance @@ -285,7 +285,7 @@ mod tests { mint: None, marketing: None, }, - registeries: Registeries { + registries: Registries { compliance_address: MockApi::default().addr_make("compliance_addr").to_string(), }, }; diff --git a/contracts/cw20-base/src/contract.rs b/contracts/cw20-base/src/contract.rs index 98320ff..5faf3ff 100644 --- a/contracts/cw20-base/src/contract.rs +++ b/contracts/cw20-base/src/contract.rs @@ -128,7 +128,7 @@ pub fn instantiate( }; let compliance_addr = deps .api - .addr_validate(&msg.registeries.compliance_address)?; + .addr_validate(&msg.registries.compliance_address)?; COMPLIANCE_ADDRESS.save(deps.storage, &compliance_addr)?; TOKEN_INFO.save(deps.storage, &data)?; @@ -684,7 +684,7 @@ mod tests { }; use super::*; - use crate::msg::{InstantiateMarketingInfo, InstantiateTokenInfo, Registeries}; + use crate::msg::{InstantiateMarketingInfo, InstantiateTokenInfo, Registries}; use utils::compliance::QueryMsg::CheckTokenCompliance; fn get_balance>(deps: Deps, address: T) -> Uint128 { @@ -734,7 +734,7 @@ mod tests { mint: mint.clone(), marketing: None, }, - registeries: Registeries { + registries: Registries { compliance_address: MockApi::default().addr_make("compliance_addr").to_string(), }, }; @@ -780,7 +780,7 @@ mod tests { mint: None, marketing: None, }, - registeries: Registeries { + registries: Registries { compliance_address: MockApi::default().addr_make("compliance_addr").to_string(), }, }; @@ -823,7 +823,7 @@ mod tests { }), marketing: None, }, - registeries: Registeries { + registries: Registries { compliance_address: MockApi::default().addr_make("compliance_addr").to_string(), }, }; @@ -873,7 +873,7 @@ mod tests { }), marketing: None, }, - registeries: Registeries { + registries: Registries { compliance_address: MockApi::default().addr_make("compliance_addr").to_string(), }, }; @@ -909,7 +909,7 @@ mod tests { logo: Some(Logo::Url("url".to_owned())), }), }, - registeries: Registeries { + registries: Registries { compliance_address: MockApi::default() .addr_make("compliance_addr") .to_string(), @@ -954,7 +954,7 @@ mod tests { logo: Some(Logo::Url("url".to_owned())), }), }, - registeries: Registeries { + registries: Registries { compliance_address: MockApi::default() .addr_make("compliance_addr") .to_string(), @@ -1234,7 +1234,7 @@ mod tests { mint: None, marketing: None, }, - registeries: Registeries { + registries: Registries { compliance_address: MockApi::default().addr_make("compliance_addr").to_string(), }, }; @@ -1261,7 +1261,7 @@ mod tests { mint: None, marketing: None, }, - registeries: Registeries { + registries: Registries { compliance_address: MockApi::default().addr_make("compliance_addr").to_string(), }, }; @@ -1617,7 +1617,7 @@ mod tests { mint: None, marketing: None, }, - registeries: Registeries { + registries: Registries { compliance_address: MockApi::default() .addr_make("compliance_addr") .to_string(), @@ -1729,7 +1729,7 @@ mod tests { logo: Some(Logo::Url("url".to_owned())), }), }, - registeries: Registeries { + registries: Registries { compliance_address: MockApi::default().addr_make("compliance_addr").to_string(), }, }; @@ -1790,7 +1790,7 @@ mod tests { logo: Some(Logo::Url("url".to_owned())), }), }, - registeries: Registeries { + registries: Registries { compliance_address: MockApi::default().addr_make("compliance_addr").to_string(), }, }; @@ -1850,7 +1850,7 @@ mod tests { logo: Some(Logo::Url("url".to_owned())), }), }, - registeries: Registeries { + registries: Registries { compliance_address: MockApi::default().addr_make("compliance_addr").to_string(), }, }; @@ -1910,7 +1910,7 @@ mod tests { logo: Some(Logo::Url("url".to_owned())), }), }, - registeries: Registeries { + registries: Registries { compliance_address: MockApi::default().addr_make("compliance_addr").to_string(), }, }; @@ -1970,7 +1970,7 @@ mod tests { logo: Some(Logo::Url("url".to_owned())), }), }, - registeries: Registeries { + registries: Registries { compliance_address: MockApi::default().addr_make("compliance_addr").to_string(), }, }; @@ -2031,7 +2031,7 @@ mod tests { logo: Some(Logo::Url("url".to_owned())), }), }, - registeries: Registeries { + registries: Registries { compliance_address: MockApi::default().addr_make("compliance_addr").to_string(), }, }; @@ -2090,7 +2090,7 @@ mod tests { logo: Some(Logo::Url("url".to_owned())), }), }, - registeries: Registeries { + registries: Registries { compliance_address: MockApi::default().addr_make("compliance_addr").to_string(), }, }; @@ -2152,7 +2152,7 @@ mod tests { logo: Some(Logo::Url("url".to_owned())), }), }, - registeries: Registeries { + registries: Registries { compliance_address: MockApi::default().addr_make("compliance_addr").to_string(), }, }; @@ -2212,7 +2212,7 @@ mod tests { logo: Some(Logo::Url("url".to_owned())), }), }, - registeries: Registeries { + registries: Registries { compliance_address: MockApi::default().addr_make("compliance_addr").to_string(), }, }; @@ -2268,7 +2268,7 @@ mod tests { logo: Some(Logo::Url("url".to_owned())), }), }, - registeries: Registeries { + registries: Registries { compliance_address: MockApi::default().addr_make("compliance_addr").to_string(), }, }; @@ -2326,7 +2326,7 @@ mod tests { logo: Some(Logo::Url("url".to_owned())), }), }, - registeries: Registeries { + registries: Registries { compliance_address: MockApi::default().addr_make("compliance_addr").to_string(), }, }; @@ -2385,7 +2385,7 @@ mod tests { logo: Some(Logo::Url("url".to_owned())), }), }, - registeries: Registeries { + registries: Registries { compliance_address: MockApi::default().addr_make("compliance_addr").to_string(), }, }; @@ -2442,7 +2442,7 @@ mod tests { logo: Some(Logo::Url("url".to_owned())), }), }, - registeries: Registeries { + registries: Registries { compliance_address: MockApi::default().addr_make("compliance_addr").to_string(), }, }; @@ -2506,7 +2506,7 @@ mod tests { logo: Some(Logo::Url("url".to_owned())), }), }, - registeries: Registeries { + registries: Registries { compliance_address: MockApi::default().addr_make("compliance_addr").to_string(), }, }; @@ -2563,7 +2563,7 @@ mod tests { logo: Some(Logo::Url("url".to_owned())), }), }, - registeries: Registeries { + registries: Registries { compliance_address: MockApi::default().addr_make("compliance_addr").to_string(), }, }; diff --git a/contracts/cw20-base/src/enumerable.rs b/contracts/cw20-base/src/enumerable.rs index 5bd6232..94effa9 100644 --- a/contracts/cw20-base/src/enumerable.rs +++ b/contracts/cw20-base/src/enumerable.rs @@ -89,7 +89,7 @@ mod tests { use cw20::{Cw20Coin, Expiration, TokenInfoResponse}; use crate::contract::{execute, instantiate, query, query_token_info}; - use crate::msg::{ExecuteMsg, InstantiateMsg, InstantiateTokenInfo, QueryMsg, Registeries}; + use crate::msg::{ExecuteMsg, InstantiateMsg, InstantiateTokenInfo, QueryMsg, Registries}; // this will set up the instantiation for other tests fn do_instantiate(mut deps: DepsMut, addr: &str, amount: Uint128) -> TokenInfoResponse { @@ -105,7 +105,7 @@ mod tests { mint: None, marketing: None, }, - registeries: Registeries { + registries: Registries { compliance_address: MockApi::default().addr_make("compliance_addr").to_string(), }, }; @@ -296,7 +296,7 @@ mod tests { do_instantiate(deps.as_mut(), &acct1, Uint128::new(12340000)); - // put money everywhere (to create balanaces) + // put money everywhere (to create balances) let info = message_info(&Addr::unchecked(acct1.clone()), &[]); let env = mock_env(); execute( diff --git a/contracts/cw20-base/src/msg.rs b/contracts/cw20-base/src/msg.rs index 1baba6b..bcf7019 100644 --- a/contracts/cw20-base/src/msg.rs +++ b/contracts/cw20-base/src/msg.rs @@ -29,12 +29,12 @@ pub struct InstantiateTokenInfo { #[cfg_attr(test, derive(Default))] pub struct InstantiateMsg { pub token_info: InstantiateTokenInfo, - pub registeries: Registeries, + pub registries: Registries, } #[cw_serde] #[derive(Default)] -pub struct Registeries { +pub struct Registries { pub compliance_address: String, } @@ -143,7 +143,7 @@ mod tests { use super::*; #[test] - fn validate_instantiatemsg_name() { + fn validate_instantiate_msg_name() { // Too short let mut msg = InstantiateMsg { token_info: InstantiateTokenInfo { @@ -164,7 +164,7 @@ mod tests { } #[test] - fn validate_instantiatemsg_symbol() { + fn validate_instantiate_msg_symbol() { // Too short let mut msg = InstantiateMsg { From 46c9600c13c8bd2698f2685857ee7c1cb8144486 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Wed, 25 Jun 2025 16:54:25 -0300 Subject: [PATCH 02/41] docs: add more info on compliance --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 983325c..0fd7681 100644 --- a/README.md +++ b/README.md @@ -102,21 +102,23 @@ Manages the list of trusted claim issuers. ### 5. Claim Topics Registry -Stores the required claim topics for token eligibility. +Stores the required claim topics for token eligibility. It is used for compliance. Only those with role ClaimRegistryManager can change this. #### Storage -- `required_claim_topics`: Vec +- `token_claim_topics`: Vec + - Token addresses are mapped to claims + - Claim is defined by a topic uint128 and an active bool #### Execute - `add_claim_topic(topic: u32) -> Result` - `remove_claim_topic(topic: u32) -> Result` #### Query -- `get_required_claim_topics() -> GetRequiredClaimTopicsResponse` +- `get_claims_for_token() -> StdResult>` ### 6. Modular Compliance -Implements transfer restriction rules. +Implements transfer restriction rules. Only compliance manager can execute changes. For now country compliance and claims compliance are the ones in use. #### Storage - `modules`: Vec From 53c370f3090ccb4d6db9464cb00f9681bb065484 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Wed, 25 Jun 2025 16:54:45 -0300 Subject: [PATCH 03/41] chore: make contract dependency more obvious --- integration-tests/tests/cw3643-full-test.js | 40 ++++++++++++--------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/integration-tests/tests/cw3643-full-test.js b/integration-tests/tests/cw3643-full-test.js index 6874e8c..5aab752 100644 --- a/integration-tests/tests/cw3643-full-test.js +++ b/integration-tests/tests/cw3643-full-test.js @@ -3,12 +3,12 @@ const assert = require('assert'); describe("CW3643 Token", function() { this.timeout(60000); // Increase timeout to 60 seconds for the entire test suite - + let owner, ownerClient; let recipient, recipientClient; let issuer, issuerClient; let contracts = {}; - + before(async function() { ({ account: owner, client: ownerClient } = await setupWallet()); ({ account: recipient, client: recipientClient } = await setupWallet()); @@ -17,10 +17,18 @@ describe("CW3643 Token", function() { // Deploy contracts in the correct order contracts.ownerRoles = await deployContract(ownerClient, owner.address, "owner_roles", { owner: owner.address }); contracts.agentRoles = await deployContract(ownerClient, owner.address, "agent_roles", { owner: owner.address }); - contracts.trustedIssuers = await deployContract(ownerClient, owner.address, "trusted_issuers", { owner_roles_address: contracts.ownerRoles.contractAddress }); - contracts.claimTopics = await deployContract(ownerClient, owner.address, "claim_topics", { owner_roles_address: contracts.ownerRoles.contractAddress }); - contracts.onChainId = await deployContract(ownerClient, owner.address, "on_chain_id", { owner: owner.address, trusted_issuer_addr: contracts.trustedIssuers.contractAddress }); - contracts.complianceRegistry = await deployContract(ownerClient, owner.address, "compliance_registry", { owner_roles_address: contracts.ownerRoles.contractAddress }); + contracts.trustedIssuers = await deployContract(ownerClient, owner.address, "trusted_issuers", { + owner_roles_address: contracts.ownerRoles.contractAddress + }); + contracts.claimTopics = await deployContract(ownerClient, owner.address, "claim_topics", { + owner_roles_address: contracts.ownerRoles.contractAddress + }); + contracts.onChainId = await deployContract(ownerClient, owner.address, "on_chain_id", { owner: owner.address, + trusted_issuer_addr: contracts.trustedIssuers.contractAddress + }); + contracts.complianceRegistry = await deployContract(ownerClient, owner.address, "compliance_registry", { + owner_roles_address: contracts.ownerRoles.contractAddress, + }); contracts.complianceClaims = await deployContract(ownerClient, owner.address, "compliance_claims", { identity_address: contracts.onChainId.contractAddress, owner_roles_address: contracts.ownerRoles.contractAddress, @@ -254,7 +262,7 @@ describe("CW3643 Token", function() { amount: null } }); - + assert.strictEqual(response, true); // Should be true because owner is still in CA }); @@ -268,7 +276,7 @@ describe("CW3643 Token", function() { amount: null } }); - + assert.strictEqual(response, true); // Should be true because issuer is still in CA }); @@ -288,7 +296,7 @@ describe("CW3643 Token", function() { it("should add a compliance module", async function() { await executeContract(ownerClient, owner.address, contracts.complianceRegistry.contractAddress, { - add_compliance_module: { + add_compliance_module: { token_address: contracts.cw20Base.contractAddress, module_address: contracts.complianceCountry.contractAddress, module_name: "CountryRestrictionCompliance" @@ -359,7 +367,7 @@ describe("CW3643 Token", function() { it("should fail transfer due to compliance check", async function() { const transferAmount = "1000"; - + // Get initial balance of recipient const initialBalance = await queryContract(ownerClient, contracts.cw20Base.contractAddress, { balance: { @@ -541,7 +549,7 @@ describe("CW3643 Token", function() { it("should remove compliance module", async function() { await executeContract(ownerClient, owner.address, contracts.complianceRegistry.contractAddress, { - remove_compliance_module: { + remove_compliance_module: { token_address: contracts.cw20Base.contractAddress, module_address: contracts.complianceCountry.contractAddress } @@ -602,7 +610,7 @@ describe("CW3643 Token", function() { const existingClaimTopics = await queryContract(ownerClient, contracts.claimTopics.contractAddress, { get_claims_for_token: { token_addr: contracts.cw20Base.contractAddress } }); - + for (const topic of existingClaimTopics) { await executeContract(ownerClient, owner.address, contracts.claimTopics.contractAddress, { remove_claim_topic_for_token: { @@ -622,7 +630,7 @@ describe("CW3643 Token", function() { // Add compliance modules await executeContract(ownerClient, owner.address, contracts.complianceRegistry.contractAddress, { - add_compliance_module: { + add_compliance_module: { token_address: contracts.cw20Base.contractAddress, module_address: contracts.complianceClaims.contractAddress, module_name: "ClaimsCompliance" @@ -630,7 +638,7 @@ describe("CW3643 Token", function() { }); await executeContract(ownerClient, owner.address, contracts.complianceRegistry.contractAddress, { - add_compliance_module: { + add_compliance_module: { token_address: contracts.cw20Base.contractAddress, module_address: contracts.complianceCountry.contractAddress, module_name: "CountryRestrictionCompliance" @@ -789,13 +797,13 @@ describe("CW3643 Token", function() { after(async function() { // Remove compliance modules await executeContract(ownerClient, owner.address, contracts.complianceRegistry.contractAddress, { - remove_compliance_module: { + remove_compliance_module: { token_address: contracts.cw20Base.contractAddress, module_address: contracts.complianceCountry.contractAddress } }); await executeContract(ownerClient, owner.address, contracts.complianceRegistry.contractAddress, { - remove_compliance_module: { + remove_compliance_module: { token_address: contracts.cw20Base.contractAddress, module_address: contracts.complianceClaims.contractAddress } From 11cc035dfccaddd3f688110906a04b5d95374964 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Wed, 25 Jun 2025 18:08:39 -0300 Subject: [PATCH 04/41] docs: add owner roles used --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0fd7681..1c2a099 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ Stores the mapping of wallet addresses to identity contracts. ### 4. Trusted Issuers Registry -Manages the list of trusted claim issuers. +Manages the list of trusted claim issuers. OwnerRole::IssuersRegistryManager can change trusted issuers. #### Storage - `trusted_issuers`: Map @@ -102,7 +102,7 @@ Manages the list of trusted claim issuers. ### 5. Claim Topics Registry -Stores the required claim topics for token eligibility. It is used for compliance. Only those with role ClaimRegistryManager can change this. +Stores the required claim topics for token eligibility. It is used for compliance. Only those with role OwnerRole::ClaimRegistryManager can change this. #### Storage - `token_claim_topics`: Vec @@ -118,7 +118,7 @@ Stores the required claim topics for token eligibility. It is used for complianc ### 6. Modular Compliance -Implements transfer restriction rules. Only compliance manager can execute changes. For now country compliance and claims compliance are the ones in use. +Implements transfer restriction rules. Only OwnerRole::ComplianceManager can execute changes. For now country compliance and claims compliance are the ones in use. #### Storage - `modules`: Vec From bc75915035324384fc9cb834ee532198ca6bdfb9 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Wed, 25 Jun 2025 18:08:53 -0300 Subject: [PATCH 05/41] docs: add initial review of codebase --- contracts-review.md | 136 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 contracts-review.md diff --git a/contracts-review.md b/contracts-review.md new file mode 100644 index 0000000..105ee09 --- /dev/null +++ b/contracts-review.md @@ -0,0 +1,136 @@ +# General +## Deploy order +- OwnerRoles +- AgentRoles (weirdly not used by others) +- TrustedIssuers (needs owner roles) +- ClaimTopics (needs owner roles) +- OnChainId (needs trusted issuer) +- ComplianceRegistry (needs owner roles) +- ComplianceClaims (needs chainId, owner roles and claim topics) +- ComplianceCountry (needs chainId and owner roles) +- CWBase (needs compliance registry) + +# Owner roles +- Stores owner, there is only a single one +- Adds verification if a specific address her a specific owner role +- Lets owner add other addresses with specific owner roles +- Stores compliance, issuer and claim registries + - IssuersRegistryManagers can add/remove trusted issuers + - ClaimRegistryManagers can add/remove topics + - Looks like unfinished center hub + +## Owner roles +```rust +pub enum OwnerRole { + OwnerAdmin, + RegistryAddressSetter, + ComplianceSetter, + ComplianceManager, + ClaimRegistryManager, + IssuersRegistryManager, + TokenInfoManager, +} +``` + +# Agent roles +- Very similar to owner roles but with agent roles + - Supply Modifiers can do mint/burn + - Transfer manager can do transfer from +- Looks like unfinished center hub +- Has placeholder implementations + - Is transfer allowed + - can transfer + - can receive +```rust +pub enum AgentRole { + SupplyModifiers, + Freezers, + TransferManager, + RecoveryAgents, + ComplianceAgent, + WhiteListManages, + AgentAdmin, +} +``` + +# Trusted issuer +- Checks if an address is a trusted issuer + - They are tied with a vector of claim topics +- OwnerRole::IssuersRegistryManager can change list + +# on chain Identity +- Holds address, country, keys and claims for an identity +- ManagementKey can change stuff +- Needs to check if an user is trusted to allow changes to execute remove/add claims + - Centralizes that +- I dont understand fully how to use this contract + - Do a centralized trusted add other identities? They add themselves? + +## Key types +- I dont quite get the key types +```rust +pub enum KeyType { + // 1: MANAGEMENT keys, which can manage the identity + ManagementKey, + // 2: EXECUTION keys, which perform actions in this identities name (signing, logins, transactions, etc.) + ExecutionKey, + // 3: CLAIM signer keys, used to sign claims on other identities which need to be revokable. + ClaimSignerKey, + // 4: ENCRYPTION keys, used to encrypt data e.g. hold in claims. + EncryptionKey, +} +``` + +# Compliance contracts +- Limit usage of a given contract + - I.e restrict US for cw20 + +## Compliance Registry +- Handles compliance modules + - When checking if compliant, checks every submodule for valid compliance +- Reqs owner roles + - Only compliance manager can change stuff + +## Compliance country restriction +- Restricts which countries comply +- Only compliance manager can add or remove restrictions +- Queries identities' country to check if valid + - Utilizes a contract that holds identity addresses + - OnChainId handles identities + +## Compliance claims +- Restricts usage based on claim topics +- Only query to check claims and compliance + - Compliance checks the claim topics + +### Claim Topics +- Holds token address <> topic association +- Claims are a topic + active bool +- ClaimRegistryManager can change topics +- Something feels off, I didn't find out how to link a claim with an user + - `I didn't quite get this` + +# CW20 base +## Optionals +Full info: https://github.com/CosmWasm/cw-plus/blob/main/packages/cw20/README.md +- Mintable + - Allows query of Minter +- Allowance + - Allows query of allowance +- Enumerable + - Allows queries of 'all' +- Marketing + - Allows more metadata info (description, logo) + - Download logo + +## Compliance +- A token address is stored +- It is used to send a message to that address, to check compliance + - Via smart wasm query +- On every burn, transfer, mint and sent + - It checks if the from, to and amount complies + +# Yet to Review +- Missing Executes of CW20 + - Just checked instantiate +- Usage test From a47b5df5b0c6c608562378a93d085792a20b22d4 Mon Sep 17 00:00:00 2001 From: Jhelison Uchoa Date: Mon, 30 Jun 2025 10:57:45 -0300 Subject: [PATCH 06/41] chore: update gitignore --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index 9095dea..539aaeb 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,9 @@ # IDEs *.iml .idea + +# Artifacts from builds +artifacts/ + +# Python artifacts +__pycache__/ From 933679f9f2cefc19d9ffb5377fb171317c9cc033 Mon Sep 17 00:00:00 2001 From: Jhelison Uchoa Date: Mon, 30 Jun 2025 10:58:13 -0300 Subject: [PATCH 07/41] refactor: fix typo on claim management --- contracts/on_chain_id/src/claim_management.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/on_chain_id/src/claim_management.rs b/contracts/on_chain_id/src/claim_management.rs index a765745..ccc2ac6 100644 --- a/contracts/on_chain_id/src/claim_management.rs +++ b/contracts/on_chain_id/src/claim_management.rs @@ -27,7 +27,7 @@ pub fn execute_add_claim( if !is_trusted { return Err(ContractError::Unauthorized { - reason: "Sender does not have persmission to add claim".to_string(), + reason: "Sender does not have permission to add claim".to_string(), }); } From d9ade8e0cc4920231fbca8f53ca524c9d5fae96a Mon Sep 17 00:00:00 2001 From: Jhelison Uchoa Date: Mon, 30 Jun 2025 10:58:40 -0300 Subject: [PATCH 08/41] chore: add contract manipulation scripts --- scripts/common.py | 213 +++++++++++++++++++++++++++++++++++++++++++++ scripts/deploy.py | 144 ++++++++++++++++++++++++++++++ scripts/manager.py | 129 +++++++++++++++++++++++++++ 3 files changed, 486 insertions(+) create mode 100644 scripts/common.py create mode 100755 scripts/deploy.py create mode 100755 scripts/manager.py diff --git a/scripts/common.py b/scripts/common.py new file mode 100644 index 0000000..26b3d1b --- /dev/null +++ b/scripts/common.py @@ -0,0 +1,213 @@ +#!/usr/bin/env python3 + +import subprocess +import json +import requests + +############################# +# Core Deployment Variables # +############################# + +RPC_URL = "https://rpc.uno.sentry.testnet.v3.kiivalidator.com" +LCD_NODE = "https://lcd.uno.sentry.testnet.v3.kiivalidator.com" +CHAIN_ID = "oro_1336-1" +TXFLAG = [ + "--gas", + "auto", + "--gas-adjustment", + "1.2", + "--gas-prices", + "500000000000akii", + "--keyring-backend", + "test", + "--node", + RPC_URL, + "--chain-id", + CHAIN_ID, + "-y", + "-o", + "json", +] + +################## +# Util functions # +################## + +# Define all the helper functions +def run_cmd(cmd): + result = subprocess.run(cmd, capture_output=True, text=True) + return result.stdout, result.stderr + +# Get the key address from the key name +def get_key_address(key_name): + # Build the command to get the key address + cmd = ["kiichaind", "keys", "show", key_name, "--output", "json"] + + # Run the command + result, err = run_cmd(cmd) + + # If there is an error, we raise + if err: + raise Exception(f"Failed to get key address: {err}") + + # Parse the result and return the address + key_info = json.loads(result) + return key_info["address"] + +# Check check a tx until a result is returned +def check_tx_until_result(tx_hash): + while True: + # We can check the TX using the APIs + res = requests.get(f"{LCD_NODE}/cosmos/tx/v1beta1/txs/{tx_hash}") + + # If the response is 404, this means the TX is not yet processed + if res.status_code == 404: + continue + + # If the response is different than 200, we raise + if res.status_code != 200: + raise Exception(f"Failed to check tx {tx_hash}: {res.text}") + + # Reaching this point means the TX is processed + result = res.json() + code = result["tx_response"]["code"] + + # If the code isn't 0 we raise + if code != 0: + raise Exception(f"Transaction {tx_hash} failed with code {code}: {result}") + + # Reaching this point we can return the result + return result + +# Store contract stores a contract and return the TX hash +def store_contract(path, from_key): + # Build the cmd for deploying the contract + cmd = ["kiichaind", "tx", "wasm", "store", path, "--from", from_key] + TXFLAG + + # Run the command to store the contract + result, err = run_cmd(cmd) + if len(err.splitlines()) > 1 or "gas estimate" not in err.splitlines()[0]: + raise Exception(f"Failed to store contract: {err}") + + # Get the result and the code + code = json.loads(result)["code"] + tx_hash = json.loads(result)["txhash"] + + # Check if the code was success + if code != 0: + raise Exception(f"Failed to store contract: {result}") + + # If all is fine we wait for the tx to be processed + result = check_tx_until_result(tx_hash) + + # From the result we can get the code id + code_id = next( + attr["value"] + for event in result["tx_response"]["events"] + for attr in event["attributes"] + if attr["key"] == "code_id" + ) + + # Return the code id + return code_id + +# Instantiate contract instantiates a contract with the given code ID and init message +def instantiate_contract(code_id, init_msg, label, from_key): + # Get the key address from the key name + key_address = get_key_address(from_key) + + # Build the cmd for instantiating the contract + cmd = [ + "kiichaind", + "tx", + "wasm", + "instantiate", + code_id, + json.dumps(init_msg), + "--label", + label, + "--admin", + key_address, + "--from", + from_key, + ] + TXFLAG + + # Run the command to instantiate the contract + result, err = run_cmd(cmd) + if len(err.splitlines()) > 1 or "gas estimate" not in err.splitlines()[0]: + raise Exception(f"Failed to instantiate contract: {err}") + + # Get the result and the code + code = json.loads(result)["code"] + tx_hash = json.loads(result)["txhash"] + + # Check if the code was success + if code != 0: + raise Exception(f"Failed to instantiate contract: {result}") + + # If all is fine we wait for the tx to be processed + result = check_tx_until_result(tx_hash) + + # Get the contract address from the result + contract_address = next( + attr["value"] + for event in result["tx_response"]["events"] + for attr in event["attributes"] + if attr["key"] == "_contract_address" + ) + + # Return the contract address + return contract_address + +# Execute contract executes a contract with the given message +def execute_contract(contract_address, msg, from_key): + # Build the command to execute the contract + cmd = [ + "kiichaind", + "tx", + "wasm", + "execute", + contract_address, + json.dumps(msg), + "--from", + from_key, + ] + TXFLAG + + # Run the command + result, err = run_cmd(cmd) + if len(err.splitlines()) > 1 or "gas estimate" not in err.splitlines()[0]: + raise Exception(f"Failed to execute contract: {err}") + + # Get the result and the code + code = json.loads(result)["code"] + tx_hash = json.loads(result)["txhash"] + + # Check if the code was success + if code != 0: + raise Exception(f"Failed to execute contract: {result}") + + # If all is fine we wait for the tx to be processed + return check_tx_until_result(tx_hash) + +# Query contract queries a contract with the given query message +def query_contract(contract_address, query_msg): + # Build the command to query the contract + cmd = [ + "kiichaind", + "query", + "wasm", + "contract-state", + "smart", + contract_address, + json.dumps(query_msg), + "--node", RPC_URL, + "-o", "json" + ] + + # Run the command + result, err = run_cmd(cmd) + if err: + raise Exception(f"Failed to query contract: {err}") + + # Parse and return the result + return json.loads(result) diff --git a/scripts/deploy.py b/scripts/deploy.py new file mode 100755 index 0000000..ec76eab --- /dev/null +++ b/scripts/deploy.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python3 + +from common import instantiate_contract, store_contract + +############################# +# Core Deployment Variables # +############################# + +# Define the main variables for the deployment +KEY_NAME = "rwa" +KEY_ADDRESS = "kii1n5j3xtefjkhrq3x3kgtl8kf0tglpfh8nax7rqj" + +############################ +# Contract Deployment Zone # +############################ + +# Owner Roles Contract +print("Deploying Owner Roles contract...") +owner_roles = store_contract("artifacts/owner_roles.wasm", KEY_NAME) +print(f"Owner roles stored with code ID {owner_roles}") +owner_roles_init_msg = { + "owner": KEY_ADDRESS, +} +owner_roles_address = instantiate_contract( + owner_roles, owner_roles_init_msg, "OwnerRoles", KEY_NAME +) +print(f"Owner roles contract instantiated at {owner_roles_address}") + +# Agent roles +print("Deploying Agent Roles contract...") +agent_roles = store_contract("artifacts/agent_roles.wasm", KEY_NAME) +print(f"Agent roles stored with code ID {agent_roles}") +agent_roles_init_msg = { + "owner": KEY_ADDRESS, +} +agent_roles_address = instantiate_contract( + agent_roles, agent_roles_init_msg, "AgentRoles", KEY_NAME +) +print(f"Agent roles contract instantiated at {agent_roles_address}") + +# trustedIssuers +print("Deploying Trusted Issuers contract...") +trusted_issuers = store_contract("artifacts/trusted_issuers.wasm", KEY_NAME) +print(f"Trusted issuers stored with code ID {trusted_issuers}") +trusted_issuers_init_msg = { + "owner_roles_address": owner_roles_address, +} +trusted_issuers_address = instantiate_contract( + trusted_issuers, trusted_issuers_init_msg, "TrustedIssuers", KEY_NAME +) +print(f"Trusted issuers contract instantiated at {trusted_issuers_address}") + +# Claim Topics +print("Deploying Claim Topics contract...") +claim_topics = store_contract("artifacts/claim_topics.wasm", KEY_NAME) +print(f"Claim topics stored with code ID {claim_topics}") +claim_topics_init_msg = { + "owner_roles_address": owner_roles_address, +} +claim_topics_address = instantiate_contract( + claim_topics, claim_topics_init_msg, "ClaimTopics", KEY_NAME +) +print(f"Claim topics contract instantiated at {claim_topics_address}") + +# On Chain ID +print("Deploying On Chain ID contract...") +on_chain_id = store_contract("artifacts/on_chain_id.wasm", KEY_NAME) +print(f"On Chain ID stored with code ID {on_chain_id}") +on_chain_id_init_msg = { + "trusted_issuer_addr": trusted_issuers_address, + "owner": KEY_ADDRESS, +} +on_chain_id_address = instantiate_contract( + on_chain_id, on_chain_id_init_msg, "OnChainID", KEY_NAME +) +print(f"On Chain ID contract instantiated at {on_chain_id_address}") + +# Compliance registry +print("Deploying Compliance Registry contract...") +compliance_registry = store_contract("artifacts/compliance_registry.wasm", KEY_NAME) +print(f"Compliance Registry stored with code ID {compliance_registry}") +compliance_registry_init_msg = { + "owner_roles_address": owner_roles_address, +} +compliance_registry_address = instantiate_contract( + compliance_registry, compliance_registry_init_msg, "ComplianceRegistry", KEY_NAME +) +print(f"Compliance Registry contract instantiated at {compliance_registry_address}") + +# Compliance claims +print("Deploying Compliance Claims contract...") +compliance_claims = store_contract("artifacts/compliance_claims.wasm", KEY_NAME) +print(f"Compliance Claims stored with code ID {compliance_claims}") +compliance_claims_init_msg = { + "identity_address": on_chain_id_address, + "owner_roles_address": owner_roles_address, + "claim_topics_address": claim_topics_address, +} +compliance_claims_address = instantiate_contract( + compliance_claims, compliance_claims_init_msg, "ComplianceClaims", KEY_NAME +) +print(f"Compliance Claims contract instantiated at {compliance_claims_address}") + +# Compliance country restriction +print("Deploying Compliance Country Restriction contract...") +compliance_country_restriction = store_contract( + "artifacts/compliance_country_restriction.wasm", KEY_NAME +) +print( + f"Compliance Country Restriction stored with code ID {compliance_country_restriction}" +) +compliance_country_restriction_init_msg = { + "identity_address": on_chain_id_address, + "owner_roles_address": owner_roles_address, +} +compliance_country_restriction_address = instantiate_contract( + compliance_country_restriction, + compliance_country_restriction_init_msg, + "ComplianceCountryRestriction", + KEY_NAME, +) +print( + f"Compliance Country Restriction contract instantiated at {compliance_country_restriction_address}" +) + +# CW20 base for test token +print("Deploying CW20 Base contract...") +cw20_base = store_contract("artifacts/cw20_base.wasm", KEY_NAME) +print(f"CW20 Base stored with code ID {cw20_base}") +cw20_base_init_msg = { + "token_info": { + "name": "Test Token", + "symbol": "TEST", + "decimals": 6, + "initial_balances": [{"address": KEY_ADDRESS, "amount": "1000000"}], + }, + "registries": { + "compliance_address": compliance_registry_address, + }, +} +cw20_base_address = instantiate_contract( + cw20_base, cw20_base_init_msg, "CW20Base", KEY_NAME +) +print(f"CW20 Base contract instantiated at {cw20_base_address}") diff --git a/scripts/manager.py b/scripts/manager.py new file mode 100755 index 0000000..dd01e09 --- /dev/null +++ b/scripts/manager.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python3 + +import json + +from common import execute_contract, get_key_address, query_contract + +############################# +# Core Deployment Variables # +############################# + +TRUSTED_ISSUE_KEY_NAME = "trusted_issuer" +TRUSTED_ISSUE_KEY_ADDRESS = get_key_address(TRUSTED_ISSUE_KEY_NAME) +OWNER_KEY_NAME = "rwa" +OWNER_KEY_ADDRESS = get_key_address(OWNER_KEY_NAME) +CONTRACTS = { + "owner_roles_address": "kii1hez8n9mnljtca28xrg4a54p4cdharlsg0vku0008ng64ldgfapdqsqctag", + "agent_roles_address": "kii14tzdqsgsdfcgxy0zu2vqcaj347lv5xpelpadusrspnkxddc53fhq9vc0h8", + "trusted_issuers_address": "kii17xsl3q2p3elhh747r3ttn08j2p95jsd3c7rfmuc5rws5a20u9kqqrrx0nd", + "claim_topics_address": "kii17k6uthn6ymd3ta25glx6qpa2sfz2hkt5a22lynzdm7xgk7kklckqc4gytm", + "on_chain_id_address": "kii1y8s38zn7ry7xc95ej2z08eq520y4v9qdsysfgkfwelhh635e4t2qq8xxp5", + "compliance_registry_address": "kii1dpws8lmu2l4awdau6zhezxm97tshu427aulr2xwp7uarpd8jqu7srjz2gm", + "compliance_claims_address": "kii12n3frtnx8mh2vpvzk7mqkr6yqkfe77339c7uakzqqa4pv46zdr9qt020h3", + "compliance_country_restriction_address": "kii1k4j6gr75k23tvqjdw9zvtrdxh7pxtexy5pdk7xjmwf385msx75fsz470kz", + "cw20_base_address": "kii1zvjy36ysunq56dhgyxggp5gwrymxjs95twj5lgcpqujftppn739s83aym4", +} + +######## +# Call # +######## + +# Setup the owner roles +has_issuers_registry_manager = query_contract( + CONTRACTS["owner_roles_address"], + {"is_owner": {"owner": OWNER_KEY_ADDRESS, "role": "issuers_registry_manager"}}, +) +if not has_issuers_registry_manager["data"]["is_owner"]: + print( + f"Key {OWNER_KEY_NAME} is not an issuers registry manager. Creating a new role..." + ) + + # Create a new role for the owner + res = execute_contract( + CONTRACTS["owner_roles_address"], + { + "add_owner_role": { + "owner": OWNER_KEY_ADDRESS, + "role": "issuers_registry_manager", + } + }, + OWNER_KEY_NAME, + ) + + print("Role created") +print(f"Key {OWNER_KEY_NAME} is an issuers registry manager.") + +# Setup the claim registry manager +has_claim_registry_manager = query_contract( + CONTRACTS["owner_roles_address"], + {"is_owner": {"owner": OWNER_KEY_ADDRESS, "role": "claim_registry_manager"}}, +) +if not has_claim_registry_manager["data"]["is_owner"]: + print(f"Key {OWNER_KEY_NAME} is not a claim registry manager. Creating a new role...") + + # Create a new role for the owner + res = execute_contract( + CONTRACTS["owner_roles_address"], + { + "add_owner_role": { + "owner": OWNER_KEY_ADDRESS, + "role": "claim_registry_manager", + } + }, + OWNER_KEY_NAME, + ) + + print("Role created") +print(f"Key {OWNER_KEY_NAME} is a claim registry manager.") + +# Check if a address is a trusted issuer +is_trusted_issuer = query_contract( + CONTRACTS["trusted_issuers_address"], + {"is_trusted_issuer": {"issuer": TRUSTED_ISSUE_KEY_ADDRESS}}, +) +if not is_trusted_issuer["data"]: + print( + f"Key {TRUSTED_ISSUE_KEY_NAME} is not a trusted issuer. Creating a new trusted issuer..." + ) + + # Create a new trusted issuer + res = execute_contract( + CONTRACTS["trusted_issuers_address"], + { + "add_trusted_issuer": { + "issuer": TRUSTED_ISSUE_KEY_ADDRESS, + "claim_topics": ["1"], + } + }, + OWNER_KEY_NAME, + ) + + print("Trusted issuer created") +print (f"Key {TRUSTED_ISSUE_KEY_NAME} is a trusted issuer.") + +# Add a claim topic to the CW20 token +claim_topics_for_token = query_contract( + CONTRACTS["claim_topics_address"], + {"get_claims_for_token": {"token_addr": CONTRACTS["cw20_base_address"] + }}, +) +if len(claim_topics_for_token["data"]) == 0: + print( + f"Token {CONTRACTS['cw20_base_address']} has no claim topics. Adding a claim topic..." + ) + + # Add a claim topic to the token + res = execute_contract( + CONTRACTS["claim_topics_address"], + { + "add_claim_topic_for_token": { + "token_addr": CONTRACTS["cw20_base_address"], + "topic": "1", + } + }, + OWNER_KEY_NAME, + ) + + print("Claim topic added") +print(f"Token {CONTRACTS['cw20_base_address']} has claim topics") + From f73c3bb28cd82dd462fb283338cbad86e40fbca0 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Tue, 1 Jul 2025 11:23:04 -0300 Subject: [PATCH 09/41] refactor: move configs to a config file --- scripts/config.py | 21 +++++++++++++++++++++ scripts/deploy.py | 6 ++++-- scripts/manager.py | 26 ++++++++------------------ 3 files changed, 33 insertions(+), 20 deletions(-) create mode 100644 scripts/config.py diff --git a/scripts/config.py b/scripts/config.py new file mode 100644 index 0000000..2557639 --- /dev/null +++ b/scripts/config.py @@ -0,0 +1,21 @@ +from common import get_key_address + +############################# +# Core Deployment Variables # +############################# + +TRUSTED_ISSUE_KEY_NAME = "trusted_issuer" +TRUSTED_ISSUE_KEY_ADDRESS = get_key_address(TRUSTED_ISSUE_KEY_NAME) +OWNER_KEY_NAME = "rwa" +OWNER_KEY_ADDRESS = get_key_address(OWNER_KEY_NAME) +CONTRACTS = { + "owner_roles_address": "kii1hez8n9mnljtca28xrg4a54p4cdharlsg0vku0008ng64ldgfapdqsqctag", + "agent_roles_address": "kii14tzdqsgsdfcgxy0zu2vqcaj347lv5xpelpadusrspnkxddc53fhq9vc0h8", + "trusted_issuers_address": "kii17xsl3q2p3elhh747r3ttn08j2p95jsd3c7rfmuc5rws5a20u9kqqrrx0nd", + "claim_topics_address": "kii17k6uthn6ymd3ta25glx6qpa2sfz2hkt5a22lynzdm7xgk7kklckqc4gytm", + "on_chain_id_address": "kii1y8s38zn7ry7xc95ej2z08eq520y4v9qdsysfgkfwelhh635e4t2qq8xxp5", + "compliance_registry_address": "kii1dpws8lmu2l4awdau6zhezxm97tshu427aulr2xwp7uarpd8jqu7srjz2gm", + "compliance_claims_address": "kii12n3frtnx8mh2vpvzk7mqkr6yqkfe77339c7uakzqqa4pv46zdr9qt020h3", + "compliance_country_restriction_address": "kii1k4j6gr75k23tvqjdw9zvtrdxh7pxtexy5pdk7xjmwf385msx75fsz470kz", + "cw20_base_address": "kii1zvjy36ysunq56dhgyxggp5gwrymxjs95twj5lgcpqujftppn739s83aym4", +} diff --git a/scripts/deploy.py b/scripts/deploy.py index ec76eab..265191e 100755 --- a/scripts/deploy.py +++ b/scripts/deploy.py @@ -2,13 +2,15 @@ from common import instantiate_contract, store_contract +import config + ############################# # Core Deployment Variables # ############################# # Define the main variables for the deployment -KEY_NAME = "rwa" -KEY_ADDRESS = "kii1n5j3xtefjkhrq3x3kgtl8kf0tglpfh8nax7rqj" +KEY_NAME = config.OWNER_KEY_NAME +KEY_ADDRESS = config.OWNER_KEY_ADDRESS ############################ # Contract Deployment Zone # diff --git a/scripts/manager.py b/scripts/manager.py index dd01e09..d3cf56d 100755 --- a/scripts/manager.py +++ b/scripts/manager.py @@ -1,28 +1,18 @@ #!/usr/bin/env python3 import json - -from common import execute_contract, get_key_address, query_contract +import config +from common import execute_contract, query_contract ############################# -# Core Deployment Variables # +# Import Core Variables # ############################# -TRUSTED_ISSUE_KEY_NAME = "trusted_issuer" -TRUSTED_ISSUE_KEY_ADDRESS = get_key_address(TRUSTED_ISSUE_KEY_NAME) -OWNER_KEY_NAME = "rwa" -OWNER_KEY_ADDRESS = get_key_address(OWNER_KEY_NAME) -CONTRACTS = { - "owner_roles_address": "kii1hez8n9mnljtca28xrg4a54p4cdharlsg0vku0008ng64ldgfapdqsqctag", - "agent_roles_address": "kii14tzdqsgsdfcgxy0zu2vqcaj347lv5xpelpadusrspnkxddc53fhq9vc0h8", - "trusted_issuers_address": "kii17xsl3q2p3elhh747r3ttn08j2p95jsd3c7rfmuc5rws5a20u9kqqrrx0nd", - "claim_topics_address": "kii17k6uthn6ymd3ta25glx6qpa2sfz2hkt5a22lynzdm7xgk7kklckqc4gytm", - "on_chain_id_address": "kii1y8s38zn7ry7xc95ej2z08eq520y4v9qdsysfgkfwelhh635e4t2qq8xxp5", - "compliance_registry_address": "kii1dpws8lmu2l4awdau6zhezxm97tshu427aulr2xwp7uarpd8jqu7srjz2gm", - "compliance_claims_address": "kii12n3frtnx8mh2vpvzk7mqkr6yqkfe77339c7uakzqqa4pv46zdr9qt020h3", - "compliance_country_restriction_address": "kii1k4j6gr75k23tvqjdw9zvtrdxh7pxtexy5pdk7xjmwf385msx75fsz470kz", - "cw20_base_address": "kii1zvjy36ysunq56dhgyxggp5gwrymxjs95twj5lgcpqujftppn739s83aym4", -} +TRUSTED_ISSUE_KEY_NAME = config.TRUSTED_ISSUE_KEY_NAME +TRUSTED_ISSUE_KEY_ADDRESS = config.TRUSTED_ISSUE_KEY_ADDRESS +OWNER_KEY_NAME = config.OWNER_KEY_NAME +OWNER_KEY_ADDRESS = config.OWNER_KEY_ADDRESS +CONTRACTS = config.CONTRACTS ######## # Call # From 721e6cf23c93551defee0cf60c255ee5a0fae968 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Tue, 1 Jul 2025 12:01:15 -0300 Subject: [PATCH 10/41] docs: add else to not duplicate messages --- scripts/{manager.py => manager_setup.py} | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) rename scripts/{manager.py => manager_setup.py} (91%) diff --git a/scripts/manager.py b/scripts/manager_setup.py similarity index 91% rename from scripts/manager.py rename to scripts/manager_setup.py index d3cf56d..40f6150 100755 --- a/scripts/manager.py +++ b/scripts/manager_setup.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 -import json import config from common import execute_contract, query_contract @@ -41,7 +40,8 @@ ) print("Role created") -print(f"Key {OWNER_KEY_NAME} is an issuers registry manager.") +else: + print(f"Key {OWNER_KEY_NAME} is an issuers registry manager.") # Setup the claim registry manager has_claim_registry_manager = query_contract( @@ -64,7 +64,8 @@ ) print("Role created") -print(f"Key {OWNER_KEY_NAME} is a claim registry manager.") +else: + print(f"Key {OWNER_KEY_NAME} is a claim registry manager.") # Check if a address is a trusted issuer is_trusted_issuer = query_contract( @@ -89,7 +90,8 @@ ) print("Trusted issuer created") -print (f"Key {TRUSTED_ISSUE_KEY_NAME} is a trusted issuer.") +else: + print (f"Key {TRUSTED_ISSUE_KEY_NAME} is a trusted issuer.") # Add a claim topic to the CW20 token claim_topics_for_token = query_contract( @@ -115,5 +117,6 @@ ) print("Claim topic added") -print(f"Token {CONTRACTS['cw20_base_address']} has claim topics") +else: + print(f"Token {CONTRACTS['cw20_base_address']} has claim topics") From eb2c84ab320e7b0971c17b6fb43f1eab38302300 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Tue, 1 Jul 2025 12:01:28 -0300 Subject: [PATCH 11/41] docs: improve legibility --- scripts/config.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/scripts/config.py b/scripts/config.py index 2557639..7629aa0 100644 --- a/scripts/config.py +++ b/scripts/config.py @@ -4,8 +4,7 @@ # Core Deployment Variables # ############################# -TRUSTED_ISSUE_KEY_NAME = "trusted_issuer" -TRUSTED_ISSUE_KEY_ADDRESS = get_key_address(TRUSTED_ISSUE_KEY_NAME) +# Deployment OWNER_KEY_NAME = "rwa" OWNER_KEY_ADDRESS = get_key_address(OWNER_KEY_NAME) CONTRACTS = { @@ -19,3 +18,11 @@ "compliance_country_restriction_address": "kii1k4j6gr75k23tvqjdw9zvtrdxh7pxtexy5pdk7xjmwf385msx75fsz470kz", "cw20_base_address": "kii1zvjy36ysunq56dhgyxggp5gwrymxjs95twj5lgcpqujftppn739s83aym4", } + +# Management +TRUSTED_ISSUE_KEY_NAME = "trusted_issuer" +TRUSTED_ISSUE_KEY_ADDRESS = get_key_address(TRUSTED_ISSUE_KEY_NAME) + +# User Variables +USER_KEY_NAME = "user1" # Can be any address but needs some funds for fees +USER_KEY_ADDRESS = get_key_address(USER_KEY_NAME) From 7e7bde40547871900e0474c6aba3ab974be620ff Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Tue, 1 Jul 2025 12:05:47 -0300 Subject: [PATCH 12/41] feat: add initial user setup --- scripts/user_setup.py | 115 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 scripts/user_setup.py diff --git a/scripts/user_setup.py b/scripts/user_setup.py new file mode 100644 index 0000000..df09038 --- /dev/null +++ b/scripts/user_setup.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 + +import config +from common import execute_contract, query_contract + +############################# +# Import Core Variables # +############################# + +TRUSTED_ISSUE_KEY_NAME = config.TRUSTED_ISSUE_KEY_NAME +TRUSTED_ISSUE_KEY_ADDRESS = config.TRUSTED_ISSUE_KEY_ADDRESS +OWNER_KEY_NAME = config.OWNER_KEY_NAME +OWNER_KEY_ADDRESS = config.OWNER_KEY_ADDRESS +CONTRACTS = config.CONTRACTS +USER_KEY_NAME = config.USER_KEY_NAME +USER_KEY_ADDRESS = config.USER_KEY_ADDRESS + +######## +# Call # +######## + +# 1. User creates own identity +# Checks if identity exists +has_identity = query_contract( + CONTRACTS["on_chain_id_address"], + {"get_identity": {"identity_owner": USER_KEY_ADDRESS}}, +) +if not has_identity["data"]: + print( + f"Key {USER_KEY_NAME} has no identity. Creating a new Brazilian identity..." + ) + + # Create a new identity for the owner + res = execute_contract( + CONTRACTS["on_chain_id_address"], + { + "add_identity": { + "country": "BR" + } + }, + USER_KEY_NAME, + ) + + print("Identity created") +else: + print(f"Key {USER_KEY_NAME} has an identity.") + +# 2. User gives permission for trusted issuer to add claims +# Check if permission already exists +issuer_has_claim_signer = query_contract( + CONTRACTS["on_chain_id_address"], + {"get_key": { + "key_owner": TRUSTED_ISSUE_KEY_ADDRESS, + "key_type" : "ClaimSignerKey", + "identity_owner": USER_KEY_ADDRESS + }}, +) +if not issuer_has_claim_signer["data"]: + print( + f"Key {TRUSTED_ISSUE_KEY_NAME} has no permission to add claims to key {USER_KEY_NAME}. Adding permission..." + ) + + # Give permission to trusted issuer to add claims + res = execute_contract( + CONTRACTS["on_chain_id_address"], + { + "add_key": { + "key_owner": TRUSTED_ISSUE_KEY_ADDRESS, + "key_type" : "ClaimSignerKey", + "identity_owner": USER_KEY_ADDRESS + } + }, + USER_KEY_ADDRESS, + ) + + print("Permission created") +else: + print(f"Key {TRUSTED_ISSUE_KEY_NAME} has permission to add claims to key {USER_KEY_NAME}.") + + +# 3. Trusted issuer creates a claim for the user +# Check if claim already exists +has_claim = query_contract( + CONTRACTS["on_chain_id_address"], + {"verify_claim": { + "claim_id": "1", + "identity_owner": USER_KEY_ADDRESS + }}, +) +if not has_claim["data"]: + print( + f"Key {USER_KEY_NAME} has no claims to topic 1. Adding claim..." + ) + + # Give permission to trusted issuer to add claims + res = execute_contract( + CONTRACTS["on_chain_id_address"], + { + "add_claim": { + "claim" : { + "token_addr": CONTRACTS["cw20_base_address"], + "topic": "1" + }, + "identity_owner": USER_KEY_ADDRESS + } + }, + TRUSTED_ISSUE_KEY_NAME, + ) + + print("Claim created") +else: + print(f"Key {USER_KEY_NAME} has claim to topic 1.") + + + From f1c66d326428b660351fd2916bcd495e42d0412d Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Tue, 1 Jul 2025 12:06:24 -0300 Subject: [PATCH 13/41] docs: add more docs to review --- contracts-review.md | 85 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/contracts-review.md b/contracts-review.md index 105ee09..ca63813 100644 --- a/contracts-review.md +++ b/contracts-review.md @@ -107,7 +107,6 @@ pub enum KeyType { - Holds token address <> topic association - Claims are a topic + active bool - ClaimRegistryManager can change topics -- Something feels off, I didn't find out how to link a claim with an user - `I didn't quite get this` # CW20 base @@ -134,3 +133,87 @@ Full info: https://github.com/CosmWasm/cw-plus/blob/main/packages/cw20/README.md - Missing Executes of CW20 - Just checked instantiate - Usage test + + +# Flows +A tool centralizes txs of the tokens + +We need to understand how to do these flows +## Deploy +Owner can issue a pool +- Pool is a trusted issuers + - Can receive sells and send out buys +## Register KYC +## Buy +- Users enters a website +- They got through a KYC +- To buy they will go through a DEX + - A liquidity pool that is trusted + - Both the pool and the buyer will need to have a topic level that matches one for the tokens +## Sell +- Goes from pool to buyer + +## Questions +- how do we buy/sell assets into this? + + +# Enzo's experiences +## Economic rights +- Can hold the token but other person can claim things for you + +## Swap place +- Listing and minting +- Swap: Takes best price and match +- Listing: lists sell/buys + +## Proof of reserve +- Users can ask for proof of reserves +- Contracts have restrictions + - Tokens needs to have a link to the rights +- We, as holders, need to have custody and proof of it + - Else we can be sued + +## Living on the chain +- If you move to another chain, it will lose it's value + - No longer tied in the contracts +- If they hold a copy, then it will just be mirrored + - Would need to be copied + +## Money Laundry +- Compliance should have a way to deal with possible MLs + +## What does plume do? +- https://plume.org/ + + +# Current Direction +1. Deploy RWA contracts +2. Deploy astroport environment (DEX contracts) +3. Deploy preparations for RWA +4. Create trusted issuer + - Backend connects issuers to claims +5. Create a pool between the RWA token and MockUSDT + - Need to check if Mock usdt (it is an ERC20 and needs to be native to the chain) + - Pool needs to be a trusted issuer +6. Users should be able to claim + - Go to RWA page + - Can see tokens but not buy + - Sign up KYC to become trusted + - Can do swaps + +Notes +- We will need to interact via cosmwasm precompile +- [Astroport](https://astroport.fi/) has a lot of flexibility on tokens used +- This is a direction, we need to make a proper plan in the future + +## Problems +- Only Cosmos addresses are valid via compliance? + - How can we add EVM users to that? Will EVM work out the bat + - Wrap the cosm? + - If this gets out of these contracts, it loses compliance +- Swap can only be done between two trusted issuers + - Do we want swap? +- New assets are done by hand +- CW20 contract is not enforcing roles correctly +- No mention to yield, distribution and so on +- Are allowances ok? From 97b0e4160a2d6755290b2e3b312ffa2ec6889c7a Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Tue, 1 Jul 2025 21:04:14 -0300 Subject: [PATCH 14/41] chore: move kiichaind to a variable --- scripts/common.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/scripts/common.py b/scripts/common.py index 26b3d1b..02a17a4 100644 --- a/scripts/common.py +++ b/scripts/common.py @@ -8,6 +8,7 @@ # Core Deployment Variables # ############################# +KIICHAIN = "kiichaind" RPC_URL = "https://rpc.uno.sentry.testnet.v3.kiivalidator.com" LCD_NODE = "https://lcd.uno.sentry.testnet.v3.kiivalidator.com" CHAIN_ID = "oro_1336-1" @@ -41,7 +42,7 @@ def run_cmd(cmd): # Get the key address from the key name def get_key_address(key_name): # Build the command to get the key address - cmd = ["kiichaind", "keys", "show", key_name, "--output", "json"] + cmd = [KIICHAIN, "keys", "show", key_name, "--output", "json"] # Run the command result, err = run_cmd(cmd) @@ -82,7 +83,7 @@ def check_tx_until_result(tx_hash): # Store contract stores a contract and return the TX hash def store_contract(path, from_key): # Build the cmd for deploying the contract - cmd = ["kiichaind", "tx", "wasm", "store", path, "--from", from_key] + TXFLAG + cmd = [KIICHAIN, "tx", "wasm", "store", path, "--from", from_key] + TXFLAG # Run the command to store the contract result, err = run_cmd(cmd) @@ -118,7 +119,7 @@ def instantiate_contract(code_id, init_msg, label, from_key): # Build the cmd for instantiating the contract cmd = [ - "kiichaind", + KIICHAIN, "tx", "wasm", "instantiate", @@ -163,7 +164,7 @@ def instantiate_contract(code_id, init_msg, label, from_key): def execute_contract(contract_address, msg, from_key): # Build the command to execute the contract cmd = [ - "kiichaind", + KIICHAIN, "tx", "wasm", "execute", @@ -193,7 +194,7 @@ def execute_contract(contract_address, msg, from_key): def query_contract(contract_address, query_msg): # Build the command to query the contract cmd = [ - "kiichaind", + KIICHAIN, "query", "wasm", "contract-state", From 5382928593a33217288dd52d39aba35b2360c6c1 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Tue, 1 Jul 2025 21:04:28 -0300 Subject: [PATCH 15/41] fix: correct typo --- scripts/manager_setup.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/manager_setup.py b/scripts/manager_setup.py index 40f6150..7771958 100755 --- a/scripts/manager_setup.py +++ b/scripts/manager_setup.py @@ -7,8 +7,8 @@ # Import Core Variables # ############################# -TRUSTED_ISSUE_KEY_NAME = config.TRUSTED_ISSUE_KEY_NAME -TRUSTED_ISSUE_KEY_ADDRESS = config.TRUSTED_ISSUE_KEY_ADDRESS +TRUSTED_ISSUER_KEY_NAME = config.TRUSTED_ISSUER_KEY_NAME +TRUSTED_ISSUER_KEY_ADDRESS = config.TRUSTED_ISSUER_KEY_ADDRESS OWNER_KEY_NAME = config.OWNER_KEY_NAME OWNER_KEY_ADDRESS = config.OWNER_KEY_ADDRESS CONTRACTS = config.CONTRACTS @@ -70,11 +70,11 @@ # Check if a address is a trusted issuer is_trusted_issuer = query_contract( CONTRACTS["trusted_issuers_address"], - {"is_trusted_issuer": {"issuer": TRUSTED_ISSUE_KEY_ADDRESS}}, + {"is_trusted_issuer": {"issuer": TRUSTED_ISSUER_KEY_ADDRESS}}, ) if not is_trusted_issuer["data"]: print( - f"Key {TRUSTED_ISSUE_KEY_NAME} is not a trusted issuer. Creating a new trusted issuer..." + f"Key {TRUSTED_ISSUER_KEY_NAME} is not a trusted issuer. Creating a new trusted issuer..." ) # Create a new trusted issuer @@ -82,7 +82,7 @@ CONTRACTS["trusted_issuers_address"], { "add_trusted_issuer": { - "issuer": TRUSTED_ISSUE_KEY_ADDRESS, + "issuer": TRUSTED_ISSUER_KEY_ADDRESS, "claim_topics": ["1"], } }, @@ -91,7 +91,7 @@ print("Trusted issuer created") else: - print (f"Key {TRUSTED_ISSUE_KEY_NAME} is a trusted issuer.") + print (f"Key {TRUSTED_ISSUER_KEY_NAME} is a trusted issuer.") # Add a claim topic to the CW20 token claim_topics_for_token = query_contract( From 5c50407412e6195c99974708d95ebacc2e6d2fc1 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Tue, 1 Jul 2025 21:05:04 -0300 Subject: [PATCH 16/41] fix: correct typo --- scripts/config.py | 8 ++------ scripts/user_setup.py | 6 ++---- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/scripts/config.py b/scripts/config.py index 7629aa0..ec36d5a 100644 --- a/scripts/config.py +++ b/scripts/config.py @@ -20,9 +20,5 @@ } # Management -TRUSTED_ISSUE_KEY_NAME = "trusted_issuer" -TRUSTED_ISSUE_KEY_ADDRESS = get_key_address(TRUSTED_ISSUE_KEY_NAME) - -# User Variables -USER_KEY_NAME = "user1" # Can be any address but needs some funds for fees -USER_KEY_ADDRESS = get_key_address(USER_KEY_NAME) +TRUSTED_ISSUER_KEY_NAME = "trusted_issuer" +TRUSTED_ISSUER_KEY_ADDRESS = get_key_address(TRUSTED_ISSUER_KEY_NAME) diff --git a/scripts/user_setup.py b/scripts/user_setup.py index df09038..6527c59 100644 --- a/scripts/user_setup.py +++ b/scripts/user_setup.py @@ -7,10 +7,8 @@ # Import Core Variables # ############################# -TRUSTED_ISSUE_KEY_NAME = config.TRUSTED_ISSUE_KEY_NAME -TRUSTED_ISSUE_KEY_ADDRESS = config.TRUSTED_ISSUE_KEY_ADDRESS -OWNER_KEY_NAME = config.OWNER_KEY_NAME -OWNER_KEY_ADDRESS = config.OWNER_KEY_ADDRESS +TRUSTED_ISSUER_KEY_NAME = config.TRUSTED_ISSUER_KEY_NAME +TRUSTED_ISSUER_KEY_ADDRESS = config.TRUSTED_ISSUER_KEY_ADDRESS CONTRACTS = config.CONTRACTS USER_KEY_NAME = config.USER_KEY_NAME USER_KEY_ADDRESS = config.USER_KEY_ADDRESS From 82c26dbbdca6d08070fbaec96103502e43c96ebb Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Tue, 1 Jul 2025 21:05:14 -0300 Subject: [PATCH 17/41] feat: add user setup --- scripts/user_setup.py | 203 ++++++++++++++++++++++-------------------- 1 file changed, 107 insertions(+), 96 deletions(-) diff --git a/scripts/user_setup.py b/scripts/user_setup.py index 6527c59..504170c 100644 --- a/scripts/user_setup.py +++ b/scripts/user_setup.py @@ -1,7 +1,8 @@ #!/usr/bin/env python3 import config -from common import execute_contract, query_contract +import sys +from common import execute_contract, get_key_address, query_contract ############################# # Import Core Variables # @@ -10,104 +11,114 @@ TRUSTED_ISSUER_KEY_NAME = config.TRUSTED_ISSUER_KEY_NAME TRUSTED_ISSUER_KEY_ADDRESS = config.TRUSTED_ISSUER_KEY_ADDRESS CONTRACTS = config.CONTRACTS -USER_KEY_NAME = config.USER_KEY_NAME -USER_KEY_ADDRESS = config.USER_KEY_ADDRESS + +############ +# Function # +############ + +def setup_user(user_name): + USER_KEY_NAME = user_name + USER_KEY_ADDRESS = get_key_address(USER_KEY_NAME) + + # 1. User creates own identity + # Checks if identity exists + has_identity = query_contract( + CONTRACTS["on_chain_id_address"], + {"get_identity": {"identity_owner": USER_KEY_ADDRESS}}, + ) + if not has_identity["data"]: + print( + f"Key {USER_KEY_NAME} has no identity. Creating a new Brazilian identity..." + ) + + # Create a new identity for the owner + res = execute_contract( + CONTRACTS["on_chain_id_address"], + { + "add_identity": { + "country": "BR" + } + }, + USER_KEY_NAME, + ) + + print("Identity created") + else: + print(f"Key {USER_KEY_NAME} has an identity.") + + # 2. User gives permission for trusted issuer to add claims + # Check if permission already exists + issuer_has_claim_signer = query_contract( + CONTRACTS["on_chain_id_address"], + {"get_key": { + "key_owner": TRUSTED_ISSUER_KEY_ADDRESS, + "key_type" : "ClaimSignerKey", + "identity_owner": USER_KEY_ADDRESS + }}, + ) + if not issuer_has_claim_signer["data"]: + print( + f"Key {TRUSTED_ISSUER_KEY_NAME} has no permission to add claims to key {USER_KEY_NAME}. Adding permission..." + ) + + # Give permission to trusted issuer to add claims + res = execute_contract( + CONTRACTS["on_chain_id_address"], + { + "add_key": { + "key_owner": TRUSTED_ISSUER_KEY_ADDRESS, + "key_type" : "ClaimSignerKey", + "identity_owner": USER_KEY_ADDRESS + } + }, + USER_KEY_ADDRESS, + ) + + print("Permission created") + else: + print(f"Key {TRUSTED_ISSUER_KEY_NAME} has permission to add claims to key {USER_KEY_NAME}.") + + + # 3. Trusted issuer creates a claim for the user + # Check if claim already exists + has_claim = query_contract( + CONTRACTS["on_chain_id_address"], + {"verify_claim": { + "claim_id": "1", + "identity_owner": USER_KEY_ADDRESS + }}, + ) + if not has_claim["data"]: + print( + f"Key {USER_KEY_NAME} has no claims to topic 1. Adding claim..." + ) + + # Give permission to trusted issuer to add claims + res = execute_contract( + CONTRACTS["on_chain_id_address"], + { + "add_claim": { + "claim" : { + "token_addr": CONTRACTS["cw20_base_address"], + "topic": "1" + }, + "identity_owner": USER_KEY_ADDRESS + } + }, + TRUSTED_ISSUER_KEY_NAME, + ) + + print("Claim created") + else: + print(f"Key {USER_KEY_NAME} has claim to topic 1.") ######## # Call # ######## -# 1. User creates own identity -# Checks if identity exists -has_identity = query_contract( - CONTRACTS["on_chain_id_address"], - {"get_identity": {"identity_owner": USER_KEY_ADDRESS}}, -) -if not has_identity["data"]: - print( - f"Key {USER_KEY_NAME} has no identity. Creating a new Brazilian identity..." - ) - - # Create a new identity for the owner - res = execute_contract( - CONTRACTS["on_chain_id_address"], - { - "add_identity": { - "country": "BR" - } - }, - USER_KEY_NAME, - ) - - print("Identity created") -else: - print(f"Key {USER_KEY_NAME} has an identity.") - -# 2. User gives permission for trusted issuer to add claims -# Check if permission already exists -issuer_has_claim_signer = query_contract( - CONTRACTS["on_chain_id_address"], - {"get_key": { - "key_owner": TRUSTED_ISSUE_KEY_ADDRESS, - "key_type" : "ClaimSignerKey", - "identity_owner": USER_KEY_ADDRESS - }}, -) -if not issuer_has_claim_signer["data"]: - print( - f"Key {TRUSTED_ISSUE_KEY_NAME} has no permission to add claims to key {USER_KEY_NAME}. Adding permission..." - ) - - # Give permission to trusted issuer to add claims - res = execute_contract( - CONTRACTS["on_chain_id_address"], - { - "add_key": { - "key_owner": TRUSTED_ISSUE_KEY_ADDRESS, - "key_type" : "ClaimSignerKey", - "identity_owner": USER_KEY_ADDRESS - } - }, - USER_KEY_ADDRESS, - ) - - print("Permission created") +if len(sys.argv) > 1: + name = sys.argv[1] + print(f"Setting up user key: {name}") + setup_user(name) else: - print(f"Key {TRUSTED_ISSUE_KEY_NAME} has permission to add claims to key {USER_KEY_NAME}.") - - -# 3. Trusted issuer creates a claim for the user -# Check if claim already exists -has_claim = query_contract( - CONTRACTS["on_chain_id_address"], - {"verify_claim": { - "claim_id": "1", - "identity_owner": USER_KEY_ADDRESS - }}, -) -if not has_claim["data"]: - print( - f"Key {USER_KEY_NAME} has no claims to topic 1. Adding claim..." - ) - - # Give permission to trusted issuer to add claims - res = execute_contract( - CONTRACTS["on_chain_id_address"], - { - "add_claim": { - "claim" : { - "token_addr": CONTRACTS["cw20_base_address"], - "topic": "1" - }, - "identity_owner": USER_KEY_ADDRESS - } - }, - TRUSTED_ISSUE_KEY_NAME, - ) - - print("Claim created") -else: - print(f"Key {USER_KEY_NAME} has claim to topic 1.") - - - + print("Please provide the user key name when calling the script") From 8d5c3cf14cc1283f648dc4784b4d6bb72c151185 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Tue, 1 Jul 2025 21:08:26 -0300 Subject: [PATCH 18/41] fix: make script chmod --- scripts/user_setup.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 scripts/user_setup.py diff --git a/scripts/user_setup.py b/scripts/user_setup.py old mode 100644 new mode 100755 From 44ab4303d8bcbadd4d59a81759e0398fa9de0143 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Tue, 1 Jul 2025 21:46:35 -0300 Subject: [PATCH 19/41] feat: full user setup flow --- scripts/user_setup.py | 155 +++++++++++++++++++++--------------------- 1 file changed, 77 insertions(+), 78 deletions(-) diff --git a/scripts/user_setup.py b/scripts/user_setup.py index 504170c..04b2964 100755 --- a/scripts/user_setup.py +++ b/scripts/user_setup.py @@ -21,96 +21,95 @@ def setup_user(user_name): USER_KEY_ADDRESS = get_key_address(USER_KEY_NAME) # 1. User creates own identity - # Checks if identity exists - has_identity = query_contract( - CONTRACTS["on_chain_id_address"], - {"get_identity": {"identity_owner": USER_KEY_ADDRESS}}, - ) - if not has_identity["data"]: - print( - f"Key {USER_KEY_NAME} has no identity. Creating a new Brazilian identity..." - ) - - # Create a new identity for the owner - res = execute_contract( - CONTRACTS["on_chain_id_address"], - { - "add_identity": { - "country": "BR" - } - }, - USER_KEY_NAME, - ) - - print("Identity created") - else: - print(f"Key {USER_KEY_NAME} has an identity.") + #Checks if identity exists + try: + has_identity = query_contract( + CONTRACTS["on_chain_id_address"], + {"get_identity": {"identity_owner": USER_KEY_ADDRESS}}, + ) + print(f"Key {USER_KEY_NAME} has an identity.") + except: + print( + f"Key {USER_KEY_NAME} has no identity. Creating a new Brazilian identity..." + ) + + # Create a new identity for the owner + res = execute_contract( + CONTRACTS["on_chain_id_address"], + { + "add_identity": { + "country": "BR" + } + }, + USER_KEY_NAME, + ) + + print("Identity created") # 2. User gives permission for trusted issuer to add claims # Check if permission already exists - issuer_has_claim_signer = query_contract( + try: + issuer_has_claim_signer = query_contract( CONTRACTS["on_chain_id_address"], {"get_key": { "key_owner": TRUSTED_ISSUER_KEY_ADDRESS, "key_type" : "ClaimSignerKey", "identity_owner": USER_KEY_ADDRESS }}, - ) - if not issuer_has_claim_signer["data"]: - print( - f"Key {TRUSTED_ISSUER_KEY_NAME} has no permission to add claims to key {USER_KEY_NAME}. Adding permission..." - ) - - # Give permission to trusted issuer to add claims - res = execute_contract( - CONTRACTS["on_chain_id_address"], - { - "add_key": { - "key_owner": TRUSTED_ISSUER_KEY_ADDRESS, - "key_type" : "ClaimSignerKey", - "identity_owner": USER_KEY_ADDRESS - } - }, - USER_KEY_ADDRESS, - ) - - print("Permission created") - else: - print(f"Key {TRUSTED_ISSUER_KEY_NAME} has permission to add claims to key {USER_KEY_NAME}.") - + ) + print(f"Key {TRUSTED_ISSUER_KEY_NAME} has permission to add claims to key {USER_KEY_NAME}.") + except: + print( + f"Key {TRUSTED_ISSUER_KEY_NAME} has no permission to add claims to key {USER_KEY_NAME}. Adding permission..." + ) + + # Give permission to trusted issuer to add claims + res = execute_contract( + CONTRACTS["on_chain_id_address"], + { + "add_key": { + "key_owner": TRUSTED_ISSUER_KEY_ADDRESS, + "key_type" : "ClaimSignerKey", + "identity_owner": USER_KEY_ADDRESS + } + }, + USER_KEY_ADDRESS, + ) + + print("Permission created") # 3. Trusted issuer creates a claim for the user # Check if claim already exists - has_claim = query_contract( - CONTRACTS["on_chain_id_address"], - {"verify_claim": { - "claim_id": "1", - "identity_owner": USER_KEY_ADDRESS - }}, - ) - if not has_claim["data"]: - print( - f"Key {USER_KEY_NAME} has no claims to topic 1. Adding claim..." - ) - - # Give permission to trusted issuer to add claims - res = execute_contract( - CONTRACTS["on_chain_id_address"], - { - "add_claim": { - "claim" : { - "token_addr": CONTRACTS["cw20_base_address"], - "topic": "1" - }, - "identity_owner": USER_KEY_ADDRESS - } - }, - TRUSTED_ISSUER_KEY_NAME, - ) - - print("Claim created") - else: - print(f"Key {USER_KEY_NAME} has claim to topic 1.") + try: + has_claim = query_contract( + CONTRACTS["on_chain_id_address"], + {"verify_claim": { + "claim_id": "1", + "identity_owner": USER_KEY_ADDRESS + }}, + ) + print(f"Key {USER_KEY_NAME} has claim to topic 1.") + except: + print( + f"Key {USER_KEY_NAME} has no claims to topic 1. Adding claim..." + ) + + # Give permission to trusted issuer to add claims + res = execute_contract( + CONTRACTS["on_chain_id_address"], + { + "add_claim": { + "claim" : { + "token_addr": CONTRACTS["cw20_base_address"], + "topic": "1" + }, + "identity_owner": USER_KEY_ADDRESS + } + }, + TRUSTED_ISSUER_KEY_NAME, + ) + + print("Claim created") ######## # Call # From 5de633dc0420ecdd870a160051abe9068c1fa379 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Tue, 1 Jul 2025 21:47:49 -0300 Subject: [PATCH 20/41] fix: add missing identity to trusted user --- scripts/manager_setup.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/scripts/manager_setup.py b/scripts/manager_setup.py index 7771958..c64c7a2 100755 --- a/scripts/manager_setup.py +++ b/scripts/manager_setup.py @@ -120,3 +120,27 @@ else: print(f"Token {CONTRACTS['cw20_base_address']} has claim topics") +# Add identity to trusted issuer +try: + has_identity = query_contract( + CONTRACTS["on_chain_id_address"], + {"get_identity": {"identity_owner": TRUSTED_ISSUER_KEY_ADDRESS}}, + ) + print(f"Key {TRUSTED_ISSUER_KEY_NAME} has an identity.") +except: + print( + f"Key {TRUSTED_ISSUER_KEY_NAME} has no identity. Creating a new Brazilian identity..." + ) + + # Create a new identity for the owner + res = execute_contract( + CONTRACTS["on_chain_id_address"], + { + "add_identity": { + "country": "BR" + } + }, + TRUSTED_ISSUER_KEY_NAME, + ) + + print("Identity created") From 1b6da8691f7f4cac4b1b428e046f383d829333e3 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Tue, 1 Jul 2025 21:47:59 -0300 Subject: [PATCH 21/41] docs: add small comment on usage --- scripts/user_setup.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/user_setup.py b/scripts/user_setup.py index 04b2964..bcb83a3 100755 --- a/scripts/user_setup.py +++ b/scripts/user_setup.py @@ -4,6 +4,8 @@ import sys from common import execute_contract, get_key_address, query_contract +# User and trusted issuer needs to have balance in account + ############################# # Import Core Variables # ############################# From c4012d580997d8a95e058f2907f50c8d6d8bc24d Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Tue, 1 Jul 2025 22:03:09 -0300 Subject: [PATCH 22/41] feat: add missing compliance setup --- scripts/compliance_setup.py | 70 +++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 scripts/compliance_setup.py diff --git a/scripts/compliance_setup.py b/scripts/compliance_setup.py new file mode 100644 index 0000000..1f2be07 --- /dev/null +++ b/scripts/compliance_setup.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 + +import sys +import config +from common import execute_contract, query_contract + +############################# +# Import Core Variables # +############################# + +OWNER_KEY_NAME = config.OWNER_KEY_NAME +OWNER_KEY_ADDRESS = config.OWNER_KEY_ADDRESS +CONTRACTS = config.CONTRACTS + +############# +# Functions # +############# + +def add_compliance_to_token(token_address): + # For now we add the compliances we have to the registry we have + # Claims compliance + print( + f"Adding claim compliance to token {token_address}..." + ) + + # Create a new identity for the owner + execute_contract( + CONTRACTS["compliance_registry_address"], + { + "add_compliance_module": { + "token_address": token_address, + "module_address": CONTRACTS["compliance_claims_address"], + "module_name": "ClaimsCompliance" + } + }, + OWNER_KEY_NAME, + ) + + print("Claim compliance added") + + # Country compliance + print( + f"Adding country compliance to token {token_address}..." + ) + + # Create a new identity for the owner + execute_contract( + CONTRACTS["compliance_registry_address"], + { + "add_compliance_module": { + "token_address": token_address, + "module_address": CONTRACTS["compliance_country_restriction_address"], + "module_name": "ClaimsCompliance" + } + }, + OWNER_KEY_NAME, + ) + + print("Country compliance added") + +######## +# Call # +######## + +if len(sys.argv) > 1: + token_address = sys.argv[1] + print(f"Setting up compliance for token: {token_address}") + add_compliance_to_token(token_address) +else: + print("Please provide the token address when calling the script") From 13f0f7859198573630249b0c274dd5fe56c8e077 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Tue, 1 Jul 2025 22:03:26 -0300 Subject: [PATCH 23/41] docs: add a new incomplete issue --- contracts-review.md | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts-review.md b/contracts-review.md index ca63813..53fc189 100644 --- a/contracts-review.md +++ b/contracts-review.md @@ -217,3 +217,4 @@ Notes - CW20 contract is not enforcing roles correctly - No mention to yield, distribution and so on - Are allowances ok? +- Compliance registry has no method to list compliances From 9f19681824e7435c63a365d3128f514ee60d93a0 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Tue, 1 Jul 2025 22:04:20 -0300 Subject: [PATCH 24/41] chore: clean up some unused variables --- scripts/user_setup.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/user_setup.py b/scripts/user_setup.py index bcb83a3..ec7fa11 100755 --- a/scripts/user_setup.py +++ b/scripts/user_setup.py @@ -14,16 +14,16 @@ TRUSTED_ISSUER_KEY_ADDRESS = config.TRUSTED_ISSUER_KEY_ADDRESS CONTRACTS = config.CONTRACTS -############ -# Function # -############ +############# +# Functions # +############# def setup_user(user_name): USER_KEY_NAME = user_name USER_KEY_ADDRESS = get_key_address(USER_KEY_NAME) # 1. User creates own identity - #Checks if identity exists + # Checks if identity exists try: has_identity = query_contract( CONTRACTS["on_chain_id_address"], @@ -36,7 +36,7 @@ def setup_user(user_name): ) # Create a new identity for the owner - res = execute_contract( + execute_contract( CONTRACTS["on_chain_id_address"], { "add_identity": { @@ -66,7 +66,7 @@ def setup_user(user_name): ) # Give permission to trusted issuer to add claims - res = execute_contract( + execute_contract( CONTRACTS["on_chain_id_address"], { "add_key": { @@ -97,7 +97,7 @@ def setup_user(user_name): ) # Give permission to trusted issuer to add claims - res = execute_contract( + execute_contract( CONTRACTS["on_chain_id_address"], { "add_claim": { From 9e06e839d3d9346fc49d79ac8902dbde90ced44a Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Tue, 1 Jul 2025 22:10:13 -0300 Subject: [PATCH 25/41] feat: add default case --- scripts/compliance_setup.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/compliance_setup.py b/scripts/compliance_setup.py index 1f2be07..72709c2 100644 --- a/scripts/compliance_setup.py +++ b/scripts/compliance_setup.py @@ -67,4 +67,6 @@ def add_compliance_to_token(token_address): print(f"Setting up compliance for token: {token_address}") add_compliance_to_token(token_address) else: - print("Please provide the token address when calling the script") + token_address = CONTRACTS["cw20_base_address"] + print(f"Assuming usage of default token address: {token_address}") + add_compliance_to_token(token_address) From 36046cbf43a6a10bc400325b71ca88a5d64f46e2 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Tue, 1 Jul 2025 22:16:13 -0300 Subject: [PATCH 26/41] fix: add missing compliance manager setup --- scripts/manager_setup.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/scripts/manager_setup.py b/scripts/manager_setup.py index c64c7a2..c7beadc 100755 --- a/scripts/manager_setup.py +++ b/scripts/manager_setup.py @@ -67,6 +67,30 @@ else: print(f"Key {OWNER_KEY_NAME} is a claim registry manager.") +# Setup the compliance manager +has_compliance_manager = query_contract( + CONTRACTS["owner_roles_address"], + {"is_owner": {"owner": OWNER_KEY_ADDRESS, "role": "compliance_manager"}}, +) +if not has_compliance_manager["data"]["is_owner"]: + print(f"Key {OWNER_KEY_NAME} is not a compliance manager. Creating a new role...") + + # Create a new role for the owner + res = execute_contract( + CONTRACTS["owner_roles_address"], + { + "add_owner_role": { + "owner": OWNER_KEY_ADDRESS, + "role": "compliance_manager", + } + }, + OWNER_KEY_NAME, + ) + + print("Role created") +else: + print(f"Key {OWNER_KEY_NAME} is a compliance registry manager.") + # Check if a address is a trusted issuer is_trusted_issuer = query_contract( CONTRACTS["trusted_issuers_address"], From 3053a0975ce247964e8479e7003d55bb7735fa06 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Tue, 1 Jul 2025 22:16:21 -0300 Subject: [PATCH 27/41] fix: chmod script --- scripts/compliance_setup.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 scripts/compliance_setup.py diff --git a/scripts/compliance_setup.py b/scripts/compliance_setup.py old mode 100644 new mode 100755 From e0d01a8a36dd968d407af8000daf21d3bb623f6a Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Tue, 1 Jul 2025 22:17:52 -0300 Subject: [PATCH 28/41] docs: add details to compliance registry limitations --- contracts-review.md | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts-review.md b/contracts-review.md index 53fc189..94fe80d 100644 --- a/contracts-review.md +++ b/contracts-review.md @@ -218,3 +218,4 @@ Notes - No mention to yield, distribution and so on - Are allowances ok? - Compliance registry has no method to list compliances + - Add always overwrites if applying to same token/name From 172162ca2018fa2560cdb7eb225fe02cf94cc513 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Tue, 1 Jul 2025 22:26:39 -0300 Subject: [PATCH 29/41] feat: add balance check script --- scripts/balance.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100755 scripts/balance.py diff --git a/scripts/balance.py b/scripts/balance.py new file mode 100755 index 0000000..f88e853 --- /dev/null +++ b/scripts/balance.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 + +import config +import sys +from common import execute_contract, get_key_address, query_contract + +############################# +# Import Core Variables # +############################# + +CONTRACTS = config.CONTRACTS + +############# +# Functions # +############# + +def balance_of(token_address, user_key): + print(f"Checking balance of {user_key} on CW20 {token_address}") + balance = query_contract( + token_address, + {"balance": {"address": get_key_address(user_key)}}, + ) + print(f"Balance: {balance}") + +######## +# Call # +######## + +if len(sys.argv) > 2: + user_key = sys.argv[1] + token_address = sys.argv[2] + balance_of(token_address, user_key) +elif len(sys.argv) > 1: + user_key = sys.argv[1] + print("Assuming usage of default contract") + balance_of(CONTRACTS["cw20_base_address"], user_key) +else: + print("Usage: ./balance user_key [token_address]") From 610231e2ac97c9cb01fffb6f87f1b110edb1ab98 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Tue, 1 Jul 2025 22:26:51 -0300 Subject: [PATCH 30/41] chore: remove excessive spaces --- scripts/compliance_setup.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/scripts/compliance_setup.py b/scripts/compliance_setup.py index 72709c2..31eb388 100755 --- a/scripts/compliance_setup.py +++ b/scripts/compliance_setup.py @@ -19,11 +19,7 @@ def add_compliance_to_token(token_address): # For now we add the compliances we have to the registry we have # Claims compliance - print( - f"Adding claim compliance to token {token_address}..." - ) - - # Create a new identity for the owner + print(f"Adding claim compliance to token {token_address}...") execute_contract( CONTRACTS["compliance_registry_address"], { @@ -35,15 +31,10 @@ def add_compliance_to_token(token_address): }, OWNER_KEY_NAME, ) - print("Claim compliance added") # Country compliance - print( - f"Adding country compliance to token {token_address}..." - ) - - # Create a new identity for the owner + print(f"Adding country compliance to token {token_address}...") execute_contract( CONTRACTS["compliance_registry_address"], { @@ -55,7 +46,6 @@ def add_compliance_to_token(token_address): }, OWNER_KEY_NAME, ) - print("Country compliance added") ######## From 2fcc1ade9cb4d1c7e66e2b9d649cde7d6278b9f1 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Tue, 1 Jul 2025 22:27:57 -0300 Subject: [PATCH 31/41] chore: remove excessive spaces --- scripts/user_setup.py | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/scripts/user_setup.py b/scripts/user_setup.py index ec7fa11..26bfcb1 100755 --- a/scripts/user_setup.py +++ b/scripts/user_setup.py @@ -31,11 +31,8 @@ def setup_user(user_name): ) print(f"Key {USER_KEY_NAME} has an identity.") except: - print( - f"Key {USER_KEY_NAME} has no identity. Creating a new Brazilian identity..." - ) - # Create a new identity for the owner + print(f"Key {USER_KEY_NAME} has no identity. Creating a new Brazilian identity...") execute_contract( CONTRACTS["on_chain_id_address"], { @@ -45,7 +42,6 @@ def setup_user(user_name): }, USER_KEY_NAME, ) - print("Identity created") # 2. User gives permission for trusted issuer to add claims @@ -61,11 +57,8 @@ def setup_user(user_name): ) print(f"Key {TRUSTED_ISSUER_KEY_NAME} has permission to add claims to key {USER_KEY_NAME}.") except: - print( - f"Key {TRUSTED_ISSUER_KEY_NAME} has no permission to add claims to key {USER_KEY_NAME}. Adding permission..." - ) - # Give permission to trusted issuer to add claims + print(f"Key {TRUSTED_ISSUER_KEY_NAME} has no permission to add claims to key {USER_KEY_NAME}. Adding permission...") execute_contract( CONTRACTS["on_chain_id_address"], { @@ -77,7 +70,6 @@ def setup_user(user_name): }, USER_KEY_ADDRESS, ) - print("Permission created") # 3. Trusted issuer creates a claim for the user @@ -92,11 +84,8 @@ def setup_user(user_name): ) print(f"Key {USER_KEY_NAME} has claim to topic 1.") except: - print( - f"Key {USER_KEY_NAME} has no claims to topic 1. Adding claim..." - ) - # Give permission to trusted issuer to add claims + print(f"Key {USER_KEY_NAME} has no claims to topic 1. Adding claim...") execute_contract( CONTRACTS["on_chain_id_address"], { @@ -110,7 +99,6 @@ def setup_user(user_name): }, TRUSTED_ISSUER_KEY_NAME, ) - print("Claim created") ######## From 96b395d10725408e3c3d8450b0af8a7145f433e7 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Tue, 1 Jul 2025 22:37:26 -0300 Subject: [PATCH 32/41] fix: incorrect claim restriction --- scripts/user_setup.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/scripts/user_setup.py b/scripts/user_setup.py index 26bfcb1..b632433 100755 --- a/scripts/user_setup.py +++ b/scripts/user_setup.py @@ -74,16 +74,14 @@ def setup_user(user_name): # 3. Trusted issuer creates a claim for the user # Check if claim already exists - try: - has_claim = query_contract( - CONTRACTS["on_chain_id_address"], - {"verify_claim": { - "claim_id": "1", - "identity_owner": USER_KEY_ADDRESS - }}, - ) - print(f"Key {USER_KEY_NAME} has claim to topic 1.") - except: + has_claim = query_contract( + CONTRACTS["on_chain_id_address"], + {"verify_claim": { + "claim_id": "1", + "identity_owner": USER_KEY_ADDRESS + }}, + ) + if not has_claim["data"]: # Give permission to trusted issuer to add claims print(f"Key {USER_KEY_NAME} has no claims to topic 1. Adding claim...") execute_contract( @@ -91,8 +89,10 @@ def setup_user(user_name): { "add_claim": { "claim" : { - "token_addr": CONTRACTS["cw20_base_address"], - "topic": "1" + "topic": "1", + "issuer": TRUSTED_ISSUER_KEY_ADDRESS, + "data": "", + "uri": "" }, "identity_owner": USER_KEY_ADDRESS } @@ -100,6 +100,8 @@ def setup_user(user_name): TRUSTED_ISSUER_KEY_NAME, ) print("Claim created") + else: + print(f"Key {USER_KEY_NAME} has claim to topic 1.") ######## # Call # From bd8b54854cb7f7adfcd013eee8bca5fe2e3c7512 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Tue, 1 Jul 2025 22:38:27 -0300 Subject: [PATCH 33/41] feat: add transfer script --- scripts/transfer.py | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100755 scripts/transfer.py diff --git a/scripts/transfer.py b/scripts/transfer.py new file mode 100755 index 0000000..102a3dc --- /dev/null +++ b/scripts/transfer.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 + +import config +import sys +from common import execute_contract, get_key_address, query_contract + +############################# +# Import Core Variables # +############################# + +CONTRACTS = config.CONTRACTS + +############# +# Functions # +############# + +def transfer(sender, receiver, quantity): + print(f"{sender} is transferring {quantity} to {receiver}. Both need to have claims and compliance") + execute_contract( + CONTRACTS["cw20_base_address"], + { + "transfer": { + "recipient" : get_key_address(sender), + "amount": quantity + } + }, + sender, + ) + print("Send completed") + +######## +# Call # +######## + +if len(sys.argv) > 3: + sender = sys.argv[1] + receiver = sys.argv[2] + quantity = sys.argv[3] + transfer(sender, receiver, quantity) +else: + print("Usage: ./transfer sender_key_name receiver_key_name quantity") From 777f6f78fbf427fa17fe74dc7630e328b8d38da8 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Wed, 2 Jul 2025 10:11:00 -0300 Subject: [PATCH 34/41] fix: correct typo on transfer --- scripts/transfer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/transfer.py b/scripts/transfer.py index 102a3dc..7af0631 100755 --- a/scripts/transfer.py +++ b/scripts/transfer.py @@ -20,7 +20,7 @@ def transfer(sender, receiver, quantity): CONTRACTS["cw20_base_address"], { "transfer": { - "recipient" : get_key_address(sender), + "recipient" : get_key_address(receiver), "amount": quantity } }, From c9ec5ffed26ceefb1ba821e39579dfd96bc0ec6f Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Wed, 2 Jul 2025 10:13:45 -0300 Subject: [PATCH 35/41] fix: correct fmt --- contracts/cw20-base/src/contract.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/contracts/cw20-base/src/contract.rs b/contracts/cw20-base/src/contract.rs index 5faf3ff..060350b 100644 --- a/contracts/cw20-base/src/contract.rs +++ b/contracts/cw20-base/src/contract.rs @@ -126,9 +126,7 @@ pub fn instantiate( total_supply, mint, }; - let compliance_addr = deps - .api - .addr_validate(&msg.registries.compliance_address)?; + let compliance_addr = deps.api.addr_validate(&msg.registries.compliance_address)?; COMPLIANCE_ADDRESS.save(deps.storage, &compliance_addr)?; TOKEN_INFO.save(deps.storage, &data)?; From eb005d0d9f69b586f5290e291b67dc7e78086c40 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Wed, 2 Jul 2025 10:28:43 -0300 Subject: [PATCH 36/41] fix: use correct main call --- scripts/compliance_setup.py | 18 +++++++++--------- scripts/transfer.py | 16 ++++++++-------- scripts/user_setup.py | 14 +++++++------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/scripts/compliance_setup.py b/scripts/compliance_setup.py index 31eb388..76d98fd 100755 --- a/scripts/compliance_setup.py +++ b/scripts/compliance_setup.py @@ -51,12 +51,12 @@ def add_compliance_to_token(token_address): ######## # Call # ######## - -if len(sys.argv) > 1: - token_address = sys.argv[1] - print(f"Setting up compliance for token: {token_address}") - add_compliance_to_token(token_address) -else: - token_address = CONTRACTS["cw20_base_address"] - print(f"Assuming usage of default token address: {token_address}") - add_compliance_to_token(token_address) +if __name__== "__main__": + if len(sys.argv) > 1: + token_address = sys.argv[1] + print(f"Setting up compliance for token: {token_address}") + add_compliance_to_token(token_address) + else: + token_address = CONTRACTS["cw20_base_address"] + print(f"Assuming usage of default token address: {token_address}") + add_compliance_to_token(token_address) diff --git a/scripts/transfer.py b/scripts/transfer.py index 7af0631..49111c7 100755 --- a/scripts/transfer.py +++ b/scripts/transfer.py @@ -31,11 +31,11 @@ def transfer(sender, receiver, quantity): ######## # Call # ######## - -if len(sys.argv) > 3: - sender = sys.argv[1] - receiver = sys.argv[2] - quantity = sys.argv[3] - transfer(sender, receiver, quantity) -else: - print("Usage: ./transfer sender_key_name receiver_key_name quantity") +if __name__== "__main__": + if len(sys.argv) > 3: + sender = sys.argv[1] + receiver = sys.argv[2] + quantity = sys.argv[3] + transfer(sender, receiver, quantity) + else: + print("Usage: ./transfer sender_key_name receiver_key_name quantity") diff --git a/scripts/user_setup.py b/scripts/user_setup.py index b632433..b2a3b47 100755 --- a/scripts/user_setup.py +++ b/scripts/user_setup.py @@ -106,10 +106,10 @@ def setup_user(user_name): ######## # Call # ######## - -if len(sys.argv) > 1: - name = sys.argv[1] - print(f"Setting up user key: {name}") - setup_user(name) -else: - print("Please provide the user key name when calling the script") +if __name__== "__main__": + if len(sys.argv) > 1: + name = sys.argv[1] + print(f"Setting up user key: {name}") + setup_user(name) + else: + print("Please provide the user key name when calling the script") From 4d68c1f64cda7813d37ae9cb442d61beabcc6f67 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Wed, 2 Jul 2025 10:29:52 -0300 Subject: [PATCH 37/41] fix: use correct main check --- scripts/balance.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/scripts/balance.py b/scripts/balance.py index f88e853..4c390a0 100755 --- a/scripts/balance.py +++ b/scripts/balance.py @@ -25,14 +25,14 @@ def balance_of(token_address, user_key): ######## # Call # ######## - -if len(sys.argv) > 2: - user_key = sys.argv[1] - token_address = sys.argv[2] - balance_of(token_address, user_key) -elif len(sys.argv) > 1: - user_key = sys.argv[1] - print("Assuming usage of default contract") - balance_of(CONTRACTS["cw20_base_address"], user_key) -else: - print("Usage: ./balance user_key [token_address]") +if __name__== "__main__": + if len(sys.argv) > 2: + user_key = sys.argv[1] + token_address = sys.argv[2] + balance_of(token_address, user_key) + elif len(sys.argv) > 1: + user_key = sys.argv[1] + print("Assuming usage of default contract") + balance_of(CONTRACTS["cw20_base_address"], user_key) + else: + print("Usage: ./balance user_key [token_address]") From 6a48241d605e0c4b3d22fce6a5eeb380d2334988 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Wed, 2 Jul 2025 10:48:57 -0300 Subject: [PATCH 38/41] feat: handle contract exceptions --- scripts/common.py | 24 ++++++++++++++++++++++++ scripts/manager_setup.py | 12 +++++++----- scripts/user_setup.py | 40 +++++++++++++++++++++------------------- 3 files changed, 52 insertions(+), 24 deletions(-) diff --git a/scripts/common.py b/scripts/common.py index 02a17a4..55a0b09 100644 --- a/scripts/common.py +++ b/scripts/common.py @@ -212,3 +212,27 @@ def query_contract(contract_address, query_msg): # Parse and return the result return json.loads(result) + +def query_contract_fail_with(contract_address, query_msg, expected_err): + # Build the command to query the contract + cmd = [ + KIICHAIN, + "query", + "wasm", + "contract-state", + "smart", + contract_address, + json.dumps(query_msg), + "--node", RPC_URL, + "-o", "json" + ] + + # Run the command + result, err = run_cmd(cmd) + if err: + if expected_err not in err: + raise Exception(f"Contract failed with unexpected error: {err}") + return result, True + + # Parse and return the result + return result, False diff --git a/scripts/manager_setup.py b/scripts/manager_setup.py index c7beadc..d6386a7 100755 --- a/scripts/manager_setup.py +++ b/scripts/manager_setup.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import config -from common import execute_contract, query_contract +from common import execute_contract, query_contract, query_contract_fail_with ############################# # Import Core Variables # @@ -145,13 +145,13 @@ print(f"Token {CONTRACTS['cw20_base_address']} has claim topics") # Add identity to trusted issuer -try: - has_identity = query_contract( + +_, inexistent = query_contract_fail_with( CONTRACTS["on_chain_id_address"], {"get_identity": {"identity_owner": TRUSTED_ISSUER_KEY_ADDRESS}}, + "not found: query wasm contract failed" ) - print(f"Key {TRUSTED_ISSUER_KEY_NAME} has an identity.") -except: +if inexistent: print( f"Key {TRUSTED_ISSUER_KEY_NAME} has no identity. Creating a new Brazilian identity..." ) @@ -168,3 +168,5 @@ ) print("Identity created") +else: + print(f"Key {TRUSTED_ISSUER_KEY_NAME} has an identity.") diff --git a/scripts/user_setup.py b/scripts/user_setup.py index b2a3b47..56a7ad2 100755 --- a/scripts/user_setup.py +++ b/scripts/user_setup.py @@ -2,7 +2,7 @@ import config import sys -from common import execute_contract, get_key_address, query_contract +from common import execute_contract, get_key_address, query_contract, query_contract_fail_with # User and trusted issuer needs to have balance in account @@ -24,13 +24,12 @@ def setup_user(user_name): # 1. User creates own identity # Checks if identity exists - try: - has_identity = query_contract( - CONTRACTS["on_chain_id_address"], - {"get_identity": {"identity_owner": USER_KEY_ADDRESS}}, - ) - print(f"Key {USER_KEY_NAME} has an identity.") - except: + _, inexistent = query_contract_fail_with( + CONTRACTS["on_chain_id_address"], + {"get_identity": {"identity_owner": USER_KEY_ADDRESS}}, + "not found: query wasm contract failed" + ) + if inexistent: # Create a new identity for the owner print(f"Key {USER_KEY_NAME} has no identity. Creating a new Brazilian identity...") execute_contract( @@ -43,20 +42,21 @@ def setup_user(user_name): USER_KEY_NAME, ) print("Identity created") + else: + print(f"Key {USER_KEY_NAME} has an identity.") # 2. User gives permission for trusted issuer to add claims # Check if permission already exists - try: - issuer_has_claim_signer = query_contract( - CONTRACTS["on_chain_id_address"], - {"get_key": { - "key_owner": TRUSTED_ISSUER_KEY_ADDRESS, - "key_type" : "ClaimSignerKey", - "identity_owner": USER_KEY_ADDRESS - }}, - ) - print(f"Key {TRUSTED_ISSUER_KEY_NAME} has permission to add claims to key {USER_KEY_NAME}.") - except: + _, inexistent = query_contract_fail_with( + CONTRACTS["on_chain_id_address"], + {"get_key": { + "key_owner": TRUSTED_ISSUER_KEY_ADDRESS, + "key_type" : "ClaimSignerKey", + "identity_owner": USER_KEY_ADDRESS + }}, + "Key not found for owner" + ) + if inexistent: # Give permission to trusted issuer to add claims print(f"Key {TRUSTED_ISSUER_KEY_NAME} has no permission to add claims to key {USER_KEY_NAME}. Adding permission...") execute_contract( @@ -71,6 +71,8 @@ def setup_user(user_name): USER_KEY_ADDRESS, ) print("Permission created") + else: + print(f"Key {TRUSTED_ISSUER_KEY_NAME} has permission to add claims to key {USER_KEY_NAME}.") # 3. Trusted issuer creates a claim for the user # Check if claim already exists From 6fd88df3820ba2ae6de354da0b35a8784c64c258 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Wed, 2 Jul 2025 11:05:28 -0300 Subject: [PATCH 39/41] chore: fix clippy warnings --- contracts/on_chain_id/src/contract.rs | 12 ++++-------- contracts/on_chain_id/src/key_management.rs | 4 ++-- contracts/on_chain_id/src/utils.rs | 2 +- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/contracts/on_chain_id/src/contract.rs b/contracts/on_chain_id/src/contract.rs index 2faf3c6..1203c19 100644 --- a/contracts/on_chain_id/src/contract.rs +++ b/contracts/on_chain_id/src/contract.rs @@ -28,17 +28,14 @@ pub fn instantiate( msg: InstantiateMsg, ) -> Result { set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION).map_err(|e| { - ContractError::Std(StdError::generic_err(format!( - "Failed to set contract version: {}", - e - ))) + ContractError::Std(StdError::generic_err(format!("Failed to set contract version: {e}"))) })?; let owner = deps .api .addr_validate(&msg.owner) .map_err(|e| ContractError::InvalidAddress { - reason: format!("Invalid owner address: {}", e), + reason: format!("Invalid owner address: {e}"), })?; // Save the owner @@ -53,7 +50,7 @@ pub fn instantiate( .api .addr_validate(&msg.trusted_issuer_addr) .map_err(|e| ContractError::InvalidAddress { - reason: format!("Invalid trusted issuer address: {}", e), + reason: format!("Invalid trusted issuer address: {e}"), })?; // Save the trusted issuers address @@ -186,8 +183,7 @@ fn query_key( .cloned() .ok_or_else(|| { StdError::not_found(format!( - "Key not found for owner {} and type {:?} in identity {}", - key_owner, key_type, identity_owner + "Key not found for owner {key_owner} and type {key_type:?} in identity {identity_owner}" )) }) } diff --git a/contracts/on_chain_id/src/key_management.rs b/contracts/on_chain_id/src/key_management.rs index 38b41c4..5771043 100644 --- a/contracts/on_chain_id/src/key_management.rs +++ b/contracts/on_chain_id/src/key_management.rs @@ -27,7 +27,7 @@ pub fn execute_add_key( &identity_owner_addr, ) .map_err(|e| ContractError::Unauthorized { - reason: format!("Sender lacks ManagementKey: {}", e), + reason: format!("Sender lacks ManagementKey: {e}"), })?; let addr_key_owner = deps.api.addr_validate(&key_owner)?; @@ -89,7 +89,7 @@ pub fn execute_remove_key( &identity_owner_addr, ) .map_err(|e| ContractError::Unauthorized { - reason: format!("Sender lacks ManagementKey: {}", e), + reason: format!("Sender lacks ManagementKey: {e}"), })?; let addr_key_owner = deps.api.addr_validate(&key_owner)?; diff --git a/contracts/on_chain_id/src/utils.rs b/contracts/on_chain_id/src/utils.rs index 066270f..cd40596 100644 --- a/contracts/on_chain_id/src/utils.rs +++ b/contracts/on_chain_id/src/utils.rs @@ -31,7 +31,7 @@ pub fn check_key_authorization( Ok(()) } else { Err(ContractError::Unauthorized { - reason: format!("Sender lacks required key type: {:?}", required_key), + reason: format!("Sender lacks required key type: {required_key:?}"), }) } } From 6020f66d0701749f6e051e02430cfbd4c97839f3 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Wed, 2 Jul 2025 11:06:53 -0300 Subject: [PATCH 40/41] chore: fix fmt --- contracts/on_chain_id/src/contract.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/contracts/on_chain_id/src/contract.rs b/contracts/on_chain_id/src/contract.rs index 1203c19..d7b645f 100644 --- a/contracts/on_chain_id/src/contract.rs +++ b/contracts/on_chain_id/src/contract.rs @@ -28,7 +28,9 @@ pub fn instantiate( msg: InstantiateMsg, ) -> Result { set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION).map_err(|e| { - ContractError::Std(StdError::generic_err(format!("Failed to set contract version: {e}"))) + ContractError::Std(StdError::generic_err(format!( + "Failed to set contract version: {e}" + ))) })?; let owner = deps From 96bc5bc42aaaf393adf91cd1b66982cfb4e448d9 Mon Sep 17 00:00:00 2001 From: Thales Zirbel Date: Wed, 2 Jul 2025 11:08:46 -0300 Subject: [PATCH 41/41] chore: fix clippy warnings --- contracts/claim-topics/src/contract.rs | 2 +- contracts/owner-roles/src/contract.rs | 8 ++++---- contracts/trusted-issuers/src/contract.rs | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/contracts/claim-topics/src/contract.rs b/contracts/claim-topics/src/contract.rs index 665f594..93768d9 100644 --- a/contracts/claim-topics/src/contract.rs +++ b/contracts/claim-topics/src/contract.rs @@ -27,7 +27,7 @@ pub fn instantiate( .api .addr_validate(msg.owner_roles_address.as_ref()) .map_err(|e| ContractError::InvalidAddress { - reason: format!("Invalid owner address: {}", e), + reason: format!("Invalid owner address: {e}"), })?; OWNER_ROLES_ADDRESS.save(deps.storage, &owner_addr)?; diff --git a/contracts/owner-roles/src/contract.rs b/contracts/owner-roles/src/contract.rs index 520f581..525bda2 100644 --- a/contracts/owner-roles/src/contract.rs +++ b/contracts/owner-roles/src/contract.rs @@ -243,7 +243,7 @@ pub mod execute { .add_message(msg) .add_attribute("action", "add_trusted_issuer") .add_attribute("issuer", issuer.to_string()) - .add_attribute("claim_topics", format!("{:?}", claim_topics))) + .add_attribute("claim_topics", format!("{claim_topics:?}"))) } pub fn remove_trusted_issuer( @@ -367,7 +367,7 @@ pub mod execute { .add_message(msg) .add_attribute("action", "update_issuer_claim_topics") .add_attribute("issuer", issuer.to_string()) - .add_attribute("claim_topics", format!("{:?}", claim_topics))) + .add_attribute("claim_topics", format!("{claim_topics:?}"))) } } @@ -877,7 +877,7 @@ mod tests { vec![ attr("action", "add_trusted_issuer"), attr("issuer", new_issuer.to_string()), - attr("claim_topics", format!("{:?}", claim_topics)), + attr("claim_topics", format!("{claim_topics:?}")), ] ); } @@ -988,7 +988,7 @@ mod tests { vec![ attr("action", "update_issuer_claim_topics"), attr("issuer", issuer_to_update.to_string()), - attr("claim_topics", format!("{:?}", new_claim_topics)), + attr("claim_topics", format!("{new_claim_topics:?}")), ] ); } diff --git a/contracts/trusted-issuers/src/contract.rs b/contracts/trusted-issuers/src/contract.rs index 189697f..3c05e88 100644 --- a/contracts/trusted-issuers/src/contract.rs +++ b/contracts/trusted-issuers/src/contract.rs @@ -134,7 +134,7 @@ pub mod execute { Ok(Response::new() .add_attribute("action", "add_trusted_issuer") .add_attribute("issuer", issuer.to_string()) - .add_attribute("claim_topics", format!("{:?}", claim_topics))) + .add_attribute("claim_topics", format!("{claim_topics:?}"))) } pub fn update_trusted_issuer( @@ -155,7 +155,7 @@ pub mod execute { Ok(Response::new() .add_attribute("action", "updated_trusted_issuer") .add_attribute("issuer", issuer.to_string()) - .add_attribute("claim_topics", format!("{:?}", claim_topics))) + .add_attribute("claim_topics", format!("{claim_topics:?}"))) } pub fn remove_trusted_issuer(deps: DepsMut, issuer: Addr) -> Result {