{"id":32024010,"url":"https://github.com/base/mcm-go","last_synced_at":"2025-10-16T10:22:24.975Z","repository":{"id":318454133,"uuid":"1068523860","full_name":"base/mcm-go","owner":"base","description":null,"archived":false,"fork":false,"pushed_at":"2025-10-07T08:44:29.000Z","size":11027,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-10-07T10:24:23.238Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/base.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-02T14:10:19.000Z","updated_at":"2025-10-07T08:44:32.000Z","dependencies_parsed_at":"2025-10-07T10:24:49.776Z","dependency_job_id":"74f0f394-304b-44b1-8ee7-d956f4ba2218","html_url":"https://github.com/base/mcm-go","commit_stats":null,"previous_names":["base/mcm-go"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/base/mcm-go","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/base%2Fmcm-go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/base%2Fmcm-go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/base%2Fmcm-go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/base%2Fmcm-go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/base","download_url":"https://codeload.github.com/base/mcm-go/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/base%2Fmcm-go/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279177991,"owners_count":26120083,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-16T02:00:06.019Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-10-16T10:22:21.273Z","updated_at":"2025-10-16T10:22:24.963Z","avatar_url":"https://github.com/base.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MCM Go SDK\n\nGo SDK for the Multi-Chain Multisig (MCM) Solana program.\n\n## CLI Tool\n\nThe SDK includes `mcmctl`, a command-line tool for managing MCM multisigs:\n\n```bash\ngo install github.com/base/mcm-go/cmd/mcmctl@latest\n```\n\n```bash\n# Set environment variables\nexport RPC_URL=\"devnet\"\nexport WS_URL=\"devnet\"\nexport MCM_PROGRAM_ID=\"YourProgramID\"\n\n# Initialize a multisig (hex values must use 0x prefix)\nmcmctl multisig init --multisig-id \u003chex32\u003e --chain-id 1\n\n# Transfer ownership (two-step process)\nmcmctl ownership transfer --multisig-id \u003chex32\u003e --proposed-owner \u003cpubkey\u003e\nmcmctl ownership accept --multisig-id \u003chex32\u003e --authority \u003cnew-owner-keypair\u003e\n\n# Manage signers\nmcmctl signers init --multisig-id \u003chex32\u003e --total 10\nmcmctl signers append --multisig-id \u003chex32\u003e --signers \u003caddr1\u003e,\u003caddr2\u003e,...\nmcmctl signers finalize --multisig-id \u003chex32\u003e\nmcmctl signers clear --multisig-id \u003chex32\u003e\nmcmctl signers set-config --multisig-id \u003chex32\u003e --signer-groups \u003cgroups\u003e --group-quorums \u003cquorums\u003e --group-parents \u003cparents\u003e\n```\n\nSee [cmd/mcmctl/README.md](cmd/mcmctl/README.md) for complete documentation.\n\n## Quick Start\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"github.com/gagliardetto/solana-go\"\n    \"github.com/base/mcm-go/pkg/client\"\n    \"github.com/base/mcm-go/pkg/proposal\"\n    \"github.com/base/mcm-go/pkg/services\"\n)\n\nfunc main() {\n    // Setup client\n    payer := solana.MustPrivateKeyFromBase58(\"your-private-key\")\n    programID := solana.MustPublicKeyFromBase58(\"YourProgramID\")\n\n    cfg := client.Config{\n        RPCURL:    \"https://api.devnet.solana.com\",\n        WSURL:     \"wss://api.devnet.solana.com\",\n        ProgramID: programID,\n        Payer:     \u0026payer,\n    }\n\n    mcmClient, _ := client.New(cfg)\n    defer mcmClient.Close()\n\n    // Create proposal from on-chain state\n    var multisigID [32]byte // Your multisig ID\n    var validUntil uint32 = 1800000000\n    var instructions []solana.Instruction // Your instructions\n\n    proposalSvc := services.NewProposalService(mcmClient)\n    ctx := context.Background()\n\n    p, _ := proposalSvc.CreateProposalFromChain(ctx, services.CreateProposalFromChainParams{\n        MultisigID:           multisigID,\n        ValidUntil:           validUntil,\n        Instructions:         instructions,\n        OverridePreviousRoot: false,\n    })\n\n    // Compute Merkle root and hash to sign\n    pwr, _ := p.WithRoot()\n    pts, _ := pwr.WithHashToSign()\n\n    // Distribute pts.HashToSign to signers for ECDSA signing\n}\n```\n\n## CLI Examples\n\nThe `cmd/mcmctl` directory provides a complete command-line interface demonstrating SDK usage:\n\n- **Multisig operations** - Initialize multisig accounts on Solana\n- **Ownership management** - Transfer multisig ownership securely (two-step process)\n- **Signers management** - Configure signer addresses and groups\n- **Signatures management** - Submit ECDSA signatures for proposal approval\n- **Proposal operations** - Create proposals from instructions, compute hash for signing, set roots, and execute operations on-chain\n  - Includes specialized commands organized by category:\n    - **loader-v3**: `proposal loader-v3 upgrade` for Solana program upgrades, `proposal loader-v3 set-authority` for changing/removing upgrade authorities\n    - **mcm**: `proposal mcm update-signers` for complete signers configuration updates\n    - **mcm**: `proposal mcm accept-ownership` for accepting ownership transfers\n    - **bridge**: `proposal bridge pause` for pausing/unpausing bridge operations\n    - **bridge**: `proposal bridge set-partner-oracle-config` for updating bridge oracle configuration\n\nSee [cmd/mcmctl/README.md](cmd/mcmctl/README.md) for detailed usage examples.\n\n## Package Structure\n\n```\nmcm-go/\n├── pkg/\n│   ├── bindings/      # Anchor-generated types from mcm.json IDL\n│   ├── client/        # Solana RPC/WebSocket client wrapper with transaction helpers\n│   ├── crypto/        # Keccak256 Merkle tree implementation with proof generation\n│   ├── pda/           # Program Derived Address utilities\n│   ├── proposal/      # Proposal types, builder, Merkle computation, signing\n│   │   ├── io/        # JSON persistence (save/load proposals)\n│   │   ├── types.go   # Core types (Proposal, ProposalWithRoot, ProposalToSign)\n│   │   ├── builder.go # Builder pattern for constructing proposals\n│   │   ├── merkle.go  # Merkle root computation (p.WithRoot())\n│   │   └── signing.go # Hash to sign computation (pwr.WithHashToSign())\n│   ├── instructions/  # MCM instruction builders (Initialize, SetConfig, etc.)\n│   ├── state/         # On-chain account fetchers\n│   └── services/      # High-level services (ProposalService, SignersService, etc.)\n├── cmd/mcmctl/        # CLI demonstrating SDK usage\n└── mcm.json          # MCM program IDL (Anchor \u003e= 0.30.0)\n```\n\n## Core Concepts\n\n### 1. Proposals\n\nProposals contain instructions and metadata. The SDK provides a fluent API for computing cryptographic components:\n\n```go\nimport \"github.com/base/mcm-go/pkg/proposal\"\n\n// Option 1: Using Builder\nbuilder := proposal.NewBuilder(multisigID, validUntil)\nbuilder.SetRootMetadata(metadata)\nbuilder.AddInstruction(instruction)\np, _ := builder.Build()\n\n// Option 2: Direct construction\np := \u0026proposal.Proposal{\n    MultisigID:   multisigID,\n    ValidUntil:   validUntil,\n    Instructions: instructions,\n    RootMetadata: metadata,\n}\n\n// Compute Merkle root and proofs\npwr, _ := p.WithRoot()\n\n// Compute hash for ECDSA signing (keccak256(root || validUntil))\npts, _ := pwr.WithHashToSign()\n\n// Distribute pts.HashToSign to signers\n```\n\n**IMPORTANT - Execution Authority:**\n\nWhen creating proposals, ensure that the account used as `authority` when executing operations is NOT present in the accounts of any proposal operation. If the same account appears in both places, the program may fail with `ProofCannotBeVerified` error due to signer flag inconsistencies during Merkle proof verification.\n\n**Recommended:** Use a dedicated account as execution authority that never appears in operation accounts.\n\n### 2. Merkle Trees\n\nKeccak256-based Merkle tree with automatic proof generation:\n\n```go\nimport \"github.com/base/mcm-go/pkg/crypto\"\n\nleaves := [][32]byte{leaf1, leaf2, leaf3}\ntree, _ := crypto.BuildMerkleTreeFromLeaves(leaves)\n\n// tree.Root is the Merkle root\n// tree.Proofs[i] is the proof for leaves[i]\n```\n\n### 3. PDA Derivation\n\nDerive Program Derived Addresses:\n\n```go\nimport \"github.com/base/mcm-go/pkg/pda\"\n\nconfigPDA, _, _ := pda.MultisigConfigPDA(programID, multisigID)\nrootMetadataPDA, _, _ := pda.RootMetadataPDA(programID, multisigID)\n```\n\n### 4. Services\n\nHigh-level services for common workflows:\n\n```go\nimport \"github.com/base/mcm-go/pkg/services\"\n\n// Signers management\nsignersSvc := services.NewSignersService(client)\nsignersSvc.InitSigners(ctx, params)\nsignersSvc.AppendSigners(ctx, params)\nsignersSvc.FinalizeSigners(ctx, params)\nsignersSvc.SetConfig(ctx, params)\n\n// Signatures management\nsigsSvc := services.NewSignaturesService(client)\nsigsSvc.InitSignatures(ctx, params)\nsigsSvc.AppendSignatures(ctx, params)\nsigsSvc.FinalizeSignatures(ctx, params)\n\n// Proposal service (includes creation, root setting, and execution)\nproposalSvc := services.NewProposalService(client)\np, _ := proposalSvc.CreateProposalFromChain(ctx, params)\nproposalSvc.SetRoot(ctx, params)\nproposalSvc.Execute(ctx, params) // Execute operations (single, multiple, or all)\n```\n\n### 5. Persistence\n\nSave and load proposals to/from JSON:\n\n```go\nimport \"github.com/base/mcm-go/pkg/proposal/io\"\n\n// Save proposal to file\nio.SaveProposal(p, \"proposal.json\")\n\n// Load proposal from file\np, _ := io.LoadProposal(\"proposal.json\")\n\n// Compute root and hash after loading\npwr, _ := p.WithRoot()\npts, _ := pwr.WithHashToSign()\n```\n\n## Complete Workflow\n\n### 1. Initialize Multisig\n\n```go\nimport \"github.com/base/mcm-go/pkg/instructions\"\n\nix, _ := instructions.Initialize(instructions.InitializeParams{\n    ChainID:    1,\n    MultisigID: multisigID,\n    Authority:  authority,\n    ProgramID:  programID,\n})\n```\n\n### 2. Configure Signers\n\n```go\nsignersSvc := services.NewSignersService(client)\n\n// Initialize signer storage\nsignersSvc.InitSigners(ctx, services.InitSignersParams{\n    MultisigID:   multisigID,\n    TotalSigners: 10,\n})\n\n// Add signers\nsignersSvc.AppendSigners(ctx, services.AppendSignersParams{\n    MultisigID:   multisigID,\n    SignersBatch: signerAddresses,\n})\n\n// Finalize\nsignersSvc.FinalizeSigners(ctx, services.FinalizeSignersParams{\n    MultisigID: multisigID,\n})\n```\n\n### 3. Set Configuration\n\n```go\nix, _ := instructions.SetConfig(instructions.SetConfigParams{\n    MultisigID:   multisigID,\n    SignerGroups: groups,\n    GroupQuorums: quorums,\n    GroupParents: parents,\n    ClearRoot:    false,\n    Authority:    authority,\n    ProgramID:    programID,\n})\n```\n\n### 4. Create Proposal and Collect Signatures\n\n```go\nproposalSvc := services.NewProposalService(client)\n\n// Create proposal from on-chain state\np, _ := proposalSvc.CreateProposalFromChain(ctx, services.CreateProposalFromChainParams{\n    MultisigID:           multisigID,\n    ValidUntil:           validUntil,\n    Instructions:         instructions,\n    OverridePreviousRoot: false,\n})\n\n// Compute root and hash\npwr, _ := p.WithRoot()\npts, _ := pwr.WithHashToSign()\n\n// Distribute pts.HashToSign to signers for off-chain ECDSA signing\n// Collect signatures...\n\n// Submit signatures on-chain\nsigsSvc := services.NewSignaturesService(client)\nsigsSvc.InitSignatures(ctx, services.InitSignaturesParams{\n    MultisigID:      multisigID,\n    Root:            pwr.Root,\n    ValidUntil:      validUntil,\n    TotalSignatures: uint8(len(signatures)),\n})\nsigsSvc.AppendSignatures(ctx, services.AppendSignaturesParams{\n    MultisigID:      multisigID,\n    Root:            pwr.Root,\n    ValidUntil:      validUntil,\n    SignaturesBatch: signatures,\n})\nsigsSvc.FinalizeSignatures(ctx, services.FinalizeSignaturesParams{\n    MultisigID: multisigID,\n    Root:       pwr.Root,\n    ValidUntil: validUntil,\n})\n```\n\n### 5. Set Root and Execute\n\n```go\n// Set root on-chain\nproposalSvc.SetRoot(ctx, services.SetRootParams{\n    MultisigID: multisigID,\n    Proposal:   pwr,\n})\n\n// Execute all operations\nproposalSvc.Execute(ctx, services.ExecuteParams{\n    MultisigID:       multisigID,\n    ProposalWithRoot: pwr,\n    StartIndex:       0,\n    OperationCount:   len(pwr.Instructions),\n})\n\n// Execute first operation\nproposalSvc.Execute(ctx, services.ExecuteParams{\n    MultisigID:       multisigID,\n    ProposalWithRoot: pwr,\n    StartIndex:       0,\n    OperationCount:   1,\n})\n// Execute next operation\nproposalSvc.Execute(ctx, services.ExecuteParams{\n    MultisigID:       multisigID,\n    ProposalWithRoot: pwr,\n    StartIndex:       1,\n    OperationCount:   1,\n})\n// Execute first two operations together\nproposalSvc.Execute(ctx, services.ExecuteParams{\n    MultisigID:       multisigID,\n    ProposalWithRoot: pwr,\n    StartIndex:       0,\n    OperationCount:   2,\n})\n```\n\n## State Fetching\n\nFetch on-chain account state:\n\n```go\nimport \"github.com/base/mcm-go/pkg/state\"\n\nfetcher := state.NewFetcher(rpcClient, programID)\n\nconfig, _ := fetcher.GetMultisigConfig(ctx, multisigID)\nrootAndOpCount, _ := fetcher.GetExpiringRootAndOpCount(ctx, multisigID)\nrootMetadata, _ := fetcher.GetRootMetadata(ctx, multisigID)\n```\n\n## Architecture\n\nThe SDK is organized in layers:\n\n1. **Bindings** (`pkg/bindings`) - Anchor-generated types from IDL\n2. **Core Utilities** (`pkg/pda`, `pkg/crypto`) - PDAs and Merkle trees\n3. **Proposal Layer** (`pkg/proposal`) - Proposal construction and cryptography\n4. **Instructions** (`pkg/instructions`) - MCM instruction builders\n5. **State** (`pkg/state`) - On-chain account fetchers\n6. **Services** (`pkg/services`) - High-level workflows\n7. **Client** (`pkg/client`) - RPC, WebSocket, and transaction handling\n\n## Testing\n\n```bash\ngo test ./...\n```\n\n## Dependencies\n\n- [solana-go](https://github.com/gagliardetto/solana-go) - Solana Go SDK\n- [anchor-go](https://github.com/gagliardetto/anchor-go) - Used to generate bindings from `mcm.json` IDL\n\n## IDL Source\n\nThe `mcm.json` IDL is sourced from the [MCM Solana program](https://github.com/smartcontractkit/chainlink-ccip/blob/main/chains/solana/contracts/target/idl/mcm.json) and updated to align with Anchor \u003e= 0.30.0.\n\n## Links\n\n- [MCM Solana Program](https://github.com/smartcontractkit/chainlink-ccip/tree/main/chains/solana/contracts/programs/mcm)\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbase%2Fmcm-go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbase%2Fmcm-go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbase%2Fmcm-go/lists"}