ika-move

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Ika Move Integration

Ika Move集成指南

Build Sui Move contracts integrating Ika dWallet 2PC-MPC for programmable cross-chain signing.
构建集成Ika dWallet 2PC-MPC的Sui Move合约,实现可编程跨链签名。

References (detailed patterns and complete code)

参考资料(详细模式与完整代码)

  • references/protocols-detailed.md
    - All coordinator function signatures with full parameter lists
  • references/patterns.md
    - Complete integration patterns: treasury, DAO governance, imported key wallet, presign pool, events, enums
  • references/typescript-integration.md
    - Full TypeScript SDK flows: DKG prep, signing, key import, polling, IkaTransaction API
  • references/protocols-detailed.md
    - 包含完整参数列表的所有协调器函数签名
  • references/patterns.md
    - 完整集成模式:金库、DAO治理、导入密钥钱包、预签名池、事件、枚举
  • references/typescript-integration.md
    - 完整TypeScript SDK流程:DKG准备、签名、密钥导入、轮询、IkaTransaction API

Setup

环境搭建

Move.toml

Move.toml

toml
[package]
name = "my_project"
edition = "2024.beta"

[dependencies]
Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/testnet" }
ika_dwallet_2pc_mpc = { git = "https://github.com/dwallet-labs/ika.git", subdir = "deployed_contracts/testnet/ika_dwallet_2pc_mpc", rev = "main" }
ika = { git = "https://github.com/dwallet-labs/ika.git", subdir = "deployed_contracts/testnet/ika", rev = "main" }

[addresses]
my_project = "0x0"
For mainnet: change
testnet
to
mainnet
in paths.
toml
[package]
name = "my_project"
edition = "2024.beta"

[dependencies]
Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/testnet" }
ika_dwallet_2pc_mpc = { git = "https://github.com/dwallet-labs/ika.git", subdir = "deployed_contracts/testnet/ika_dwallet_2pc_mpc", rev = "main" }
ika = { git = "https://github.com/dwallet-labs/ika.git", subdir = "deployed_contracts/testnet/ika", rev = "main" }

[addresses]
my_project = "0x0"
主网配置:将路径中的
testnet
替换为
mainnet

TypeScript SDK

TypeScript SDK

bash
pnpm add @ika.xyz/sdk
typescript
import { getNetworkConfig, IkaClient } from '@ika.xyz/sdk';
import { getJsonRpcFullnodeUrl, SuiJsonRpcClient } from '@mysten/sui/jsonRpc';
const suiClient = new SuiJsonRpcClient({ url: getJsonRpcFullnodeUrl('testnet'), network: 'testnet' });
const ikaClient = new IkaClient({ suiClient, config: getNetworkConfig('testnet'), cache: true });
await ikaClient.initialize();
bash
pnpm add @ika.xyz/sdk
typescript
import { getNetworkConfig, IkaClient } from '@ika.xyz/sdk';
import { getJsonRpcFullnodeUrl, SuiJsonRpcClient } from '@mysten/sui/jsonRpc';
const suiClient = new SuiJsonRpcClient({ url: getJsonRpcFullnodeUrl('testnet'), network: 'testnet' });
const ikaClient = new IkaClient({ suiClient, config: getNetworkConfig('testnet'), cache: true });
await ikaClient.initialize();

Crypto Constants

加密常量

Curves

曲线类型

  • 0
    = SECP256K1 (Bitcoin, Ethereum)
  • 1
    = SECP256R1 (WebAuthn)
  • 2
    = ED25519 (Solana, Substrate)
  • 3
    = RISTRETTO (Privacy)
  • 0
    = SECP256K1(比特币、以太坊)
  • 1
    = SECP256R1(WebAuthn)
  • 2
    = ED25519(Solana、Substrate)
  • 3
    = RISTRETTO(隐私场景)

Signature Algorithms (relative to curve)

签名算法(与曲线对应)

  • SECP256K1:
    0
    =ECDSA,
    1
    =Taproot
  • SECP256R1:
    0
    =ECDSA
  • ED25519:
    0
    =EdDSA
  • RISTRETTO:
    0
    =Schnorrkel
  • SECP256K1:
    0
    =ECDSA,
    1
    =Taproot
  • SECP256R1:
    0
    =ECDSA
  • ED25519:
    0
    =EdDSA
  • RISTRETTO:
    0
    =Schnorrkel

Hash Schemes (relative to curve+algo)

哈希方案(与曲线+算法对应)

  • SECP256K1+ECDSA:
    0
    =KECCAK256(Ethereum),
    1
    =SHA256,
    2
    =DoubleSHA256(Bitcoin)
  • SECP256K1+Taproot:
    0
    =SHA256
  • SECP256R1+ECDSA:
    0
    =SHA256
  • ED25519+EdDSA:
    0
    =SHA512
  • RISTRETTO+Schnorrkel:
    0
    =Merlin
  • SECP256K1+ECDSA:
    0
    =KECCAK256(以太坊),
    1
    =SHA256,
    2
    =DoubleSHA256(比特币)
  • SECP256K1+Taproot:
    0
    =SHA256
  • SECP256R1+ECDSA:
    0
    =SHA256
  • ED25519+EdDSA:
    0
    =SHA512
  • RISTRETTO+Schnorrkel:
    0
    =Merlin

Core Imports

核心导入

rust
use ika::ika::IKA;
use ika_dwallet_2pc_mpc::{
    coordinator::DWalletCoordinator,
    coordinator_inner::{
        DWalletCap, ImportedKeyDWalletCap,
        UnverifiedPresignCap, VerifiedPresignCap,
        UnverifiedPartialUserSignatureCap, VerifiedPartialUserSignatureCap,
        MessageApproval, ImportedKeyMessageApproval
    },
    sessions_manager::SessionIdentifier
};
use sui::{balance::Balance, coin::Coin, sui::SUI};
rust
use ika::ika::IKA;
use ika_dwallet_2pc_mpc::{
    coordinator::DWalletCoordinator,
    coordinator_inner::{
        DWalletCap, ImportedKeyDWalletCap,
        UnverifiedPresignCap, VerifiedPresignCap,
        UnverifiedPartialUserSignatureCap, VerifiedPartialUserSignatureCap,
        MessageApproval, ImportedKeyMessageApproval
    },
    sessions_manager::SessionIdentifier
};
use sui::{balance::Balance, coin::Coin, sui::SUI};

Contract Template

合约模板

rust
public struct MyContract has key, store {
    id: UID,
    dwallet_cap: DWalletCap,
    presigns: vector<UnverifiedPresignCap>,
    ika_balance: Balance<IKA>,
    sui_balance: Balance<SUI>,
    dwallet_network_encryption_key_id: ID,
}
rust
public struct MyContract has key, store {
    id: UID,
    dwallet_cap: DWalletCap,
    presigns: vector<UnverifiedPresignCap>,
    ika_balance: Balance<IKA>,
    sui_balance: Balance<SUI>,
    dwallet_network_encryption_key_id: ID,
}

Required Helpers

必备辅助函数

rust
fun random_session(coordinator: &mut DWalletCoordinator, ctx: &mut TxContext): SessionIdentifier {
    coordinator.register_session_identifier(ctx.fresh_object_address().to_bytes(), ctx)
}

fun withdraw_payment_coins(self: &mut MyContract, ctx: &mut TxContext): (Coin<IKA>, Coin<SUI>) {
    let ika = self.ika_balance.withdraw_all().into_coin(ctx);
    let sui = self.sui_balance.withdraw_all().into_coin(ctx);
    (ika, sui)
}

fun return_payment_coins(self: &mut MyContract, ika: Coin<IKA>, sui: Coin<SUI>) {
    self.ika_balance.join(ika.into_balance());
    self.sui_balance.join(sui.into_balance());
}

public fun add_ika_balance(self: &mut MyContract, coin: Coin<IKA>) {
    self.ika_balance.join(coin.into_balance());
}

public fun add_sui_balance(self: &mut MyContract, coin: Coin<SUI>) {
    self.sui_balance.join(coin.into_balance());
}
rust
fun random_session(coordinator: &mut DWalletCoordinator, ctx: &mut TxContext): SessionIdentifier {
    coordinator.register_session_identifier(ctx.fresh_object_address().to_bytes(), ctx)
}

fun withdraw_payment_coins(self: &mut MyContract, ctx: &mut TxContext): (Coin<IKA>, Coin<SUI>) {
    let ika = self.ika_balance.withdraw_all().into_coin(ctx);
    let sui = self.sui_balance.withdraw_all().into_coin(ctx);
    (ika, sui)
}

fun return_payment_coins(self: &mut MyContract, ika: Coin<IKA>, sui: Coin<SUI>) {
    self.ika_balance.join(ika.into_balance());
    self.sui_balance.join(sui.into_balance());
}

public fun add_ika_balance(self: &mut MyContract, coin: Coin<IKA>) {
    self.ika_balance.join(coin.into_balance());
}

public fun add_sui_balance(self: &mut MyContract, coin: Coin<SUI>) {
    self.sui_balance.join(coin.into_balance());
}

DWalletCoordinator

DWalletCoordinator

Central shared object for all operations. Pass as
&mut DWalletCoordinator
for mutations,
&DWalletCoordinator
for reads.
Get ID in TypeScript:
ikaClient.ikaConfig.objects.ikaDWalletCoordinator.objectID
所有操作的核心共享对象。变更操作传入
&mut DWalletCoordinator
,只读操作传入
&DWalletCoordinator
在TypeScript中获取ID:
ikaClient.ikaConfig.objects.ikaDWalletCoordinator.objectID

Capabilities

权限能力(Capabilities)

CapabilityPurposeCreated By
DWalletCap
Authorize signingDKG
ImportedKeyDWalletCap
Authorize imported key signingImport verification
UnverifiedPresignCap
Presign reference (needs verify)Presign request
VerifiedPresignCap
Ready for signing
verify_presign_cap()
UnverifiedPartialUserSignatureCap
Partial sig (needs verify)Future sign
VerifiedPartialUserSignatureCap
Ready for completion
verify_partial_user_signature_cap()
MessageApproval
Auth to sign specific message
approve_message()
ImportedKeyMessageApproval
Auth for imported keys
approve_imported_key_message()
权限能力用途创建方式
DWalletCap
授权签名DKG流程
ImportedKeyDWalletCap
授权导入密钥签名导入验证流程
UnverifiedPresignCap
预签名引用(需验证)预签名请求
VerifiedPresignCap
可用于签名
verify_presign_cap()
UnverifiedPartialUserSignatureCap
部分签名(需验证)未来签名流程
VerifiedPartialUserSignatureCap
可完成签名
verify_partial_user_signature_cap()
MessageApproval
授权签署特定消息
approve_message()
ImportedKeyMessageApproval
导入密钥的授权
approve_imported_key_message()

SessionIdentifier

SessionIdentifier

Every protocol op needs a unique
SessionIdentifier
. Create via:
rust
let session = coordinator.register_session_identifier(ctx.fresh_object_address().to_bytes(), ctx);
Rules: create just before use, never reuse, one per operation.
每个协议操作都需要唯一的
SessionIdentifier
,创建方式:
rust
let session = coordinator.register_session_identifier(ctx.fresh_object_address().to_bytes(), ctx);
规则:使用前创建,绝不重复使用,每个操作对应一个。

Payment Pattern

支付模式

All ops require IKA+SUI fees. Pattern: withdraw all -> perform ops (fees auto-deducted from
&mut Coin
) -> return remainder.
所有操作都需要支付IKA+SUI费用。模式:取出全部费用 -> 执行操作(费用自动从
&mut Coin
中扣除) -> 返回剩余费用。

Protocol: DKG (Create dWallet)

协议:DKG(创建dWallet)

Shared dWallet (recommended for contracts)

共享dWallet(合约场景推荐)

Public user share, network signs without user interaction.
rust
let (dwallet_cap, _) = coordinator.request_dwallet_dkg_with_public_user_secret_key_share(
    dwallet_network_encryption_key_id, curve,
    centralized_public_key_share_and_proof, user_public_output, public_user_secret_key_share,
    option::none(), // sign_during_dkg_request
    session, &mut ika, &mut sui, ctx,
);
用户密钥份额公开,无需用户交互即可由网络完成签名。
rust
let (dwallet_cap, _) = coordinator.request_dwallet_dkg_with_public_user_secret_key_share(
    dwallet_network_encryption_key_id, curve,
    centralized_public_key_share_and_proof, user_public_output, public_user_secret_key_share,
    option::none(), // sign_during_dkg_request
    session, &mut ika, &mut sui, ctx,
);

Zero-Trust dWallet

零信任dWallet

Encrypted user share, user must participate in every signature.
rust
let (dwallet_cap, _) = coordinator.request_dwallet_dkg(
    dwallet_network_encryption_key_id, curve,
    centralized_public_key_share_and_proof, encrypted_centralized_secret_share_and_proof,
    encryption_key_address, user_public_output, signer_public_key,
    option::none(), session, &mut ika, &mut sui, ctx,
);
Important: After zero-trust DKG (or key import), the dWallet is in
AwaitingKeyHolderSignature
state. The user must call
acceptEncryptedUserShare
to transition the dWallet to
Active
state before it can be used for signing:
typescript
// Wait for DKG to complete and dWallet to reach AwaitingKeyHolderSignature state
const awaitingDWallet = await ikaClient.getDWalletInParticularState(dwalletId, 'AwaitingKeyHolderSignature');
// The encrypted user secret key share ID comes from the DKG transaction event,
// NOT from the dWallet ID.
const encryptedShare = await ikaClient.getEncryptedUserSecretKeyShare(encryptedUserSecretKeyShareId);

const tx = new Transaction();
const ikaTx = new IkaTransaction({ ikaClient, transaction: tx, userShareEncryptionKeys: keys });
await ikaTx.acceptEncryptedUserShare({
    dWallet: awaitingDWallet,
    encryptedUserSecretKeyShareId: encryptedShare.id,
    userPublicOutput: new Uint8Array(dkgData.userPublicOutput),
});
await suiClient.core.signAndExecuteTransaction({ transaction: tx, signer: keypair });

// dWallet is now Active and ready for signing
const activeDWallet = await ikaClient.getDWalletInParticularState(dwalletId, 'Active');
This step is NOT needed for shared dWallets (created with
request_dwallet_dkg_with_public_user_secret_key_share
).
用户密钥份额加密,每次签名都需要用户参与。
rust
let (dwallet_cap, _) = coordinator.request_dwallet_dkg(
    dwallet_network_encryption_key_id, curve,
    centralized_public_key_share_and_proof, encrypted_centralized_secret_share_and_proof,
    encryption_key_address, user_public_output, signer_public_key,
    option::none(), session, &mut ika, &mut sui, ctx,
);
重要提示: 零信任DKG(或密钥导入)完成后,dWallet处于
AwaitingKeyHolderSignature
状态。用户必须调用
acceptEncryptedUserShare
将dWallet切换到
Active
状态后才能用于签名:
typescript
// 等待DKG完成,dWallet进入AwaitingKeyHolderSignature状态
const awaitingDWallet = await ikaClient.getDWalletInParticularState(dwalletId, 'AwaitingKeyHolderSignature');
// 加密的用户密钥份额ID来自DKG交易事件,而非dWallet ID
const encryptedShare = await ikaClient.getEncryptedUserSecretKeyShare(encryptedUserSecretKeyShareId);

const tx = new Transaction();
const ikaTx = new IkaTransaction({ ikaClient, transaction: tx, userShareEncryptionKeys: keys });
await ikaTx.acceptEncryptedUserShare({
    dWallet: awaitingDWallet,
    encryptedUserSecretKeyShareId: encryptedShare.id,
    userPublicOutput: new Uint8Array(dkgData.userPublicOutput),
});
await suiClient.core.signAndExecuteTransaction({ transaction: tx, signer: keypair });

// dWallet现在处于Active状态,可用于签名
const activeDWallet = await ikaClient.getDWalletInParticularState(dwalletId, 'Active');
共享dWallet(通过
request_dwallet_dkg_with_public_user_secret_key_share
创建)无需此步骤。

TypeScript DKG Prep

TypeScript DKG准备流程

typescript
import { prepareDKGAsync, UserShareEncryptionKeys, Curve, createRandomSessionIdentifier } from '@ika.xyz/sdk';
const keys = await UserShareEncryptionKeys.fromRootSeedKey(seed, Curve.SECP256K1);
const bytesToHash = createRandomSessionIdentifier(); // bytes hashed to derive the session identifier
const dkgData = await prepareDKGAsync(ikaClient, Curve.SECP256K1, keys, bytesToHash, signerAddress);
const networkKey = await ikaClient.getLatestNetworkEncryptionKey();
// dkgData has: userDKGMessage, userPublicOutput, encryptedUserShareAndProof, userSecretKeyShare
typescript
import { prepareDKGAsync, UserShareEncryptionKeys, Curve, createRandomSessionIdentifier } from '@ika.xyz/sdk';
const keys = await UserShareEncryptionKeys.fromRootSeedKey(seed, Curve.SECP256K1);
const bytesToHash = createRandomSessionIdentifier(); // 用于派生session identifier的字节数据
const dkgData = await prepareDKGAsync(ikaClient, Curve.SECP256K1, keys, bytesToHash, signerAddress);
const networkKey = await ikaClient.getLatestNetworkEncryptionKey();
// dkgData包含:userDKGMessage, userPublicOutput, encryptedUserShareAndProof, userSecretKeyShare

Sign During DKG (optional)

DKG期间签名(可选)

Pass existing presign to get signature during DKG:
rust
let sign_req = coordinator.sign_during_dkg_request(verified_presign, hash_scheme, message, msg_sig);
// Pass option::some(sign_req) instead of option::none() in DKG call
传入已有的预签名,在DKG流程中同时获取签名:
rust
let sign_req = coordinator.sign_during_dkg_request(verified_presign, hash_scheme, message, msg_sig);
// 在DKG调用中传入option::some(sign_req)替代option::none()

Protocol: Presigning

协议:预签名

Each signature consumes one presign. Manage a pool.
每个签名会消耗一个预签名,建议维护预签名池。

Global Presign (most common: Taproot, EdDSA, Schnorr, ECDSA)

全局预签名(最常用:Taproot、EdDSA、Schnorr、ECDSA)

rust
let cap = coordinator.request_global_presign(
    dwallet_network_encryption_key_id, curve, signature_algorithm,
    session, &mut ika, &mut sui, ctx,
);
self.presigns.push_back(cap);
rust
let cap = coordinator.request_global_presign(
    dwallet_network_encryption_key_id, curve, signature_algorithm,
    session, &mut ika, &mut sui, ctx,
);
self.presigns.push_back(cap);

dWallet-Specific Presign (ECDSA with imported keys)

特定dWallet预签名(导入密钥的ECDSA场景)

rust
let cap = coordinator.request_presign(
    dwallet_id, signature_algorithm, session, &mut ika, &mut sui, ctx,
);
rust
let cap = coordinator.request_presign(
    dwallet_id, signature_algorithm, session, &mut ika, &mut sui, ctx,
);

Verify Before Use

使用前验证

rust
let is_ready = coordinator.is_presign_valid(&unverified_cap);
let verified = coordinator.verify_presign_cap(unverified_cap, ctx); // fails if not ready
rust
let is_ready = coordinator.is_presign_valid(&unverified_cap);
let verified = coordinator.verify_presign_cap(unverified_cap, ctx); // 验证失败则报错

Pool Management

预签名池管理

rust
// Pop from pool
let unverified = self.presigns.swap_remove(0);

// Auto-replenish after signing
if (self.presigns.length() < MIN_POOL) {
    let s = random_session(coordinator, ctx);
    self.presigns.push_back(coordinator.request_global_presign(
        self.dwallet_network_encryption_key_id, curve, sig_algo, s, &mut ika, &mut sui, ctx,
    ));
};

// Batch add
let mut i = 0;
while (i < count) {
    let s = random_session(coordinator, ctx);
    self.presigns.push_back(coordinator.request_global_presign(..., s, &mut ika, &mut sui, ctx));
    i = i + 1;
};
rust
// 从池中取出
let unverified = self.presigns.swap_remove(0);

// 签名后自动补充
if (self.presigns.length() < MIN_POOL) {
    let s = random_session(coordinator, ctx);
    self.presigns.push_back(coordinator.request_global_presign(
        self.dwallet_network_encryption_key_id, curve, sig_algo, s, &mut ika, &mut sui, ctx,
    ));
};

// 批量添加
let mut i = 0;
while (i < count) {
    let s = random_session(coordinator, ctx);
    self.presigns.push_back(coordinator.request_global_presign(..., s, &mut ika, &mut sui, ctx));
    i = i + 1;
};

Protocol: Direct Signing

协议:直接签名

Single-phase, immediate signature. Use when no governance needed.
rust
// 1. Verify presign
let verified = coordinator.verify_presign_cap(self.presigns.swap_remove(0), ctx);

// 2. Approve message
let approval = coordinator.approve_message(&self.dwallet_cap, sig_algo, hash_scheme, message);

// 3. Sign
let sign_id = coordinator.request_sign_and_return_id(
    verified, approval, message_centralized_signature, session, &mut ika, &mut sui, ctx,
);
// Also: coordinator.request_sign(...) without return ID
单阶段、即时签名,适用于无需治理的场景。
rust
// 1. 验证预签名
let verified = coordinator.verify_presign_cap(self.presigns.swap_remove(0), ctx);

// 2. 授权消息
let approval = coordinator.approve_message(&self.dwallet_cap, sig_algo, hash_scheme, message);

// 3. 签名
let sign_id = coordinator.request_sign_and_return_id(
    verified, approval, message_centralized_signature, session, &mut ika, &mut sui, ctx,
);
// 也可使用:coordinator.request_sign(...) 不返回ID

TypeScript: Create User Signature

TypeScript:创建用户签名

typescript
import { createUserSignMessageWithPublicOutput, Curve, SignatureAlgorithm, Hash } from '@ika.xyz/sdk';
const completedPresign = await ikaClient.getPresignInParticularState(presignId, 'Completed');
const protocolPublicParameters = await ikaClient.getProtocolPublicParameters(undefined, Curve.SECP256K1);
const msgSig = await createUserSignMessageWithPublicOutput(
    protocolPublicParameters,
    dWallet.state.Active!.public_output,
    dWallet.public_user_secret_key_share,
    completedPresign.presign,
    message,
    Hash.SHA256,
    SignatureAlgorithm.Taproot,
    Curve.SECP256K1,
);
// Pass msgSig as message_centralized_signature to Move
typescript
import { createUserSignMessageWithPublicOutput, Curve, SignatureAlgorithm, Hash } from '@ika.xyz/sdk';
const completedPresign = await ikaClient.getPresignInParticularState(presignId, 'Completed');
const protocolPublicParameters = await ikaClient.getProtocolPublicParameters(undefined, Curve.SECP256K1);
const msgSig = await createUserSignMessageWithPublicOutput(
    protocolPublicParameters,
    dWallet.state.Active!.public_output,
    dWallet.public_user_secret_key_share,
    completedPresign.presign,
    message,
    Hash.SHA256,
    SignatureAlgorithm.Taproot,
    Curve.SECP256K1,
);
// 将msgSig作为message_centralized_signature传入Move合约

Retrieve Signature

获取签名

typescript
const signSession = await ikaClient.getSignInParticularState(
    signId, Curve.SECP256K1, SignatureAlgorithm.Taproot, 'Completed',
);
const sig = await parseSignatureFromSignOutput(Curve.SECP256K1, SignatureAlgorithm.Taproot, signSession.signature);
typescript
const signSession = await ikaClient.getSignInParticularState(
    signId, Curve.SECP256K1, SignatureAlgorithm.Taproot, 'Completed',
);
const sig = await parseSignatureFromSignOutput(Curve.SECP256K1, SignatureAlgorithm.Taproot, signSession.signature);

Protocol: Future Signing (Two-Phase)

协议:未来签名(两阶段)

Separates commitment from execution. For governance/multisig/delayed signing.
将承诺与执行分离,适用于治理/多签/延迟签名场景。

Phase 1: Create Partial Signature

阶段1:创建部分签名

rust
let partial_cap = coordinator.request_future_sign(
    self.dwallet_cap.dwallet_id(), verified_presign, message, hash_scheme,
    message_centralized_signature, session, &mut ika, &mut sui, ctx,
);
// Store partial_cap with request for later
rust
let partial_cap = coordinator.request_future_sign(
    self.dwallet_cap.dwallet_id(), verified_presign, message, hash_scheme,
    message_centralized_signature, session, &mut ika, &mut sui, ctx,
);
// 将partial_cap与请求一起存储,留待后续使用

Phase 2: Complete After Approval

阶段2:审批完成后执行

rust
// Verify partial sig
let verified_partial = coordinator.verify_partial_user_signature_cap(partial_cap, ctx);

// Create approval
let approval = coordinator.approve_message(&self.dwallet_cap, sig_algo, hash_scheme, message);

// Complete signature
let sign_id = coordinator.request_sign_with_partial_user_signature_and_return_id(
    verified_partial, approval, session, &mut ika, &mut sui, ctx,
);
rust
// 验证部分签名
let verified_partial = coordinator.verify_partial_user_signature_cap(partial_cap, ctx);

// 创建授权
let approval = coordinator.approve_message(&self.dwallet_cap, sig_algo, hash_scheme, message);

// 完成签名
let sign_id = coordinator.request_sign_with_partial_user_signature_and_return_id(
    verified_partial, approval, session, &mut ika, &mut sui, ctx,
);

Check/Match

检查/匹配

rust
let ready = coordinator.is_partial_user_signature_valid(&unverified_cap);
let matches = coordinator.match_partial_user_signature_with_message_approval(&verified, &approval);
rust
let ready = coordinator.is_partial_user_signature_valid(&unverified_cap);
let matches = coordinator.match_partial_user_signature_with_message_approval(&verified, &approval);

Protocol: Key Importing

协议:密钥导入

Import existing private key into dWallet system. Returns
ImportedKeyDWalletCap
.
rust
let imported_cap = coordinator.request_imported_key_dwallet_verification(
    dwallet_network_encryption_key_id, curve, centralized_party_message,
    encrypted_centralized_secret_share_and_proof, encryption_key_address,
    user_public_output, signer_public_key, session, &mut ika, &mut sui, ctx,
);
将现有私钥导入dWallet系统,返回
ImportedKeyDWalletCap
rust
let imported_cap = coordinator.request_imported_key_dwallet_verification(
    dwallet_network_encryption_key_id, curve, centralized_party_message,
    encrypted_centralized_secret_share_and_proof, encryption_key_address,
    user_public_output, signer_public_key, session, &mut ika, &mut sui, ctx,
);

Imported Key Differences

导入密钥的差异

  • Use
    ImportedKeyDWalletCap
    instead of
    DWalletCap
  • Use
    coordinator.approve_imported_key_message(...)
    instead of
    approve_message
  • Use
    coordinator.request_imported_key_sign_and_return_id(...)
    for signing
  • Use
    coordinator.request_presign(dwallet_id, ...)
    for ECDSA presigns (dWallet-specific)
  • Future sign completion:
    request_imported_key_sign_with_partial_user_signature_and_return_id
  • 使用
    ImportedKeyDWalletCap
    替代
    DWalletCap
  • 使用
    coordinator.approve_imported_key_message(...)
    替代
    approve_message
  • 使用
    coordinator.request_imported_key_sign_and_return_id(...)
    进行签名
  • 预签名使用
    coordinator.request_presign(dwallet_id, ...)
    (仅适用于ECDSA的特定dWallet场景)
  • 未来签名完成使用:
    request_imported_key_sign_with_partial_user_signature_and_return_id

Protocol: Convert Zero-Trust to Shared

协议:零信任转共享dWallet

Irreversible. Makes user secret key share public, enabling contract-owned signing.
rust
coordinator.request_make_dwallet_user_secret_key_shares_public(
    dwallet_id, public_user_secret_key_shares, session, &mut ika, &mut sui, ctx,
);
TypeScript: save
dkgData.userSecretKeyShare
during DKG for potential later conversion.
不可逆操作,将用户密钥份额公开,支持合约自主签名。
rust
coordinator.request_make_dwallet_user_secret_key_shares_public(
    dwallet_id, public_user_secret_key_shares, session, &mut ika, &mut sui, ctx,
);
TypeScript提示:DKG期间保存
dkgData.userSecretKeyShare
,以备后续转换使用。

Complete Example: Bitcoin Taproot Treasury

完整示例:比特币Taproot金库

rust
module my_protocol::treasury;

use ika::ika::IKA;
use ika_dwallet_2pc_mpc::{
    coordinator::DWalletCoordinator,
    coordinator_inner::{DWalletCap, UnverifiedPresignCap, UnverifiedPartialUserSignatureCap}
};
use sui::{balance::Balance, coin::Coin, sui::SUI, table::{Self, Table}};

const SECP256K1: u32 = 0;
const TAPROOT: u32 = 1;
const SHA256: u32 = 0;
const MIN_PRESIGNS: u64 = 3;

public struct Treasury has key, store {
    id: UID,
    dwallet_cap: DWalletCap,
    presigns: vector<UnverifiedPresignCap>,
    members: vector<address>,
    approval_threshold: u64,
    proposals: Table<u64, Proposal>,
    next_id: u64,
    ika_balance: Balance<IKA>,
    sui_balance: Balance<SUI>,
    dwallet_network_encryption_key_id: ID,
}

public struct Proposal has store {
    message: vector<u8>,
    partial_cap: Option<UnverifiedPartialUserSignatureCap>,
    approvals: u64,
    voters: Table<address, bool>,
    executed: bool,
}

// Create treasury with shared dWallet
public fun create(
    coordinator: &mut DWalletCoordinator,
    mut ika: Coin<IKA>, mut sui: Coin<SUI>,
    enc_key_id: ID,
    dkg_msg: vector<u8>, user_output: vector<u8>, user_share: vector<u8>,
    session_bytes: vector<u8>,
    members: vector<address>, threshold: u64,
    ctx: &mut TxContext,
) {
    let session = coordinator.register_session_identifier(session_bytes, ctx);
    let (dwallet_cap, _) = coordinator.request_dwallet_dkg_with_public_user_secret_key_share(
        enc_key_id, SECP256K1, dkg_msg, user_output, user_share,
        option::none(), session, &mut ika, &mut sui, ctx,
    );

    let treasury = Treasury {
        id: object::new(ctx), dwallet_cap, presigns: vector::empty(),
        members, approval_threshold: threshold,
        proposals: table::new(ctx), next_id: 0,
        ika_balance: ika.into_balance(), sui_balance: sui.into_balance(),
        dwallet_network_encryption_key_id: enc_key_id,
    };
    transfer::public_share_object(treasury);
}

// Create proposal with future sign (Phase 1)
public fun propose(
    self: &mut Treasury, coordinator: &mut DWalletCoordinator,
    message: vector<u8>, msg_sig: vector<u8>, ctx: &mut TxContext,
): u64 {
    assert!(self.members.contains(&ctx.sender()), 0);
    let (mut ika, mut sui) = self.withdraw_payment_coins(ctx);

    let verified = coordinator.verify_presign_cap(self.presigns.swap_remove(0), ctx);
    let session = random_session(coordinator, ctx);
    let partial = coordinator.request_future_sign(
        self.dwallet_cap.dwallet_id(), verified, message, SHA256,
        msg_sig, session, &mut ika, &mut sui, ctx,
    );

    let id = self.next_id;
    self.next_id = id + 1;
    self.proposals.add(id, Proposal {
        message, partial_cap: option::some(partial),
        approvals: 0, voters: table::new(ctx), executed: false,
    });

    // Auto-replenish
    if (self.presigns.length() < MIN_PRESIGNS) {
        let s = random_session(coordinator, ctx);
        self.presigns.push_back(coordinator.request_global_presign(
            self.dwallet_network_encryption_key_id, SECP256K1, TAPROOT, s,
            &mut ika, &mut sui, ctx,
        ));
    };
    self.return_payment_coins(ika, sui);
    id
}

// Vote
public fun vote(self: &mut Treasury, id: u64, approve: bool, ctx: &TxContext) {
    assert!(self.members.contains(&ctx.sender()), 0);
    let p = self.proposals.borrow_mut(id);
    assert!(!p.voters.contains(ctx.sender()) && !p.executed, 1);
    p.voters.add(ctx.sender(), approve);
    if (approve) { p.approvals = p.approvals + 1; };
}

// Execute after approval (Phase 2)
public fun execute(
    self: &mut Treasury, coordinator: &mut DWalletCoordinator,
    id: u64, ctx: &mut TxContext,
): ID {
    let p = self.proposals.borrow_mut(id);
    assert!(p.approvals >= self.approval_threshold && !p.executed, 2);
    let (mut ika, mut sui) = self.withdraw_payment_coins(ctx);

    let verified = coordinator.verify_partial_user_signature_cap(p.partial_cap.extract(), ctx);
    let approval = coordinator.approve_message(&self.dwallet_cap, TAPROOT, SHA256, p.message);
    let session = random_session(coordinator, ctx);
    let sign_id = coordinator.request_sign_with_partial_user_signature_and_return_id(
        verified, approval, session, &mut ika, &mut sui, ctx,
    );
    p.executed = true;
    self.return_payment_coins(ika, sui);
    sign_id
}

fun random_session(c: &mut DWalletCoordinator, ctx: &mut TxContext): SessionIdentifier {
    c.register_session_identifier(ctx.fresh_object_address().to_bytes(), ctx)
}
fun withdraw_payment_coins(self: &mut Treasury, ctx: &mut TxContext): (Coin<IKA>, Coin<SUI>) {
    (self.ika_balance.withdraw_all().into_coin(ctx), self.sui_balance.withdraw_all().into_coin(ctx))
}
fun return_payment_coins(self: &mut Treasury, ika: Coin<IKA>, sui: Coin<SUI>) {
    self.ika_balance.join(ika.into_balance());
    self.sui_balance.join(sui.into_balance());
}
rust
module my_protocol::treasury;

use ika::ika::IKA;
use ika_dwallet_2pc_mpc::{
    coordinator::DWalletCoordinator,
    coordinator_inner::{DWalletCap, UnverifiedPresignCap, UnverifiedPartialUserSignatureCap}
};
use sui::{balance::Balance, coin::Coin, sui::SUI, table::{Self, Table}};

const SECP256K1: u32 = 0;
const TAPROOT: u32 = 1;
const SHA256: u32 = 0;
const MIN_PRESIGNS: u64 = 3;

public struct Treasury has key, store {
    id: UID,
    dwallet_cap: DWalletCap,
    presigns: vector<UnverifiedPresignCap>,
    members: vector<address>,
    approval_threshold: u64,
    proposals: Table<u64, Proposal>,
    next_id: u64,
    ika_balance: Balance<IKA>,
    sui_balance: Balance<SUI>,
    dwallet_network_encryption_key_id: ID,
}

public struct Proposal has store {
    message: vector<u8>,
    partial_cap: Option<UnverifiedPartialUserSignatureCap>,
    approvals: u64,
    voters: Table<address, bool>,
    executed: bool,
}

// 创建带共享dWallet的金库
public fun create(
    coordinator: &mut DWalletCoordinator,
    mut ika: Coin<IKA>, mut sui: Coin<SUI>,
    enc_key_id: ID,
    dkg_msg: vector<u8>, user_output: vector<u8>, user_share: vector<u8>,
    session_bytes: vector<u8>,
    members: vector<address>, threshold: u64,
    ctx: &mut TxContext,
) {
    let session = coordinator.register_session_identifier(session_bytes, ctx);
    let (dwallet_cap, _) = coordinator.request_dwallet_dkg_with_public_user_secret_key_share(
        enc_key_id, SECP256K1, dkg_msg, user_output, user_share,
        option::none(), session, &mut ika, &mut sui, ctx,
    );

    let treasury = Treasury {
        id: object::new(ctx), dwallet_cap, presigns: vector::empty(),
        members, approval_threshold: threshold,
        proposals: table::new(ctx), next_id: 0,
        ika_balance: ika.into_balance(), sui_balance: sui.into_balance(),
        dwallet_network_encryption_key_id: enc_key_id,
    };
    transfer::public_share_object(treasury);
}

// 创建未来签名提案(阶段1)
public fun propose(
    self: &mut Treasury, coordinator: &mut DWalletCoordinator,
    message: vector<u8>, msg_sig: vector<u8>, ctx: &mut TxContext,
): u64 {
    assert!(self.members.contains(&ctx.sender()), 0);
    let (mut ika, mut sui) = self.withdraw_payment_coins(ctx);

    let verified = coordinator.verify_presign_cap(self.presigns.swap_remove(0), ctx);
    let session = random_session(coordinator, ctx);
    let partial = coordinator.request_future_sign(
        self.dwallet_cap.dwallet_id(), verified, message, SHA256,
        msg_sig, session, &mut ika, &mut sui, ctx,
    );

    let id = self.next_id;
    self.next_id = id + 1;
    self.proposals.add(id, Proposal {
        message, partial_cap: option::some(partial),
        approvals: 0, voters: table::new(ctx), executed: false,
    });

    // 自动补充预签名
    if (self.presigns.length() < MIN_PRESIGNS) {
        let s = random_session(coordinator, ctx);
        self.presigns.push_back(coordinator.request_global_presign(
            self.dwallet_network_encryption_key_id, SECP256K1, TAPROOT, s,
            &mut ika, &mut sui, ctx,
        ));
    };
    self.return_payment_coins(ika, sui);
    id
}

// 投票
public fun vote(self: &mut Treasury, id: u64, approve: bool, ctx: &TxContext) {
    assert!(self.members.contains(&ctx.sender()), 0);
    let p = self.proposals.borrow_mut(id);
    assert!(!p.voters.contains(ctx.sender()) && !p.executed, 1);
    p.voters.add(ctx.sender(), approve);
    if (approve) { p.approvals = p.approvals + 1; };
}

// 审批通过后执行(阶段2)
public fun execute(
    self: &mut Treasury, coordinator: &mut DWalletCoordinator,
    id: u64, ctx: &mut TxContext,
): ID {
    let p = self.proposals.borrow_mut(id);
    assert!(p.approvals >= self.approval_threshold && !p.executed, 2);
    let (mut ika, mut sui) = self.withdraw_payment_coins(ctx);

    let verified = coordinator.verify_partial_user_signature_cap(p.partial_cap.extract(), ctx);
    let approval = coordinator.approve_message(&self.dwallet_cap, TAPROOT, SHA256, p.message);
    let session = random_session(coordinator, ctx);
    let sign_id = coordinator.request_sign_with_partial_user_signature_and_return_id(
        verified, approval, session, &mut ika, &mut sui, ctx,
    );
    p.executed = true;
    self.return_payment_coins(ika, sui);
    sign_id
}

fun random_session(c: &mut DWalletCoordinator, ctx: &mut TxContext): SessionIdentifier {
    c.register_session_identifier(ctx.fresh_object_address().to_bytes(), ctx)
}
fun withdraw_payment_coins(self: &mut Treasury, ctx: &mut TxContext): (Coin<IKA>, Coin<SUI>) {
    (self.ika_balance.withdraw_all().into_coin(ctx), self.sui_balance.withdraw_all().into_coin(ctx))
}
fun return_payment_coins(self: &mut Treasury, ika: Coin<IKA>, sui: Coin<SUI>) {
    self.ika_balance.join(ika.into_balance());
    self.sui_balance.join(sui.into_balance());
}

TypeScript: Calling Move Functions

TypeScript:调用Move函数

typescript
const tx = new Transaction();
tx.moveCall({
    target: `${PACKAGE_ID}::treasury::create`,
    arguments: [
        tx.object(coordinatorId),
        tx.object(ikaCoinId),
        tx.splitCoins(tx.gas, [1000000]),
        tx.pure.id(networkKeyId),
        tx.pure.vector('u8', Array.from(dkgData.userDKGMessage)),
        tx.pure.vector('u8', Array.from(dkgData.userPublicOutput)),
        tx.pure.vector('u8', Array.from(dkgData.userSecretKeyShare)),
        tx.pure.vector('u8', Array.from(sessionIdentifier)),
        tx.pure.vector('address', members),
        tx.pure.u64(threshold),
    ],
});
await suiClient.core.signAndExecuteTransaction({ transaction: tx, signer: keypair });
typescript
const tx = new Transaction();
tx.moveCall({
    target: `${PACKAGE_ID}::treasury::create`,
    arguments: [
        tx.object(coordinatorId),
        tx.object(ikaCoinId),
        tx.splitCoins(tx.gas, [1000000]),
        tx.pure.id(networkKeyId),
        tx.pure.vector('u8', Array.from(dkgData.userDKGMessage)),
        tx.pure.vector('u8', Array.from(dkgData.userPublicOutput)),
        tx.pure.vector('u8', Array.from(dkgData.userSecretKeyShare)),
        tx.pure.vector('u8', Array.from(sessionIdentifier)),
        tx.pure.vector('address', members),
        tx.pure.u64(threshold),
    ],
});
await suiClient.core.signAndExecuteTransaction({ transaction: tx, signer: keypair });

Key Decision Points

关键决策点

  • Shared vs Zero-Trust: Use shared for contract-owned signing (DAOs, treasuries, bots). Use zero-trust for user-held wallets.
  • Direct vs Future Sign: Direct for immediate signing. Future for governance/multisig (two-phase).
  • Global vs dWallet-Specific Presign: Global for most cases. dWallet-specific only for ECDSA with imported keys.
  • DKG vs Import: DKG generates new distributed key (more secure). Import brings existing private key.
  • 共享vs零信任dWallet:共享dWallet适用于合约自主签名场景(DAO、金库、机器人);零信任dWallet适用于用户自持钱包。
  • 直接vs未来签名:直接签名适用于即时签名场景;未来签名适用于治理/多签场景(两阶段)。
  • 全局vs特定dWallet预签名:全局预签名适用于大多数场景;特定dWallet预签名仅适用于导入密钥的ECDSA场景。
  • DKGvs导入:DKG生成新的分布式密钥(更安全);导入用于接入现有私钥。

Constants Module Pattern (Bitcoin Taproot)

常量模块模式(比特币Taproot)

rust
module my_protocol::constants;
public macro fun curve(): u32 { 0 }
public macro fun signature_algorithm(): u32 { 1 }
public macro fun hash_scheme(): u32 { 0 }
rust
module my_protocol::constants;
public macro fun curve(): u32 { 0 }
public macro fun signature_algorithm(): u32 { 1 }
public macro fun hash_scheme(): u32 { 0 }

Chain-Specific Config Quick Reference

链特定配置速查

ChainCurveSig AlgoHash
Bitcoin (Taproot)010 (SHA256)
Bitcoin (Legacy)002 (DoubleSHA256)
Ethereum000 (KECCAK256)
Solana200 (SHA512)
WebAuthn100 (SHA256)
Substrate300 (Merlin)
曲线签名算法哈希
比特币(Taproot)010 (SHA256)
比特币(Legacy)002 (DoubleSHA256)
以太坊000 (KECCAK256)
Solana200 (SHA512)
WebAuthn100 (SHA256)
Substrate300 (Merlin)