Skip to content

4.3 Custody and MPC Integration

Institutional DeFi vaults primarily rely on non-custodial key management solutions, where the underlying assets are secured by the institution itself or a licensed, technology-enabled custodian.

MPC Technology Overview

Multiparty Computation (MPC) allows multiple parties to jointly compute a function over their inputs while keeping those inputs private. For digital asset custody, MPC enables distributed key generation and signing without ever reconstructing the full private key.

MPC-CMP Protocol

Modern MPC implementations use MPC-CMP (Multiparty Computation - Canetti-Makriyannis-Peled), which provides:

  • Single-round signing: Low latency for production use
  • Threshold signatures: t-of-n signing with configurable threshold
  • Proactive security: Key shares can be refreshed without changing the public key
  • Robust to node failure: Continues operating with threshold available

Key Requirements

1. Multichain Address Consistency

The custody solution must maintain an identical vault address across all target EVM chains:

Address Derivation:
  Private Key → Public Key → Ethereum Address
  0x742d... (same on all EVM chains)

Benefits:
  ✓ Simplified cross-chain management
  ✓ Unified accounting
  ✓ Single custody integration
  ✓ Easier user experience

2. Auditability

Beyond policy audit provided by Canton, the MPC system must track all asset movements:

typescript
interface CustodyAuditLog {
  timestamp: number;
  operation: 'sign' | 'transfer' | 'swap';
  chain: string;
  txHash: string;
  vaultAddress: string;
  cantonIntentId: string;
  signers: string[];  // Which key shares participated
  policyValidation: boolean;
}

Integration Patterns

Canton guarantees policy compliance, while MPC provides custody and execution speed.

┌────────────────────────────────┐
│     Canton Policy Layer        │
│  • Enforces VaultPolicy        │
│  • Generates signed Intent     │
└────────────┬───────────────────┘
             │ Verified Intent

┌────────────▼───────────────────┐
│      Relayer Service           │
│  • Verifies Canton signature   │
│  • Prepares raw transaction    │
└────────────┬───────────────────┘
             │ Unsigned Tx +
             │ Canton Intent
┌────────────▼───────────────────┐
│       MPC Cluster              │
│  • Validates against policy    │
│  • Distributed signing         │
│  • Broadcasts transaction      │
└────────────┬───────────────────┘
             │ Signed Tx

┌────────────▼───────────────────┐
│      Public Blockchain         │
│  • Executes transaction        │
└────────────────────────────────┘

MPC Policy Enforcement

The MPC service must mirror Canton policy constraints:

typescript
interface MPCPolicyEngine {
  // Check transaction against policy
  async validateTransaction(
    tx: Transaction,
    cantonIntent: ExecutionIntent
  ): Promise<ValidationResult>;

  // Enforce limits
  checkDailyLimit(amount: BigNumber): Promise<boolean>;
  checkWhitelist(address: string): Promise<boolean>;
  checkVelocityLimit(timeWindow: number): Promise<boolean>;
}

class MPCService {
  async signTransaction(request: SignRequest): Promise<SignedTx> {
    // 1. Verify Canton intent
    const intentValid = await this.verifyCantonSignature(
      request.cantonIntent
    );
    if (!intentValid) {
      throw new Error('Invalid Canton intent');
    }

    // 2. Validate against MPC policy
    const policyCheck = await this.policyEngine.validateTransaction(
      request.transaction,
      request.cantonIntent
    );

    if (!policyCheck.valid) {
      throw new Error(`Policy violation: ${policyCheck.reason}`);
    }

    // 3. Execute MPC signing ceremony
    const signature = await this.mpcSign(
      request.transaction,
      this.keyShares
    );

    // 4. Audit log
    await this.auditLog.record({
      operation: 'sign',
      cantonIntentId: request.cantonIntent.id,
      txHash: computeTxHash(request.transaction, signature),
      policyValidation: true
    });

    return {
      ...request.transaction,
      signature
    };
  }
}

MPC Providers

Leading Solutions

ProviderFeaturesUse Case
FireblocksEnterprise-grade, insurance, wide chain supportInstitutional custody
QredoDecentralized MPC, Layer 2 settlementCross-chain DeFi
SepiorFIPS 140-2 certified, on-premiseRegulated entities
Coinbase PrimeIntegrated with exchange, prime brokerageHedge funds
Anchorage DigitalOCC-chartered bank, regulatory clarityUS institutions

Integration Example: Fireblocks

typescript
import { FireblocksSDK } from '@fireblocks/sdk';

class FireblocksCustody implements CustodyProvider {
  private fireblocks: FireblocksSDK;

  constructor(apiKey: string, privateKeyPath: string) {
    this.fireblocks = new FireblocksSDK(
      privateKeyPath,
      apiKey
    );
  }

  async signAndSubmit(
    transaction: Transaction,
    cantonIntent: ExecutionIntent
  ): Promise<string> {
    // Create Fireblocks transaction
    const txInfo = await this.fireblocks.createTransaction({
      operation: 'CONTRACT_CALL',
      source: {
        type: 'VAULT_ACCOUNT',
        id: this.vaultAccountId
      },
      destination: {
        type: 'EXTERNAL_WALLET',
        oneTimeAddress: {
          address: transaction.to
        }
      },
      assetId: this.getAssetId(transaction.chainId),
      amount: transaction.value.toString(),
      extraParameters: {
        contractCallData: transaction.data
      },
      note: `Canton Intent: ${cantonIntent.id}`,
      externalTxId: cantonIntent.nonce.toString()
    });

    // Wait for confirmation
    const result = await this.waitForConfirmation(txInfo.id);

    return result.txHash;
  }
}

Security Considerations

Threshold Configuration

Conservative (2-of-3):
  • 2 signatures required
  • Tolerates 1 node failure
  • Higher security, moderate availability

Balanced (3-of-5):
  • 3 signatures required
  • Tolerates 2 node failures
  • Balanced security and availability

High Availability (5-of-9):
  • 5 signatures required
  • Tolerates 4 node failures
  • Maximum availability

Key Share Distribution

Geographic Distribution:
  Share 1: US East (AWS)
  Share 2: US West (Azure)
  Share 3: EU West (GCP)
  Share 4: Asia Pacific (Alibaba Cloud)
  Share 5: On-premise HSM

Prevents:
  ✓ Single data center failure
  ✓ Regional disasters
  ✓ Cloud provider outages
  ✓ Regulatory seizure risk

Operational Security

typescript
// Require multi-person approval for sensitive operations
interface ApprovalPolicy {
  operations: {
    keyRefresh: { required: 2, timeout: '1h' };
    thresholdChange: { required: 3, timeout: '24h' };
    emergencyWithdraw: { required: 3, timeout: '15m' };
  };
}

// Implement velocity limits
interface VelocityLimits {
  hourly: BigNumber;   // Max per hour
  daily: BigNumber;    // Max per day
  perTransaction: BigNumber;  // Max per tx
}

Best Practices

1. Policy Synchronization

Ensure Canton and MPC policies stay synchronized:

typescript
async function syncPolicies(): Promise<void> {
  // Fetch latest Canton policy
  const cantonPolicy = await canton.fetchPolicy(policyId);

  // Update MPC policy engine
  await mpc.updatePolicy({
    maxExposure: cantonPolicy.maxExposurePerc,
    allowedProtocols: cantonPolicy.allowedProtocols,
    dailyLimit: cantonPolicy.dailyLimit,
    whitelistedAddresses: cantonPolicy.allowedContracts
  });

  // Verify sync
  const mpcPolicy = await mpc.getPolicy();
  assert.deepEqual(mpcPolicy, cantonPolicy);
}

2. Disaster Recovery

Recovery Procedure:
1. Secure key share backups in cold storage
2. Implement Shamir's Secret Sharing for recovery keys
3. Regular DR drills (quarterly)
4. Document recovery procedures
5. 24/7 on-call rotation

3. Monitoring

typescript
const metrics = {
  mpcLatency: new Histogram({
    name: 'mpc_signing_latency_seconds',
    help: 'Time to complete MPC signing ceremony'
  }),

  policyViolations: new Counter({
    name: 'mpc_policy_violations_total',
    help: 'Number of policy violations caught by MPC'
  }),

  keyShareHealth: new Gauge({
    name: 'mpc_key_share_health',
    help: 'Health status of each key share',
    labelNames: ['share_id', 'location']
  })
};

4. Audit Trail

Maintain comprehensive custody audit logs:

sql
CREATE TABLE custody_audit_log (
  id SERIAL PRIMARY KEY,
  timestamp TIMESTAMPTZ NOT NULL,
  operation TEXT NOT NULL,
  canton_intent_id TEXT NOT NULL,
  vault_address TEXT NOT NULL,
  chain TEXT NOT NULL,
  tx_hash TEXT,
  signers JSONB NOT NULL,  -- Which key shares signed
  policy_validated BOOLEAN NOT NULL,
  metadata JSONB
);

-- Index for fast lookups
CREATE INDEX idx_intent ON custody_audit_log(canton_intent_id);
CREATE INDEX idx_timestamp ON custody_audit_log(timestamp DESC);

Canton DeFi - Multichain DeFi Technical Reference