This demo provides an example of how to generate a Sui address controlled by an ICP canister smart contract.
ICP canister smart contracts can create and manage wallet addresses on any blockchain that uses the ECDSA signature scheme including Ethereum, EVM, and Sui. This feature named threshold ECDSA enables various cross-chain use cases.
An ECDSA-based public key generated by an ICP canister smart contract can be derived into an unlimited amount of addresses on multiple chains. Therefore, this enables developers to create one ICP canister smart contract to interact with any blockchain that uses the ECDSA signature scheme including Ethereum, EVM, and Sui.
Developers can now easily create the following without needing to deploy multiple smart contracts on multiple chains:
- Multi-chain wallets (Example: Oisy Wallet
- Cross-chain messaging (Example: Omnity Network)
This demo example implements an ICP canister smart contract on Typecript using the Azle CDK.
src/backend/index.ts
stores the canister logic.
In src/backend/index.ts
, the suiAddress
function calls ICP to generate the ECDSA public key and uses the Sui SDK to convert the ECDSA public key to a Sui address.
suiAddress: update([], text, async () => {
const caller = ic.caller().toUint8Array();
const publicKeyResult = await ic.call(
managementCanister.ecdsa_public_key,
{
args: [
{
canister_id: None,
derivation_path: [caller],
key_id: {
curve: { secp256k1: null },
name: 'dfx_test_key'
}
}
]
}
);
const suiPublicKey = new Secp256k1PublicKey(publicKeyResult.public_key);
const suiAddress = suiPublicKey.toSuiAddress();
return suiAddress;
})
This code snippet calls the ICP management canister which exposes ICP system features including the creation of an ECDSA public key.
const caller = ic.caller().toUint8Array();
const publicKeyResult = await ic.call(
managementCanister.ecdsa_public_key,
{
args: [
{
canister_id: None,
derivation_path: [caller],
key_id: {
curve: { secp256k1: null },
name: 'dfx_test_key'
}
}
]
}
);
This code snippet converts the bytes of the public key returned from the ICP management canister into a secp256k1 public key as a base-64 encoded string. It then converts the secp256k1 public key into a Sui address.
const suiPublicKey = new Secp256k1PublicKey(publicKeyResult.public_key);
const suiAddress = suiPublicKey.toSuiAddress();
In src/backend/index.ts
, the sign
function is able to sign transactions for the ECDSA public key created above.
sign: update([blob], Signature, async (messageHash) => {
if (messageHash.length !== 32) {
ic.trap('messageHash must be 32 bytes');
}
const caller = ic.caller().toUint8Array();
const signatureResult = await ic.call(
managementCanister.sign_with_ecdsa,
{
args: [
{
message_hash: messageHash,
derivation_path: [caller],
key_id: {
curve: { secp256k1: null },
name: 'dfx_test_key'
}
}
],
cycles: 10_000_000_000n
}
);
return {
signature: signatureResult.signature
};
})